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. |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
57 generator_default_variables[dirname] = '!!!' | 57 generator_default_variables[dirname] = '!!!' |
58 | 58 |
59 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', | 59 for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', |
60 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', | 60 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', |
61 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', | 61 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', |
62 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', | 62 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', |
63 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', | 63 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', |
64 'CONFIGURATION_NAME']: | 64 'CONFIGURATION_NAME']: |
65 generator_default_variables[unused] = '' | 65 generator_default_variables[unused] = '' |
66 | 66 |
67 def _ToGypPath(path): | |
scottmg
2014/08/05 23:02:43
I don't understand why some functions are _ and so
sky
2014/08/06 00:26:07
Indeed. Python sites recommend __, which is why I
| |
68 """Converts a path to the format used by gyp.""" | |
69 if os.sep == '\\' and os.altsep == '/': | |
70 return path.replace('\\', '/') | |
71 return path | |
72 | |
67 def __ExtractBasePath(target): | 73 def __ExtractBasePath(target): |
68 """Extracts the path components of the specified gyp target path.""" | 74 """Extracts the path components of the specified gyp target path.""" |
69 last_index = target.rfind('/') | 75 last_index = target.rfind('/') |
70 if last_index == -1: | 76 if last_index == -1: |
71 return '' | 77 return '' |
72 return target[0:(last_index + 1)] | 78 return target[0:(last_index + 1)] |
73 | 79 |
74 def __ResolveParent(path, base_path_components): | 80 def __ResolveParent(path, base_path_components): |
75 """Resolves |path|, which starts with at least one '../'. Returns an empty | 81 """Resolves |path|, which starts with at least one '../'. Returns an empty |
76 string if the path shouldn't be considered. See __AddSources() for a | 82 string if the path shouldn't be considered. See __AddSources() for a |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
113 | 119 |
114 def __ExtractSourcesFromAction(action, base_path, base_path_components, | 120 def __ExtractSourcesFromAction(action, base_path, base_path_components, |
115 results): | 121 results): |
116 if 'inputs' in action: | 122 if 'inputs' in action: |
117 __AddSources(action['inputs'], base_path, base_path_components, results) | 123 __AddSources(action['inputs'], base_path, base_path_components, results) |
118 | 124 |
119 def __ExtractSources(target, target_dict, toplevel_dir): | 125 def __ExtractSources(target, target_dict, toplevel_dir): |
120 # |target| is either absolute or relative and in the format of the OS. Gyp | 126 # |target| is either absolute or relative and in the format of the OS. Gyp |
121 # source paths are always posix. Convert |target| to a posix path relative to | 127 # source paths are always posix. Convert |target| to a posix path relative to |
122 # |toplevel_dir_|. This is done to make it easy to build source paths. | 128 # |toplevel_dir_|. This is done to make it easy to build source paths. |
123 if os.sep == '\\' and os.altsep == '/': | 129 base_path = _ToGypPath(target) |
124 base_path = target.replace('\\', '/') | |
125 else: | |
126 base_path = target | |
127 if base_path == toplevel_dir: | 130 if base_path == toplevel_dir: |
128 base_path = '' | 131 base_path = '' |
129 elif base_path.startswith(toplevel_dir + '/'): | 132 elif base_path.startswith(toplevel_dir + '/'): |
130 base_path = base_path[len(toplevel_dir) + len('/'):] | 133 base_path = base_path[len(toplevel_dir) + len('/'):] |
131 base_path = posixpath.dirname(base_path) | 134 base_path = posixpath.dirname(base_path) |
132 base_path_components = base_path.split('/') | 135 base_path_components = base_path.split('/') |
133 | 136 |
134 # Add a trailing '/' so that __AddSources() can easily build paths. | 137 # Add a trailing '/' so that __AddSources() can easily build paths. |
135 if len(base_path): | 138 if len(base_path): |
136 base_path += '/' | 139 base_path += '/' |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 f = open(file_path, 'r') | 212 f = open(file_path, 'r') |
210 for file_name in f: | 213 for file_name in f: |
211 if file_name.endswith('\n'): | 214 if file_name.endswith('\n'): |
212 file_name = file_name[0:len(file_name) - 1] | 215 file_name = file_name[0:len(file_name) - 1] |
213 if len(file_name): | 216 if len(file_name): |
214 self.files.append(file_name) | 217 self.files.append(file_name) |
215 f.close() | 218 f.close() |
216 except IOError: | 219 except IOError: |
217 raise Exception('Unable to open file', file_path) | 220 raise Exception('Unable to open file', file_path) |
218 | 221 |
219 def __GenerateTargets(target_list, target_dicts, toplevel_dir, files): | 222 def _WasBuildFileModified(build_file, data, files): |
223 """Returns true if the build file |build_file| is either in |files| or | |
224 one of the files included by |build_file| is in |files|.""" | |
225 if _ToGypPath(build_file) in files: | |
226 if debug: | |
227 print 'gyp file modified', build_file | |
228 return True | |
229 | |
230 # First element of included_files is the file itself. | |
231 if len(data[build_file]['included_files']) <= 1: | |
232 return False | |
233 | |
234 for include_file in data[build_file]['included_files'][1:]: | |
235 # |included_files| are relative to the directory of the |build_file|. | |
236 rel_include_file = \ | |
237 _ToGypPath(gyp.common.UnrelativePath(include_file, build_file)) | |
238 if rel_include_file in files: | |
239 if debug: | |
240 print 'included gyp file modified, gyp_file=', build_file, \ | |
241 'included file=', rel_include_file | |
242 return True | |
243 return False | |
244 | |
245 def __GenerateTargets(data, target_list, target_dicts, toplevel_dir, files): | |
220 """Generates a dictionary with the key the name of a target and the value a | 246 """Generates a dictionary with the key the name of a target and the value a |
221 Target. |toplevel_dir| is the root of the source tree. If the sources of | 247 Target. |toplevel_dir| is the root of the source tree. If the sources of |
222 a target match that of |files|, then |target.matched| is set to True. | 248 a target match that of |files|, then |target.matched| is set to True. |
223 This returns a tuple of the dictionary and whether at least one target's | 249 This returns a tuple of the dictionary and whether at least one target's |
224 sources listed one of the paths in |files|.""" | 250 sources listed one of the paths in |files|.""" |
225 targets = {} | 251 targets = {} |
226 | 252 |
227 # Queue of targets to visit. | 253 # Queue of targets to visit. |
228 targets_to_visit = target_list[:] | 254 targets_to_visit = target_list[:] |
229 | 255 |
230 matched = False | 256 matched = False |
231 | 257 |
258 # Maps from build file to a boolean indicating whether the build file is in | |
259 # |files|. | |
260 build_file_in_files = {} | |
261 | |
232 while len(targets_to_visit) > 0: | 262 while len(targets_to_visit) > 0: |
233 target_name = targets_to_visit.pop() | 263 target_name = targets_to_visit.pop() |
234 if target_name in targets: | 264 if target_name in targets: |
235 continue | 265 continue |
236 | 266 |
237 target = Target() | 267 target = Target() |
238 targets[target_name] = target | 268 targets[target_name] = target |
239 sources = __ExtractSources(target_name, target_dicts[target_name], | 269 |
240 toplevel_dir) | 270 build_file = gyp.common.ParseQualifiedTarget(target_name)[0] |
241 for source in sources: | 271 if not build_file in build_file_in_files: |
242 if source in files: | 272 build_file_in_files[build_file] = \ |
243 target.match_status = MATCH_STATUS_MATCHES | 273 _WasBuildFileModified(build_file, data, files) |
244 matched = True | 274 |
245 break | 275 # If a build file (or any of its included files) is modified we assume all |
276 # targets in the file are modified. | |
277 if build_file_in_files[build_file]: | |
278 target.match_status = MATCH_STATUS_MATCHES | |
279 matched = True | |
280 else: | |
281 sources = __ExtractSources(target_name, target_dicts[target_name], | |
282 toplevel_dir) | |
283 for source in sources: | |
284 if source in files: | |
285 target.match_status = MATCH_STATUS_MATCHES | |
286 matched = True | |
287 break | |
246 | 288 |
247 for dep in target_dicts[target_name].get('dependencies', []): | 289 for dep in target_dicts[target_name].get('dependencies', []): |
248 targets[target_name].deps.add(dep) | 290 targets[target_name].deps.add(dep) |
249 targets_to_visit.append(dep) | 291 targets_to_visit.append(dep) |
250 | 292 |
251 return targets, matched | 293 return targets, matched |
252 | 294 |
253 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): | 295 def _GetUnqualifiedToQualifiedMapping(all_targets, to_find): |
254 """Returns a mapping (dictionary) from unqualified name to qualified name for | 296 """Returns a mapping (dictionary) from unqualified name to qualified name for |
255 all the targets in |to_find|.""" | 297 all the targets in |to_find|.""" |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
338 config = Config() | 380 config = Config() |
339 try: | 381 try: |
340 config.Init(params) | 382 config.Init(params) |
341 if not config.files: | 383 if not config.files: |
342 if config.look_for_dependency_only: | 384 if config.look_for_dependency_only: |
343 print 'Must specify files to analyze via file_path generator flag' | 385 print 'Must specify files to analyze via file_path generator flag' |
344 return | 386 return |
345 raise Exception('Must specify files to analyze via config_path generator ' | 387 raise Exception('Must specify files to analyze via config_path generator ' |
346 'flag') | 388 'flag') |
347 | 389 |
348 toplevel_dir = os.path.abspath(params['options'].toplevel_dir) | 390 toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir)) |
349 if os.sep == '\\' and os.altsep == '/': | |
350 toplevel_dir = toplevel_dir.replace('\\', '/') | |
351 if debug: | 391 if debug: |
352 print 'toplevel_dir', toplevel_dir | 392 print 'toplevel_dir', toplevel_dir |
353 | 393 |
354 all_targets, matched = __GenerateTargets(target_list, target_dicts, | 394 matched = False |
355 toplevel_dir, | 395 matched_include = False |
356 frozenset(config.files)) | 396 |
397 # If one of the modified files is an include file then everything is | |
398 # effected. | |
scottmg
2014/08/05 23:02:43
nit; affected.
sky
2014/08/06 00:26:07
I ALWAYS get that wrong.
| |
399 if params['options'].includes: | |
400 for include in params['options'].includes: | |
401 if _ToGypPath(include) in config.files: | |
402 if debug: | |
403 print 'include path modified', include | |
404 matched_include = True | |
405 matched = True | |
406 break | |
407 | |
408 if not matched: | |
409 all_targets, matched = __GenerateTargets(data, target_list, target_dicts, | |
410 toplevel_dir, | |
411 frozenset(config.files)) | |
357 | 412 |
358 # Set of targets that refer to one of the files. | 413 # Set of targets that refer to one of the files. |
359 if config.look_for_dependency_only: | 414 if config.look_for_dependency_only: |
360 print found_dependency_string if matched else no_dependency_string | 415 print found_dependency_string if matched else no_dependency_string |
361 return | 416 return |
362 | 417 |
363 warning = None | 418 warning = None |
364 if matched: | 419 if matched_include: |
420 output_targets = config.targets | |
421 elif matched: | |
365 unqualified_mapping = _GetUnqualifiedToQualifiedMapping( | 422 unqualified_mapping = _GetUnqualifiedToQualifiedMapping( |
366 all_targets, config.targets) | 423 all_targets, config.targets) |
367 if len(unqualified_mapping) != len(config.targets): | 424 if len(unqualified_mapping) != len(config.targets): |
368 not_found = [] | 425 not_found = [] |
369 for target in config.targets: | 426 for target in config.targets: |
370 if not target in unqualified_mapping: | 427 if not target in unqualified_mapping: |
371 not_found.append(target) | 428 not_found.append(target) |
372 warning = 'Unable to find all targets: ' + str(not_found) | 429 warning = 'Unable to find all targets: ' + str(not_found) |
373 qualified_targets = [] | 430 qualified_targets = [] |
374 for target in config.targets: | 431 for target in config.targets: |
375 if target in unqualified_mapping: | 432 if target in unqualified_mapping: |
376 qualified_targets.append(unqualified_mapping[target]) | 433 qualified_targets.append(unqualified_mapping[target]) |
377 output_targets = _GetTargetsDependingOn(all_targets, qualified_targets) | 434 output_targets = _GetTargetsDependingOn(all_targets, qualified_targets) |
378 else: | 435 else: |
379 output_targets = [] | 436 output_targets = [] |
380 | 437 |
381 result_dict = { 'targets': output_targets, | 438 result_dict = { 'targets': output_targets, |
382 'status': found_dependency_string if matched else | 439 'status': found_dependency_string if matched else |
383 no_dependency_string } | 440 no_dependency_string } |
384 if warning: | 441 if warning: |
385 result_dict['warning'] = warning | 442 result_dict['warning'] = warning |
386 _WriteOutput(params, **result_dict) | 443 _WriteOutput(params, **result_dict) |
387 | 444 |
388 except Exception as e: | 445 except Exception as e: |
389 _WriteOutput(params, error=str(e)) | 446 _WriteOutput(params, error=str(e)) |
OLD | NEW |