OLD | NEW |
1 # Copyright (c) 2014 Google Inc. All rights reserved. | 1 # Copyright (c) 2014 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of | 6 This script is intended for use as a GYP_GENERATOR. It takes as input (by way of |
7 the generator flag config_path) the path of a json file that dictates the files | 7 the generator flag config_path) the path of a json file that dictates the files |
8 and targets to search for. The following keys are supported: | 8 and targets to search for. The following keys are supported: |
9 files: list of paths (relative) of the files to search for. | 9 files: list of paths (relative) of the files to search for. |
10 targets: list of targets to search for. The target names are unqualified. | 10 targets: list of targets to search for. The target names are unqualified. |
11 | 11 |
12 The following is output: | 12 The following is output: |
13 error: only supplied if there is an error. | 13 error: only supplied if there is an error. |
14 warning: only supplied if there is a warning. | 14 warning: only supplied if there is a warning. |
15 targets: the set of targets passed in via targets that either directly or | 15 targets: the set of targets passed in via targets that either directly or |
16 indirectly depend upon the set of paths supplied in files. | 16 indirectly depend upon the set of paths supplied in files. |
17 status: indicates if any of the supplied files matched at least one target. | 17 build_targets: minimal set of targets that directly depend on the changed |
| 18 files and need to be built. The expectation is this set of targets is passed |
| 19 into a build step. |
| 20 status: outputs one of three values: none of the supplied files were found, |
| 21 one of the include files changed so that it should be assumed everything |
| 22 changed (in this case targets and build_targets are not output) or at |
| 23 least one file was found. |
18 | 24 |
19 If the generator flag analyzer_output_path is specified, output is written | 25 If the generator flag analyzer_output_path is specified, output is written |
20 there. Otherwise output is written to stdout. | 26 there. Otherwise output is written to stdout. |
21 """ | 27 """ |
22 | 28 |
23 import gyp.common | 29 import gyp.common |
24 import gyp.ninja_syntax as ninja_syntax | 30 import gyp.ninja_syntax as ninja_syntax |
25 import json | 31 import json |
26 import os | 32 import os |
27 import posixpath | 33 import posixpath |
28 import sys | 34 import sys |
29 | 35 |
30 debug = False | 36 debug = False |
31 | 37 |
32 found_dependency_string = 'Found dependency' | 38 found_dependency_string = 'Found dependency' |
33 no_dependency_string = 'No dependencies' | 39 no_dependency_string = 'No dependencies' |
| 40 # Status when it should be assumed that everything has changed. |
| 41 all_changed_string = 'Found dependency (all)' |
34 | 42 |
35 # MatchStatus is used indicate if and how a target depends upon the supplied | 43 # MatchStatus is used indicate if and how a target depends upon the supplied |
36 # sources. | 44 # sources. |
37 # The target's sources contain one of the supplied paths. | 45 # The target's sources contain one of the supplied paths. |
38 MATCH_STATUS_MATCHES = 1 | 46 MATCH_STATUS_MATCHES = 1 |
39 # The target has a dependency on another target that contains one of the | 47 # The target has a dependency on another target that contains one of the |
40 # supplied paths. | 48 # supplied paths. |
41 MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2 | 49 MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2 |
42 # The target's sources weren't in the supplied paths and none of the target's | 50 # The target's sources weren't in the supplied paths and none of the target's |
43 # dependencies depend upon a target that matched. | 51 # dependencies depend upon a target that matched. |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 if len(base_path): | 144 if len(base_path): |
137 base_path += '/' | 145 base_path += '/' |
138 | 146 |
139 if debug: | 147 if debug: |
140 print 'ExtractSources', target, base_path | 148 print 'ExtractSources', target, base_path |
141 | 149 |
142 results = [] | 150 results = [] |
143 if 'sources' in target_dict: | 151 if 'sources' in target_dict: |
144 _AddSources(target_dict['sources'], base_path, base_path_components, | 152 _AddSources(target_dict['sources'], base_path, base_path_components, |
145 results) | 153 results) |
146 # Include the inputs from any actions. Any changes to these effect the | 154 # Include the inputs from any actions. Any changes to these affect the |
147 # resulting output. | 155 # resulting output. |
148 if 'actions' in target_dict: | 156 if 'actions' in target_dict: |
149 for action in target_dict['actions']: | 157 for action in target_dict['actions']: |
150 _ExtractSourcesFromAction(action, base_path, base_path_components, | 158 _ExtractSourcesFromAction(action, base_path, base_path_components, |
151 results) | 159 results) |
152 if 'rules' in target_dict: | 160 if 'rules' in target_dict: |
153 for rule in target_dict['rules']: | 161 for rule in target_dict['rules']: |
154 _ExtractSourcesFromAction(rule, base_path, base_path_components, results) | 162 _ExtractSourcesFromAction(rule, base_path, base_path_components, results) |
155 | 163 |
156 return results | 164 return results |
157 | 165 |
158 | 166 |
159 class Target(object): | 167 class Target(object): |
160 """Holds information about a particular target: | 168 """Holds information about a particular target: |
161 deps: set of the names of direct dependent targets. | 169 deps: set of Targets this Target depends upon. This is not recursive, only the |
162 match_staus: one of the MatchStatus values""" | 170 direct dependent Targets. |
163 def __init__(self): | 171 match_status: one of the MatchStatus values. |
| 172 back_deps: set of Targets that have a dependency on this Target. |
| 173 visited: used during iteration to indicate whether we've visited this target. |
| 174 This is used for two iterations, once in building the set of Targets and |
| 175 again in _GetBuildTargets(). |
| 176 name: fully qualified name of the target. |
| 177 requires_build: True if the target type is such that it needs to be built. |
| 178 See _DoesTargetTypeRequireBuild for details. |
| 179 added_to_compile_targets: used when determining if the target was added to the |
| 180 set of targets that needs to be built. |
| 181 in_roots: true if this target is a descendant of one of the root nodes.""" |
| 182 def __init__(self, name): |
164 self.deps = set() | 183 self.deps = set() |
165 self.match_status = MATCH_STATUS_TBD | 184 self.match_status = MATCH_STATUS_TBD |
| 185 self.back_deps = set() |
| 186 self.name = name |
| 187 # TODO(sky): I don't like hanging this off Target. This state is specific |
| 188 # to certain functions and should be isolated there. |
| 189 self.visited = False |
| 190 self.requires_build = False |
| 191 self.added_to_compile_targets = False |
| 192 self.in_roots = False |
166 | 193 |
167 | 194 |
168 class Config(object): | 195 class Config(object): |
169 """Details what we're looking for | 196 """Details what we're looking for |
170 files: set of files to search for | 197 files: set of files to search for |
171 targets: see file description for details""" | 198 targets: see file description for details.""" |
172 def __init__(self): | 199 def __init__(self): |
173 self.files = [] | 200 self.files = [] |
174 self.targets = [] | 201 self.targets = set() |
175 | 202 |
176 def Init(self, params): | 203 def Init(self, params): |
177 """Initializes Config. This is a separate method as it raises an exception | 204 """Initializes Config. This is a separate method as it raises an exception |
178 if there is a parse error.""" | 205 if there is a parse error.""" |
179 generator_flags = params.get('generator_flags', {}) | 206 generator_flags = params.get('generator_flags', {}) |
180 config_path = generator_flags.get('config_path', None) | 207 config_path = generator_flags.get('config_path', None) |
181 if not config_path: | 208 if not config_path: |
182 return | 209 return |
183 try: | 210 try: |
184 f = open(config_path, 'r') | 211 f = open(config_path, 'r') |
185 config = json.load(f) | 212 config = json.load(f) |
186 f.close() | 213 f.close() |
187 except IOError: | 214 except IOError: |
188 raise Exception('Unable to open file ' + config_path) | 215 raise Exception('Unable to open file ' + config_path) |
189 except ValueError as e: | 216 except ValueError as e: |
190 raise Exception('Unable to parse config file ' + config_path + str(e)) | 217 raise Exception('Unable to parse config file ' + config_path + str(e)) |
191 if not isinstance(config, dict): | 218 if not isinstance(config, dict): |
192 raise Exception('config_path must be a JSON file containing a dictionary') | 219 raise Exception('config_path must be a JSON file containing a dictionary') |
193 self.files = config.get('files', []) | 220 self.files = config.get('files', []) |
194 # Coalesce duplicates | 221 self.targets = set(config.get('targets', [])) |
195 self.targets = list(set(config.get('targets', []))) | |
196 | 222 |
197 | 223 |
198 def _WasBuildFileModified(build_file, data, files): | 224 def _WasBuildFileModified(build_file, data, files): |
199 """Returns true if the build file |build_file| is either in |files| or | 225 """Returns true if the build file |build_file| is either in |files| or |
200 one of the files included by |build_file| is in |files|.""" | 226 one of the files included by |build_file| is in |files|.""" |
201 if _ToGypPath(build_file) in files: | 227 if _ToGypPath(build_file) in files: |
202 if debug: | 228 if debug: |
203 print 'gyp file modified', build_file | 229 print 'gyp file modified', build_file |
204 return True | 230 return True |
205 | 231 |
206 # First element of included_files is the file itself. | 232 # First element of included_files is the file itself. |
207 if len(data[build_file]['included_files']) <= 1: | 233 if len(data[build_file]['included_files']) <= 1: |
208 return False | 234 return False |
209 | 235 |
210 for include_file in data[build_file]['included_files'][1:]: | 236 for include_file in data[build_file]['included_files'][1:]: |
211 # |included_files| are relative to the directory of the |build_file|. | 237 # |included_files| are relative to the directory of the |build_file|. |
212 rel_include_file = \ | 238 rel_include_file = \ |
213 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file)) | 239 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file)) |
214 if rel_include_file in files: | 240 if rel_include_file in files: |
215 if debug: | 241 if debug: |
216 print 'included gyp file modified, gyp_file=', build_file, \ | 242 print 'included gyp file modified, gyp_file=', build_file, \ |
217 'included file=', rel_include_file | 243 'included file=', rel_include_file |
218 return True | 244 return True |
219 return False | 245 return False |
220 | 246 |
221 | 247 |
222 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files): | 248 def _GetOrCreateTargetByName(targets, target_name): |
223 """Generates a dictionary with the key the name of a target and the value a | 249 """Creates or returns the Target at targets[target_name]. If there is no |
224 Target. |toplevel_dir| is the root of the source tree. If the sources of | 250 Target for |target_name| one is created. Returns a tuple of whether a new |
225 a target match that of |files|, then |target.matched| is set to True. | 251 Target was created and the Target.""" |
226 This returns a tuple of the dictionary and whether at least one target's | 252 if target_name in targets: |
227 sources listed one of the paths in |files|.""" | 253 return False, targets[target_name] |
| 254 target = Target(target_name) |
| 255 targets[target_name] = target |
| 256 return True, target |
| 257 |
| 258 |
| 259 def _DoesTargetTypeRequireBuild(target_dict): |
| 260 """Returns true if the target type is such that it needs to be built.""" |
| 261 # If a 'none' target has rules or actions we assume it requires a build. |
| 262 return target_dict['type'] != 'none' or \ |
| 263 target_dict.get('actions') or target_dict.get('rules') |
| 264 |
| 265 |
| 266 def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, |
| 267 build_files): |
| 268 """Returns a tuple of the following: |
| 269 . A dictionary mapping from fully qualified name to Target. |
| 270 . A list of the targets that have a source file in |files|. |
| 271 . Set of root Targets reachable from the the files |build_files|. |
| 272 This sets the |match_status| of the targets that contain any of the source |
| 273 files in |files| to MATCH_STATUS_MATCHES. |
| 274 |toplevel_dir| is the root of the source tree.""" |
| 275 # Maps from target name to Target. |
228 targets = {} | 276 targets = {} |
229 | 277 |
| 278 # Targets that matched. |
| 279 matching_targets = [] |
| 280 |
230 # Queue of targets to visit. | 281 # Queue of targets to visit. |
231 targets_to_visit = target_list[:] | 282 targets_to_visit = target_list[:] |
232 | 283 |
233 matched = False | |
234 | |
235 # Maps from build file to a boolean indicating whether the build file is in | 284 # Maps from build file to a boolean indicating whether the build file is in |
236 # |files|. | 285 # |files|. |
237 build_file_in_files = {} | 286 build_file_in_files = {} |
238 | 287 |
| 288 # Root targets across all files. |
| 289 roots = set() |
| 290 |
| 291 # Set of Targets in |build_files|. |
| 292 build_file_targets = set() |
| 293 |
239 while len(targets_to_visit) > 0: | 294 while len(targets_to_visit) > 0: |
240 target_name = targets_to_visit.pop() | 295 target_name = targets_to_visit.pop() |
241 if target_name in targets: | 296 created_target, target = _GetOrCreateTargetByName(targets, target_name) |
| 297 if created_target: |
| 298 roots.add(target) |
| 299 elif target.visited: |
242 continue | 300 continue |
243 | 301 |
244 target = Target() | 302 target.visited = True |
245 targets[target_name] = target | 303 target.requires_build = _DoesTargetTypeRequireBuild( |
| 304 target_dicts[target_name]) |
246 | 305 |
247 build_file = gyp.common.ParseQualifiedTarget(target_name)[0] | 306 build_file = gyp.common.ParseQualifiedTarget(target_name)[0] |
248 if not build_file in build_file_in_files: | 307 if not build_file in build_file_in_files: |
249 build_file_in_files[build_file] = \ | 308 build_file_in_files[build_file] = \ |
250 _WasBuildFileModified(build_file, data, files) | 309 _WasBuildFileModified(build_file, data, files) |
251 | 310 |
| 311 if build_file in build_files: |
| 312 build_file_targets.add(target) |
| 313 |
252 # If a build file (or any of its included files) is modified we assume all | 314 # If a build file (or any of its included files) is modified we assume all |
253 # targets in the file are modified. | 315 # targets in the file are modified. |
254 if build_file_in_files[build_file]: | 316 if build_file_in_files[build_file]: |
| 317 print 'matching target from modified build file', target_name |
255 target.match_status = MATCH_STATUS_MATCHES | 318 target.match_status = MATCH_STATUS_MATCHES |
256 matched = True | 319 matching_targets.append(target) |
257 else: | 320 else: |
258 sources = _ExtractSources(target_name, target_dicts[target_name], | 321 sources = _ExtractSources(target_name, target_dicts[target_name], |
259 toplevel_dir) | 322 toplevel_dir) |
260 for source in sources: | 323 for source in sources: |
261 if source in files: | 324 if source in files: |
| 325 print 'target', target_name, 'matches', source |
262 target.match_status = MATCH_STATUS_MATCHES | 326 target.match_status = MATCH_STATUS_MATCHES |
263 matched = True | 327 matching_targets.append(target) |
264 break | 328 break |
265 | 329 |
| 330 # Add dependencies to visit as well as updating back pointers for deps. |
266 for dep in target_dicts[target_name].get('dependencies', []): | 331 for dep in target_dicts[target_name].get('dependencies', []): |
267 targets[target_name].deps.add(dep) | |
268 targets_to_visit.append(dep) | 332 targets_to_visit.append(dep) |
269 | 333 |
270 return targets, matched | 334 created_dep_target, dep_target = _GetOrCreateTargetByName(targets, dep) |
| 335 if not created_dep_target: |
| 336 roots.discard(dep_target) |
| 337 |
| 338 target.deps.add(dep_target) |
| 339 dep_target.back_deps.add(target) |
| 340 |
| 341 return targets, matching_targets, roots & build_file_targets |
271 | 342 |
272 | 343 |
273 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): | 344 def _GetUnqualifiedToTargetMapping(all_targets, to_find): |
274 """Returns a mapping (dictionary) from unqualified name to qualified name for | 345 """Returns a mapping (dictionary) from unqualified name to Target for all the |
275 all the targets in |to_find|.""" | 346 Targets in |to_find|.""" |
276 result = {} | 347 result = {} |
277 if not to_find: | 348 if not to_find: |
278 return result | 349 return result |
279 to_find = set(to_find) | 350 to_find = set(to_find) |
280 for target_name in all_targets.keys(): | 351 for target_name in all_targets.keys(): |
281 extracted = gyp.common.ParseQualifiedTarget(target_name) | 352 extracted = gyp.common.ParseQualifiedTarget(target_name) |
282 if len(extracted) > 1 and extracted[1] in to_find: | 353 if len(extracted) > 1 and extracted[1] in to_find: |
283 to_find.remove(extracted[1]) | 354 to_find.remove(extracted[1]) |
284 result[extracted[1]] = target_name | 355 result[extracted[1]] = all_targets[target_name] |
285 if not to_find: | 356 if not to_find: |
286 return result | 357 return result |
287 return result | 358 return result |
288 | 359 |
289 | 360 |
290 def _DoesTargetDependOn(target, all_targets): | 361 def _DoesTargetDependOn(target): |
291 """Returns true if |target| or any of its dependencies matches the supplied | 362 """Returns true if |target| or any of its dependencies matches the supplied |
292 set of paths. This updates |matches| of the Targets as it recurses. | 363 set of paths. This updates |matches| of the Targets as it recurses. |
293 target: the Target to look for. | 364 target: the Target to look for.""" |
294 all_targets: mapping from target name to Target. | |
295 matching_targets: set of targets looking for.""" | |
296 if target.match_status == MATCH_STATUS_DOESNT_MATCH: | 365 if target.match_status == MATCH_STATUS_DOESNT_MATCH: |
297 return False | 366 return False |
298 if target.match_status == MATCH_STATUS_MATCHES or \ | 367 if target.match_status == MATCH_STATUS_MATCHES or \ |
299 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY: | 368 target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY: |
300 return True | 369 return True |
301 for dep_name in target.deps: | 370 for dep in target.deps: |
302 dep_target = all_targets[dep_name] | 371 if _DoesTargetDependOn(dep): |
303 if _DoesTargetDependOn(dep_target, all_targets): | 372 target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY |
304 dep_target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY | |
305 return True | 373 return True |
306 dep_target.match_status = MATCH_STATUS_DOESNT_MATCH | 374 target.match_status = MATCH_STATUS_DOESNT_MATCH |
307 return False | 375 return False |
308 | 376 |
309 | 377 |
310 def _GetTargetsDependingOn(all_targets, possible_targets): | 378 def _GetTargetsDependingOn(possible_targets): |
311 """Returns the list of targets in |possible_targets| that depend (either | 379 """Returns the list of Targets in |possible_targets| that depend (either |
312 directly on indirectly) on the matched files. | 380 directly on indirectly) on the matched targets. |
313 all_targets: mapping from target name to Target. | |
314 possible_targets: targets to search from.""" | 381 possible_targets: targets to search from.""" |
315 found = [] | 382 found = [] |
316 for target in possible_targets: | 383 for target in possible_targets: |
317 if _DoesTargetDependOn(all_targets[target], all_targets): | 384 if _DoesTargetDependOn(target): |
318 # possible_targets was initially unqualified, keep it unqualified. | 385 found.append(target) |
319 found.append(gyp.common.ParseQualifiedTarget(target)[1]) | |
320 return found | 386 return found |
321 | 387 |
322 | 388 |
| 389 def _AddBuildTargets(target, roots, add_if_no_ancestor, result): |
| 390 """Recurses through all targets that depend on |target|, adding all targets |
| 391 that need to be built (and are in |roots|) to |result|. |
| 392 roots: set of root targets. |
| 393 add_if_no_ancestor: If true and there are no ancestors of |target| then add |
| 394 |target| to |result|. |target| must still be in |roots|. |
| 395 result: targets that need to be built are added here.""" |
| 396 if target.visited: |
| 397 return |
| 398 |
| 399 target.visited = True |
| 400 target.in_roots = not target.back_deps and target in roots |
| 401 |
| 402 for back_dep_target in target.back_deps: |
| 403 _AddBuildTargets(back_dep_target, roots, False, result) |
| 404 target.added_to_compile_targets |= back_dep_target.added_to_compile_targets |
| 405 target.in_roots |= back_dep_target.in_roots |
| 406 |
| 407 if not target.added_to_compile_targets and target.in_roots and \ |
| 408 (add_if_no_ancestor or target.requires_build): |
| 409 result.add(target) |
| 410 target.added_to_compile_targets = True |
| 411 |
| 412 |
| 413 def _GetBuildTargets(matching_targets, roots): |
| 414 """Returns the set of Targets that require a build. |
| 415 matching_targets: targets that changed and need to be built. |
| 416 roots: set of root targets in the build files to search from.""" |
| 417 result = set() |
| 418 for target in matching_targets: |
| 419 _AddBuildTargets(target, roots, True, result) |
| 420 return result |
| 421 |
| 422 |
323 def _WriteOutput(params, **values): | 423 def _WriteOutput(params, **values): |
324 """Writes the output, either to stdout or a file is specified.""" | 424 """Writes the output, either to stdout or a file is specified.""" |
| 425 if 'error' in values: |
| 426 print 'Error:', values['error'] |
| 427 if 'status' in values: |
| 428 print values['status'] |
| 429 if 'targets' in values: |
| 430 values['targets'].sort() |
| 431 print 'Supplied targets that depend on changed files:' |
| 432 for target in values['targets']: |
| 433 print '\t', target |
| 434 if 'build_targets' in values: |
| 435 values['build_targets'].sort() |
| 436 print 'Targets that require a build:' |
| 437 for target in values['build_targets']: |
| 438 print '\t', target |
| 439 |
325 output_path = params.get('generator_flags', {}).get( | 440 output_path = params.get('generator_flags', {}).get( |
326 'analyzer_output_path', None) | 441 'analyzer_output_path', None) |
327 if not output_path: | 442 if not output_path: |
328 print json.dumps(values) | 443 print json.dumps(values) |
329 return | 444 return |
330 try: | 445 try: |
331 f = open(output_path, 'w') | 446 f = open(output_path, 'w') |
332 f.write(json.dumps(values) + '\n') | 447 f.write(json.dumps(values) + '\n') |
333 f.close() | 448 f.close() |
334 except IOError as e: | 449 except IOError as e: |
335 print 'Error writing to output file', output_path, str(e) | 450 print 'Error writing to output file', output_path, str(e) |
336 | 451 |
337 | 452 |
| 453 def _WasGypIncludeFileModified(params, files): |
| 454 """Returns true if one of the files in |files| is in the set of included |
| 455 files.""" |
| 456 if params['options'].includes: |
| 457 for include in params['options'].includes: |
| 458 if _ToGypPath(include) in files: |
| 459 print 'Include file modified, assuming all changed', include |
| 460 return True |
| 461 return False |
| 462 |
| 463 |
| 464 def _NamesNotIn(names, mapping): |
| 465 """Returns a list of the values in |names| that are not in |mapping|.""" |
| 466 return [name for name in names if name not in mapping] |
| 467 |
| 468 |
| 469 def _LookupTargets(names, mapping): |
| 470 """Returns a list of the mapping[name] for each value in |names| that is in |
| 471 |mapping|.""" |
| 472 return [mapping[name] for name in names if name in mapping] |
| 473 |
| 474 |
338 def CalculateVariables(default_variables, params): | 475 def CalculateVariables(default_variables, params): |
339 """Calculate additional variables for use in the build (called by gyp).""" | 476 """Calculate additional variables for use in the build (called by gyp).""" |
340 flavor = gyp.common.GetFlavor(params) | 477 flavor = gyp.common.GetFlavor(params) |
341 if flavor == 'mac': | 478 if flavor == 'mac': |
342 default_variables.setdefault('OS', 'mac') | 479 default_variables.setdefault('OS', 'mac') |
343 elif flavor == 'win': | 480 elif flavor == 'win': |
344 default_variables.setdefault('OS', 'win') | 481 default_variables.setdefault('OS', 'win') |
345 # Copy additional generator configuration data from VS, which is shared | 482 # Copy additional generator configuration data from VS, which is shared |
346 # by the Windows Ninja generator. | 483 # by the Windows Ninja generator. |
347 import gyp.generator.msvs as msvs_generator | 484 import gyp.generator.msvs as msvs_generator |
(...skipping 16 matching lines...) Expand all Loading... |
364 try: | 501 try: |
365 config.Init(params) | 502 config.Init(params) |
366 if not config.files: | 503 if not config.files: |
367 raise Exception('Must specify files to analyze via config_path generator ' | 504 raise Exception('Must specify files to analyze via config_path generator ' |
368 'flag') | 505 'flag') |
369 | 506 |
370 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) | 507 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) |
371 if debug: | 508 if debug: |
372 print 'toplevel_dir', toplevel_dir | 509 print 'toplevel_dir', toplevel_dir |
373 | 510 |
374 matched = False | 511 if _WasGypIncludeFileModified(params, config.files): |
375 matched_include = False | 512 result_dict = { 'status': all_changed_string, |
| 513 'targets': list(config.targets) } |
| 514 _WriteOutput(params, **result_dict) |
| 515 return |
376 | 516 |
377 # If one of the modified files is an include file then everything is | 517 all_targets, matching_targets, roots = _GenerateTargets( |
378 # affected. | 518 data, target_list, target_dicts, toplevel_dir, frozenset(config.files), |
379 if params['options'].includes: | 519 params['build_files']) |
380 for include in params['options'].includes: | |
381 if _ToGypPath(include) in config.files: | |
382 if debug: | |
383 print 'include path modified', include | |
384 matched_include = True | |
385 matched = True | |
386 break | |
387 | |
388 if not matched: | |
389 all_targets, matched = _GenerateTargets(data, target_list, target_dicts, | |
390 toplevel_dir, | |
391 frozenset(config.files)) | |
392 | 520 |
393 warning = None | 521 warning = None |
394 if matched_include: | 522 unqualified_mapping = _GetUnqualifiedToTargetMapping(all_targets, |
395 output_targets = config.targets | 523 config.targets) |
396 elif matched: | 524 if len(unqualified_mapping) != len(config.targets): |
397 unqualified_mapping = _GetUnqualifiedToQualifiedMapping( | 525 not_found = _NamesNotIn(config.targets, unqualified_mapping) |
398 all_targets, config.targets) | 526 warning = 'Unable to find all targets: ' + str(not_found) |
399 if len(unqualified_mapping) != len(config.targets): | 527 |
400 not_found = [] | 528 if matching_targets: |
401 for target in config.targets: | 529 search_targets = _LookupTargets(config.targets, unqualified_mapping) |
402 if not target in unqualified_mapping: | 530 matched_search_targets = _GetTargetsDependingOn(search_targets) |
403 not_found.append(target) | 531 # Reset the visited status for _GetBuildTargets. |
404 warning = 'Unable to find all targets: ' + str(not_found) | 532 for target in all_targets.itervalues(): |
405 qualified_targets = [] | 533 target.visited = False |
406 for target in config.targets: | 534 build_targets = _GetBuildTargets(matching_targets, roots) |
407 if target in unqualified_mapping: | 535 matched_search_targets = [gyp.common.ParseQualifiedTarget(target.name)[1] |
408 qualified_targets.append(unqualified_mapping[target]) | 536 for target in matched_search_targets] |
409 output_targets = _GetTargetsDependingOn(all_targets, qualified_targets) | 537 build_targets = [gyp.common.ParseQualifiedTarget(target.name)[1] |
| 538 for target in build_targets] |
410 else: | 539 else: |
411 output_targets = [] | 540 matched_search_targets = [] |
| 541 build_targets = [] |
412 | 542 |
413 result_dict = { 'targets': output_targets, | 543 result_dict = { 'targets': matched_search_targets, |
414 'status': found_dependency_string if matched else | 544 'status': found_dependency_string if matching_targets else |
415 no_dependency_string } | 545 no_dependency_string, |
| 546 'build_targets': build_targets} |
416 if warning: | 547 if warning: |
417 result_dict['warning'] = warning | 548 result_dict['warning'] = warning |
418 _WriteOutput(params, **result_dict) | 549 _WriteOutput(params, **result_dict) |
419 | 550 |
420 except Exception as e: | 551 except Exception as e: |
421 _WriteOutput(params, error=str(e)) | 552 _WriteOutput(params, error=str(e)) |
OLD | NEW |