Chromium Code Reviews| 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 |