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

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

Issue 10447063: Fix make and ninja backends to sensibly handle duplicate target names in different directories (Closed) Base URL: http://git.chromium.org/external/gyp.git@master
Patch Set: Created 8 years, 6 months 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
« no previous file with comments | « pylib/gyp/generator/make.py ('k') | pylib/gyp/generator/ninja_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 Google Inc. All rights reserved. 1 # Copyright (c) 2012 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 copy 5 import copy
6 import gyp 6 import gyp
7 import gyp.common 7 import gyp.common
8 import gyp.msvs_emulation 8 import gyp.msvs_emulation
9 import gyp.MSVSVersion 9 import gyp.MSVSVersion
10 import gyp.system_test 10 import gyp.system_test
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 # All paths as written into the .ninja files are relative to the build 182 # All paths as written into the .ninja files are relative to the build
183 # directory. Call these paths "ninja paths". 183 # directory. Call these paths "ninja paths".
184 # 184 #
185 # We translate between these two notions of paths with two helper 185 # We translate between these two notions of paths with two helper
186 # functions: 186 # functions:
187 # 187 #
188 # - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file) 188 # - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file)
189 # into the equivalent ninja path. 189 # into the equivalent ninja path.
190 # 190 #
191 # - GypPathToUniqueOutput translates a gyp path into a ninja path to write 191 # - GypPathToUniqueOutput translates a gyp path into a ninja path to write
192 # an output file; the result can be namespaced such that is unique 192 # an output file; the result can be namespaced such that it is unique
193 # to the input file name as well as the output target name. 193 # to the input file name as well as the output target name.
194 194
195 class NinjaWriter: 195 class NinjaWriter:
196 def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor, 196 def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
197 abs_build_dir=None): 197 output_file, flavor, abs_build_dir=None):
198 """ 198 """
199 base_dir: path from source root to directory containing this gyp file, 199 base_dir: path from source root to directory containing this gyp file,
200 by gyp semantics, all input paths are relative to this 200 by gyp semantics, all input paths are relative to this
201 build_dir: path from source root to build output 201 build_dir: path from source root to build output
202 abs_build_dir: absolute path to the build directory 202 abs_build_dir: absolute path to the build directory
203 """ 203 """
204 204
205 self.qualified_target = qualified_target
205 self.target_outputs = target_outputs 206 self.target_outputs = target_outputs
206 self.base_dir = base_dir 207 self.base_dir = base_dir
207 self.build_dir = build_dir 208 self.build_dir = build_dir
208 self.ninja = ninja_syntax.Writer(output_file) 209 self.ninja = ninja_syntax.Writer(output_file)
209 self.flavor = flavor 210 self.flavor = flavor
210 self.abs_build_dir = abs_build_dir 211 self.abs_build_dir = abs_build_dir
211 self.obj_ext = '.obj' if flavor == 'win' else '.o' 212 self.obj_ext = '.obj' if flavor == 'win' else '.o'
212 213
213 # Relative path from build output dir to base dir. 214 # Relative path from build output dir to base dir.
214 self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir) 215 self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir)
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 if self.is_mac_bundle: 407 if self.is_mac_bundle:
407 mac_bundle_depends.append(output) 408 mac_bundle_depends.append(output)
408 409
409 # Bundle all of the above together, if needed. 410 # Bundle all of the above together, if needed.
410 if self.is_mac_bundle: 411 if self.is_mac_bundle:
411 output = self.WriteMacBundle(spec, mac_bundle_depends) 412 output = self.WriteMacBundle(spec, mac_bundle_depends)
412 413
413 if not output: 414 if not output:
414 return None 415 return None
415 416
416 if self.name != output and self.toolset == 'target':
417 # Write a short name to build this target. This benefits both the
418 # "build chrome" case as well as the gyp tests, which expect to be
419 # able to run actions and build libraries by their short name.
420 self.ninja.build(self.name, 'phony', output)
421
422 assert self.target.FinalOutput(), output 417 assert self.target.FinalOutput(), output
423 return self.target 418 return self.target
424 419
425 def _WinIdlRule(self, source, prebuild, outputs): 420 def _WinIdlRule(self, source, prebuild, outputs):
426 """Handle the implicit VS .idl rule for one source file. Fills |outputs| 421 """Handle the implicit VS .idl rule for one source file. Fills |outputs|
427 with files that are generated.""" 422 with files that are generated."""
428 outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData( 423 outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData(
429 source, self.config_name) 424 source, self.config_name)
430 outdir = self.GypPathToNinja(outdir) 425 outdir = self.GypPathToNinja(outdir)
431 def fix_path(path, rel=None): 426 def fix_path(path, rel=None):
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 496
502 def WriteActions(self, actions, extra_sources, prebuild, 497 def WriteActions(self, actions, extra_sources, prebuild,
503 extra_mac_bundle_resources): 498 extra_mac_bundle_resources):
504 # Actions cd into the base directory. 499 # Actions cd into the base directory.
505 env = self.GetSortedXcodeEnv() 500 env = self.GetSortedXcodeEnv()
506 if self.flavor == 'win': 501 if self.flavor == 'win':
507 env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR') 502 env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR')
508 all_outputs = [] 503 all_outputs = []
509 for action in actions: 504 for action in actions:
510 # First write out a rule for the action. 505 # First write out a rule for the action.
511 name = action['action_name'] 506 name = '%s_%s' % (self.qualified_target, action['action_name'])
Nico 2012/06/08 19:31:02 This will make target 'a' with rule 'b_c' clash wi
Steve Block 2012/06/11 09:33:55 Yes, but I think it's unlikely we'll hit such prob
512 description = self.GenerateDescription('ACTION', 507 description = self.GenerateDescription('ACTION',
513 action.get('message', None), 508 action.get('message', None),
514 name) 509 name)
515 is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action) 510 is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action)
516 if self.flavor == 'win' else False) 511 if self.flavor == 'win' else False)
517 args = action['action'] 512 args = action['action']
518 args = [self.msvs_settings.ConvertVSMacros(arg, self.base_to_build) 513 args = [self.msvs_settings.ConvertVSMacros(arg, self.base_to_build)
519 for arg in args] if self.flavor == 'win' else args 514 for arg in args] if self.flavor == 'win' else args
520 rule_name = self.WriteNewNinjaRule(name, args, description, 515 rule_name = self.WriteNewNinjaRule(name, args, description,
521 is_cygwin, env=env) 516 is_cygwin, env=env)
(...skipping 12 matching lines...) Expand all
534 529
535 self.ninja.newline() 530 self.ninja.newline()
536 531
537 return all_outputs 532 return all_outputs
538 533
539 def WriteRules(self, rules, extra_sources, prebuild, 534 def WriteRules(self, rules, extra_sources, prebuild,
540 extra_mac_bundle_resources): 535 extra_mac_bundle_resources):
541 all_outputs = [] 536 all_outputs = []
542 for rule in rules: 537 for rule in rules:
543 # First write out a rule for the rule action. 538 # First write out a rule for the rule action.
544 name = rule['rule_name'] 539 name = '%s_%s' % (self.qualified_target, rule['rule_name'])
545 # Skip a rule with no action and no inputs. 540 # Skip a rule with no action and no inputs.
546 if 'action' not in rule and not rule.get('rule_sources', []): 541 if 'action' not in rule and not rule.get('rule_sources', []):
547 continue 542 continue
548 args = rule['action'] 543 args = rule['action']
549 description = self.GenerateDescription( 544 description = self.GenerateDescription(
550 'RULE', 545 'RULE',
551 rule.get('message', None), 546 rule.get('message', None),
552 ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name) 547 ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
553 is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule) 548 is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule)
554 if self.flavor == 'win' else False) 549 if self.flavor == 'win' else False)
(...skipping 535 matching lines...) Expand 10 before | Expand all | Expand 10 after
1090 1085
1091 Returns the name of the new rule.""" 1086 Returns the name of the new rule."""
1092 1087
1093 # TODO: we shouldn't need to qualify names; we do it because 1088 # TODO: we shouldn't need to qualify names; we do it because
1094 # currently the ninja rule namespace is global, but it really 1089 # currently the ninja rule namespace is global, but it really
1095 # should be scoped to the subninja. 1090 # should be scoped to the subninja.
1096 rule_name = self.name 1091 rule_name = self.name
1097 if self.toolset == 'target': 1092 if self.toolset == 'target':
1098 rule_name += '.' + self.toolset 1093 rule_name += '.' + self.toolset
1099 rule_name += '.' + name 1094 rule_name += '.' + name
1100 rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name) 1095 rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name)
Nico 2012/06/08 19:31:02 Likewise, one issue with this is that 'a+b' and 'a
1101 1096
1102 args = args[:] 1097 args = args[:]
1103 1098
1104 if self.flavor == 'win': 1099 if self.flavor == 'win':
1105 description = self.msvs_settings.ConvertVSMacros(description) 1100 description = self.msvs_settings.ConvertVSMacros(description)
1106 1101
1107 # gyp dictates that commands are run from the base directory. 1102 # gyp dictates that commands are run from the base directory.
1108 # cd into the directory before running, and adjust paths in 1103 # cd into the directory before running, and adjust paths in
1109 # the arguments to point to the proper locations. 1104 # the arguments to point to the proper locations.
1110 if self.flavor == 'win': 1105 if self.flavor == 'win':
(...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after
1480 all_targets = set() 1475 all_targets = set()
1481 for build_file in params['build_files']: 1476 for build_file in params['build_files']:
1482 for target in gyp.common.AllTargets(target_list, 1477 for target in gyp.common.AllTargets(target_list,
1483 target_dicts, 1478 target_dicts,
1484 os.path.normpath(build_file)): 1479 os.path.normpath(build_file)):
1485 all_targets.add(target) 1480 all_targets.add(target)
1486 all_outputs = set() 1481 all_outputs = set()
1487 1482
1488 # target_outputs is a map from qualified target name to a Target object. 1483 # target_outputs is a map from qualified target name to a Target object.
1489 target_outputs = {} 1484 target_outputs = {}
1485 # target_short_names is a map from target short name to a list of Target
1486 # objects.
1487 target_short_names = {}
1490 for qualified_target in target_list: 1488 for qualified_target in target_list:
1491 # qualified_target is like: third_party/icu/icu.gyp:icui18n#target 1489 # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
1492 build_file, name, toolset = \ 1490 build_file, name, toolset = \
1493 gyp.common.ParseQualifiedTarget(qualified_target) 1491 gyp.common.ParseQualifiedTarget(qualified_target)
1494 1492
1495 this_make_global_settings = data[build_file].get('make_global_settings', []) 1493 this_make_global_settings = data[build_file].get('make_global_settings', [])
1496 assert make_global_settings == this_make_global_settings, ( 1494 assert make_global_settings == this_make_global_settings, (
1497 "make_global_settings needs to be the same for all targets.") 1495 "make_global_settings needs to be the same for all targets.")
1498 1496
1499 spec = target_dicts[qualified_target] 1497 spec = target_dicts[qualified_target]
1500 if flavor == 'mac': 1498 if flavor == 'mac':
1501 gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec) 1499 gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
1502 1500
1503 build_file = gyp.common.RelativePath(build_file, options.toplevel_dir) 1501 build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
1504 1502
1505 base_path = os.path.dirname(build_file) 1503 base_path = os.path.dirname(build_file)
1506 obj = 'obj' 1504 obj = 'obj'
1507 if toolset != 'target': 1505 if toolset != 'target':
1508 obj += '.' + toolset 1506 obj += '.' + toolset
1509 output_file = os.path.join(obj, base_path, name + '.ninja') 1507 output_file = os.path.join(obj, base_path, name + '.ninja')
1510 1508
1511 abs_build_dir = os.path.abspath(toplevel_build) 1509 abs_build_dir = os.path.abspath(toplevel_build)
1512 writer = NinjaWriter(target_outputs, base_path, build_dir, 1510 writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
1513 OpenOutput(os.path.join(toplevel_build, output_file)), 1511 OpenOutput(os.path.join(toplevel_build, output_file)),
1514 flavor, abs_build_dir=abs_build_dir) 1512 flavor, abs_build_dir=abs_build_dir)
1515 master_ninja.subninja(output_file) 1513 master_ninja.subninja(output_file)
1516 1514
1517 target = writer.WriteSpec(spec, config_name, generator_flags) 1515 target = writer.WriteSpec(spec, config_name, generator_flags)
1518 if target: 1516 if target:
1517 if name != target.FinalOutput() and spec['toolset'] == 'target':
1518 target_short_names.setdefault(name, []).append(target)
1519 target_outputs[qualified_target] = target 1519 target_outputs[qualified_target] = target
1520 if qualified_target in all_targets: 1520 if qualified_target in all_targets:
1521 all_outputs.add(target.FinalOutput()) 1521 all_outputs.add(target.FinalOutput())
1522 1522
1523 if target_short_names:
1524 # Write a short name to build this target. This benefits both the
1525 # "build chrome" case as well as the gyp tests, which expect to be
1526 # able to run actions and build libraries by their short name.
1527 master_ninja.newline()
1528 master_ninja.comment('Short names for targets.')
1529 for short_name in target_short_names:
1530 master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
1531 target_short_names[short_name]])
1532
1523 if all_outputs: 1533 if all_outputs:
1524 master_ninja.newline() 1534 master_ninja.newline()
1525 master_ninja.build('all', 'phony', list(all_outputs)) 1535 master_ninja.build('all', 'phony', list(all_outputs))
1526 master_ninja.default('all') 1536 master_ninja.default('all')
1527 1537
1528 1538
1529 def GenerateOutput(target_list, target_dicts, data, params): 1539 def GenerateOutput(target_list, target_dicts, data, params):
1530 if params['options'].generator_output: 1540 if params['options'].generator_output:
1531 raise NotImplementedError, "--generator_output not implemented for ninja" 1541 raise NotImplementedError, "--generator_output not implemented for ninja"
1532 1542
1533 user_config = params.get('generator_flags', {}).get('config', None) 1543 user_config = params.get('generator_flags', {}).get('config', None)
1534 if user_config: 1544 if user_config:
1535 GenerateOutputForConfig(target_list, target_dicts, data, params, 1545 GenerateOutputForConfig(target_list, target_dicts, data, params,
1536 user_config) 1546 user_config)
1537 else: 1547 else:
1538 config_names = target_dicts[target_list[0]]['configurations'].keys() 1548 config_names = target_dicts[target_list[0]]['configurations'].keys()
1539 for config_name in config_names: 1549 for config_name in config_names:
1540 GenerateOutputForConfig(target_list, target_dicts, data, params, 1550 GenerateOutputForConfig(target_list, target_dicts, data, params,
1541 config_name) 1551 config_name)
OLDNEW
« no previous file with comments | « pylib/gyp/generator/make.py ('k') | pylib/gyp/generator/ninja_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698