Merge from Chromium at DEPS revision 262940
This commit was generated by merge_to_master.py.
Change-Id: I746d5be76019b85e4be03e63a7511c9b432cb3b8
diff --git a/pylib/gyp/generator/android.py b/pylib/gyp/generator/android.py
index 131a265..0149c27 100644
--- a/pylib/gyp/generator/android.py
+++ b/pylib/gyp/generator/android.py
@@ -55,7 +55,7 @@
generator_extra_sources_for_rules = []
-SHARED_FOOTER = """\
+ALL_MODULES_FOOTER = """\
# "gyp_all_modules" is a concatenation of the "gyp_all_modules" targets from
# all the included sub-makefiles. This is just here to clarify.
gyp_all_modules:
@@ -133,7 +133,7 @@
self.android_top_dir = android_top_dir
def Write(self, qualified_target, relative_target, base_path, output_filename,
- spec, configs, part_of_all):
+ spec, configs, part_of_all, write_alias_target):
"""The main entry point: writes a .mk file for a single target.
Arguments:
@@ -144,6 +144,8 @@
output_filename: output .mk file name to write
spec, configs: gyp info
part_of_all: flag indicating this target is part of 'all'
+ write_alias_target: flag indicating whether to create short aliases for
+ this target
"""
gyp.common.EnsureDirExists(output_filename)
@@ -186,11 +188,13 @@
self.WriteLn('LOCAL_MODULE_TAGS := optional')
if self.toolset == 'host':
self.WriteLn('LOCAL_IS_HOST_MODULE := true')
+ self.WriteLn('LOCAL_MODULE_TARGET_ARCH := $(TARGET_$(GYP_VAR_PREFIX)ARCH)')
# Grab output directories; needed for Actions and Rules.
- self.WriteLn('gyp_intermediate_dir := $(call local-intermediates-dir)')
+ self.WriteLn('gyp_intermediate_dir := '
+ '$(call local-intermediates-dir,,$(GYP_VAR_PREFIX))')
self.WriteLn('gyp_shared_intermediate_dir := '
- '$(call intermediates-dir-for,GYP,shared)')
+ '$(call intermediates-dir-for,GYP,shared,,,$(GYP_VAR_PREFIX))')
self.WriteLn()
# List files this target depends on so that actions/rules/copies/sources
@@ -226,7 +230,8 @@
if spec.get('sources', []) or extra_sources:
self.WriteSources(spec, configs, extra_sources)
- self.WriteTarget(spec, configs, deps, link_deps, part_of_all)
+ self.WriteTarget(spec, configs, deps, link_deps, part_of_all,
+ write_alias_target)
# Update global list of target outputs, used in dependency tracking.
target_outputs[qualified_target] = ('path', self.output_binary)
@@ -337,13 +342,10 @@
"""
if len(rules) == 0:
return
- rule_trigger = '%s_rule_trigger' % self.android_module
- did_write_rule = False
for rule in rules:
if len(rule.get('rule_sources', [])) == 0:
continue
- did_write_rule = True
name = make.StringToMakefileVariable('%s_%s' % (self.relative_target,
rule['rule_name']))
self.WriteLn('\n### Generated for rule "%s":' % name)
@@ -412,13 +414,9 @@
# Make each output depend on the main output, with an empty command
# to force make to notice that the mtime has changed.
self.WriteLn('%s: %s ;' % (output, main_output))
- self.WriteLn('.PHONY: %s' % (rule_trigger))
- self.WriteLn('%s: %s' % (rule_trigger, main_output))
- self.WriteLn('')
- if did_write_rule:
- extra_sources.append(rule_trigger) # Force all rules to run.
- self.WriteLn('### Finished generating for all rules')
- self.WriteLn('')
+ self.WriteLn()
+
+ self.WriteLn()
def WriteCopies(self, copies, extra_outputs):
@@ -612,16 +610,16 @@
prefix = ''
if spec['toolset'] == 'host':
- suffix = '_host_gyp'
+ suffix = '_$(TARGET_$(GYP_VAR_PREFIX)ARCH)_host_gyp'
else:
suffix = '_gyp'
if self.path:
- name = '%s%s_%s%s' % (prefix, self.path, self.target, suffix)
+ middle = make.StringToMakefileVariable('%s_%s' % (self.path, self.target))
else:
- name = '%s%s%s' % (prefix, self.target, suffix)
+ middle = make.StringToMakefileVariable(self.target)
- return make.StringToMakefileVariable(name)
+ return ''.join([prefix, middle, suffix])
def ComputeOutputParts(self, spec):
@@ -683,15 +681,15 @@
if self.toolset == 'host':
path = '$(HOST_OUT_INTERMEDIATE_LIBRARIES)'
else:
- path = '$(TARGET_OUT_INTERMEDIATE_LIBRARIES)'
+ path = '$($(GYP_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)'
else:
# Other targets just get built into their intermediate dir.
if self.toolset == 'host':
path = '$(call intermediates-dir-for,%s,%s,true)' % (self.android_class,
self.android_module)
else:
- path = '$(call intermediates-dir-for,%s,%s)' % (self.android_class,
- self.android_module)
+ path = ('$(call intermediates-dir-for,%s,%s,,,$(GYP_VAR_PREFIX))'
+ % (self.android_class, self.android_module))
assert spec.get('product_dir') is None # TODO: not supported?
return os.path.join(path, self.ComputeOutputBasename(spec))
@@ -819,12 +817,15 @@
'LOCAL_SHARED_LIBRARIES')
- def WriteTarget(self, spec, configs, deps, link_deps, part_of_all):
+ def WriteTarget(self, spec, configs, deps, link_deps, part_of_all,
+ write_alias_target):
"""Write Makefile code to produce the final target of the gyp spec.
spec, configs: input from gyp.
deps, link_deps: dependency lists; see ComputeDeps()
part_of_all: flag indicating this target is part of 'all'
+ write_alias_target: flag indicating whether to create short aliases for this
+ target
"""
self.WriteLn('### Rules for final target.')
@@ -835,7 +836,7 @@
# name 'gyp_all_modules' as the Android build system doesn't allow the use
# of the Make target 'all' and because 'all_modules' is the equivalent of
# the Make target 'all' on Android.
- if part_of_all:
+ if part_of_all and write_alias_target:
self.WriteLn('# Add target alias to "gyp_all_modules" target.')
self.WriteLn('.PHONY: gyp_all_modules')
self.WriteLn('gyp_all_modules: %s' % self.android_module)
@@ -844,7 +845,7 @@
# Add an alias from the gyp target name to the Android module name. This
# simplifies manual builds of the target, and is required by the test
# framework.
- if self.target != self.android_module:
+ if self.target != self.android_module and write_alias_target:
self.WriteLn('# Alias gyp target name.')
self.WriteLn('.PHONY: %s' % self.target)
self.WriteLn('%s: %s' % (self.target, self.android_module))
@@ -873,6 +874,7 @@
else:
self.WriteLn('LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp')
self.WriteLn('LOCAL_UNINSTALLABLE_MODULE := true')
+ self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX := $(GYP_VAR_PREFIX)')
self.WriteLn()
self.WriteLn('include $(BUILD_SYSTEM)/base_rules.mk')
self.WriteLn()
@@ -880,6 +882,8 @@
self.WriteLn('\t$(hide) echo "Gyp timestamp: $@"')
self.WriteLn('\t$(hide) mkdir -p $(dir $@)')
self.WriteLn('\t$(hide) touch $@')
+ self.WriteLn()
+ self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX :=')
def WriteList(self, value_list, variable=None, prefix='',
@@ -949,6 +953,7 @@
generator_flags = params.get('generator_flags', {})
builddir_name = generator_flags.get('output_dir', 'out')
limit_to_target_all = generator_flags.get('limit_to_target_all', False)
+ write_alias_targets = generator_flags.get('write_alias_targets', True)
android_top_dir = os.environ.get('ANDROID_BUILD_TOP')
assert android_top_dir, '$ANDROID_BUILD_TOP not set; you need to run lunch.'
@@ -1044,7 +1049,8 @@
writer = AndroidMkWriter(android_top_dir)
android_module = writer.Write(qualified_target, relative_target, base_path,
output_file, spec, configs,
- part_of_all=part_of_all)
+ part_of_all=part_of_all,
+ write_alias_target=write_alias_targets)
if android_module in android_modules:
print ('ERROR: Android module names must be unique. The following '
'targets both generate Android module name %s.\n %s\n %s' %
@@ -1060,6 +1066,7 @@
include_list.add(mkfile_rel_path)
root_makefile.write('GYP_CONFIGURATION ?= %s\n' % default_configuration)
+ root_makefile.write('GYP_VAR_PREFIX ?=\n')
# Write out the sorted list of includes.
root_makefile.write('\n')
@@ -1067,6 +1074,7 @@
root_makefile.write('include $(LOCAL_PATH)/' + include_file + '\n')
root_makefile.write('\n')
- root_makefile.write(SHARED_FOOTER)
+ if write_alias_targets:
+ root_makefile.write(ALL_MODULES_FOOTER)
root_makefile.close()
diff --git a/pylib/gyp/generator/xcode.py b/pylib/gyp/generator/xcode.py
index 8751810..7972459 100644
--- a/pylib/gyp/generator/xcode.py
+++ b/pylib/gyp/generator/xcode.py
@@ -5,6 +5,7 @@
import filecmp
import gyp.common
import gyp.xcodeproj_file
+import gyp.xcode_ninja
import errno
import os
import sys
@@ -575,6 +576,12 @@
def GenerateOutput(target_list, target_dicts, data, params):
+ # Optionally configure each spec to use ninja as the external builder.
+ ninja_wrapper = params.get('flavor') == 'ninja'
+ if ninja_wrapper:
+ (target_list, target_dicts, data) = \
+ gyp.xcode_ninja.CreateWrapper(target_list, target_dicts, data, params)
+
options = params['options']
generator_flags = params.get('generator_flags', {})
parallel_builds = generator_flags.get('xcode_parallel_builds', True)
@@ -703,7 +710,10 @@
# and is made a dependency of this target. This way the work is done
# before the dependency checks for what should be recompiled.
support_xct = None
- if type != 'none' and (spec_actions or spec_rules):
+ # The Xcode "issues" don't affect xcode-ninja builds, since the dependency
+ # logic all happens in ninja. Don't bother creating the extra targets in
+ # that case.
+ if type != 'none' and (spec_actions or spec_rules) and not ninja_wrapper:
support_xccl = CreateXCConfigurationList(configuration_names);
support_target_suffix = generator_flags.get(
'support_target_suffix', ' Support')
diff --git a/pylib/gyp/input.py b/pylib/gyp/input.py
index f694e57..2b33dda 100644
--- a/pylib/gyp/input.py
+++ b/pylib/gyp/input.py
@@ -2172,6 +2172,7 @@
if not target_dict['configurations'][i].get('abstract')]
target_dict['default_configuration'] = sorted(concrete)[0]
+ merged_configurations = {}
for configuration in target_dict['configurations'].keys():
old_configuration_dict = target_dict['configurations'][configuration]
# Skip abstract configurations (saves work only).
@@ -2203,8 +2204,12 @@
MergeConfigWithInheritance(new_configuration_dict, build_file,
target_dict, configuration, [])
- # Put the new result back into the target dict as a configuration.
- target_dict['configurations'][configuration] = new_configuration_dict
+ merged_configurations[configuration] = new_configuration_dict
+
+ # Put the new configurations back into the target dict as a configuration.
+ for configuration in merged_configurations.keys():
+ target_dict['configurations'][configuration] = (
+ merged_configurations[configuration])
# Now drop all the abstract ones.
for configuration in target_dict['configurations'].keys():
diff --git a/pylib/gyp/xcode_emulation.py b/pylib/gyp/xcode_emulation.py
index d86413a..d856e57 100644
--- a/pylib/gyp/xcode_emulation.py
+++ b/pylib/gyp/xcode_emulation.py
@@ -21,7 +21,125 @@
# Populated lazily by XcodeVersion, for efficiency, and to fix an issue when
# "xcodebuild" is called too quickly (it has been found to return incorrect
# version number).
-XCODE_VERSION_CACHE = []
+XCODE_VERSION_CACHE = None
+
+# Populated lazily by GetXcodeArchsDefault, to an |XcodeArchsDefault| instance
+# corresponding to the installed version of Xcode.
+XCODE_ARCHS_DEFAULT_CACHE = None
+
+
+def XcodeArchsVariableMapping(archs, archs_including_64_bit=None):
+ """Constructs a dictionary with expansion for $(ARCHS_STANDARD) variable,
+ and optionally for $(ARCHS_STANDARD_INCLUDING_64_BIT)."""
+ mapping = {'$(ARCHS_STANDARD)': archs}
+ if archs_including_64_bit:
+ mapping['$(ARCHS_STANDARD_INCLUDING_64_BIT)'] = archs_including_64_bit
+ return mapping
+
+class XcodeArchsDefault(object):
+ """A class to resolve ARCHS variable from xcode_settings, resolving Xcode
+ macros and implementing filtering by VALID_ARCHS. The expansion of macros
+ depends on the SDKROOT used ("macosx", "iphoneos", "iphonesimulator") and
+ on the version of Xcode.
+ """
+
+ # Match variable like $(ARCHS_STANDARD).
+ variable_pattern = re.compile(r'\$\([a-zA-Z_][a-zA-Z0-9_]*\)$')
+
+ def __init__(self, default, mac, iphonesimulator, iphoneos):
+ self._default = (default,)
+ self._archs = {'mac': mac, 'ios': iphoneos, 'iossim': iphonesimulator}
+
+ def _VariableMapping(self, sdkroot):
+ """Returns the dictionary of variable mapping depending on the SDKROOT."""
+ sdkroot = sdkroot.lower()
+ if 'iphoneos' in sdkroot:
+ return self._archs['ios']
+ elif 'iphonesimulator' in sdkroot:
+ return self._archs['iossim']
+ else:
+ return self._archs['mac']
+
+ def _ExpandArchs(self, archs, sdkroot):
+ """Expands variables references in ARCHS, and remove duplicates."""
+ variable_mapping = self._VariableMapping(sdkroot)
+ expanded_archs = []
+ for arch in archs:
+ if self.variable_pattern.match(arch):
+ variable = arch
+ try:
+ variable_expansion = variable_mapping[variable]
+ for arch in variable_expansion:
+ if arch not in expanded_archs:
+ expanded_archs.append(arch)
+ except KeyError as e:
+ print 'Warning: Ignoring unsupported variable "%s".' % variable
+ elif arch not in expanded_archs:
+ expanded_archs.append(arch)
+ return expanded_archs
+
+ def ActiveArchs(self, archs, valid_archs, sdkroot):
+ """Expands variables references in ARCHS, and filter by VALID_ARCHS if it
+ is defined (if not set, Xcode accept any value in ARCHS, otherwise, only
+ values present in VALID_ARCHS are kept)."""
+ expanded_archs = self._ExpandArchs(archs or self._default, sdkroot or '')
+ if valid_archs:
+ filtered_archs = []
+ for arch in expanded_archs:
+ if arch in valid_archs:
+ filtered_archs.append(arch)
+ expanded_archs = filtered_archs
+ return expanded_archs
+
+
+def GetXcodeArchsDefault():
+ """Returns the |XcodeArchsDefault| object to use to expand ARCHS for the
+ installed version of Xcode. The default values used by Xcode for ARCHS
+ and the expansion of the variables depends on the version of Xcode used.
+
+ For all version anterior to Xcode 5.0 or posterior to Xcode 5.1 included
+ uses $(ARCHS_STANDARD) if ARCHS is unset, while Xcode 5.0 to 5.0.2 uses
+ $(ARCHS_STANDARD_INCLUDING_64_BIT). This variable was added to Xcode 5.0
+ and deprecated with Xcode 5.1.
+
+ For "macosx" SDKROOT, all version starting with Xcode 5.0 includes 64-bit
+ architecture as part of $(ARCHS_STANDARD) and default to only building it.
+
+ For "iphoneos" and "iphonesimulator" SDKROOT, 64-bit architectures are part
+ of $(ARCHS_STANDARD_INCLUDING_64_BIT) from Xcode 5.0. From Xcode 5.1, they
+ are also part of $(ARCHS_STANDARD).
+
+ All thoses rules are coded in the construction of the |XcodeArchsDefault|
+ object to use depending on the version of Xcode detected. The object is
+ for performance reason."""
+ global XCODE_ARCHS_DEFAULT_CACHE
+ if XCODE_ARCHS_DEFAULT_CACHE:
+ return XCODE_ARCHS_DEFAULT_CACHE
+ xcode_version, _ = XcodeVersion()
+ if xcode_version < '0500':
+ XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
+ '$(ARCHS_STANDARD)',
+ XcodeArchsVariableMapping(['i386']),
+ XcodeArchsVariableMapping(['i386']),
+ XcodeArchsVariableMapping(['armv7']))
+ elif xcode_version < '0510':
+ XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
+ '$(ARCHS_STANDARD_INCLUDING_64_BIT)',
+ XcodeArchsVariableMapping(['x86_64'], ['x86_64']),
+ XcodeArchsVariableMapping(['i386'], ['i386', 'x86_64']),
+ XcodeArchsVariableMapping(
+ ['armv7', 'armv7s'],
+ ['armv7', 'armv7s', 'arm64']))
+ else:
+ XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
+ '$(ARCHS_STANDARD)',
+ XcodeArchsVariableMapping(['x86_64'], ['x86_64']),
+ XcodeArchsVariableMapping(['i386', 'x86_64'], ['i386', 'x86_64']),
+ XcodeArchsVariableMapping(
+ ['armv7', 'armv7s', 'arm64'],
+ ['armv7', 'armv7s', 'arm64']))
+ return XCODE_ARCHS_DEFAULT_CACHE
+
class XcodeSettings(object):
"""A class that understands the gyp 'xcode_settings' object."""
@@ -268,9 +386,12 @@
def GetActiveArchs(self, configname):
"""Returns the architectures this target should be built for."""
- # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set
- # CURRENT_ARCH / NATIVE_ARCH env vars?
- return self.xcode_settings[configname].get('ARCHS', [self._DefaultArch()])
+ config_settings = self.xcode_settings[configname]
+ xcode_archs_default = GetXcodeArchsDefault()
+ return xcode_archs_default.ActiveArchs(
+ config_settings.get('ARCHS'),
+ config_settings.get('VALID_ARCHS'),
+ config_settings.get('SDKROOT'))
def _GetSdkVersionInfoItem(self, sdk, infoitem):
# xcodebuild requires Xcode and can't run on Command Line Tools-only
@@ -389,7 +510,8 @@
if arch is not None:
archs = [arch]
else:
- archs = self._Settings().get('ARCHS', [self._DefaultArch()])
+ assert self.configname
+ archs = self.GetActiveArchs(self.configname)
if len(archs) != 1:
# TODO: Supporting fat binaries will be annoying.
self._WarnUnimplemented('ARCHS')
@@ -646,7 +768,8 @@
if arch is not None:
archs = [arch]
else:
- archs = self._Settings().get('ARCHS', [self._DefaultArch()])
+ assert self.configname
+ archs = self.GetActiveArchs(self.configname)
if len(archs) != 1:
# TODO: Supporting fat binaries will be annoying.
self._WarnUnimplemented('ARCHS')
@@ -938,28 +1061,6 @@
return sdk_root
return ''
- def _DefaultArch(self):
- # For Mac projects, Xcode changed the default value used when ARCHS is not
- # set from "i386" to "x86_64".
- #
- # For iOS projects, if ARCHS is unset, it defaults to "armv7 armv7s" when
- # building for a device, and the simulator binaries are always build for
- # "i386".
- #
- # For new projects, ARCHS is set to $(ARCHS_STANDARD_INCLUDING_64_BIT),
- # which correspond to "armv7 armv7s arm64", and when building the simulator
- # the architecture is either "i386" or "x86_64" depending on the simulated
- # device (respectively 32-bit or 64-bit device).
- #
- # Since the value returned by this function is only used when ARCHS is not
- # set, then on iOS we return "i386", as the default xcode project generator
- # does not set ARCHS if it is not set in the .gyp file.
- if self.isIOS:
- return 'i386'
- version, build = XcodeVersion()
- if version >= '0500':
- return 'x86_64'
- return 'i386'
class MacPrefixHeader(object):
"""A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature.
@@ -1077,9 +1178,9 @@
# Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0
# BuildVersion: 10M2518
# Convert that to '0463', '4H1503'.
+ global XCODE_VERSION_CACHE
if XCODE_VERSION_CACHE:
- assert len(XCODE_VERSION_CACHE) >= 2
- return tuple(XCODE_VERSION_CACHE[:2])
+ return XCODE_VERSION_CACHE
try:
version_list = GetStdout(['xcodebuild', '-version']).splitlines()
# In some circumstances xcodebuild exits 0 but doesn't return
@@ -1104,8 +1205,8 @@
version = (version + '0' * (3 - len(version))).zfill(4)
if build:
build = build.split()[-1]
- XCODE_VERSION_CACHE.extend((version, build))
- return version, build
+ XCODE_VERSION_CACHE = (version, build)
+ return XCODE_VERSION_CACHE
# This function ported from the logic in Homebrew's CLT version check
@@ -1434,53 +1535,6 @@
return False
-def _IOSIsDeviceSDKROOT(sdkroot):
- """Tests if |sdkroot| is a SDK for building for device."""
- return 'iphoneos' in sdkroot.lower()
-
-
-def _IOSDefaultArchForSDKRoot(sdkroot):
- """Returns the expansion of standard ARCHS macro depending on the version
- of Xcode installed and configured, and which |sdkroot| to use (iphoneos or
- simulator)."""
- xcode_version, xcode_build = XcodeVersion()
- if xcode_version < '0500':
- if _IOSIsDeviceSDKROOT(sdkroot):
- return {'$(ARCHS_STANDARD)': ['armv7']}
- else:
- return {'$(ARCHS_STANDARD)': ['i386']}
- else:
- if _IOSIsDeviceSDKROOT(sdkroot):
- return {
- '$(ARCHS_STANDARD)': ['armv7', 'armv7s'],
- '$(ARCHS_STANDARD_INCLUDING_64_BIT)': ['armv7', 'armv7s', 'arm64'],
- }
- else:
- return {
- '$(ARCHS_STANDARD)': ['i386'],
- '$(ARCHS_STANDARD_INCLUDING_64_BIT)': ['i386', 'x86_64'],
- }
-
-
-def _FilterIOSArchitectureForSDKROOT(xcode_settings):
- """Filter the ARCHS value from the |xcode_settings| dictionary to only
- contains architectures valid for the sdk configured in SDKROOT value."""
- defaults_archs = _IOSDefaultArchForSDKRoot(xcode_settings.get('SDKROOT', ''))
- allowed_archs = set()
- for archs in defaults_archs.itervalues():
- allowed_archs.update(archs)
- selected_archs = set()
- for arch in (xcode_settings.get('ARCHS', []) or ['$(ARCHS_STANDARD)']):
- if arch in defaults_archs:
- selected_archs.update(defaults_archs[arch])
- elif arch in allowed_archs:
- selected_archs.add(arch)
- valid_archs = set(xcode_settings.get('VALID_ARCHS', []))
- if valid_archs:
- selected_archs = selected_archs & valid_archs
- xcode_settings['ARCHS'] = list(selected_archs)
-
-
def _AddIOSDeviceConfigurations(targets):
"""Clone all targets and append -iphoneos to the name. Configure these targets
to build for iOS devices and use correct architectures for those builds."""
@@ -1490,10 +1544,9 @@
for config_name, config_dict in dict(configs).iteritems():
iphoneos_config_dict = copy.deepcopy(config_dict)
configs[config_name + '-iphoneos'] = iphoneos_config_dict
+ configs[config_name + '-iphonesimulator'] = config_dict
if toolset == 'target':
iphoneos_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos'
- _FilterIOSArchitectureForSDKROOT(iphoneos_config_dict['xcode_settings'])
- _FilterIOSArchitectureForSDKROOT(config_dict['xcode_settings'])
return targets
def CloneConfigurationForDeviceAndEmulator(target_dicts):
diff --git a/pylib/gyp/xcode_ninja.py b/pylib/gyp/xcode_ninja.py
new file mode 100644
index 0000000..c886798
--- /dev/null
+++ b/pylib/gyp/xcode_ninja.py
@@ -0,0 +1,230 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Xcode-ninja wrapper project file generator.
+
+This updates the data structures passed to the Xcode gyp generator to build
+with ninja instead. The Xcode project itself is transformed into a list of
+executable targets, each with a build step to build with ninja, and a target
+with every source and resource file. This appears to sidestep some of the
+major performance headaches experienced using complex projects and large number
+of targets within Xcode.
+"""
+
+import errno
+import gyp.generator.ninja
+import os
+import re
+import xml.sax.saxutils
+
+
+def _WriteWorkspace(main_gyp, sources_gyp):
+ """ Create a workspace to wrap main and sources gyp paths. """
+ (build_file_root, build_file_ext) = os.path.splitext(main_gyp)
+ workspace_path = build_file_root + '.xcworkspace'
+ try:
+ os.makedirs(workspace_path)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+ output_string = '<?xml version="1.0" encoding="UTF-8"?>\n' + \
+ '<Workspace version = "1.0">\n'
+ for gyp_name in [main_gyp, sources_gyp]:
+ name = os.path.splitext(os.path.basename(gyp_name))[0] + '.xcodeproj'
+ name = xml.sax.saxutils.quoteattr("group:" + name)
+ output_string += ' <FileRef location = %s></FileRef>\n' % name
+ output_string += '</Workspace>\n'
+
+ workspace_file = os.path.join(workspace_path, "contents.xcworkspacedata")
+
+ try:
+ with open(workspace_file, 'r') as input_file:
+ input_string = input_file.read()
+ if input_string == output_string:
+ return
+ except IOError:
+ # Ignore errors if the file doesn't exist.
+ pass
+
+ with open(workspace_file, 'w') as output_file:
+ output_file.write(output_string)
+
+def _TargetFromSpec(old_spec, params):
+ """ Create fake target for xcode-ninja wrapper. """
+ # Determine ninja top level build dir (e.g. /path/to/out).
+ ninja_toplevel = None
+ jobs = 0
+ if params:
+ options = params['options']
+ ninja_toplevel = \
+ os.path.join(options.toplevel_dir,
+ gyp.generator.ninja.ComputeOutputDir(params))
+ jobs = params.get('generator_flags', {}).get('xcode_ninja_jobs', 0)
+
+ target_name = old_spec.get('target_name')
+ product_name = old_spec.get('product_name', target_name)
+
+ ninja_target = {}
+ ninja_target['target_name'] = target_name
+ ninja_target['product_name'] = product_name
+ ninja_target['toolset'] = old_spec.get('toolset')
+ ninja_target['default_configuration'] = old_spec.get('default_configuration')
+ ninja_target['configurations'] = {}
+
+ # Tell Xcode to look in |ninja_toplevel| for build products.
+ new_xcode_settings = {}
+ if ninja_toplevel:
+ new_xcode_settings['CONFIGURATION_BUILD_DIR'] = \
+ "%s/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)" % ninja_toplevel
+
+ if 'configurations' in old_spec:
+ for config in old_spec['configurations'].iterkeys():
+ old_xcode_settings = old_spec['configurations'][config]['xcode_settings']
+ if 'IPHONEOS_DEPLOYMENT_TARGET' in old_xcode_settings:
+ new_xcode_settings['CODE_SIGNING_REQUIRED'] = "NO"
+ new_xcode_settings['IPHONEOS_DEPLOYMENT_TARGET'] = \
+ old_xcode_settings['IPHONEOS_DEPLOYMENT_TARGET']
+ ninja_target['configurations'][config] = {}
+ ninja_target['configurations'][config]['xcode_settings'] = \
+ new_xcode_settings
+
+ ninja_target['mac_bundle'] = old_spec.get('mac_bundle', 0)
+ ninja_target['type'] = old_spec['type']
+ if ninja_toplevel:
+ ninja_target['actions'] = [
+ {
+ 'action_name': 'Compile and copy %s via ninja' % target_name,
+ 'inputs': [],
+ 'outputs': [],
+ 'action': [
+ 'env',
+ 'PATH=%s' % os.environ['PATH'],
+ 'ninja',
+ '-C',
+ new_xcode_settings['CONFIGURATION_BUILD_DIR'],
+ target_name,
+ ],
+ 'message': 'Compile and copy %s via ninja' % target_name,
+ },
+ ]
+ if jobs > 0:
+ ninja_target['actions'][0]['action'].extend(('-j', jobs))
+ return ninja_target
+
+def CreateWrapper(target_list, target_dicts, data, params):
+ """Initialize targets for the ninja wrapper.
+
+ This sets up the necessary variables in the targets to generate Xcode projects
+ that use ninja as an external builder.
+ Arguments:
+ target_list: List of target pairs: 'base/base.gyp:base'.
+ target_dicts: Dict of target properties keyed on target pair.
+ data: Dict of flattened build files keyed on gyp path.
+ params: Dict of global options for gyp.
+ """
+ orig_gyp = params['build_files'][0]
+ for gyp_name, gyp_dict in data.iteritems():
+ if gyp_name == orig_gyp:
+ depth = gyp_dict['_DEPTH']
+
+ # Check for custom main gyp name, otherwise use the default CHROMIUM_GYP_FILE
+ # and prepend .ninja before the .gyp extension.
+ generator_flags = params.get('generator_flags', {})
+ main_gyp = generator_flags.get('xcode_ninja_main_gyp', None)
+ if main_gyp is None:
+ (build_file_root, build_file_ext) = os.path.splitext(orig_gyp)
+ main_gyp = build_file_root + ".ninja" + build_file_ext
+
+ # Create new |target_list|, |target_dicts| and |data| data structures.
+ new_target_list = []
+ new_target_dicts = {}
+ new_data = {}
+
+ # Set base keys needed for |data|.
+ new_data[main_gyp] = {}
+ new_data[main_gyp]['included_files'] = []
+ new_data[main_gyp]['targets'] = []
+ new_data[main_gyp]['xcode_settings'] = \
+ data[orig_gyp].get('xcode_settings', {})
+
+ for old_qualified_target in target_list:
+ spec = target_dicts[old_qualified_target]
+ if spec.get('type', '') == 'executable' and \
+ spec.get('product_extension', '') != 'bundle':
+
+ # Add to new_target_list.
+ target_name = spec.get('target_name')
+
+ # Filter target names if requested.
+ target_filter = generator_flags.get('xcode_ninja_target_filter', None)
+ if target_filter is not None:
+ if not re.search(target_filter, target_name):
+ continue;
+
+ new_target_name = '%s:%s#target' % (main_gyp, target_name)
+ new_target_list.append(new_target_name)
+
+ # Add to new_target_dicts.
+ new_target_dicts[new_target_name] = _TargetFromSpec(spec, params)
+
+ # Add to new_data.
+ for old_target in data[old_qualified_target.split(':')[0]]['targets']:
+ if old_target['target_name'] == target_name:
+ new_data_target = {}
+ new_data_target['target_name'] = old_target['target_name']
+ new_data_target['toolset'] = old_target['toolset']
+ new_data[main_gyp]['targets'].append(new_data_target)
+
+ # Create sources target.
+ sources_target_name = 'sources_for_indexing'
+ sources_target = _TargetFromSpec(
+ { 'target_name' : sources_target_name,
+ 'toolset': 'target',
+ 'default_configuration': 'Default',
+ 'mac_bundle': '0',
+ 'type': 'executable'
+ }, None)
+
+ # Tell Xcode to look everywhere for headers.
+ sources_target['configurations'] = {'Default': { 'include_dirs': [ depth ] } }
+
+ sources = []
+ for target, target_dict in target_dicts.iteritems():
+ base = os.path.dirname(target)
+ files = target_dict.get('sources', []) + \
+ target_dict.get('mac_bundle_resources', [])
+ # Remove files starting with $. These are mostly intermediate files for the
+ # build system.
+ files = [ file for file in files if not file.startswith('$')]
+
+ # Make sources relative to root build file.
+ relative_path = os.path.dirname(main_gyp)
+ sources += [ os.path.relpath(os.path.join(base, file), relative_path)
+ for file in files ]
+
+ sources_target['sources'] = sorted(set(sources))
+
+ # Put sources_to_index in it's own gyp.
+ sources_gyp = \
+ os.path.join(os.path.dirname(main_gyp), sources_target_name + ".gyp")
+ fully_qualified_target_name = \
+ '%s:%s#target' % (sources_gyp, sources_target_name)
+
+ # Add to new_target_list, new_target_dicts and new_data.
+ new_target_list.append(fully_qualified_target_name)
+ new_target_dicts[fully_qualified_target_name] = sources_target
+ new_data_target = {}
+ new_data_target['target_name'] = sources_target['target_name']
+ new_data_target['_DEPTH'] = depth
+ new_data_target['toolset'] = "target"
+ new_data[sources_gyp] = {}
+ new_data[sources_gyp]['targets'] = []
+ new_data[sources_gyp]['included_files'] = []
+ new_data[sources_gyp]['xcode_settings'] = \
+ data[orig_gyp].get('xcode_settings', {})
+ new_data[sources_gyp]['targets'].append(new_data_target)
+
+ # Write workspace to file.
+ _WriteWorkspace(main_gyp, sources_gyp)
+ return (new_target_list, new_target_dicts, new_data)
diff --git a/test/android/gyptest-noalias.py b/test/android/gyptest-noalias.py
new file mode 100755
index 0000000..4840c4c
--- /dev/null
+++ b/test/android/gyptest-noalias.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that disabling target aliases works.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+test.run_gyp('hello.gyp', '-G', 'write_alias_targets=0')
+
+test.build('hello.gyp', 'hello', status=2, stderr=None)
+
+test.build('hello.gyp', 'gyp_all_modules', status=2, stderr=None)
+
+test.pass_test()
diff --git a/test/android/hello.c b/test/android/hello.c
new file mode 100644
index 0000000..e6bf622
--- /dev/null
+++ b/test/android/hello.c
@@ -0,0 +1,12 @@
+/* Copyright (c) 2014 Google Inc. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdio.h>
+
+int main()
+{
+ printf("Hello, world!\n");
+ return 0;
+}
diff --git a/test/android/hello.gyp b/test/android/hello.gyp
new file mode 100644
index 0000000..da58a2b
--- /dev/null
+++ b/test/android/hello.gyp
@@ -0,0 +1,15 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ },
+ ],
+}
diff --git a/test/configurations/inheritance/duplicates.gyp b/test/configurations/inheritance/duplicates.gyp
new file mode 100644
index 0000000..6930ce3
--- /dev/null
+++ b/test/configurations/inheritance/duplicates.gyp
@@ -0,0 +1,27 @@
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'A',
+ 'configurations': {
+ 'A': {
+ 'defines': ['SOMETHING'],
+ },
+ 'B': {
+ 'inherit_from': ['A'],
+ },
+ },
+ 'cflags': ['-g'],
+ },
+ 'targets': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'executable',
+ 'sources': [
+ 'configurations.c',
+ ],
+ },
+ ],
+}
diff --git a/test/configurations/inheritance/duplicates.gypd.golden b/test/configurations/inheritance/duplicates.gypd.golden
new file mode 100644
index 0000000..719b708
--- /dev/null
+++ b/test/configurations/inheritance/duplicates.gypd.golden
@@ -0,0 +1,12 @@
+{'_DEPTH': '.',
+ 'included_files': ['duplicates.gyp'],
+ 'targets': [{'configurations': {'A': {'cflags': ['-g'],
+ 'defines': ['SOMETHING']},
+ 'B': {'cflags': ['-g'],
+ 'defines': ['SOMETHING'],
+ 'inherit_from': ['A']}},
+ 'default_configuration': 'A',
+ 'sources': ['configurations.c'],
+ 'target_name': 'configurations',
+ 'toolset': 'target',
+ 'type': 'executable'}]}
diff --git a/test/configurations/inheritance/gyptest-duplicates.py b/test/configurations/inheritance/gyptest-duplicates.py
new file mode 100755
index 0000000..46687b4
--- /dev/null
+++ b/test/configurations/inheritance/gyptest-duplicates.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that configurations do not duplicate other settings.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(format='gypd')
+
+test.run_gyp('duplicates.gyp')
+
+# Verify the duplicates.gypd against the checked-in expected contents.
+#
+# Normally, we should canonicalize line endings in the expected
+# contents file setting the Subversion svn:eol-style to native,
+# but that would still fail if multiple systems are sharing a single
+# workspace on a network-mounted file system. Consequently, we
+# massage the Windows line endings ('\r\n') in the output to the
+# checked-in UNIX endings ('\n').
+
+contents = test.read('duplicates.gypd').replace(
+ '\r', '').replace('\\\\', '/')
+expect = test.read('duplicates.gypd.golden').replace('\r', '')
+if not test.match(contents, expect):
+ print "Unexpected contents of `duplicates.gypd'"
+ test.diff(expect, contents, 'duplicates.gypd ')
+ test.fail_test()
+
+test.pass_test()
diff --git a/test/ios/app-bundle/test-crosscompile.gyp b/test/ios/app-bundle/test-crosscompile.gyp
new file mode 100644
index 0000000..d904958
--- /dev/null
+++ b/test/ios/app-bundle/test-crosscompile.gyp
@@ -0,0 +1,47 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ],
+ 'targets': [
+ # This target will not be built, but is here so that ninja Xcode emulation
+ # understand this is a multi-platform (ios + mac) build.
+ {
+ 'target_name': 'TestDummy',
+ 'product_name': 'TestDummy',
+ 'toolsets': ['target'],
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'tool_main.cc',
+ ],
+ 'xcode_settings': {
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ },
+ },
+ {
+ 'target_name': 'TestHost',
+ 'product_name': 'TestHost',
+ 'toolsets': ['host'],
+ 'type': 'executable',
+ 'mac_bundle': 0,
+ 'sources': [
+ 'tool_main.cc',
+ ],
+ 'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'ARCHS': [
+ '$(ARCHS_STANDARD)',
+ 'x86_64',
+ ],
+ 'VALID_ARCHS': [
+ 'x86_64',
+ ],
+ }
+ }
+ ],
+}
diff --git a/test/ios/app-bundle/test.gyp b/test/ios/app-bundle/test.gyp
index 619976d..a8601e8 100644
--- a/test/ios/app-bundle/test.gyp
+++ b/test/ios/app-bundle/test.gyp
@@ -15,7 +15,6 @@
'target_name': 'test_app',
'product_name': 'Test App Gyp',
'type': 'executable',
- 'product_extension': 'bundle',
'mac_bundle': 1,
'sources': [
'TestApp/main.m',
diff --git a/test/ios/app-bundle/tool_main.cc b/test/ios/app-bundle/tool_main.cc
new file mode 100644
index 0000000..9dc3c94
--- /dev/null
+++ b/test/ios/app-bundle/tool_main.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+int main() {
+ return 0;
+}
diff --git a/test/ios/gyptest-app-ios.py b/test/ios/gyptest-app-ios.py
index 48da70d..37afbfe 100755
--- a/test/ios/gyptest-app-ios.py
+++ b/test/ios/gyptest-app-ios.py
@@ -20,27 +20,27 @@
test.build('test.gyp', test.ALL, chdir='app-bundle')
# Test that the extension is .bundle
- test.built_file_must_exist('Test App Gyp.bundle/Test App Gyp',
+ test.built_file_must_exist('Test App Gyp.app/Test App Gyp',
chdir='app-bundle')
# Info.plist
- info_plist = test.built_file_path('Test App Gyp.bundle/Info.plist',
+ info_plist = test.built_file_path('Test App Gyp.app/Info.plist',
chdir='app-bundle')
# Resources
test.built_file_must_exist(
- 'Test App Gyp.bundle/English.lproj/InfoPlist.strings',
+ 'Test App Gyp.app/English.lproj/InfoPlist.strings',
chdir='app-bundle')
test.built_file_must_exist(
- 'Test App Gyp.bundle/English.lproj/MainMenu.nib',
+ 'Test App Gyp.app/English.lproj/MainMenu.nib',
chdir='app-bundle')
test.built_file_must_exist(
- 'Test App Gyp.bundle/English.lproj/Main_iPhone.storyboardc',
+ 'Test App Gyp.app/English.lproj/Main_iPhone.storyboardc',
chdir='app-bundle')
# Packaging
- test.built_file_must_exist('Test App Gyp.bundle/PkgInfo',
+ test.built_file_must_exist('Test App Gyp.app/PkgInfo',
chdir='app-bundle')
- test.built_file_must_match('Test App Gyp.bundle/PkgInfo', 'APPLause',
+ test.built_file_must_match('Test App Gyp.app/PkgInfo', 'APPLause',
chdir='app-bundle')
test.pass_test()
diff --git a/test/ios/gyptest-archs.py b/test/ios/gyptest-archs.py
index 8870fec..38c5c61 100644
--- a/test/ios/gyptest-archs.py
+++ b/test/ios/gyptest-archs.py
@@ -12,52 +12,40 @@
import TestMac
import collections
-import plistlib
-import os
-import re
-import struct
-import subprocess
import sys
-import tempfile
if sys.platform == 'darwin':
test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
test_cases = [
- ('Default', 'TestNoArchs', ['i386']),
('Default', 'TestArch32Bits', ['i386']),
- ('Default', 'TestArch64Bits', ['x86_64']),
- ('Default', 'TestMultiArchs', ['i386', 'x86_64']),
- ('Default-iphoneos', 'TestNoArchs', ['armv7']),
('Default-iphoneos', 'TestArch32Bits', ['armv7']),
- ('Default-iphoneos', 'TestArch64Bits', ['arm64']),
- ('Default-iphoneos', 'TestMultiArchs', ['armv7', 'arm64']),
]
+ if TestMac.Xcode.Version() < '0510':
+ test_cases.extend([
+ ('Default', 'TestNoArchs', ['i386']),
+ ('Default-iphoneos', 'TestNoArchs', ['armv7'])])
+
+ if TestMac.Xcode.Version() >= '0500':
+ test_cases.extend([
+ ('Default', 'TestArch64Bits', ['x86_64']),
+ ('Default', 'TestMultiArchs', ['i386', 'x86_64']),
+ ('Default-iphoneos', 'TestArch64Bits', ['arm64']),
+ ('Default-iphoneos', 'TestMultiArchs', ['armv7', 'arm64'])])
+
test.run_gyp('test-archs.gyp', chdir='app-bundle')
for configuration, target, archs in test_cases:
- is_64_bit_build = ('arm64' in archs or 'x86_64' in archs)
is_device_build = configuration.endswith('-iphoneos')
kwds = collections.defaultdict(list)
- if test.format == 'xcode' and is_device_build:
- configuration, sdk = configuration.split('-')
- kwds['arguments'].extend(['-sdk', sdk])
-
- # TODO(sdefresne): remove those special-cases once the bots have been
- # updated to use a more recent version of Xcode.
- if TestMac.Xcode.Version() < '0500':
- if is_64_bit_build:
- continue
- if test.format == 'xcode':
- arch = 'i386'
- if is_device_build:
- arch = 'armv7'
- kwds['arguments'].extend(['-arch', arch])
- elif TestMac.Xcode.Version() >= '0510':
- if target == 'TestNoArchs':
- continue
+ if test.format == 'xcode':
+ if is_device_build:
+ configuration, sdk = configuration.split('-')
+ kwds['arguments'].extend(['-sdk', sdk])
+ if TestMac.Xcode.Version() < '0500':
+ kwds['arguments'].extend(['-arch', archs[0]])
test.set_configuration(configuration)
filename = '%s.bundle/%s' % (target, target)
diff --git a/test/ios/gyptest-crosscompile.py b/test/ios/gyptest-crosscompile.py
new file mode 100644
index 0000000..a081683
--- /dev/null
+++ b/test/ios/gyptest-crosscompile.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that tools are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import sys
+import os
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ oldenv = os.environ.copy()
+ try:
+ os.environ['GYP_CROSSCOMPILE'] = '1'
+ test.run_gyp('test-crosscompile.gyp', chdir='app-bundle')
+ finally:
+ os.environ.clear()
+ os.environ.update(oldenv)
+
+ test.set_configuration('Default')
+ test.build('test-crosscompile.gyp', 'TestHost', chdir='app-bundle')
+ result_file = test.built_file_path('TestHost', chdir='app-bundle')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['x86_64'])
+
+ test.pass_test()
diff --git a/test/ios/gyptest-xcode-ninja.py b/test/ios/gyptest-xcode-ninja.py
new file mode 100644
index 0000000..d2ff333
--- /dev/null
+++ b/test/ios/gyptest-xcode-ninja.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2012 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verify that the xcode-ninja GYP_GENERATOR runs and builds correctly.
+"""
+
+import TestGyp
+
+import os
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+ # Run ninja first
+ test.format = 'ninja'
+ test.run_gyp('test.gyp', chdir='app-bundle')
+
+ # Then run xcode-ninja
+ test.format = 'xcode-ninja'
+ test.run_gyp('test.gyp', chdir='app-bundle')
+
+ # If it builds the target, it works.
+ test.build('test.ninja.gyp', chdir='app-bundle')
+ test.pass_test()
diff --git a/test/lib/TestMac.py b/test/lib/TestMac.py
index 755d40e..68605d7 100644
--- a/test/lib/TestMac.py
+++ b/test/lib/TestMac.py
@@ -29,7 +29,7 @@
found_file, found_archs = match.groups()
if found_file != file or set(found_archs.split()) != set(archs):
print 'Expected file %s with arch %s, got %s with arch %s' % (
- file, ' '.join(archs), found_file, ' '.join(found_archs))
+ file, ' '.join(archs), found_file, found_archs)
test.fail_test()
diff --git a/test/mac/archs/test-valid-archs.gyp b/test/mac/archs/test-valid-archs.gyp
new file mode 100644
index 0000000..c90ec1f
--- /dev/null
+++ b/test/mac/archs/test-valid-archs.gyp
@@ -0,0 +1,28 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lib',
+ 'product_name': 'Test',
+ 'type': 'static_library',
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': ['i386', 'x86_64', 'unknown-arch'],
+ 'VALID_ARCHS': ['x86_64'],
+ },
+ },
+ {
+ 'target_name': 'exe',
+ 'product_name': 'Test',
+ 'type': 'executable',
+ 'dependencies': [ 'lib' ],
+ 'sources': [ 'my_main_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': ['i386', 'x86_64', 'unknown-arch'],
+ 'VALID_ARCHS': ['x86_64'],
+ },
+ }]
+}
diff --git a/test/mac/gyptest-archs.py b/test/mac/gyptest-archs.py
index 8ec5b60..ff632d7 100644
--- a/test/mac/gyptest-archs.py
+++ b/test/mac/gyptest-archs.py
@@ -29,6 +29,12 @@
expected_type = ['i386']
TestMac.CheckFileType(test, result_file, expected_type)
+ test.run_gyp('test-valid-archs.gyp', chdir='archs')
+ test.build('test-valid-archs.gyp', test.ALL, chdir='archs')
+ result_file = test.built_file_path('Test', chdir='archs')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['x86_64'])
+
test.run_gyp('test-archs-x86_64.gyp', chdir='archs')
test.build('test-archs-x86_64.gyp', test.ALL, chdir='archs')
result_file = test.built_file_path('Test64', chdir='archs')