Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(424)

Side by Side Diff: pylib/gyp/generator/ninja.py

Issue 1506733002: GYP: Make GYP build deterministic (Closed) Base URL: https://chromium.googlesource.com/external/gyp.git@master
Patch Set: All tested Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
11 import re 11 import re
12 import signal 12 import signal
13 import subprocess 13 import subprocess
14 import sys 14 import sys
15 import gyp 15 import gyp
16 import gyp.common 16 import gyp.common
17 from gyp.common import OrderedSet 17 from gyp.common import OrderedSet
18 from gyp.common import OrderDeterministically
19
18 import gyp.msvs_emulation 20 import gyp.msvs_emulation
19 import gyp.MSVSUtil as MSVSUtil 21 import gyp.MSVSUtil as MSVSUtil
20 import gyp.xcode_emulation 22 import gyp.xcode_emulation
21 from cStringIO import StringIO 23 from cStringIO import StringIO
22 24
23 from gyp.common import GetEnvironFallback 25 from gyp.common import GetEnvironFallback
24 import gyp.ninja_syntax as ninja_syntax 26 import gyp.ninja_syntax as ninja_syntax
25 27
26 generator_default_variables = { 28 generator_default_variables = {
27 'EXECUTABLE_PREFIX': '', 29 'EXECUTABLE_PREFIX': '',
(...skipping 621 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 651
650 # Rules can potentially make use of some special variables which 652 # Rules can potentially make use of some special variables which
651 # must vary per source file. 653 # must vary per source file.
652 # Compute the list of variables we'll need to provide. 654 # Compute the list of variables we'll need to provide.
653 special_locals = ('source', 'root', 'dirname', 'ext', 'name') 655 special_locals = ('source', 'root', 'dirname', 'ext', 'name')
654 needed_variables = set(['source']) 656 needed_variables = set(['source'])
655 for argument in args: 657 for argument in args:
656 for var in special_locals: 658 for var in special_locals:
657 if '${%s}' % var in argument: 659 if '${%s}' % var in argument:
658 needed_variables.add(var) 660 needed_variables.add(var)
661 needed_variables = OrderDeterministically(needed_variables)
659 662
660 def cygwin_munge(path): 663 def cygwin_munge(path):
661 # pylint: disable=cell-var-from-loop 664 # pylint: disable=cell-var-from-loop
662 if is_cygwin: 665 if is_cygwin:
663 return path.replace('\\', '/') 666 return path.replace('\\', '/')
664 return path 667 return path
665 668
666 inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])] 669 inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])]
667 670
668 # If there are n source files matching the rule, and m additional rule 671 # If there are n source files matching the rule, and m additional rule
(...skipping 11 matching lines...) Expand all
680 # For each source file, write an edge that generates all the outputs. 683 # For each source file, write an edge that generates all the outputs.
681 for source in sources: 684 for source in sources:
682 source = os.path.normpath(source) 685 source = os.path.normpath(source)
683 dirname, basename = os.path.split(source) 686 dirname, basename = os.path.split(source)
684 root, ext = os.path.splitext(basename) 687 root, ext = os.path.splitext(basename)
685 688
686 # Gather the list of inputs and outputs, expanding $vars if possible. 689 # Gather the list of inputs and outputs, expanding $vars if possible.
687 outputs = [self.ExpandRuleVariables(o, root, dirname, 690 outputs = [self.ExpandRuleVariables(o, root, dirname,
688 source, ext, basename) 691 source, ext, basename)
689 for o in rule['outputs']] 692 for o in rule['outputs']]
690
691 if int(rule.get('process_outputs_as_sources', False)): 693 if int(rule.get('process_outputs_as_sources', False)):
692 extra_sources += outputs 694 extra_sources += outputs
693
694 was_mac_bundle_resource = source in mac_bundle_resources 695 was_mac_bundle_resource = source in mac_bundle_resources
695 if was_mac_bundle_resource or \ 696 if was_mac_bundle_resource or \
696 int(rule.get('process_outputs_as_mac_bundle_resources', False)): 697 int(rule.get('process_outputs_as_mac_bundle_resources', False)):
697 extra_mac_bundle_resources += outputs 698 extra_mac_bundle_resources += outputs
698 # Note: This is n_resources * n_outputs_in_rule. Put to-be-removed 699 # Note: This is n_resources * n_outputs_in_rule. Put to-be-removed
699 # items in a set and remove them all in a single pass if this becomes 700 # items in a set and remove them all in a single pass if this becomes
700 # a performance issue. 701 # a performance issue.
701 if was_mac_bundle_resource: 702 if was_mac_bundle_resource:
702 mac_bundle_resources.remove(source) 703 mac_bundle_resources.remove(source)
703
704 extra_bindings = [] 704 extra_bindings = []
705 for var in needed_variables: 705 for var in needed_variables:
706 if var == 'root': 706 if var == 'root':
707 extra_bindings.append(('root', cygwin_munge(root))) 707 extra_bindings.append(('root', cygwin_munge(root)))
708 elif var == 'dirname': 708 elif var == 'dirname':
709 # '$dirname' is a parameter to the rule action, which means 709 # '$dirname' is a parameter to the rule action, which means
710 # it shouldn't be converted to a Ninja path. But we don't 710 # it shouldn't be converted to a Ninja path. But we don't
711 # want $!PRODUCT_DIR in there either. 711 # want $!PRODUCT_DIR in there either.
712 dirname_expanded = self.ExpandSpecial(dirname, self.base_to_build) 712 dirname_expanded = self.ExpandSpecial(dirname, self.base_to_build)
713 extra_bindings.append(('dirname', cygwin_munge(dirname_expanded))) 713 extra_bindings.append(('dirname', cygwin_munge(dirname_expanded)))
714 elif var == 'source': 714 elif var == 'source':
715 # '$source' is a parameter to the rule action, which means 715 # '$source' is a parameter to the rule action, which means
716 # it shouldn't be converted to a Ninja path. But we don't 716 # it shouldn't be converted to a Ninja path. But we don't
717 # want $!PRODUCT_DIR in there either. 717 # want $!PRODUCT_DIR in there either.
718 source_expanded = self.ExpandSpecial(source, self.base_to_build) 718 source_expanded = self.ExpandSpecial(source, self.base_to_build)
719 extra_bindings.append(('source', cygwin_munge(source_expanded))) 719 extra_bindings.append(('source', cygwin_munge(source_expanded)))
720 elif var == 'ext': 720 elif var == 'ext':
721 extra_bindings.append(('ext', ext)) 721 extra_bindings.append(('ext', ext))
722 elif var == 'name': 722 elif var == 'name':
723 extra_bindings.append(('name', cygwin_munge(basename))) 723 extra_bindings.append(('name', cygwin_munge(basename)))
724 else: 724 else:
725 assert var == None, repr(var) 725 assert var == None, repr(var)
726 726
727 outputs = [self.GypPathToNinja(o, env) for o in outputs] 727 outputs = [self.GypPathToNinja(o, env) for o in outputs]
728 if self.flavor == 'win': 728 if self.flavor == 'win':
729 # WriteNewNinjaRule uses unique_name for creating an rsp file on win. 729 # WriteNewNinjaRule uses unique_name for creating an rsp file on win.
730 extra_bindings.append(('unique_name', 730 extra_bindings.append(('unique_name',
731 hashlib.md5(outputs[0]).hexdigest())) 731 hashlib.md5(outputs[0]).hexdigest()))
732
732 self.ninja.build(outputs, rule_name, self.GypPathToNinja(source), 733 self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
733 implicit=inputs, 734 implicit=inputs,
734 order_only=prebuild, 735 order_only=prebuild,
735 variables=extra_bindings) 736 variables=extra_bindings)
736 737
737 all_outputs.extend(outputs) 738 all_outputs.extend(outputs)
738 739
739 return all_outputs 740 return all_outputs
740 741
741 def WriteCopies(self, copies, prebuild, mac_bundle_depends): 742 def WriteCopies(self, copies, prebuild, mac_bundle_depends):
(...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after
1245 command = command + '_notoc' 1246 command = command + '_notoc'
1246 elif self.flavor == 'win': 1247 elif self.flavor == 'win':
1247 extra_bindings.append(('binary', output)) 1248 extra_bindings.append(('binary', output))
1248 pdbname = self.msvs_settings.GetPDBName( 1249 pdbname = self.msvs_settings.GetPDBName(
1249 config_name, self.ExpandSpecial, output + '.pdb') 1250 config_name, self.ExpandSpecial, output + '.pdb')
1250 if pdbname: 1251 if pdbname:
1251 output = [output, pdbname] 1252 output = [output, pdbname]
1252 1253
1253 1254
1254 if len(solibs): 1255 if len(solibs):
1255 extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs))) 1256 extra_bindings.append(('solibs',
1257 gyp.common.EncodePOSIXShellList(OrderDeterministically(solibs))))
1256 1258
1257 ninja_file.build(output, command + command_suffix, link_deps, 1259 ninja_file.build(output, command + command_suffix, link_deps,
1258 implicit=list(implicit_deps), 1260 implicit=OrderDeterministically(implicit_deps),
1259 order_only=list(order_deps), 1261 order_only=order_deps,
1260 variables=extra_bindings) 1262 variables=extra_bindings)
1261 return linked_binary 1263 return linked_binary
1262 1264
1263 def WriteTarget(self, spec, config_name, config, link_deps, compile_deps): 1265 def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
1264 extra_link_deps = any(self.target_outputs.get(dep).Linkable() 1266 extra_link_deps = any(self.target_outputs.get(dep).Linkable()
1265 for dep in spec.get('dependencies', []) 1267 for dep in spec.get('dependencies', [])
1266 if dep in self.target_outputs) 1268 if dep in self.target_outputs)
1267 if spec['type'] == 'none' or (not link_deps and not extra_link_deps): 1269 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 1270 # 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. 1271 # 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
2330 non_empty_target_names.add(name) 2332 non_empty_target_names.add(name)
2331 else: 2333 else:
2332 empty_target_names.add(name) 2334 empty_target_names.add(name)
2333 2335
2334 if target_short_names: 2336 if target_short_names:
2335 # Write a short name to build this target. This benefits both the 2337 # 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 2338 # "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. 2339 # able to run actions and build libraries by their short name.
2338 master_ninja.newline() 2340 master_ninja.newline()
2339 master_ninja.comment('Short names for targets.') 2341 master_ninja.comment('Short names for targets.')
2340 for short_name in target_short_names: 2342 for short_name in OrderDeterministically(target_short_names):
2341 master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in 2343 master_ninja.build(short_name, 'phony', ([x.FinalOutput() for x in
2342 target_short_names[short_name]]) 2344 target_short_names[short_name]]))
2343 2345
2344 # Write phony targets for any empty targets that weren't written yet. As 2346 # 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 2347 # short names are not necessarily unique only do this for short names that
2346 # haven't already been output for another target. 2348 # haven't already been output for another target.
2347 empty_target_names = empty_target_names - non_empty_target_names 2349 empty_target_names = empty_target_names - non_empty_target_names
2348 if empty_target_names: 2350 if empty_target_names:
2349 master_ninja.newline() 2351 master_ninja.newline()
2350 master_ninja.comment('Empty targets (output for completeness).') 2352 master_ninja.comment('Empty targets (output for completeness).')
2351 for name in sorted(empty_target_names): 2353 for name in OrderDeterministically(empty_target_names):
2352 master_ninja.build(name, 'phony') 2354 master_ninja.build(name, 'phony')
2353 2355
2354 if all_outputs: 2356 if all_outputs:
2355 master_ninja.newline() 2357 master_ninja.newline()
2356 master_ninja.build('all', 'phony', list(all_outputs)) 2358 master_ninja.build('all', 'phony', OrderDeterministically(all_outputs))
2357 master_ninja.default(generator_flags.get('default_target', 'all')) 2359 master_ninja.default(generator_flags.get('default_target', 'all'))
2358 2360
2359 master_ninja_file.close() 2361 master_ninja_file.close()
2360 2362
2361 2363
2362 def PerformBuild(data, configurations, params): 2364 def PerformBuild(data, configurations, params):
2363 options = params['options'] 2365 options = params['options']
2364 for config in configurations: 2366 for config in configurations:
2365 builddir = os.path.join(options.toplevel_dir, 'out', config) 2367 builddir = os.path.join(options.toplevel_dir, 'out', config)
2366 arguments = ['ninja', '-C', builddir] 2368 arguments = ['ninja', '-C', builddir]
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
2401 arglists.append( 2403 arglists.append(
2402 (target_list, target_dicts, data, params, config_name)) 2404 (target_list, target_dicts, data, params, config_name))
2403 pool.map(CallGenerateOutputForConfig, arglists) 2405 pool.map(CallGenerateOutputForConfig, arglists)
2404 except KeyboardInterrupt, e: 2406 except KeyboardInterrupt, e:
2405 pool.terminate() 2407 pool.terminate()
2406 raise e 2408 raise e
2407 else: 2409 else:
2408 for config_name in config_names: 2410 for config_name in config_names:
2409 GenerateOutputForConfig(target_list, target_dicts, data, params, 2411 GenerateOutputForConfig(target_list, target_dicts, data, params,
2410 config_name) 2412 config_name)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698