summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2011-09-08 01:11:08 -0700
committerRyan Dahl <ry@tinyclouds.org>2011-09-08 01:11:45 -0700
commitee2c12d48e52c066c74fd646d6a38e880b0bfa0a (patch)
treed579379ec18b28d2aa3b6091ad5df9ee719fc0e8 /tools
parent0bca54444a5bb869ddebaadc65cd0bf381bb1e81 (diff)
downloadnode-new-ee2c12d48e52c066c74fd646d6a38e880b0bfa0a.tar.gz
Upgrade GYP to r1034
Diffstat (limited to 'tools')
-rw-r--r--tools/gyp/pylib/gyp/MSVSNew.py5
-rw-r--r--tools/gyp/pylib/gyp/generator/dump_dependency_json.py9
-rw-r--r--tools/gyp/pylib/gyp/generator/make.py200
-rw-r--r--tools/gyp/pylib/gyp/generator/msvs.py36
-rw-r--r--tools/gyp/pylib/gyp/generator/ninja.py375
-rw-r--r--tools/gyp/pylib/gyp/input.py43
-rw-r--r--tools/gyp/pylib/gyp/ninja_syntax.py6
7 files changed, 442 insertions, 232 deletions
diff --git a/tools/gyp/pylib/gyp/MSVSNew.py b/tools/gyp/pylib/gyp/MSVSNew.py
index 9b9b848fe7..ae8cbee688 100644
--- a/tools/gyp/pylib/gyp/MSVSNew.py
+++ b/tools/gyp/pylib/gyp/MSVSNew.py
@@ -248,10 +248,13 @@ class MSVSSolution:
sln_root = os.path.split(self.path)[0]
for e in all_entries:
relative_path = gyp.common.RelativePath(e.path, sln_root)
+ # msbuild does not accept an empty folder_name.
+ # use '.' in case relative_path is empty.
+ folder_name = relative_path.replace('/', '\\') or '.'
f.write('Project("%s") = "%s", "%s", "%s"\r\n' % (
e.entry_type_guid, # Entry type GUID
e.name, # Folder name
- relative_path.replace('/', '\\'), # Folder name (again)
+ folder_name, # Folder name (again)
e.get_guid(), # Entry GUID
))
diff --git a/tools/gyp/pylib/gyp/generator/dump_dependency_json.py b/tools/gyp/pylib/gyp/generator/dump_dependency_json.py
index aacf232c8b..ebb7ff97be 100644
--- a/tools/gyp/pylib/gyp/generator/dump_dependency_json.py
+++ b/tools/gyp/pylib/gyp/generator/dump_dependency_json.py
@@ -32,6 +32,15 @@ def CalculateVariables(default_variables, params):
default_variables['OS'] = generator_flags.get('os', 'linux')
+def CalculateGeneratorInputInfo(params):
+ """Calculate the generator specific info that gets fed to input (called by
+ gyp)."""
+ generator_flags = params.get('generator_flags', {})
+ if generator_flags.get('adjust_static_libraries', False):
+ global generator_wants_static_library_dependencies_adjusted
+ generator_wants_static_library_dependencies_adjusted = True
+
+
def GenerateOutput(target_list, target_dicts, data, params):
# Map of target -> list of targets it depends on.
edges = {}
diff --git a/tools/gyp/pylib/gyp/generator/make.py b/tools/gyp/pylib/gyp/generator/make.py
index b8914785bf..56a02eb06b 100644
--- a/tools/gyp/pylib/gyp/generator/make.py
+++ b/tools/gyp/pylib/gyp/generator/make.py
@@ -43,7 +43,6 @@ generator_default_variables = {
'INTERMEDIATE_DIR': '$(obj).$(TOOLSET)/geni',
'SHARED_INTERMEDIATE_DIR': '$(obj)/gen',
'PRODUCT_DIR': '$(builddir)',
- 'LIB_DIR': '$(obj).$(TOOLSET)',
'RULE_INPUT_ROOT': '%(INPUT_ROOT)s', # This gets expanded by Python.
'RULE_INPUT_PATH': '$(abspath $<)',
'RULE_INPUT_EXT': '$(suffix $<)',
@@ -76,6 +75,8 @@ def CalculateVariables(default_variables, params):
default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
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 Make generator.
@@ -95,6 +96,7 @@ def CalculateVariables(default_variables, params):
default_variables.setdefault('OS', 'linux')
default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
default_variables.setdefault('SHARED_LIB_DIR','$(builddir)/lib.$(TOOLSET)')
+ default_variables.setdefault('LIB_DIR', '$(obj).$(TOOLSET)')
def CalculateGeneratorInputInfo(params):
@@ -230,8 +232,10 @@ all_deps :=
#
# This will allow make to invoke N linker processes as specified in -jN.
FLOCK ?= %(flock)s $(builddir)/linker.lock
-LINK ?= $(FLOCK) $(CXX)
+%(make_global_settings)s
+
+LINK ?= $(FLOCK) $(CXX)
CC.target ?= $(CC)
CFLAGS.target ?= $(CFLAGS)
CXX.target ?= $(CXX)
@@ -738,6 +742,14 @@ class XcodeSettings(object):
else:
return self._GetStandaloneBinaryPath()
+ def _SdkPath(self):
+ sdk_root = 'macosx10.5'
+ if 'SDKROOT' in self._Settings():
+ sdk_root = self._Settings()['SDKROOT']
+ if sdk_root.startswith('macosx'):
+ sdk_root = 'MacOSX' + sdk_root[len('macosx'):]
+ return '/Developer/SDKs/%s.sdk' % sdk_root
+
def GetCflags(self, configname):
"""Returns flags that need to be added to .c, .cc, .m, and .mm
compilations."""
@@ -747,11 +759,9 @@ class XcodeSettings(object):
self.configname = configname
cflags = []
- sdk_root = 'Mac10.5'
+ sdk_root = self._SdkPath()
if 'SDKROOT' in self._Settings():
- sdk_root = self._Settings()['SDKROOT']
- cflags.append('-isysroot /Developer/SDKs/%s.sdk' % sdk_root)
- sdk_root_dir = '/Developer/SDKs/%s.sdk' % sdk_root
+ cflags.append('-isysroot %s' % sdk_root)
if self._Test('GCC_CW_ASM_SYNTAX', 'YES', default='YES'):
cflags.append('-fasm-blocks')
@@ -770,22 +780,21 @@ class XcodeSettings(object):
self._Appendf(cflags, 'GCC_OPTIMIZATION_LEVEL', '-O%s')
- dbg_format = self._Settings().get('DEBUG_INFORMATION_FORMAT', 'dwarf')
- if dbg_format == 'none':
- pass
- elif dbg_format == 'dwarf':
- cflags.append('-gdwarf-2')
- elif dbg_format == 'stabs':
- raise NotImplementedError('stabs debug format is not supported yet.')
- elif dbg_format == 'dwarf-with-dsym':
- # TODO(thakis): this is needed for mac_breakpad chromium builds, but not
- # for regular chromium builds.
- # -gdwarf-2 as well, but needs to invoke dsymutil after linking too:
- # dsymutil build/Default/TestAppGyp.app/Contents/MacOS/TestAppGyp \
- # -o build/Default/TestAppGyp.app.dSYM
- raise NotImplementedError('dsym debug format is not supported yet.')
- else:
- raise NotImplementedError('Unknown debug format %s' % dbg_format)
+ if self._Test('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES', default='YES'):
+ dbg_format = self._Settings().get('DEBUG_INFORMATION_FORMAT', 'dwarf')
+ if dbg_format == 'dwarf':
+ cflags.append('-gdwarf-2')
+ elif dbg_format == 'stabs':
+ raise NotImplementedError('stabs debug format is not supported yet.')
+ elif dbg_format == 'dwarf-with-dsym':
+ # TODO(thakis): this is needed for mac_breakpad chromium builds, but not
+ # for regular chromium builds.
+ # -gdwarf-2 as well, but needs to invoke dsymutil after linking too:
+ # dsymutil build/Default/TestAppGyp.app/Contents/MacOS/TestAppGyp \
+ # -o build/Default/TestAppGyp.app.dSYM
+ raise NotImplementedError('dsym debug format is not supported yet.')
+ else:
+ raise NotImplementedError('Unknown debug format %s' % dbg_format)
if self._Test('GCC_SYMBOLS_PRIVATE_EXTERN', 'YES', default='NO'):
cflags.append('-fvisibility=hidden')
@@ -802,6 +811,9 @@ class XcodeSettings(object):
self._WarnUnimplemented('ARCHS')
self._WarnUnimplemented('COPY_PHASE_STRIP')
self._WarnUnimplemented('DEPLOYMENT_POSTPROCESSING')
+ self._WarnUnimplemented('GCC_DEBUGGING_SYMBOLS')
+ self._WarnUnimplemented('GCC_ENABLE_OBJC_EXCEPTIONS')
+ self._WarnUnimplemented('GCC_ENABLE_OBJC_GC')
self._WarnUnimplemented('INFOPLIST_PREPROCESS')
self._WarnUnimplemented('INFOPLIST_PREPROCESSOR_DEFINITIONS')
self._WarnUnimplemented('STRIPFLAGS')
@@ -816,7 +828,7 @@ class XcodeSettings(object):
config = self.spec['configurations'][self.configname]
framework_dirs = config.get('mac_framework_dirs', [])
for directory in framework_dirs:
- cflags.append('-F ' + os.path.join(sdk_root_dir, directory))
+ cflags.append('-F ' + os.path.join(sdk_root, directory))
self.configname = None
return cflags
@@ -891,8 +903,8 @@ class XcodeSettings(object):
ldflags, 'DYLIB_CURRENT_VERSION', '-current_version %s')
self._Appendf(
ldflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
- self._Appendf(
- ldflags, 'SDKROOT', '-isysroot /Developer/SDKs/%s.sdk')
+ if 'SDKROOT' in self._Settings():
+ ldflags.append('-isysroot ' + self._SdkPath())
for library_path in self._Settings().get('LIBRARY_SEARCH_PATHS', []):
ldflags.append('-L' + library_path)
@@ -904,9 +916,7 @@ class XcodeSettings(object):
# TODO: Do not hardcode arch. Supporting fat binaries will be annoying.
ldflags.append('-arch i386')
- # Xcode adds the product directory by default. It writes static libraries
- # into the product directory. So add both.
- ldflags.append('-L' + generator_default_variables['LIB_DIR'])
+ # Xcode adds the product directory by default.
ldflags.append('-L' + generator_default_variables['PRODUCT_DIR'])
install_name = self.GetPerTargetSetting('LD_DYLIB_INSTALL_NAME')
@@ -955,6 +965,23 @@ class XcodeSettings(object):
self.configname = None
return ldflags
+ def GetPerTargetSettings(self):
+ """Gets a list of all the per-target settings. This will only fetch keys
+ whose values are the same across all configurations."""
+ first_pass = True
+ result = {}
+ for configname in sorted(self.xcode_settings.keys()):
+ if first_pass:
+ result = dict(self.xcode_settings[configname])
+ first_pass = False
+ else:
+ for key, value in self.xcode_settings[configname].iteritems():
+ if key not in result:
+ continue
+ elif result[key] != value:
+ del result[key]
+ return result
+
def GetPerTargetSetting(self, setting, default=None):
"""Tries to get xcode_settings.setting from spec. Assumes that the setting
has the same value in all configurations and throws otherwise."""
@@ -1531,7 +1558,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
path = generator_default_variables['PRODUCT_DIR']
dest_plist = os.path.join(path, self.xcode_settings.GetBundlePlistPath())
dest_plist = QuoteSpaces(dest_plist)
- self.WriteXcodeEnv(dest_plist, spec) # plists can contain envvars.
+ extra_settings = self.xcode_settings.GetPerTargetSettings()
+ # plists can contain envvars and substitute them into the file..
+ self.WriteXcodeEnv(dest_plist, spec, additional_settings=extra_settings)
self.WriteDoCmd([dest_plist], [info_plist], 'mac_tool,,,copy-info-plist',
part_of_all=True)
bundle_deps.append(dest_plist)
@@ -1698,6 +1727,11 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
return target_prefix + target + target_ext
+ def _InstallImmediately(self):
+ return self.toolset == 'target' and self.flavor == 'mac' and self.type in (
+ 'static_library', 'executable', 'shared_library', 'loadable_module')
+
+
def ComputeOutput(self, spec):
"""Return the 'output' (full output path) of a gyp spec.
@@ -1710,7 +1744,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
return '' # Doesn't have any output.
path = os.path.join('$(obj).' + self.toolset, self.path)
- if self.type == 'executable':
+ if self.type == 'executable' or self._InstallImmediately():
path = '$(builddir)'
path = spec.get('product_dir', path)
return os.path.join(path, self.ComputeOutputBasename(spec))
@@ -1838,7 +1872,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# After the framework is built, package it. Needs to happen before
# postbuilds, since postbuilds depend on this.
if self.type in ('shared_library', 'loadable_module'):
- self.WriteLn('\t@$(call do_cmd,mac_package_framework,0,0,%s)' %
+ self.WriteLn('\t@$(call do_cmd,mac_package_framework,,,%s)' %
self.xcode_settings.GetFrameworkVersion())
# Bundle postbuilds can depend on the whole bundle, so run them after
@@ -1860,6 +1894,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
if postbuilds:
assert not self.is_mac_bundle, ('Postbuilds for bundles should be done '
'on the bundle, not the binary (target \'%s\')' % self.target)
+ assert 'product_dir' not in spec, ('Postbuilds do not work with '
+ 'custom product_dir')
self.WriteXcodeEnv(self.output_binary, spec) # For postbuilds
postbuilds = [EscapeShellArgument(p) for p in postbuilds]
self.WriteLn('%s: builddir := $(abs_builddir)' % self.output_binary)
@@ -1921,8 +1957,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
file_desc = 'executable'
install_path = self._InstallableTargetInstallPath()
installable_deps = [self.output]
- if self.is_mac_bundle:
- # Bundles are created in their install_path location immediately.
+ if self.flavor == 'mac' and not 'product_dir' in spec:
+ # On mac, products are created in install_path immediately.
assert install_path == self.output, '%s != %s' % (
install_path, self.output)
@@ -2102,10 +2138,15 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
modules.append(filename[len(prefix):-len(suffix)])
return modules
+ # Retrieve the default value of 'SHARED_LIB_SUFFIX'
+ params = {'flavor': 'linux'}
+ default_variables = {}
+ CalculateVariables(default_variables, params)
+
self.WriteList(
DepsToModules(link_deps,
generator_default_variables['SHARED_LIB_PREFIX'],
- generator_default_variables['SHARED_LIB_SUFFIX']),
+ default_variables['SHARED_LIB_SUFFIX']),
'LOCAL_SHARED_LIBRARIES')
self.WriteList(
DepsToModules(link_deps,
@@ -2132,26 +2173,17 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
for a full list."""
if self.flavor != 'mac': return {}
+ built_products_dir = generator_default_variables['PRODUCT_DIR']
def StripProductDir(s):
- product_dir = generator_default_variables['PRODUCT_DIR']
- assert s.startswith(product_dir), s
- return s[len(product_dir) + 1:]
+ assert s.startswith(built_products_dir), s
+ return s[len(built_products_dir) + 1:]
product_name = spec.get('product_name', self.output)
- # Some postbuilds try to read a build output file at
- # ""${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}". Static libraries end up
- # "$(obj).target", so
- # BUILT_PRODUCTS_DIR is $(builddir)
- # FULL_PRODUCT_NAME is $(out).target/path/to/lib.a
- # Since $(obj) contains out/Debug already, the postbuild
- # would get out/Debug/out/Debug/obj.target/path/to/lib.a. To prevent this,
- # remove the "out/Debug" prefix from $(obj).
- if product_name.startswith('$(obj)'):
- product_name = (
- '$(subst $(builddir)/,,$(obj))' + product_name[len('$(obj)'):])
+ if self._InstallImmediately():
+ if product_name.startswith(built_products_dir):
+ product_name = StripProductDir(product_name)
- built_products_dir = generator_default_variables['PRODUCT_DIR']
srcroot = self.path
if target_relative_path:
built_products_dir = os.path.relpath(built_products_dir, srcroot)
@@ -2171,11 +2203,14 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
}
if self.type in ('executable', 'shared_library'):
env['EXECUTABLE_NAME'] = os.path.basename(self.output_binary)
- if self.type in ('executable', 'shared_library', 'loadable_module'):
+ if self.type in (
+ 'executable', 'static_library', 'shared_library', 'loadable_module'):
env['EXECUTABLE_PATH'] = self.xcode_settings.GetExecutablePath()
if self.is_mac_bundle:
env['CONTENTS_FOLDER_PATH'] = \
self.xcode_settings.GetBundleContentsFolderPath()
+ env['UNLOCALIZED_RESOURCES_FOLDER_PATH'] = \
+ self.xcode_settings.GetBundleResourceFolder()
env['INFOPLIST_PATH'] = self.xcode_settings.GetBundlePlistPath()
# TODO(thakis): Remove this.
@@ -2186,16 +2221,40 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
return env
- def WriteXcodeEnv(self, target, spec, target_relative_path=False):
- env = self.GetXcodeEnv(spec, target_relative_path)
- # For
- # foo := a\ b
- # the escaped space does the right thing. For
- # export foo := a\ b
- # it does not -- the backslash is written to the env as literal character.
- # Hence, unescape all spaces here.
+ def WriteXcodeEnv(self,
+ target,
+ spec,
+ target_relative_path=False,
+ additional_settings={}):
+ env = additional_settings
+ env.update(self.GetXcodeEnv(spec, target_relative_path))
+
+ # Keys whose values will not have $(builddir) replaced with $(abs_builddir).
+ # These have special substitution rules in some cases; see above in
+ # GetXcodeEnv() for the full rationale.
+ keys_to_not_absolutify = ('PRODUCT_NAME', 'FULL_PRODUCT_NAME')
+
+ # Perform some transformations that are required to mimic Xcode behavior.
for k in env:
+ # Values that are not strings but are, for example, lists or tuples such
+ # as LDFLAGS or CFLAGS, should not be written out because they are
+ # not needed and it's undefined how multi-valued keys should be written.
+ if not isinstance(env[k], str):
+ continue
+
+ # For
+ # foo := a\ b
+ # the escaped space does the right thing. For
+ # export foo := a\ b
+ # it does not -- the backslash is written to the env as literal character.
+ # Hence, unescape all spaces here.
v = env[k].replace(r'\ ', ' ')
+
+ # Xcode works purely with absolute paths. When writing env variables to
+ # mimic its usage, replace $(builddir) with $(abs_builddir).
+ if k not in keys_to_not_absolutify:
+ v = v.replace('$(builddir)', '$(abs_builddir)')
+
self.WriteLn('%s: export %s := %s' % (target, k, v))
@@ -2399,6 +2458,26 @@ def GenerateOutput(target_list, target_dicts, data, params):
})
header_params.update(RunSystemTests(flavor))
+ build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
+ make_global_settings_dict = data[build_file].get('make_global_settings', {})
+ make_global_settings = ''
+ for key, value in make_global_settings_dict:
+ if value[0] != '$':
+ value = '$(abspath %s)' % value
+ if key == 'LINK':
+ make_global_settings += '%s ?= $(FLOCK) %s\n' % (key, value)
+ elif key in ['CC', 'CXX']:
+ make_global_settings += (
+ 'ifneq (,$(filter $(origin %s), undefined default))\n' % key)
+ # Let gyp-time envvars win over global settings.
+ if key in os.environ:
+ value = os.environ[key]
+ make_global_settings += ' %s = %s\n' % (key, value)
+ make_global_settings += 'endif\n'
+ else:
+ make_global_settings += '%s ?= %s\n' % (key, value)
+ header_params['make_global_settings'] = make_global_settings
+
ensure_directory_exists(makefile_path)
root_makefile = open(makefile_path, 'w')
root_makefile.write(SHARED_HEADER % header_params)
@@ -2433,6 +2512,11 @@ def GenerateOutput(target_list, target_dicts, data, params):
for qualified_target in target_list:
build_file, target, toolset = gyp.common.ParseQualifiedTarget(
qualified_target)
+
+ this_make_global_settings = data[build_file].get('make_global_settings', {})
+ assert make_global_settings_dict == this_make_global_settings, (
+ "make_global_settings needs to be the same for all targets.")
+
build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir))
included_files = data[build_file]['included_files']
for included_file in included_files:
diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py
index 92cbf776a7..dfecbda786 100644
--- a/tools/gyp/pylib/gyp/generator/msvs.py
+++ b/tools/gyp/pylib/gyp/generator/msvs.py
@@ -56,7 +56,7 @@ generator_default_variables = {
# of the warnings.
# TODO(jeanluc) I had: 'LIB_DIR': '$(OutDir)lib',
- #'LIB_DIR': '$(OutDir)/lib',
+ 'LIB_DIR': '$(OutDir)/lib',
'RULE_INPUT_ROOT': '$(InputName)',
'RULE_INPUT_EXT': '$(InputExt)',
'RULE_INPUT_NAME': '$(InputFileName)',
@@ -575,18 +575,7 @@ def _GenerateExternalRules(rules, output_dir, spec,
'IntDir=$(IntDir)',
'-j', '${NUMBER_OF_PROCESSORS_PLUS_1}',
'-f', filename]
-
- # Currently this weird argument munging is used to duplicate the way a
- # python script would need to be run as part of the chrome tree.
- # Eventually we should add some sort of rule_default option to set this
- # per project. For now the behavior chrome needs is the default.
- mcs = rule.get('msvs_cygwin_shell')
- if mcs is None:
- mcs = int(spec.get('msvs_cygwin_shell', 1))
- elif isinstance(mcs, str):
- mcs = int(mcs)
- quote_cmd = int(rule.get('msvs_quote_cmd', 1))
- cmd = _BuildCommandLineForRuleRaw(spec, cmd, mcs, False, quote_cmd)
+ cmd = _BuildCommandLineForRuleRaw(spec, cmd, True, False, True)
# Insert makefile as 0'th input, so it gets the action attached there,
# as this is easier to understand from in the IDE.
all_inputs = list(all_inputs)
@@ -1117,7 +1106,7 @@ def _GetOutputFilePathAndTool(spec):
# TODO(jeanluc) If we want to avoid the MSB8012 warnings in
# VisualStudio 2010, we will have to change the value of $(OutDir)
# to contain the \lib suffix, rather than doing it as below.
- 'static_library': ('VCLibrarianTool', 'Lib', '$(OutDir)\\', '.lib'),
+ 'static_library': ('VCLibrarianTool', 'Lib', '$(OutDir)\\lib\\', '.lib'),
'dummy_executable': ('VCLinkerTool', 'Link', '$(IntDir)\\', '.junk'),
}
output_file_props = output_file_map.get(spec['type'])
@@ -2650,6 +2639,7 @@ def _GetMSBuildSources(spec, sources, exclusions, extension_to_rule_name,
def _AddSources2(spec, sources, exclusions, grouped_sources,
extension_to_rule_name, sources_handled_by_action):
+ extensions_excluded_from_precompile = []
for source in sources:
if isinstance(source, MSVSProject.Filter):
_AddSources2(spec, source.contents, exclusions, grouped_sources,
@@ -2670,12 +2660,30 @@ def _AddSources2(spec, sources, exclusions, grouped_sources,
for config_name, configuration in spec['configurations'].iteritems():
precompiled_source = configuration.get('msvs_precompiled_source', '')
precompiled_source = _FixPath(precompiled_source)
+ if not extensions_excluded_from_precompile:
+ # If the precompiled header is generated by a C source, we must
+ # not try to use it for C++ sources, and vice versa.
+ basename, extension = os.path.splitext(precompiled_source)
+ if extension == '.c':
+ extensions_excluded_from_precompile = ['.cc', '.cpp', '.cxx']
+ else:
+ extensions_excluded_from_precompile = ['.c']
+
if precompiled_source == source:
condition = _GetConfigurationCondition(config_name, configuration)
detail.append(['PrecompiledHeader',
{'Condition': condition},
'Create'
])
+ else:
+ # Turn off precompiled header usage for source files of a
+ # different type than the file that generated the
+ # precompiled header.
+ for extension in extensions_excluded_from_precompile:
+ if source.endswith(extension):
+ detail.append(['PrecompiledHeader', ''])
+ detail.append(['ForcedIncludeFiles', ''])
+
group, element = _MapFileToMsBuildSourceType(source,
extension_to_rule_name)
grouped_sources[group].append([element, {'Include': source}] + detail)
diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py
index 8a8e2900ce..b63e42cdb0 100644
--- a/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/tools/gyp/pylib/gyp/generator/ninja.py
@@ -23,16 +23,20 @@ generator_default_variables = {
'STATIC_LIB_SUFFIX': '.a',
'SHARED_LIB_PREFIX': 'lib',
'SHARED_LIB_SUFFIX': '.so',
- # TODO: intermediate dir should *not* be shared between different targets.
- # Unfortunately, whatever we provide here gets written into many different
- # places within the gyp spec so it's difficult to make it target-specific.
- # Apparently we've made it this far with one global path for the make build
- # we're safe for now.
- 'INTERMEDIATE_DIR': '$b/geni',
- 'SHARED_INTERMEDIATE_DIR': '$b/gen',
- 'PRODUCT_DIR': '$b',
- 'SHARED_LIB_DIR': '$b/lib',
- 'LIB_DIR': '$b',
+
+ # Gyp expects the following variables to be expandable by the build
+ # system to the appropriate locations. Ninja prefers paths to be
+ # known at compile time. To resolve this, introduce special
+ # variables starting with $! (which begin with a $ so gyp knows it
+ # should be treated as a path, but is otherwise an invalid
+ # ninja/shell variable) that are passed to gyp here but expanded
+ # before writing out into the target .ninja files; see
+ # ExpandSpecial.
+ '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
@@ -43,51 +47,11 @@ generator_default_variables = {
'RULE_INPUT_NAME': '$name',
}
-NINJA_BASE = """\
-builddir = %(builddir)s
-# Short alias for builddir.
-b = %(builddir)s
-
-cc = %(cc)s
-cxx = %(cxx)s
-
-rule cc
- depfile = $out.d
- description = CC $out
- command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c $
- -c $in -o $out
-
-rule cxx
- depfile = $out.d
- description = CXX $out
- command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc $
- -c $in -o $out
-
-rule alink
- description = AR $out
- command = rm -f $out && ar rcsT $out $in
-
-rule solink
- description = SOLINK $out
- command = g++ -Wl,--threads -Wl,--thread-count=4 $
- -shared $ldflags -o $out -Wl,-soname=$soname $
- -Wl,--whole-archive $in -Wl,--no-whole-archive $libs
-
-rule link
- description = LINK $out
- command = g++ -Wl,--threads -Wl,--thread-count=4 $
- $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib $
- -Wl,--start-group $in -Wl,--end-group $libs
-
-rule stamp
- description = STAMP $out
- command = touch $out
-
-rule copy
- description = COPY $in $out
- command = ln -f $in $out 2>/dev/null || cp -af $in $out
-
-"""
+# TODO: enable cross compiling once we figure out:
+# - how to not build extra host objects in the non-cross-compile case.
+# - how to decide what the host compiler is (should not just be $cc).
+# - need ld_host as well.
+generator_supports_multiple_toolsets = False
def StripPrefix(arg, prefix):
@@ -106,19 +70,34 @@ def MaybeQuoteShellArgument(arg):
return arg
+def InvertRelativePath(path):
+ """Given a relative path like foo/bar, return the inverse relative path:
+ the path from the relative path back to the origin dir.
+
+ E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
+ should always produce the empty string."""
+
+ if not 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)
+
+
# A small discourse on paths as used within the Ninja build:
+# All files we produce (both at gyp and at build time) appear in the
+# build directory (e.g. out/Debug).
#
# Paths within a given .gyp file are always relative to the directory
# containing the .gyp file. Call these "gyp paths". This includes
# sources as well as the starting directory a given gyp rule/action
-# expects to be run from. We call this directory "base_dir" within
-# the per-.gyp-file NinjaWriter code.
+# expects to be run from. We call the path from the source root to
+# the gyp file the "base directory" within the per-.gyp-file
+# NinjaWriter code.
#
-# All paths as written into the .ninja files are relative to the root
-# of the tree. Call these paths "ninja paths". We set up the ninja
-# variable "$b" to be the path to the root of the build output,
-# e.g. out/Debug/. All files we produce (both at gyp and at build
-# time) appear in that output directory.
+# All paths as written into the .ninja files are relative to the build
+# directory. Call these paths "ninja paths".
#
# We translate between these two notions of paths with two helper
# functions:
@@ -131,24 +110,59 @@ def MaybeQuoteShellArgument(arg):
# to the input file name as well as the output target name.
class NinjaWriter:
- def __init__(self, target_outputs, base_dir, output_file):
+ def __init__(self, target_outputs, base_dir, build_dir, output_file):
+ """
+ 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
+ """
+
self.target_outputs = target_outputs
- # The root-relative path to the source .gyp file; by gyp
- # semantics, all input paths are relative to this.
self.base_dir = base_dir
+ self.build_dir = build_dir
self.ninja = ninja_syntax.Writer(output_file)
+ # Relative path from build output dir to base dir.
+ self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir)
+ # Relative path from base dir to build dir.
+ self.base_to_build = os.path.join(InvertRelativePath(base_dir), build_dir)
+
+ def ExpandSpecial(self, path, product_dir=None):
+ """Expand specials like $!PRODUCT_DIR in |path|.
+
+ If |product_dir| is None, assumes the cwd is already the product
+ dir. Otherwise, |product_dir| is the relative path to the product
+ dir.
+ """
+
+ PRODUCT_DIR = '$!PRODUCT_DIR'
+ if PRODUCT_DIR in path:
+ if product_dir:
+ path = path.replace(PRODUCT_DIR, product_dir)
+ else:
+ path = path.replace(PRODUCT_DIR + '/', '')
+ path = path.replace(PRODUCT_DIR, '.')
+
+ INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR'
+ if INTERMEDIATE_DIR in path:
+ int_dir = self.GypPathToUniqueOutput('gen')
+ # GypPathToUniqueOutput generates a path relative to the product dir,
+ # so insert product_dir in front if it is provided.
+ path = path.replace(INTERMEDIATE_DIR,
+ os.path.join(product_dir or '', int_dir))
+
+ return path
+
def GypPathToNinja(self, path):
"""Translate a gyp path to a ninja path.
See the above discourse on path conversions."""
- if path.startswith('$'):
- # If the path contains a reference to a ninja variable, we know
- # it's already relative to the source root.
- return path
- return os.path.normpath(os.path.join(self.base_dir, path))
+ if path.startswith('$!'):
+ return self.ExpandSpecial(path)
+ assert '$' not in path, path
+ return os.path.normpath(os.path.join(self.build_to_base, path))
- def GypPathToUniqueOutput(self, path, qualified=False):
+ def GypPathToUniqueOutput(self, path, qualified=True):
"""Translate a gyp path to a ninja path for writing output.
If qualified is True, qualify the resulting filename with the name
@@ -157,30 +171,28 @@ class NinjaWriter:
See the above discourse on path conversions."""
- # It may seem strange to discard components of the path, but we are just
- # attempting to produce a known-unique output filename; we don't want to
- # reuse any global directory.
- genvars = generator_default_variables
- assert not genvars['SHARED_INTERMEDIATE_DIR'].startswith(
- genvars['INTERMEDIATE_DIR'])
- path = StripPrefix(path, genvars['INTERMEDIATE_DIR'])
- path = StripPrefix(path, genvars['SHARED_INTERMEDIATE_DIR'])
- path = StripPrefix(path, '/')
- assert not path.startswith('$')
+ path = self.ExpandSpecial(path)
+ assert not path.startswith('$'), path
# Translate the path following this scheme:
# Input: foo/bar.gyp, target targ, references baz/out.o
- # Output: $b/obj/foo/baz/targ.out.o (if qualified)
- # $b/obj/foo/baz/out.o (otherwise)
+ # Output: obj/foo/baz/targ.out.o (if qualified)
+ # obj/foo/baz/out.o (otherwise)
+ # (and obj.host instead of obj for cross-compiles)
#
# Why this scheme and not some other one?
# 1) for a given input, you can compute all derived outputs by matching
# its path, even if the input is brought via a gyp file with '..'.
# 2) simple files like libraries and stamps have a simple filename.
+
+ obj = 'obj'
+ if self.toolset != 'target':
+ obj += '.' + self.toolset
+
path_dir, path_basename = os.path.split(path)
if qualified:
path_basename = self.name + '.' + path_basename
- return os.path.normpath(os.path.join('$b/obj', self.base_dir, path_dir,
+ return os.path.normpath(os.path.join(obj, self.base_dir, path_dir,
path_basename))
def StampPath(self, name):
@@ -188,7 +200,7 @@ class NinjaWriter:
Stamp files are used to collapse a dependency on a bunch of files
into a single file."""
- return self.GypPathToUniqueOutput(name + '.stamp', qualified=True)
+ return self.GypPathToUniqueOutput(name + '.stamp')
def WriteSpec(self, spec, config):
"""The main entry point for NinjaWriter: write the build rules for a spec.
@@ -201,6 +213,7 @@ class NinjaWriter:
return None
self.name = spec['target_name']
+ self.toolset = spec['toolset']
# Compute predepends for all rules.
# prebuild is the dependencies this target depends on before
@@ -230,13 +243,20 @@ class NinjaWriter:
link_deps = self.WriteSources(config, sources,
sources_predepends or prebuild)
# Some actions/rules output 'sources' that are already object files.
- link_deps += [f for f in sources if f.endswith('.o')]
+ link_deps += [self.GypPathToNinja(f) for f in sources if f.endswith('.o')]
# The final output of our target depends on the last output of the
# above steps.
+ output = None
final_deps = link_deps or sources_predepends or prebuild
if final_deps:
- return self.WriteTarget(spec, config, final_deps)
+ output = self.WriteTarget(spec, config, final_deps)
+ 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
def WriteActionsRulesCopies(self, spec, extra_sources, prebuild):
"""Write out the Actions, Rules, and Copies steps. Return any outputs
@@ -258,15 +278,28 @@ class NinjaWriter:
return outputs
+ def GenerateDescription(self, verb, message, fallback):
+ """Generate and return a description of a build step.
+
+ |verb| is the short summary, e.g. ACTION or RULE.
+ |message| is a hand-written description, or None if not available.
+ |fallback| is the gyp-level name of the step, usable as a fallback.
+ """
+ if self.toolset != 'target':
+ verb += '(%s)' % self.toolset
+ if message:
+ return '%s %s' % (verb, self.ExpandSpecial(message))
+ else:
+ return '%s %s: %s' % (verb, self.name, fallback)
+
def WriteActions(self, actions, extra_sources, prebuild):
all_outputs = []
for action in actions:
# First write out a rule for the action.
name = action['action_name']
- if 'message' in action:
- description = 'ACTION ' + action['message']
- else:
- description = 'ACTION %s: %s' % (self.name, action['action_name'])
+ description = self.GenerateDescription('ACTION',
+ action.get('message', None),
+ name)
rule_name = self.WriteNewNinjaRule(name, action['action'], description)
inputs = [self.GypPathToNinja(i) for i in action['inputs']]
@@ -289,10 +322,9 @@ class NinjaWriter:
# First write out a rule for the rule action.
name = rule['rule_name']
args = rule['action']
- if 'message' in rule:
- description = 'RULE ' + rule['message']
- else:
- description = 'RULE %s: %s $source' % (self.name, name)
+ description = self.GenerateDescription('RULE',
+ rule.get('message', None),
+ '%s $source' % name)
rule_name = self.WriteNewNinjaRule(name, args, description)
# TODO: if the command references the outputs directly, we should
@@ -312,18 +344,25 @@ class NinjaWriter:
for source in rule.get('rule_sources', []):
basename = os.path.basename(source)
root, ext = os.path.splitext(basename)
- source = self.GypPathToNinja(source)
+ # Gather the list of outputs, expanding $vars if possible.
outputs = []
for output in rule['outputs']:
outputs.append(output.replace('$root', root))
+ if int(rule.get('process_outputs_as_sources', False)):
+ extra_sources += outputs
+
extra_bindings = []
for var in needed_variables:
if var == 'root':
extra_bindings.append(('root', root))
elif var == 'source':
- extra_bindings.append(('source', source))
+ # '$source' 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.
+ source_expanded = self.ExpandSpecial(source, self.base_to_build)
+ extra_bindings.append(('source', source_expanded))
elif var == 'ext':
extra_bindings.append(('ext', ext))
elif var == 'name':
@@ -332,14 +371,12 @@ class NinjaWriter:
assert var == None, repr(var)
inputs = map(self.GypPathToNinja, rule.get('inputs', []))
- self.ninja.build(outputs, rule_name, source,
+ outputs = map(self.GypPathToNinja, outputs)
+ self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
implicit=inputs,
order_only=prebuild,
variables=extra_bindings)
- if int(rule.get('process_outputs_as_sources', False)):
- extra_sources += outputs
-
all_outputs.extend(outputs)
return all_outputs
@@ -360,6 +397,10 @@ class NinjaWriter:
def WriteSources(self, config, sources, predepends):
"""Write build rules to compile all of |sources|."""
+ if self.toolset == 'host':
+ self.ninja.variable('cc', '$cc_host')
+ self.ninja.variable('cxx', '$cxx_host')
+
self.WriteVariableList('defines',
['-D' + MaybeQuoteShellArgument(ninja_syntax.escape(d))
for d in config.get('defines', [])])
@@ -382,7 +423,7 @@ class NinjaWriter:
# TODO: should we assert here on unexpected extensions?
continue
input = self.GypPathToNinja(source)
- output = self.GypPathToUniqueOutput(filename + '.o', qualified=True)
+ output = self.GypPathToUniqueOutput(filename + '.o')
self.ninja.build(output, command, input,
order_only=predepends)
outputs.append(output)
@@ -390,6 +431,14 @@ class NinjaWriter:
return outputs
def WriteTarget(self, spec, config, final_deps):
+ 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.
+
output = self.ComputeOutput(spec)
output_uses_linker = spec['type'] in ('executable', 'loadable_module',
@@ -412,7 +461,7 @@ class NinjaWriter:
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.stamp' not in input:
+ if 'lastchange' not in input:
implicit_deps.add(input)
final_deps.extend(list(extra_deps))
command_map = {
@@ -426,9 +475,11 @@ class NinjaWriter:
if output_uses_linker:
self.WriteVariableList('ldflags',
- gyp.common.uniquer(config.get('ldflags', [])))
+ gyp.common.uniquer(map(self.ExpandSpecial,
+ config.get('ldflags', []))))
self.WriteVariableList('libs',
- gyp.common.uniquer(spec.get('libraries', [])))
+ gyp.common.uniquer(map(self.ExpandSpecial,
+ spec.get('libraries', []))))
extra_bindings = []
if command == 'solink':
@@ -438,11 +489,6 @@ class NinjaWriter:
implicit=list(implicit_deps),
variables=extra_bindings)
- # 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
def ComputeOutputFileName(self, spec):
@@ -495,17 +541,20 @@ class NinjaWriter:
if 'product_dir' in spec:
path = os.path.join(spec['product_dir'], filename)
- return path
+ 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'):
- return os.path.join('$b', filename)
+ return filename
elif spec['type'] == 'shared_library':
- return os.path.join('$b/lib', filename)
+ libdir = 'lib'
+ if self.toolset != 'target':
+ libdir = 'lib/%s' % self.toolset
+ return os.path.join(libdir, filename)
else:
- return self.GypPathToUniqueOutput(filename)
+ return self.GypPathToUniqueOutput(filename, qualified=False)
def WriteVariableList(self, var, values):
if values is None:
@@ -520,20 +569,19 @@ class NinjaWriter:
# TODO: we shouldn't need to qualify names; we do it because
# currently the ninja rule namespace is global, but it really
# should be scoped to the subninja.
- rule_name = ('%s.%s' % (self.name, name)).replace(' ', '_')
+ rule_name = self.name
+ if self.toolset == 'target':
+ rule_name += '.' + self.toolset
+ rule_name += '.' + name
+ rule_name = rule_name.replace(' ', '_')
- cd = ''
args = args[:]
- if self.base_dir:
- # gyp dictates that commands are run from the base directory.
- # cd into the directory before running, and adjust all paths in
- # the arguments point to the proper locations.
- cd = 'cd %s; ' % self.base_dir
- cdup = '../' * len(self.base_dir.split('/'))
- for i, arg in enumerate(args):
- arg = arg.replace('$b', cdup + '$b')
- arg = arg.replace('$source', cdup + '$source')
- args[i] = arg
+
+ # 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
+ 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)
@@ -575,13 +623,53 @@ def GenerateOutput(target_list, target_dicts, data, params):
# e.g. "out/Debug"
builddir = os.path.join(generator_flags.get('output_dir', 'out'), config_name)
- master_ninja = OpenOutput(os.path.join(options.toplevel_dir, builddir,
- 'build.ninja'))
- master_ninja.write(NINJA_BASE % {
- 'builddir': builddir,
- 'cc': os.environ.get('CC', 'gcc'),
- 'cxx': os.environ.get('CXX', 'g++'),
- })
+ master_ninja = ninja_syntax.Writer(
+ OpenOutput(os.path.join(options.toplevel_dir, builddir, '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++'))
+ master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4')
+ master_ninja.variable('cc_host', '$cc')
+ master_ninja.variable('cxx_host', '$cxx')
+ 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')
+ master_ninja.rule(
+ 'alink',
+ description='AR $out',
+ command='rm -f $out && ar rcsT $out $in')
+ master_ninja.rule(
+ 'solink',
+ description='SOLINK $out',
+ command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname '
+ '-Wl,--whole-archive $in -Wl,--no-whole-archive $libs'))
+ master_ninja.rule(
+ 'link',
+ description='LINK $out',
+ command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib '
+ '-Wl,--start-group $in -Wl,--end-group $libs'))
+ 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 || cp -af $in $out')
+ master_ninja.newline()
all_targets = set()
for build_file in params['build_files']:
@@ -589,25 +677,29 @@ def GenerateOutput(target_list, target_dicts, data, params):
all_targets.add(target)
all_outputs = set()
- subninjas = set()
target_outputs = {}
for qualified_target in target_list:
# qualified_target is like: third_party/icu/icu.gyp:icui18n#target
- build_file, target, _ = gyp.common.ParseQualifiedTarget(qualified_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)
base_path = os.path.dirname(build_file)
- output_file = os.path.join(builddir, 'obj', base_path, target + '.ninja')
+ 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,
+ writer = NinjaWriter(target_outputs, base_path, builddir,
OpenOutput(os.path.join(options.toplevel_dir,
+ builddir,
output_file)))
- subninjas.add(output_file)
+ master_ninja.subninja(output_file)
output = writer.WriteSpec(spec, config)
if output:
@@ -617,10 +709,5 @@ def GenerateOutput(target_list, target_dicts, data, params):
if qualified_target in all_targets:
all_outputs.add(output)
- for ninja in subninjas:
- print >>master_ninja, 'subninja', ninja
-
if all_outputs:
- print >>master_ninja, 'build all: phony ||' + ' '.join(all_outputs)
-
- master_ninja.close()
+ master_ninja.build('all', 'phony', list(all_outputs))
diff --git a/tools/gyp/pylib/gyp/input.py b/tools/gyp/pylib/gyp/input.py
index 314b5c66de..d4eeebc0fc 100644
--- a/tools/gyp/pylib/gyp/input.py
+++ b/tools/gyp/pylib/gyp/input.py
@@ -1525,26 +1525,39 @@ def AdjustStaticLibraryDependencies(flat_list, targets, dependency_nodes,
target_dict['dependencies_original'] = target_dict.get(
'dependencies', [])[:]
+ # A static library should not depend on another static library unless
+ # the dependency relationship is "hard," which should only be done when
+ # a dependent relies on some side effect other than just the build
+ # product, like a rule or action output. Further, if a target has a
+ # non-hard dependency, but that dependency exports a hard dependency,
+ # the non-hard dependency can safely be removed, but the exported hard
+ # dependency must be added to the target to keep the same dependency
+ # ordering.
+ dependencies = \
+ dependency_nodes[target].DirectAndImportedDependencies(targets)
index = 0
- while index < len(target_dict['dependencies']):
- dependency = target_dict['dependencies'][index]
+ while index < len(dependencies):
+ dependency = dependencies[index]
dependency_dict = targets[dependency]
- if dependency_dict['type'] == 'static_library' and \
- (not 'hard_dependency' in dependency_dict or \
- not dependency_dict['hard_dependency']):
- # A static library should not depend on another static library unless
- # the dependency relationship is "hard," which should only be done
- # when a dependent relies on some side effect other than just the
- # build product, like a rule or action output. Take the dependency
- # out of the list, and don't increment index because the next
- # dependency to analyze will shift into the index formerly occupied
- # by the one being removed.
- del target_dict['dependencies'][index]
+
+ # Remove every non-hard static library dependency and remove every
+ # non-static library dependency that isn't a direct dependency.
+ if (dependency_dict['type'] == 'static_library' and \
+ not dependency_dict.get('hard_dependency', False)) or \
+ (dependency_dict['type'] != 'static_library' and \
+ not dependency in target_dict['dependencies']):
+ # Take the dependency out of the list, and don't increment index
+ # because the next dependency to analyze will shift into the index
+ # formerly occupied by the one being removed.
+ del dependencies[index]
else:
index = index + 1
- # If the dependencies list is empty, it's not needed, so unhook it.
- if len(target_dict['dependencies']) == 0:
+ # Update the dependencies. If the dependencies list is empty, it's not
+ # needed, so unhook it.
+ if len(dependencies) > 0:
+ target_dict['dependencies'] = dependencies
+ else:
del target_dict['dependencies']
elif target_type in linkable_types:
diff --git a/tools/gyp/pylib/gyp/ninja_syntax.py b/tools/gyp/pylib/gyp/ninja_syntax.py
index e2dca2dc5b..a8735225b1 100644
--- a/tools/gyp/pylib/gyp/ninja_syntax.py
+++ b/tools/gyp/pylib/gyp/ninja_syntax.py
@@ -58,6 +58,12 @@ class Writer(object):
return outputs
+ def include(self, path):
+ self._line('include %s' % path)
+
+ def subninja(self, path):
+ self._line('subninja %s' % path)
+
def _line(self, text, indent=0):
"""Write 'text' word-wrapped at self.width characters."""
leading_space = ' ' * indent