summaryrefslogtreecommitdiff
path: root/tools/gyp/pylib/gyp/generator/ninja.py
diff options
context:
space:
mode:
authorTimothy J Fontaine <tjfontaine@gmail.com>2013-11-10 15:15:34 -0800
committerTimothy J Fontaine <tjfontaine@gmail.com>2013-11-10 15:15:34 -0800
commit201098535443ef6588b0fd176a425fdf3213ee70 (patch)
tree3259766e34c4a70f087c0ea52c16e0bc74eb11a7 /tools/gyp/pylib/gyp/generator/ninja.py
parent3dcc9b93e1e4d80aa15a40a9d82a6b9f0bef0ec0 (diff)
downloadios-node-v8-201098535443ef6588b0fd176a425fdf3213ee70.tar.gz
ios-node-v8-201098535443ef6588b0fd176a425fdf3213ee70.tar.bz2
ios-node-v8-201098535443ef6588b0fd176a425fdf3213ee70.zip
gyp: update to bebdcea
Diffstat (limited to 'tools/gyp/pylib/gyp/generator/ninja.py')
-rw-r--r--tools/gyp/pylib/gyp/generator/ninja.py867
1 files changed, 604 insertions, 263 deletions
diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py
index c6bceaf382..23fb9b8afd 100644
--- a/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/tools/gyp/pylib/gyp/generator/ninja.py
@@ -4,6 +4,7 @@
import copy
import hashlib
+import json
import multiprocessing
import os.path
import re
@@ -15,6 +16,7 @@ import gyp.common
import gyp.msvs_emulation
import gyp.MSVSUtil as MSVSUtil
import gyp.xcode_emulation
+from cStringIO import StringIO
from gyp.common import GetEnvironFallback
import gyp.ninja_syntax as ninja_syntax
@@ -56,6 +58,7 @@ generator_default_variables = {
generator_additional_non_configuration_keys = []
generator_additional_path_sections = []
generator_extra_sources_for_rules = []
+generator_filelist_paths = None
# TODO: figure out how to not build extra host objects in the non-cross-compile
# case when this is enabled, and enable unconditionally.
@@ -97,6 +100,12 @@ def Define(d, flavor):
return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
+def AddArch(output, arch):
+ """Adds an arch string to an output path."""
+ output, extension = os.path.splitext(output)
+ return '%s.%s%s' % (output, arch, extension)
+
+
class Target:
"""Target represents the paths used within a single gyp target.
@@ -203,7 +212,8 @@ class Target:
class NinjaWriter:
def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
- output_file, flavor, toplevel_dir=None):
+ output_file, toplevel_build, output_file_name, flavor,
+ toplevel_dir=None):
"""
base_dir: path from source root to directory containing this gyp file,
by gyp semantics, all input paths are relative to this
@@ -216,6 +226,9 @@ class NinjaWriter:
self.base_dir = base_dir
self.build_dir = build_dir
self.ninja = ninja_syntax.Writer(output_file)
+ self.toplevel_build = toplevel_build
+ self.output_file_name = output_file_name
+
self.flavor = flavor
self.abs_build_dir = None
if toplevel_dir is not None:
@@ -293,7 +306,7 @@ class NinjaWriter:
expanded = os.path.normpath(expanded)
return expanded
if '$|' in path:
- path = self.ExpandSpecial(path)
+ path = self.ExpandSpecial(path)
assert '$' not in path, path
return os.path.normpath(os.path.join(self.build_to_base, path))
@@ -345,8 +358,11 @@ class NinjaWriter:
self.ninja.newline()
return targets[0]
- def WriteSpec(self, spec, config_name, generator_flags,
- case_sensitive_filesystem):
+ def _SubninjaNameForArch(self, arch):
+ output_file_base = os.path.splitext(self.output_file_name)[0]
+ return '%s.%s.ninja' % (output_file_base, arch)
+
+ def WriteSpec(self, spec, config_name, generator_flags):
"""The main entry point for NinjaWriter: write the build rules for a spec.
Returns a Target object, which represents the output paths for this spec.
@@ -360,6 +376,9 @@ class NinjaWriter:
self.target = Target(spec['type'])
self.is_standalone_static_library = bool(
spec.get('standalone_static_library', 0))
+ # Track if this target contains any C++ files, to decide if gcc or g++
+ # should be used for linking.
+ self.uses_cpp = False
self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
self.xcode_settings = self.msvs_settings = None
@@ -370,6 +389,18 @@ class NinjaWriter:
generator_flags)
arch = self.msvs_settings.GetArch(config_name)
self.ninja.variable('arch', self.win_env[arch])
+ self.ninja.variable('cc', '$cl_' + arch)
+ self.ninja.variable('cxx', '$cl_' + arch)
+
+ if self.flavor == 'mac':
+ self.archs = self.xcode_settings.GetActiveArchs(config_name)
+ if len(self.archs) > 1:
+ self.arch_subninjas = dict(
+ (arch, ninja_syntax.Writer(
+ OpenOutput(os.path.join(self.toplevel_build,
+ self._SubninjaNameForArch(arch)),
+ 'w')))
+ for arch in self.archs)
# Compute predepends for all rules.
# actions_depends is the dependencies this target depends on before running
@@ -411,8 +442,14 @@ class NinjaWriter:
# Write out the compilation steps, if any.
link_deps = []
- sources = spec.get('sources', []) + extra_sources
+ sources = extra_sources + spec.get('sources', [])
if sources:
+ if self.flavor == 'mac' and len(self.archs) > 1:
+ # Write subninja file containing compile and link commands scoped to
+ # a single arch if a fat binary is being built.
+ for arch in self.archs:
+ self.ninja.subninja(self._SubninjaNameForArch(arch))
+
pch = None
if self.flavor == 'win':
gyp.msvs_emulation.VerifyMissingSources(
@@ -425,17 +462,24 @@ class NinjaWriter:
self.xcode_settings, self.GypPathToNinja,
lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang))
link_deps = self.WriteSources(
- config_name, config, sources, compile_depends_stamp, pch,
- case_sensitive_filesystem, spec)
+ self.ninja, config_name, config, sources, compile_depends_stamp, pch,
+ spec)
# Some actions/rules output 'sources' that are already object files.
- link_deps += [self.GypPathToNinja(f)
- for f in sources if f.endswith(self.obj_ext)]
+ obj_outputs = [f for f in sources if f.endswith(self.obj_ext)]
+ if obj_outputs:
+ if self.flavor != 'mac' or len(self.archs) == 1:
+ link_deps += [self.GypPathToNinja(o) for o in obj_outputs]
+ else:
+ print "Warning: Actions/rules writing object files don't work with " \
+ "multiarch targets, dropping. (target %s)" % spec['target_name']
+
if self.flavor == 'win' and self.target.type == 'static_library':
self.target.component_objs = link_deps
# Write out a link step, if needed.
output = None
+ is_empty_bundle = not link_deps and not mac_bundle_depends
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)
@@ -444,7 +488,7 @@ class NinjaWriter:
# Bundle all of the above together, if needed.
if self.is_mac_bundle:
- output = self.WriteMacBundle(spec, mac_bundle_depends)
+ output = self.WriteMacBundle(spec, mac_bundle_depends, is_empty_bundle)
if not output:
return None
@@ -491,6 +535,10 @@ class NinjaWriter:
"""Write out the Actions, Rules, and Copies steps. Return a path
representing the outputs of these steps."""
outputs = []
+ if self.is_mac_bundle:
+ mac_bundle_resources = spec.get('mac_bundle_resources', [])[:]
+ else:
+ mac_bundle_resources = []
extra_mac_bundle_resources = []
if 'actions' in spec:
@@ -498,6 +546,7 @@ class NinjaWriter:
extra_mac_bundle_resources)
if 'rules' in spec:
outputs += self.WriteRules(spec['rules'], extra_sources, prebuild,
+ mac_bundle_resources,
extra_mac_bundle_resources)
if 'copies' in spec:
outputs += self.WriteCopies(spec['copies'], prebuild, mac_bundle_depends)
@@ -508,9 +557,8 @@ class NinjaWriter:
stamp = self.WriteCollapsedDependencies('actions_rules_copies', 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.WriteMacBundleResources(
+ extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends)
self.WriteMacInfoPlist(mac_bundle_depends)
return stamp
@@ -567,7 +615,7 @@ class NinjaWriter:
return all_outputs
def WriteRules(self, rules, extra_sources, prebuild,
- extra_mac_bundle_resources):
+ mac_bundle_resources, extra_mac_bundle_resources):
env = self.GetSortedXcodeEnv()
all_outputs = []
for rule in rules:
@@ -607,6 +655,7 @@ class NinjaWriter:
# For each source file, write an edge that generates all the outputs.
for source in rule.get('rule_sources', []):
+ source = os.path.normpath(source)
dirname, basename = os.path.split(source)
root, ext = os.path.splitext(basename)
@@ -620,15 +669,27 @@ 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)):
+
+ was_mac_bundle_resource = source in mac_bundle_resources
+ if was_mac_bundle_resource or \
+ int(rule.get('process_outputs_as_mac_bundle_resources', False)):
extra_mac_bundle_resources += outputs
+ # Note: This is n_resources * n_outputs_in_rule. Put to-be-removed
+ # items in a set and remove them all in a single pass if this becomes
+ # a performance issue.
+ if was_mac_bundle_resource:
+ mac_bundle_resources.remove(source)
extra_bindings = []
for var in needed_variables:
if var == 'root':
extra_bindings.append(('root', cygwin_munge(root)))
elif var == 'dirname':
- extra_bindings.append(('dirname', cygwin_munge(dirname)))
+ # '$dirname' is a parameter to the rule action, which means
+ # it shouldn't be converted to a Ninja path. But we don't
+ # want $!PRODUCT_DIR in there either.
+ dirname_expanded = self.ExpandSpecial(dirname, self.base_to_build)
+ extra_bindings.append(('dirname', cygwin_munge(dirname_expanded)))
elif var == 'source':
# '$source' is a parameter to the rule action, which means
# it shouldn't be converted to a Ninja path. But we don't
@@ -682,8 +743,9 @@ class NinjaWriter:
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']),
+ generator_default_variables['PRODUCT_DIR'],
self.xcode_settings, map(self.GypPathToNinja, resources)):
+ output = self.ExpandSpecial(output)
self.ninja.build(output, 'mac_tool', res,
variables=[('mactool_cmd', 'copy-bundle-resource')])
bundle_depends.append(output)
@@ -691,38 +753,56 @@ class NinjaWriter:
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']),
+ generator_default_variables['PRODUCT_DIR'],
self.xcode_settings, self.GypPathToNinja)
if not info_plist:
return
+ out = self.ExpandSpecial(out)
if defines:
# Create an intermediate file to store preprocessed results.
intermediate_plist = self.GypPathToUniqueOutput(
os.path.basename(info_plist))
defines = ' '.join([Define(d, self.flavor) for d in defines])
- info_plist = self.ninja.build(intermediate_plist, 'infoplist', info_plist,
- variables=[('defines',defines)])
+ info_plist = self.ninja.build(
+ intermediate_plist, 'preprocess_infoplist', info_plist,
+ variables=[('defines',defines)])
env = self.GetSortedXcodeEnv(additional_settings=extra_env)
env = self.ComputeExportEnvString(env)
- self.ninja.build(out, 'mac_tool', info_plist,
- variables=[('mactool_cmd', 'copy-info-plist'),
- ('env', env)])
+ keys = self.xcode_settings.GetExtraPlistItems(self.config_name)
+ keys = QuoteShellArgument(json.dumps(keys), self.flavor)
+ self.ninja.build(out, 'copy_infoplist', info_plist,
+ variables=[('env', env), ('keys', keys)])
bundle_depends.append(out)
- def WriteSources(self, config_name, config, sources, predepends,
- precompiled_header, case_sensitive_filesystem, spec):
+ def WriteSources(self, ninja_file, config_name, config, sources, predepends,
+ precompiled_header, spec):
"""Write build rules to compile all of |sources|."""
if self.toolset == 'host':
self.ninja.variable('ar', '$ar_host')
self.ninja.variable('cc', '$cc_host')
self.ninja.variable('cxx', '$cxx_host')
self.ninja.variable('ld', '$ld_host')
+ self.ninja.variable('ldxx', '$ldxx_host')
+
+ if self.flavor != 'mac' or len(self.archs) == 1:
+ return self.WriteSourcesForArch(
+ self.ninja, config_name, config, sources, predepends,
+ precompiled_header, spec)
+ else:
+ return dict((arch, self.WriteSourcesForArch(
+ self.arch_subninjas[arch], config_name, config, sources, predepends,
+ precompiled_header, spec, arch=arch))
+ for arch in self.archs)
+
+ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
+ predepends, precompiled_header, spec, arch=None):
+ """Write build rules to compile all of |sources|."""
extra_defines = []
if self.flavor == 'mac':
- cflags = self.xcode_settings.GetCflags(config_name)
+ cflags = self.xcode_settings.GetCflags(config_name, arch=arch)
cflags_c = self.xcode_settings.GetCflagsC(config_name)
cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
cflags_objc = ['$cflags_c'] + \
@@ -742,56 +822,70 @@ class NinjaWriter:
obj += '.' + self.toolset
pdbpath = os.path.normpath(os.path.join(obj, self.base_dir,
self.name + '.pdb'))
- self.WriteVariableList('pdbname', [pdbpath])
- self.WriteVariableList('pchprefix', [self.name])
+ self.WriteVariableList(ninja_file, 'pdbname', [pdbpath])
+ self.WriteVariableList(ninja_file, 'pchprefix', [self.name])
else:
cflags = config.get('cflags', [])
cflags_c = config.get('cflags_c', [])
cflags_cc = config.get('cflags_cc', [])
+ # Respect environment variables related to build, but target-specific
+ # flags can still override them.
+ if self.toolset == 'target':
+ cflags_c = (os.environ.get('CPPFLAGS', '').split() +
+ os.environ.get('CFLAGS', '').split() + cflags_c)
+ cflags_cc = (os.environ.get('CPPFLAGS', '').split() +
+ os.environ.get('CXXFLAGS', '').split() + cflags_cc)
+
defines = config.get('defines', []) + extra_defines
- self.WriteVariableList('defines', [Define(d, self.flavor) for d in defines])
+ self.WriteVariableList(ninja_file, 'defines',
+ [Define(d, self.flavor) for d in defines])
if self.flavor == 'win':
- self.WriteVariableList('rcflags',
+ self.WriteVariableList(ninja_file, 'rcflags',
[QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
for f in self.msvs_settings.GetRcflags(config_name,
self.GypPathToNinja)])
include_dirs = config.get('include_dirs', [])
+ env = self.GetSortedXcodeEnv()
if self.flavor == 'win':
+ env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
+ config=config_name)
include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
config_name)
- self.WriteVariableList('includes',
- [QuoteShellArgument('-I' + self.GypPathToNinja(i), self.flavor)
+ self.WriteVariableList(ninja_file, 'includes',
+ [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
for i in include_dirs])
- pch_commands = precompiled_header.GetPchBuildCommands()
+ pch_commands = precompiled_header.GetPchBuildCommands(arch)
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))
+ # Most targets use no precompiled headers, so only write these if needed.
+ for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'),
+ ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]:
+ include = precompiled_header.GetInclude(ext, arch)
+ if include: ninja_file.variable(var, include)
+
+ self.WriteVariableList(ninja_file, 'cflags',
+ map(self.ExpandSpecial, cflags))
+ self.WriteVariableList(ninja_file, 'cflags_c',
+ map(self.ExpandSpecial, cflags_c))
+ self.WriteVariableList(ninja_file, 'cflags_cc',
+ map(self.ExpandSpecial, cflags_cc))
if self.flavor == 'mac':
- self.WriteVariableList('cflags_objc', map(self.ExpandSpecial,
- cflags_objc))
- self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial,
- cflags_objcc))
- self.ninja.newline()
+ self.WriteVariableList(ninja_file, 'cflags_objc',
+ map(self.ExpandSpecial, cflags_objc))
+ self.WriteVariableList(ninja_file, 'cflags_objcc',
+ map(self.ExpandSpecial, cflags_objcc))
+ ninja_file.newline()
outputs = []
+ has_rc_source = False
for source in sources:
filename, ext = os.path.splitext(source)
ext = ext[1:]
obj_ext = self.obj_ext
if ext in ('cc', 'cpp', 'cxx'):
command = 'cxx'
+ self.uses_cpp = True
elif ext == 'c' or (ext == 'S' and self.flavor != 'win'):
command = 'cc'
elif ext == 's' and self.flavor != 'win': # Doesn't generate .o.d files.
@@ -808,37 +902,41 @@ class NinjaWriter:
command = 'objc'
elif self.flavor == 'mac' and ext == 'mm':
command = 'objcxx'
+ self.uses_cpp = True
elif self.flavor == 'win' and ext == 'rc':
command = 'rc'
obj_ext = '.res'
+ has_rc_source = True
else:
# Ignore unhandled extensions.
continue
input = self.GypPathToNinja(source)
output = self.GypPathToUniqueOutput(filename + obj_ext)
- # Ninja's depfile handling gets confused when the case of a filename
- # changes on a case-insensitive file system. To work around that, always
- # convert .o filenames to lowercase on such file systems. See
- # https://github.com/martine/ninja/issues/402 for details.
- if not case_sensitive_filesystem:
- output = output.lower()
- implicit = precompiled_header.GetObjDependencies([input], [output])
+ if arch is not None:
+ output = AddArch(output, arch)
+ implicit = precompiled_header.GetObjDependencies([input], [output], arch)
variables = []
if self.flavor == 'win':
variables, output, implicit = precompiled_header.GetFlagsModifications(
input, output, implicit, command, cflags_c, cflags_cc,
self.ExpandSpecial)
- self.ninja.build(output, command, input,
+ ninja_file.build(output, command, input,
implicit=[gch for _, _, gch in implicit],
order_only=predepends, variables=variables)
outputs.append(output)
- self.WritePchTargets(pch_commands)
+ if has_rc_source:
+ resource_include_dirs = config.get('resource_include_dirs', include_dirs)
+ self.WriteVariableList(ninja_file, 'resource_includes',
+ [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
+ for i in resource_include_dirs])
- self.ninja.newline()
+ self.WritePchTargets(ninja_file, pch_commands)
+
+ ninja_file.newline()
return outputs
- def WritePchTargets(self, pch_commands):
+ def WritePchTargets(self, ninja_file, pch_commands):
"""Writes ninja rules to compile prefix headers."""
if not pch_commands:
return
@@ -853,16 +951,34 @@ class NinjaWriter:
map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
cmd = map.get(lang)
- self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)])
+ ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
def WriteLink(self, spec, config_name, config, link_deps):
"""Write out a link step. Fills out target.binary. """
-
+ if self.flavor != 'mac' or len(self.archs) == 1:
+ return self.WriteLinkForArch(
+ self.ninja, spec, config_name, config, link_deps)
+ else:
+ output = self.ComputeOutput(spec)
+ inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec,
+ config_name, config, link_deps[arch],
+ arch=arch)
+ for arch in self.archs]
+ extra_bindings = []
+ if not self.is_mac_bundle:
+ self.AppendPostbuildVariable(extra_bindings, spec, output, output)
+ self.ninja.build(output, 'lipo', inputs, variables=extra_bindings)
+ return output
+
+ def WriteLinkForArch(self, ninja_file, spec, config_name, config,
+ link_deps, arch=None):
+ """Write out a link step. Fills out target.binary. """
command = {
'executable': 'link',
'loadable_module': 'solink_module',
'shared_library': 'solink',
}[spec['type']]
+ command_suffix = ''
implicit_deps = set()
solibs = set()
@@ -879,61 +995,92 @@ class NinjaWriter:
continue
linkable = target.Linkable()
if linkable:
+ new_deps = []
if (self.flavor == 'win' and
target.component_objs and
self.msvs_settings.IsUseLibraryDependencyInputs(config_name)):
- extra_link_deps |= set(target.component_objs)
+ new_deps = target.component_objs
elif self.flavor == 'win' and target.import_lib:
- extra_link_deps.add(target.import_lib)
+ new_deps = [target.import_lib]
elif target.UsesToc(self.flavor):
solibs.add(target.binary)
implicit_deps.add(target.binary + '.TOC')
else:
- extra_link_deps.add(target.binary)
+ new_deps = [target.binary]
+ for new_dep in new_deps:
+ if new_dep not in extra_link_deps:
+ extra_link_deps.add(new_dep)
+ link_deps.append(new_dep)
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.uses_cpp and self.flavor != 'win':
+ extra_bindings.append(('ld', '$ldxx'))
+
+ output = self.ComputeOutput(spec, arch)
+ if arch is None and not self.is_mac_bundle:
+ self.AppendPostbuildVariable(extra_bindings, spec, output, output)
is_executable = spec['type'] == 'executable'
+ # The ldflags config key is not used on mac or win. On those platforms
+ # linker flags are set via xcode_settings and msvs_settings, respectively.
+ env_ldflags = os.environ.get('LDFLAGS', '').split()
if self.flavor == 'mac':
ldflags = self.xcode_settings.GetLdflags(config_name,
self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
- self.GypPathToNinja)
+ self.GypPathToNinja, arch)
+ ldflags = env_ldflags + ldflags
elif self.flavor == 'win':
manifest_name = self.GypPathToUniqueOutput(
self.ComputeOutputFileName(spec))
ldflags, manifest_files = self.msvs_settings.GetLdflags(config_name,
self.GypPathToNinja, self.ExpandSpecial, manifest_name, is_executable)
- self.WriteVariableList('manifests', manifest_files)
+ ldflags = env_ldflags + ldflags
+ self.WriteVariableList(ninja_file, 'manifests', manifest_files)
+ command_suffix = _GetWinLinkRuleNameSuffix(
+ self.msvs_settings.IsEmbedManifest(config_name),
+ self.msvs_settings.IsLinkIncremental(config_name))
+ def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja)
+ if def_file:
+ implicit_deps.add(def_file)
else:
- ldflags = config.get('ldflags', [])
+ # Respect environment variables related to build, but target-specific
+ # flags can still override them.
+ ldflags = env_ldflags + config.get('ldflags', [])
if is_executable and len(solibs):
- ldflags.append('-Wl,-rpath=\$$ORIGIN/lib/')
- ldflags.append('-Wl,-rpath-link=lib/')
- self.WriteVariableList('ldflags',
- gyp.common.uniquer(map(self.ExpandSpecial,
- ldflags)))
+ rpath = 'lib/'
+ if self.toolset != 'target':
+ rpath += self.toolset
+ ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
+ ldflags.append('-Wl,-rpath-link=%s' % rpath)
+ self.WriteVariableList(ninja_file, 'ldflags',
+ gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
+
+ library_dirs = config.get('library_dirs', [])
+ if self.flavor == 'win':
+ library_dirs = [self.msvs_settings.ConvertVSMacros(l, config_name)
+ for l in library_dirs]
+ library_dirs = ['/LIBPATH:' + QuoteShellArgument(self.GypPathToNinja(l),
+ self.flavor)
+ for l in library_dirs]
+ else:
+ library_dirs = [QuoteShellArgument('-L' + self.GypPathToNinja(l),
+ self.flavor)
+ for l in library_dirs]
libraries = gyp.common.uniquer(map(self.ExpandSpecial,
spec.get('libraries', [])))
if self.flavor == 'mac':
- libraries = self.xcode_settings.AdjustLibraries(libraries)
+ libraries = self.xcode_settings.AdjustLibraries(libraries, config_name)
elif self.flavor == 'win':
libraries = self.msvs_settings.AdjustLibraries(libraries)
- self.WriteVariableList('libs', libraries)
- self.target.binary = output
+ self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries)
+
+ linked_binary = output
if command in ('solink', 'solink_module'):
extra_bindings.append(('soname', os.path.split(output)[1]))
@@ -946,56 +1093,80 @@ class NinjaWriter:
extra_bindings.append(('implibflag',
'/IMPLIB:%s' % self.target.import_lib))
output = [output, self.target.import_lib]
- else:
+ elif not self.is_mac_bundle:
output = [output, output + '.TOC']
+ else:
+ command = command + '_notoc'
if len(solibs):
extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs)))
- self.ninja.build(output, command, link_deps,
+ ninja_file.build(output, command + command_suffix, link_deps,
implicit=list(implicit_deps),
variables=extra_bindings)
+ return linked_binary
def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
- if spec['type'] == 'none':
+ extra_link_deps = any(self.target_outputs.get(dep).Linkable()
+ for dep in spec.get('dependencies', [])
+ if dep in self.target_outputs)
+ if spec['type'] == 'none' or (not link_deps and not extra_link_deps):
# 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
+ self.target.type = 'none'
elif spec['type'] == 'static_library':
self.target.binary = self.ComputeOutput(spec)
- variables = []
- postbuild = self.GetPostbuildCommand(
- spec, self.target.binary, self.target.binary)
- if postbuild:
- variables.append(('postbuilds', postbuild))
- if self.xcode_settings:
- variables.append(('libtool_flags',
- self.xcode_settings.GetLibtoolflags(config_name)))
- if (self.flavor not in ('mac', 'win') and not
+ if (self.flavor not in ('mac', 'openbsd', 'win') and not
self.is_standalone_static_library):
self.ninja.build(self.target.binary, 'alink_thin', link_deps,
- order_only=compile_deps, variables=variables)
+ order_only=compile_deps)
else:
+ variables = []
+ if self.xcode_settings:
+ libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
+ if libtool_flags:
+ variables.append(('libtool_flags', libtool_flags))
if self.msvs_settings:
libflags = self.msvs_settings.GetLibFlags(config_name,
self.GypPathToNinja)
variables.append(('libflags', libflags))
- self.ninja.build(self.target.binary, 'alink', link_deps,
- order_only=compile_deps, variables=variables)
+
+ if self.flavor != 'mac' or len(self.archs) == 1:
+ self.AppendPostbuildVariable(variables, spec,
+ self.target.binary, self.target.binary)
+ self.ninja.build(self.target.binary, 'alink', link_deps,
+ order_only=compile_deps, variables=variables)
+ else:
+ inputs = []
+ for arch in self.archs:
+ output = self.ComputeOutput(spec, arch)
+ self.arch_subninjas[arch].build(output, 'alink', link_deps[arch],
+ order_only=compile_deps,
+ variables=variables)
+ inputs.append(output)
+ # TODO: It's not clear if libtool_flags should be passed to the alink
+ # call that combines single-arch .a files into a fat .a file.
+ self.AppendPostbuildVariable(variables, spec,
+ self.target.binary, self.target.binary)
+ self.ninja.build(self.target.binary, 'alink', inputs,
+ # FIXME: test proving order_only=compile_deps isn't
+ # needed.
+ variables=variables)
else:
- self.WriteLink(spec, config_name, config, link_deps)
+ self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
return self.target.binary
- def WriteMacBundle(self, spec, mac_bundle_depends):
+ def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
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)
+ if is_empty:
+ output += '.stamp'
variables = []
- if postbuild:
- variables.append(('postbuilds', postbuild))
- if package_framework:
+ self.AppendPostbuildVariable(variables, spec, output, self.target.binary,
+ is_command_start=not package_framework)
+ if package_framework and not is_empty:
variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
self.ninja.build(output, 'package_framework', mac_bundle_depends,
variables=variables)
@@ -1025,23 +1196,30 @@ class NinjaWriter:
postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
- def GetPostbuildCommand(self, spec, output, output_binary,
- is_command_start=False):
+ def AppendPostbuildVariable(self, variables, spec, output, binary,
+ is_command_start=False):
+ """Adds a 'postbuild' variable if there is a postbuild for |output|."""
+ postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
+ if postbuild:
+ variables.append(('postbuilds', postbuild))
+
+ def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
"""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, self.flavor)
- target_postbuilds = self.xcode_settings.GetTargetPostbuilds(
- self.config_name,
- os.path.normpath(os.path.join(self.base_to_build, output)),
- QuoteShellArgument(
- os.path.normpath(os.path.join(self.base_to_build, output_binary)),
- self.flavor),
- quiet=True)
postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True)
- postbuilds = target_postbuilds + postbuilds
+ if output_binary is not None:
+ postbuilds = self.xcode_settings.AddImplicitPostbuilds(
+ self.config_name,
+ os.path.normpath(os.path.join(self.base_to_build, output)),
+ QuoteShellArgument(
+ os.path.normpath(os.path.join(self.base_to_build, output_binary)),
+ self.flavor),
+ postbuilds, quiet=True)
+
if not postbuilds:
return ''
# Postbuilds expect to be run in the gyp file's directory, so insert an
@@ -1074,14 +1252,9 @@ class NinjaWriter:
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())
+ path = generator_default_variables['PRODUCT_DIR']
+ return self.ExpandSpecial(
+ os.path.join(path, self.xcode_settings.GetWrapperName()))
def ComputeOutputFileName(self, spec, type=None):
"""Compute the filename of the final output for the current target."""
@@ -1133,12 +1306,9 @@ class NinjaWriter:
else:
raise Exception('Unhandled output type %s' % type)
- def ComputeOutput(self, spec, type=None):
+ def ComputeOutput(self, spec, arch=None):
"""Compute the path for the final output of the spec."""
- assert not self.is_mac_bundle or type
-
- if not type:
- type = spec['type']
+ type = spec['type']
if self.flavor == 'win':
override = self.msvs_settings.GetOutputName(self.config_name,
@@ -1146,13 +1316,13 @@ class NinjaWriter:
if override:
return override
- if self.flavor == 'mac' and type in (
+ if arch is None and 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:
+ if arch is None and 'product_dir' in spec:
path = os.path.join(spec['product_dir'], filename)
return self.ExpandSpecial(path)
@@ -1164,7 +1334,14 @@ class NinjaWriter:
elif self.flavor == 'win' and self.toolset == 'target':
type_in_output_root += ['shared_library']
- if type in type_in_output_root or self.is_standalone_static_library:
+ if arch is not None:
+ # Make sure partial executables don't end up in a bundle or the regular
+ # output directory.
+ archdir = 'arch'
+ if self.toolset != 'target':
+ archdir = os.path.join('arch', '%s' % self.toolset)
+ return os.path.join(archdir, AddArch(filename, arch))
+ elif type in type_in_output_root or self.is_standalone_static_library:
return filename
elif type == 'shared_library':
libdir = 'lib'
@@ -1174,11 +1351,11 @@ class NinjaWriter:
else:
return self.GypPathToUniqueOutput(filename, qualified=False)
- def WriteVariableList(self, var, values):
+ def WriteVariableList(self, ninja_file, var, values):
assert not isinstance(values, str)
if values is None:
values = []
- self.ninja.variable(var, ' '.join(values))
+ ninja_file.variable(var, ' '.join(values))
def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
"""Write out a new ninja "rule" statement for a given command.
@@ -1275,7 +1452,6 @@ def CalculateVariables(default_variables, params):
default_variables['STATIC_LIB_SUFFIX'] = '.lib'
default_variables['SHARED_LIB_PREFIX'] = ''
default_variables['SHARED_LIB_SUFFIX'] = '.dll'
- generator_flags = params.get('generator_flags', {})
# Copy additional generator configuration data from VS, which is shared
# by the Windows Ninja generator.
@@ -1285,19 +1461,7 @@ def CalculateVariables(default_variables, params):
generator_additional_path_sections = getattr(msvs_generator,
'generator_additional_path_sections', [])
- # Set a variable so conditions can be based on msvs_version.
- msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
- default_variables['MSVS_VERSION'] = msvs_version.ShortName()
-
- # To determine processor word size on Windows, in addition to checking
- # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
- # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
- # contains the actual word size of the system when running thru WOW64).
- if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
- '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
- default_variables['MSVS_OS_BITS'] = 64
- else:
- default_variables['MSVS_OS_BITS'] = 32
+ gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
else:
operating_system = flavor
if flavor == 'android':
@@ -1309,6 +1473,32 @@ def CalculateVariables(default_variables, params):
default_variables.setdefault('LIB_DIR',
os.path.join('$!PRODUCT_DIR', 'obj'))
+def ComputeOutputDir(params):
+ """Returns the path from the toplevel_dir to the build output directory."""
+ # generator_dir: relative path from pwd to where make puts build files.
+ # Makes migrating from make to ninja easier, ninja doesn't put anything here.
+ generator_dir = os.path.relpath(params['options'].generator_output or '.')
+
+ # output_dir: relative path from generator_dir to the build directory.
+ output_dir = params.get('generator_flags', {}).get('output_dir', 'out')
+
+ # Relative path from source root to our output files. e.g. "out"
+ return os.path.normpath(os.path.join(generator_dir, output_dir))
+
+
+def CalculateGeneratorInputInfo(params):
+ """Called by __init__ to initialize generator values based on params."""
+ # E.g. "out/gypfiles"
+ toplevel = params['options'].toplevel_dir
+ qualified_out_dir = os.path.normpath(os.path.join(
+ toplevel, ComputeOutputDir(params), 'gypfiles'))
+
+ global generator_filelist_paths
+ generator_filelist_paths = {
+ 'toplevel': toplevel,
+ 'qualified_out_dir': qualified_out_dir,
+ }
+
def OpenOutput(path, mode='w'):
"""Open |path| for writing, creating directories if necessary."""
@@ -1326,32 +1516,149 @@ def CommandWithWrapper(cmd, wrappers, prog):
return prog
+def GetDefaultConcurrentLinks():
+ """Returns a best-guess for a number of concurrent links."""
+ if sys.platform in ('win32', 'cygwin'):
+ import ctypes
+
+ class MEMORYSTATUSEX(ctypes.Structure):
+ _fields_ = [
+ ("dwLength", ctypes.c_ulong),
+ ("dwMemoryLoad", ctypes.c_ulong),
+ ("ullTotalPhys", ctypes.c_ulonglong),
+ ("ullAvailPhys", ctypes.c_ulonglong),
+ ("ullTotalPageFile", ctypes.c_ulonglong),
+ ("ullAvailPageFile", ctypes.c_ulonglong),
+ ("ullTotalVirtual", ctypes.c_ulonglong),
+ ("ullAvailVirtual", ctypes.c_ulonglong),
+ ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
+ ]
+
+ stat = MEMORYSTATUSEX()
+ stat.dwLength = ctypes.sizeof(stat)
+ ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
+
+ mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30))) # total / 4GB
+ hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
+ return min(mem_limit, hard_cap)
+ elif sys.platform.startswith('linux'):
+ with open("/proc/meminfo") as meminfo:
+ memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
+ for line in meminfo:
+ match = memtotal_re.match(line)
+ if not match:
+ continue
+ # Allow 8Gb per link on Linux because Gold is quite memory hungry
+ return max(1, int(match.group(1)) / (8 * (2 ** 20)))
+ return 1
+ elif sys.platform == 'darwin':
+ try:
+ avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
+ # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
+ # 4GB per ld process allows for some more bloat.
+ return max(1, avail_bytes / (4 * (2 ** 30))) # total / 4GB
+ except:
+ return 1
+ else:
+ # TODO(scottmg): Implement this for other platforms.
+ return 1
+
+
+def _GetWinLinkRuleNameSuffix(embed_manifest, link_incremental):
+ """Returns the suffix used to select an appropriate linking rule depending on
+ whether the manifest embedding and/or incremental linking is enabled."""
+ suffix = ''
+ if embed_manifest:
+ suffix += '_embed'
+ if link_incremental:
+ suffix += '_inc'
+ return suffix
+
+
+def _AddWinLinkRules(master_ninja, embed_manifest, link_incremental):
+ """Adds link rules for Windows platform to |master_ninja|."""
+ def FullLinkCommand(ldcmd, out, binary_type):
+ cmd = ('cmd /c %(ldcmd)s'
+ ' && %(python)s gyp-win-tool manifest-wrapper $arch'
+ ' cmd /c if exist %(out)s.manifest del %(out)s.manifest'
+ ' && %(python)s gyp-win-tool manifest-wrapper $arch'
+ ' $mt -nologo -manifest $manifests')
+ if embed_manifest and not link_incremental:
+ # Embed manifest into a binary. If incremental linking is enabled,
+ # embedding is postponed to the re-linking stage (see below).
+ cmd += ' -outputresource:%(out)s;%(resname)s'
+ else:
+ # Save manifest as an external file.
+ cmd += ' -out:%(out)s.manifest'
+ if link_incremental:
+ # There is no point in generating separate rule for the case when
+ # incremental linking is enabled, but manifest embedding is disabled.
+ # In that case the basic rule should be used (e.g. 'link').
+ # See also implementation of _GetWinLinkRuleNameSuffix().
+ assert embed_manifest
+ # Make .rc file out of manifest, compile it to .res file and re-link.
+ cmd += (' && %(python)s gyp-win-tool manifest-to-rc $arch'
+ ' %(out)s.manifest %(out)s.manifest.rc %(resname)s'
+ ' && %(python)s gyp-win-tool rc-wrapper $arch $rc'
+ ' %(out)s.manifest.rc'
+ ' && %(ldcmd)s %(out)s.manifest.res')
+ resource_name = {
+ 'exe': '1',
+ 'dll': '2',
+ }[binary_type]
+ return cmd % {'python': sys.executable,
+ 'out': out,
+ 'ldcmd': ldcmd,
+ 'resname': resource_name}
+
+ rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest, link_incremental)
+ dlldesc = 'LINK%s(DLL) $dll' % rule_name_suffix.upper()
+ dllcmd = ('%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo $implibflag /DLL /OUT:$dll '
+ '/PDB:$dll.pdb @$dll.rsp' % sys.executable)
+ dllcmd = FullLinkCommand(dllcmd, '$dll', 'dll')
+ master_ninja.rule('solink' + rule_name_suffix,
+ description=dlldesc, command=dllcmd,
+ rspfile='$dll.rsp',
+ rspfile_content='$libs $in_newline $ldflags',
+ restat=True,
+ pool='link_pool')
+ master_ninja.rule('solink_module' + rule_name_suffix,
+ description=dlldesc, command=dllcmd,
+ rspfile='$dll.rsp',
+ rspfile_content='$libs $in_newline $ldflags',
+ restat=True,
+ pool='link_pool')
+ # Note that ldflags goes at the end so that it has the option of
+ # overriding default settings earlier in the command line.
+ exe_cmd = ('%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
+ sys.executable)
+ exe_cmd = FullLinkCommand(exe_cmd, '$out', 'exe')
+ master_ninja.rule('link' + rule_name_suffix,
+ description='LINK%s $out' % rule_name_suffix.upper(),
+ command=exe_cmd,
+ rspfile='$out.rsp',
+ rspfile_content='$in_newline $libs $ldflags',
+ pool='link_pool')
+
+
def GenerateOutputForConfig(target_list, target_dicts, data, params,
config_name):
options = params['options']
flavor = gyp.common.GetFlavor(params)
generator_flags = params.get('generator_flags', {})
- # generator_dir: relative path from pwd to where make puts build files.
- # Makes migrating from make to ninja easier, ninja doesn't put anything here.
- generator_dir = os.path.relpath(params['options'].generator_output or '.')
-
- # output_dir: relative path from generator_dir to the build directory.
- output_dir = generator_flags.get('output_dir', 'out')
-
# build_dir: relative path from source root to our output files.
# e.g. "out/Debug"
- build_dir = os.path.normpath(os.path.join(generator_dir,
- output_dir,
- config_name))
+ build_dir = os.path.normpath(
+ os.path.join(ComputeOutputDir(params), config_name))
toplevel_build = os.path.join(options.toplevel_dir, build_dir)
master_ninja = ninja_syntax.Writer(
OpenOutput(os.path.join(toplevel_build, 'build.ninja')),
width=120)
- case_sensitive_filesystem = not os.path.exists(
- os.path.join(toplevel_build, 'BUILD.NINJA'))
# Put build-time support tools in out/{config_name}.
gyp.common.CopyTool(flavor, toplevel_build)
@@ -1364,17 +1671,20 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# 'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
# to cc/cxx.
if flavor == 'win':
+ # Overridden by local arch choice in the use_deps case.
+ # Chromium's ffmpeg c99conv.py currently looks for a 'cc =' line in
+ # build.ninja so needs something valid here. http://crbug.com/233985
cc = 'cl.exe'
cxx = 'cl.exe'
ld = 'link.exe'
- gyp.msvs_emulation.GenerateEnvironmentFiles(
- toplevel_build, generator_flags, OpenOutput)
ld_host = '$ld'
else:
cc = 'gcc'
cxx = 'g++'
- ld = '$cxx'
- ld_host = '$cxx_host'
+ ld = '$cc'
+ ldxx = '$cxx'
+ ld_host = '$cc_host'
+ ldxx_host = '$cxx_host'
cc_host = None
cxx_host = None
@@ -1385,40 +1695,40 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
make_global_settings = data[build_file].get('make_global_settings', [])
build_to_root = gyp.common.InvertRelativePath(build_dir,
options.toplevel_dir)
- flock = 'flock'
- if flavor == 'mac':
- flock = './gyp-mac-tool flock'
wrappers = {}
- if flavor != 'win':
- wrappers['LINK'] = flock + ' linker.lock'
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)
- if key == 'LD':
- ld = os.path.join(build_to_root, value)
if key == 'CC.host':
cc_host = os.path.join(build_to_root, value)
cc_host_global_setting = value
if key == 'CXX.host':
cxx_host = os.path.join(build_to_root, value)
cxx_host_global_setting = value
- if key == 'LD.host':
- ld_host = os.path.join(build_to_root, value)
if key.endswith('_wrapper'):
wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
+ # Support wrappers from environment variables too.
+ for key, value in os.environ.iteritems():
+ if key.lower().endswith('_wrapper'):
+ key_prefix = key[:-len('_wrapper')]
+ key_prefix = re.sub(r'\.HOST$', '.host', key_prefix)
+ wrappers[key_prefix] = os.path.join(build_to_root, value)
+
+ if flavor == 'win':
+ cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
+ toplevel_build, generator_flags, OpenOutput)
+ for arch, path in cl_paths.iteritems():
+ master_ninja.variable(
+ 'cl_' + arch, CommandWithWrapper('CC', wrappers,
+ QuoteShellArgument(path, flavor)))
+
cc = GetEnvironFallback(['CC_target', 'CC'], cc)
master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx))
- ld = GetEnvironFallback(['LD_target', 'LD'], ld)
-
- if not cc_host:
- cc_host = cc
- if not cxx_host:
- cxx_host = cxx
if flavor == 'win':
master_ninja.variable('ld', ld)
@@ -1427,41 +1737,54 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
master_ninja.variable('rc', 'rc.exe')
master_ninja.variable('asm', 'ml.exe')
master_ninja.variable('mt', 'mt.exe')
- master_ninja.variable('use_dep_database', '1')
else:
master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
+ master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
- master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
- cc_host = GetEnvironFallback(['CC_host'], cc_host)
- cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
- ld_host = GetEnvironFallback(['LD_host'], ld_host)
-
- # The environment variable could be used in 'make_global_settings', like
- # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
- if '$(CC)' in cc_host and cc_host_global_setting:
- cc_host = cc_host_global_setting.replace('$(CC)', cc)
- if '$(CXX)' in cxx_host and cxx_host_global_setting:
- cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
- master_ninja.variable('cc_host',
- CommandWithWrapper('CC.host', wrappers, cc_host))
- master_ninja.variable('cxx_host',
- CommandWithWrapper('CXX.host', wrappers, cxx_host))
- if flavor == 'win':
- master_ninja.variable('ld_host', ld_host)
- else:
- master_ninja.variable('ld_host', CommandWithWrapper(
- 'LINK', wrappers, ld_host))
+ if generator_supports_multiple_toolsets:
+ if not cc_host:
+ cc_host = cc
+ if not cxx_host:
+ cxx_host = cxx
+
+ master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
+ cc_host = GetEnvironFallback(['CC_host'], cc_host)
+ cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
+
+ # The environment variable could be used in 'make_global_settings', like
+ # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
+ if '$(CC)' in cc_host and cc_host_global_setting:
+ cc_host = cc_host_global_setting.replace('$(CC)', cc)
+ if '$(CXX)' in cxx_host and cxx_host_global_setting:
+ cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
+ master_ninja.variable('cc_host',
+ CommandWithWrapper('CC.host', wrappers, cc_host))
+ master_ninja.variable('cxx_host',
+ CommandWithWrapper('CXX.host', wrappers, cxx_host))
+ if flavor == 'win':
+ master_ninja.variable('ld_host', ld_host)
+ else:
+ master_ninja.variable('ld_host', CommandWithWrapper(
+ 'LINK', wrappers, ld_host))
+ master_ninja.variable('ldxx_host', CommandWithWrapper(
+ 'LINK', wrappers, ldxx_host))
+
+ master_ninja.newline()
+ master_ninja.pool('link_pool', depth=GetDefaultConcurrentLinks())
master_ninja.newline()
+ deps = 'msvc' if flavor == 'win' else 'gcc'
+
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')
+ depfile='$out.d',
+ deps=deps)
master_ninja.rule(
'cc_s',
description='CC $out',
@@ -1472,13 +1795,14 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
description='CXX $out',
command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
'$cflags_pch_cc -c $in -o $out'),
- depfile='$out.d')
+ depfile='$out.d',
+ deps=deps)
else:
- cc_command = ('ninja -t msvc -o $out -e $arch '
+ cc_command = ('ninja -t msvc -e $arch ' +
'-- '
'$cc /nologo /showIncludes /FC '
'@$out.rsp /c $in /Fo$out /Fd$pdbname ')
- cxx_command = ('ninja -t msvc -o $out -e $arch '
+ cxx_command = ('ninja -t msvc -e $arch ' +
'-- '
'$cxx /nologo /showIncludes /FC '
'@$out.rsp /c $in /Fo$out /Fd$pdbname ')
@@ -1486,16 +1810,16 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
'cc',
description='CC $out',
command=cc_command,
- depfile='$out.d',
rspfile='$out.rsp',
- rspfile_content='$defines $includes $cflags $cflags_c')
+ rspfile_content='$defines $includes $cflags $cflags_c',
+ deps=deps)
master_ninja.rule(
'cxx',
description='CXX $out',
command=cxx_command,
- depfile='$out.d',
rspfile='$out.rsp',
- rspfile_content='$defines $includes $cflags $cflags_cc')
+ rspfile_content='$defines $includes $cflags $cflags_cc',
+ deps=deps)
master_ninja.rule(
'idl',
description='IDL $in',
@@ -1507,7 +1831,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
description='RC $in',
# Note: $in must be last otherwise rc.exe complains.
command=('%s gyp-win-tool rc-wrapper '
- '$arch $rc $defines $includes $rcflags /fo$out $in' %
+ '$arch $rc $defines $resource_includes $rcflags /fo$out $in' %
sys.executable))
master_ninja.rule(
'asm',
@@ -1549,18 +1873,22 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
restat=True,
command=(mtime_preserving_solink_base % {
'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
- '$libs'}))
+ '$libs'}),
+ pool='link_pool')
master_ninja.rule(
'solink_module',
description='SOLINK(module) $lib',
restat=True,
command=(mtime_preserving_solink_base % {
- 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group $libs'}))
+ 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
+ '$libs'}),
+ pool='link_pool')
master_ninja.rule(
'link',
description='LINK $out',
command=('$ld $ldflags -o $out '
- '-Wl,--start-group $in $solibs -Wl,--end-group $libs'))
+ '-Wl,--start-group $in $solibs -Wl,--end-group $libs'),
+ pool='link_pool')
elif flavor == 'win':
master_ninja.rule(
'alink',
@@ -1570,51 +1898,27 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
sys.executable),
rspfile='$out.rsp',
rspfile_content='$in_newline $libflags')
- dlldesc = 'LINK(DLL) $dll'
- dllcmd = ('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo $implibflag /DLL /OUT:$dll '
- '/PDB:$dll.pdb @$dll.rsp' % sys.executable)
- dllcmd += (' && %s gyp-win-tool manifest-wrapper $arch '
- 'cmd /c if exist $dll.manifest del $dll.manifest' %
- sys.executable)
- dllcmd += (' && %s gyp-win-tool manifest-wrapper $arch '
- '$mt -nologo -manifest $manifests -out:$dll.manifest' %
- sys.executable)
- master_ninja.rule('solink', description=dlldesc, command=dllcmd,
- rspfile='$dll.rsp',
- rspfile_content='$libs $in_newline $ldflags',
- restat=True)
- master_ninja.rule('solink_module', description=dlldesc, command=dllcmd,
- rspfile='$dll.rsp',
- rspfile_content='$libs $in_newline $ldflags',
- restat=True)
- # Note that ldflags goes at the end so that it has the option of
- # overriding default settings earlier in the command line.
- master_ninja.rule(
- 'link',
- description='LINK $out',
- command=('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp && '
- '%s gyp-win-tool manifest-wrapper $arch '
- 'cmd /c if exist $out.manifest del $out.manifest && '
- '%s gyp-win-tool manifest-wrapper $arch '
- '$mt -nologo -manifest $manifests -out:$out.manifest' %
- (sys.executable, sys.executable, sys.executable)),
- rspfile='$out.rsp',
- rspfile_content='$in_newline $libs $ldflags')
+ _AddWinLinkRules(master_ninja, embed_manifest=True, link_incremental=True)
+ _AddWinLinkRules(master_ninja, embed_manifest=True, link_incremental=False)
+ _AddWinLinkRules(master_ninja, embed_manifest=False, link_incremental=False)
+ # Do not generate rules for embed_manifest=False and link_incremental=True
+ # because in that case rules for (False, False) should be used (see
+ # implementation of _GetWinLinkRuleNameSuffix()).
else:
master_ninja.rule(
'objc',
description='OBJC $out',
command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
'$cflags_pch_objc -c $in -o $out'),
- depfile='$out.d')
+ depfile='$out.d',
+ deps=deps)
master_ninja.rule(
'objcxx',
description='OBJCXX $out',
command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
'$cflags_pch_objcc -c $in -o $out'),
- depfile='$out.d')
+ depfile='$out.d',
+ deps=deps)
master_ninja.rule(
'alink',
description='LIBTOOL-STATIC $out, POSTBUILDS',
@@ -1622,9 +1926,14 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
'./gyp-mac-tool filter-libtool libtool $libtool_flags '
'-static -o $out $in'
'$postbuilds')
+ master_ninja.rule(
+ 'lipo',
+ description='LIPO $out, POSTBUILDS',
+ command='rm -f $out && lipo -create $in -output $out$postbuilds')
# Record the public interface of $lib in $lib.TOC. See the corresponding
# comment in the posix section above for details.
+ solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
mtime_preserving_solink_base = (
'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
# Always force dependent targets to relink if this library
@@ -1638,37 +1947,57 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
'mv ${lib}.tmp ${lib}.TOC ; '
'fi; '
'fi'
- % { 'solink': '$ld -shared $ldflags -o $lib %(suffix)s',
+ % { 'solink': solink_base,
'extract_toc':
'{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
- # TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass
- # -bundle -single_module here (for osmesa.so).
+ solink_suffix = '$in $solibs $libs$postbuilds'
master_ninja.rule(
'solink',
description='SOLINK $lib, POSTBUILDS',
restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '$in $solibs $libs$postbuilds'}))
+ command=mtime_preserving_solink_base % {'suffix': solink_suffix,
+ 'type': '-shared'},
+ pool='link_pool')
+ master_ninja.rule(
+ 'solink_notoc',
+ description='SOLINK $lib, POSTBUILDS',
+ restat=True,
+ command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
+ pool='link_pool')
+
+ solink_module_suffix = '$in $solibs $libs$postbuilds'
master_ninja.rule(
'solink_module',
description='SOLINK(module) $lib, POSTBUILDS',
restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '$in $solibs $libs$postbuilds'}))
+ command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
+ 'type': '-bundle'},
+ pool='link_pool')
+ master_ninja.rule(
+ 'solink_module_notoc',
+ description='SOLINK(module) $lib, POSTBUILDS',
+ restat=True,
+ command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
+ pool='link_pool')
master_ninja.rule(
'link',
description='LINK $out, POSTBUILDS',
command=('$ld $ldflags -o $out '
- '$in $solibs $libs$postbuilds'))
+ '$in $solibs $libs$postbuilds'),
+ pool='link_pool')
master_ninja.rule(
- 'infoplist',
- description='INFOPLIST $out',
+ 'preprocess_infoplist',
+ description='PREPROCESS INFOPLIST $out',
command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
'plutil -convert xml1 $out $out'))
master_ninja.rule(
+ 'copy_infoplist',
+ description='COPY INFOPLIST $in',
+ command='$env ./gyp-mac-tool copy-info-plist $in $out $keys')
+ master_ninja.rule(
'mac_tool',
description='MACTOOL $mactool_cmd $in',
command='$env ./gyp-mac-tool $mactool_cmd $in $out')
@@ -1710,6 +2039,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# target_short_names is a map from target short name to a list of Target
# objects.
target_short_names = {}
+
for qualified_target in target_list:
# qualified_target is like: third_party/icu/icu.gyp:icui18n#target
build_file, name, toolset = \
@@ -1731,14 +2061,21 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
obj += '.' + toolset
output_file = os.path.join(obj, base_path, name + '.ninja')
- abs_build_dir = os.path.abspath(toplevel_build)
+ ninja_output = StringIO()
writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
- OpenOutput(os.path.join(toplevel_build, output_file)),
+ ninja_output,
+ toplevel_build, output_file,
flavor, toplevel_dir=options.toplevel_dir)
- master_ninja.subninja(output_file)
- target = writer.WriteSpec(
- spec, config_name, generator_flags, case_sensitive_filesystem)
+ target = writer.WriteSpec(spec, config_name, generator_flags)
+
+ if ninja_output.tell() > 0:
+ # Only create files for ninja files that actually have contents.
+ with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file:
+ ninja_file.write(ninja_output.getvalue())
+ ninja_output.close()
+ master_ninja.subninja(output_file)
+
if target:
if name != target.FinalOutput() and spec['toolset'] == 'target':
target_short_names.setdefault(name, []).append(target)
@@ -1781,6 +2118,10 @@ def CallGenerateOutputForConfig(arglist):
def GenerateOutput(target_list, target_dicts, data, params):
+ # Update target_dicts for iOS device builds.
+ target_dicts = gyp.xcode_emulation.CloneConfigurationForDeviceAndEmulator(
+ target_dicts)
+
user_config = params.get('generator_flags', {}).get('config', None)
if gyp.common.GetFlavor(params) == 'win':
target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
@@ -1799,7 +2140,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
for config_name in config_names:
arglists.append(
(target_list, target_dicts, data, params, config_name))
- pool.map(CallGenerateOutputForConfig, arglists)
+ pool.map(CallGenerateOutputForConfig, arglists)
except KeyboardInterrupt, e:
pool.terminate()
raise e