OLD | NEW |
---|---|
1 # Copyright (c) 2013 Google Inc. All rights reserved. | 1 # Copyright (c) 2013 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 import collections | 5 import collections |
6 import copy | 6 import copy |
7 import hashlib | 7 import hashlib |
8 import json | 8 import json |
9 import multiprocessing | 9 import multiprocessing |
10 import os.path | 10 import os.path |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
62 generator_extra_sources_for_rules = [] | 62 generator_extra_sources_for_rules = [] |
63 generator_filelist_paths = None | 63 generator_filelist_paths = None |
64 | 64 |
65 generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested() | 65 generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested() |
66 | 66 |
67 def StripPrefix(arg, prefix): | 67 def StripPrefix(arg, prefix): |
68 if arg.startswith(prefix): | 68 if arg.startswith(prefix): |
69 return arg[len(prefix):] | 69 return arg[len(prefix):] |
70 return arg | 70 return arg |
71 | 71 |
72 def OrderDeterministically(l, **kwargs): | |
73 """ Sorts l so that it is ordered deterministically. """ | |
74 return sorted(l, **kwargs) | |
mithro-old
2015/12/10 03:15:27
I think this should be in gyp.common with OrderedS
Zachary Forman
2015/12/30 02:18:43
Yeah, you caught me mid refactor. Good spot for it
| |
72 | 75 |
73 def QuoteShellArgument(arg, flavor): | 76 def QuoteShellArgument(arg, flavor): |
74 """Quote a string such that it will be interpreted as a single argument | 77 """Quote a string such that it will be interpreted as a single argument |
75 by the shell.""" | 78 by the shell.""" |
76 # Rather than attempting to enumerate the bad shell characters, just | 79 # Rather than attempting to enumerate the bad shell characters, just |
77 # whitelist common OK ones and quote anything else. | 80 # whitelist common OK ones and quote anything else. |
78 if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg): | 81 if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg): |
79 return arg # No quoting necessary. | 82 return arg # No quoting necessary. |
80 if flavor == 'win': | 83 if flavor == 'win': |
81 return gyp.msvs_emulation.QuoteForRspFile(arg) | 84 return gyp.msvs_emulation.QuoteForRspFile(arg) |
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
414 # TODO(evan): it is rather confusing which things are lists and which | 417 # TODO(evan): it is rather confusing which things are lists and which |
415 # are strings. Fix these. | 418 # are strings. Fix these. |
416 if 'dependencies' in spec: | 419 if 'dependencies' in spec: |
417 for dep in spec['dependencies']: | 420 for dep in spec['dependencies']: |
418 if dep in self.target_outputs: | 421 if dep in self.target_outputs: |
419 target = self.target_outputs[dep] | 422 target = self.target_outputs[dep] |
420 actions_depends.append(target.PreActionInput(self.flavor)) | 423 actions_depends.append(target.PreActionInput(self.flavor)) |
421 compile_depends.append(target.PreCompileInput()) | 424 compile_depends.append(target.PreCompileInput()) |
422 actions_depends = filter(None, actions_depends) | 425 actions_depends = filter(None, actions_depends) |
423 compile_depends = filter(None, compile_depends) | 426 compile_depends = filter(None, compile_depends) |
424 actions_depends = self.WriteCollapsedDependencies('actions_depends', | 427 actions_depends = self.WriteCollapsedDependencies( |
425 actions_depends) | 428 'actions_depends', OrderDeterministically(actions_depends)) |
426 compile_depends = self.WriteCollapsedDependencies('compile_depends', | 429 compile_depends = self.WriteCollapsedDependencies( |
427 compile_depends) | 430 'compile_depends', OrderDeterministically(compile_depends)) |
428 self.target.preaction_stamp = actions_depends | 431 self.target.preaction_stamp = actions_depends |
429 self.target.precompile_stamp = compile_depends | 432 self.target.precompile_stamp = compile_depends |
430 | 433 |
431 # Write out actions, rules, and copies. These must happen before we | 434 # Write out actions, rules, and copies. These must happen before we |
432 # compile any sources, so compute a list of predependencies for sources | 435 # compile any sources, so compute a list of predependencies for sources |
433 # while we do it. | 436 # while we do it. |
434 extra_sources = [] | 437 extra_sources = [] |
435 mac_bundle_depends = [] | 438 mac_bundle_depends = [] |
436 self.target.actions_stamp = self.WriteActionsRulesCopies( | 439 self.target.actions_stamp = self.WriteActionsRulesCopies( |
437 spec, extra_sources, actions_depends, mac_bundle_depends) | 440 spec, extra_sources, actions_depends, mac_bundle_depends) |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
552 if 'rules' in spec: | 555 if 'rules' in spec: |
553 outputs += self.WriteRules(spec['rules'], extra_sources, prebuild, | 556 outputs += self.WriteRules(spec['rules'], extra_sources, prebuild, |
554 mac_bundle_resources, | 557 mac_bundle_resources, |
555 extra_mac_bundle_resources) | 558 extra_mac_bundle_resources) |
556 if 'copies' in spec: | 559 if 'copies' in spec: |
557 outputs += self.WriteCopies(spec['copies'], prebuild, mac_bundle_depends) | 560 outputs += self.WriteCopies(spec['copies'], prebuild, mac_bundle_depends) |
558 | 561 |
559 if 'sources' in spec and self.flavor == 'win': | 562 if 'sources' in spec and self.flavor == 'win': |
560 outputs += self.WriteWinIdlFiles(spec, prebuild) | 563 outputs += self.WriteWinIdlFiles(spec, prebuild) |
561 | 564 |
562 stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs) | 565 stamp = self.WriteCollapsedDependencies('actions_rules_copies', |
566 OrderDeterministically(outputs)) | |
563 | 567 |
564 if self.is_mac_bundle: | 568 if self.is_mac_bundle: |
565 xcassets = self.WriteMacBundleResources( | 569 xcassets = self.WriteMacBundleResources( |
566 extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends) | 570 extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends) |
567 partial_info_plist = self.WriteMacXCassets(xcassets, mac_bundle_depends) | 571 partial_info_plist = self.WriteMacXCassets(xcassets, mac_bundle_depends) |
568 self.WriteMacInfoPlist(partial_info_plist, mac_bundle_depends) | 572 self.WriteMacInfoPlist(partial_info_plist, mac_bundle_depends) |
569 | 573 |
570 return stamp | 574 return stamp |
571 | 575 |
572 def GenerateDescription(self, verb, message, fallback): | 576 def GenerateDescription(self, verb, message, fallback): |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
650 # Rules can potentially make use of some special variables which | 654 # Rules can potentially make use of some special variables which |
651 # must vary per source file. | 655 # must vary per source file. |
652 # Compute the list of variables we'll need to provide. | 656 # Compute the list of variables we'll need to provide. |
653 special_locals = ('source', 'root', 'dirname', 'ext', 'name') | 657 special_locals = ('source', 'root', 'dirname', 'ext', 'name') |
654 needed_variables = set(['source']) | 658 needed_variables = set(['source']) |
655 for argument in args: | 659 for argument in args: |
656 for var in special_locals: | 660 for var in special_locals: |
657 if '${%s}' % var in argument: | 661 if '${%s}' % var in argument: |
658 needed_variables.add(var) | 662 needed_variables.add(var) |
659 | 663 |
664 needed_variables = OrderDeterministically(needed_variables) | |
665 | |
660 def cygwin_munge(path): | 666 def cygwin_munge(path): |
661 # pylint: disable=cell-var-from-loop | 667 # pylint: disable=cell-var-from-loop |
662 if is_cygwin: | 668 if is_cygwin: |
663 return path.replace('\\', '/') | 669 return path.replace('\\', '/') |
664 return path | 670 return path |
665 | 671 |
666 inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])] | 672 inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])] |
667 | 673 |
668 # If there are n source files matching the rule, and m additional rule | 674 # If there are n source files matching the rule, and m additional rule |
669 # inputs, then adding 'inputs' to each build edge written below will | 675 # inputs, then adding 'inputs' to each build edge written below will |
670 # write m * n inputs. Collapsing reduces this to m + n. | 676 # write m * n inputs. Collapsing reduces this to m + n. |
671 sources = rule.get('rule_sources', []) | 677 sources = rule.get('rule_sources', []) |
672 num_inputs = len(inputs) | 678 num_inputs = len(inputs) |
673 if prebuild: | 679 if prebuild: |
674 num_inputs += 1 | 680 num_inputs += 1 |
675 if num_inputs > 2 and len(sources) > 2: | 681 if num_inputs > 2 and len(sources) > 2: |
676 inputs = [self.WriteCollapsedDependencies( | 682 inputs = [self.WriteCollapsedDependencies( |
677 rule['rule_name'], inputs, order_only=prebuild)] | 683 rule['rule_name'], OrderDeterministically(inputs), order_only=prebuild )] |
678 prebuild = [] | 684 prebuild = [] |
679 | 685 |
680 # For each source file, write an edge that generates all the outputs. | 686 # For each source file, write an edge that generates all the outputs. |
681 for source in sources: | 687 for source in sources: |
682 source = os.path.normpath(source) | 688 source = os.path.normpath(source) |
683 dirname, basename = os.path.split(source) | 689 dirname, basename = os.path.split(source) |
684 root, ext = os.path.splitext(basename) | 690 root, ext = os.path.splitext(basename) |
685 | 691 |
686 # Gather the list of inputs and outputs, expanding $vars if possible. | 692 # Gather the list of inputs and outputs, expanding $vars if possible. |
687 outputs = [self.ExpandRuleVariables(o, root, dirname, | 693 outputs = [self.ExpandRuleVariables(o, root, dirname, |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
722 elif var == 'name': | 728 elif var == 'name': |
723 extra_bindings.append(('name', cygwin_munge(basename))) | 729 extra_bindings.append(('name', cygwin_munge(basename))) |
724 else: | 730 else: |
725 assert var == None, repr(var) | 731 assert var == None, repr(var) |
726 | 732 |
727 outputs = [self.GypPathToNinja(o, env) for o in outputs] | 733 outputs = [self.GypPathToNinja(o, env) for o in outputs] |
728 if self.flavor == 'win': | 734 if self.flavor == 'win': |
729 # WriteNewNinjaRule uses unique_name for creating an rsp file on win. | 735 # WriteNewNinjaRule uses unique_name for creating an rsp file on win. |
730 extra_bindings.append(('unique_name', | 736 extra_bindings.append(('unique_name', |
731 hashlib.md5(outputs[0]).hexdigest())) | 737 hashlib.md5(outputs[0]).hexdigest())) |
738 | |
739 # Make sure we sort extra_bindings so that output is deterministic. | |
732 self.ninja.build(outputs, rule_name, self.GypPathToNinja(source), | 740 self.ninja.build(outputs, rule_name, self.GypPathToNinja(source), |
733 implicit=inputs, | 741 implicit=inputs, |
734 order_only=prebuild, | 742 order_only=prebuild, |
735 variables=extra_bindings) | 743 variables=extra_bindings) |
736 | 744 |
737 all_outputs.extend(outputs) | 745 all_outputs.extend(outputs) |
738 | 746 |
739 return all_outputs | 747 return all_outputs |
740 | 748 |
741 def WriteCopies(self, copies, prebuild, mac_bundle_depends): | 749 def WriteCopies(self, copies, prebuild, mac_bundle_depends): |
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1127 new_deps = [target.binary] | 1135 new_deps = [target.binary] |
1128 for new_dep in new_deps: | 1136 for new_dep in new_deps: |
1129 if new_dep not in extra_link_deps: | 1137 if new_dep not in extra_link_deps: |
1130 extra_link_deps.add(new_dep) | 1138 extra_link_deps.add(new_dep) |
1131 link_deps.append(new_dep) | 1139 link_deps.append(new_dep) |
1132 | 1140 |
1133 final_output = target.FinalOutput() | 1141 final_output = target.FinalOutput() |
1134 if not linkable or final_output != target.binary: | 1142 if not linkable or final_output != target.binary: |
1135 implicit_deps.add(final_output) | 1143 implicit_deps.add(final_output) |
1136 | 1144 |
1145 link_deps = OrderDeterministically(link_deps) | |
1146 | |
1137 extra_bindings = [] | 1147 extra_bindings = [] |
1138 if self.uses_cpp and self.flavor != 'win': | 1148 if self.uses_cpp and self.flavor != 'win': |
1139 extra_bindings.append(('ld', '$ldxx')) | 1149 extra_bindings.append(('ld', '$ldxx')) |
1140 | 1150 |
1141 output = self.ComputeOutput(spec, arch) | 1151 output = self.ComputeOutput(spec, arch) |
1142 if arch is None and not self.is_mac_bundle: | 1152 if arch is None and not self.is_mac_bundle: |
1143 self.AppendPostbuildVariable(extra_bindings, spec, output, output) | 1153 self.AppendPostbuildVariable(extra_bindings, spec, output, output) |
1144 | 1154 |
1145 is_executable = spec['type'] == 'executable' | 1155 is_executable = spec['type'] == 'executable' |
1146 # The ldflags config key is not used on mac or win. On those platforms | 1156 # The ldflags config key is not used on mac or win. On those platforms |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1243 output = [output, output + '.TOC'] | 1253 output = [output, output + '.TOC'] |
1244 else: | 1254 else: |
1245 command = command + '_notoc' | 1255 command = command + '_notoc' |
1246 elif self.flavor == 'win': | 1256 elif self.flavor == 'win': |
1247 extra_bindings.append(('binary', output)) | 1257 extra_bindings.append(('binary', output)) |
1248 pdbname = self.msvs_settings.GetPDBName( | 1258 pdbname = self.msvs_settings.GetPDBName( |
1249 config_name, self.ExpandSpecial, output + '.pdb') | 1259 config_name, self.ExpandSpecial, output + '.pdb') |
1250 if pdbname: | 1260 if pdbname: |
1251 output = [output, pdbname] | 1261 output = [output, pdbname] |
1252 | 1262 |
1253 | |
1254 if len(solibs): | 1263 if len(solibs): |
1255 extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs))) | 1264 extra_bindings.append(('solibs', |
1265 gyp.common.EncodePOSIXShellList(OrderDeterministically(solibs)))) | |
1256 | 1266 |
1257 ninja_file.build(output, command + command_suffix, link_deps, | 1267 ninja_file.build(output, command + command_suffix, link_deps, |
1258 implicit=list(implicit_deps), | 1268 implicit=OrderDeterministically(implicit_deps), |
1259 order_only=list(order_deps), | 1269 order_only=order_deps, |
1260 variables=extra_bindings) | 1270 variables=extra_bindings) |
1261 return linked_binary | 1271 return linked_binary |
1262 | 1272 |
1263 def WriteTarget(self, spec, config_name, config, link_deps, compile_deps): | 1273 def WriteTarget(self, spec, config_name, config, link_deps, compile_deps): |
1264 extra_link_deps = any(self.target_outputs.get(dep).Linkable() | 1274 extra_link_deps = any(self.target_outputs.get(dep).Linkable() |
1265 for dep in spec.get('dependencies', []) | 1275 for dep in spec.get('dependencies', []) |
1266 if dep in self.target_outputs) | 1276 if dep in self.target_outputs) |
1267 if spec['type'] == 'none' or (not link_deps and not extra_link_deps): | 1277 if spec['type'] == 'none' or (not link_deps and not extra_link_deps): |
1268 # TODO(evan): don't call this function for 'none' target types, as | 1278 # TODO(evan): don't call this function for 'none' target types, as |
1269 # it doesn't do anything, and we fake out a 'binary' with a stamp file. | 1279 # it doesn't do anything, and we fake out a 'binary' with a stamp file. |
(...skipping 1060 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2330 non_empty_target_names.add(name) | 2340 non_empty_target_names.add(name) |
2331 else: | 2341 else: |
2332 empty_target_names.add(name) | 2342 empty_target_names.add(name) |
2333 | 2343 |
2334 if target_short_names: | 2344 if target_short_names: |
2335 # Write a short name to build this target. This benefits both the | 2345 # Write a short name to build this target. This benefits both the |
2336 # "build chrome" case as well as the gyp tests, which expect to be | 2346 # "build chrome" case as well as the gyp tests, which expect to be |
2337 # able to run actions and build libraries by their short name. | 2347 # able to run actions and build libraries by their short name. |
2338 master_ninja.newline() | 2348 master_ninja.newline() |
2339 master_ninja.comment('Short names for targets.') | 2349 master_ninja.comment('Short names for targets.') |
2340 for short_name in target_short_names: | 2350 |
2341 master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in | 2351 for short_name in OrderDeterministically(target_short_names): |
2342 target_short_names[short_name]]) | 2352 master_ninja.build(short_name, 'phony', ([x.FinalOutput() for x in |
2353 target_short_names[short_name]])) | |
2343 | 2354 |
2344 # Write phony targets for any empty targets that weren't written yet. As | 2355 # Write phony targets for any empty targets that weren't written yet. As |
2345 # short names are not necessarily unique only do this for short names that | 2356 # short names are not necessarily unique only do this for short names that |
2346 # haven't already been output for another target. | 2357 # haven't already been output for another target. |
2347 empty_target_names = empty_target_names - non_empty_target_names | 2358 empty_target_names = empty_target_names - non_empty_target_names |
2348 if empty_target_names: | 2359 if empty_target_names: |
2349 master_ninja.newline() | 2360 master_ninja.newline() |
2350 master_ninja.comment('Empty targets (output for completeness).') | 2361 master_ninja.comment('Empty targets (output for completeness).') |
2351 for name in sorted(empty_target_names): | 2362 |
2363 # Iterate over empty target names in a deterministic order by sorting. | |
2364 for name in OrderDeterministically(empty_target_names): | |
2352 master_ninja.build(name, 'phony') | 2365 master_ninja.build(name, 'phony') |
2353 | 2366 |
2354 if all_outputs: | 2367 if all_outputs: |
2355 master_ninja.newline() | 2368 master_ninja.newline() |
2356 master_ninja.build('all', 'phony', list(all_outputs)) | 2369 master_ninja.build('all', 'phony', OrderDeterministically(all_outputs)) |
2357 master_ninja.default(generator_flags.get('default_target', 'all')) | 2370 master_ninja.default(generator_flags.get('default_target', 'all')) |
2358 | 2371 |
2359 master_ninja_file.close() | 2372 master_ninja_file.close() |
2360 | 2373 |
2361 | 2374 |
2362 def PerformBuild(data, configurations, params): | 2375 def PerformBuild(data, configurations, params): |
2363 options = params['options'] | 2376 options = params['options'] |
2364 for config in configurations: | 2377 for config in configurations: |
2365 builddir = os.path.join(options.toplevel_dir, 'out', config) | 2378 builddir = os.path.join(options.toplevel_dir, 'out', config) |
2366 arguments = ['ninja', '-C', builddir] | 2379 arguments = ['ninja', '-C', builddir] |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2401 arglists.append( | 2414 arglists.append( |
2402 (target_list, target_dicts, data, params, config_name)) | 2415 (target_list, target_dicts, data, params, config_name)) |
2403 pool.map(CallGenerateOutputForConfig, arglists) | 2416 pool.map(CallGenerateOutputForConfig, arglists) |
2404 except KeyboardInterrupt, e: | 2417 except KeyboardInterrupt, e: |
2405 pool.terminate() | 2418 pool.terminate() |
2406 raise e | 2419 raise e |
2407 else: | 2420 else: |
2408 for config_name in config_names: | 2421 for config_name in config_names: |
2409 GenerateOutputForConfig(target_list, target_dicts, data, params, | 2422 GenerateOutputForConfig(target_list, target_dicts, data, params, |
2410 config_name) | 2423 config_name) |
OLD | NEW |