summaryrefslogtreecommitdiff
path: root/tools/gyp/pylib/gyp/generator/ninja.py
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2012-02-20 11:47:03 +0100
committerBen Noordhuis <info@bnoordhuis.nl>2012-02-20 11:47:03 +0100
commit4af673e161138fbed287a8e00cf7b2b0aafdd8b3 (patch)
tree90fb82bb98fdbcbb0c86c446e029c13bf999c8b0 /tools/gyp/pylib/gyp/generator/ninja.py
parent7ae0d473a6b6ac8cb3e55d665528667566cd8e60 (diff)
downloadandroid-node-v8-4af673e161138fbed287a8e00cf7b2b0aafdd8b3.tar.gz
android-node-v8-4af673e161138fbed287a8e00cf7b2b0aafdd8b3.tar.bz2
android-node-v8-4af673e161138fbed287a8e00cf7b2b0aafdd8b3.zip
gyp: update to r1214
Diffstat (limited to 'tools/gyp/pylib/gyp/generator/ninja.py')
-rw-r--r--tools/gyp/pylib/gyp/generator/ninja.py869
1 files changed, 643 insertions, 226 deletions
diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py
index 193f295a55..8d6c6f52d1 100644
--- a/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/tools/gyp/pylib/gyp/generator/ninja.py
@@ -1,12 +1,14 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import copy
import gyp
import gyp.common
import gyp.system_test
+import gyp.xcode_emulation
import os.path
-import pprint
+import re
import subprocess
import sys
@@ -15,7 +17,7 @@ import gyp.ninja_syntax as ninja_syntax
generator_default_variables = {
'EXECUTABLE_PREFIX': '',
'EXECUTABLE_SUFFIX': '',
- 'STATIC_LIB_PREFIX': '',
+ 'STATIC_LIB_PREFIX': 'lib',
'STATIC_LIB_SUFFIX': '.a',
'SHARED_LIB_PREFIX': 'lib',
@@ -30,8 +32,6 @@ generator_default_variables = {
'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR',
'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen',
'PRODUCT_DIR': '$!PRODUCT_DIR',
- 'SHARED_LIB_DIR': '$!PRODUCT_DIR/lib',
- 'LIB_DIR': '',
# Special variables that may be used by gyp 'rule' targets.
# We generate definitions for these variables on the fly when processing a
@@ -57,6 +57,12 @@ def StripPrefix(arg, prefix):
def QuoteShellArgument(arg):
+ """Quote a string such that it will be interpreted as a single argument
+ by the shell."""
+ # Rather than attempting to enumerate the bad shell characters, just
+ # whitelist common OK ones and quote anything else.
+ if re.match(r'^[a-zA-Z0-9_=-]+$', arg):
+ return arg # No quoting necessary.
return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
@@ -71,8 +77,68 @@ def InvertRelativePath(path):
return path
# Only need to handle relative paths into subdirectories for now.
assert '..' not in path, path
- depth = len(path.split('/'))
- return '/'.join(['..'] * depth)
+ depth = len(path.split(os.path.sep))
+ return os.path.sep.join(['..'] * depth)
+
+
+class Target:
+ """Target represents the paths used within a single gyp target.
+
+ Conceptually, building a single target A is a series of steps:
+
+ 1) actions/rules/copies generates source/resources/etc.
+ 2) compiles generates .o files
+ 3) link generates a binary (library/executable)
+ 4) bundle merges the above in a mac bundle
+
+ (Any of these steps can be optional.)
+
+ From a build ordering perspective, a dependent target B could just
+ depend on the last output of this series of steps.
+
+ But some dependent commands sometimes need to reach inside the box.
+ For example, when linking B it needs to get the path to the static
+ library generated by A.
+
+ This object stores those paths. To keep things simple, member
+ variables only store concrete paths to single files, while methods
+ compute derived values like "the last output of the target".
+ """
+ def __init__(self, type):
+ # Gyp type ("static_library", etc.) of this target.
+ self.type = type
+ # File representing whether any input dependencies necessary for
+ # dependent actions have completed.
+ self.preaction_stamp = None
+ # File representing whether any input dependencies necessary for
+ # dependent compiles have completed.
+ self.precompile_stamp = None
+ # File representing the completion of actions/rules/copies, if any.
+ self.actions_stamp = None
+ # Path to the output of the link step, if any.
+ self.binary = None
+ # Path to the file representing the completion of building the bundle,
+ # if any.
+ self.bundle = None
+
+ def Linkable(self):
+ """Return true if this is a target that can be linked against."""
+ return self.type in ('static_library', 'shared_library')
+
+ def PreActionInput(self):
+ """Return the path, if any, that should be used as a dependency of
+ any dependent action step."""
+ return self.FinalOutput() or self.preaction_stamp
+
+ def PreCompileInput(self):
+ """Return the path, if any, that should be used as a dependency of
+ any dependent compile step."""
+ return self.actions_stamp or self.precompile_stamp
+
+ def FinalOutput(self):
+ """Return the last output of the target, which depends on all prior
+ steps."""
+ return self.bundle or self.binary or self.actions_stamp
# A small discourse on paths as used within the Ninja build:
@@ -100,11 +166,13 @@ def InvertRelativePath(path):
# to the input file name as well as the output target name.
class NinjaWriter:
- def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor):
+ def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor,
+ abs_build_dir=None):
"""
base_dir: path from source root to directory containing this gyp file,
by gyp semantics, all input paths are relative to this
build_dir: path from source root to build output
+ abs_build_dir: absolute path to the build directory
"""
self.target_outputs = target_outputs
@@ -112,6 +180,8 @@ class NinjaWriter:
self.build_dir = build_dir
self.ninja = ninja_syntax.Writer(output_file)
self.flavor = flavor
+ self.abs_build_dir = abs_build_dir
+ self.obj_ext = '.obj' if flavor == 'win' else '.o'
# Relative path from build output dir to base dir.
self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir)
@@ -132,6 +202,7 @@ class NinjaWriter:
path = path.replace(PRODUCT_DIR, product_dir)
else:
path = path.replace(PRODUCT_DIR + '/', '')
+ path = path.replace(PRODUCT_DIR + '\\', '')
path = path.replace(PRODUCT_DIR, '.')
INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR'
@@ -142,7 +213,7 @@ class NinjaWriter:
path = path.replace(INTERMEDIATE_DIR,
os.path.join(product_dir or '', int_dir))
- return path
+ return os.path.normpath(path)
def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root)
@@ -153,10 +224,13 @@ class NinjaWriter:
path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name)
return path
- def GypPathToNinja(self, path):
- """Translate a gyp path to a ninja path.
+ def GypPathToNinja(self, path, env=None):
+ """Translate a gyp path to a ninja path, optionally expanding environment
+ variable references in |path| with |env|.
See the above discourse on path conversions."""
+ if env:
+ path = gyp.xcode_emulation.ExpandEnvVars(path, env)
if path.startswith('$!'):
return self.ExpandSpecial(path)
assert '$' not in path, path
@@ -196,34 +270,38 @@ class NinjaWriter:
path_basename))
def WriteCollapsedDependencies(self, name, targets):
- """Given a list of targets, return a dependency list for a single
- file representing the result of building all the targets.
+ """Given a list of targets, return a path for a single file
+ representing the result of building all the targets or None.
Uses a stamp file if necessary."""
+ assert targets == filter(None, targets), targets
+ if len(targets) == 0:
+ return None
if len(targets) > 1:
stamp = self.GypPathToUniqueOutput(name + '.stamp')
targets = self.ninja.build(stamp, 'stamp', targets)
self.ninja.newline()
- return targets
+ return targets[0]
- def WriteSpec(self, spec, config):
+ def WriteSpec(self, spec, config_name):
"""The main entry point for NinjaWriter: write the build rules for a spec.
- Returns the path to the build output, or None, and a list of targets for
- dependencies of its compile steps."""
+ Returns a Target object, which represents the output paths for this spec.
+ Returns None if there are no outputs (e.g. a settings-only 'none' type
+ target)."""
+ self.config_name = config_name
self.name = spec['target_name']
self.toolset = spec['toolset']
+ config = spec['configurations'][config_name]
+ self.target = Target(spec['type'])
- if spec['type'] == 'settings':
- # TODO: 'settings' is not actually part of gyp; it was
- # accidentally introduced somehow into just the Linux build files.
- # Remove this (or make it an error) once all the users are fixed.
- print ("WARNING: %s uses invalid type 'settings'. " % self.name +
- "Please fix the source gyp file to use type 'none'.")
- print "See http://code.google.com/p/chromium/issues/detail?id=96629 ."
- spec['type'] = 'none'
+ self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
+ if self.flavor == 'mac':
+ self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
+ else:
+ self.xcode_settings = None
# Compute predepends for all rules.
# actions_depends is the dependencies this target depends on before running
@@ -232,66 +310,99 @@ class NinjaWriter:
# any of its compile steps.
actions_depends = []
compile_depends = []
+ # TODO(evan): it is rather confusing which things are lists and which
+ # are strings. Fix these.
if 'dependencies' in spec:
for dep in spec['dependencies']:
if dep in self.target_outputs:
- input, precompile_input, linkable = self.target_outputs[dep]
- actions_depends.append(input)
- compile_depends.extend(precompile_input)
+ target = self.target_outputs[dep]
+ actions_depends.append(target.PreActionInput())
+ compile_depends.append(target.PreCompileInput())
+ actions_depends = filter(None, actions_depends)
+ compile_depends = filter(None, compile_depends)
actions_depends = self.WriteCollapsedDependencies('actions_depends',
actions_depends)
+ compile_depends = self.WriteCollapsedDependencies('compile_depends',
+ compile_depends)
+ self.target.preaction_stamp = actions_depends
+ self.target.precompile_stamp = compile_depends
# Write out actions, rules, and copies. These must happen before we
# compile any sources, so compute a list of predependencies for sources
# while we do it.
extra_sources = []
- sources_depends = self.WriteActionsRulesCopies(spec, extra_sources,
- actions_depends)
+ mac_bundle_depends = []
+ self.target.actions_stamp = self.WriteActionsRulesCopies(
+ spec, extra_sources, actions_depends, mac_bundle_depends)
# If we have actions/rules/copies, we depend directly on those, but
# otherwise we depend on dependent target's actions/rules/copies etc.
# We never need to explicitly depend on previous target's link steps,
# because no compile ever depends on them.
- compile_depends = self.WriteCollapsedDependencies('compile_depends',
- sources_depends or compile_depends)
+ compile_depends_stamp = (self.target.actions_stamp or compile_depends)
# Write out the compilation steps, if any.
link_deps = []
sources = spec.get('sources', []) + extra_sources
if sources:
- link_deps = self.WriteSources(config, sources, compile_depends)
+ link_deps = self.WriteSources(
+ config_name, config, sources, compile_depends_stamp,
+ gyp.xcode_emulation.MacPrefixHeader(
+ self.xcode_settings, self.GypPathToNinja,
+ lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang)))
# Some actions/rules output 'sources' that are already object files.
- link_deps += [self.GypPathToNinja(f) for f in sources if f.endswith('.o')]
+ link_deps += [self.GypPathToNinja(f)
+ for f in sources if f.endswith(self.obj_ext)]
- # The final output of our target depends on the last output of the
- # above steps.
+ # Write out a link step, if needed.
output = None
- final_deps = link_deps or sources_depends or actions_depends
- if final_deps:
- output = self.WriteTarget(spec, config, final_deps,
- order_only=actions_depends)
- if self.name != output and self.toolset == 'target':
- # Write a short name to build this target. This benefits both the
- # "build chrome" case as well as the gyp tests, which expect to be
- # able to run actions and build libraries by their short name.
- self.ninja.build(self.name, 'phony', output)
- return output, compile_depends
-
- def WriteActionsRulesCopies(self, spec, extra_sources, prebuild):
- """Write out the Actions, Rules, and Copies steps. Return any outputs
- of these steps (or a stamp file if there are lots of outputs)."""
+ if link_deps or self.target.actions_stamp or actions_depends:
+ output = self.WriteTarget(spec, config_name, config, link_deps,
+ self.target.actions_stamp or actions_depends)
+ if self.is_mac_bundle:
+ mac_bundle_depends.append(output)
+
+ # Bundle all of the above together, if needed.
+ if self.is_mac_bundle:
+ output = self.WriteMacBundle(spec, mac_bundle_depends)
+
+ if not output:
+ return None
+
+ if self.name != output and self.toolset == 'target':
+ # Write a short name to build this target. This benefits both the
+ # "build chrome" case as well as the gyp tests, which expect to be
+ # able to run actions and build libraries by their short name.
+ self.ninja.build(self.name, 'phony', output)
+
+ assert self.target.FinalOutput(), output
+ return self.target
+
+ def WriteActionsRulesCopies(self, spec, extra_sources, prebuild,
+ mac_bundle_depends):
+ """Write out the Actions, Rules, and Copies steps. Return a path
+ representing the outputs of these steps."""
outputs = []
+ extra_mac_bundle_resources = []
if 'actions' in spec:
- outputs += self.WriteActions(spec['actions'], extra_sources, prebuild)
+ outputs += self.WriteActions(spec['actions'], extra_sources, prebuild,
+ extra_mac_bundle_resources)
if 'rules' in spec:
- outputs += self.WriteRules(spec['rules'], extra_sources, prebuild)
+ outputs += self.WriteRules(spec['rules'], extra_sources, prebuild,
+ extra_mac_bundle_resources)
if 'copies' in spec:
outputs += self.WriteCopies(spec['copies'], prebuild)
- outputs = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
+ stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
- return outputs
+ if self.is_mac_bundle:
+ mac_bundle_resources = spec.get('mac_bundle_resources', []) + \
+ extra_mac_bundle_resources
+ self.WriteMacBundleResources(mac_bundle_resources, mac_bundle_depends)
+ self.WriteMacInfoPlist(mac_bundle_depends)
+
+ return stamp
def GenerateDescription(self, verb, message, fallback):
"""Generate and return a description of a build step.
@@ -307,20 +418,26 @@ class NinjaWriter:
else:
return '%s %s: %s' % (verb, self.name, fallback)
- def WriteActions(self, actions, extra_sources, prebuild):
+ def WriteActions(self, actions, extra_sources, prebuild,
+ extra_mac_bundle_resources):
+ # Actions cd into the base directory.
+ env = self.GetXcodeEnv()
all_outputs = []
for action in actions:
# First write out a rule for the action.
- name = action['action_name']
+ name = re.sub(r'[ {}$]', '_', action['action_name'])
description = self.GenerateDescription('ACTION',
action.get('message', None),
name)
- rule_name = self.WriteNewNinjaRule(name, action['action'], description)
+ rule_name = self.WriteNewNinjaRule(name, action['action'], description,
+ env=env)
- inputs = [self.GypPathToNinja(i) for i in action['inputs']]
+ inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
if int(action.get('process_outputs_as_sources', False)):
extra_sources += action['outputs']
- outputs = [self.GypPathToNinja(o) for o in action['outputs']]
+ if int(action.get('process_outputs_as_mac_bundle_resources', False)):
+ extra_mac_bundle_resources += action['outputs']
+ outputs = [self.GypPathToNinja(o, env) for o in action['outputs']]
# Then write out an edge using the rule.
self.ninja.build(outputs, rule_name, inputs,
@@ -331,7 +448,8 @@ class NinjaWriter:
return all_outputs
- def WriteRules(self, rules, extra_sources, prebuild):
+ def WriteRules(self, rules, extra_sources, prebuild,
+ extra_mac_bundle_resources):
all_outputs = []
for rule in rules:
# First write out a rule for the rule action.
@@ -369,6 +487,8 @@ class NinjaWriter:
if int(rule.get('process_outputs_as_sources', False)):
extra_sources += outputs
+ if int(rule.get('process_outputs_as_mac_bundle_resources', False)):
+ extra_mac_bundle_resources += outputs
extra_bindings = []
for var in needed_variables:
@@ -402,31 +522,67 @@ class NinjaWriter:
def WriteCopies(self, copies, prebuild):
outputs = []
+ env = self.GetXcodeEnv()
for copy in copies:
for path in copy['files']:
# Normalize the path so trailing slashes don't confuse us.
path = os.path.normpath(path)
basename = os.path.split(path)[1]
- src = self.GypPathToNinja(path)
- dst = self.GypPathToNinja(os.path.join(copy['destination'], basename))
- outputs += self.ninja.build(dst, 'copy', src,
- order_only=prebuild)
+ src = self.GypPathToNinja(path, env)
+ dst = self.GypPathToNinja(os.path.join(copy['destination'], basename),
+ env)
+ outputs += self.ninja.build(dst, 'copy', src, order_only=prebuild)
return outputs
- def WriteSources(self, config, sources, predepends):
+ def WriteMacBundleResources(self, resources, bundle_depends):
+ """Writes ninja edges for 'mac_bundle_resources'."""
+ for output, res in gyp.xcode_emulation.GetMacBundleResources(
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
+ self.xcode_settings, map(self.GypPathToNinja, resources)):
+ self.ninja.build(output, 'mac_tool', res,
+ variables=[('mactool_cmd', 'copy-bundle-resource')])
+ bundle_depends.append(output)
+
+ def WriteMacInfoPlist(self, bundle_depends):
+ """Write build rules for bundle Info.plist files."""
+ info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
+ self.xcode_settings, self.GypPathToNinja)
+ if not info_plist:
+ return
+ if defines:
+ # Create an intermediate file to store preprocessed results.
+ intermediate_plist = self.GypPathToUniqueOutput(
+ os.path.basename(info_plist))
+ defines = ' '.join(
+ [QuoteShellArgument(ninja_syntax.escape('-D' + d)) for d in defines])
+ info_plist = self.ninja.build(intermediate_plist, 'infoplist', info_plist,
+ variables=[('defines',defines)])
+
+ env = self.GetXcodeEnv(additional_settings=extra_env)
+ env = self.ComputeExportEnvString(env)
+
+ self.ninja.build(out, 'mac_tool', info_plist,
+ variables=[('mactool_cmd', 'copy-info-plist'),
+ ('env', env)])
+ bundle_depends.append(out)
+
+ def WriteSources(self, config_name, config, sources, predepends,
+ precompiled_header):
"""Write build rules to compile all of |sources|."""
if self.toolset == 'host':
self.ninja.variable('cc', '$cc_host')
self.ninja.variable('cxx', '$cxx_host')
if self.flavor == 'mac':
- # TODO(jeremya/thakis): Extract these from XcodeSettings instead.
- cflags = []
- cflags_c = []
- cflags_cc = []
- cflags_objc = []
- cflags_objcc = []
+ cflags = self.xcode_settings.GetCflags(config_name)
+ cflags_c = self.xcode_settings.GetCflagsC(config_name)
+ cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
+ cflags_objc = ['$cflags_c'] + \
+ self.xcode_settings.GetCflagsObjC(config_name)
+ cflags_objcc = ['$cflags_cc'] + \
+ self.xcode_settings.GetCflagsObjCC(config_name)
else:
cflags = config.get('cflags', [])
cflags_c = config.get('cflags_c', [])
@@ -438,14 +594,26 @@ class NinjaWriter:
self.WriteVariableList('includes',
['-I' + self.GypPathToNinja(i)
for i in config.get('include_dirs', [])])
+
+ pch_commands = precompiled_header.GetGchBuildCommands()
+ if self.flavor == 'mac':
+ self.WriteVariableList('cflags_pch_c',
+ [precompiled_header.GetInclude('c')])
+ self.WriteVariableList('cflags_pch_cc',
+ [precompiled_header.GetInclude('cc')])
+ self.WriteVariableList('cflags_pch_objc',
+ [precompiled_header.GetInclude('m')])
+ self.WriteVariableList('cflags_pch_objcc',
+ [precompiled_header.GetInclude('mm')])
+
self.WriteVariableList('cflags', map(self.ExpandSpecial, cflags))
self.WriteVariableList('cflags_c', map(self.ExpandSpecial, cflags_c))
self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc))
if self.flavor == 'mac':
self.WriteVariableList('cflags_objc', map(self.ExpandSpecial,
- cflags_objc))
+ cflags_objc))
self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial,
- cflags_objcc))
+ cflags_objcc))
self.ninja.newline()
outputs = []
for source in sources:
@@ -463,106 +631,232 @@ class NinjaWriter:
# TODO: should we assert here on unexpected extensions?
continue
input = self.GypPathToNinja(source)
- output = self.GypPathToUniqueOutput(filename + '.o')
+ output = self.GypPathToUniqueOutput(filename + self.obj_ext)
+ implicit = precompiled_header.GetObjDependencies([input], [output])
self.ninja.build(output, command, input,
+ implicit=[gch for _, _, gch in implicit],
order_only=predepends)
outputs.append(output)
+
+ self.WritePchTargets(pch_commands)
+
self.ninja.newline()
return outputs
- def WriteTarget(self, spec, config, final_deps, order_only):
- if spec['type'] == 'none':
- # This target doesn't have any explicit final output, but is instead
- # used for its effects before the final output (e.g. copies steps).
- # Reuse the existing output if it's easy.
- if len(final_deps) == 1:
- return final_deps[0]
- # Otherwise, fall through to writing out a stamp file.
+ def WritePchTargets(self, pch_commands):
+ """Writes ninja rules to compile prefix headers."""
+ if not pch_commands:
+ return
+
+ for gch, lang_flag, lang, input in pch_commands:
+ var_name = {
+ 'c': 'cflags_pch_c',
+ 'cc': 'cflags_pch_cc',
+ 'm': 'cflags_pch_objc',
+ 'mm': 'cflags_pch_objcc',
+ }[lang]
+
+ cmd = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }.get(lang)
+ self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)])
- output = self.ComputeOutput(spec)
- output_uses_linker = spec['type'] in ('executable', 'loadable_module',
- 'shared_library')
+ def WriteLink(self, spec, config_name, config, link_deps):
+ """Write out a link step. Returns the path to the output."""
+
+ command = {
+ 'executable': 'link',
+ 'loadable_module': 'solink_module',
+ 'shared_library': 'solink',
+ }[spec['type']]
implicit_deps = set()
+
if 'dependencies' in spec:
# Two kinds of dependencies:
# - Linkable dependencies (like a .a or a .so): add them to the link line.
# - Non-linkable dependencies (like a rule that generates a file
# and writes a stamp file): add them to implicit_deps
- if output_uses_linker:
- extra_deps = set()
- for dep in spec['dependencies']:
- input, _, linkable = self.target_outputs.get(dep, (None, [], False))
- if not input:
- continue
- if linkable:
- extra_deps.add(input)
- else:
- # TODO: Chrome-specific HACK. Chrome runs this lastchange rule on
- # every build, but we don't want to rebuild when it runs.
- if 'lastchange' not in input:
- implicit_deps.add(input)
- final_deps.extend(list(extra_deps))
- command_map = {
- 'executable': 'link',
- 'static_library': 'alink',
- 'loadable_module': 'solink_module',
- 'shared_library': 'solink',
- 'none': 'stamp',
- }
- command = command_map[spec['type']]
-
- if output_uses_linker:
- if self.flavor == 'mac':
- # TODO(jeremya/thakis): Get this from XcodeSettings.
- ldflags = []
- else:
- ldflags = config.get('ldflags', [])
- self.WriteVariableList('ldflags',
- gyp.common.uniquer(map(self.ExpandSpecial,
- ldflags)))
- self.WriteVariableList('libs',
- gyp.common.uniquer(map(self.ExpandSpecial,
- spec.get('libraries', []))))
+ extra_link_deps = set()
+ for dep in spec['dependencies']:
+ target = self.target_outputs.get(dep)
+ if not target:
+ continue
+ linkable = target.Linkable()
+ if linkable:
+ extra_link_deps.add(target.binary)
+
+ final_output = target.FinalOutput()
+ if not linkable or final_output != target.binary:
+ implicit_deps.add(final_output)
+
+ link_deps.extend(list(extra_link_deps))
extra_bindings = []
+ if self.is_mac_bundle:
+ output = self.ComputeMacBundleBinaryOutput()
+ else:
+ output = self.ComputeOutput(spec)
+ extra_bindings.append(('postbuilds',
+ self.GetPostbuildCommand(spec, output, output)))
+
+ if self.flavor == 'mac':
+ ldflags = self.xcode_settings.GetLdflags(config_name,
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
+ self.GypPathToNinja)
+ else:
+ ldflags = config.get('ldflags', [])
+ self.WriteVariableList('ldflags',
+ gyp.common.uniquer(map(self.ExpandSpecial,
+ ldflags)))
+
+ libraries = gyp.common.uniquer(map(self.ExpandSpecial,
+ spec.get('libraries', [])))
+ if self.flavor == 'mac':
+ libraries = self.xcode_settings.AdjustLibraries(libraries)
+ self.WriteVariableList('libs', libraries)
+
if command in ('solink', 'solink_module'):
extra_bindings.append(('soname', os.path.split(output)[1]))
- self.ninja.build(output, command, final_deps,
+ self.ninja.build(output, command, link_deps,
implicit=list(implicit_deps),
- order_only=order_only,
variables=extra_bindings)
+ return output
+ def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
+ if spec['type'] == 'none':
+ # TODO(evan): don't call this function for 'none' target types, as
+ # it doesn't do anything, and we fake out a 'binary' with a stamp file.
+ self.target.binary = compile_deps
+ elif spec['type'] == 'static_library':
+ self.target.binary = self.ComputeOutput(spec)
+ self.ninja.build(self.target.binary, 'alink', link_deps,
+ order_only=compile_deps,
+ variables=[('postbuilds', self.GetPostbuildCommand(
+ spec, self.target.binary, self.target.binary))])
+ else:
+ self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
+ return self.target.binary
+
+ def WriteMacBundle(self, spec, mac_bundle_depends):
+ assert self.is_mac_bundle
+ package_framework = spec['type'] in ('shared_library', 'loadable_module')
+ output = self.ComputeMacBundleOutput()
+ postbuild = self.GetPostbuildCommand(spec, output, self.target.binary,
+ is_command_start=not package_framework)
+ variables = []
+ if postbuild:
+ variables.append(('postbuilds', postbuild))
+ if package_framework:
+ variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
+ self.ninja.build(output, 'package_framework', mac_bundle_depends,
+ variables=variables)
+ else:
+ self.ninja.build(output, 'stamp', mac_bundle_depends,
+ variables=variables)
+ self.target.bundle = output
return output
- def ComputeOutputFileName(self, spec):
+ def GetXcodeEnv(self, additional_settings=None):
+ """Returns the variables Xcode would set for build steps."""
+ assert self.abs_build_dir
+ abs_build_dir = self.abs_build_dir
+ return gyp.xcode_emulation.GetXcodeEnv(
+ self.xcode_settings, abs_build_dir,
+ os.path.join(abs_build_dir, self.build_to_base), self.config_name,
+ additional_settings)
+
+ def GetXcodePostbuildEnv(self):
+ """Returns the variables Xcode would set for postbuild steps."""
+ postbuild_settings = {}
+ # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
+ # TODO(thakis): It would be nice to have some general mechanism instead.
+ strip_save_file = self.xcode_settings.GetPerTargetSetting(
+ 'CHROMIUM_STRIP_SAVE_FILE')
+ if strip_save_file:
+ postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = self.GypPathToNinja(
+ strip_save_file)
+ return self.GetXcodeEnv(additional_settings=postbuild_settings)
+
+ def GetPostbuildCommand(self, spec, output, output_binary,
+ is_command_start=False):
+ """Returns a shell command that runs all the postbuilds, and removes
+ |output| if any of them fails. If |is_command_start| is False, then the
+ returned string will start with ' && '."""
+ if not self.xcode_settings or spec['type'] == 'none' or not output:
+ return ''
+ output = QuoteShellArgument(output)
+ target_postbuilds = self.xcode_settings.GetTargetPostbuilds(
+ self.config_name, output, QuoteShellArgument(output_binary), quiet=True)
+ postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(
+ spec, self.GypPathToNinja, quiet=True)
+ postbuilds = target_postbuilds + postbuilds
+ if not postbuilds:
+ return ''
+ env = self.ComputeExportEnvString(self.GetXcodePostbuildEnv())
+ commands = env + ' F=0; ' + \
+ ' '.join([ninja_syntax.escape(command) + ' || F=$$?;'
+ for command in postbuilds])
+ command_string = env + commands + ' ((exit $$F) || rm -rf %s) ' % output + \
+ '&& exit $$F)'
+ if is_command_start:
+ return '(' + command_string + ' && '
+ else:
+ return '$ && (' + command_string
+
+ def ComputeExportEnvString(self, env):
+ """Given an environment, returns a string looking like
+ 'export FOO=foo; export BAR="${FOO} bar;'
+ that exports |env| to the shell."""
+ export_str = []
+ for k in gyp.xcode_emulation.TopologicallySortedEnvVarKeys(env):
+ export_str.append('export %s=%s;' %
+ (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(env[k]))))
+ return ' '.join(export_str)
+
+ def ComputeMacBundleOutput(self):
+ """Return the 'output' (full output path) to a bundle output directory."""
+ assert self.is_mac_bundle
+ path = self.ExpandSpecial(generator_default_variables['PRODUCT_DIR'])
+ return os.path.join(path, self.xcode_settings.GetWrapperName())
+
+ def ComputeMacBundleBinaryOutput(self):
+ """Return the 'output' (full output path) to the binary in a bundle."""
+ assert self.is_mac_bundle
+ path = self.ExpandSpecial(generator_default_variables['PRODUCT_DIR'])
+ return os.path.join(path, self.xcode_settings.GetExecutablePath())
+
+ def ComputeOutputFileName(self, spec, type=None):
"""Compute the filename of the final output for the current target."""
+ if not type:
+ type = spec['type']
+
+ default_variables = copy.copy(generator_default_variables)
+ CalculateVariables(default_variables, {'flavor': self.flavor})
# Compute filename prefix: the product prefix, or a default for
# the product type.
DEFAULT_PREFIX = {
- 'loadable_module': 'lib',
- 'shared_library': 'lib',
+ 'loadable_module': default_variables['SHARED_LIB_PREFIX'],
+ 'shared_library': default_variables['SHARED_LIB_PREFIX'],
+ 'static_library': default_variables['STATIC_LIB_PREFIX'],
+ 'executable': default_variables['EXECUTABLE_PREFIX'],
}
- prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(spec['type'], ''))
+ prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
# Compute filename extension: the product extension, or a default
# for the product type.
DEFAULT_EXTENSION = {
- 'static_library': 'a',
- 'loadable_module': 'so',
- 'shared_library': 'so',
+ 'loadable_module': default_variables['SHARED_LIB_SUFFIX'],
+ 'shared_library': default_variables['SHARED_LIB_SUFFIX'],
+ 'static_library': default_variables['STATIC_LIB_SUFFIX'],
+ 'executable': default_variables['EXECUTABLE_SUFFIX'],
}
- # TODO(thakis/jeremya): Remove once the mac path name computation is done
- # by XcodeSettings.
- if self.flavor == 'mac':
- DEFAULT_EXTENSION['shared_library'] = 'dylib'
- extension = spec.get('product_extension',
- DEFAULT_EXTENSION.get(spec['type'], ''))
+ extension = spec.get('product_extension')
if extension:
extension = '.' + extension
+ else:
+ extension = DEFAULT_EXTENSION.get(type, '')
if 'product_name' in spec:
# If we were given an explicit name, use that.
@@ -574,37 +868,44 @@ class NinjaWriter:
# Snip out an extra 'lib' from libs if appropriate.
target = StripPrefix(target, 'lib')
- if spec['type'] in ('static_library', 'loadable_module', 'shared_library',
+ if type in ('static_library', 'loadable_module', 'shared_library',
'executable'):
return '%s%s%s' % (prefix, target, extension)
- elif spec['type'] == 'none':
+ elif type == 'none':
return '%s.stamp' % target
else:
- raise 'Unhandled output type', spec['type']
+ raise 'Unhandled output type', type
- def ComputeOutput(self, spec):
+ def ComputeOutput(self, spec, type=None):
"""Compute the path for the final output of the spec."""
+ assert not self.is_mac_bundle or type
- filename = self.ComputeOutputFileName(spec)
+ if not type:
+ type = spec['type']
+
+ if self.flavor == 'mac' and type in (
+ 'static_library', 'executable', 'shared_library', 'loadable_module'):
+ filename = self.xcode_settings.GetExecutablePath()
+ else:
+ filename = self.ComputeOutputFileName(spec, type)
if 'product_dir' in spec:
path = os.path.join(spec['product_dir'], filename)
return self.ExpandSpecial(path)
- # Executables and loadable modules go into the output root,
- # libraries go into shared library dir, and everything else
- # goes into the normal place.
- if spec['type'] in ('executable', 'loadable_module'):
+ # Some products go into the output root, libraries go into shared library
+ # dir, and everything else goes into the normal place.
+ type_in_output_root = ['executable', 'loadable_module']
+ if self.flavor == 'mac' and self.toolset == 'target':
+ type_in_output_root += ['shared_library', 'static_library']
+
+ if type in type_in_output_root:
return filename
- elif spec['type'] == 'shared_library':
+ elif type == 'shared_library':
libdir = 'lib'
if self.toolset != 'target':
- libdir = 'lib/%s' % self.toolset
+ libdir = os.path.join('lib', '%s' % self.toolset)
return os.path.join(libdir, filename)
- # TODO(thakis/jeremya): Remove once the mac path name computation is done
- # by XcodeSettings.
- elif spec['type'] == 'static_library' and self.flavor == 'mac':
- return filename
else:
return self.GypPathToUniqueOutput(filename, qualified=False)
@@ -613,7 +914,7 @@ class NinjaWriter:
values = []
self.ninja.variable(var, ' '.join(values))
- def WriteNewNinjaRule(self, name, args, description):
+ def WriteNewNinjaRule(self, name, args, description, env={}):
"""Write out a new ninja "rule" statement for a given command.
Returns the name of the new rule."""
@@ -632,11 +933,29 @@ class NinjaWriter:
# gyp dictates that commands are run from the base directory.
# cd into the directory before running, and adjust paths in
# the arguments to point to the proper locations.
- cd = 'cd %s; ' % self.build_to_base
+ if self.flavor == 'win':
+ cd = 'cmd /s /c "cd %s && ' % self.build_to_base
+ else:
+ cd = 'cd %s; ' % self.build_to_base
args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
-
- command = cd + gyp.common.EncodePOSIXShellList(args)
- self.ninja.rule(rule_name, command, description)
+ env = self.ComputeExportEnvString(env)
+ if self.flavor == 'win':
+ # TODO(scottmg): Really don't want encourage cygwin, but I'm not sure
+ # how much sh is depended upon. For now, double quote args to make most
+ # things work.
+ command = args[0] + ' "' + '" "'.join(args[1:]) + '""'
+ else:
+ command = gyp.common.EncodePOSIXShellList(args)
+ if env:
+ # If an environment is passed in, variables in the command should be
+ # read from it, instead of from ninja's internal variables.
+ command = ninja_syntax.escape(command)
+
+ command = cd + env + command
+ # GYP rules/actions express being no-ops by not touching their outputs.
+ # Avoid executing downstream dependencies in this case by specifying
+ # restat=1 to ninja.
+ self.ninja.rule(rule_name, command, description, restat=True)
self.ninja.newline()
return rule_name
@@ -645,15 +964,14 @@ class NinjaWriter:
def CalculateVariables(default_variables, params):
"""Calculate additional variables for use in the build (called by gyp)."""
cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc'))
- default_variables['LINKER_SUPPORTS_ICF'] = \
- gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target)
-
flavor = gyp.common.GetFlavor(params)
if flavor == 'mac':
default_variables.setdefault('OS', 'mac')
default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
-
- # TODO(jeremya/thakis): Set SHARED_LIB_DIR / LIB_DIR.
+ default_variables.setdefault('SHARED_LIB_DIR',
+ generator_default_variables['PRODUCT_DIR'])
+ default_variables.setdefault('LIB_DIR',
+ generator_default_variables['PRODUCT_DIR'])
# Copy additional generator configuration data from Xcode, which is shared
# by the Mac Ninja generator.
@@ -667,12 +985,22 @@ def CalculateVariables(default_variables, params):
global generator_extra_sources_for_rules
generator_extra_sources_for_rules = getattr(xcode_generator,
'generator_extra_sources_for_rules', [])
+ elif flavor == 'win':
+ default_variables['OS'] = 'win'
+ default_variables['EXECUTABLE_SUFFIX'] = '.exe'
+ default_variables['STATIC_LIB_PREFIX'] = ''
+ default_variables['STATIC_LIB_SUFFIX'] = '.lib'
+ default_variables['SHARED_LIB_PREFIX'] = ''
+ default_variables['SHARED_LIB_SUFFIX'] = '.dll'
else:
operating_system = flavor
if flavor == 'android':
operating_system = 'linux' # Keep this legacy behavior for now.
default_variables.setdefault('OS', operating_system)
default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
+ default_variables.setdefault('SHARED_LIB_DIR',
+ os.path.join('$!PRODUCT_DIR', 'lib'))
+ default_variables.setdefault('LIB_DIR', '')
def OpenOutput(path):
@@ -684,55 +1012,85 @@ def OpenOutput(path):
return open(path, 'w')
-def GenerateOutput(target_list, target_dicts, data, params):
+def GenerateOutputForConfig(target_list, target_dicts, data, params,
+ config_name):
options = params['options']
flavor = gyp.common.GetFlavor(params)
generator_flags = params.get('generator_flags', {})
- if options.generator_output:
- raise NotImplementedError, "--generator_output not implemented for ninja"
-
- config_name = generator_flags.get('config', None)
- if config_name is None:
- # Guess which config we want to use: pick the first one from the
- # first target.
- config_name = target_dicts[target_list[0]]['default_configuration']
-
- # builddir: relative path from source root to our output files.
+ # build_dir: relative path from source root to our output files.
# e.g. "out/Debug"
- builddir = os.path.join(generator_flags.get('output_dir', 'out'), config_name)
+ build_dir = os.path.join(generator_flags.get('output_dir', 'out'),
+ config_name)
master_ninja = ninja_syntax.Writer(
- OpenOutput(os.path.join(options.toplevel_dir, builddir, 'build.ninja')),
+ OpenOutput(os.path.join(options.toplevel_dir, build_dir, 'build.ninja')),
width=120)
- # TODO: compute cc/cxx/ld/etc. by command-line arguments and system tests.
- master_ninja.variable('cc', os.environ.get('CC', 'gcc'))
- master_ninja.variable('cxx', os.environ.get('CXX', 'g++'))
- # TODO(bradnelson): remove NOGOLD when this is resolved:
- # http://code.google.com/p/chromium/issues/detail?id=108251
- if flavor != 'mac' and not os.environ.get('NOGOLD'):
- master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4')
+ # Put build-time support tools in out/{config_name}.
+ gyp.common.CopyTool(flavor, os.path.join(options.toplevel_dir, build_dir))
+
+ # Grab make settings for CC/CXX.
+ if flavor == 'win':
+ cc = cxx = 'cl'
else:
- # TODO(jeremya/thakis): flock
- master_ninja.variable('ld', '$cxx')
+ cc, cxx = 'gcc', 'g++'
+ build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
+ make_global_settings = data[build_file].get('make_global_settings', [])
+ build_to_root = InvertRelativePath(build_dir)
+ for key, value in make_global_settings:
+ if key == 'CC': cc = os.path.join(build_to_root, value)
+ if key == 'CXX': cxx = os.path.join(build_to_root, value)
+
+ flock = 'flock'
+ if flavor == 'mac':
+ flock = './gyp-mac-tool flock'
+ master_ninja.variable('cc', os.environ.get('CC', cc))
+ master_ninja.variable('cxx', os.environ.get('CXX', cxx))
+ if flavor == 'win':
+ master_ninja.variable('ld', 'link')
+ else:
+ master_ninja.variable('ld', flock + ' linker.lock $cxx')
master_ninja.variable('cc_host', '$cc')
master_ninja.variable('cxx_host', '$cxx')
+ if flavor == 'mac':
+ master_ninja.variable('mac_tool', os.path.join('.', 'gyp-mac-tool'))
master_ninja.newline()
- master_ninja.rule(
- 'cc',
- description='CC $out',
- command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
- '-c $in -o $out'),
- depfile='$out.d')
- master_ninja.rule(
- 'cxx',
- description='CXX $out',
- command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
- '-c $in -o $out'),
- depfile='$out.d')
- if flavor != 'mac':
+ if flavor != 'win':
+ master_ninja.rule(
+ 'cc',
+ description='CC $out',
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
+ '$cflags_pch_c -c $in -o $out'),
+ depfile='$out.d')
+ master_ninja.rule(
+ 'cxx',
+ description='CXX $out',
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
+ '$cflags_pch_cc -c $in -o $out'),
+ depfile='$out.d')
+ else:
+ # TODO(scottmg): Requires deplist branch of ninja for now (for
+ # /showIncludes handling).
+ master_ninja.rule(
+ 'cc',
+ description='CC $out',
+ command=('cmd /c $cc /nologo /showIncludes '
+ '$defines $includes $cflags $cflags_c '
+ '$cflags_pch_c /c $in /Fo$out '
+ '| ninja-deplist-helper -f cl -o $out.dl'),
+ deplist='$out.dl')
+ master_ninja.rule(
+ 'cxx',
+ description='CXX $out',
+ command=('cmd /c $cxx /nologo /showIncludes '
+ '$defines $includes $cflags $cflags_cc '
+ '$cflags_pch_cc /c $in /Fo$out '
+ '| ninja-deplist-helper -f cl -o $out.dl'),
+ deplist='$out.dl')
+
+ if flavor != 'mac' and flavor != 'win':
master_ninja.rule(
'alink',
description='AR $out',
@@ -752,48 +1110,88 @@ def GenerateOutput(target_list, target_dicts, data, params):
description='LINK $out',
command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib '
'-Wl,--start-group $in -Wl,--end-group $libs'))
+ elif flavor == 'win':
+ master_ninja.rule(
+ 'alink',
+ description='AR $out',
+ command='lib /nologo /OUT:$out $in')
+ master_ninja.rule(
+ 'solink',
+ description='SOLINK $out',
+ command=('$ld /nologo /DLL $ldflags /OUT:$out $in $libs'))
+ master_ninja.rule(
+ 'solink_module',
+ description='SOLINK(module) $out',
+ command=('$ld /nologo /DLL $ldflags /OUT:$out $in $libs'))
+ master_ninja.rule(
+ 'link',
+ description='LINK $out',
+ command=('$ld /nologo $ldflags /OUT:$out $in $libs'))
else:
master_ninja.rule(
'objc',
description='OBJC $out',
- command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
- '$cflags_objc -c $in -o $out'),
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
+ '$cflags_pch_objc -c $in -o $out'),
depfile='$out.d')
master_ninja.rule(
'objcxx',
description='OBJCXX $out',
- command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
- '$cflags_objcc -c $in -o $out'),
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
+ '$cflags_pch_objcc -c $in -o $out'),
depfile='$out.d')
master_ninja.rule(
'alink',
- description='LIBTOOL-STATIC $out',
- command='rm -f $out && libtool -static -o $out $in')
+ description='LIBTOOL-STATIC $out, POSTBUILDS',
+ command='rm -f $out && '
+ './gyp-mac-tool filter-libtool libtool -static -o $out $in'
+ '$postbuilds')
# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass
# -bundle -single_module here (for osmesa.so).
master_ninja.rule(
'solink',
- description='SOLINK $out',
+ description='SOLINK $out, POSTBUILDS',
command=('$ld -shared $ldflags -o $out '
- '$in $libs'))
+ '$in $libs$postbuilds'))
master_ninja.rule(
'solink_module',
- description='SOLINK(module) $out',
+ description='SOLINK(module) $out, POSTBUILDS',
command=('$ld -shared $ldflags -o $out '
- '$in $libs'))
+ '$in $libs$postbuilds'))
master_ninja.rule(
'link',
- description='LINK $out',
+ description='LINK $out, POSTBUILDS',
command=('$ld $ldflags -o $out '
- '$in $libs'))
+ '$in $libs$postbuilds'))
+ master_ninja.rule(
+ 'infoplist',
+ description='INFOPLIST $out',
+ command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
+ 'plutil -convert xml1 $out $out'))
+ master_ninja.rule(
+ 'mac_tool',
+ description='MACTOOL $mactool_cmd $in',
+ command='$env $mac_tool $mactool_cmd $in $out')
+ master_ninja.rule(
+ 'package_framework',
+ description='PACKAGE FRAMEWORK $out, POSTBUILDS',
+ command='$mac_tool package-framework $out $version$postbuilds '
+ '&& touch $out')
master_ninja.rule(
'stamp',
description='STAMP $out',
- command='touch $out')
- master_ninja.rule(
- 'copy',
- description='COPY $in $out',
- command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
+ command='${postbuilds}touch $out')
+ if flavor == 'win':
+ # TODO(scottmg): Copy fallback?
+ master_ninja.rule(
+ 'copy',
+ description='COPY $in $out',
+ command='cmd /c mklink /h $out $in >nul || mklink /h /j $out $in >nul')
+ else:
+ master_ninja.rule(
+ 'copy',
+ description='COPY $in $out',
+ command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
master_ninja.newline()
all_targets = set()
@@ -802,38 +1200,57 @@ def GenerateOutput(target_list, target_dicts, data, params):
all_targets.add(target)
all_outputs = set()
+ # target_outputs is a map from qualified target name to a Target object.
target_outputs = {}
for qualified_target in target_list:
# qualified_target is like: third_party/icu/icu.gyp:icui18n#target
build_file, name, toolset = \
gyp.common.ParseQualifiedTarget(qualified_target)
- # TODO: what is options.depth and how is it different than
- # options.toplevel_dir?
- build_file = gyp.common.RelativePath(build_file, options.depth)
+ this_make_global_settings = data[build_file].get('make_global_settings', [])
+ assert make_global_settings == this_make_global_settings, (
+ "make_global_settings needs to be the same for all targets.")
+
+ spec = target_dicts[qualified_target]
+ if flavor == 'mac':
+ gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
+
+ build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
base_path = os.path.dirname(build_file)
obj = 'obj'
if toolset != 'target':
obj += '.' + toolset
output_file = os.path.join(obj, base_path, name + '.ninja')
- spec = target_dicts[qualified_target]
- config = spec['configurations'][config_name]
- writer = NinjaWriter(target_outputs, base_path, builddir,
+ abs_build_dir=os.path.abspath(os.path.join(options.toplevel_dir, build_dir))
+ writer = NinjaWriter(target_outputs, base_path, build_dir,
OpenOutput(os.path.join(options.toplevel_dir,
- builddir,
+ build_dir,
output_file)),
- flavor)
+ flavor, abs_build_dir=abs_build_dir)
master_ninja.subninja(output_file)
- output, compile_depends = writer.WriteSpec(spec, config)
- if output:
- linkable = spec['type'] in ('static_library', 'shared_library')
- target_outputs[qualified_target] = (output, compile_depends, linkable)
-
+ target = writer.WriteSpec(spec, config_name)
+ if target:
+ target_outputs[qualified_target] = target
if qualified_target in all_targets:
- all_outputs.add(output)
+ all_outputs.add(target.FinalOutput())
if all_outputs:
master_ninja.build('all', 'phony', list(all_outputs))
+
+
+def GenerateOutput(target_list, target_dicts, data, params):
+ if params['options'].generator_output:
+ raise NotImplementedError, "--generator_output not implemented for ninja"
+
+ user_config = params.get('generator_flags', {}).get('config', None)
+ if user_config:
+ GenerateOutputForConfig(target_list, target_dicts, data, params,
+ user_config)
+ else:
+ config_names = target_dicts[target_list[0]]['configurations'].keys()
+ for config_name in config_names:
+ GenerateOutputForConfig(target_list, target_dicts, data, params,
+ config_name)