|
dvander@65
|
1 |
# vim: set ts=8 sts=2 sw=2 tw=99 et: |
|
dvander@72
|
2 |
# |
|
dvander@72
|
3 |
# This file is part of AMBuild. |
|
dvander@72
|
4 |
# |
|
dvander@72
|
5 |
# AMBuild is free software: you can redistribute it and/or modify |
|
dvander@72
|
6 |
# it under the terms of the GNU General Public License as published by |
|
dvander@72
|
7 |
# the Free Software Foundation, either version 3 of the License, or |
|
dvander@72
|
8 |
# (at your option) any later version. |
|
dvander@72
|
9 |
# |
|
dvander@72
|
10 |
# AMBuild is distributed in the hope that it will be useful, |
|
dvander@72
|
11 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
dvander@72
|
12 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
dvander@72
|
13 |
# GNU General Public License for more details. |
|
dvander@72
|
14 |
# |
|
dvander@72
|
15 |
# You should have received a copy of the GNU General Public License |
|
dvander@72
|
16 |
# along with AMBuild. If not, see <http://www.gnu.org/licenses/>. |
|
dvander@65
|
17 |
import os |
|
dvander@167
|
18 |
from ambuild2 import util |
|
dvander@167
|
19 |
from ambuild2 import nodetypes |
|
dvander@167
|
20 |
from ambuild2.frontend.cpp import DetectCompiler |
|
dvander@167
|
21 |
from ambuild2.frontend import base_gen |
|
dvander@167
|
22 |
from ambuild2 import database |
|
dvander@65
|
23 |
|
|
dvander@125
|
24 |
class CppNodes(object): |
|
dvander@125
|
25 |
def __init__(self, output, debug_outputs): |
|
dvander@125
|
26 |
self.binary = output |
|
dvander@125
|
27 |
self.debug = debug_outputs |
|
dvander@125
|
28 |
|
|
dvander@65
|
29 |
class Generator(base_gen.Generator): |
|
dvander@236
|
30 |
def __init__(self, sourcePath, buildPath, originalCwd, options, args, db=None, refactoring=False): |
|
dvander@236
|
31 |
super(Generator, self).__init__(sourcePath, buildPath, originalCwd, options, args) |
|
dvander@128
|
32 |
self.cacheFolder = os.path.join(self.buildPath, '.ambuild2') |
|
dvander@157
|
33 |
self.old_scripts_ = set() |
|
dvander@153
|
34 |
self.old_folders_ = set() |
|
dvander@159
|
35 |
self.old_commands_ = set() |
|
dvander@160
|
36 |
self.old_groups_ = set() |
|
danderson@175
|
37 |
self.rm_list_ = [] |
|
dvander@153
|
38 |
self.bad_outputs_ = set() |
|
dvander@158
|
39 |
self.db = db |
|
dvander@158
|
40 |
self.is_bootstrap = not self.db |
|
dvander@163
|
41 |
self.refactoring = refactoring |
|
dvander@65
|
42 |
|
|
dvander@236
|
43 |
@classmethod |
|
dvander@236
|
44 |
def FromVars(cls, vars, db, refactoring): |
|
dvander@236
|
45 |
# Backwards compatibility: for an automatic reconfigure on an older build, |
|
dvander@236
|
46 |
# just assume the source path is the cwd. If the AMBuildScript suddenly |
|
dvander@236
|
47 |
# has decided to depend on originalCwd, then the user may have to manually |
|
dvander@236
|
48 |
# run configure.py again, until we remove configure.py entirely. |
|
dvander@236
|
49 |
if 'originalCwd' in vars: |
|
dvander@236
|
50 |
originalCwd = vars['originalCwd'] |
|
dvander@236
|
51 |
else: |
|
dvander@236
|
52 |
originalCwd = vars['sourcePath'] |
|
dvander@236
|
53 |
|
|
dvander@236
|
54 |
gen = cls( |
|
dvander@236
|
55 |
sourcePath = vars['sourcePath'], |
|
dvander@236
|
56 |
buildPath = vars['buildPath'], |
|
dvander@236
|
57 |
originalCwd = originalCwd, |
|
dvander@236
|
58 |
options = vars['options'], |
|
dvander@236
|
59 |
args = vars['args'], |
|
dvander@236
|
60 |
db = db, |
|
dvander@236
|
61 |
refactoring = refactoring |
|
dvander@236
|
62 |
) |
|
dvander@236
|
63 |
return gen |
|
dvander@236
|
64 |
|
|
dvander@67
|
65 |
def preGenerate(self): |
|
dvander@153
|
66 |
if not os.path.isdir(self.cacheFolder): |
|
dvander@153
|
67 |
os.mkdir(self.cacheFolder) |
|
dvander@158
|
68 |
if not self.db: |
|
dvander@225
|
69 |
db_path = os.path.join(self.cacheFolder, 'graph') |
|
dvander@226
|
70 |
if not os.path.isfile(db_path): |
|
dvander@226
|
71 |
self.db = database.CreateDatabase(db_path) |
|
dvander@226
|
72 |
else: |
|
dvander@226
|
73 |
self.db = database.Database(db_path) |
|
dvander@226
|
74 |
self.db.connect() |
|
dvander@153
|
75 |
|
|
dvander@157
|
76 |
self.db.query_scripts(lambda id,path,stamp: self.old_scripts_.add(path)) |
|
dvander@153
|
77 |
self.db.query_mkdir(lambda entry: self.old_folders_.add(entry)) |
|
dvander@153
|
78 |
self.db.query_commands(lambda entry: self.old_commands_.add(entry)) |
|
dvander@160
|
79 |
self.db.query_groups(lambda entry:self.old_groups_.add(entry)) |
|
dvander@153
|
80 |
|
|
dvander@161
|
81 |
def cleanup(self): |
|
danderson@175
|
82 |
for path in self.rm_list_: |
|
danderson@175
|
83 |
util.rm_path(path) |
|
danderson@175
|
84 |
|
|
dvander@161
|
85 |
for cmd_entry in self.old_commands_: |
|
danderson@175
|
86 |
if self.refactoring: |
|
danderson@175
|
87 |
util.con_err(util.ConsoleRed, 'Command removed during refactoring: \n', |
|
danderson@175
|
88 |
util.ConsoleBlue, cmd_entry.format(), |
|
danderson@175
|
89 |
util.ConsoleNormal) |
|
danderson@175
|
90 |
raise Exception('Refactoring error: command removed') |
|
dvander@161
|
91 |
self.db.drop_command(cmd_entry) |
|
dvander@161
|
92 |
|
|
dvander@161
|
93 |
for path in self.old_scripts_: |
|
dvander@161
|
94 |
self.db.drop_script(path) |
|
dvander@161
|
95 |
|
|
dvander@161
|
96 |
for group in self.old_groups_: |
|
dvander@161
|
97 |
self.db.drop_group(group) |
|
dvander@161
|
98 |
|
|
dvander@235
|
99 |
self.db.query_dead_sources(lambda e: self.db.drop_source(e)) |
|
dvander@226
|
100 |
self.db.query_dead_shared_outputs(lambda e: self.db.drop_output(e)) |
|
dvander@226
|
101 |
|
|
dvander@161
|
102 |
class Node: |
|
dvander@161
|
103 |
def __init__(self): |
|
dvander@161
|
104 |
self.incoming = set() |
|
dvander@161
|
105 |
self.outgoing = set() |
|
dvander@161
|
106 |
|
|
dvander@161
|
107 |
# Build a tree of dead folders. |
|
dvander@161
|
108 |
tracker = {} |
|
dvander@161
|
109 |
for entry in self.old_folders_: |
|
dvander@161
|
110 |
if entry not in tracker: |
|
dvander@161
|
111 |
tracker[entry] = Node() |
|
dvander@161
|
112 |
|
|
dvander@161
|
113 |
if entry.folder is None: |
|
dvander@161
|
114 |
continue |
|
dvander@161
|
115 |
|
|
danderson@175
|
116 |
# If our parent is not a dead folder, don't create an edge. It should be |
|
danderson@175
|
117 |
# impossible for a/b to be dead, a/b/c to be alive, and a/b/c/d to be |
|
danderson@175
|
118 |
# dead, since a/b/c will implicitly keep a/b alive. |
|
danderson@175
|
119 |
if entry.folder not in self.old_folders_: |
|
danderson@175
|
120 |
continue |
|
danderson@175
|
121 |
|
|
dvander@161
|
122 |
if entry.folder not in tracker: |
|
dvander@161
|
123 |
tracker[entry.folder] = Node() |
|
dvander@161
|
124 |
|
|
dvander@161
|
125 |
parent = tracker[entry.folder] |
|
dvander@161
|
126 |
child = tracker[entry] |
|
dvander@161
|
127 |
parent.incoming.add(entry) |
|
dvander@161
|
128 |
child.outgoing.add(entry.folder) |
|
dvander@161
|
129 |
|
|
dvander@161
|
130 |
# Find the leaves. Sets start out >= 1 items. Remove them as they they |
|
dvander@161
|
131 |
# are empty. |
|
dvander@161
|
132 |
dead_folders = [entry for entry in self.old_folders_ if len(tracker[entry].incoming) == 0] |
|
dvander@161
|
133 |
while len(dead_folders): |
|
dvander@161
|
134 |
child_entry = dead_folders.pop() |
|
dvander@161
|
135 |
child_node = tracker[child_entry] |
|
dvander@161
|
136 |
|
|
danderson@175
|
137 |
if self.refactoring: |
|
danderson@175
|
138 |
util.con_err(util.ConsoleRed, 'Folder removed during refactoring: \n', |
|
danderson@175
|
139 |
util.ConsoleBlue, child_entry.format(), |
|
danderson@175
|
140 |
util.ConsoleNormal) |
|
danderson@175
|
141 |
raise Exception('Refactoring error: command removed') |
|
danderson@175
|
142 |
|
|
dvander@161
|
143 |
self.db.drop_folder(child_entry) |
|
dvander@161
|
144 |
for parent_entry in child_node.outgoing: |
|
dvander@161
|
145 |
parent_node = tracker[parent_entry] |
|
dvander@161
|
146 |
parent_node.incoming.remove(child_entry) |
|
dvander@161
|
147 |
if not len(parent_node.incoming): |
|
dvander@161
|
148 |
dead_folders.append(parent_entry) |
|
dvander@161
|
149 |
|
|
dvander@161
|
150 |
def postGenerate(self): |
|
dvander@161
|
151 |
self.cleanup() |
|
dvander@161
|
152 |
self.db.commit() |
|
dvander@235
|
153 |
self.db.vacuum() |
|
dvander@161
|
154 |
if self.is_bootstrap: |
|
dvander@161
|
155 |
self.saveVars() |
|
dvander@161
|
156 |
self.db.close() |
|
dvander@161
|
157 |
|
|
dvander@161
|
158 |
def saveVars(self): |
|
dvander@161
|
159 |
vars = { |
|
dvander@161
|
160 |
'sourcePath': self.sourcePath, |
|
dvander@161
|
161 |
'buildPath': self.buildPath, |
|
dvander@236
|
162 |
'originalCwd': self.originalCwd, |
|
dvander@161
|
163 |
'options': self.options, |
|
dvander@161
|
164 |
'args': self.args |
|
dvander@161
|
165 |
} |
|
dvander@161
|
166 |
with open(os.path.join(self.cacheFolder, 'vars'), 'wb') as fp: |
|
dvander@161
|
167 |
util.DiskPickle(vars, fp) |
|
dvander@161
|
168 |
|
|
dvander@161
|
169 |
def getLocalFolder(self, context): |
|
dvander@161
|
170 |
if type(context.localFolder_) is nodetypes.Entry or context.localFolder_ is None: |
|
dvander@161
|
171 |
return context.localFolder_ |
|
dvander@161
|
172 |
|
|
dvander@161
|
173 |
if len(context.buildFolder): |
|
dvander@161
|
174 |
context.localFolder_ = self.generateFolder(None, context.buildFolder) |
|
dvander@161
|
175 |
else: |
|
dvander@161
|
176 |
context.localFolder_ = None |
|
dvander@161
|
177 |
return context.localFolder_ |
|
dvander@161
|
178 |
|
|
dvander@161
|
179 |
def generateFolder(self, parent, folder): |
|
dvander@161
|
180 |
parent_path = '' |
|
dvander@161
|
181 |
if parent: |
|
dvander@161
|
182 |
parent_path = parent.path |
|
dvander@161
|
183 |
path = os.path.normpath(os.path.join(parent_path, folder)) |
|
dvander@161
|
184 |
|
|
dvander@161
|
185 |
if path.startswith('..'): |
|
dvander@161
|
186 |
util.con_err( |
|
dvander@161
|
187 |
util.ConsoleRed, |
|
dvander@161
|
188 |
'Output path ', |
|
dvander@161
|
189 |
util.ConsoleBlue, |
|
dvander@161
|
190 |
path, |
|
dvander@161
|
191 |
util.ConsoleRed, |
|
dvander@161
|
192 |
' is outside the build folder!', |
|
dvander@161
|
193 |
util.ConsoleNormal |
|
dvander@161
|
194 |
) |
|
dvander@153
|
195 |
raise Exception('Cannot generate folders outside the build folder') |
|
dvander@153
|
196 |
|
|
dvander@161
|
197 |
# Quick check. If this folder is not in our old folder list, and it's in |
|
danderson@175
|
198 |
# the DB, then we already have an entry for it that has already negotiated |
|
danderson@175
|
199 |
# its parent paths. |
|
dvander@161
|
200 |
old_entry = self.db.query_path(path) |
|
danderson@175
|
201 |
if old_entry and self.isValidFolderEntry(old_entry): |
|
danderson@175
|
202 |
return old_entry |
|
dvander@161
|
203 |
|
|
dvander@159
|
204 |
components = [] |
|
dvander@161
|
205 |
while folder: |
|
dvander@161
|
206 |
folder, name = os.path.split(folder) |
|
dvander@159
|
207 |
if not name: |
|
dvander@159
|
208 |
break |
|
dvander@159
|
209 |
components.append(name) |
|
dvander@159
|
210 |
|
|
dvander@161
|
211 |
path = parent_path |
|
dvander@159
|
212 |
while len(components): |
|
dvander@159
|
213 |
name = components.pop() |
|
dvander@159
|
214 |
path = os.path.join(path, name) |
|
dvander@159
|
215 |
entry = self.db.query_path(path) |
|
dvander@159
|
216 |
if not entry: |
|
dvander@164
|
217 |
if self.refactoring: |
|
dvander@164
|
218 |
util.con_err(util.ConsoleRed, 'New folder introduced: ', |
|
dvander@164
|
219 |
util.ConsoleBlue, path, |
|
dvander@164
|
220 |
util.ConsoleNormal) |
|
danderson@175
|
221 |
raise Exception('Refactoring error: new folder') |
|
dvander@161
|
222 |
entry = self.db.add_folder(parent, path) |
|
dvander@226
|
223 |
elif entry.type == nodetypes.Output or entry.type == nodetypes.SharedOutput: |
|
dvander@226
|
224 |
if entry.type == nodetypes.Output: |
|
dvander@226
|
225 |
cmd_entries = [self.db.query_command_of(entry)] |
|
dvander@226
|
226 |
elif entry.type == nodetypes.SharedOutput: |
|
dvander@226
|
227 |
cmd_entries = self.db.query_shared_commands_of(entry) |
|
dvander@226
|
228 |
|
|
dvander@226
|
229 |
for cmd_entry in cmd_entries: |
|
dvander@226
|
230 |
if cmd_entry not in self.old_commands_: |
|
dvander@226
|
231 |
util.con_err(util.ConsoleRed, 'Folder has the same path as an output file generated by:\n', |
|
dvander@226
|
232 |
util.ConsoleBlue, cmd_entry.format(), |
|
dvander@226
|
233 |
util.ConsoleNormal) |
|
dvander@226
|
234 |
raise Exception('Output has been duplicated: {0}'.format(entry.path)) |
|
danderson@175
|
235 |
|
|
danderson@175
|
236 |
if self.refactoring: |
|
danderson@175
|
237 |
util.con_err(util.ConsoleRed, 'Path "', |
|
danderson@175
|
238 |
util.ConsoleBlue, entry.path, |
|
danderson@175
|
239 |
util.ConsoleRed, '" has changed from a file to a folder.', |
|
danderson@175
|
240 |
util.ConsoleNormal) |
|
danderson@175
|
241 |
raise Exception('Refactoring error: path changed from file to folder') |
|
danderson@175
|
242 |
|
|
danderson@175
|
243 |
self.rm_list_.append(entry.path) |
|
dvander@226
|
244 |
self.db.change_to_folder(entry) |
|
danderson@175
|
245 |
elif entry.type == nodetypes.Mkdir: |
|
dvander@161
|
246 |
# We let the same folder be generated twice, so use discard, not remove. |
|
dvander@159
|
247 |
self.old_folders_.discard(entry) |
|
danderson@175
|
248 |
else: |
|
dvander@190
|
249 |
util.con_err(util.ConsoleRed, 'Folder has the same node signature as: ', |
|
dvander@190
|
250 |
util.ConsoleBlue, entry.format(), |
|
dvander@190
|
251 |
util.ConsoleNormal) |
|
danderson@175
|
252 |
raise Exception('Output has been duplicated: {0}'.format(entry.path)) |
|
dvander@161
|
253 |
|
|
dvander@159
|
254 |
parent = entry |
|
dvander@153
|
255 |
|
|
dvander@153
|
256 |
return entry |
|
dvander@153
|
257 |
|
|
dvander@162
|
258 |
def isValidFolderEntry(self, folder_entry): |
|
dvander@162
|
259 |
# If it's a mkdir and it's created, we're done. |
|
danderson@175
|
260 |
return folder_entry.type == nodetypes.Mkdir and folder_entry not in self.old_folders_ |
|
dvander@162
|
261 |
|
|
dvander@162
|
262 |
def validateOutputFolder(self, path): |
|
dvander@162
|
263 |
# Empty path is the root folder, which is null. |
|
dvander@162
|
264 |
if not len(path): |
|
dvander@162
|
265 |
return None |
|
dvander@162
|
266 |
|
|
dvander@162
|
267 |
# The folder must already exist. |
|
dvander@162
|
268 |
folder_entry = self.db.query_path(path) |
|
dvander@162
|
269 |
if not folder_entry: |
|
dvander@162
|
270 |
util.con_err(util.ConsoleRed, 'Path "', |
|
dvander@162
|
271 |
util.ConsoleBlue, path, |
|
dvander@162
|
272 |
util.ConsoleRed, '" specifies a folder that does not exist.', |
|
dvander@162
|
273 |
util.ConsoleNormal) |
|
dvander@162
|
274 |
raise Exception('path specifies a folder that does not exist') |
|
dvander@162
|
275 |
|
|
dvander@162
|
276 |
if self.isValidFolderEntry(folder_entry): |
|
dvander@162
|
277 |
return folder_entry |
|
dvander@162
|
278 |
|
|
dvander@162
|
279 |
# If it's a folder or an output, we can give a better error message. |
|
dvander@162
|
280 |
if folder_entry.type == nodetypes.Output or folder_entry.type == nodetypes.Mkdir: |
|
dvander@162
|
281 |
util.con_err(util.ConsoleRed, 'Folder "', |
|
dvander@162
|
282 |
util.ConsoleBlue, folder_entry.path, |
|
dvander@162
|
283 |
util.ConsoleRed, '" was never created.', |
|
dvander@162
|
284 |
util.ConsoleNormal) |
|
dvander@162
|
285 |
raise Exception('path {0} was never created', folder_entry.path) |
|
dvander@162
|
286 |
|
|
dvander@162
|
287 |
util.con_err(util.ConsoleRed, 'Attempted to use node "', |
|
dvander@162
|
288 |
util.ConsoleBlue, folder_entry.format(), |
|
dvander@162
|
289 |
util.ConsoleRed, '" as a path component.', |
|
dvander@162
|
290 |
util.ConsoleNormal) |
|
dvander@162
|
291 |
raise Exception('illegal path component') |
|
dvander@162
|
292 |
|
|
dvander@226
|
293 |
def parseOutput(self, cwd_entry, path, kind): |
|
dvander@162
|
294 |
if path[-1] == os.sep or path[-1] == os.altsep or path == '.' or path == '': |
|
dvander@162
|
295 |
util.con_err(util.ConsoleRed, 'Path "', |
|
dvander@162
|
296 |
util.ConsoleBlue, path, |
|
dvander@162
|
297 |
util.ConsoleRed, '" looks like a folder; a folder was not expected.', |
|
dvander@162
|
298 |
util.ConsoleNormal) |
|
dvander@162
|
299 |
raise Exception('Expected folder, but path has a trailing slash') |
|
dvander@162
|
300 |
|
|
dvander@177
|
301 |
path = os.path.normpath(path) |
|
dvander@177
|
302 |
|
|
dvander@162
|
303 |
path, name = os.path.split(path) |
|
dvander@162
|
304 |
path = nodetypes.combine(cwd_entry, path) |
|
dvander@162
|
305 |
|
|
dvander@162
|
306 |
# We should have caught a case like 'x/' earlier. |
|
dvander@162
|
307 |
assert len(name) |
|
dvander@162
|
308 |
|
|
dvander@177
|
309 |
# If we resolved that there is no prefix path, then take this to mean the |
|
dvander@177
|
310 |
# root folder. |
|
dvander@177
|
311 |
if path: |
|
dvander@177
|
312 |
folder_entry = self.validateOutputFolder(path) |
|
dvander@177
|
313 |
output_path = os.path.join(path, name) |
|
dvander@177
|
314 |
else: |
|
dvander@177
|
315 |
folder_entry = None |
|
dvander@177
|
316 |
output_path = name |
|
dvander@162
|
317 |
|
|
dvander@162
|
318 |
entry = self.db.query_path(output_path) |
|
dvander@162
|
319 |
if not entry: |
|
dvander@163
|
320 |
if self.refactoring: |
|
danderson@175
|
321 |
util.con_err(util.ConsoleRed, 'New output file introduced: ', |
|
danderson@175
|
322 |
util.ConsoleBlue, output_path, |
|
dvander@163
|
323 |
util.ConsoleNormal) |
|
dvander@163
|
324 |
raise Exception('Refactoring error') |
|
dvander@226
|
325 |
return self.db.add_output(folder_entry, output_path, kind) |
|
dvander@162
|
326 |
|
|
dvander@226
|
327 |
if entry.type == kind: |
|
dvander@162
|
328 |
return entry |
|
dvander@162
|
329 |
|
|
dvander@226
|
330 |
if entry.type == nodetypes.Mkdir: |
|
dvander@226
|
331 |
if entry not in self.old_folders_: |
|
dvander@226
|
332 |
util.con_err(util.ConsoleRed, 'A folder is being re-used as an output file: "', |
|
dvander@226
|
333 |
util.ConsoleBlue, entry.path, |
|
dvander@226
|
334 |
util.ConsoleRed, '"', |
|
dvander@226
|
335 |
util.ConsoleNormal) |
|
dvander@226
|
336 |
raise Exception('Attempted to re-use a folder as generated file') |
|
dvander@226
|
337 |
|
|
dvander@226
|
338 |
if self.refactoring: |
|
dvander@226
|
339 |
util.con_err(util.ConsoleRed, 'A generated folder has changed to a generated file: ', |
|
dvander@226
|
340 |
util.ConsoleBlue, entry.path, |
|
dvander@226
|
341 |
util.ConsoleNormal) |
|
dvander@226
|
342 |
raise Exception('Refactoring error') |
|
dvander@226
|
343 |
|
|
dvander@226
|
344 |
# We keep the node in old_folders_. This should be okay, since we've |
|
dvander@226
|
345 |
# changed the type to Output now. This way we can stick to one folder |
|
dvander@226
|
346 |
# deletion routine, since it's fairly complicated. |
|
dvander@226
|
347 |
elif entry.type == nodetypes.Output: |
|
dvander@226
|
348 |
# If we're asking for a shared output, make sure we can reuse this one. |
|
dvander@226
|
349 |
input_cmd = self.db.query_command_of(entry) |
|
dvander@226
|
350 |
if input_cmd and input_cmd not in self.old_commands_: |
|
dvander@226
|
351 |
util.con_err(util.ConsoleRed, 'First defined with command: ', input_cmd.format(), util.ConsoleNormal) |
|
dvander@226
|
352 |
raise Exception('Existing output cannot be a shared output: {0}'.format(entry.path)) |
|
dvander@226
|
353 |
|
|
dvander@226
|
354 |
if self.refactoring: |
|
dvander@226
|
355 |
util.con_err(util.ConsoleRed, 'An output has changed to a shared output: ', |
|
dvander@226
|
356 |
util.ConsoleBlue, entry.path, |
|
dvander@226
|
357 |
util.ConsoleNormal) |
|
dvander@226
|
358 |
raise Exception('Refactoring error') |
|
dvander@226
|
359 |
elif entry.type == nodetypes.SharedOutput: |
|
dvander@226
|
360 |
input_cmds = self.db.query_shared_commands_of(entry) |
|
dvander@226
|
361 |
for input_cmd in input_cmds: |
|
dvander@226
|
362 |
if input_cmd not in self.old_commands_: |
|
dvander@226
|
363 |
util.con_err(util.ConsoleRed, 'A shared output cannot be specified as an normal output.', |
|
dvander@226
|
364 |
util.ConsoleNormal) |
|
dvander@226
|
365 |
raise Exception('Existing shared output cannot be a normal output: {0}'.format(entry.path)) |
|
dvander@226
|
366 |
|
|
dvander@226
|
367 |
if self.refactoring: |
|
dvander@226
|
368 |
util.con_err(util.ConsoleRed, 'A shared output has changed to a normal output: ', |
|
dvander@226
|
369 |
util.ConsoleBlue, entry.path, |
|
dvander@226
|
370 |
util.ConsoleNormal) |
|
dvander@226
|
371 |
raise Exception('Refactoring error') |
|
dvander@226
|
372 |
else: |
|
danderson@175
|
373 |
util.con_err(util.ConsoleRed, 'An existing node has been specified as an output file: "', |
|
dvander@162
|
374 |
util.ConsoleBlue, entry.format(), |
|
dvander@162
|
375 |
util.ConsoleRed, '"', |
|
dvander@162
|
376 |
util.ConsoleNormal) |
|
dvander@162
|
377 |
raise Exception('Attempted to re-use an incompatible node as an output') |
|
dvander@162
|
378 |
|
|
dvander@226
|
379 |
self.db.change_to_output(entry, kind) |
|
dvander@162
|
380 |
return entry |
|
dvander@162
|
381 |
|
|
dvander@176
|
382 |
def parseInput(self, context, source): |
|
dvander@163
|
383 |
if util.IsString(source): |
|
dvander@176
|
384 |
if not os.path.isabs(source): |
|
dvander@176
|
385 |
source = os.path.join(context.currentSourcePath, source) |
|
dvander@177
|
386 |
source = os.path.normpath(source) |
|
dvander@176
|
387 |
|
|
dvander@162
|
388 |
entry = self.db.query_path(source) |
|
dvander@162
|
389 |
if not entry: |
|
dvander@162
|
390 |
return self.db.add_source(source) |
|
dvander@162
|
391 |
|
|
dvander@176
|
392 |
# Otherwise, we have to valid the node. |
|
dvander@162
|
393 |
source = entry |
|
dvander@162
|
394 |
|
|
dvander@162
|
395 |
if source.type == nodetypes.Source or source.type == nodetypes.Output: |
|
dvander@162
|
396 |
return source |
|
dvander@162
|
397 |
|
|
dvander@162
|
398 |
if source.type == nodetypes.Mkdir: |
|
dvander@162
|
399 |
if source not in self.bad_outputs_: |
|
dvander@162
|
400 |
util.con_err(util.ConsoleRed, 'Tried to use folder path ', |
|
dvander@162
|
401 |
util.ConsoleBlue, source.path, |
|
dvander@162
|
402 |
util.ConsoleRed, ' as a file path.', |
|
dvander@162
|
403 |
util.ConsoleNormal) |
|
dvander@162
|
404 |
raise Exception('Tried to use folder path as a file path') |
|
dvander@162
|
405 |
|
|
dvander@162
|
406 |
util.con_err(util.ConsoleRed, 'Tried to use incompatible node "', |
|
dvander@162
|
407 |
util.ConsoleBlue, source.format(), |
|
dvander@162
|
408 |
util.ConsoleRed, '" as a file path.', |
|
dvander@162
|
409 |
util.ConsoleNormal) |
|
dvander@162
|
410 |
raise Exception('Tried to use non-file node as a file path') |
|
dvander@162
|
411 |
|
|
dvander@226
|
412 |
def addCommand(self, context, node_type, folder, data, inputs, outputs, |
|
dvander@226
|
413 |
weak_inputs=[], shared_outputs=[]): |
|
dvander@167
|
414 |
assert not folder or isinstance(folder, nodetypes.Entry) |
|
dvander@153
|
415 |
|
|
dvander@153
|
416 |
# Build the set of weak links. |
|
dvander@153
|
417 |
weak_links = set() |
|
dvander@153
|
418 |
for weak_input in weak_inputs: |
|
dvander@153
|
419 |
assert type(weak_input) is nodetypes.Entry |
|
dvander@153
|
420 |
assert weak_input.type != nodetypes.Source |
|
dvander@153
|
421 |
weak_links.add(weak_input) |
|
dvander@153
|
422 |
|
|
dvander@153
|
423 |
# Build the set of strong links. |
|
dvander@153
|
424 |
strong_links = set() |
|
dvander@153
|
425 |
for strong_input in inputs: |
|
dvander@176
|
426 |
strong_input = self.parseInput(context, strong_input) |
|
dvander@153
|
427 |
strong_links.add(strong_input) |
|
dvander@153
|
428 |
|
|
dvander@226
|
429 |
# Build the list of outputs. |
|
dvander@153
|
430 |
cmd_entry = None |
|
dvander@153
|
431 |
output_nodes = [] |
|
dvander@153
|
432 |
for output in outputs: |
|
dvander@226
|
433 |
output_node = self.parseOutput(folder, output, nodetypes.Output) |
|
dvander@162
|
434 |
output_nodes.append(output_node) |
|
dvander@153
|
435 |
|
|
danderson@175
|
436 |
input_entry = self.db.query_command_of(output_node) |
|
danderson@175
|
437 |
if not input_entry: |
|
dvander@162
|
438 |
continue |
|
dvander@153
|
439 |
|
|
dvander@162
|
440 |
# Make sure this output won't be duplicated. |
|
dvander@162
|
441 |
if input_entry not in self.old_commands_: |
|
dvander@162
|
442 |
util.con_err(util.ConsoleRed, 'Command: ', input_entry.format(), util.ConsoleNormal) |
|
dvander@162
|
443 |
raise Exception('Output has been duplicated: {0}'.format(output_node.path)) |
|
dvander@153
|
444 |
|
|
dvander@162
|
445 |
if not cmd_entry: |
|
dvander@162
|
446 |
cmd_entry = input_entry |
|
dvander@153
|
447 |
# end for |
|
dvander@153
|
448 |
|
|
dvander@226
|
449 |
# Build the list of shared outputs. |
|
dvander@226
|
450 |
shared_output_nodes = [] |
|
dvander@226
|
451 |
for shared_output in shared_outputs: |
|
dvander@226
|
452 |
shared_output_node = self.parseOutput(folder, shared_output, nodetypes.SharedOutput) |
|
dvander@226
|
453 |
shared_output_nodes.append(shared_output_node) |
|
dvander@226
|
454 |
|
|
dvander@226
|
455 |
output_links = set(output_nodes) |
|
dvander@226
|
456 |
shared_links = set(shared_output_nodes) |
|
dvander@226
|
457 |
|
|
dvander@226
|
458 |
# There should be no duplicates in either output list. These error messages |
|
dvander@226
|
459 |
# could be better. |
|
dvander@226
|
460 |
if len(output_nodes) > len(output_links): |
|
dvander@226
|
461 |
util.con_err(util.ConsoleRed, 'The output list contains duplicate files.', |
|
dvander@226
|
462 |
util.ConsoleNormal) |
|
dvander@226
|
463 |
raise Exception('Shared output list contains duplicate files.') |
|
dvander@226
|
464 |
if len(shared_output_nodes) > len(shared_links): |
|
dvander@226
|
465 |
util.con_err(util.ConsoleRed, 'The output list contains duplicate files.', |
|
dvander@226
|
466 |
util.ConsoleNormal) |
|
dvander@226
|
467 |
raise Exception('Shared output list contains duplicate files.') |
|
dvander@226
|
468 |
|
|
dvander@226
|
469 |
# The intersection of output_links and shared_links should be the empty set. |
|
dvander@226
|
470 |
duplicates = output_links.intersection(shared_links) |
|
dvander@226
|
471 |
if len(duplicates): |
|
dvander@226
|
472 |
bad_entry = duplicates.pop() |
|
dvander@226
|
473 |
util.con_err(util.ConsoleRed, 'An output has been duplicated as a shared output: ', |
|
dvander@226
|
474 |
util.ConsoleBlue, bad_entry.path, |
|
dvander@226
|
475 |
util.ConsoleNormal) |
|
dvander@226
|
476 |
raise Exception('An output has been duplicated as a shared output.') |
|
dvander@153
|
477 |
|
|
dvander@153
|
478 |
if cmd_entry: |
|
dvander@153
|
479 |
# Update the entry in the database. |
|
dvander@163
|
480 |
self.db.update_command(cmd_entry, node_type, folder, data, self.refactoring) |
|
dvander@153
|
481 |
|
|
dvander@153
|
482 |
# Disconnect any outputs that are no longer connected to this output. |
|
dvander@226
|
483 |
# It's okay to use output_links since there should never be duplicate |
|
dvander@153
|
484 |
# outputs. |
|
dvander@153
|
485 |
for outgoing in self.db.query_strong_outgoing(cmd_entry): |
|
dvander@226
|
486 |
if outgoing not in output_links: |
|
dvander@153
|
487 |
self.db.drop_strong_edge(cmd_entry, outgoing) |
|
dvander@185
|
488 |
self.db.drop_output(outgoing) |
|
dvander@153
|
489 |
else: |
|
dvander@226
|
490 |
output_links.remove(outgoing) |
|
dvander@226
|
491 |
|
|
dvander@226
|
492 |
# Do the same for shared outputs. Since there is a many:1 relationship, |
|
dvander@226
|
493 |
# we can't drop shared outputs here. We save that for a cleanup step. |
|
dvander@226
|
494 |
for outgoing in self.db.query_shared_outputs(cmd_entry): |
|
dvander@226
|
495 |
if outgoing not in shared_links: |
|
dvander@226
|
496 |
self.db.drop_shared_output_edge(cmd_entry, outgoing) |
|
dvander@226
|
497 |
else: |
|
dvander@226
|
498 |
shared_links.remove(outgoing) |
|
dvander@153
|
499 |
|
|
dvander@153
|
500 |
# Remove us from the list of commands to delete. |
|
dvander@153
|
501 |
self.old_commands_.remove(cmd_entry) |
|
dvander@153
|
502 |
else: |
|
dvander@226
|
503 |
# Note that if there are no outputs, we will always add a new command, |
|
dvander@226
|
504 |
# and the old (identical) command will be deleted. |
|
dvander@153
|
505 |
cmd_entry = self.db.add_command(node_type, folder, data) |
|
dvander@153
|
506 |
|
|
dvander@226
|
507 |
# Local helper function to warn about refactoring problems. |
|
dvander@164
|
508 |
def refactoring_error(node): |
|
dvander@163
|
509 |
util.con_err(util.ConsoleRed, 'New input introduced: ', |
|
dvander@163
|
510 |
util.ConsoleBlue, node.path + '\n', |
|
dvander@163
|
511 |
util.ConsoleRed, 'Command: ', |
|
dvander@163
|
512 |
util.ConsoleBlue, cmd_entry.format(), |
|
dvander@163
|
513 |
util.ConsoleNormal) |
|
dvander@163
|
514 |
raise Exception('Refactoring error: new input introduced') |
|
dvander@163
|
515 |
|
|
dvander@226
|
516 |
if len(output_links) and self.refactoring: |
|
dvander@226
|
517 |
refactoring_error(output_links.pop()) |
|
dvander@226
|
518 |
if len(shared_links) and self.refactoring: |
|
dvander@226
|
519 |
refactoring_error(shared_links.pop()) |
|
dvander@164
|
520 |
|
|
dvander@153
|
521 |
# Connect each output. |
|
dvander@226
|
522 |
for output_node in output_links: |
|
dvander@153
|
523 |
self.db.add_strong_edge(cmd_entry, output_node) |
|
dvander@226
|
524 |
for shared_output_node in shared_links: |
|
dvander@226
|
525 |
self.db.add_shared_output_edge(cmd_entry, shared_output_node) |
|
dvander@153
|
526 |
|
|
dvander@153
|
527 |
# Connect/disconnect strong inputs. |
|
dvander@153
|
528 |
strong_inputs = self.db.query_strong_inputs(cmd_entry) |
|
dvander@153
|
529 |
strong_added = strong_links - strong_inputs |
|
dvander@153
|
530 |
strong_removed = strong_inputs - strong_links |
|
dvander@163
|
531 |
|
|
dvander@164
|
532 |
if len(strong_added) and self.refactoring: |
|
dvander@164
|
533 |
refactoring_error(strong_added.pop()) |
|
dvander@164
|
534 |
|
|
dvander@153
|
535 |
for strong_input in strong_added: |
|
dvander@153
|
536 |
self.db.add_strong_edge(strong_input, cmd_entry) |
|
dvander@153
|
537 |
for strong_input in strong_removed: |
|
dvander@153
|
538 |
self.db.drop_strong_edge(strong_input, cmd_entry) |
|
dvander@153
|
539 |
|
|
dvander@153
|
540 |
# Connect/disconnect weak inputs. |
|
dvander@153
|
541 |
weak_inputs = self.db.query_weak_inputs(cmd_entry) |
|
dvander@153
|
542 |
weak_added = weak_links - weak_inputs |
|
dvander@153
|
543 |
weak_removed = weak_inputs - weak_links |
|
dvander@163
|
544 |
|
|
dvander@164
|
545 |
if len(weak_added) and self.refactoring: |
|
dvander@164
|
546 |
refactoring_error(weak_added.pop()) |
|
dvander@164
|
547 |
|
|
dvander@153
|
548 |
for weak_input in weak_added: |
|
dvander@153
|
549 |
self.db.add_weak_edge(weak_input, cmd_entry) |
|
dvander@153
|
550 |
for weak_input in weak_removed: |
|
dvander@153
|
551 |
self.db.drop_weak_edge(weak_input, cmd_entry) |
|
dvander@153
|
552 |
|
|
dvander@153
|
553 |
# If we got new outputs or inputs, we need to re-run the command. |
|
dvander@226
|
554 |
changed = len(output_links) + len(strong_added) + len(weak_added) |
|
dvander@153
|
555 |
if changed and not cmd_entry.dirty: |
|
dvander@153
|
556 |
self.db.mark_dirty(cmd_entry) |
|
dvander@153
|
557 |
|
|
dvander@153
|
558 |
return cmd_entry, output_nodes |
|
dvander@65
|
559 |
|
|
dvander@163
|
560 |
def parseCxxDeps(self, context, binary, inputs, items): |
|
dvander@163
|
561 |
for val in items: |
|
dvander@163
|
562 |
if util.IsString(val): |
|
dvander@163
|
563 |
continue |
|
dvander@163
|
564 |
|
|
dvander@210
|
565 |
if type(val) is nodetypes.Entry: |
|
dvander@210
|
566 |
item = val |
|
dvander@210
|
567 |
elif util.IsLambda(val.node): |
|
dvander@163
|
568 |
item = val.node(context, binary) |
|
dvander@186
|
569 |
elif val.node is None: |
|
dvander@186
|
570 |
item = val.text |
|
dvander@163
|
571 |
else: |
|
dvander@163
|
572 |
item = val.node |
|
dvander@163
|
573 |
|
|
dvander@163
|
574 |
if type(item) is list: |
|
dvander@163
|
575 |
inputs.extend(item) |
|
dvander@163
|
576 |
else: |
|
dvander@163
|
577 |
inputs.append(item) |
|
dvander@163
|
578 |
|
|
dvander@67
|
579 |
def addCxxTasks(self, cx, binary): |
|
dvander@161
|
580 |
folder_node = self.generateFolder(cx.localFolder, binary.localFolder) |
|
dvander@67
|
581 |
|
|
dvander@163
|
582 |
# Find dependencies |
|
dvander@153
|
583 |
inputs = [] |
|
dvander@163
|
584 |
self.parseCxxDeps(cx, binary, inputs, binary.compiler.linkflags) |
|
dvander@163
|
585 |
self.parseCxxDeps(cx, binary, inputs, binary.compiler.postlink) |
|
dvander@114
|
586 |
|
|
dvander@190
|
587 |
for objfile in binary.objects: |
|
dvander@67
|
588 |
cxxData = { |
|
dvander@67
|
589 |
'argv': objfile.argv, |
|
dvander@67
|
590 |
'type': binary.linker.behavior |
|
dvander@67
|
591 |
} |
|
dvander@153
|
592 |
cxxCmd, (cxxNode,) = self.addCommand( |
|
dvander@162
|
593 |
context = cx, |
|
dvander@162
|
594 |
weak_inputs = binary.compiler.sourcedeps, |
|
dvander@162
|
595 |
inputs = [objfile.sourceFile], |
|
dvander@162
|
596 |
outputs = [objfile.outputFile], |
|
dvander@162
|
597 |
node_type = nodetypes.Cxx, |
|
dvander@162
|
598 |
folder = folder_node, |
|
dvander@227
|
599 |
data = cxxData, |
|
dvander@227
|
600 |
shared_outputs = objfile.sharedOutputs |
|
dvander@120
|
601 |
) |
|
dvander@153
|
602 |
inputs.append(cxxNode) |
|
dvander@190
|
603 |
for rcfile in binary.resources: |
|
dvander@190
|
604 |
rcData = { |
|
dvander@190
|
605 |
'cl_argv': rcfile.cl_argv, |
|
dvander@190
|
606 |
'rc_argv': rcfile.rc_argv, |
|
dvander@190
|
607 |
} |
|
dvander@201
|
608 |
rcCmd, (preprocNode, rcNode) = self.addCommand( |
|
dvander@190
|
609 |
context = cx, |
|
dvander@190
|
610 |
weak_inputs = binary.compiler.sourcedeps, |
|
dvander@190
|
611 |
inputs = [rcfile.sourceFile], |
|
dvander@199
|
612 |
outputs = [rcfile.preprocFile, rcfile.outputFile], |
|
dvander@190
|
613 |
node_type = nodetypes.Rc, |
|
dvander@190
|
614 |
folder = folder_node, |
|
dvander@190
|
615 |
data = rcData |
|
dvander@190
|
616 |
) |
|
dvander@190
|
617 |
inputs.append(rcNode) |
|
dvander@67
|
618 |
|
|
danderson@220
|
619 |
output_file, debug_file = binary.link( |
|
dvander@162
|
620 |
context = cx, |
|
dvander@162
|
621 |
folder = folder_node, |
|
danderson@220
|
622 |
inputs = inputs |
|
dvander@153
|
623 |
) |
|
dvander@153
|
624 |
|
|
danderson@220
|
625 |
return CppNodes(output_file, debug_file) |
|
dvander@118
|
626 |
|
|
dvander@162
|
627 |
def addFileOp(self, cmd, context, source, output_path): |
|
dvander@162
|
628 |
# Try to detect if our output_path is actually a folder, via trailing |
|
dvander@162
|
629 |
# slash or '.'/'' indicating the context folder. |
|
dvander@162
|
630 |
detected_folder = None |
|
dvander@163
|
631 |
if util.IsString(output_path): |
|
dvander@162
|
632 |
if output_path[-1] == os.sep or output_path[-1] == os.altsep: |
|
dvander@162
|
633 |
detected_folder = os.path.join(context.buildFolder, os.path.normpath(output_path)) |
|
dvander@162
|
634 |
elif output_path == '.' or output_path == '': |
|
dvander@168
|
635 |
detected_folder = context.buildFolder |
|
dvander@168
|
636 |
|
|
dvander@168
|
637 |
# Since we're building something relative to the context folder, ensure |
|
dvander@168
|
638 |
# that the context folder exists. |
|
dvander@168
|
639 |
self.getLocalFolder(context) |
|
dvander@162
|
640 |
else: |
|
dvander@162
|
641 |
assert output_path.type != nodetypes.Source |
|
dvander@163
|
642 |
local_path = os.path.relpath(output_path.path, context.buildFolder) |
|
dvander@163
|
643 |
detected_folder = os.path.join(context.buildFolder, local_path) |
|
dvander@163
|
644 |
detected_folder = os.path.normpath(detected_folder) |
|
dvander@162
|
645 |
|
|
dvander@176
|
646 |
source_entry = self.parseInput(context, source) |
|
dvander@159
|
647 |
|
|
dvander@162
|
648 |
# This is similar to a "cp a b/", so we append to get "b/a" as the path. |
|
dvander@162
|
649 |
if detected_folder is not None: |
|
dvander@168
|
650 |
base, output_path = os.path.split(source_entry.path) |
|
dvander@168
|
651 |
assert len(output_path) |
|
dvander@168
|
652 |
|
|
dvander@168
|
653 |
output_folder = detected_folder |
|
dvander@168
|
654 |
else: |
|
dvander@192
|
655 |
output_folder = context.buildFolder |
|
dvander@168
|
656 |
|
|
dvander@176
|
657 |
output_path = nodetypes.combine(output_folder, output_path) |
|
dvander@159
|
658 |
|
|
dvander@168
|
659 |
# For copy operations, it's okay to use the path from the current folder. |
|
dvander@193
|
660 |
# However, when performing symlinks, we always want an absolute path. |
|
dvander@168
|
661 |
if cmd == nodetypes.Symlink: |
|
dvander@193
|
662 |
if source_entry.type == nodetypes.Source: |
|
dvander@193
|
663 |
source_path = source_entry.path |
|
dvander@193
|
664 |
else: |
|
dvander@193
|
665 |
source_path = os.path.join(context.buildPath, source_entry.path) |
|
dvander@163
|
666 |
else: |
|
dvander@168
|
667 |
source_path = source_entry.path |
|
dvander@168
|
668 |
|
|
dvander@162
|
669 |
# For clarity of spew, we always execute file operations in the root of |
|
dvander@162
|
670 |
# the build folder. This means that no matter what context we're in, |
|
dvander@162
|
671 |
# we can use absolute-ish folders and get away with it. |
|
dvander@162
|
672 |
return self.addCommand( |
|
dvander@162
|
673 |
context = context, |
|
dvander@162
|
674 |
node_type = cmd, |
|
dvander@162
|
675 |
folder = None, |
|
dvander@168
|
676 |
data = (source_path, output_path), |
|
dvander@162
|
677 |
inputs = [source_entry], |
|
dvander@162
|
678 |
outputs = [output_path] |
|
dvander@162
|
679 |
) |
|
dvander@153
|
680 |
|
|
dvander@157
|
681 |
def addSource(self, context, source_path): |
|
dvander@153
|
682 |
return self.graph.addSource(source_path) |
|
dvander@153
|
683 |
|
|
dvander@161
|
684 |
def addCopy(self, context, source, output_path): |
|
dvander@161
|
685 |
return self.addFileOp(nodetypes.Copy, context, source, output_path) |
|
dvander@161
|
686 |
|
|
dvander@157
|
687 |
def addSymlink(self, context, source, output_path): |
|
dvander@153
|
688 |
if util.IsWindows(): |
|
dvander@153
|
689 |
# Windows pre-Vista does not support symlinks. Windows Vista+ supports |
|
dvander@153
|
690 |
# symlinks via mklink, but it's Administrator-only by default. |
|
dvander@161
|
691 |
return self.addFileOp(nodetypes.Copy, context, source, output_path) |
|
dvander@161
|
692 |
return self.addFileOp(nodetypes.Symlink, context, source, output_path) |
|
dvander@153
|
693 |
|
|
dvander@157
|
694 |
def addFolder(self, context, folder): |
|
dvander@161
|
695 |
return self.generateFolder(context.localFolder, folder) |
|
dvander@153
|
696 |
|
|
danderson@219
|
697 |
def addShellCommand(self, |
|
danderson@219
|
698 |
context, |
|
danderson@219
|
699 |
inputs, |
|
danderson@219
|
700 |
argv, |
|
danderson@219
|
701 |
outputs, |
|
danderson@219
|
702 |
folder=-1, |
|
danderson@219
|
703 |
dep_type=None, |
|
dvander@226
|
704 |
weak_inputs=[], |
|
dvander@226
|
705 |
shared_outputs=[]): |
|
dvander@163
|
706 |
if folder is -1: |
|
dvander@163
|
707 |
folder = context.localFolder |
|
dvander@153
|
708 |
|
|
danderson@218
|
709 |
if dep_type is None: |
|
danderson@218
|
710 |
node_type = nodetypes.Command |
|
danderson@218
|
711 |
data = argv |
|
danderson@218
|
712 |
else: |
|
danderson@218
|
713 |
node_type = nodetypes.Cxx |
|
danderson@218
|
714 |
if dep_type not in ['gcc', 'msvc', 'sun']: |
|
danderson@218
|
715 |
util.con_err(util.ConsoleRed, 'Invalid dependency spew type: ', |
|
danderson@218
|
716 |
util.ConsoleBlue, dep_type, |
|
danderson@218
|
717 |
util.ConsoleNormal) |
|
danderson@218
|
718 |
raise Exception('Invalid dependency spew type') |
|
danderson@218
|
719 |
data = { |
|
danderson@218
|
720 |
'type': dep_type, |
|
danderson@218
|
721 |
'argv': argv, |
|
danderson@218
|
722 |
} |
|
danderson@218
|
723 |
|
|
dvander@160
|
724 |
return self.addCommand( |
|
dvander@163
|
725 |
context = context, |
|
danderson@218
|
726 |
node_type = node_type, |
|
dvander@163
|
727 |
folder = folder, |
|
danderson@218
|
728 |
data = data, |
|
dvander@163
|
729 |
inputs = inputs, |
|
danderson@219
|
730 |
outputs = outputs, |
|
dvander@226
|
731 |
weak_inputs = weak_inputs, |
|
dvander@226
|
732 |
shared_outputs = shared_outputs |
|
dvander@160
|
733 |
) |
|
dvander@157
|
734 |
|
|
dvander@157
|
735 |
def addConfigureFile(self, context, path): |
|
dvander@235
|
736 |
if not os.path.isabs(path): |
|
dvander@235
|
737 |
path = os.path.join(context.currentSourcePath, path) |
|
dvander@235
|
738 |
path = os.path.normpath(path) |
|
dvander@160
|
739 |
|
|
dvander@235
|
740 |
self.old_scripts_.discard(path) |
|
dvander@235
|
741 |
self.db.add_or_update_script(path) |
|
dvander@194
|
742 |
|