summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2012-02-20 11:47:03 +0100
committerBen Noordhuis <info@bnoordhuis.nl>2012-02-20 11:47:03 +0100
commit4af673e161138fbed287a8e00cf7b2b0aafdd8b3 (patch)
tree90fb82bb98fdbcbb0c86c446e029c13bf999c8b0 /tools
parent7ae0d473a6b6ac8cb3e55d665528667566cd8e60 (diff)
downloadandroid-node-v8-4af673e161138fbed287a8e00cf7b2b0aafdd8b3.tar.gz
android-node-v8-4af673e161138fbed287a8e00cf7b2b0aafdd8b3.tar.bz2
android-node-v8-4af673e161138fbed287a8e00cf7b2b0aafdd8b3.zip
gyp: update to r1214
Diffstat (limited to 'tools')
-rwxr-xr-xtools/gyp/buildbot/buildbot_run.py43
-rwxr-xr-xtools/gyp/gyptest.py4
-rw-r--r--tools/gyp/pylib/gyp/common.py29
-rw-r--r--tools/gyp/pylib/gyp/generator/dump_dependency_json.py8
-rw-r--r--tools/gyp/pylib/gyp/generator/make.py1149
-rw-r--r--tools/gyp/pylib/gyp/generator/msvs.py101
-rw-r--r--tools/gyp/pylib/gyp/generator/ninja.py869
-rw-r--r--tools/gyp/pylib/gyp/generator/ninja_test.py42
-rw-r--r--tools/gyp/pylib/gyp/input.py20
-rwxr-xr-xtools/gyp/pylib/gyp/mac_tool.py150
-rw-r--r--tools/gyp/pylib/gyp/ninja_syntax.py66
-rwxr-xr-xtools/gyp/pylib/gyp/system_test.py15
-rw-r--r--tools/gyp/pylib/gyp/xcode_emulation.py972
-rwxr-xr-xtools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py6
-rwxr-xr-xtools/gyp/test/home_dot_gyp/gyptest-home-includes.py4
-rw-r--r--tools/gyp/test/lib/TestGyp.py29
-rw-r--r--tools/gyp/test/mac/action-envvars/action/action.gyp34
-rwxr-xr-xtools/gyp/test/mac/action-envvars/action/action.sh8
-rw-r--r--tools/gyp/test/mac/archs/my_file.cc4
-rw-r--r--tools/gyp/test/mac/archs/my_main_file.cc9
-rw-r--r--tools/gyp/test/mac/archs/test-archs-x86_64.gyp27
-rw-r--r--tools/gyp/test/mac/archs/test-no-archs.gyp21
-rw-r--r--tools/gyp/test/mac/copy-dylib/empty.c1
-rw-r--r--tools/gyp/test/mac/copy-dylib/test.gyp31
-rw-r--r--tools/gyp/test/mac/depend-on-bundle/English.lproj/InfoPlist.strings1
-rw-r--r--tools/gyp/test/mac/depend-on-bundle/Info.plist28
-rw-r--r--tools/gyp/test/mac/depend-on-bundle/bundle.c1
-rw-r--r--tools/gyp/test/mac/depend-on-bundle/executable.c4
-rw-r--r--tools/gyp/test/mac/depend-on-bundle/test.gyp28
-rw-r--r--tools/gyp/test/mac/global-settings/src/dir1/dir1.gyp11
-rw-r--r--tools/gyp/test/mac/global-settings/src/dir2/dir2.gyp22
-rw-r--r--tools/gyp/test/mac/global-settings/src/dir2/file.txt1
-rw-r--r--tools/gyp/test/mac/gyptest-action-envvars.py30
-rwxr-xr-xtools/gyp/test/mac/gyptest-app.py4
-rw-r--r--tools/gyp/test/mac/gyptest-archs.py37
-rwxr-xr-xtools/gyp/test/mac/gyptest-copies.py4
-rw-r--r--tools/gyp/test/mac/gyptest-copy-dylib.py25
-rwxr-xr-xtools/gyp/test/mac/gyptest-debuginfo.py2
-rw-r--r--tools/gyp/test/mac/gyptest-depend-on-bundle.py40
-rwxr-xr-xtools/gyp/test/mac/gyptest-framework.py4
-rw-r--r--tools/gyp/test/mac/gyptest-global-settings.py26
-rwxr-xr-xtools/gyp/test/mac/gyptest-infoplist-process.py4
-rwxr-xr-xtools/gyp/test/mac/gyptest-libraries.py22
-rwxr-xr-xtools/gyp/test/mac/gyptest-loadable-module.py4
-rw-r--r--tools/gyp/test/mac/gyptest-non-strs-flattened-to-env.py33
-rw-r--r--tools/gyp/test/mac/gyptest-postbuild-copy-bundle.py62
-rw-r--r--tools/gyp/test/mac/gyptest-postbuild-defaults.py29
-rwxr-xr-xtools/gyp/test/mac/gyptest-postbuild-fail.py4
-rw-r--r--tools/gyp/test/mac/gyptest-postbuild-multiple-configurations.py26
-rw-r--r--tools/gyp/test/mac/gyptest-postbuild-static-library.gyp28
-rwxr-xr-xtools/gyp/test/mac/gyptest-postbuild.py2
-rwxr-xr-xtools/gyp/test/mac/gyptest-prefixheader.py4
-rwxr-xr-xtools/gyp/test/mac/gyptest-rebuild.py29
-rw-r--r--tools/gyp/test/mac/gyptest-sourceless-module.gyp46
-rwxr-xr-xtools/gyp/test/mac/gyptest-strip.py2
-rwxr-xr-xtools/gyp/test/mac/gyptest-type-envvars.py4
-rwxr-xr-xtools/gyp/test/mac/gyptest-xcode-env-order.py59
-rw-r--r--tools/gyp/test/mac/libraries/subdir/README.txt1
-rw-r--r--tools/gyp/test/mac/libraries/subdir/hello.cc10
-rw-r--r--tools/gyp/test/mac/libraries/subdir/mylib.c7
-rw-r--r--tools/gyp/test/mac/libraries/subdir/test.gyp66
-rw-r--r--tools/gyp/test/mac/non-strs-flattened-to-env/Info.plist15
-rw-r--r--tools/gyp/test/mac/non-strs-flattened-to-env/main.c7
-rw-r--r--tools/gyp/test/mac/non-strs-flattened-to-env/test.gyp24
-rw-r--r--tools/gyp/test/mac/postbuild-copy-bundle/Framework-Info.plist30
-rw-r--r--tools/gyp/test/mac/postbuild-copy-bundle/TestApp-Info.plist32
-rw-r--r--tools/gyp/test/mac/postbuild-copy-bundle/empty.c0
-rw-r--r--tools/gyp/test/mac/postbuild-copy-bundle/main.c4
-rwxr-xr-xtools/gyp/test/mac/postbuild-copy-bundle/postbuild-copy-framework.sh9
-rw-r--r--tools/gyp/test/mac/postbuild-copy-bundle/resource_file.sb1
-rw-r--r--tools/gyp/test/mac/postbuild-copy-bundle/test.gyp43
-rw-r--r--tools/gyp/test/mac/postbuild-defaults/Info.plist13
-rw-r--r--tools/gyp/test/mac/postbuild-defaults/main.c7
-rwxr-xr-xtools/gyp/test/mac/postbuild-defaults/postbuild-defaults.sh15
-rw-r--r--tools/gyp/test/mac/postbuild-defaults/test.gyp26
-rwxr-xr-xtools/gyp/test/mac/postbuild-fail/postbuild-fail.sh2
-rw-r--r--tools/gyp/test/mac/postbuild-multiple-configurations/main.c4
-rwxr-xr-xtools/gyp/test/mac/postbuild-multiple-configurations/postbuild-touch-file.sh7
-rw-r--r--tools/gyp/test/mac/postbuild-multiple-configurations/test.gyp26
-rw-r--r--tools/gyp/test/mac/postbuild-static-library/empty.c4
-rwxr-xr-xtools/gyp/test/mac/postbuild-static-library/postbuild-touch-file.sh7
-rw-r--r--tools/gyp/test/mac/postbuild-static-library/test.gyp34
-rw-r--r--tools/gyp/test/mac/prefixheader/file.cc1
-rw-r--r--tools/gyp/test/mac/prefixheader/file.m1
-rw-r--r--tools/gyp/test/mac/prefixheader/file.mm1
-rw-r--r--tools/gyp/test/mac/prefixheader/test.gyp61
-rw-r--r--tools/gyp/test/mac/rebuild/TestApp-Info.plist32
-rwxr-xr-xtools/gyp/test/mac/rebuild/delay-touch.sh6
-rw-r--r--tools/gyp/test/mac/rebuild/empty.c0
-rw-r--r--tools/gyp/test/mac/rebuild/main.c1
-rw-r--r--tools/gyp/test/mac/rebuild/test.gyp56
-rw-r--r--tools/gyp/test/mac/sourceless-module/empty.c1
-rw-r--r--tools/gyp/test/mac/sourceless-module/test.gyp39
-rw-r--r--tools/gyp/test/mac/type_envvars/test.gyp11
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_bundle_executable.sh8
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh9
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh8
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh8
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_none.sh16
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh8
-rwxr-xr-xtools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh8
-rw-r--r--tools/gyp/test/mac/xcode-env-order/Info.plist30
-rw-r--r--tools/gyp/test/mac/xcode-env-order/test.gyp68
-rw-r--r--tools/gyp/test/module/src/module.gyp2
-rw-r--r--tools/gyp/test/msvs/list_excluded/gyptest-all.py51
-rw-r--r--tools/gyp/test/msvs/list_excluded/hello.cpp10
-rw-r--r--tools/gyp/test/msvs/list_excluded/hello_exclude.gyp19
-rw-r--r--tools/gyp/test/msvs/list_excluded/hello_mac.cpp10
-rw-r--r--tools/gyp/test/msvs/uldi2010/gyptest-all.py20
-rw-r--r--tools/gyp/test/msvs/uldi2010/hello.c13
-rw-r--r--tools/gyp/test/msvs/uldi2010/hello.gyp26
-rw-r--r--tools/gyp/test/msvs/uldi2010/hello2.c10
-rw-r--r--tools/gyp/test/ninja/chained-dependency/chained-dependency.gyp52
-rw-r--r--tools/gyp/test/ninja/chained-dependency/chained.c5
-rwxr-xr-xtools/gyp/test/ninja/chained-dependency/gyptest-chained-dependency.py23
-rw-r--r--tools/gyp/test/restat/gyptest-restat.py31
-rw-r--r--tools/gyp/test/restat/src/create_intermediate.py17
-rw-r--r--tools/gyp/test/restat/src/restat.gyp48
-rwxr-xr-xtools/gyp/test/same-gyp-name/gyptest-all.py4
-rwxr-xr-xtools/gyp/test/same-gyp-name/gyptest-default.py4
-rwxr-xr-xtools/gyp/test/settings/gyptest-settings.py18
-rw-r--r--tools/gyp/test/settings/settings.gyp20
-rwxr-xr-xtools/gyp/test/small/gyptest-small.py3
-rwxr-xr-xtools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py2
-rw-r--r--tools/gyp/test/variables/commands/commands.gyp2
-rw-r--r--tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout14
-rw-r--r--tools/gyp/test/variables/commands/commands.gyp.stdout14
-rw-r--r--tools/gyp/test/variables/commands/commands.gypd.golden2
-rw-r--r--tools/gyp/test/variables/commands/test.py1
130 files changed, 3936 insertions, 1529 deletions
diff --git a/tools/gyp/buildbot/buildbot_run.py b/tools/gyp/buildbot/buildbot_run.py
index a8531258b2..e0fda368ae 100755
--- a/tools/gyp/buildbot/buildbot_run.py
+++ b/tools/gyp/buildbot/buildbot_run.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -23,32 +23,6 @@ BUILDBOT_DIR = os.path.dirname(os.path.abspath(__file__))
TRUNK_DIR = os.path.dirname(BUILDBOT_DIR)
ROOT_DIR = os.path.dirname(TRUNK_DIR)
OUT_DIR = os.path.join(TRUNK_DIR, 'out')
-NINJA_PATH = os.path.join(TRUNK_DIR, 'ninja' + EXE_SUFFIX)
-NINJA_WORK_DIR = os.path.join(ROOT_DIR, 'ninja_work')
-
-
-def InstallNinja():
- """Install + build ninja.
-
- Returns:
- 0 for success, 1 for failure.
- """
- print '@@@BUILD_STEP install ninja@@@'
- # Delete old version if any.
- try:
- shutil.rmtree(NINJA_WORK_DIR, ignore_errors=True)
- except:
- pass
- # Sync new copy from git.
- subprocess.check_call(
- 'git clone https://github.com/martine/ninja.git ' + NINJA_WORK_DIR,
- shell=True)
- # Bootstrap.
- subprocess.check_call('./bootstrap.sh', cwd=NINJA_WORK_DIR, shell=True)
- # Copy out ninja.
- shutil.copyfile(os.path.join(NINJA_WORK_DIR, 'ninja' + EXE_SUFFIX),
- NINJA_PATH)
- os.chmod(NINJA_PATH, 0777)
def GypTestFormat(title, format=None, msvs_version=None):
@@ -64,17 +38,6 @@ def GypTestFormat(title, format=None, msvs_version=None):
if not format:
format = title
- # Install ninja if needed.
- # NOTE: as ninja gets installed each time, regressions to ninja can come
- # either from changes to ninja itself, or changes to gyp.
- if format == 'ninja':
- try:
- InstallNinja()
- except Exception, e:
- print '@@@STEP_FAILURE@@@'
- print str(e)
- return 1
-
print '@@@BUILD_STEP ' + title + '@@@'
sys.stdout.flush()
env = os.environ.copy()
@@ -104,10 +67,6 @@ def GypBuild():
print '@@@BUILD_STEP cleanup@@@'
print 'Removing %s...' % OUT_DIR
shutil.rmtree(OUT_DIR, ignore_errors=True)
- print 'Removing %s...' % NINJA_WORK_DIR
- shutil.rmtree(NINJA_WORK_DIR, ignore_errors=True)
- print 'Removing %s...' % NINJA_PATH
- shutil.rmtree(NINJA_PATH, ignore_errors=True)
print 'Done.'
retcode = 0
diff --git a/tools/gyp/gyptest.py b/tools/gyp/gyptest.py
index e8bf482d99..d9c814f3fa 100755
--- a/tools/gyp/gyptest.py
+++ b/tools/gyp/gyptest.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -209,7 +209,7 @@ def main(argv=None):
'freebsd7': ['make'],
'freebsd8': ['make'],
'cygwin': ['msvs'],
- 'win32': ['msvs'],
+ 'win32': ['msvs', 'ninja'],
'linux2': ['make', 'ninja'],
'linux3': ['make', 'ninja'],
'darwin': ['make', 'ninja', 'xcode'],
diff --git a/tools/gyp/pylib/gyp/common.py b/tools/gyp/pylib/gyp/common.py
index 97594cd5d6..614d207c30 100644
--- a/tools/gyp/pylib/gyp/common.py
+++ b/tools/gyp/pylib/gyp/common.py
@@ -1,7 +1,9 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from __future__ import with_statement
+
import errno
import filecmp
import os.path
@@ -347,6 +349,8 @@ def WriteOnDiff(filename):
def GetFlavor(params):
"""Returns |params.flavor| if it's set, the system's default flavor else."""
flavors = {
+ 'cygwin': 'win',
+ 'win32': 'win',
'darwin': 'mac',
'sunos5': 'solaris',
'freebsd7': 'freebsd',
@@ -356,6 +360,29 @@ def GetFlavor(params):
return params.get('flavor', flavor)
+def CopyTool(flavor, out_path):
+ """Finds (mac|sun)_tool.gyp in the gyp directory and copies it
+ to |out_path|."""
+ prefix = { 'solaris': 'sun', 'mac': 'mac' }.get(flavor, None)
+ if not prefix:
+ return
+
+ # Slurp input file.
+ source_path = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), '%s_tool.py' % prefix)
+ with open(source_path) as source_file:
+ source = source_file.readlines()
+
+ # Add header and write it out.
+ tool_path = os.path.join(out_path, 'gyp-%s-tool' % prefix)
+ with open(tool_path, 'w') as tool_file:
+ tool_file.write(
+ ''.join([source[0], '# Generated by gyp. Do not edit.\n'] + source[1:]))
+
+ # Make file executable.
+ os.chmod(tool_path, 0755)
+
+
# From Alex Martelli,
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
# ASPN: Python Cookbook: Remove duplicates from a sequence
diff --git a/tools/gyp/pylib/gyp/generator/dump_dependency_json.py b/tools/gyp/pylib/gyp/generator/dump_dependency_json.py
index 1e0900a231..c1c2fbdc98 100644
--- a/tools/gyp/pylib/gyp/generator/dump_dependency_json.py
+++ b/tools/gyp/pylib/gyp/generator/dump_dependency_json.py
@@ -20,15 +20,15 @@ for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
- 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
- 'LINKER_SUPPORTS_ICF']:
+ 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX']:
generator_default_variables[unused] = ''
def CalculateVariables(default_variables, params):
generator_flags = params.get('generator_flags', {})
- default_variables['OS'] = generator_flags.get(
- 'os', gyp.common.GetFlavor(params))
+ for key, val in generator_flags.items():
+ default_variables.setdefault(key, val)
+ default_variables.setdefault('OS', gyp.common.GetFlavor(params))
def CalculateGeneratorInputInfo(params):
diff --git a/tools/gyp/pylib/gyp/generator/make.py b/tools/gyp/pylib/gyp/generator/make.py
index 87ad79a675..e93cb03314 100644
--- a/tools/gyp/pylib/gyp/generator/make.py
+++ b/tools/gyp/pylib/gyp/generator/make.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -24,9 +24,9 @@
import gyp
import gyp.common
import gyp.system_test
+import gyp.xcode_emulation
import os
import re
-import shlex
import sys
generator_default_variables = {
@@ -58,9 +58,6 @@ generator_wants_sorted_dependencies = False
def CalculateVariables(default_variables, params):
"""Calculate additional variables for use in the build (called by gyp)."""
cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc'))
- default_variables['LINKER_SUPPORTS_ICF'] = \
- gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target)
-
flavor = gyp.common.GetFlavor(params)
if flavor == 'mac':
default_variables.setdefault('OS', 'mac')
@@ -159,7 +156,7 @@ cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSE
LINK_COMMANDS_MAC = """\
quiet_cmd_alink = LIBTOOL-STATIC $@
-cmd_alink = rm -f $@ && libtool -static -o $@ $(filter %.o,$^)
+cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool -static -o $@ $(filter %.o,$^)
quiet_cmd_link = LINK($(TOOLSET)) $@
cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
@@ -213,6 +210,7 @@ MAKEFLAGS=-r
# The source directory tree.
srcdir := %(srcdir)s
+abs_srcdir := $(abspath $(srcdir))
# The name of the builddir.
builddir_name ?= %(builddir)s
@@ -260,7 +258,7 @@ CFLAGS.target ?= $(CFLAGS)
CXX.target ?= $(CXX)
CXXFLAGS.target ?= $(CXXFLAGS)
LINK.target ?= $(LINK)
-LDFLAGS.target ?= $(LDFLAGS) %(LINK_flags)s
+LDFLAGS.target ?= $(LDFLAGS)
AR.target ?= $(AR)
ARFLAGS.target ?= %(ARFLAGS.target)s
@@ -458,7 +456,7 @@ cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
quiet_cmd_pch_c = CXX($(TOOLSET)) $@
cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_pch_cc = CXX($(TOOLSET)) $@
-cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CCFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
+cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
quiet_cmd_pch_m = CXX($(TOOLSET)) $@
cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
quiet_cmd_pch_mm = CXX($(TOOLSET)) $@
@@ -605,7 +603,7 @@ def QuoteIfNecessary(string):
def StringToMakefileVariable(string):
"""Convert a string to a value that is acceptable as a make variable name."""
# TODO: replace other metacharacters that we encounter.
- return string.replace(' ', '_')
+ return re.sub('[ {}$]', '_', string)
srcdir_prefix = ''
@@ -618,12 +616,8 @@ def Sourceify(path):
return srcdir_prefix + path
-def QuoteSpaces(s):
- return s.replace(' ', r'\ ')
-
-
-def ReplaceQuotedSpaces(s):
- return s.replace(r'\ ', SPACE_REPLACEMENT)
+def QuoteSpaces(s, quote=r'\ '):
+ return s.replace(' ', quote)
# Map from qualified target to path to output.
@@ -635,603 +629,6 @@ target_outputs = {}
target_link_deps = {}
-class XcodeSettings(object):
- """A class that understands the gyp 'xcode_settings' object."""
-
- def __init__(self, spec):
- self.spec = spec
-
- # Per-target 'xcode_settings' are pushed down into configs earlier by gyp.
- # This means self.xcode_settings[config] always contains all settings
- # for that config -- the per-target settings as well. Settings that are
- # the same for all configs are implicitly per-target settings.
- self.xcode_settings = {}
- configs = spec['configurations']
- for configname, config in configs.iteritems():
- self.xcode_settings[configname] = config.get('xcode_settings', {})
-
- # This is only non-None temporarily during the execution of some methods.
- self.configname = None
-
- def _Settings(self):
- assert self.configname
- return self.xcode_settings[self.configname]
-
- def _Test(self, test_key, cond_key, default):
- return self._Settings().get(test_key, default) == cond_key
-
- def _Appendf(self, lst, test_key, format_str, default=None):
- if test_key in self._Settings():
- lst.append(format_str % str(self._Settings()[test_key]))
- elif default:
- lst.append(format_str % str(default))
-
- def _WarnUnimplemented(self, test_key):
- if test_key in self._Settings():
- print 'Warning: Ignoring not yet implemented key "%s".' % test_key
-
- def _IsBundle(self):
- return int(self.spec.get('mac_bundle', 0)) != 0
-
- def GetFrameworkVersion(self):
- """Returns the framework version of the current target. Only valid for
- bundles."""
- assert self._IsBundle()
- return self.GetPerTargetSetting('FRAMEWORK_VERSION', default='A')
-
- def GetWrapperExtension(self):
- """Returns the bundle extension (.app, .framework, .plugin, etc). Only
- valid for bundles."""
- assert self._IsBundle()
- if self.spec['type'] in ('loadable_module', 'shared_library'):
- default_wrapper_extension = {
- 'loadable_module': 'bundle',
- 'shared_library': 'framework',
- }[self.spec['type']]
- wrapper_extension = self.GetPerTargetSetting(
- 'WRAPPER_EXTENSION', default=default_wrapper_extension)
- return '.' + self.spec.get('product_extension', wrapper_extension)
- elif self.spec['type'] == 'executable':
- return '.app'
- else:
- assert False, "Don't know extension for '%s', target '%s'" % (
- self.spec['type'], self.spec['target_name'])
-
- def GetProductName(self):
- """Returns PRODUCT_NAME."""
- return self.spec.get('product_name', self.spec['target_name'])
-
- def GetWrapperName(self):
- """Returns the directory name of the bundle represented by this target.
- Only valid for bundles."""
- assert self._IsBundle()
- return self.GetProductName() + self.GetWrapperExtension()
-
- def GetBundleContentsFolderPath(self):
- """Returns the qualified path to the bundle's contents folder. E.g.
- Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles."""
- assert self._IsBundle()
- if self.spec['type'] == 'shared_library':
- return os.path.join(
- self.GetWrapperName(), 'Versions', self.GetFrameworkVersion())
- else:
- # loadable_modules have a 'Contents' folder like executables.
- return os.path.join(self.GetWrapperName(), 'Contents')
-
- def GetBundleResourceFolder(self):
- """Returns the qualified path to the bundle's resource folder. E.g.
- Chromium.app/Contents/Resources. Only valid for bundles."""
- assert self._IsBundle()
- return os.path.join(self.GetBundleContentsFolderPath(), 'Resources')
-
- def GetBundlePlistPath(self):
- """Returns the qualified path to the bundle's plist file. E.g.
- Chromium.app/Contents/Info.plist. Only valid for bundles."""
- assert self._IsBundle()
- if self.spec['type'] in ('executable', 'loadable_module'):
- return os.path.join(self.GetBundleContentsFolderPath(), 'Info.plist')
- else:
- return os.path.join(self.GetBundleContentsFolderPath(),
- 'Resources', 'Info.plist')
-
- def GetProductType(self):
- """Returns the PRODUCT_TYPE of this target."""
- if self._IsBundle():
- return {
- 'executable': 'com.apple.product-type.application',
- 'loadable_module': 'com.apple.product-type.bundle',
- 'shared_library': 'com.apple.product-type.framework',
- }[self.spec['type']]
- else:
- return {
- 'executable': 'com.apple.product-type.tool',
- 'loadable_module': 'com.apple.product-type.library.dynamic',
- 'shared_library': 'com.apple.product-type.library.dynamic',
- 'static_library': 'com.apple.product-type.library.static',
- }[self.spec['type']]
-
- def GetMachOType(self):
- """Returns the MACH_O_TYPE of this target."""
- # Weird, but matches Xcode.
- if not self._IsBundle() and self.spec['type'] == 'executable':
- return ''
- return {
- 'executable': 'mh_execute',
- 'static_library': 'staticlib',
- 'shared_library': 'mh_dylib',
- 'loadable_module': 'mh_bundle',
- }[self.spec['type']]
-
- def _GetBundleBinaryPath(self):
- """Returns the name of the bundle binary of by this target.
- E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles."""
- assert self._IsBundle()
- if self.spec['type'] in ('shared_library'):
- path = self.GetBundleContentsFolderPath()
- elif self.spec['type'] in ('executable', 'loadable_module'):
- path = os.path.join(self.GetBundleContentsFolderPath(), 'MacOS')
- return os.path.join(path, self.spec.get('product_name',
- self.spec['target_name']))
-
- def _GetStandaloneExecutableSuffix(self):
- if 'product_extension' in self.spec:
- return '.' + self.spec['product_extension']
- return {
- 'executable': '',
- 'static_library': '.a',
- 'shared_library': '.dylib',
- 'loadable_module': '.so',
- }[self.spec['type']]
-
- def _GetStandaloneExecutablePrefix(self):
- return self.spec.get('product_prefix', {
- 'executable': '',
- 'static_library': 'lib',
- 'shared_library': 'lib',
- # Non-bundled loadable_modules are called foo.so for some reason
- # (that is, .so and no prefix) with the xcode build -- match that.
- 'loadable_module': '',
- }[self.spec['type']])
-
- def _GetStandaloneBinaryPath(self):
- """Returns the name of the non-bundle binary represented by this target.
- E.g. hello_world. Only valid for non-bundles."""
- assert not self._IsBundle()
- assert self.spec['type'] in (
- 'executable', 'shared_library', 'static_library', 'loadable_module')
- target = self.spec['target_name']
- if self.spec['type'] == 'static_library':
- if target[:3] == 'lib':
- target = target[3:]
- elif self.spec['type'] in ('loadable_module', 'shared_library'):
- if target[:3] == 'lib':
- target = target[3:]
-
- target_prefix = self._GetStandaloneExecutablePrefix()
- target = self.spec.get('product_name', target)
- target_ext = self._GetStandaloneExecutableSuffix()
- return target_prefix + target + target_ext
-
- def GetExecutablePath(self):
- """Returns the directory name of the bundle represented by this target. E.g.
- Chromium.app/Contents/MacOS/Chromium."""
- if self._IsBundle():
- return self._GetBundleBinaryPath()
- 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."""
- # This functions (and the similar ones below) do not offer complete
- # emulation of all xcode_settings keys. They're implemented on demand.
-
- self.configname = configname
- cflags = []
-
- sdk_root = self._SdkPath()
- if 'SDKROOT' in self._Settings():
- cflags.append('-isysroot %s' % sdk_root)
-
- if self._Test('GCC_CW_ASM_SYNTAX', 'YES', default='YES'):
- cflags.append('-fasm-blocks')
-
- if 'GCC_DYNAMIC_NO_PIC' in self._Settings():
- if self._Settings()['GCC_DYNAMIC_NO_PIC'] == 'YES':
- cflags.append('-mdynamic-no-pic')
- else:
- pass
- # TODO: In this case, it depends on the target. xcode passes
- # mdynamic-no-pic by default for executable and possibly static lib
- # according to mento
-
- if self._Test('GCC_ENABLE_PASCAL_STRINGS', 'YES', default='YES'):
- cflags.append('-mpascal-strings')
-
- self._Appendf(cflags, 'GCC_OPTIMIZATION_LEVEL', '-O%s', default='s')
-
- 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':
- cflags.append('-gdwarf-2')
- else:
- raise NotImplementedError('Unknown debug format %s' % dbg_format)
-
- if self._Test('GCC_SYMBOLS_PRIVATE_EXTERN', 'YES', default='NO'):
- cflags.append('-fvisibility=hidden')
-
- if self._Test('GCC_TREAT_WARNINGS_AS_ERRORS', 'YES', default='NO'):
- cflags.append('-Werror')
-
- if self._Test('GCC_WARN_ABOUT_MISSING_NEWLINE', 'YES', default='NO'):
- cflags.append('-Wnewline-eof')
-
- self._Appendf(cflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
-
- # TODO:
- self._WarnUnimplemented('ARCHS')
- if self._Test('COPY_PHASE_STRIP', 'YES', default='NO'):
- self._WarnUnimplemented('COPY_PHASE_STRIP')
- self._WarnUnimplemented('GCC_DEBUGGING_SYMBOLS')
- self._WarnUnimplemented('GCC_ENABLE_OBJC_EXCEPTIONS')
- self._WarnUnimplemented('GCC_ENABLE_OBJC_GC')
-
- # TODO: This is exported correctly, but assigning to it is not supported.
- self._WarnUnimplemented('MACH_O_TYPE')
- self._WarnUnimplemented('PRODUCT_TYPE')
-
- # TODO: Do not hardcode arch. Supporting fat binaries will be annoying.
- cflags.append('-arch i386')
-
- cflags += self._Settings().get('OTHER_CFLAGS', [])
- cflags += self._Settings().get('WARNING_CFLAGS', [])
-
- config = self.spec['configurations'][self.configname]
- framework_dirs = config.get('mac_framework_dirs', [])
- for directory in framework_dirs:
- cflags.append('-F ' + directory.replace('$(SDKROOT)', sdk_root))
-
- self.configname = None
- return cflags
-
- def GetCflagsC(self, configname):
- """Returns flags that need to be added to .c, and .m compilations."""
- self.configname = configname
- cflags_c = []
- self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s')
- self.configname = None
- return cflags_c
-
- def GetCflagsCC(self, configname):
- """Returns flags that need to be added to .cc, and .mm compilations."""
- self.configname = configname
- cflags_cc = []
- if self._Test('GCC_ENABLE_CPP_RTTI', 'NO', default='YES'):
- cflags_cc.append('-fno-rtti')
- if self._Test('GCC_ENABLE_CPP_EXCEPTIONS', 'NO', default='YES'):
- cflags_cc.append('-fno-exceptions')
- if self._Test('GCC_INLINES_ARE_PRIVATE_EXTERN', 'YES', default='NO'):
- cflags_cc.append('-fvisibility-inlines-hidden')
- if self._Test('GCC_THREADSAFE_STATICS', 'NO', default='YES'):
- cflags_cc.append('-fno-threadsafe-statics')
- self.configname = None
- return cflags_cc
-
- def GetCflagsObjC(self, configname):
- """Returns flags that need to be added to .m compilations."""
- self.configname = configname
- self.configname = None
- return []
-
- def GetCflagsObjCC(self, configname):
- """Returns flags that need to be added to .mm compilations."""
- self.configname = configname
- cflags_objcc = []
- if self._Test('GCC_OBJC_CALL_CXX_CDTORS', 'YES', default='NO'):
- cflags_objcc.append('-fobjc-call-cxx-cdtors')
- self.configname = None
- return cflags_objcc
-
- def GetLdflags(self, target, configname):
- """Returns flags that need to be passed to the linker."""
- self.configname = configname
- ldflags = []
-
- # The xcode build is relative to a gyp file's directory, and OTHER_LDFLAGS
- # contains two entries that depend on this. Explicitly absolutify for these
- # two cases.
- def AbsolutifyPrefix(flag, prefix):
- if flag.startswith(prefix):
- flag = prefix + target.Absolutify(flag[len(prefix):])
- return flag
- for ldflag in self._Settings().get('OTHER_LDFLAGS', []):
- # Required for ffmpeg (no idea why they don't use LIBRARY_SEARCH_PATHS,
- # TODO(thakis): Update ffmpeg.gyp):
- ldflag = AbsolutifyPrefix(ldflag, '-L')
- # Required for the nacl plugin:
- ldflag = AbsolutifyPrefix(ldflag, '-Wl,-exported_symbols_list ')
- ldflags.append(ldflag)
-
- if self._Test('DEAD_CODE_STRIPPING', 'YES', default='NO'):
- ldflags.append('-Wl,-dead_strip')
-
- if self._Test('PREBINDING', 'YES', default='NO'):
- ldflags.append('-Wl,-prebind')
-
- self._Appendf(
- ldflags, 'DYLIB_COMPATIBILITY_VERSION', '-compatibility_version %s')
- self._Appendf(
- ldflags, 'DYLIB_CURRENT_VERSION', '-current_version %s')
- self._Appendf(
- ldflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
- 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)
-
- if 'ORDER_FILE' in self._Settings():
- ldflags.append('-Wl,-order_file ' +
- '-Wl,' + target.Absolutify(self._Settings()['ORDER_FILE']))
-
- # TODO: Do not hardcode arch. Supporting fat binaries will be annoying.
- ldflags.append('-arch i386')
-
- # Xcode adds the product directory by default.
- ldflags.append('-L' + generator_default_variables['PRODUCT_DIR'])
-
- install_name = self.GetPerTargetSetting('LD_DYLIB_INSTALL_NAME')
- install_base = self.GetPerTargetSetting('DYLIB_INSTALL_NAME_BASE')
- default_install_name = \
- '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)'
- if not install_name and install_base:
- install_name = default_install_name
-
- if install_name:
- # Hardcode support for the variables used in chromium for now, to unblock
- # people using the make build.
- if '$' in install_name:
- assert install_name in ('$(DYLIB_INSTALL_NAME_BASE:standardizepath)/'
- '$(WRAPPER_NAME)/$(PRODUCT_NAME)', default_install_name), (
- 'Variables in LD_DYLIB_INSTALL_NAME are not generally supported yet'
- ' in target \'%s\' (got \'%s\')' %
- (self.spec['target_name'], install_name))
- # I'm not quite sure what :standardizepath does. Just call normpath(),
- # but don't let @executable_path/../foo collapse to foo.
- if '/' in install_base:
- prefix, rest = '', install_base
- if install_base.startswith('@'):
- prefix, rest = install_base.split('/', 1)
- rest = os.path.normpath(rest) # :standardizepath
- install_base = os.path.join(prefix, rest)
-
- install_name = install_name.replace(
- '$(DYLIB_INSTALL_NAME_BASE:standardizepath)', install_base)
- if self._IsBundle():
- # These are only valid for bundles, hence the |if|.
- install_name = install_name.replace(
- '$(WRAPPER_NAME)', self.GetWrapperName())
- install_name = install_name.replace(
- '$(PRODUCT_NAME)', self.GetProductName())
- else:
- assert '$(WRAPPER_NAME)' not in install_name
- assert '$(PRODUCT_NAME)' not in install_name
-
- install_name = install_name.replace(
- '$(EXECUTABLE_PATH)', self.GetExecutablePath())
-
- install_name = QuoteSpaces(install_name)
- ldflags.append('-install_name ' + install_name)
-
- 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."""
- first_pass = True
- result = None
- for configname in sorted(self.xcode_settings.keys()):
- if first_pass:
- result = self.xcode_settings[configname].get(setting, None)
- first_pass = False
- else:
- assert result == self.xcode_settings[configname].get(setting, None), (
- "Expected per-target setting for '%s', got per-config setting "
- "(target %s)" % (setting, spec['target_name']))
- if result is None:
- return default
- return result
-
- def _GetStripPostbuilds(self, configname, output_binary):
- """Returns a list of shell commands that contain the shell commands
- neccessary to strip this target's binary. These should be run as postbuilds
- before the actual postbuilds run."""
- self.configname = configname
-
- result = []
- if (self._Test('DEPLOYMENT_POSTPROCESSING', 'YES', default='NO') and
- self._Test('STRIP_INSTALLED_PRODUCT', 'YES', default='NO')):
-
- default_strip_style = 'debugging'
- if self._IsBundle():
- default_strip_style = 'non-global'
- elif self.spec['type'] == 'executable':
- default_strip_style = 'all'
-
- strip_style = self._Settings().get('STRIP_STYLE', default_strip_style)
- strip_flags = {
- 'all': '',
- 'non-global': '-x',
- 'debugging': '-S',
- }[strip_style]
-
- explicit_strip_flags = self._Settings().get('STRIPFLAGS', '')
- if explicit_strip_flags:
- strip_flags += ' ' + explicit_strip_flags
-
- result.append('echo STRIP\\(%s\\)' % self.spec['target_name'])
- result.append('strip %s %s' % (strip_flags, output_binary))
-
- self.configname = None
- return result
-
- def _GetDebugPostbuilds(self, configname, output, output_binary):
- """Returns a list of shell commands that contain the shell commands
- neccessary to massage this target's debug information. These should be run
- as postbuilds before the actual postbuilds run."""
- self.configname = configname
-
- # For static libraries, no dSYMs are created.
- result = []
- if (self._Test('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES', default='YES') and
- self._Test(
- 'DEBUG_INFORMATION_FORMAT', 'dwarf-with-dsym', default='dwarf') and
- self.spec['type'] != 'static_library'):
- result.append('echo DSYMUTIL\\(%s\\)' % self.spec['target_name'])
- result.append('dsymutil %s -o %s' % (output_binary, output + '.dSYM'))
-
- self.configname = None
- return result
-
- def GetTargetPostbuilds(self, configname, output, output_binary):
- """Returns a list of shell commands that contain the shell commands
- to run as postbuilds for this target, before the actual postbuilds."""
- # dSYMs need to build before stripping happens.
- return (self._GetDebugPostbuilds(configname, output, output_binary) +
- self._GetStripPostbuilds(configname, output_binary))
-
-
-class MacPrefixHeader(object):
- """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. If
- GCC_PREFIX_HEADER isn't present (in most gyp targets on mac, and always on
- non-mac systems), all methods of this class are no-ops."""
-
- def __init__(self, path_provider):
- # This doesn't support per-configuration prefix headers. Good enough
- # for now.
- self.header = None
- self.compile_headers = False
- if path_provider.flavor == 'mac':
- self.header = path_provider.xcode_settings.GetPerTargetSetting(
- 'GCC_PREFIX_HEADER')
- self.compile_headers = path_provider.xcode_settings.GetPerTargetSetting(
- 'GCC_PRECOMPILE_PREFIX_HEADER', default='NO') != 'NO'
- self.compiled_headers = {}
- if self.header:
- self.header = path_provider.Absolutify(self.header)
- if self.compile_headers:
- for lang in ['c', 'cc', 'm', 'mm']:
- self.compiled_headers[lang] = path_provider.Pchify(self.header, lang)
-
- def _Gch(self, lang):
- """Returns the actual file name of the prefix header for language |lang|."""
- assert self.compile_headers
- return self.compiled_headers[lang] + '.gch'
-
- def WriteObjDependencies(self, compilable, objs, writer):
- """Writes dependencies from the object files in |objs| to the corresponding
- precompiled header file. |compilable[i]| has to be the source file belonging
- to |objs[i]|."""
- if not self.header or not self.compile_headers:
- return
-
- writer.WriteLn('# Dependencies from obj files to their precompiled headers')
- for source, obj in zip(compilable, objs):
- ext = os.path.splitext(source)[1]
- lang = {
- '.c': 'c',
- '.cpp': 'cc', '.cc': 'cc', '.cxx': 'cc',
- '.m': 'm',
- '.mm': 'mm',
- }.get(ext, None)
- if lang:
- writer.WriteLn('%s: %s' % (obj, self._Gch(lang)))
- writer.WriteLn('# End precompiled header dependencies')
-
- def GetInclude(self, lang):
- """Gets the cflags to include the prefix header for language |lang|."""
- if self.compile_headers and lang in self.compiled_headers:
- return '-include %s ' % self.compiled_headers[lang]
- elif self.header:
- return '-include %s ' % self.header
- else:
- return ''
-
- def WritePchTargets(self, writer):
- """Writes make rules to compile the prefix headers."""
- if not self.header or not self.compile_headers:
- return
-
- writer.WriteLn(self._Gch('c') + ": GYP_PCH_CFLAGS := "
- "-x c-header "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_C_$(BUILDTYPE))")
-
- writer.WriteLn(self._Gch('cc') + ": GYP_PCH_CCFLAGS := "
- "-x c++-header "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_CC_$(BUILDTYPE))")
-
- writer.WriteLn(self._Gch('m') + ": GYP_PCH_OBJCFLAGS := "
- "-x objective-c-header "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_C_$(BUILDTYPE)) "
- "$(CFLAGS_OBJC_$(BUILDTYPE))")
-
- writer.WriteLn(self._Gch('mm') + ": GYP_PCH_OBJCXXFLAGS := "
- "-x objective-c++-header "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_CC_$(BUILDTYPE)) "
- "$(CFLAGS_OBJCC_$(BUILDTYPE))")
-
- for lang in self.compiled_headers:
- writer.WriteLn('%s: %s FORCE_DO_CMD' % (self._Gch(lang), self.header))
- writer.WriteLn('\t@$(call do_cmd,pch_%s,1)' % lang)
- writer.WriteLn('')
- assert ' ' not in self._Gch(lang), (
- "Spaces in gch filenames not supported (%s)" % self._Gch(lang))
- writer.WriteLn('all_deps += %s' % self._Gch(lang))
- writer.WriteLn('')
-
-
class MakefileWriter:
"""MakefileWriter packages up the writing of one target-specific foobar.mk.
@@ -1294,27 +691,11 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
self.type = spec['type']
self.toolset = spec['toolset']
- if self.type == 'settings':
- # TODO: 'settings' is not actually part of gyp; it was
- # accidentally introduced somehow into just the Linux build files.
- # Remove this (or make it an error) once all the users are fixed.
- print ("WARNING: %s uses invalid type 'settings'. " % self.target +
- "Please fix the source gyp file to use type 'none'.")
- print "See http://code.google.com/p/chromium/issues/detail?id=96629 ."
- self.type = 'none'
-
- # Bundles are directories with a certain subdirectory structure, instead of
- # just a single file. Bundle rules do not produce a binary but also package
- # resources into that directory.
- self.is_mac_bundle = (int(spec.get('mac_bundle', 0)) != 0 and
- self.flavor == 'mac')
- if self.is_mac_bundle:
- assert self.type != 'none', (
- 'mac_bundle targets cannot have type none (target "%s")' %
- self.target)
-
+ self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
if self.flavor == 'mac':
- self.xcode_settings = XcodeSettings(spec)
+ self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
+ else:
+ self.xcode_settings = None
deps, link_deps = self.ComputeDeps(spec)
@@ -1333,9 +714,6 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
else:
self.output = self.output_binary = self.ComputeOutput(spec)
- self.output = QuoteSpaces(self.output)
- self.output_binary = QuoteSpaces(self.output_binary)
-
self._INSTALLABLE_TARGETS = ('executable', 'loadable_module',
'shared_library')
if self.type in self._INSTALLABLE_TARGETS:
@@ -1351,7 +729,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# Actions must come first, since they can generate more OBJs for use below.
if 'actions' in spec:
self.WriteActions(spec['actions'], extra_sources, extra_outputs,
- extra_mac_bundle_resources, part_of_all, spec)
+ extra_mac_bundle_resources, part_of_all)
# Rules must be early like actions.
if 'rules' in spec:
@@ -1359,25 +737,23 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
extra_mac_bundle_resources, part_of_all)
if 'copies' in spec:
- self.WriteCopies(spec['copies'], extra_outputs, part_of_all, spec)
+ self.WriteCopies(spec['copies'], extra_outputs, part_of_all)
# Bundle resources.
if self.is_mac_bundle:
all_mac_bundle_resources = (
spec.get('mac_bundle_resources', []) + extra_mac_bundle_resources)
- if all_mac_bundle_resources:
- self.WriteMacBundleResources(
- all_mac_bundle_resources, mac_bundle_deps, spec)
- info_plist = self.xcode_settings.GetPerTargetSetting('INFOPLIST_FILE')
- if info_plist:
- self.WriteMacInfoPlist(info_plist, mac_bundle_deps, spec)
+ self.WriteMacBundleResources(all_mac_bundle_resources, mac_bundle_deps)
+ self.WriteMacInfoPlist(mac_bundle_deps)
# Sources.
all_sources = spec.get('sources', []) + extra_sources
if all_sources:
self.WriteSources(
configs, deps, all_sources, extra_outputs,
- extra_link_deps, part_of_all, MacPrefixHeader(self))
+ extra_link_deps, part_of_all,
+ gyp.xcode_emulation.MacPrefixHeader(
+ self.xcode_settings, self.Absolutify, self.Pchify))
sources = filter(Compilable, all_sources)
if sources:
self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1)
@@ -1444,7 +820,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
def WriteActions(self, actions, extra_sources, extra_outputs,
- extra_mac_bundle_resources, part_of_all, spec):
+ extra_mac_bundle_resources, part_of_all):
"""Write Makefile code for any 'actions' from the gyp input.
extra_sources: a list that will be filled in with newly generated source
@@ -1510,10 +886,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# it's superfluous for the "extra outputs", and this avoids accidentally
# writing duplicate dummy rules for those outputs.
# Same for environment.
- self.WriteMakeRule(outputs[:1], ['obj := $(abs_obj)'])
- # Needs to be before builddir is redefined in the next line!
- self.WriteXcodeEnv(outputs[0], spec, target_relative_path=True)
- self.WriteMakeRule(outputs[:1], ['builddir := $(abs_builddir)'])
+ self.WriteLn("%s: obj := $(abs_obj)" % QuoteSpaces(outputs[0]))
+ self.WriteLn("%s: builddir := $(abs_builddir)" % QuoteSpaces(outputs[0]))
+ self.WriteXcodeEnv(outputs[0], self.GetXcodeEnv())
for input in inputs:
assert ' ' not in input, (
@@ -1522,6 +897,11 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
assert ' ' not in output, (
"Spaces in action output filenames not supported (%s)" % output)
+ # See the comment in WriteCopies about expanding env vars.
+ env = self.GetXcodeEnv()
+ outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs]
+ inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs]
+
self.WriteDoCmd(outputs, map(Sourceify, map(self.Absolutify, inputs)),
part_of_all=part_of_all, command=name)
@@ -1592,8 +972,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# Only write the 'obj' and 'builddir' rules for the "primary" output
# (:1); it's superfluous for the "extra outputs", and this avoids
# accidentally writing duplicate dummy rules for those outputs.
- self.WriteMakeRule(outputs[:1], ['obj := $(abs_obj)'])
- self.WriteMakeRule(outputs[:1], ['builddir := $(abs_builddir)'])
+ self.WriteLn('%s: obj := $(abs_obj)' % outputs[0])
+ self.WriteLn('%s: builddir := $(abs_builddir)' % outputs[0])
self.WriteMakeRule(outputs, inputs + ['FORCE_DO_CMD'], actions)
for output in outputs:
assert ' ' not in output, (
@@ -1652,7 +1032,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
self.WriteLn('')
- def WriteCopies(self, copies, extra_outputs, part_of_all, spec):
+ def WriteCopies(self, copies, extra_outputs, part_of_all):
"""Write Makefile code for any 'copies' from the gyp input.
extra_outputs: a list that will be filled in with any outputs of this action
@@ -1670,8 +1050,6 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
filename = os.path.split(path)[1]
output = Sourceify(self.Absolutify(os.path.join(copy['destination'],
filename)))
- path = QuoteSpaces(path)
- output = QuoteSpaces(output)
# If the output path has variables in it, which happens in practice for
# 'copies', writing the environment as target-local doesn't work,
@@ -1682,65 +1060,39 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# As a workaround, manually expand variables at gyp time. Since 'copies'
# can't run scripts, there's no need to write the env then.
# WriteDoCmd() will escape spaces for .d files.
- import gyp.generator.xcode as xcode_generator
- env = self.GetXcodeEnv(spec)
- output = xcode_generator.ExpandXcodeVariables(output, env)
- path = xcode_generator.ExpandXcodeVariables(path, env)
+ env = self.GetXcodeEnv()
+ output = gyp.xcode_emulation.ExpandEnvVars(output, env)
+ path = gyp.xcode_emulation.ExpandEnvVars(path, env)
self.WriteDoCmd([output], [path], 'copy', part_of_all)
outputs.append(output)
- self.WriteLn('%s = %s' % (variable, ' '.join(outputs)))
+ self.WriteLn('%s = %s' % (variable, ' '.join(map(QuoteSpaces, outputs))))
extra_outputs.append('$(%s)' % variable)
self.WriteLn()
- def WriteMacBundleResources(self, resources, bundle_deps, spec):
+ def WriteMacBundleResources(self, resources, bundle_deps):
"""Writes Makefile code for 'mac_bundle_resources'."""
self.WriteLn('### Generated for mac_bundle_resources')
- variable = self.target + '_mac_bundle_resources'
- path = generator_default_variables['PRODUCT_DIR']
- dest = os.path.join(path, self.xcode_settings.GetBundleResourceFolder())
- dest = QuoteSpaces(dest)
- for res in resources:
- output = dest
-
- assert ' ' not in res, (
- "Spaces in resource filenames not supported (%s)" % res)
-
- # Split into (path,file).
- path = Sourceify(self.Absolutify(res))
- path_parts = os.path.split(path)
-
- # Now split the path into (prefix,maybe.lproj).
- lproj_parts = os.path.split(path_parts[0])
- # If the resource lives in a .lproj bundle, add that to the destination.
- if lproj_parts[1].endswith('.lproj'):
- output = os.path.join(output, lproj_parts[1])
-
- output = Sourceify(self.Absolutify(os.path.join(output, path_parts[1])))
- # Compiled XIB files are referred to by .nib.
- if output.endswith('.xib'):
- output = output[0:-3] + 'nib'
-
- self.WriteDoCmd([output], [path], 'mac_tool,,,copy-bundle-resource',
+
+ for output, res in gyp.xcode_emulation.GetMacBundleResources(
+ generator_default_variables['PRODUCT_DIR'], self.xcode_settings,
+ map(Sourceify, map(self.Absolutify, resources))):
+ self.WriteDoCmd([output], [res], 'mac_tool,,,copy-bundle-resource',
part_of_all=True)
bundle_deps.append(output)
- def WriteMacInfoPlist(self, info_plist, bundle_deps, spec):
+ def WriteMacInfoPlist(self, bundle_deps):
"""Write Makefile code for bundle Info.plist files."""
- assert ' ' not in info_plist, (
- "Spaces in resource filenames not supported (%s)" % info_plist)
- info_plist = self.Absolutify(info_plist)
- settings = self.xcode_settings
-
- # If explicilty set to preprocess the plist, invoke the C preprocessor and
- # specify any defines as -D flags.
- if settings.GetPerTargetSetting('INFOPLIST_PREPROCESS', 'NO') == 'YES':
- # Create an intermediate file based on the path.
+ info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
+ generator_default_variables['PRODUCT_DIR'], self.xcode_settings,
+ self.Absolutify)
+ if not info_plist:
+ return
+ if defines:
+ # Create an intermediate file to store preprocessed results.
intermediate_plist = ('$(obj).$(TOOLSET)/$(TARGET)/' +
os.path.basename(info_plist))
- defines = shlex.split(settings.GetPerTargetSetting(
- 'INFOPLIST_PREPROCESSOR_DEFINITIONS', ''))
self.WriteList(defines, intermediate_plist + ': INFOPLIST_DEFINES', '-D',
quoter=EscapeCppDefine)
self.WriteMakeRule([intermediate_plist], [info_plist],
@@ -1749,16 +1101,11 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# preprocessor do not affect the XML parser in mac_tool.
'@plutil -convert xml1 $@ $@'])
info_plist = intermediate_plist
-
- path = generator_default_variables['PRODUCT_DIR']
- dest_plist = os.path.join(path, settings.GetBundlePlistPath())
- dest_plist = QuoteSpaces(dest_plist)
- extra_settings = 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',
+ # plists can contain envvars and substitute them into the file.
+ self.WriteXcodeEnv(out, self.GetXcodeEnv(additional_settings=extra_env))
+ self.WriteDoCmd([out], [info_plist], 'mac_tool,,,copy-info-plist',
part_of_all=True)
- bundle_deps.append(dest_plist)
+ bundle_deps.append(out)
def WriteSources(self, configs, deps, sources,
@@ -1837,7 +1184,12 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
'before any of us.',
order_only = True)
- precompiled_header.WriteObjDependencies(compilable, objs, self)
+ pchdeps = precompiled_header.GetObjDependencies(compilable, objs )
+ if pchdeps:
+ self.WriteLn('# Dependencies from obj files to their precompiled headers')
+ for source, obj, gch in pchdeps:
+ self.WriteLn('%s: %s' % (obj, gch))
+ self.WriteLn('# End precompiled header dependencies')
if objs:
extra_link_deps.append('$(OBJS)')
@@ -1848,32 +1200,32 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
self.WriteLn("$(OBJS): GYP_CFLAGS := "
"$(DEFS_$(BUILDTYPE)) "
"$(INCS_$(BUILDTYPE)) "
- "%s" % precompiled_header.GetInclude('c') +
+ "%s " % precompiled_header.GetInclude('c') +
"$(CFLAGS_$(BUILDTYPE)) "
"$(CFLAGS_C_$(BUILDTYPE))")
self.WriteLn("$(OBJS): GYP_CXXFLAGS := "
"$(DEFS_$(BUILDTYPE)) "
"$(INCS_$(BUILDTYPE)) "
- "%s" % precompiled_header.GetInclude('cc') +
+ "%s " % precompiled_header.GetInclude('cc') +
"$(CFLAGS_$(BUILDTYPE)) "
"$(CFLAGS_CC_$(BUILDTYPE))")
if self.flavor == 'mac':
self.WriteLn("$(OBJS): GYP_OBJCFLAGS := "
"$(DEFS_$(BUILDTYPE)) "
"$(INCS_$(BUILDTYPE)) "
- "%s" % precompiled_header.GetInclude('m') +
+ "%s " % precompiled_header.GetInclude('m') +
"$(CFLAGS_$(BUILDTYPE)) "
"$(CFLAGS_C_$(BUILDTYPE)) "
"$(CFLAGS_OBJC_$(BUILDTYPE))")
self.WriteLn("$(OBJS): GYP_OBJCXXFLAGS := "
"$(DEFS_$(BUILDTYPE)) "
"$(INCS_$(BUILDTYPE)) "
- "%s" % precompiled_header.GetInclude('mm') +
+ "%s " % precompiled_header.GetInclude('mm') +
"$(CFLAGS_$(BUILDTYPE)) "
"$(CFLAGS_CC_$(BUILDTYPE)) "
"$(CFLAGS_OBJCC_$(BUILDTYPE))")
- precompiled_header.WritePchTargets(self)
+ self.WritePchTargets(precompiled_header.GetGchBuildCommands())
# If there are any object files in our input file list, link them into our
# output.
@@ -1881,6 +1233,38 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
self.WriteLn()
+ def WritePchTargets(self, pch_commands):
+ """Writes make rules to compile prefix headers."""
+ if not pch_commands:
+ return
+
+ for gch, lang_flag, lang, input in pch_commands:
+ extra_flags = {
+ 'c': '$(CFLAGS_C_$(BUILDTYPE))',
+ 'cc': '$(CFLAGS_CC_$(BUILDTYPE))',
+ 'm': '$(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))',
+ 'mm': '$(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))',
+ }[lang]
+ var_name = {
+ 'c': 'GYP_PCH_CFLAGS',
+ 'cc': 'GYP_PCH_CXXFLAGS',
+ 'm': 'GYP_PCH_OBJCFLAGS',
+ 'mm': 'GYP_PCH_OBJCXXFLAGS',
+ }[lang]
+ self.WriteLn("%s: %s := %s " % (gch, var_name, lang_flag) +
+ "$(DEFS_$(BUILDTYPE)) "
+ "$(INCS_$(BUILDTYPE)) "
+ "$(CFLAGS_$(BUILDTYPE)) " +
+ extra_flags)
+
+ self.WriteLn('%s: %s FORCE_DO_CMD' % (gch, input))
+ self.WriteLn('\t@$(call do_cmd,pch_%s,1)' % lang)
+ self.WriteLn('')
+ assert ' ' not in gch, (
+ "Spaces in gch filenames not supported (%s)" % gch)
+ self.WriteLn('all_deps += %s' % gch)
+ self.WriteLn('')
+
def ComputeOutputBasename(self, spec):
"""Return the 'output basename' of a gyp spec.
@@ -2003,43 +1387,21 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
order_only = True,
multiple_output_trick = False)
- if self.flavor == 'mac':
- # Write an envvar for postbuilds.
- # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
- # TODO(thakis): It would be nice to have some general mechanism instead.
- # This variable may be referenced by TARGET_POSTBUILDS_$(BUILDTYPE),
- # so we must output its definition first, since we declare variables
- # using ":=".
- # TODO(thakis): Write this only for targets that actually have
- # postbuilds.
- strip_save_file = self.xcode_settings.GetPerTargetSetting(
- 'CHROMIUM_STRIP_SAVE_FILE')
- if strip_save_file:
- strip_save_file = self.Absolutify(strip_save_file)
- else:
- # Explicitly clear this out, else a postbuild might pick up an export
- # from an earlier target.
- strip_save_file = ''
- self.WriteXcodeEnv(
- self.output, spec,
- additional_settings={'CHROMIUM_STRIP_SAVE_FILE': strip_save_file})
-
- has_target_postbuilds = False
+ target_postbuilds = {}
if self.type != 'none':
for configname in sorted(configs.keys()):
config = configs[configname]
if self.flavor == 'mac':
- ldflags = self.xcode_settings.GetLdflags(self, configname)
+ ldflags = self.xcode_settings.GetLdflags(configname,
+ generator_default_variables['PRODUCT_DIR'], self.Absolutify)
# TARGET_POSTBUILDS_$(BUILDTYPE) is added to postbuilds later on.
- target_postbuilds = self.xcode_settings.GetTargetPostbuilds(
- configname, self.output, self.output_binary)
- if target_postbuilds:
- has_target_postbuilds = True
- self.WriteLn('%s: TARGET_POSTBUILDS_%s := %s' %
- (self.output,
- configname,
- gyp.common.EncodePOSIXShellList(target_postbuilds)))
+ target_postbuild = self.xcode_settings.GetTargetPostbuilds(
+ configname,
+ QuoteSpaces(self.output),
+ QuoteSpaces(self.output_binary))
+ if target_postbuild:
+ target_postbuilds[configname] = target_postbuild
else:
ldflags = config.get('ldflags', [])
# Compute an rpath for this output if needed.
@@ -2054,44 +1416,40 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
if libraries:
# Remove duplicate entries
libraries = gyp.common.uniquer(libraries)
- # On Mac, framework libraries need to be passed as '-framework Cocoa'.
if self.flavor == 'mac':
- libraries = [
- '-framework ' + os.path.splitext(os.path.basename(library))[0]
- if library.endswith('.framework') else library
- for library in libraries]
+ libraries = self.xcode_settings.AdjustLibraries(libraries)
self.WriteList(libraries, 'LIBS')
- self.WriteLn(
- '%s: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))' % self.output_binary)
- self.WriteLn('%s: LIBS := $(LIBS)' % self.output_binary)
+ self.WriteLn('%s: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))' %
+ QuoteSpaces(self.output_binary))
+ self.WriteLn('%s: LIBS := $(LIBS)' % QuoteSpaces(self.output_binary))
+ # Postbuild actions. Like actions, but implicitly depend on the target's
+ # output.
postbuilds = []
if self.flavor == 'mac':
- if has_target_postbuilds:
+ if target_postbuilds:
postbuilds.append('$(TARGET_POSTBUILDS_$(BUILDTYPE))')
- # Postbuild actions. Like actions, but implicitly depend on the target's
- # output.
- for postbuild in spec.get('postbuilds', []):
- postbuilds.append('echo POSTBUILD\\(%s\\) %s' % (
- self.target, postbuild['postbuild_name']))
- shell_list = postbuild['action']
- # The first element is the command. If it's a relative path, it's
- # a script in the source tree relative to the gyp file and needs to be
- # absolutified. Else, it's in the PATH (e.g. install_name_tool, ln).
- if os.path.sep in shell_list[0]:
- shell_list[0] = self.Absolutify(shell_list[0])
-
- # "script.sh" -> "./script.sh"
- if not os.path.sep in shell_list[0]:
- shell_list[0] = os.path.join('.', shell_list[0])
- postbuilds.append(gyp.common.EncodePOSIXShellList(shell_list))
+ postbuilds.extend(
+ gyp.xcode_emulation.GetSpecPostbuildCommands(spec, self.Absolutify))
if postbuilds:
+ # Envvars may be referenced by TARGET_POSTBUILDS_$(BUILDTYPE),
+ # so we must output its definition first, since we declare variables
+ # using ":=".
+ self.WriteXcodeEnv(self.output, self.GetXcodePostbuildEnv())
+
+ for configname in target_postbuilds:
+ self.WriteLn('%s: TARGET_POSTBUILDS_%s := %s' %
+ (QuoteSpaces(self.output),
+ configname,
+ gyp.common.EncodePOSIXShellList(target_postbuilds[configname])))
+
for i in xrange(len(postbuilds)):
if not postbuilds[i].startswith('$'):
postbuilds[i] = EscapeShellArgument(postbuilds[i])
- self.WriteLn('%s: builddir := $(abs_builddir)' % self.output)
- self.WriteLn('%s: POSTBUILDS := %s' % (self.output, ' '.join(postbuilds)))
+ self.WriteLn('%s: builddir := $(abs_builddir)' % QuoteSpaces(self.output))
+ self.WriteLn('%s: POSTBUILDS := %s' % (
+ QuoteSpaces(self.output), ' '.join(postbuilds)))
# A bundle directory depends on its dependencies such as bundle resources
# and bundle binary. When all dependencies have been built, the bundle
@@ -2103,8 +1461,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# Bundle dependencies. Note that the code below adds actions to this
# target, so if you move these two lines, move the lines below as well.
- self.WriteList(bundle_deps, 'BUNDLE_DEPS')
- self.WriteLn('%s: $(BUNDLE_DEPS)' % self.output)
+ self.WriteList(map(QuoteSpaces, bundle_deps), 'BUNDLE_DEPS')
+ self.WriteLn('%s: $(BUNDLE_DEPS)' % QuoteSpaces(self.output))
# After the framework is built, package it. Needs to happen before
# postbuilds, since postbuilds depend on this.
@@ -2126,7 +1484,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# its dependencies usually. To prevent this rule from executing
# on every build (expensive, especially with postbuilds), expliclity
# update the time on the framework directory.
- self.WriteLn('\t@touch -c %s' % self.output)
+ self.WriteLn('\t@touch -c %s' % QuoteSpaces(self.output))
if postbuilds:
assert not self.is_mac_bundle, ('Postbuilds for bundles should be done '
@@ -2135,8 +1493,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
'custom product_dir')
if self.type == 'executable':
- self.WriteLn(
- '%s: LD_INPUTS := %s' % (self.output_binary, ' '.join(link_deps)))
+ self.WriteLn('%s: LD_INPUTS := %s' % (
+ QuoteSpaces(self.output_binary),
+ ' '.join(map(QuoteSpaces, link_deps))))
if self.toolset == 'host' and self.flavor == 'android':
self.WriteDoCmd([self.output_binary], link_deps, 'link_host',
part_of_all, postbuilds=postbuilds)
@@ -2151,8 +1510,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
self.WriteDoCmd([self.output_binary], link_deps, 'alink', part_of_all,
postbuilds=postbuilds)
elif self.type == 'shared_library':
- self.WriteLn(
- '%s: LD_INPUTS := %s' % (self.output_binary, ' '.join(link_deps)))
+ self.WriteLn('%s: LD_INPUTS := %s' % (
+ QuoteSpaces(self.output_binary),
+ ' '.join(map(QuoteSpaces, link_deps))))
self.WriteDoCmd([self.output_binary], link_deps, 'solink', part_of_all,
postbuilds=postbuilds)
elif self.type == 'loadable_module':
@@ -2252,7 +1612,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
# all_deps is only used for deps file reading, and for deps files we replace
# spaces with ? because escaping doesn't work with make's $(sort) and
# other functions.
- outputs = [ReplaceQuotedSpaces(o) for o in outputs]
+ outputs = [QuoteSpaces(o, SPACE_REPLACEMENT) for o in outputs]
self.WriteLn('all_deps += %s' % ' '.join(outputs))
self._num_outputs += len(outputs)
@@ -2275,6 +1635,9 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
multiple_output_trick: if true (the default), perform tricks such as dummy
rules to avoid problems with multiple outputs.
"""
+ outputs = map(QuoteSpaces, outputs)
+ inputs = map(QuoteSpaces, inputs)
+
if comment:
self.WriteLn('# ' + comment)
if phony:
@@ -2405,154 +1768,37 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
self.fp.write(text + '\n')
- def GetXcodeEnv(self, spec, target_relative_path=False):
- """Return the environment variables that Xcode would set. See
- http://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW153
- for a full list."""
- if self.flavor != 'mac': return {}
-
- built_products_dir = generator_default_variables['PRODUCT_DIR']
- def StripProductDir(s):
- assert s.startswith(built_products_dir), s
- return s[len(built_products_dir) + 1:]
-
- product_name = spec.get('product_name', self.output)
-
- if self._InstallImmediately():
- if product_name.startswith(built_products_dir):
- product_name = StripProductDir(product_name)
-
- srcroot = self.path
- if target_relative_path:
- built_products_dir = os.path.relpath(built_products_dir, srcroot)
- srcroot = '.'
- # These are filled in on a as-needed basis.
- env = {
- 'BUILT_PRODUCTS_DIR' : built_products_dir,
- 'CONFIGURATION' : '$(BUILDTYPE)',
- 'PRODUCT_NAME' : product_name,
- # See /Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX\ Product\ Types.xcspec for FULL_PRODUCT_NAME
- 'FULL_PRODUCT_NAME' : product_name,
- 'SRCROOT' : srcroot,
- 'SOURCE_ROOT': '$(SRCROOT)',
- # This is not true for static libraries, but currently the env is only
- # written for bundles:
- 'TARGET_BUILD_DIR' : built_products_dir,
- 'TEMP_DIR' : '$(TMPDIR)',
- }
- if self.type in ('executable', 'shared_library', 'loadable_module'):
- env['EXECUTABLE_NAME'] = os.path.basename(self.output_binary)
- if self.type in (
- 'executable', 'static_library', 'shared_library', 'loadable_module'):
- env['EXECUTABLE_PATH'] = self.xcode_settings.GetExecutablePath()
- mach_o_type = self.xcode_settings.GetMachOType()
- if mach_o_type:
- env['MACH_O_TYPE'] = mach_o_type
- env['PRODUCT_TYPE'] = self.xcode_settings.GetProductType()
- 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()
- env['WRAPPER_NAME'] = self.xcode_settings.GetWrapperName()
-
- # TODO(thakis): Remove this.
- env['EXECUTABLE_PATH'] = QuoteSpaces(env['EXECUTABLE_PATH'])
- env['CONTENTS_FOLDER_PATH'] = QuoteSpaces(env['CONTENTS_FOLDER_PATH'])
- env['INFOPLIST_PATH'] = QuoteSpaces(env['INFOPLIST_PATH'])
- env['WRAPPER_NAME'] = QuoteSpaces(env['WRAPPER_NAME'])
-
- return env
-
-
- 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')
-
- # First sort the list of keys, removing any non-string values.
- # 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.
- key_list = env.keys()
- key_list.sort()
- key_list = [k for k in key_list if isinstance(env[k], str)]
-
- # Since environment variables can refer to other variables, the evaluation
- # order is important. Below is the logic to compute the dependency graph
- # and sort it.
- regex = re.compile(r'\$\(([a-zA-Z0-9\-_]+)\)')
-
- # Phase 1: Create a set of edges of (DEPENDEE, DEPENDER) where in the graph,
- # DEPENDEE -> DEPENDER. Also create sets of dependers and dependees.
- edges = set()
- dependees = set()
- dependers = set()
- for k in key_list:
- matches = regex.findall(env[k])
- if not len(matches):
- continue
-
- dependers.add(k)
- for dependee in matches:
- if dependee in env:
- edges.add((dependee, k))
- dependees.add(dependee)
-
- # Phase 2: Create a list of graph nodes with no incoming edges.
- sorted_nodes = []
- edgeless_nodes = dependees - dependers
-
- # Phase 3: Perform Kahn topological sort.
- while len(edgeless_nodes):
- # Find a node with no incoming edges, add it to the sorted list, and
- # remove it from the list of nodes that aren't part of the graph.
- node = edgeless_nodes.pop()
- sorted_nodes.append(node)
- key_list.remove(node)
-
- # Find all the edges between |node| and other nodes.
- edges_to_node = [e for e in edges if e[0] == node]
- for edge in edges_to_node:
- edges.remove(edge)
- # If the node connected to |node| by |edge| has no other incoming edges,
- # add it to |edgeless_nodes|.
- if not len([e for e in edges if e[1] == edge[1]]):
- edgeless_nodes.add(edge[1])
-
- # Any remaining edges indicate a cycle.
- if len(edges):
- raise Exception('Xcode environment variables are cyclically dependent: ' +
- str(edges))
-
- # Append the "nodes" not in the graph to those that were just sorted.
- sorted_nodes.extend(key_list)
-
- # Perform some transformations that are required to mimic Xcode behavior.
- for k in sorted_nodes:
+ def GetXcodeEnv(self, additional_settings=None):
+ return gyp.xcode_emulation.GetXcodeEnv(
+ self.xcode_settings, "$(abs_builddir)",
+ os.path.join("$(abs_srcdir)", self.path), "$(BUILDTYPE)",
+ additional_settings)
+
+
+ def GetXcodePostbuildEnv(self):
+ # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
+ # TODO(thakis): It would be nice to have some general mechanism instead.
+ strip_save_file = self.xcode_settings.GetPerTargetSetting(
+ 'CHROMIUM_STRIP_SAVE_FILE')
+ if strip_save_file:
+ strip_save_file = self.Absolutify(strip_save_file)
+ else:
+ # Explicitly clear this out, else a postbuild might pick up an export
+ # from an earlier target.
+ strip_save_file = ''
+ return self.GetXcodeEnv(
+ additional_settings={'CHROMIUM_STRIP_SAVE_FILE': strip_save_file})
+
+
+ def WriteXcodeEnv(self, target, env):
+ for k in gyp.xcode_emulation.TopologicallySortedEnvVarKeys(env):
# 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))
+ # So don't escape spaces in |env[k]|.
+ self.WriteLn('%s: export %s := %s' % (QuoteSpaces(target), k, env[k]))
def Objectify(self, path):
@@ -2565,6 +1811,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
def Pchify(self, path, lang):
"""Convert a prefix header path to its output directory form."""
+ path = self.Absolutify(path)
if '$(' in path:
path = path.replace('$(obj)/', '$(obj).%s/$(TARGET)/pch-%s' %
(self.toolset, lang))
@@ -2663,49 +1910,8 @@ def RunSystemTests(flavor):
cc_command=cc_host):
arflags_host = 'crsT'
- link_flags = ''
- if gyp.system_test.TestLinkerSupportsThreads(cc_command=cc_target):
- # N.B. we don't test for cross-compilation; as currently written, we
- # don't even use flock when linking in the cross-compile setup!
- # TODO(evan): refactor cross-compilation such that this code can
- # be reused.
- link_flags = '-Wl,--threads -Wl,--thread-count=4'
-
- # TODO(evan): cache this output. (But then we'll need to add extra
- # flags to gyp to flush the cache, yuk! It's fast enough for now to
- # just run it every time.)
-
return { 'ARFLAGS.target': arflags_target,
- 'ARFLAGS.host': arflags_host,
- 'LINK_flags': link_flags }
-
-
-def CopyTool(flavor, out_path):
- """Finds (mac|sun)_tool.gyp in the gyp directory and copies it
- to |out_path|."""
- prefix = { 'solaris': 'sun', 'mac': 'mac' }.get(flavor, None)
- if not prefix:
- return
-
- tool_path = os.path.join(out_path, 'gyp-%s-tool' % prefix)
- if os.path.exists(tool_path):
- os.remove(tool_path)
-
- # Slurp input file.
- source_path = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), '..', '%s_tool.py' % prefix)
- source_file = open(source_path)
- source = source_file.readlines()
- source_file.close()
-
- # Add header and write it out.
- tool_file = open(tool_path, 'w')
- tool_file.write(
- ''.join([source[0], '# Generated by gyp. Do not edit.\n'] + source[1:]))
- tool_file.close()
-
- # Make file executable.
- os.chmod(tool_path, 0755)
+ 'ARFLAGS.host': arflags_host }
def GenerateOutput(target_list, target_dicts, data, params):
@@ -2824,9 +2030,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
root_makefile.write('TOOLSET := %s\n' % toolset)
WriteRootHeaderSuffixRules(root_makefile)
- # Put mac_tool next to the root Makefile.
+ # Put build-time support tools next to the root Makefile.
dest_path = os.path.dirname(makefile_path)
- CopyTool(flavor, dest_path)
+ gyp.common.CopyTool(flavor, dest_path)
# Find the list of targets that derive from the gyp file(s) being built.
needed_targets = set()
@@ -2869,17 +2075,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
spec = target_dicts[qualified_target]
configs = spec['configurations']
- # The xcode generator special-cases global xcode_settings and does something
- # that amounts to merging in the global xcode_settings into each local
- # xcode_settings dict.
if flavor == 'mac':
- global_xcode_settings = data[build_file].get('xcode_settings', {})
- for configname in configs.keys():
- config = configs[configname]
- if 'xcode_settings' in config:
- new_settings = global_xcode_settings.copy()
- new_settings.update(config['xcode_settings'])
- config['xcode_settings'] = new_settings
+ gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
writer = MakefileWriter(generator_flags, flavor)
writer.Write(qualified_target, base_path, output_file, spec, configs,
diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py
index e9ead80d6a..a4c6efd1a4 100644
--- a/tools/gyp/pylib/gyp/generator/msvs.py
+++ b/tools/gyp/pylib/gyp/generator/msvs.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -162,7 +162,8 @@ def _FixPaths(paths):
return [_FixPath(i) for i in paths]
-def _ConvertSourcesToFilterHierarchy(sources, prefix=None, excluded=None):
+def _ConvertSourcesToFilterHierarchy(sources, prefix=None, excluded=None,
+ list_excluded=True):
"""Converts a list split source file paths into a vcproj folder hierarchy.
Arguments:
@@ -197,14 +198,15 @@ def _ConvertSourcesToFilterHierarchy(sources, prefix=None, excluded=None):
folders[s[0]] = []
folders[s[0]].append(s[1:])
# Add a folder for excluded files.
- if excluded_result:
+ if excluded_result and list_excluded:
excluded_folder = MSVSProject.Filter('_excluded_files',
contents=excluded_result)
result.append(excluded_folder)
# Populate all the folders.
for f in folders:
contents = _ConvertSourcesToFilterHierarchy(folders[f], prefix=prefix + [f],
- excluded=excluded)
+ excluded=excluded,
+ list_excluded=list_excluded)
contents = MSVSProject.Filter(f, contents=contents)
result.append(contents)
@@ -831,13 +833,14 @@ def _GetGuidOfProject(proj_path, spec):
return guid
-def _GenerateProject(project, options, version):
+def _GenerateProject(project, options, version, generator_flags):
"""Generates a vcproj file.
Arguments:
project: the MSVSProject object.
options: global generator options.
version: the MSVSVersion object.
+ generator_flags: dict of generator-specific flags.
"""
default_config = _GetDefaultConfiguration(project.spec)
@@ -846,18 +849,19 @@ def _GenerateProject(project, options, version):
return
if version.UsesVcxproj():
- _GenerateMSBuildProject(project, options, version)
+ _GenerateMSBuildProject(project, options, version, generator_flags)
else:
- _GenerateMSVSProject(project, options, version)
+ _GenerateMSVSProject(project, options, version, generator_flags)
-def _GenerateMSVSProject(project, options, version):
+def _GenerateMSVSProject(project, options, version, generator_flags):
"""Generates a .vcproj file. It may create .rules and .user files too.
Arguments:
project: The project object we will generate the file for.
options: Global options passed to the generator.
version: The VisualStudioVersion object.
+ generator_flags: dict of generator-specific flags.
"""
spec = project.spec
vcproj_dir = os.path.dirname(project.path)
@@ -886,9 +890,10 @@ def _GenerateMSVSProject(project, options, version):
_GenerateRulesForMSVS(p, project_dir, options, spec,
sources, excluded_sources,
actions_to_add)
+ list_excluded = generator_flags.get('msvs_list_excluded_files', True)
sources, excluded_sources, excluded_idl = (
_AdjustSourcesAndConvertToFilterHierarchy(
- spec, options, project_dir, sources, excluded_sources))
+ spec, options, project_dir, sources, excluded_sources, list_excluded))
# Add in files.
_VerifySourcesExist(sources, project_dir)
@@ -904,7 +909,8 @@ def _GenerateMSVSProject(project, options, version):
# Don't excluded sources with actions attached, or they won't run.
excluded_sources = _FilterActionsFromExcluded(
excluded_sources, actions_to_add)
- _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl)
+ _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl,
+ list_excluded)
_AddAccumulatedActionsToMSVS(p, spec, actions_to_add)
# Write it out.
@@ -963,7 +969,6 @@ def _GetMSVSConfigurationType(spec, build_file):
'loadable_module': '2', # .dll
'static_library': '4', # .lib
'none': '10', # Utility type
- 'dummy_executable': '1', # .exe
}[spec['type']]
except KeyError:
if spec.get('type'):
@@ -1120,7 +1125,6 @@ def _GetOutputFilePathAndTool(spec):
# 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\\', '.lib'),
- 'dummy_executable': ('VCLinkerTool', 'Link', '$(IntDir)\\', '.junk'),
}
output_file_props = output_file_map.get(spec['type'])
if output_file_props and int(spec.get('msvs_auto_output_file', 1)):
@@ -1287,7 +1291,7 @@ def _PrepareListOfSources(spec, gyp_file):
def _AdjustSourcesAndConvertToFilterHierarchy(
- spec, options, gyp_dir, sources, excluded_sources):
+ spec, options, gyp_dir, sources, excluded_sources, list_excluded):
"""Adjusts the list of sources and excluded sources.
Also converts the sets to lists.
@@ -1321,13 +1325,8 @@ def _AdjustSourcesAndConvertToFilterHierarchy(
# Convert to folders and the right slashes.
sources = [i.split('\\') for i in sources]
- sources = _ConvertSourcesToFilterHierarchy(sources, excluded=fully_excluded)
- # Add in dummy file for type none.
- if spec['type'] == 'dummy_executable':
- # Pull in a dummy main so it can link successfully.
- dummy_relpath = gyp.common.RelativePath(
- options.depth + '\\tools\\gyp\\gyp_dummy.c', gyp_dir)
- sources.append(dummy_relpath)
+ sources = _ConvertSourcesToFilterHierarchy(sources, excluded=fully_excluded,
+ list_excluded=list_excluded)
return sources, excluded_sources, excluded_idl
@@ -1358,12 +1357,19 @@ def _GetPrecompileRelatedFiles(spec):
return precompiled_related
-def _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl):
+def _ExcludeFilesFromBeingBuilt(p, spec, excluded_sources, excluded_idl,
+ list_excluded):
exclusions = _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl)
for file_name, excluded_configs in exclusions.iteritems():
- for config_name, config in excluded_configs:
- p.AddFileConfig(file_name, _ConfigFullName(config_name, config),
- {'ExcludedFromBuild': 'true'})
+ if (not list_excluded and
+ len(excluded_configs) == len(spec['configurations'])):
+ # If we're not listing excluded files, then they won't appear in the
+ # project, so don't try to configure them to be excluded.
+ pass
+ else:
+ for config_name, config in excluded_configs:
+ p.AddFileConfig(file_name, _ConfigFullName(config_name, config),
+ {'ExcludedFromBuild': 'true'})
def _GetExcludedFilesFromBuild(spec, excluded_sources, excluded_idl):
@@ -1751,6 +1757,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
# GeneratorCalculatedVariables.
msvs_version = params['msvs_version']
+ generator_flags = params.get('generator_flags', {})
+
# Optionally shard targets marked with 'msvs_shard': SHARD_COUNT.
(target_list, target_dicts) = _ShardTargets(target_list, target_dicts)
@@ -1769,7 +1777,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
# Generate each project.
for project in project_objects.values():
fixpath_prefix = project.fixpath_prefix
- _GenerateProject(project, options, msvs_version)
+ _GenerateProject(project, options, msvs_version, generator_flags)
fixpath_prefix = None
for build_file in data:
@@ -1873,7 +1881,10 @@ def _MapFileToMsBuildSourceType(source, extension_to_rule_name):
A pair of (group this file should be part of, the label of element)
"""
_, ext = os.path.splitext(source)
- if ext in ['.cc', '.cpp', '.c', '.cxx']:
+ if ext in extension_to_rule_name:
+ group = 'rule'
+ element = extension_to_rule_name[ext]
+ elif ext in ['.cc', '.cpp', '.c', '.cxx']:
group = 'compile'
element = 'ClCompile'
elif ext in ['.h', '.hxx']:
@@ -1885,9 +1896,6 @@ def _MapFileToMsBuildSourceType(source, extension_to_rule_name):
elif ext == '.idl':
group = 'midl'
element = 'Midl'
- elif ext in extension_to_rule_name:
- group = 'rule'
- element = extension_to_rule_name[ext]
else:
group = 'none'
element = 'None'
@@ -2663,14 +2671,14 @@ def _VerifySourcesExist(sources, root_dir):
def _GetMSBuildSources(spec, sources, exclusions, extension_to_rule_name,
- actions_spec, sources_handled_by_action):
+ actions_spec, sources_handled_by_action, list_excluded):
groups = ['none', 'midl', 'include', 'compile', 'resource', 'rule']
grouped_sources = {}
for g in groups:
grouped_sources[g] = []
_AddSources2(spec, sources, exclusions, grouped_sources,
- extension_to_rule_name, sources_handled_by_action)
+ extension_to_rule_name, sources_handled_by_action, list_excluded)
sources = []
for g in groups:
if grouped_sources[g]:
@@ -2681,12 +2689,14 @@ def _GetMSBuildSources(spec, sources, exclusions, extension_to_rule_name,
def _AddSources2(spec, sources, exclusions, grouped_sources,
- extension_to_rule_name, sources_handled_by_action):
+ extension_to_rule_name, sources_handled_by_action,
+ list_excluded):
extensions_excluded_from_precompile = []
for source in sources:
if isinstance(source, MSVSProject.Filter):
_AddSources2(spec, source.contents, exclusions, grouped_sources,
- extension_to_rule_name, sources_handled_by_action)
+ extension_to_rule_name, sources_handled_by_action,
+ list_excluded)
else:
if not source in sources_handled_by_action:
detail = []
@@ -2741,17 +2751,22 @@ def _GetMSBuildProjectReferences(project):
guid = dependency.guid
project_dir = os.path.split(project.path)[0]
relative_path = gyp.common.RelativePath(dependency.path, project_dir)
- group.append(
- ['ProjectReference',
- {'Include': relative_path},
- ['Project', guid],
- ['ReferenceOutputAssembly', 'false']
- ])
+ project_ref = ['ProjectReference',
+ {'Include': relative_path},
+ ['Project', guid],
+ ['ReferenceOutputAssembly', 'false']
+ ]
+ for config in dependency.spec.get('configurations', {}).itervalues():
+ # If it's disabled in any config, turn it off in the reference.
+ if config.get('msvs_2010_disable_uldi_when_referenced', 0):
+ project_ref.append(['UseLibraryDependencyInputs', 'false'])
+ break
+ group.append(project_ref)
references.append(group)
return references
-def _GenerateMSBuildProject(project, options, version):
+def _GenerateMSBuildProject(project, options, version, generator_flags):
spec = project.spec
configurations = spec['configurations']
project_dir, project_file_name = os.path.split(project.path)
@@ -2769,6 +2784,7 @@ def _GenerateMSBuildProject(project, options, version):
props_files_of_rules = set()
targets_files_of_rules = set()
extension_to_rule_name = {}
+ list_excluded = generator_flags.get('msvs_list_excluded_files', True)
_GenerateRulesForMSBuild(project_dir, options, spec,
sources, excluded_sources,
props_files_of_rules, targets_files_of_rules,
@@ -2776,7 +2792,8 @@ def _GenerateMSBuildProject(project, options, version):
sources, excluded_sources, excluded_idl = (
_AdjustSourcesAndConvertToFilterHierarchy(spec, options,
project_dir, sources,
- excluded_sources))
+ excluded_sources,
+ list_excluded))
_AddActions(actions_to_add, spec, project.build_file)
_AddCopies(actions_to_add, spec)
@@ -2826,7 +2843,7 @@ def _GenerateMSBuildProject(project, options, version):
content += _GetMSBuildToolSettingsSections(spec, configurations)
content += _GetMSBuildSources(
spec, sources, exclusions, extension_to_rule_name, actions_spec,
- sources_handled_by_action)
+ sources_handled_by_action, list_excluded)
content += _GetMSBuildProjectReferences(project)
content += import_cpp_targets_section
content += _GetMSBuildExtensionTargets(targets_files_of_rules)
diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py
index 193f295a55..8d6c6f52d1 100644
--- a/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/tools/gyp/pylib/gyp/generator/ninja.py
@@ -1,12 +1,14 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import copy
import gyp
import gyp.common
import gyp.system_test
+import gyp.xcode_emulation
import os.path
-import pprint
+import re
import subprocess
import sys
@@ -15,7 +17,7 @@ import gyp.ninja_syntax as ninja_syntax
generator_default_variables = {
'EXECUTABLE_PREFIX': '',
'EXECUTABLE_SUFFIX': '',
- 'STATIC_LIB_PREFIX': '',
+ 'STATIC_LIB_PREFIX': 'lib',
'STATIC_LIB_SUFFIX': '.a',
'SHARED_LIB_PREFIX': 'lib',
@@ -30,8 +32,6 @@ generator_default_variables = {
'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR',
'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen',
'PRODUCT_DIR': '$!PRODUCT_DIR',
- 'SHARED_LIB_DIR': '$!PRODUCT_DIR/lib',
- 'LIB_DIR': '',
# Special variables that may be used by gyp 'rule' targets.
# We generate definitions for these variables on the fly when processing a
@@ -57,6 +57,12 @@ def StripPrefix(arg, prefix):
def QuoteShellArgument(arg):
+ """Quote a string such that it will be interpreted as a single argument
+ by the shell."""
+ # Rather than attempting to enumerate the bad shell characters, just
+ # whitelist common OK ones and quote anything else.
+ if re.match(r'^[a-zA-Z0-9_=-]+$', arg):
+ return arg # No quoting necessary.
return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
@@ -71,8 +77,68 @@ def InvertRelativePath(path):
return path
# Only need to handle relative paths into subdirectories for now.
assert '..' not in path, path
- depth = len(path.split('/'))
- return '/'.join(['..'] * depth)
+ depth = len(path.split(os.path.sep))
+ return os.path.sep.join(['..'] * depth)
+
+
+class Target:
+ """Target represents the paths used within a single gyp target.
+
+ Conceptually, building a single target A is a series of steps:
+
+ 1) actions/rules/copies generates source/resources/etc.
+ 2) compiles generates .o files
+ 3) link generates a binary (library/executable)
+ 4) bundle merges the above in a mac bundle
+
+ (Any of these steps can be optional.)
+
+ From a build ordering perspective, a dependent target B could just
+ depend on the last output of this series of steps.
+
+ But some dependent commands sometimes need to reach inside the box.
+ For example, when linking B it needs to get the path to the static
+ library generated by A.
+
+ This object stores those paths. To keep things simple, member
+ variables only store concrete paths to single files, while methods
+ compute derived values like "the last output of the target".
+ """
+ def __init__(self, type):
+ # Gyp type ("static_library", etc.) of this target.
+ self.type = type
+ # File representing whether any input dependencies necessary for
+ # dependent actions have completed.
+ self.preaction_stamp = None
+ # File representing whether any input dependencies necessary for
+ # dependent compiles have completed.
+ self.precompile_stamp = None
+ # File representing the completion of actions/rules/copies, if any.
+ self.actions_stamp = None
+ # Path to the output of the link step, if any.
+ self.binary = None
+ # Path to the file representing the completion of building the bundle,
+ # if any.
+ self.bundle = None
+
+ def Linkable(self):
+ """Return true if this is a target that can be linked against."""
+ return self.type in ('static_library', 'shared_library')
+
+ def PreActionInput(self):
+ """Return the path, if any, that should be used as a dependency of
+ any dependent action step."""
+ return self.FinalOutput() or self.preaction_stamp
+
+ def PreCompileInput(self):
+ """Return the path, if any, that should be used as a dependency of
+ any dependent compile step."""
+ return self.actions_stamp or self.precompile_stamp
+
+ def FinalOutput(self):
+ """Return the last output of the target, which depends on all prior
+ steps."""
+ return self.bundle or self.binary or self.actions_stamp
# A small discourse on paths as used within the Ninja build:
@@ -100,11 +166,13 @@ def InvertRelativePath(path):
# to the input file name as well as the output target name.
class NinjaWriter:
- def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor):
+ def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor,
+ abs_build_dir=None):
"""
base_dir: path from source root to directory containing this gyp file,
by gyp semantics, all input paths are relative to this
build_dir: path from source root to build output
+ abs_build_dir: absolute path to the build directory
"""
self.target_outputs = target_outputs
@@ -112,6 +180,8 @@ class NinjaWriter:
self.build_dir = build_dir
self.ninja = ninja_syntax.Writer(output_file)
self.flavor = flavor
+ self.abs_build_dir = abs_build_dir
+ self.obj_ext = '.obj' if flavor == 'win' else '.o'
# Relative path from build output dir to base dir.
self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir)
@@ -132,6 +202,7 @@ class NinjaWriter:
path = path.replace(PRODUCT_DIR, product_dir)
else:
path = path.replace(PRODUCT_DIR + '/', '')
+ path = path.replace(PRODUCT_DIR + '\\', '')
path = path.replace(PRODUCT_DIR, '.')
INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR'
@@ -142,7 +213,7 @@ class NinjaWriter:
path = path.replace(INTERMEDIATE_DIR,
os.path.join(product_dir or '', int_dir))
- return path
+ return os.path.normpath(path)
def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root)
@@ -153,10 +224,13 @@ class NinjaWriter:
path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name)
return path
- def GypPathToNinja(self, path):
- """Translate a gyp path to a ninja path.
+ def GypPathToNinja(self, path, env=None):
+ """Translate a gyp path to a ninja path, optionally expanding environment
+ variable references in |path| with |env|.
See the above discourse on path conversions."""
+ if env:
+ path = gyp.xcode_emulation.ExpandEnvVars(path, env)
if path.startswith('$!'):
return self.ExpandSpecial(path)
assert '$' not in path, path
@@ -196,34 +270,38 @@ class NinjaWriter:
path_basename))
def WriteCollapsedDependencies(self, name, targets):
- """Given a list of targets, return a dependency list for a single
- file representing the result of building all the targets.
+ """Given a list of targets, return a path for a single file
+ representing the result of building all the targets or None.
Uses a stamp file if necessary."""
+ assert targets == filter(None, targets), targets
+ if len(targets) == 0:
+ return None
if len(targets) > 1:
stamp = self.GypPathToUniqueOutput(name + '.stamp')
targets = self.ninja.build(stamp, 'stamp', targets)
self.ninja.newline()
- return targets
+ return targets[0]
- def WriteSpec(self, spec, config):
+ def WriteSpec(self, spec, config_name):
"""The main entry point for NinjaWriter: write the build rules for a spec.
- Returns the path to the build output, or None, and a list of targets for
- dependencies of its compile steps."""
+ Returns a Target object, which represents the output paths for this spec.
+ Returns None if there are no outputs (e.g. a settings-only 'none' type
+ target)."""
+ self.config_name = config_name
self.name = spec['target_name']
self.toolset = spec['toolset']
+ config = spec['configurations'][config_name]
+ self.target = Target(spec['type'])
- if spec['type'] == 'settings':
- # TODO: 'settings' is not actually part of gyp; it was
- # accidentally introduced somehow into just the Linux build files.
- # Remove this (or make it an error) once all the users are fixed.
- print ("WARNING: %s uses invalid type 'settings'. " % self.name +
- "Please fix the source gyp file to use type 'none'.")
- print "See http://code.google.com/p/chromium/issues/detail?id=96629 ."
- spec['type'] = 'none'
+ self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
+ if self.flavor == 'mac':
+ self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
+ else:
+ self.xcode_settings = None
# Compute predepends for all rules.
# actions_depends is the dependencies this target depends on before running
@@ -232,66 +310,99 @@ class NinjaWriter:
# any of its compile steps.
actions_depends = []
compile_depends = []
+ # TODO(evan): it is rather confusing which things are lists and which
+ # are strings. Fix these.
if 'dependencies' in spec:
for dep in spec['dependencies']:
if dep in self.target_outputs:
- input, precompile_input, linkable = self.target_outputs[dep]
- actions_depends.append(input)
- compile_depends.extend(precompile_input)
+ target = self.target_outputs[dep]
+ actions_depends.append(target.PreActionInput())
+ compile_depends.append(target.PreCompileInput())
+ actions_depends = filter(None, actions_depends)
+ compile_depends = filter(None, compile_depends)
actions_depends = self.WriteCollapsedDependencies('actions_depends',
actions_depends)
+ compile_depends = self.WriteCollapsedDependencies('compile_depends',
+ compile_depends)
+ self.target.preaction_stamp = actions_depends
+ self.target.precompile_stamp = compile_depends
# Write out actions, rules, and copies. These must happen before we
# compile any sources, so compute a list of predependencies for sources
# while we do it.
extra_sources = []
- sources_depends = self.WriteActionsRulesCopies(spec, extra_sources,
- actions_depends)
+ mac_bundle_depends = []
+ self.target.actions_stamp = self.WriteActionsRulesCopies(
+ spec, extra_sources, actions_depends, mac_bundle_depends)
# If we have actions/rules/copies, we depend directly on those, but
# otherwise we depend on dependent target's actions/rules/copies etc.
# We never need to explicitly depend on previous target's link steps,
# because no compile ever depends on them.
- compile_depends = self.WriteCollapsedDependencies('compile_depends',
- sources_depends or compile_depends)
+ compile_depends_stamp = (self.target.actions_stamp or compile_depends)
# Write out the compilation steps, if any.
link_deps = []
sources = spec.get('sources', []) + extra_sources
if sources:
- link_deps = self.WriteSources(config, sources, compile_depends)
+ link_deps = self.WriteSources(
+ config_name, config, sources, compile_depends_stamp,
+ gyp.xcode_emulation.MacPrefixHeader(
+ self.xcode_settings, self.GypPathToNinja,
+ lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang)))
# Some actions/rules output 'sources' that are already object files.
- link_deps += [self.GypPathToNinja(f) for f in sources if f.endswith('.o')]
+ link_deps += [self.GypPathToNinja(f)
+ for f in sources if f.endswith(self.obj_ext)]
- # The final output of our target depends on the last output of the
- # above steps.
+ # Write out a link step, if needed.
output = None
- final_deps = link_deps or sources_depends or actions_depends
- if final_deps:
- output = self.WriteTarget(spec, config, final_deps,
- order_only=actions_depends)
- if self.name != output and self.toolset == 'target':
- # Write a short name to build this target. This benefits both the
- # "build chrome" case as well as the gyp tests, which expect to be
- # able to run actions and build libraries by their short name.
- self.ninja.build(self.name, 'phony', output)
- return output, compile_depends
-
- def WriteActionsRulesCopies(self, spec, extra_sources, prebuild):
- """Write out the Actions, Rules, and Copies steps. Return any outputs
- of these steps (or a stamp file if there are lots of outputs)."""
+ if link_deps or self.target.actions_stamp or actions_depends:
+ output = self.WriteTarget(spec, config_name, config, link_deps,
+ self.target.actions_stamp or actions_depends)
+ if self.is_mac_bundle:
+ mac_bundle_depends.append(output)
+
+ # Bundle all of the above together, if needed.
+ if self.is_mac_bundle:
+ output = self.WriteMacBundle(spec, mac_bundle_depends)
+
+ if not output:
+ return None
+
+ if self.name != output and self.toolset == 'target':
+ # Write a short name to build this target. This benefits both the
+ # "build chrome" case as well as the gyp tests, which expect to be
+ # able to run actions and build libraries by their short name.
+ self.ninja.build(self.name, 'phony', output)
+
+ assert self.target.FinalOutput(), output
+ return self.target
+
+ def WriteActionsRulesCopies(self, spec, extra_sources, prebuild,
+ mac_bundle_depends):
+ """Write out the Actions, Rules, and Copies steps. Return a path
+ representing the outputs of these steps."""
outputs = []
+ extra_mac_bundle_resources = []
if 'actions' in spec:
- outputs += self.WriteActions(spec['actions'], extra_sources, prebuild)
+ outputs += self.WriteActions(spec['actions'], extra_sources, prebuild,
+ extra_mac_bundle_resources)
if 'rules' in spec:
- outputs += self.WriteRules(spec['rules'], extra_sources, prebuild)
+ outputs += self.WriteRules(spec['rules'], extra_sources, prebuild,
+ extra_mac_bundle_resources)
if 'copies' in spec:
outputs += self.WriteCopies(spec['copies'], prebuild)
- outputs = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
+ stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
- return outputs
+ if self.is_mac_bundle:
+ mac_bundle_resources = spec.get('mac_bundle_resources', []) + \
+ extra_mac_bundle_resources
+ self.WriteMacBundleResources(mac_bundle_resources, mac_bundle_depends)
+ self.WriteMacInfoPlist(mac_bundle_depends)
+
+ return stamp
def GenerateDescription(self, verb, message, fallback):
"""Generate and return a description of a build step.
@@ -307,20 +418,26 @@ class NinjaWriter:
else:
return '%s %s: %s' % (verb, self.name, fallback)
- def WriteActions(self, actions, extra_sources, prebuild):
+ def WriteActions(self, actions, extra_sources, prebuild,
+ extra_mac_bundle_resources):
+ # Actions cd into the base directory.
+ env = self.GetXcodeEnv()
all_outputs = []
for action in actions:
# First write out a rule for the action.
- name = action['action_name']
+ name = re.sub(r'[ {}$]', '_', action['action_name'])
description = self.GenerateDescription('ACTION',
action.get('message', None),
name)
- rule_name = self.WriteNewNinjaRule(name, action['action'], description)
+ rule_name = self.WriteNewNinjaRule(name, action['action'], description,
+ env=env)
- inputs = [self.GypPathToNinja(i) for i in action['inputs']]
+ inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
if int(action.get('process_outputs_as_sources', False)):
extra_sources += action['outputs']
- outputs = [self.GypPathToNinja(o) for o in action['outputs']]
+ if int(action.get('process_outputs_as_mac_bundle_resources', False)):
+ extra_mac_bundle_resources += action['outputs']
+ outputs = [self.GypPathToNinja(o, env) for o in action['outputs']]
# Then write out an edge using the rule.
self.ninja.build(outputs, rule_name, inputs,
@@ -331,7 +448,8 @@ class NinjaWriter:
return all_outputs
- def WriteRules(self, rules, extra_sources, prebuild):
+ def WriteRules(self, rules, extra_sources, prebuild,
+ extra_mac_bundle_resources):
all_outputs = []
for rule in rules:
# First write out a rule for the rule action.
@@ -369,6 +487,8 @@ class NinjaWriter:
if int(rule.get('process_outputs_as_sources', False)):
extra_sources += outputs
+ if int(rule.get('process_outputs_as_mac_bundle_resources', False)):
+ extra_mac_bundle_resources += outputs
extra_bindings = []
for var in needed_variables:
@@ -402,31 +522,67 @@ class NinjaWriter:
def WriteCopies(self, copies, prebuild):
outputs = []
+ env = self.GetXcodeEnv()
for copy in copies:
for path in copy['files']:
# Normalize the path so trailing slashes don't confuse us.
path = os.path.normpath(path)
basename = os.path.split(path)[1]
- src = self.GypPathToNinja(path)
- dst = self.GypPathToNinja(os.path.join(copy['destination'], basename))
- outputs += self.ninja.build(dst, 'copy', src,
- order_only=prebuild)
+ src = self.GypPathToNinja(path, env)
+ dst = self.GypPathToNinja(os.path.join(copy['destination'], basename),
+ env)
+ outputs += self.ninja.build(dst, 'copy', src, order_only=prebuild)
return outputs
- def WriteSources(self, config, sources, predepends):
+ def WriteMacBundleResources(self, resources, bundle_depends):
+ """Writes ninja edges for 'mac_bundle_resources'."""
+ for output, res in gyp.xcode_emulation.GetMacBundleResources(
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
+ self.xcode_settings, map(self.GypPathToNinja, resources)):
+ self.ninja.build(output, 'mac_tool', res,
+ variables=[('mactool_cmd', 'copy-bundle-resource')])
+ bundle_depends.append(output)
+
+ def WriteMacInfoPlist(self, bundle_depends):
+ """Write build rules for bundle Info.plist files."""
+ info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
+ self.xcode_settings, self.GypPathToNinja)
+ if not info_plist:
+ return
+ if defines:
+ # Create an intermediate file to store preprocessed results.
+ intermediate_plist = self.GypPathToUniqueOutput(
+ os.path.basename(info_plist))
+ defines = ' '.join(
+ [QuoteShellArgument(ninja_syntax.escape('-D' + d)) for d in defines])
+ info_plist = self.ninja.build(intermediate_plist, 'infoplist', info_plist,
+ variables=[('defines',defines)])
+
+ env = self.GetXcodeEnv(additional_settings=extra_env)
+ env = self.ComputeExportEnvString(env)
+
+ self.ninja.build(out, 'mac_tool', info_plist,
+ variables=[('mactool_cmd', 'copy-info-plist'),
+ ('env', env)])
+ bundle_depends.append(out)
+
+ def WriteSources(self, config_name, config, sources, predepends,
+ precompiled_header):
"""Write build rules to compile all of |sources|."""
if self.toolset == 'host':
self.ninja.variable('cc', '$cc_host')
self.ninja.variable('cxx', '$cxx_host')
if self.flavor == 'mac':
- # TODO(jeremya/thakis): Extract these from XcodeSettings instead.
- cflags = []
- cflags_c = []
- cflags_cc = []
- cflags_objc = []
- cflags_objcc = []
+ cflags = self.xcode_settings.GetCflags(config_name)
+ cflags_c = self.xcode_settings.GetCflagsC(config_name)
+ cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
+ cflags_objc = ['$cflags_c'] + \
+ self.xcode_settings.GetCflagsObjC(config_name)
+ cflags_objcc = ['$cflags_cc'] + \
+ self.xcode_settings.GetCflagsObjCC(config_name)
else:
cflags = config.get('cflags', [])
cflags_c = config.get('cflags_c', [])
@@ -438,14 +594,26 @@ class NinjaWriter:
self.WriteVariableList('includes',
['-I' + self.GypPathToNinja(i)
for i in config.get('include_dirs', [])])
+
+ pch_commands = precompiled_header.GetGchBuildCommands()
+ if self.flavor == 'mac':
+ self.WriteVariableList('cflags_pch_c',
+ [precompiled_header.GetInclude('c')])
+ self.WriteVariableList('cflags_pch_cc',
+ [precompiled_header.GetInclude('cc')])
+ self.WriteVariableList('cflags_pch_objc',
+ [precompiled_header.GetInclude('m')])
+ self.WriteVariableList('cflags_pch_objcc',
+ [precompiled_header.GetInclude('mm')])
+
self.WriteVariableList('cflags', map(self.ExpandSpecial, cflags))
self.WriteVariableList('cflags_c', map(self.ExpandSpecial, cflags_c))
self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc))
if self.flavor == 'mac':
self.WriteVariableList('cflags_objc', map(self.ExpandSpecial,
- cflags_objc))
+ cflags_objc))
self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial,
- cflags_objcc))
+ cflags_objcc))
self.ninja.newline()
outputs = []
for source in sources:
@@ -463,106 +631,232 @@ class NinjaWriter:
# TODO: should we assert here on unexpected extensions?
continue
input = self.GypPathToNinja(source)
- output = self.GypPathToUniqueOutput(filename + '.o')
+ output = self.GypPathToUniqueOutput(filename + self.obj_ext)
+ implicit = precompiled_header.GetObjDependencies([input], [output])
self.ninja.build(output, command, input,
+ implicit=[gch for _, _, gch in implicit],
order_only=predepends)
outputs.append(output)
+
+ self.WritePchTargets(pch_commands)
+
self.ninja.newline()
return outputs
- def WriteTarget(self, spec, config, final_deps, order_only):
- if spec['type'] == 'none':
- # This target doesn't have any explicit final output, but is instead
- # used for its effects before the final output (e.g. copies steps).
- # Reuse the existing output if it's easy.
- if len(final_deps) == 1:
- return final_deps[0]
- # Otherwise, fall through to writing out a stamp file.
+ def WritePchTargets(self, pch_commands):
+ """Writes ninja rules to compile prefix headers."""
+ if not pch_commands:
+ return
+
+ for gch, lang_flag, lang, input in pch_commands:
+ var_name = {
+ 'c': 'cflags_pch_c',
+ 'cc': 'cflags_pch_cc',
+ 'm': 'cflags_pch_objc',
+ 'mm': 'cflags_pch_objcc',
+ }[lang]
+
+ cmd = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }.get(lang)
+ self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)])
- output = self.ComputeOutput(spec)
- output_uses_linker = spec['type'] in ('executable', 'loadable_module',
- 'shared_library')
+ def WriteLink(self, spec, config_name, config, link_deps):
+ """Write out a link step. Returns the path to the output."""
+
+ command = {
+ 'executable': 'link',
+ 'loadable_module': 'solink_module',
+ 'shared_library': 'solink',
+ }[spec['type']]
implicit_deps = set()
+
if 'dependencies' in spec:
# Two kinds of dependencies:
# - Linkable dependencies (like a .a or a .so): add them to the link line.
# - Non-linkable dependencies (like a rule that generates a file
# and writes a stamp file): add them to implicit_deps
- if output_uses_linker:
- extra_deps = set()
- for dep in spec['dependencies']:
- input, _, linkable = self.target_outputs.get(dep, (None, [], False))
- if not input:
- continue
- if linkable:
- extra_deps.add(input)
- else:
- # TODO: Chrome-specific HACK. Chrome runs this lastchange rule on
- # every build, but we don't want to rebuild when it runs.
- if 'lastchange' not in input:
- implicit_deps.add(input)
- final_deps.extend(list(extra_deps))
- command_map = {
- 'executable': 'link',
- 'static_library': 'alink',
- 'loadable_module': 'solink_module',
- 'shared_library': 'solink',
- 'none': 'stamp',
- }
- command = command_map[spec['type']]
-
- if output_uses_linker:
- if self.flavor == 'mac':
- # TODO(jeremya/thakis): Get this from XcodeSettings.
- ldflags = []
- else:
- ldflags = config.get('ldflags', [])
- self.WriteVariableList('ldflags',
- gyp.common.uniquer(map(self.ExpandSpecial,
- ldflags)))
- self.WriteVariableList('libs',
- gyp.common.uniquer(map(self.ExpandSpecial,
- spec.get('libraries', []))))
+ extra_link_deps = set()
+ for dep in spec['dependencies']:
+ target = self.target_outputs.get(dep)
+ if not target:
+ continue
+ linkable = target.Linkable()
+ if linkable:
+ extra_link_deps.add(target.binary)
+
+ final_output = target.FinalOutput()
+ if not linkable or final_output != target.binary:
+ implicit_deps.add(final_output)
+
+ link_deps.extend(list(extra_link_deps))
extra_bindings = []
+ if self.is_mac_bundle:
+ output = self.ComputeMacBundleBinaryOutput()
+ else:
+ output = self.ComputeOutput(spec)
+ extra_bindings.append(('postbuilds',
+ self.GetPostbuildCommand(spec, output, output)))
+
+ if self.flavor == 'mac':
+ ldflags = self.xcode_settings.GetLdflags(config_name,
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
+ self.GypPathToNinja)
+ else:
+ ldflags = config.get('ldflags', [])
+ self.WriteVariableList('ldflags',
+ gyp.common.uniquer(map(self.ExpandSpecial,
+ ldflags)))
+
+ libraries = gyp.common.uniquer(map(self.ExpandSpecial,
+ spec.get('libraries', [])))
+ if self.flavor == 'mac':
+ libraries = self.xcode_settings.AdjustLibraries(libraries)
+ self.WriteVariableList('libs', libraries)
+
if command in ('solink', 'solink_module'):
extra_bindings.append(('soname', os.path.split(output)[1]))
- self.ninja.build(output, command, final_deps,
+ self.ninja.build(output, command, link_deps,
implicit=list(implicit_deps),
- order_only=order_only,
variables=extra_bindings)
+ return output
+ def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
+ if spec['type'] == 'none':
+ # TODO(evan): don't call this function for 'none' target types, as
+ # it doesn't do anything, and we fake out a 'binary' with a stamp file.
+ self.target.binary = compile_deps
+ elif spec['type'] == 'static_library':
+ self.target.binary = self.ComputeOutput(spec)
+ self.ninja.build(self.target.binary, 'alink', link_deps,
+ order_only=compile_deps,
+ variables=[('postbuilds', self.GetPostbuildCommand(
+ spec, self.target.binary, self.target.binary))])
+ else:
+ self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
+ return self.target.binary
+
+ def WriteMacBundle(self, spec, mac_bundle_depends):
+ assert self.is_mac_bundle
+ package_framework = spec['type'] in ('shared_library', 'loadable_module')
+ output = self.ComputeMacBundleOutput()
+ postbuild = self.GetPostbuildCommand(spec, output, self.target.binary,
+ is_command_start=not package_framework)
+ variables = []
+ if postbuild:
+ variables.append(('postbuilds', postbuild))
+ if package_framework:
+ variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
+ self.ninja.build(output, 'package_framework', mac_bundle_depends,
+ variables=variables)
+ else:
+ self.ninja.build(output, 'stamp', mac_bundle_depends,
+ variables=variables)
+ self.target.bundle = output
return output
- def ComputeOutputFileName(self, spec):
+ def GetXcodeEnv(self, additional_settings=None):
+ """Returns the variables Xcode would set for build steps."""
+ assert self.abs_build_dir
+ abs_build_dir = self.abs_build_dir
+ return gyp.xcode_emulation.GetXcodeEnv(
+ self.xcode_settings, abs_build_dir,
+ os.path.join(abs_build_dir, self.build_to_base), self.config_name,
+ additional_settings)
+
+ def GetXcodePostbuildEnv(self):
+ """Returns the variables Xcode would set for postbuild steps."""
+ postbuild_settings = {}
+ # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
+ # TODO(thakis): It would be nice to have some general mechanism instead.
+ strip_save_file = self.xcode_settings.GetPerTargetSetting(
+ 'CHROMIUM_STRIP_SAVE_FILE')
+ if strip_save_file:
+ postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = self.GypPathToNinja(
+ strip_save_file)
+ return self.GetXcodeEnv(additional_settings=postbuild_settings)
+
+ def GetPostbuildCommand(self, spec, output, output_binary,
+ is_command_start=False):
+ """Returns a shell command that runs all the postbuilds, and removes
+ |output| if any of them fails. If |is_command_start| is False, then the
+ returned string will start with ' && '."""
+ if not self.xcode_settings or spec['type'] == 'none' or not output:
+ return ''
+ output = QuoteShellArgument(output)
+ target_postbuilds = self.xcode_settings.GetTargetPostbuilds(
+ self.config_name, output, QuoteShellArgument(output_binary), quiet=True)
+ postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(
+ spec, self.GypPathToNinja, quiet=True)
+ postbuilds = target_postbuilds + postbuilds
+ if not postbuilds:
+ return ''
+ env = self.ComputeExportEnvString(self.GetXcodePostbuildEnv())
+ commands = env + ' F=0; ' + \
+ ' '.join([ninja_syntax.escape(command) + ' || F=$$?;'
+ for command in postbuilds])
+ command_string = env + commands + ' ((exit $$F) || rm -rf %s) ' % output + \
+ '&& exit $$F)'
+ if is_command_start:
+ return '(' + command_string + ' && '
+ else:
+ return '$ && (' + command_string
+
+ def ComputeExportEnvString(self, env):
+ """Given an environment, returns a string looking like
+ 'export FOO=foo; export BAR="${FOO} bar;'
+ that exports |env| to the shell."""
+ export_str = []
+ for k in gyp.xcode_emulation.TopologicallySortedEnvVarKeys(env):
+ export_str.append('export %s=%s;' %
+ (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(env[k]))))
+ return ' '.join(export_str)
+
+ def ComputeMacBundleOutput(self):
+ """Return the 'output' (full output path) to a bundle output directory."""
+ assert self.is_mac_bundle
+ path = self.ExpandSpecial(generator_default_variables['PRODUCT_DIR'])
+ return os.path.join(path, self.xcode_settings.GetWrapperName())
+
+ def ComputeMacBundleBinaryOutput(self):
+ """Return the 'output' (full output path) to the binary in a bundle."""
+ assert self.is_mac_bundle
+ path = self.ExpandSpecial(generator_default_variables['PRODUCT_DIR'])
+ return os.path.join(path, self.xcode_settings.GetExecutablePath())
+
+ def ComputeOutputFileName(self, spec, type=None):
"""Compute the filename of the final output for the current target."""
+ if not type:
+ type = spec['type']
+
+ default_variables = copy.copy(generator_default_variables)
+ CalculateVariables(default_variables, {'flavor': self.flavor})
# Compute filename prefix: the product prefix, or a default for
# the product type.
DEFAULT_PREFIX = {
- 'loadable_module': 'lib',
- 'shared_library': 'lib',
+ 'loadable_module': default_variables['SHARED_LIB_PREFIX'],
+ 'shared_library': default_variables['SHARED_LIB_PREFIX'],
+ 'static_library': default_variables['STATIC_LIB_PREFIX'],
+ 'executable': default_variables['EXECUTABLE_PREFIX'],
}
- prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(spec['type'], ''))
+ prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
# Compute filename extension: the product extension, or a default
# for the product type.
DEFAULT_EXTENSION = {
- 'static_library': 'a',
- 'loadable_module': 'so',
- 'shared_library': 'so',
+ 'loadable_module': default_variables['SHARED_LIB_SUFFIX'],
+ 'shared_library': default_variables['SHARED_LIB_SUFFIX'],
+ 'static_library': default_variables['STATIC_LIB_SUFFIX'],
+ 'executable': default_variables['EXECUTABLE_SUFFIX'],
}
- # TODO(thakis/jeremya): Remove once the mac path name computation is done
- # by XcodeSettings.
- if self.flavor == 'mac':
- DEFAULT_EXTENSION['shared_library'] = 'dylib'
- extension = spec.get('product_extension',
- DEFAULT_EXTENSION.get(spec['type'], ''))
+ extension = spec.get('product_extension')
if extension:
extension = '.' + extension
+ else:
+ extension = DEFAULT_EXTENSION.get(type, '')
if 'product_name' in spec:
# If we were given an explicit name, use that.
@@ -574,37 +868,44 @@ class NinjaWriter:
# Snip out an extra 'lib' from libs if appropriate.
target = StripPrefix(target, 'lib')
- if spec['type'] in ('static_library', 'loadable_module', 'shared_library',
+ if type in ('static_library', 'loadable_module', 'shared_library',
'executable'):
return '%s%s%s' % (prefix, target, extension)
- elif spec['type'] == 'none':
+ elif type == 'none':
return '%s.stamp' % target
else:
- raise 'Unhandled output type', spec['type']
+ raise 'Unhandled output type', type
- def ComputeOutput(self, spec):
+ def ComputeOutput(self, spec, type=None):
"""Compute the path for the final output of the spec."""
+ assert not self.is_mac_bundle or type
- filename = self.ComputeOutputFileName(spec)
+ if not type:
+ type = spec['type']
+
+ if self.flavor == 'mac' and type in (
+ 'static_library', 'executable', 'shared_library', 'loadable_module'):
+ filename = self.xcode_settings.GetExecutablePath()
+ else:
+ filename = self.ComputeOutputFileName(spec, type)
if 'product_dir' in spec:
path = os.path.join(spec['product_dir'], filename)
return self.ExpandSpecial(path)
- # Executables and loadable modules go into the output root,
- # libraries go into shared library dir, and everything else
- # goes into the normal place.
- if spec['type'] in ('executable', 'loadable_module'):
+ # Some products go into the output root, libraries go into shared library
+ # dir, and everything else goes into the normal place.
+ type_in_output_root = ['executable', 'loadable_module']
+ if self.flavor == 'mac' and self.toolset == 'target':
+ type_in_output_root += ['shared_library', 'static_library']
+
+ if type in type_in_output_root:
return filename
- elif spec['type'] == 'shared_library':
+ elif type == 'shared_library':
libdir = 'lib'
if self.toolset != 'target':
- libdir = 'lib/%s' % self.toolset
+ libdir = os.path.join('lib', '%s' % self.toolset)
return os.path.join(libdir, filename)
- # TODO(thakis/jeremya): Remove once the mac path name computation is done
- # by XcodeSettings.
- elif spec['type'] == 'static_library' and self.flavor == 'mac':
- return filename
else:
return self.GypPathToUniqueOutput(filename, qualified=False)
@@ -613,7 +914,7 @@ class NinjaWriter:
values = []
self.ninja.variable(var, ' '.join(values))
- def WriteNewNinjaRule(self, name, args, description):
+ def WriteNewNinjaRule(self, name, args, description, env={}):
"""Write out a new ninja "rule" statement for a given command.
Returns the name of the new rule."""
@@ -632,11 +933,29 @@ class NinjaWriter:
# gyp dictates that commands are run from the base directory.
# cd into the directory before running, and adjust paths in
# the arguments to point to the proper locations.
- cd = 'cd %s; ' % self.build_to_base
+ if self.flavor == 'win':
+ cd = 'cmd /s /c "cd %s && ' % self.build_to_base
+ else:
+ cd = 'cd %s; ' % self.build_to_base
args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
-
- command = cd + gyp.common.EncodePOSIXShellList(args)
- self.ninja.rule(rule_name, command, description)
+ env = self.ComputeExportEnvString(env)
+ if self.flavor == 'win':
+ # TODO(scottmg): Really don't want encourage cygwin, but I'm not sure
+ # how much sh is depended upon. For now, double quote args to make most
+ # things work.
+ command = args[0] + ' "' + '" "'.join(args[1:]) + '""'
+ else:
+ command = gyp.common.EncodePOSIXShellList(args)
+ if env:
+ # If an environment is passed in, variables in the command should be
+ # read from it, instead of from ninja's internal variables.
+ command = ninja_syntax.escape(command)
+
+ command = cd + env + command
+ # GYP rules/actions express being no-ops by not touching their outputs.
+ # Avoid executing downstream dependencies in this case by specifying
+ # restat=1 to ninja.
+ self.ninja.rule(rule_name, command, description, restat=True)
self.ninja.newline()
return rule_name
@@ -645,15 +964,14 @@ class NinjaWriter:
def CalculateVariables(default_variables, params):
"""Calculate additional variables for use in the build (called by gyp)."""
cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc'))
- default_variables['LINKER_SUPPORTS_ICF'] = \
- gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target)
-
flavor = gyp.common.GetFlavor(params)
if flavor == 'mac':
default_variables.setdefault('OS', 'mac')
default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
-
- # TODO(jeremya/thakis): Set SHARED_LIB_DIR / LIB_DIR.
+ default_variables.setdefault('SHARED_LIB_DIR',
+ generator_default_variables['PRODUCT_DIR'])
+ default_variables.setdefault('LIB_DIR',
+ generator_default_variables['PRODUCT_DIR'])
# Copy additional generator configuration data from Xcode, which is shared
# by the Mac Ninja generator.
@@ -667,12 +985,22 @@ def CalculateVariables(default_variables, params):
global generator_extra_sources_for_rules
generator_extra_sources_for_rules = getattr(xcode_generator,
'generator_extra_sources_for_rules', [])
+ elif flavor == 'win':
+ default_variables['OS'] = 'win'
+ default_variables['EXECUTABLE_SUFFIX'] = '.exe'
+ default_variables['STATIC_LIB_PREFIX'] = ''
+ default_variables['STATIC_LIB_SUFFIX'] = '.lib'
+ default_variables['SHARED_LIB_PREFIX'] = ''
+ default_variables['SHARED_LIB_SUFFIX'] = '.dll'
else:
operating_system = flavor
if flavor == 'android':
operating_system = 'linux' # Keep this legacy behavior for now.
default_variables.setdefault('OS', operating_system)
default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
+ default_variables.setdefault('SHARED_LIB_DIR',
+ os.path.join('$!PRODUCT_DIR', 'lib'))
+ default_variables.setdefault('LIB_DIR', '')
def OpenOutput(path):
@@ -684,55 +1012,85 @@ def OpenOutput(path):
return open(path, 'w')
-def GenerateOutput(target_list, target_dicts, data, params):
+def GenerateOutputForConfig(target_list, target_dicts, data, params,
+ config_name):
options = params['options']
flavor = gyp.common.GetFlavor(params)
generator_flags = params.get('generator_flags', {})
- if options.generator_output:
- raise NotImplementedError, "--generator_output not implemented for ninja"
-
- config_name = generator_flags.get('config', None)
- if config_name is None:
- # Guess which config we want to use: pick the first one from the
- # first target.
- config_name = target_dicts[target_list[0]]['default_configuration']
-
- # builddir: relative path from source root to our output files.
+ # build_dir: relative path from source root to our output files.
# e.g. "out/Debug"
- builddir = os.path.join(generator_flags.get('output_dir', 'out'), config_name)
+ build_dir = os.path.join(generator_flags.get('output_dir', 'out'),
+ config_name)
master_ninja = ninja_syntax.Writer(
- OpenOutput(os.path.join(options.toplevel_dir, builddir, 'build.ninja')),
+ OpenOutput(os.path.join(options.toplevel_dir, build_dir, 'build.ninja')),
width=120)
- # TODO: compute cc/cxx/ld/etc. by command-line arguments and system tests.
- master_ninja.variable('cc', os.environ.get('CC', 'gcc'))
- master_ninja.variable('cxx', os.environ.get('CXX', 'g++'))
- # TODO(bradnelson): remove NOGOLD when this is resolved:
- # http://code.google.com/p/chromium/issues/detail?id=108251
- if flavor != 'mac' and not os.environ.get('NOGOLD'):
- master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4')
+ # Put build-time support tools in out/{config_name}.
+ gyp.common.CopyTool(flavor, os.path.join(options.toplevel_dir, build_dir))
+
+ # Grab make settings for CC/CXX.
+ if flavor == 'win':
+ cc = cxx = 'cl'
else:
- # TODO(jeremya/thakis): flock
- master_ninja.variable('ld', '$cxx')
+ cc, cxx = 'gcc', 'g++'
+ build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
+ make_global_settings = data[build_file].get('make_global_settings', [])
+ build_to_root = InvertRelativePath(build_dir)
+ for key, value in make_global_settings:
+ if key == 'CC': cc = os.path.join(build_to_root, value)
+ if key == 'CXX': cxx = os.path.join(build_to_root, value)
+
+ flock = 'flock'
+ if flavor == 'mac':
+ flock = './gyp-mac-tool flock'
+ master_ninja.variable('cc', os.environ.get('CC', cc))
+ master_ninja.variable('cxx', os.environ.get('CXX', cxx))
+ if flavor == 'win':
+ master_ninja.variable('ld', 'link')
+ else:
+ master_ninja.variable('ld', flock + ' linker.lock $cxx')
master_ninja.variable('cc_host', '$cc')
master_ninja.variable('cxx_host', '$cxx')
+ if flavor == 'mac':
+ master_ninja.variable('mac_tool', os.path.join('.', 'gyp-mac-tool'))
master_ninja.newline()
- master_ninja.rule(
- 'cc',
- description='CC $out',
- command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
- '-c $in -o $out'),
- depfile='$out.d')
- master_ninja.rule(
- 'cxx',
- description='CXX $out',
- command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
- '-c $in -o $out'),
- depfile='$out.d')
- if flavor != 'mac':
+ if flavor != 'win':
+ master_ninja.rule(
+ 'cc',
+ description='CC $out',
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
+ '$cflags_pch_c -c $in -o $out'),
+ depfile='$out.d')
+ master_ninja.rule(
+ 'cxx',
+ description='CXX $out',
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
+ '$cflags_pch_cc -c $in -o $out'),
+ depfile='$out.d')
+ else:
+ # TODO(scottmg): Requires deplist branch of ninja for now (for
+ # /showIncludes handling).
+ master_ninja.rule(
+ 'cc',
+ description='CC $out',
+ command=('cmd /c $cc /nologo /showIncludes '
+ '$defines $includes $cflags $cflags_c '
+ '$cflags_pch_c /c $in /Fo$out '
+ '| ninja-deplist-helper -f cl -o $out.dl'),
+ deplist='$out.dl')
+ master_ninja.rule(
+ 'cxx',
+ description='CXX $out',
+ command=('cmd /c $cxx /nologo /showIncludes '
+ '$defines $includes $cflags $cflags_cc '
+ '$cflags_pch_cc /c $in /Fo$out '
+ '| ninja-deplist-helper -f cl -o $out.dl'),
+ deplist='$out.dl')
+
+ if flavor != 'mac' and flavor != 'win':
master_ninja.rule(
'alink',
description='AR $out',
@@ -752,48 +1110,88 @@ def GenerateOutput(target_list, target_dicts, data, params):
description='LINK $out',
command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib '
'-Wl,--start-group $in -Wl,--end-group $libs'))
+ elif flavor == 'win':
+ master_ninja.rule(
+ 'alink',
+ description='AR $out',
+ command='lib /nologo /OUT:$out $in')
+ master_ninja.rule(
+ 'solink',
+ description='SOLINK $out',
+ command=('$ld /nologo /DLL $ldflags /OUT:$out $in $libs'))
+ master_ninja.rule(
+ 'solink_module',
+ description='SOLINK(module) $out',
+ command=('$ld /nologo /DLL $ldflags /OUT:$out $in $libs'))
+ master_ninja.rule(
+ 'link',
+ description='LINK $out',
+ command=('$ld /nologo $ldflags /OUT:$out $in $libs'))
else:
master_ninja.rule(
'objc',
description='OBJC $out',
- command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
- '$cflags_objc -c $in -o $out'),
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
+ '$cflags_pch_objc -c $in -o $out'),
depfile='$out.d')
master_ninja.rule(
'objcxx',
description='OBJCXX $out',
- command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
- '$cflags_objcc -c $in -o $out'),
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
+ '$cflags_pch_objcc -c $in -o $out'),
depfile='$out.d')
master_ninja.rule(
'alink',
- description='LIBTOOL-STATIC $out',
- command='rm -f $out && libtool -static -o $out $in')
+ description='LIBTOOL-STATIC $out, POSTBUILDS',
+ command='rm -f $out && '
+ './gyp-mac-tool filter-libtool libtool -static -o $out $in'
+ '$postbuilds')
# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass
# -bundle -single_module here (for osmesa.so).
master_ninja.rule(
'solink',
- description='SOLINK $out',
+ description='SOLINK $out, POSTBUILDS',
command=('$ld -shared $ldflags -o $out '
- '$in $libs'))
+ '$in $libs$postbuilds'))
master_ninja.rule(
'solink_module',
- description='SOLINK(module) $out',
+ description='SOLINK(module) $out, POSTBUILDS',
command=('$ld -shared $ldflags -o $out '
- '$in $libs'))
+ '$in $libs$postbuilds'))
master_ninja.rule(
'link',
- description='LINK $out',
+ description='LINK $out, POSTBUILDS',
command=('$ld $ldflags -o $out '
- '$in $libs'))
+ '$in $libs$postbuilds'))
+ master_ninja.rule(
+ 'infoplist',
+ description='INFOPLIST $out',
+ command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
+ 'plutil -convert xml1 $out $out'))
+ master_ninja.rule(
+ 'mac_tool',
+ description='MACTOOL $mactool_cmd $in',
+ command='$env $mac_tool $mactool_cmd $in $out')
+ master_ninja.rule(
+ 'package_framework',
+ description='PACKAGE FRAMEWORK $out, POSTBUILDS',
+ command='$mac_tool package-framework $out $version$postbuilds '
+ '&& touch $out')
master_ninja.rule(
'stamp',
description='STAMP $out',
- command='touch $out')
- master_ninja.rule(
- 'copy',
- description='COPY $in $out',
- command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
+ command='${postbuilds}touch $out')
+ if flavor == 'win':
+ # TODO(scottmg): Copy fallback?
+ master_ninja.rule(
+ 'copy',
+ description='COPY $in $out',
+ command='cmd /c mklink /h $out $in >nul || mklink /h /j $out $in >nul')
+ else:
+ master_ninja.rule(
+ 'copy',
+ description='COPY $in $out',
+ command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
master_ninja.newline()
all_targets = set()
@@ -802,38 +1200,57 @@ def GenerateOutput(target_list, target_dicts, data, params):
all_targets.add(target)
all_outputs = set()
+ # target_outputs is a map from qualified target name to a Target object.
target_outputs = {}
for qualified_target in target_list:
# qualified_target is like: third_party/icu/icu.gyp:icui18n#target
build_file, name, toolset = \
gyp.common.ParseQualifiedTarget(qualified_target)
- # TODO: what is options.depth and how is it different than
- # options.toplevel_dir?
- build_file = gyp.common.RelativePath(build_file, options.depth)
+ this_make_global_settings = data[build_file].get('make_global_settings', [])
+ assert make_global_settings == this_make_global_settings, (
+ "make_global_settings needs to be the same for all targets.")
+
+ spec = target_dicts[qualified_target]
+ if flavor == 'mac':
+ gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
+
+ build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
base_path = os.path.dirname(build_file)
obj = 'obj'
if toolset != 'target':
obj += '.' + toolset
output_file = os.path.join(obj, base_path, name + '.ninja')
- spec = target_dicts[qualified_target]
- config = spec['configurations'][config_name]
- writer = NinjaWriter(target_outputs, base_path, builddir,
+ abs_build_dir=os.path.abspath(os.path.join(options.toplevel_dir, build_dir))
+ writer = NinjaWriter(target_outputs, base_path, build_dir,
OpenOutput(os.path.join(options.toplevel_dir,
- builddir,
+ build_dir,
output_file)),
- flavor)
+ flavor, abs_build_dir=abs_build_dir)
master_ninja.subninja(output_file)
- output, compile_depends = writer.WriteSpec(spec, config)
- if output:
- linkable = spec['type'] in ('static_library', 'shared_library')
- target_outputs[qualified_target] = (output, compile_depends, linkable)
-
+ target = writer.WriteSpec(spec, config_name)
+ if target:
+ target_outputs[qualified_target] = target
if qualified_target in all_targets:
- all_outputs.add(output)
+ all_outputs.add(target.FinalOutput())
if all_outputs:
master_ninja.build('all', 'phony', list(all_outputs))
+
+
+def GenerateOutput(target_list, target_dicts, data, params):
+ if params['options'].generator_output:
+ raise NotImplementedError, "--generator_output not implemented for ninja"
+
+ user_config = params.get('generator_flags', {}).get('config', None)
+ if user_config:
+ GenerateOutputForConfig(target_list, target_dicts, data, params,
+ user_config)
+ else:
+ config_names = target_dicts[target_list[0]]['configurations'].keys()
+ for config_name in config_names:
+ GenerateOutputForConfig(target_list, target_dicts, data, params,
+ config_name)
diff --git a/tools/gyp/pylib/gyp/generator/ninja_test.py b/tools/gyp/pylib/gyp/generator/ninja_test.py
new file mode 100644
index 0000000000..af2e6d32fc
--- /dev/null
+++ b/tools/gyp/pylib/gyp/generator/ninja_test.py
@@ -0,0 +1,42 @@
+#!/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.
+
+""" Unit tests for the ninja.py file. """
+
+import gyp.generator.ninja as ninja
+import unittest
+import StringIO
+import TestCommon
+
+
+class TestPrefixesAndSuffixes(unittest.TestCase):
+ def test_BinaryNamesWindows(self):
+ writer = ninja.NinjaWriter('wee', '.', '.', 'ninja.build', 'win')
+ spec = { 'target_name': 'wee' }
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'executable').
+ endswith('.exe'))
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
+ endswith('.dll'))
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
+ endswith('.lib'))
+
+ def test_BinaryNamesLinux(self):
+ writer = ninja.NinjaWriter('wee', '.', '.', 'ninja.build', 'linux')
+ spec = {
+ 'target_name': 'wee'
+ }
+ self.assertTrue('.' not in writer.ComputeOutputFileName(spec, 'executable'))
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
+ startswith('lib'))
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
+ startswith('lib'))
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
+ endswith('.so'))
+ self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
+ endswith('.a'))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/gyp/pylib/gyp/input.py b/tools/gyp/pylib/gyp/input.py
index 19a16288cd..74a96b6d8b 100644
--- a/tools/gyp/pylib/gyp/input.py
+++ b/tools/gyp/pylib/gyp/input.py
@@ -2065,6 +2065,25 @@ def ProcessListFiltersInList(name, the_list):
ProcessListFiltersInList(name, item)
+def ValidateTargetType(target, target_dict):
+ """Ensures the 'type' field on the target is one of the known types.
+
+ Arguments:
+ target: string, name of target.
+ target_dict: dict, target spec.
+
+ Raises an exception on error.
+ """
+ VALID_TARGET_TYPES = ('executable', 'loadable_module',
+ 'static_library', 'shared_library',
+ 'none')
+ target_type = target_dict.get('type', None)
+ if target_type not in VALID_TARGET_TYPES:
+ raise Exception("Target %s has an invalid target type '%s'. "
+ "Must be one of %s." %
+ (target, target_type, '/'.join(VALID_TARGET_TYPES)))
+
+
def ValidateRulesInTarget(target, target_dict, extra_sources_for_rules):
"""Ensures that the rules sections in target_dict are valid and consistent,
and determines which sources they apply to.
@@ -2349,6 +2368,7 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
for target in flat_list:
target_dict = targets[target]
build_file = gyp.common.BuildFile(target)
+ ValidateTargetType(target, target_dict)
ValidateRulesInTarget(target, target_dict, extra_sources_for_rules)
ValidateRunAsInTarget(target, target_dict, build_file)
ValidateActionsInTarget(target, target_dict, build_file)
diff --git a/tools/gyp/pylib/gyp/mac_tool.py b/tools/gyp/pylib/gyp/mac_tool.py
index 64707762d3..42dd6c9923 100755
--- a/tools/gyp/pylib/gyp/mac_tool.py
+++ b/tools/gyp/pylib/gyp/mac_tool.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -8,9 +8,10 @@
These functions are executed via gyp-mac-tool when using the Makefile generator.
"""
-import os
import fcntl
+import os
import plistlib
+import re
import shutil
import string
import subprocess
@@ -19,7 +20,9 @@ import sys
def main(args):
executor = MacTool()
- executor.Dispatch(args)
+ exit_code = executor.Dispatch(args)
+ if exit_code is not None:
+ sys.exit(exit_code)
class MacTool(object):
@@ -32,18 +35,70 @@ class MacTool(object):
raise Exception("Not enough arguments")
method = "Exec%s" % self._CommandifyName(args[0])
- getattr(self, method)(*args[1:])
+ return getattr(self, method)(*args[1:])
def _CommandifyName(self, name_string):
"""Transforms a tool name like copy-info-plist to CopyInfoPlist"""
return name_string.title().replace('-', '')
- def ExecFlock(self, lockfile, *cmd_list):
- """Emulates the most basic behavior of Linux's flock(1)."""
- # Rely on exception handling to report errors.
- fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666)
- fcntl.flock(fd, fcntl.LOCK_EX)
- return subprocess.call(cmd_list)
+ def ExecCopyBundleResource(self, source, dest):
+ """Copies a resource file to the bundle/Resources directory, performing any
+ necessary compilation on each resource."""
+ extension = os.path.splitext(source)[1].lower()
+ if os.path.isdir(source):
+ # Copy tree.
+ if os.path.exists(dest):
+ shutil.rmtree(dest)
+ shutil.copytree(source, dest)
+ elif extension == '.xib':
+ self._CopyXIBFile(source, dest)
+ elif extension == '.strings':
+ self._CopyStringsFile(source, dest)
+ # TODO: Given that files with arbitrary extensions can be copied to the
+ # bundle, we will want to get rid of this whitelist eventually.
+ elif extension in [
+ '.icns', '.manifest', '.pak', '.pdf', '.png', '.sb', '.sh',
+ '.ttf', '.sdef']:
+ shutil.copyfile(source, dest)
+ else:
+ raise NotImplementedError(
+ "Don't know how to copy bundle resources of type %s while copying "
+ "%s to %s)" % (extension, source, dest))
+
+ def _CopyXIBFile(self, source, dest):
+ """Compiles a XIB file with ibtool into a binary plist in the bundle."""
+ args = ['/Developer/usr/bin/ibtool', '--errors', '--warnings',
+ '--notices', '--output-format', 'human-readable-text', '--compile',
+ dest, source]
+ subprocess.call(args)
+
+ def _CopyStringsFile(self, source, dest):
+ """Copies a .strings file using iconv to reconvert the input into UTF-16."""
+ input_code = self._DetectInputEncoding(source) or "UTF-8"
+ fp = open(dest, 'w')
+ args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code',
+ 'UTF-16', source]
+ subprocess.call(args, stdout=fp)
+ fp.close()
+
+ def _DetectInputEncoding(self, file_name):
+ """Reads the first few bytes from file_name and tries to guess the text
+ encoding. Returns None as a guess if it can't detect it."""
+ fp = open(file_name, 'rb')
+ try:
+ header = fp.read(3)
+ except e:
+ fp.close()
+ return None
+ fp.close()
+ if header.startswith("\xFE\xFF"):
+ return "UTF-16BE"
+ elif header.startswith("\xFF\xFE"):
+ return "UTF-16LE"
+ elif header.startswith("\xEF\xBB\xBF"):
+ return "UTF-8"
+ else:
+ return None
def ExecCopyInfoPlist(self, source, dest):
"""Copies the |source| Info.plist to the destination directory |dest|."""
@@ -92,6 +147,22 @@ class MacTool(object):
fp.write('%s%s' % (package_type, signature_code))
fp.close()
+ def ExecFlock(self, lockfile, *cmd_list):
+ """Emulates the most basic behavior of Linux's flock(1)."""
+ # Rely on exception handling to report errors.
+ fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666)
+ fcntl.flock(fd, fcntl.LOCK_EX)
+ return subprocess.call(cmd_list)
+
+ def ExecFilterLibtool(self, *cmd_list):
+ """Calls libtool and filters out 'libtool: file: foo.o has no symbols'."""
+ libtool_re = re.compile(r'^libtool: file: .* has no symbols$')
+ libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE)
+ for line in libtoolout.stderr:
+ if not libtool_re.match(line):
+ sys.stderr.write(line)
+ return libtoolout.returncode
+
def ExecPackageFramework(self, framework, version):
"""Takes a path to Something.framework and the Current version of that and
sets up all the symlinks."""
@@ -128,65 +199,6 @@ class MacTool(object):
os.remove(link)
os.symlink(dest, link)
- def ExecCopyBundleResource(self, source, dest):
- """Copies a resource file to the bundle/Resources directory, performing any
- necessary compilation on each resource."""
- extension = os.path.splitext(source)[1].lower()
- if os.path.isdir(source):
- # Copy tree.
- if os.path.exists(dest):
- shutil.rmtree(dest)
- shutil.copytree(source, dest)
- elif extension == '.xib':
- self._CopyXIBFile(source, dest)
- elif extension == '.strings':
- self._CopyStringsFile(source, dest)
- # TODO: Given that files with arbitrary extensions can be copied to the
- # bundle, we will want to get rid of this whitelist eventually.
- elif extension in [
- '.icns', '.manifest', '.pak', '.pdf', '.png', '.sb', '.sh',
- '.ttf', '.sdef']:
- shutil.copyfile(source, dest)
- else:
- raise NotImplementedError(
- "Don't know how to copy bundle resources of type %s while copying "
- "%s to %s)" % (extension, source, dest))
-
- def _CopyXIBFile(self, source, dest):
- """Compiles a XIB file with ibtool into a binary plist in the bundle."""
- args = ['/Developer/usr/bin/ibtool', '--errors', '--warnings',
- '--notices', '--output-format', 'human-readable-text', '--compile',
- dest, source]
- subprocess.call(args)
-
- def _CopyStringsFile(self, source, dest):
- """Copies a .strings file using iconv to reconvert the input into UTF-16."""
- input_code = self._DetectInputEncoding(source) or "UTF-8"
- fp = open(dest, 'w')
- args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code',
- 'UTF-16', source]
- subprocess.call(args, stdout=fp)
- fp.close()
-
- def _DetectInputEncoding(self, file_name):
- """Reads the first few bytes from file_name and tries to guess the text
- encoding. Returns None as a guess if it can't detect it."""
- fp = open(file_name, 'rb')
- try:
- header = fp.read(3)
- except e:
- fp.close()
- return None
- fp.close()
- if header.startswith("\xFE\xFF"):
- return "UTF-16BE"
- elif header.startswith("\xFF\xFE"):
- return "UTF-16LE"
- elif header.startswith("\xEF\xBB\xBF"):
- return "UTF-8"
- else:
- return None
-
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/tools/gyp/pylib/gyp/ninja_syntax.py b/tools/gyp/pylib/gyp/ninja_syntax.py
index 35cda05707..27e1908761 100644
--- a/tools/gyp/pylib/gyp/ninja_syntax.py
+++ b/tools/gyp/pylib/gyp/ninja_syntax.py
@@ -31,19 +31,23 @@ class Writer(object):
if value is None:
return
if isinstance(value, list):
- value = ' '.join(value)
+ value = ' '.join(filter(None, value)) # Filter out empty strings.
self._line('%s = %s' % (key, value), indent)
def rule(self, name, command, description=None, depfile=None,
- generator=False):
+ generator=False, restat=False, deplist=None):
self._line('rule %s' % name)
self.variable('command', command, indent=1)
if description:
self.variable('description', description, indent=1)
if depfile:
self.variable('depfile', depfile, indent=1)
+ if deplist:
+ self.variable('deplist', deplist, indent=1)
if generator:
self.variable('generator', '1', indent=1)
+ if restat:
+ self.variable('restat', '1', indent=1)
def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
variables=None):
@@ -80,49 +84,49 @@ class Writer(object):
def default(self, paths):
self._line('default %s' % ' '.join(self._as_list(paths)))
+ def _count_dollars_before_index(self, s, i):
+ """Returns the number of '$' characters right in front of s[i]."""
+ dollar_count = 0
+ dollar_index = i - 1
+ while dollar_index > 0 and s[dollar_index] == '$':
+ dollar_count += 1
+ dollar_index -= 1
+ return dollar_count
+
def _line(self, text, indent=0):
"""Write 'text' word-wrapped at self.width characters."""
leading_space = ' ' * indent
while len(text) > self.width:
# The text is too wide; wrap if possible.
- self.output.write(leading_space)
-
+ # Find the rightmost space that would obey our width constraint and
+ # that's not an escaped space.
available_space = self.width - len(leading_space) - len(' $')
+ space = available_space
+ while True:
+ space = text.rfind(' ', 0, space)
+ if space < 0 or \
+ self._count_dollars_before_index(text, space) % 2 == 0:
+ break
- # Write as much as we can into this line.
- done = False
- written_stuff = False
- while available_space > 0:
- space = re.search('((\$\$)+([^$]|^)|[^$]|^) ', text)
- if space:
- space_idx = space.start() + 1
- else:
- # No spaces left.
- done = True
+ if space < 0:
+ # No such space; just use the first unescaped space we can find.
+ space = available_space - 1
+ while True:
+ space = text.find(' ', space + 1)
+ if space < 0 or \
+ self._count_dollars_before_index(text, space) % 2 == 0:
break
+ if space < 0:
+ # Give up on breaking.
+ break
- if space_idx > available_space:
- # We're out of space.
- if written_stuff:
- # See if we can fit it on the next line.
- break
- # If we haven't written anything yet on this line, don't
- # try to wrap.
- self.output.write(text[0:space_idx] + ' ')
- written_stuff = True
- text = text[space_idx+1:]
- available_space -= space_idx+1
-
- self.output.write('$\n')
+ self.output.write(leading_space + text[0:space] + ' $\n')
+ text = text[space+1:]
# Subsequent lines are continuations, so indent them.
leading_space = ' ' * (indent+2)
- if done:
- # No more spaces, so bail.
- break
-
self.output.write(leading_space + text + '\n')
def _as_list(self, input):
diff --git a/tools/gyp/pylib/gyp/system_test.py b/tools/gyp/pylib/gyp/system_test.py
index 4245f86e11..51c71e36be 100755
--- a/tools/gyp/pylib/gyp/system_test.py
+++ b/tools/gyp/pylib/gyp/system_test.py
@@ -51,20 +51,6 @@ def TestArSupportsT(ar_command='ar', cc_command='cc'):
env={'ar': ar_command, 'cc': cc_command})
-def TestLinkerSupportsThreads(cc_command='cc'):
- """Test whether the linker supports the --threads flag."""
- return TestCommands(['%(cc)s -Wl,--threads test.c'],
- files={'test.c': 'int main(){}'},
- env={'cc': cc_command})
-
-
-def TestLinkerSupportsICF(cc_command='cc'):
- """Test whether the linker supports identical code folding."""
- return TestCommands(['%(cc)s -Wl,--icf=safe test.c'],
- files={'test.c': 'int main(){}'},
- env={'cc': cc_command})
-
-
def main():
# Run the various test functions and print the results.
def RunTest(description, function, **kwargs):
@@ -75,7 +61,6 @@ def main():
print 'fail'
RunTest("ar 'T' flag", TestArSupportsT)
RunTest("ar 'T' flag with ccache", TestArSupportsT, cc_command='ccache cc')
- RunTest("ld --threads", TestLinkerSupportsThreads)
return 0
diff --git a/tools/gyp/pylib/gyp/xcode_emulation.py b/tools/gyp/pylib/gyp/xcode_emulation.py
new file mode 100644
index 0000000000..4d8440b69f
--- /dev/null
+++ b/tools/gyp/pylib/gyp/xcode_emulation.py
@@ -0,0 +1,972 @@
+# 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.
+
+"""
+This module contains classes that help to emulate xcodebuild behavior on top of
+other build systems, such as make and ninja.
+"""
+
+import gyp.common
+import os.path
+import re
+import shlex
+
+class XcodeSettings(object):
+ """A class that understands the gyp 'xcode_settings' object."""
+
+ def __init__(self, spec):
+ self.spec = spec
+
+ # Per-target 'xcode_settings' are pushed down into configs earlier by gyp.
+ # This means self.xcode_settings[config] always contains all settings
+ # for that config -- the per-target settings as well. Settings that are
+ # the same for all configs are implicitly per-target settings.
+ self.xcode_settings = {}
+ configs = spec['configurations']
+ for configname, config in configs.iteritems():
+ self.xcode_settings[configname] = config.get('xcode_settings', {})
+
+ # This is only non-None temporarily during the execution of some methods.
+ self.configname = None
+
+ # Used by _AdjustLibrary to match .a and .dylib entries in libraries.
+ self.library_re = re.compile(r'^lib([^/]+)\.(a|dylib)$')
+
+ def _Settings(self):
+ assert self.configname
+ return self.xcode_settings[self.configname]
+
+ def _Test(self, test_key, cond_key, default):
+ return self._Settings().get(test_key, default) == cond_key
+
+ def _Appendf(self, lst, test_key, format_str, default=None):
+ if test_key in self._Settings():
+ lst.append(format_str % str(self._Settings()[test_key]))
+ elif default:
+ lst.append(format_str % str(default))
+
+ def _WarnUnimplemented(self, test_key):
+ if test_key in self._Settings():
+ print 'Warning: Ignoring not yet implemented key "%s".' % test_key
+
+ def _IsBundle(self):
+ return int(self.spec.get('mac_bundle', 0)) != 0
+
+ def GetFrameworkVersion(self):
+ """Returns the framework version of the current target. Only valid for
+ bundles."""
+ assert self._IsBundle()
+ return self.GetPerTargetSetting('FRAMEWORK_VERSION', default='A')
+
+ def GetWrapperExtension(self):
+ """Returns the bundle extension (.app, .framework, .plugin, etc). Only
+ valid for bundles."""
+ assert self._IsBundle()
+ if self.spec['type'] in ('loadable_module', 'shared_library'):
+ default_wrapper_extension = {
+ 'loadable_module': 'bundle',
+ 'shared_library': 'framework',
+ }[self.spec['type']]
+ wrapper_extension = self.GetPerTargetSetting(
+ 'WRAPPER_EXTENSION', default=default_wrapper_extension)
+ return '.' + self.spec.get('product_extension', wrapper_extension)
+ elif self.spec['type'] == 'executable':
+ return '.app'
+ else:
+ assert False, "Don't know extension for '%s', target '%s'" % (
+ self.spec['type'], self.spec['target_name'])
+
+ def GetProductName(self):
+ """Returns PRODUCT_NAME."""
+ return self.spec.get('product_name', self.spec['target_name'])
+
+ def GetFullProductName(self):
+ """Returns FULL_PRODUCT_NAME."""
+ if self._IsBundle():
+ return self.GetWrapperName()
+ else:
+ return self._GetStandaloneBinaryPath()
+
+ def GetWrapperName(self):
+ """Returns the directory name of the bundle represented by this target.
+ Only valid for bundles."""
+ assert self._IsBundle()
+ return self.GetProductName() + self.GetWrapperExtension()
+
+ def GetBundleContentsFolderPath(self):
+ """Returns the qualified path to the bundle's contents folder. E.g.
+ Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles."""
+ assert self._IsBundle()
+ if self.spec['type'] == 'shared_library':
+ return os.path.join(
+ self.GetWrapperName(), 'Versions', self.GetFrameworkVersion())
+ else:
+ # loadable_modules have a 'Contents' folder like executables.
+ return os.path.join(self.GetWrapperName(), 'Contents')
+
+ def GetBundleResourceFolder(self):
+ """Returns the qualified path to the bundle's resource folder. E.g.
+ Chromium.app/Contents/Resources. Only valid for bundles."""
+ assert self._IsBundle()
+ return os.path.join(self.GetBundleContentsFolderPath(), 'Resources')
+
+ def GetBundlePlistPath(self):
+ """Returns the qualified path to the bundle's plist file. E.g.
+ Chromium.app/Contents/Info.plist. Only valid for bundles."""
+ assert self._IsBundle()
+ if self.spec['type'] in ('executable', 'loadable_module'):
+ return os.path.join(self.GetBundleContentsFolderPath(), 'Info.plist')
+ else:
+ return os.path.join(self.GetBundleContentsFolderPath(),
+ 'Resources', 'Info.plist')
+
+ def GetProductType(self):
+ """Returns the PRODUCT_TYPE of this target."""
+ if self._IsBundle():
+ return {
+ 'executable': 'com.apple.product-type.application',
+ 'loadable_module': 'com.apple.product-type.bundle',
+ 'shared_library': 'com.apple.product-type.framework',
+ }[self.spec['type']]
+ else:
+ return {
+ 'executable': 'com.apple.product-type.tool',
+ 'loadable_module': 'com.apple.product-type.library.dynamic',
+ 'shared_library': 'com.apple.product-type.library.dynamic',
+ 'static_library': 'com.apple.product-type.library.static',
+ }[self.spec['type']]
+
+ def GetMachOType(self):
+ """Returns the MACH_O_TYPE of this target."""
+ # Weird, but matches Xcode.
+ if not self._IsBundle() and self.spec['type'] == 'executable':
+ return ''
+ return {
+ 'executable': 'mh_execute',
+ 'static_library': 'staticlib',
+ 'shared_library': 'mh_dylib',
+ 'loadable_module': 'mh_bundle',
+ }[self.spec['type']]
+
+ def _GetBundleBinaryPath(self):
+ """Returns the name of the bundle binary of by this target.
+ E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles."""
+ assert self._IsBundle()
+ if self.spec['type'] in ('shared_library'):
+ path = self.GetBundleContentsFolderPath()
+ elif self.spec['type'] in ('executable', 'loadable_module'):
+ path = os.path.join(self.GetBundleContentsFolderPath(), 'MacOS')
+ return os.path.join(path, self.GetExecutableName())
+
+ def _GetStandaloneExecutableSuffix(self):
+ if 'product_extension' in self.spec:
+ return '.' + self.spec['product_extension']
+ return {
+ 'executable': '',
+ 'static_library': '.a',
+ 'shared_library': '.dylib',
+ 'loadable_module': '.so',
+ }[self.spec['type']]
+
+ def _GetStandaloneExecutablePrefix(self):
+ return self.spec.get('product_prefix', {
+ 'executable': '',
+ 'static_library': 'lib',
+ 'shared_library': 'lib',
+ # Non-bundled loadable_modules are called foo.so for some reason
+ # (that is, .so and no prefix) with the xcode build -- match that.
+ 'loadable_module': '',
+ }[self.spec['type']])
+
+ def _GetStandaloneBinaryPath(self):
+ """Returns the name of the non-bundle binary represented by this target.
+ E.g. hello_world. Only valid for non-bundles."""
+ assert not self._IsBundle()
+ assert self.spec['type'] in (
+ 'executable', 'shared_library', 'static_library', 'loadable_module'), (
+ 'Unexpected type %s' % self.spec['type'])
+ target = self.spec['target_name']
+ if self.spec['type'] == 'static_library':
+ if target[:3] == 'lib':
+ target = target[3:]
+ elif self.spec['type'] in ('loadable_module', 'shared_library'):
+ if target[:3] == 'lib':
+ target = target[3:]
+
+ target_prefix = self._GetStandaloneExecutablePrefix()
+ target = self.spec.get('product_name', target)
+ target_ext = self._GetStandaloneExecutableSuffix()
+ return target_prefix + target + target_ext
+
+ def GetExecutableName(self):
+ """Returns the executable name of the bundle represented by this target.
+ E.g. Chromium."""
+ if self._IsBundle():
+ return self.spec.get('product_name', self.spec['target_name'])
+ else:
+ return self._GetStandaloneBinaryPath()
+
+ def GetExecutablePath(self):
+ """Returns the directory name of the bundle represented by this target. E.g.
+ Chromium.app/Contents/MacOS/Chromium."""
+ if self._IsBundle():
+ return self._GetBundleBinaryPath()
+ else:
+ return self._GetStandaloneBinaryPath()
+
+ def _SdkPath(self):
+ sdk_root = self.GetPerTargetSetting('SDKROOT', default='macosx10.5')
+ 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."""
+ # This functions (and the similar ones below) do not offer complete
+ # emulation of all xcode_settings keys. They're implemented on demand.
+
+ self.configname = configname
+ cflags = []
+
+ sdk_root = self._SdkPath()
+ if 'SDKROOT' in self._Settings():
+ cflags.append('-isysroot %s' % sdk_root)
+
+ if self._Test('GCC_CHAR_IS_UNSIGNED_CHAR', 'YES', default='NO'):
+ cflags.append('-funsigned-char')
+
+ if self._Test('GCC_CW_ASM_SYNTAX', 'YES', default='YES'):
+ cflags.append('-fasm-blocks')
+
+ if 'GCC_DYNAMIC_NO_PIC' in self._Settings():
+ if self._Settings()['GCC_DYNAMIC_NO_PIC'] == 'YES':
+ cflags.append('-mdynamic-no-pic')
+ else:
+ pass
+ # TODO: In this case, it depends on the target. xcode passes
+ # mdynamic-no-pic by default for executable and possibly static lib
+ # according to mento
+
+ if self._Test('GCC_ENABLE_PASCAL_STRINGS', 'YES', default='YES'):
+ cflags.append('-mpascal-strings')
+
+ self._Appendf(cflags, 'GCC_OPTIMIZATION_LEVEL', '-O%s', default='s')
+
+ 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':
+ cflags.append('-gdwarf-2')
+ else:
+ raise NotImplementedError('Unknown debug format %s' % dbg_format)
+
+ if self._Test('GCC_SYMBOLS_PRIVATE_EXTERN', 'YES', default='NO'):
+ cflags.append('-fvisibility=hidden')
+
+ if self._Test('GCC_TREAT_WARNINGS_AS_ERRORS', 'YES', default='NO'):
+ cflags.append('-Werror')
+
+ if self._Test('GCC_WARN_ABOUT_MISSING_NEWLINE', 'YES', default='NO'):
+ cflags.append('-Wnewline-eof')
+
+ self._Appendf(cflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
+
+ # TODO:
+ if self._Test('COPY_PHASE_STRIP', 'YES', default='NO'):
+ self._WarnUnimplemented('COPY_PHASE_STRIP')
+ self._WarnUnimplemented('GCC_DEBUGGING_SYMBOLS')
+ self._WarnUnimplemented('GCC_ENABLE_OBJC_EXCEPTIONS')
+ self._WarnUnimplemented('GCC_ENABLE_OBJC_GC')
+
+ # TODO: This is exported correctly, but assigning to it is not supported.
+ self._WarnUnimplemented('MACH_O_TYPE')
+ self._WarnUnimplemented('PRODUCT_TYPE')
+
+ archs = self._Settings().get('ARCHS', ['i386'])
+ if len(archs) != 1:
+ # TODO: Supporting fat binaries will be annoying.
+ self._WarnUnimplemented('ARCHS')
+ archs = ['i386']
+ cflags.append('-arch ' + archs[0])
+
+ if archs[0] in ('i386', 'x86_64'):
+ if self._Test('GCC_ENABLE_SSE3_EXTENSIONS', 'YES', default='NO'):
+ cflags.append('-msse3')
+ if self._Test('GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS', 'YES',
+ default='NO'):
+ cflags.append('-mssse3') # Note 3rd 's'.
+ if self._Test('GCC_ENABLE_SSE41_EXTENSIONS', 'YES', default='NO'):
+ cflags.append('-msse4.1')
+ if self._Test('GCC_ENABLE_SSE42_EXTENSIONS', 'YES', default='NO'):
+ cflags.append('-msse4.2')
+
+ cflags += self._Settings().get('OTHER_CFLAGS', [])
+ cflags += self._Settings().get('WARNING_CFLAGS', [])
+
+ config = self.spec['configurations'][self.configname]
+ framework_dirs = config.get('mac_framework_dirs', [])
+ for directory in framework_dirs:
+ cflags.append('-F ' + directory.replace('$(SDKROOT)', sdk_root))
+
+ self.configname = None
+ return cflags
+
+ def GetCflagsC(self, configname):
+ """Returns flags that need to be added to .c, and .m compilations."""
+ self.configname = configname
+ cflags_c = []
+ self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s')
+ self.configname = None
+ return cflags_c
+
+ def GetCflagsCC(self, configname):
+ """Returns flags that need to be added to .cc, and .mm compilations."""
+ self.configname = configname
+ cflags_cc = []
+ if self._Test('GCC_ENABLE_CPP_RTTI', 'NO', default='YES'):
+ cflags_cc.append('-fno-rtti')
+ if self._Test('GCC_ENABLE_CPP_EXCEPTIONS', 'NO', default='YES'):
+ cflags_cc.append('-fno-exceptions')
+ if self._Test('GCC_INLINES_ARE_PRIVATE_EXTERN', 'YES', default='NO'):
+ cflags_cc.append('-fvisibility-inlines-hidden')
+ if self._Test('GCC_THREADSAFE_STATICS', 'NO', default='YES'):
+ cflags_cc.append('-fno-threadsafe-statics')
+ self.configname = None
+ return cflags_cc
+
+ def GetCflagsObjC(self, configname):
+ """Returns flags that need to be added to .m compilations."""
+ self.configname = configname
+ self.configname = None
+ return []
+
+ def GetCflagsObjCC(self, configname):
+ """Returns flags that need to be added to .mm compilations."""
+ self.configname = configname
+ cflags_objcc = []
+ if self._Test('GCC_OBJC_CALL_CXX_CDTORS', 'YES', default='NO'):
+ cflags_objcc.append('-fobjc-call-cxx-cdtors')
+ self.configname = None
+ return cflags_objcc
+
+ def GetLdflags(self, configname, product_dir, gyp_to_build_path):
+ """Returns flags that need to be passed to the linker.
+
+ Args:
+ configname: The name of the configuration to get ld flags for.
+ product_dir: The directory where products such static and dynamic
+ libraries are placed. This is added to the library search path.
+ gyp_to_build_path: A function that converts paths relative to the
+ current gyp file to paths relative to the build direcotry.
+ """
+ self.configname = configname
+ ldflags = []
+
+ # The xcode build is relative to a gyp file's directory, and OTHER_LDFLAGS
+ # contains two entries that depend on this. Explicitly absolutify for these
+ # two cases.
+ def MapGypPathWithPrefix(flag, prefix):
+ if flag.startswith(prefix):
+ flag = prefix + gyp_to_build_path(flag[len(prefix):])
+ return flag
+ for ldflag in self._Settings().get('OTHER_LDFLAGS', []):
+ # Required for ffmpeg (no idea why they don't use LIBRARY_SEARCH_PATHS,
+ # TODO(thakis): Update ffmpeg.gyp):
+ ldflag = MapGypPathWithPrefix(ldflag, '-L')
+ # Required for the nacl plugin:
+ ldflag = MapGypPathWithPrefix(ldflag, '-Wl,-exported_symbols_list ')
+ ldflags.append(ldflag)
+
+ if self._Test('DEAD_CODE_STRIPPING', 'YES', default='NO'):
+ ldflags.append('-Wl,-dead_strip')
+
+ if self._Test('PREBINDING', 'YES', default='NO'):
+ ldflags.append('-Wl,-prebind')
+
+ self._Appendf(
+ ldflags, 'DYLIB_COMPATIBILITY_VERSION', '-compatibility_version %s')
+ self._Appendf(
+ ldflags, 'DYLIB_CURRENT_VERSION', '-current_version %s')
+ self._Appendf(
+ ldflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
+ if 'SDKROOT' in self._Settings():
+ ldflags.append('-isysroot ' + self._SdkPath())
+
+ for library_path in self._Settings().get('LIBRARY_SEARCH_PATHS', []):
+ ldflags.append('-L' + gyp_to_build_path(library_path))
+
+ if 'ORDER_FILE' in self._Settings():
+ ldflags.append('-Wl,-order_file ' +
+ '-Wl,' + gyp_to_build_path(
+ self._Settings()['ORDER_FILE']))
+
+ archs = self._Settings().get('ARCHS', ['i386'])
+ if len(archs) != 1:
+ # TODO: Supporting fat binaries will be annoying.
+ self._WarnUnimplemented('ARCHS')
+ archs = ['i386']
+ ldflags.append('-arch ' + archs[0])
+
+ # Xcode adds the product directory by default.
+ ldflags.append('-L' + product_dir)
+
+ install_name = self.GetPerTargetSetting('LD_DYLIB_INSTALL_NAME')
+ install_base = self.GetPerTargetSetting('DYLIB_INSTALL_NAME_BASE')
+ default_install_name = \
+ '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)'
+ if not install_name and install_base:
+ install_name = default_install_name
+
+ if install_name:
+ # Hardcode support for the variables used in chromium for now, to unblock
+ # people using the make build.
+ if '$' in install_name:
+ assert install_name in ('$(DYLIB_INSTALL_NAME_BASE:standardizepath)/'
+ '$(WRAPPER_NAME)/$(PRODUCT_NAME)', default_install_name), (
+ 'Variables in LD_DYLIB_INSTALL_NAME are not generally supported yet'
+ ' in target \'%s\' (got \'%s\')' %
+ (self.spec['target_name'], install_name))
+ # I'm not quite sure what :standardizepath does. Just call normpath(),
+ # but don't let @executable_path/../foo collapse to foo.
+ if '/' in install_base:
+ prefix, rest = '', install_base
+ if install_base.startswith('@'):
+ prefix, rest = install_base.split('/', 1)
+ rest = os.path.normpath(rest) # :standardizepath
+ install_base = os.path.join(prefix, rest)
+
+ install_name = install_name.replace(
+ '$(DYLIB_INSTALL_NAME_BASE:standardizepath)', install_base)
+ if self._IsBundle():
+ # These are only valid for bundles, hence the |if|.
+ install_name = install_name.replace(
+ '$(WRAPPER_NAME)', self.GetWrapperName())
+ install_name = install_name.replace(
+ '$(PRODUCT_NAME)', self.GetProductName())
+ else:
+ assert '$(WRAPPER_NAME)' not in install_name
+ assert '$(PRODUCT_NAME)' not in install_name
+
+ install_name = install_name.replace(
+ '$(EXECUTABLE_PATH)', self.GetExecutablePath())
+
+ install_name = install_name.replace(' ', r'\ ')
+ ldflags.append('-install_name ' + install_name)
+
+ 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."""
+ first_pass = True
+ result = None
+ for configname in sorted(self.xcode_settings.keys()):
+ if first_pass:
+ result = self.xcode_settings[configname].get(setting, None)
+ first_pass = False
+ else:
+ assert result == self.xcode_settings[configname].get(setting, None), (
+ "Expected per-target setting for '%s', got per-config setting "
+ "(target %s)" % (setting, spec['target_name']))
+ if result is None:
+ return default
+ return result
+
+ def _GetStripPostbuilds(self, configname, output_binary, quiet):
+ """Returns a list of shell commands that contain the shell commands
+ neccessary to strip this target's binary. These should be run as postbuilds
+ before the actual postbuilds run."""
+ self.configname = configname
+
+ result = []
+ if (self._Test('DEPLOYMENT_POSTPROCESSING', 'YES', default='NO') and
+ self._Test('STRIP_INSTALLED_PRODUCT', 'YES', default='NO')):
+
+ default_strip_style = 'debugging'
+ if self._IsBundle():
+ default_strip_style = 'non-global'
+ elif self.spec['type'] == 'executable':
+ default_strip_style = 'all'
+
+ strip_style = self._Settings().get('STRIP_STYLE', default_strip_style)
+ strip_flags = {
+ 'all': '',
+ 'non-global': '-x',
+ 'debugging': '-S',
+ }[strip_style]
+
+ explicit_strip_flags = self._Settings().get('STRIPFLAGS', '')
+ if explicit_strip_flags:
+ strip_flags += ' ' + _NormalizeEnvVarReferences(explicit_strip_flags)
+
+ if not quiet:
+ result.append('echo STRIP\\(%s\\)' % self.spec['target_name'])
+ result.append('strip %s %s' % (strip_flags, output_binary))
+
+ self.configname = None
+ return result
+
+ def _GetDebugInfoPostbuilds(self, configname, output, output_binary, quiet):
+ """Returns a list of shell commands that contain the shell commands
+ neccessary to massage this target's debug information. These should be run
+ as postbuilds before the actual postbuilds run."""
+ self.configname = configname
+
+ # For static libraries, no dSYMs are created.
+ result = []
+ if (self._Test('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES', default='YES') and
+ self._Test(
+ 'DEBUG_INFORMATION_FORMAT', 'dwarf-with-dsym', default='dwarf') and
+ self.spec['type'] != 'static_library'):
+ if not quiet:
+ result.append('echo DSYMUTIL\\(%s\\)' % self.spec['target_name'])
+ result.append('dsymutil %s -o %s' % (output_binary, output + '.dSYM'))
+
+ self.configname = None
+ return result
+
+ def GetTargetPostbuilds(self, configname, output, output_binary, quiet=False):
+ """Returns a list of shell commands that contain the shell commands
+ to run as postbuilds for this target, before the actual postbuilds."""
+ # dSYMs need to build before stripping happens.
+ return (
+ self._GetDebugInfoPostbuilds(configname, output, output_binary, quiet) +
+ self._GetStripPostbuilds(configname, output_binary, quiet))
+
+ def _AdjustLibrary(self, library):
+ if library.endswith('.framework'):
+ l = '-framework ' + os.path.splitext(os.path.basename(library))[0]
+ else:
+ m = self.library_re.match(library)
+ if m:
+ l = '-l' + m.group(1)
+ else:
+ l = library
+ return l.replace('$(SDKROOT)', self._SdkPath())
+
+ def AdjustLibraries(self, libraries):
+ """Transforms entries like 'Cocoa.framework' in libraries into entries like
+ '-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc.
+ """
+ libraries = [ self._AdjustLibrary(library) for library in libraries]
+ return libraries
+
+
+class MacPrefixHeader(object):
+ """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature.
+
+ This feature consists of several pieces:
+ * If GCC_PREFIX_HEADER is present, all compilations in that project get an
+ additional |-include path_to_prefix_header| cflag.
+ * If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is
+ instead compiled, and all other compilations in the project get an
+ additional |-include path_to_compiled_header| instead.
+ + Compiled prefix headers have the extension gch. There is one gch file for
+ every language used in the project (c, cc, m, mm), since gch files for
+ different languages aren't compatible.
+ + gch files themselves are built with the target's normal cflags, but they
+ obviously don't get the |-include| flag. Instead, they need a -x flag that
+ describes their language.
+ + All o files in the target need to depend on the gch file, to make sure
+ it's built before any o file is built.
+
+ This class helps with some of these tasks, but it needs help from the build
+ system for writing dependencies to the gch files, for writing build commands
+ for the gch files, and for figuring out the location of the gch files.
+ """
+ def __init__(self, xcode_settings,
+ gyp_path_to_build_path, gyp_path_to_build_output):
+ """If xcode_settings is None, all methods on this class are no-ops.
+
+ Args:
+ gyp_path_to_build_path: A function that takes a gyp-relative path,
+ and returns a path relative to the build directory.
+ gyp_path_to_build_output: A function that takes a gyp-relative path and
+ a language code ('c', 'cc', 'm', or 'mm'), and that returns a path
+ to where the output of precompiling that path for that language
+ should be placed (without the trailing '.gch').
+ """
+ # This doesn't support per-configuration prefix headers. Good enough
+ # for now.
+ self.header = None
+ self.compile_headers = False
+ if xcode_settings:
+ self.header = xcode_settings.GetPerTargetSetting('GCC_PREFIX_HEADER')
+ self.compile_headers = xcode_settings.GetPerTargetSetting(
+ 'GCC_PRECOMPILE_PREFIX_HEADER', default='NO') != 'NO'
+ self.compiled_headers = {}
+ if self.header:
+ if self.compile_headers:
+ for lang in ['c', 'cc', 'm', 'mm']:
+ self.compiled_headers[lang] = gyp_path_to_build_output(
+ self.header, lang)
+ self.header = gyp_path_to_build_path(self.header)
+
+ def GetInclude(self, lang):
+ """Gets the cflags to include the prefix header for language |lang|."""
+ if self.compile_headers and lang in self.compiled_headers:
+ return '-include %s' % self.compiled_headers[lang]
+ elif self.header:
+ return '-include %s' % self.header
+ else:
+ return ''
+
+ def _Gch(self, lang):
+ """Returns the actual file name of the prefix header for language |lang|."""
+ assert self.compile_headers
+ return self.compiled_headers[lang] + '.gch'
+
+ def GetObjDependencies(self, sources, objs):
+ """Given a list of source files and the corresponding object files, returns
+ a list of (source, object, gch) tuples, where |gch| is the build-directory
+ relative path to the gch file each object file depends on. |compilable[i]|
+ has to be the source file belonging to |objs[i]|."""
+ if not self.header or not self.compile_headers:
+ return []
+
+ result = []
+ for source, obj in zip(sources, objs):
+ ext = os.path.splitext(source)[1]
+ lang = {
+ '.c': 'c',
+ '.cpp': 'cc', '.cc': 'cc', '.cxx': 'cc',
+ '.m': 'm',
+ '.mm': 'mm',
+ }.get(ext, None)
+ if lang:
+ result.append((source, obj, self._Gch(lang)))
+ return result
+
+ def GetGchBuildCommands(self):
+ """Returns [(path_to_gch, language_flag, language, header)].
+ |path_to_gch| and |header| are relative to the build directory.
+ """
+ if not self.header or not self.compile_headers:
+ return []
+ return [
+ (self._Gch('c'), '-x c-header', 'c', self.header),
+ (self._Gch('cc'), '-x c++-header', 'cc', self.header),
+ (self._Gch('m'), '-x objective-c-header', 'm', self.header),
+ (self._Gch('mm'), '-x objective-c++-header', 'mm', self.header),
+ ]
+
+
+def MergeGlobalXcodeSettingsToSpec(global_dict, spec):
+ """Merges the global xcode_settings dictionary into each configuration of the
+ target represented by spec. For keys that are both in the global and the local
+ xcode_settings dict, the local key gets precendence.
+ """
+ # The xcode generator special-cases global xcode_settings and does something
+ # that amounts to merging in the global xcode_settings into each local
+ # xcode_settings dict.
+ global_xcode_settings = global_dict.get('xcode_settings', {})
+ for config in spec['configurations'].values():
+ if 'xcode_settings' in config:
+ new_settings = global_xcode_settings.copy()
+ new_settings.update(config['xcode_settings'])
+ config['xcode_settings'] = new_settings
+
+
+def IsMacBundle(flavor, spec):
+ """Returns if |spec| should be treated as a bundle.
+
+ Bundles are directories with a certain subdirectory structure, instead of
+ just a single file. Bundle rules do not produce a binary but also package
+ resources into that directory."""
+ is_mac_bundle = (int(spec.get('mac_bundle', 0)) != 0 and flavor == 'mac')
+ if is_mac_bundle:
+ assert spec['type'] != 'none', (
+ 'mac_bundle targets cannot have type none (target "%s")' %
+ spec['target_name'])
+ return is_mac_bundle
+
+
+def GetMacBundleResources(product_dir, xcode_settings, resources):
+ """Yields (output, resource) pairs for every resource in |resources|.
+ Only call this for mac bundle targets.
+
+ Args:
+ product_dir: Path to the directory containing the output bundle,
+ relative to the build directory.
+ xcode_settings: The XcodeSettings of the current target.
+ resources: A list of bundle resources, relative to the build directory.
+ """
+ dest = os.path.join(product_dir,
+ xcode_settings.GetBundleResourceFolder())
+ for res in resources:
+ output = dest
+
+ # The make generator doesn't support it, so forbid it everywhere
+ # to keep the generators more interchangable.
+ assert ' ' not in res, (
+ "Spaces in resource filenames not supported (%s)" % res)
+
+ # Split into (path,file).
+ res_parts = os.path.split(res)
+
+ # Now split the path into (prefix,maybe.lproj).
+ lproj_parts = os.path.split(res_parts[0])
+ # If the resource lives in a .lproj bundle, add that to the destination.
+ if lproj_parts[1].endswith('.lproj'):
+ output = os.path.join(output, lproj_parts[1])
+
+ output = os.path.join(output, res_parts[1])
+ # Compiled XIB files are referred to by .nib.
+ if output.endswith('.xib'):
+ output = output[0:-3] + 'nib'
+
+ yield output, res
+
+
+def GetMacInfoPlist(product_dir, xcode_settings, gyp_path_to_build_path):
+ """Returns (info_plist, dest_plist, defines, extra_env), where:
+ * |info_plist| is the sourc plist path, relative to the
+ build directory,
+ * |dest_plist| is the destination plist path, relative to the
+ build directory,
+ * |defines| is a list of preprocessor defines (empty if the plist
+ shouldn't be preprocessed,
+ * |extra_env| is a dict of env variables that should be exported when
+ invoking |mac_tool copy-info-plist|.
+
+ Only call this for mac bundle targets.
+
+ Args:
+ product_dir: Path to the directory containing the output bundle,
+ relative to the build directory.
+ xcode_settings: The XcodeSettings of the current target.
+ gyp_to_build_path: A function that converts paths relative to the
+ current gyp file to paths relative to the build direcotry.
+ """
+ info_plist = xcode_settings.GetPerTargetSetting('INFOPLIST_FILE')
+ if not info_plist:
+ return None, None, [], {}
+
+ # The make generator doesn't support it, so forbid it everywhere
+ # to keep the generators more interchangable.
+ assert ' ' not in info_plist, (
+ "Spaces in Info.plist filenames not supported (%s)" % info_plist)
+
+ info_plist = gyp_path_to_build_path(info_plist)
+
+ # If explicitly set to preprocess the plist, invoke the C preprocessor and
+ # specify any defines as -D flags.
+ if xcode_settings.GetPerTargetSetting(
+ 'INFOPLIST_PREPROCESS', default='NO') == 'YES':
+ # Create an intermediate file based on the path.
+ defines = shlex.split(xcode_settings.GetPerTargetSetting(
+ 'INFOPLIST_PREPROCESSOR_DEFINITIONS', default=''))
+ else:
+ defines = []
+
+ dest_plist = os.path.join(product_dir, xcode_settings.GetBundlePlistPath())
+ extra_env = xcode_settings.GetPerTargetSettings()
+
+ return info_plist, dest_plist, defines, extra_env
+
+
+def GetXcodeEnv(xcode_settings, built_products_dir, srcroot, configuration,
+ additional_settings=None):
+ """Return the environment variables that Xcode would set. See
+ http://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW153
+ for a full list.
+
+ Args:
+ xcode_settings: An XcodeSettings object. If this is None, this function
+ returns an empty dict.
+ built_products_dir: Absolute path to the built products dir.
+ srcroot: Absolute path to the source root.
+ configuration: The build configuration name.
+ additional_settings: An optional dict with more values to add to the
+ result.
+ """
+ if not xcode_settings: return {}
+
+ # This function is considered a friend of XcodeSettings, so let it reach into
+ # its implementation details.
+ spec = xcode_settings.spec
+
+ # These are filled in on a as-needed basis.
+ env = {
+ 'BUILT_PRODUCTS_DIR' : built_products_dir,
+ 'CONFIGURATION' : configuration,
+ 'PRODUCT_NAME' : xcode_settings.GetProductName(),
+ # See /Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX\ Product\ Types.xcspec for FULL_PRODUCT_NAME
+ 'SRCROOT' : srcroot,
+ 'SOURCE_ROOT': '${SRCROOT}',
+ # This is not true for static libraries, but currently the env is only
+ # written for bundles:
+ 'TARGET_BUILD_DIR' : built_products_dir,
+ 'TEMP_DIR' : '${TMPDIR}',
+ }
+ if spec['type'] in (
+ 'executable', 'static_library', 'shared_library', 'loadable_module'):
+ env['EXECUTABLE_NAME'] = xcode_settings.GetExecutableName()
+ env['EXECUTABLE_PATH'] = xcode_settings.GetExecutablePath()
+ env['FULL_PRODUCT_NAME'] = xcode_settings.GetFullProductName()
+ mach_o_type = xcode_settings.GetMachOType()
+ if mach_o_type:
+ env['MACH_O_TYPE'] = mach_o_type
+ env['PRODUCT_TYPE'] = xcode_settings.GetProductType()
+ if xcode_settings._IsBundle():
+ env['CONTENTS_FOLDER_PATH'] = \
+ xcode_settings.GetBundleContentsFolderPath()
+ env['UNLOCALIZED_RESOURCES_FOLDER_PATH'] = \
+ xcode_settings.GetBundleResourceFolder()
+ env['INFOPLIST_PATH'] = xcode_settings.GetBundlePlistPath()
+ env['WRAPPER_NAME'] = xcode_settings.GetWrapperName()
+
+ if not additional_settings:
+ additional_settings = {}
+ else:
+ # Flatten lists to strings.
+ for k in additional_settings:
+ if not isinstance(additional_settings[k], str):
+ additional_settings[k] = ' '.join(additional_settings[k])
+ additional_settings.update(env)
+
+ for k in additional_settings:
+ additional_settings[k] = _NormalizeEnvVarReferences(additional_settings[k])
+
+ return additional_settings
+
+
+def _NormalizeEnvVarReferences(str):
+ """Takes a string containing variable references in the form ${FOO}, $(FOO),
+ or $FOO, and returns a string with all variable references in the form ${FOO}.
+ """
+ # $FOO -> ${FOO}
+ str = re.sub(r'\$([a-zA-Z_][a-zA-Z0-9_]*)', r'${\1}', str)
+
+ # $(FOO) -> ${FOO}
+ matches = re.findall(r'(\$\(([a-zA-Z0-9\-_]+)\))', str)
+ for match in matches:
+ to_replace, variable = match
+ assert '$(' not in match, '$($(FOO)) variables not supported: ' + match
+ str = str.replace(to_replace, '${' + variable + '}')
+
+ return str
+
+
+def ExpandEnvVars(string, expansions):
+ """Expands ${VARIABLES}, $(VARIABLES), and $VARIABLES in string per the
+ expansions dict. If the variable expands to something that references
+ another variable, this variable is expanded as well if it's in env --
+ until no variables present in env are left."""
+ for k in reversed(TopologicallySortedEnvVarKeys(expansions)):
+ string = string.replace('${' + k + '}', expansions[k])
+ string = string.replace('$(' + k + ')', expansions[k])
+ string = string.replace('$' + k, expansions[k])
+ return string
+
+
+def TopologicallySortedEnvVarKeys(env):
+ """Takes a dict |env| whose values are strings that can refer to other keys,
+ for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of
+ env such that key2 is after key1 in L if env[key2] refers to env[key1].
+
+ Throws an Exception in case of dependency cycles.
+ """
+ # Since environment variables can refer to other variables, the evaluation
+ # order is important. Below is the logic to compute the dependency graph
+ # and sort it.
+ regex = re.compile(r'\$\{([a-zA-Z0-9\-_]+)\}')
+
+ # First sort the list of keys.
+ key_list = sorted(env.keys())
+
+ # Phase 1: Create a set of edges of (DEPENDEE, DEPENDER) where in the graph,
+ # DEPENDEE -> DEPENDER. Also create sets of dependers and dependees.
+ edges = set()
+ dependees = set()
+ dependers = set()
+ for k in key_list:
+ matches = regex.findall(env[k])
+ if not len(matches):
+ continue
+
+ depends_on_other_var = False
+ for dependee in matches:
+ assert '${' not in dependee, 'Nested variables not supported: ' + dependee
+ if dependee in env:
+ edges.add((dependee, k))
+ dependees.add(dependee)
+ depends_on_other_var = True
+ if depends_on_other_var:
+ dependers.add(k)
+
+ # Phase 2: Create a list of graph nodes with no incoming edges.
+ sorted_nodes = []
+ edgeless_nodes = dependees - dependers
+
+ # Phase 3: Perform Kahn topological sort.
+ while len(edgeless_nodes):
+ # Find a node with no incoming edges, add it to the sorted list, and
+ # remove it from the list of nodes that aren't part of the graph.
+ node = edgeless_nodes.pop()
+ sorted_nodes.append(node)
+ key_list.remove(node)
+
+ # Find all the edges between |node| and other nodes.
+ edges_to_node = [e for e in edges if e[0] == node]
+ for edge in edges_to_node:
+ edges.remove(edge)
+ # If the node connected to |node| by |edge| has no other incoming edges,
+ # add it to |edgeless_nodes|.
+ if not len([e for e in edges if e[1] == edge[1]]):
+ edgeless_nodes.add(edge[1])
+
+ # Any remaining edges indicate a cycle.
+ if len(edges):
+ raise Exception('Xcode environment variables are cyclically dependent: ' +
+ str(edges))
+
+ # Append the "nodes" not in the graph to those that were just sorted.
+ sorted_nodes.extend(key_list)
+
+ return sorted_nodes
+
+def GetSpecPostbuildCommands(spec, gyp_path_to_build_path, quiet=False):
+ """Returns the list of postbuilds explicitly defined on |spec|, in a form
+ executable by a shell."""
+ postbuilds = []
+ for postbuild in spec.get('postbuilds', []):
+ if not quiet:
+ postbuilds.append('echo POSTBUILD\\(%s\\) %s' % (
+ spec['target_name'], postbuild['postbuild_name']))
+ shell_list = postbuild['action'][:]
+ # The first element is the command. If it's a relative path, it's
+ # a script in the source tree relative to the gyp file and needs to be
+ # absolutified. Else, it's in the PATH (e.g. install_name_tool, ln).
+ if os.path.sep in shell_list[0]:
+ shell_list[0] = gyp_path_to_build_path(shell_list[0])
+
+ # "script.sh" -> "./script.sh"
+ if not os.path.sep in shell_list[0]:
+ shell_list[0] = os.path.join('.', shell_list[0])
+ postbuilds.append(gyp.common.EncodePOSIXShellList(shell_list))
+
+ return postbuilds
diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py
index 974db7fee5..59573414ae 100755
--- a/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py
+++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py
@@ -5,7 +5,7 @@
# found in the LICENSE file.
"""
-Verifies inclusion of $HOME/.gyp/includes.gypi works properly with relocation
+Verifies inclusion of $HOME/.gyp/include.gypi works properly with relocation
and with regeneration.
"""
@@ -21,7 +21,7 @@ os.environ['HOME'] = os.path.abspath('home')
test.run_gyp('all.gyp', chdir='src')
# After relocating, we should still be able to build (build file shouldn't
-# contain relative reference to ~/.gyp/includes.gypi)
+# contain relative reference to ~/.gyp/include.gypi)
test.relocate('src', 'relocate/src')
test.build('all.gyp', test.ALL, chdir='relocate/src')
@@ -30,7 +30,7 @@ test.run_built_executable('printfoo',
chdir='relocate/src',
stdout='FOO is fromhome\n')
-# Building should notice any changes to ~/.gyp/includes.gypi and regyp.
+# Building should notice any changes to ~/.gyp/include.gypi and regyp.
test.sleep()
test.write('home/.gyp/include.gypi', test.read('home2/.gyp/include.gypi'))
diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py
index 3131e2d248..8ad52556be 100755
--- a/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py
+++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py
@@ -5,7 +5,7 @@
# found in the LICENSE file.
"""
-Verifies inclusion of $HOME/.gyp/includes.gypi works.
+Verifies inclusion of $HOME/.gyp/include.gypi works.
"""
import os
@@ -18,7 +18,7 @@ os.environ['HOME'] = os.path.abspath('home')
test.run_gyp('all.gyp', chdir='src')
# After relocating, we should still be able to build (build file shouldn't
-# contain relative reference to ~/.gyp/includes.gypi)
+# contain relative reference to ~/.gyp/include.gypi)
test.relocate('src', 'relocate/src')
test.build('all.gyp', test.ALL, chdir='relocate/src')
diff --git a/tools/gyp/test/lib/TestGyp.py b/tools/gyp/test/lib/TestGyp.py
index 2b4b0db967..7fbb56bfac 100644
--- a/tools/gyp/test/lib/TestGyp.py
+++ b/tools/gyp/test/lib/TestGyp.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -222,7 +222,11 @@ class TestGypBase(TestCommon.TestCommon):
Runs gyp against the specified gyp_file with the specified args.
"""
# TODO: --depth=. works around Chromium-specific tree climbing.
- args = ('--depth=.', '--format='+self.format, gyp_file) + args
+ depth = '.'
+ if 'depth' in kw:
+ depth = kw['depth']
+ del kw['depth']
+ args = ('--depth='+depth, '--format='+self.format, gyp_file) + args
return self.run(program=self.gyp, arguments=args, **kw)
def run(self, *args, **kw):
@@ -415,24 +419,10 @@ class TestGypNinja(TestGypBase):
ALL = 'all'
DEFAULT = 'all'
- # The default library prefix is computed from TestCommon.lib_prefix,
- # but ninja uses no prefix for static libraries.
- lib_ = ''
-
def run_gyp(self, gyp_file, *args, **kw):
- # We must pass the desired configuration as a parameter.
- if self.configuration:
- args = list(args) + ['-Gconfig=' + self.configuration]
- # Stash the gyp configuration we used to run gyp, so we can
- # know whether we need to rerun it later.
- self.last_gyp_configuration = self.configuration
TestGypBase.run_gyp(self, gyp_file, *args, **kw)
def build(self, gyp_file, target=None, **kw):
- if self.last_gyp_configuration != self.configuration:
- # Rerun gyp if necessary.
- self.run_gyp(gyp_file)
-
arguments = kw.get('arguments', [])[:]
# Add a -C output/path to the command line.
@@ -450,9 +440,9 @@ class TestGypNinja(TestGypBase):
# Enclosing the name in a list avoids prepending the original dir.
program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
if sys.platform == 'darwin':
- libdir = os.path.join('out', 'Default', 'lib')
+ libdir = os.path.join('out', 'Default')
if self.configuration:
- libdir = os.path.join('out', self.configuration, 'lib')
+ libdir = os.path.join('out', self.configuration)
os.environ['DYLD_LIBRARY_PATH'] = libdir
return self.run(program=program, *args, **kw)
@@ -467,7 +457,8 @@ class TestGypNinja(TestGypBase):
if sys.platform != 'darwin':
result.append('obj')
elif type == self.SHARED_LIB:
- result.append('lib')
+ if sys.platform != 'darwin':
+ result.append('lib')
subdir = kw.get('subdir')
if subdir:
result.append(subdir)
diff --git a/tools/gyp/test/mac/action-envvars/action/action.gyp b/tools/gyp/test/mac/action-envvars/action/action.gyp
new file mode 100644
index 0000000000..d9d65745ca
--- /dev/null
+++ b/tools/gyp/test/mac/action-envvars/action/action.gyp
@@ -0,0 +1,34 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'action',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'inputs': [ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/result',
+ '<(SHARED_INTERMEDIATE_DIR)/tempfile',
+ ],
+ 'action_name': 'Test action',
+ 'action': ['./action.sh', '<(SHARED_INTERMEDIATE_DIR)/tempfile' ],
+ },
+ {
+ 'inputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/tempfile',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/other_result',
+ ],
+ 'action_name': 'Other test action',
+ 'action': ['cp', '<(SHARED_INTERMEDIATE_DIR)/tempfile',
+ '<(PRODUCT_DIR)/other_result' ],
+ },
+ ],
+ },
+ ],
+}
+
diff --git a/tools/gyp/test/mac/action-envvars/action/action.sh b/tools/gyp/test/mac/action-envvars/action/action.sh
new file mode 100755
index 0000000000..48d5f6bf86
--- /dev/null
+++ b/tools/gyp/test/mac/action-envvars/action/action.sh
@@ -0,0 +1,8 @@
+# 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.
+
+set -e
+
+echo 'Test output' > "${BUILT_PRODUCTS_DIR}/result"
+echo 'Other output' > "$1"
diff --git a/tools/gyp/test/mac/archs/my_file.cc b/tools/gyp/test/mac/archs/my_file.cc
new file mode 100644
index 0000000000..94216a74df
--- /dev/null
+++ b/tools/gyp/test/mac/archs/my_file.cc
@@ -0,0 +1,4 @@
+/* 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. */
+int x = 1;
diff --git a/tools/gyp/test/mac/archs/my_main_file.cc b/tools/gyp/test/mac/archs/my_main_file.cc
new file mode 100644
index 0000000000..f1fa06f276
--- /dev/null
+++ b/tools/gyp/test/mac/archs/my_main_file.cc
@@ -0,0 +1,9 @@
+/* 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. */
+#include <stdio.h>
+extern int x;
+int main() {
+ printf("hello, world %d\n", x);
+}
+
diff --git a/tools/gyp/test/mac/archs/test-archs-x86_64.gyp b/tools/gyp/test/mac/archs/test-archs-x86_64.gyp
new file mode 100644
index 0000000000..d11a896273
--- /dev/null
+++ b/tools/gyp/test/mac/archs/test-archs-x86_64.gyp
@@ -0,0 +1,27 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lib',
+ 'product_name': 'Test64',
+ 'type': 'static_library',
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'exe',
+ 'product_name': 'Test64',
+ 'type': 'executable',
+ 'dependencies': [ 'lib' ],
+ 'sources': [ 'my_main_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'x86_64' ],
+ },
+ },
+ ]
+}
diff --git a/tools/gyp/test/mac/archs/test-no-archs.gyp b/tools/gyp/test/mac/archs/test-no-archs.gyp
new file mode 100644
index 0000000000..8f3b6b47cc
--- /dev/null
+++ b/tools/gyp/test/mac/archs/test-no-archs.gyp
@@ -0,0 +1,21 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lib',
+ 'product_name': 'Test',
+ 'type': 'static_library',
+ 'sources': [ 'my_file.cc' ],
+ },
+ {
+ 'target_name': 'exe',
+ 'product_name': 'Test',
+ 'type': 'executable',
+ 'dependencies': [ 'lib' ],
+ 'sources': [ 'my_main_file.cc' ],
+ },
+ ]
+}
diff --git a/tools/gyp/test/mac/copy-dylib/empty.c b/tools/gyp/test/mac/copy-dylib/empty.c
new file mode 100644
index 0000000000..237c8ce181
--- /dev/null
+++ b/tools/gyp/test/mac/copy-dylib/empty.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/copy-dylib/test.gyp b/tools/gyp/test/mac/copy-dylib/test.gyp
new file mode 100644
index 0000000000..4210c51463
--- /dev/null
+++ b/tools/gyp/test/mac/copy-dylib/test.gyp
@@ -0,0 +1,31 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'my_dylib',
+ 'type': 'shared_library',
+ 'sources': [ 'empty.c', ],
+ },
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'dependencies': [ 'my_dylib', ],
+ 'sources': [
+ 'empty.c',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/Test App.app/Contents/Resources',
+ 'files': [
+ '<(PRODUCT_DIR)/libmy_dylib.dylib',
+ ],
+ },
+ ],
+ },
+ ],
+}
+
diff --git a/tools/gyp/test/mac/depend-on-bundle/English.lproj/InfoPlist.strings b/tools/gyp/test/mac/depend-on-bundle/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..b92732c79e
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/English.lproj/InfoPlist.strings
@@ -0,0 +1 @@
+/* Localized versions of Info.plist keys */
diff --git a/tools/gyp/test/mac/depend-on-bundle/Info.plist b/tools/gyp/test/mac/depend-on-bundle/Info.plist
new file mode 100644
index 0000000000..5e05a5190c
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
diff --git a/tools/gyp/test/mac/depend-on-bundle/bundle.c b/tools/gyp/test/mac/depend-on-bundle/bundle.c
new file mode 100644
index 0000000000..d64ff8ca23
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/bundle.c
@@ -0,0 +1 @@
+int f() { return 42; }
diff --git a/tools/gyp/test/mac/depend-on-bundle/executable.c b/tools/gyp/test/mac/depend-on-bundle/executable.c
new file mode 100644
index 0000000000..931bce637e
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/executable.c
@@ -0,0 +1,4 @@
+int f();
+int main() {
+ return f();
+}
diff --git a/tools/gyp/test/mac/depend-on-bundle/test.gyp b/tools/gyp/test/mac/depend-on-bundle/test.gyp
new file mode 100644
index 0000000000..e00b105415
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/test.gyp
@@ -0,0 +1,28 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'my_bundle',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'bundle.c' ],
+ 'mac_bundle_resources': [
+ 'English.lproj/InfoPlist.strings',
+ ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ }
+ },
+ {
+ 'target_name': 'dependent_on_bundle',
+ 'type': 'executable',
+ 'sources': [ 'executable.c' ],
+ 'dependencies': [
+ 'my_bundle',
+ ],
+ },
+ ],
+}
+
diff --git a/tools/gyp/test/mac/global-settings/src/dir1/dir1.gyp b/tools/gyp/test/mac/global-settings/src/dir1/dir1.gyp
new file mode 100644
index 0000000000..153e34ddd6
--- /dev/null
+++ b/tools/gyp/test/mac/global-settings/src/dir1/dir1.gyp
@@ -0,0 +1,11 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'dir1_target',
+ 'type': 'none',
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/global-settings/src/dir2/dir2.gyp b/tools/gyp/test/mac/global-settings/src/dir2/dir2.gyp
new file mode 100644
index 0000000000..cda46c839b
--- /dev/null
+++ b/tools/gyp/test/mac/global-settings/src/dir2/dir2.gyp
@@ -0,0 +1,22 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'dir2_target',
+ 'type': 'none',
+ 'dependencies': [
+ '../dir1/dir1.gyp:dir1_target',
+ ],
+ 'actions': [
+ {
+ 'inputs': [ ],
+ 'outputs': [ '<(PRODUCT_DIR)/file.txt' ],
+ 'action_name': 'Test action',
+ 'action': ['cp', 'file.txt', '${BUILT_PRODUCTS_DIR}/file.txt' ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/global-settings/src/dir2/file.txt b/tools/gyp/test/mac/global-settings/src/dir2/file.txt
new file mode 100644
index 0000000000..58da2d8e9a
--- /dev/null
+++ b/tools/gyp/test/mac/global-settings/src/dir2/file.txt
@@ -0,0 +1 @@
+File.
diff --git a/tools/gyp/test/mac/gyptest-action-envvars.py b/tools/gyp/test/mac/gyptest-action-envvars.py
new file mode 100644
index 0000000000..b4f37c43a2
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-action-envvars.py
@@ -0,0 +1,30 @@
+#!/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.
+
+"""
+Verifies that env vars work with actions, with relative directory paths.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'action-envvars'
+ test.run_gyp('action/action.gyp', chdir=CHDIR)
+ test.build('action/action.gyp', 'action', chdir=CHDIR, SYMROOT='../build')
+
+ result_file = test.built_file_path('result', chdir=CHDIR)
+ test.must_exist(result_file)
+ test.must_contain(result_file, 'Test output')
+
+ other_result_file = test.built_file_path('other_result', chdir=CHDIR)
+ test.must_exist(other_result_file)
+ test.must_contain(other_result_file, 'Other output')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-app.py b/tools/gyp/test/mac/gyptest-app.py
index 5e772f8144..bce48f8938 100755
--- a/tools/gyp/test/mac/gyptest-app.py
+++ b/tools/gyp/test/mac/gyptest-app.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='app-bundle')
diff --git a/tools/gyp/test/mac/gyptest-archs.py b/tools/gyp/test/mac/gyptest-archs.py
new file mode 100644
index 0000000000..781e9ef169
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-archs.py
@@ -0,0 +1,37 @@
+#!/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.
+
+"""
+Tests things related to ARCHS.
+"""
+
+import TestGyp
+
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ def CheckFileType(file, expected):
+ proc = subprocess.Popen(['file', '-b', file], stdout=subprocess.PIPE)
+ o = proc.communicate()[0].strip()
+ assert not proc.returncode
+ if o != expected:
+ print 'File: Expected %s, got %s' % (expected, o)
+ test.fail_test()
+
+ test.run_gyp('test-no-archs.gyp', chdir='archs')
+ test.build('test-no-archs.gyp', test.ALL, chdir='archs')
+ result_file = test.built_file_path('Test', chdir='archs')
+ test.must_exist(result_file)
+ CheckFileType(result_file, 'Mach-O executable i386')
+
+ 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')
+ test.must_exist(result_file)
+ CheckFileType(result_file, 'Mach-O 64-bit executable x86_64')
diff --git a/tools/gyp/test/mac/gyptest-copies.py b/tools/gyp/test/mac/gyptest-copies.py
index 091b1dec5b..c88065eade 100755
--- a/tools/gyp/test/mac/gyptest-copies.py
+++ b/tools/gyp/test/mac/gyptest-copies.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -15,7 +15,7 @@ import sys
import time
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('framework.gyp', chdir='framework')
diff --git a/tools/gyp/test/mac/gyptest-copy-dylib.py b/tools/gyp/test/mac/gyptest-copy-dylib.py
new file mode 100644
index 0000000000..253623d1c6
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-copy-dylib.py
@@ -0,0 +1,25 @@
+#!/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.
+
+"""
+Verifies that dylibs can be copied into app bundles.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='copy-dylib')
+
+ test.build('test.gyp', 'test_app', chdir='copy-dylib')
+
+ test.built_file_must_exist(
+ 'Test App.app/Contents/Resources/libmy_dylib.dylib', chdir='copy-dylib')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-debuginfo.py b/tools/gyp/test/mac/gyptest-debuginfo.py
index 1ec2d1da10..a0e9438e2a 100755
--- a/tools/gyp/test/mac/gyptest-debuginfo.py
+++ b/tools/gyp/test/mac/gyptest-debuginfo.py
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='debuginfo')
diff --git a/tools/gyp/test/mac/gyptest-depend-on-bundle.py b/tools/gyp/test/mac/gyptest-depend-on-bundle.py
new file mode 100644
index 0000000000..5cccb03227
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-depend-on-bundle.py
@@ -0,0 +1,40 @@
+#!/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.
+
+"""
+Verifies that a dependency on a bundle causes the whole bundle to be built.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='depend-on-bundle')
+
+ test.build('test.gyp', 'dependent_on_bundle', chdir='depend-on-bundle')
+
+ # Binary itself.
+ test.built_file_must_exist('dependent_on_bundle', chdir='depend-on-bundle')
+
+ # Bundle dependency.
+ test.built_file_must_exist(
+ 'my_bundle.framework/Versions/A/my_bundle',
+ chdir='depend-on-bundle')
+ test.built_file_must_exist( # package_framework
+ 'my_bundle.framework/my_bundle',
+ chdir='depend-on-bundle')
+ test.built_file_must_exist( # plist
+ 'my_bundle.framework/Versions/A/Resources/Info.plist',
+ chdir='depend-on-bundle')
+ test.built_file_must_exist(
+ 'my_bundle.framework/Versions/A/Resources/English.lproj/' # Resources
+ 'InfoPlist.strings',
+ chdir='depend-on-bundle')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-framework.py b/tools/gyp/test/mac/gyptest-framework.py
index 6c19e40b33..e4342d8420 100755
--- a/tools/gyp/test/mac/gyptest-framework.py
+++ b/tools/gyp/test/mac/gyptest-framework.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('framework.gyp', chdir='framework')
diff --git a/tools/gyp/test/mac/gyptest-global-settings.py b/tools/gyp/test/mac/gyptest-global-settings.py
new file mode 100644
index 0000000000..648d32cded
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-global-settings.py
@@ -0,0 +1,26 @@
+#!/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.
+
+"""
+Verifies that the global xcode_settings processing doesn't throw.
+Regression test for http://crbug.com/109163
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+ test.run_gyp('src/dir2/dir2.gyp', chdir='global-settings', depth='src')
+ # run_gyp shouldn't throw.
+
+ # Check that BUILT_PRODUCTS_DIR was set correctly, too.
+ test.build('dir2/dir2.gyp', 'dir2_target', chdir='global-settings/src',
+ SYMROOT='../build')
+ test.built_file_must_exist('file.txt', chdir='global-settings/src')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-infoplist-process.py b/tools/gyp/test/mac/gyptest-infoplist-process.py
index 9a2b140d31..20874a398a 100755
--- a/tools/gyp/test/mac/gyptest-infoplist-process.py
+++ b/tools/gyp/test/mac/gyptest-infoplist-process.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
CHDIR = 'infoplist-process'
INFO_PLIST_PATH = 'Test.app/Contents/Info.plist'
diff --git a/tools/gyp/test/mac/gyptest-libraries.py b/tools/gyp/test/mac/gyptest-libraries.py
new file mode 100755
index 0000000000..46814d65af
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-libraries.py
@@ -0,0 +1,22 @@
+#!/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.
+
+"""
+Verifies libraries (in link_settings) are properly found.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('subdir/test.gyp', chdir='libraries')
+
+ test.build('subdir/test.gyp', test.ALL, chdir='libraries')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-loadable-module.py b/tools/gyp/test/mac/gyptest-loadable-module.py
index 39e87f0605..e5e022c9fb 100755
--- a/tools/gyp/test/mac/gyptest-loadable-module.py
+++ b/tools/gyp/test/mac/gyptest-loadable-module.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -14,7 +14,7 @@ import os
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='loadable-module')
test.build('test.gyp', test.ALL, chdir='loadable-module')
diff --git a/tools/gyp/test/mac/gyptest-non-strs-flattened-to-env.py b/tools/gyp/test/mac/gyptest-non-strs-flattened-to-env.py
new file mode 100644
index 0000000000..504dcd589b
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-non-strs-flattened-to-env.py
@@ -0,0 +1,33 @@
+#!/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.
+
+"""
+Verifies that list xcode_settings are flattened before being exported to the
+environment.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'non-strs-flattened-to-env'
+ INFO_PLIST_PATH = 'Test.app/Contents/Info.plist'
+
+ test.run_gyp('test.gyp', chdir=CHDIR)
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+ info_plist = test.built_file_path(INFO_PLIST_PATH, chdir=CHDIR)
+ test.must_exist(info_plist)
+ test.must_contain(info_plist, '''\
+\t<key>My Variable</key>
+\t<string>some expansion</string>''')
+ test.must_contain(info_plist, '''\
+\t<key>CFlags</key>
+\t<string>-fstack-protector-all -fno-strict-aliasing -DS="A Space"</string>''')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-postbuild-copy-bundle.py b/tools/gyp/test/mac/gyptest-postbuild-copy-bundle.py
new file mode 100644
index 0000000000..0f63ad5241
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-copy-bundle.py
@@ -0,0 +1,62 @@
+#!/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.
+
+"""
+Verifies that a postbuild copying a dependend framework into an app bundle is
+rerun if the resources in the framework change.
+"""
+
+import TestGyp
+
+import os.path
+import sys
+
+if sys.platform == 'darwin':
+ # TODO(thakis): Make this pass with the make generator, http://crbug.com/95529
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ CHDIR = 'postbuild-copy-bundle'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ app_bundle_dir = test.built_file_path('Test app.app', chdir=CHDIR)
+ bundled_framework_dir = os.path.join(
+ app_bundle_dir, 'Contents', 'My Framework.framework', 'Resources')
+ final_plist_path = os.path.join(bundled_framework_dir, 'Info.plist')
+ final_resource_path = os.path.join(bundled_framework_dir, 'resource_file.sb')
+
+ # Check that the dependency was built and copied into the app bundle:
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
+ test.must_exist(final_resource_path)
+ test.must_match(final_resource_path,
+ 'This is included in the framework bundle.\n')
+
+ test.must_exist(final_plist_path)
+ test.must_contain(final_plist_path, '''\
+\t<key>RandomKey</key>
+\t<string>RandomValue</string>''')
+
+ # Touch the dependency's bundle resource, and check that the modification
+ # makes it all the way into the app bundle:
+ test.sleep()
+ test.write('postbuild-copy-bundle/resource_file.sb', 'New text\n')
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
+
+ test.must_exist(final_resource_path)
+ test.must_match(final_resource_path, 'New text\n')
+
+ # Check the same for the plist file.
+ test.sleep()
+ contents = test.read('postbuild-copy-bundle/Framework-Info.plist')
+ contents = contents.replace('RandomValue', 'NewRandomValue')
+ test.write('postbuild-copy-bundle/Framework-Info.plist', contents)
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
+
+ test.must_exist(final_plist_path)
+ test.must_contain(final_plist_path, '''\
+\t<key>RandomKey</key>
+\t<string>NewRandomValue</string>''')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-postbuild-defaults.py b/tools/gyp/test/mac/gyptest-postbuild-defaults.py
new file mode 100644
index 0000000000..0560904c29
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-defaults.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.
+
+"""
+Verifies that a postbuild invoking |defaults| works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'postbuild-defaults'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ result_file = test.built_file_path('result', chdir=CHDIR)
+ test.must_exist(result_file)
+ test.must_contain(result_file, '''\
+Test
+${PRODUCT_NAME}
+''')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-postbuild-fail.py b/tools/gyp/test/mac/gyptest-postbuild-fail.py
index 28b1a8061f..dba0d6f042 100755
--- a/tools/gyp/test/mac/gyptest-postbuild-fail.py
+++ b/tools/gyp/test/mac/gyptest-postbuild-fail.py
@@ -14,13 +14,15 @@ import sys
if sys.platform == 'darwin':
# set |match| to ignore build stderr output.
- test = TestGyp.TestGyp(formats=['make', 'xcode'], match = lambda a, b: True)
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'],
+ match = lambda a, b: True)
test.run_gyp('test.gyp', chdir='postbuild-fail')
build_error_code = {
'xcode': 1,
'make': 2,
+ 'ninja': 1,
}[test.format]
diff --git a/tools/gyp/test/mac/gyptest-postbuild-multiple-configurations.py b/tools/gyp/test/mac/gyptest-postbuild-multiple-configurations.py
new file mode 100644
index 0000000000..84694f36cc
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-multiple-configurations.py
@@ -0,0 +1,26 @@
+#!/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.
+
+"""
+Verifies that a postbuild work in projects with multiple configurations.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'postbuild-multiple-configurations'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ for configuration in ['Debug', 'Release']:
+ test.set_configuration(configuration)
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+ test.built_file_must_exist('postbuild-file', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-postbuild-static-library.gyp b/tools/gyp/test/mac/gyptest-postbuild-static-library.gyp
new file mode 100644
index 0000000000..8f9a6ebcb0
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-static-library.gyp
@@ -0,0 +1,28 @@
+#!/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.
+
+"""
+Verifies that a postbuilds on static libraries work, and that sourceless
+libraries don't cause failures at gyp time.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['make', 'xcode'])
+
+ CHDIR = 'postbuild-static-library'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+ test.build('test.gyp', 'my_lib', chdir=CHDIR)
+ # Building my_sourceless_lib doesn't work with make. gyp should probably
+ # forbid sourceless static libraries, since they're pretty pointless.
+ # But they shouldn't cause gyp time exceptions.
+
+ test.built_file_must_exist('postbuild-file', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-postbuild.py b/tools/gyp/test/mac/gyptest-postbuild.py
index b69a0bdfea..d1dcba7158 100755
--- a/tools/gyp/test/mac/gyptest-postbuild.py
+++ b/tools/gyp/test/mac/gyptest-postbuild.py
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='postbuilds')
diff --git a/tools/gyp/test/mac/gyptest-prefixheader.py b/tools/gyp/test/mac/gyptest-prefixheader.py
index 04b00f6b13..768551f9b0 100755
--- a/tools/gyp/test/mac/gyptest-prefixheader.py
+++ b/tools/gyp/test/mac/gyptest-prefixheader.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='prefixheader')
test.build('test.gyp', test.ALL, chdir='prefixheader')
test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-rebuild.py b/tools/gyp/test/mac/gyptest-rebuild.py
index f88dcb94b2..c130bd32af 100755
--- a/tools/gyp/test/mac/gyptest-rebuild.py
+++ b/tools/gyp/test/mac/gyptest-rebuild.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -14,16 +14,29 @@ import os
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
- test.run_gyp('test.gyp', chdir='app-bundle')
+ CHDIR = 'rebuild'
+ test.run_gyp('test.gyp', chdir=CHDIR)
- test.build('test.gyp', test.ALL, chdir='app-bundle')
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
# Touch a source file, rebuild, and check that the app target is up-to-date.
- os.utime('app-bundle/TestApp/main.m', None)
- test.build('test.gyp', test.ALL, chdir='app-bundle')
-
- test.up_to_date('test.gyp', test.ALL, chdir='app-bundle')
+ test.touch('rebuild/main.c')
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
+
+ test.up_to_date('test.gyp', 'test_app', chdir=CHDIR)
+
+ # Xcode runs postbuilds on every build, so targets with postbuilds are
+ # never marked as up_to_date.
+ if test.format != 'xcode':
+ # Same for a framework bundle.
+ test.build('test.gyp', 'test_framework_postbuilds', chdir=CHDIR)
+ test.up_to_date('test.gyp', 'test_framework_postbuilds', chdir=CHDIR)
+
+ # Test that an app bundle with a postbuild that touches the app binary needs
+ # to be built only once.
+ test.build('test.gyp', 'test_app_postbuilds', chdir=CHDIR)
+ test.up_to_date('test.gyp', 'test_app_postbuilds', chdir=CHDIR)
test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-sourceless-module.gyp b/tools/gyp/test/mac/gyptest-sourceless-module.gyp
new file mode 100644
index 0000000000..c3ea73a171
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-sourceless-module.gyp
@@ -0,0 +1,46 @@
+#!/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.
+
+"""
+Verifies that bundles that have no 'sources' (pure resource containers) work.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='sourceless-module')
+
+ # Just needs to build without errors.
+ test.build('test.gyp', 'empty_bundle', chdir='sourceless-module')
+ test.built_file_must_not_exist(
+ 'empty_bundle.bundle', chdir='sourceless-module')
+
+ # Needs to build, and contain a resource.
+ test.build('test.gyp', 'resource_bundle', chdir='sourceless-module')
+
+ test.built_file_must_exist(
+ 'resource_bundle.bundle/Contents/Resources/foo.manifest',
+ chdir='sourceless-module')
+ test.built_file_must_not_exist(
+ 'resource_bundle.bundle/Contents/MacOS/resource_bundle',
+ chdir='sourceless-module')
+
+ # Needs to build and cause the bundle to be built.
+ test.build(
+ 'test.gyp', 'dependent_on_resource_bundle', chdir='sourceless-module')
+
+ test.built_file_must_exist(
+ 'resource_bundle.bundle/Contents/Resources/foo.manifest',
+ chdir='sourceless-module')
+ test.built_file_must_not_exist(
+ 'resource_bundle.bundle/Contents/MacOS/resource_bundle',
+ chdir='sourceless-module')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-strip.py b/tools/gyp/test/mac/gyptest-strip.py
index d031d4b3d8..7d4abc6004 100755
--- a/tools/gyp/test/mac/gyptest-strip.py
+++ b/tools/gyp/test/mac/gyptest-strip.py
@@ -17,7 +17,7 @@ import sys
import time
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='strip')
diff --git a/tools/gyp/test/mac/gyptest-type-envvars.py b/tools/gyp/test/mac/gyptest-type-envvars.py
index 1ef677319f..61596bae23 100755
--- a/tools/gyp/test/mac/gyptest-type-envvars.py
+++ b/tools/gyp/test/mac/gyptest-type-envvars.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,7 +13,7 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
test.run_gyp('test.gyp', chdir='type_envvars')
diff --git a/tools/gyp/test/mac/gyptest-xcode-env-order.py b/tools/gyp/test/mac/gyptest-xcode-env-order.py
index 6459373247..d1c8542876 100755
--- a/tools/gyp/test/mac/gyptest-xcode-env-order.py
+++ b/tools/gyp/test/mac/gyptest-xcode-env-order.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,18 +13,65 @@ import TestGyp
import sys
if sys.platform == 'darwin':
- test = TestGyp.TestGyp(formats=['make', 'xcode'])
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
CHDIR = 'xcode-env-order'
INFO_PLIST_PATH = 'Test.app/Contents/Info.plist'
test.run_gyp('test.gyp', chdir=CHDIR)
test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ # Env vars in 'copies' filenames.
+ test.built_file_must_exist('Test-copy-brace/main.c', chdir=CHDIR)
+ test.built_file_must_exist('Test-copy-paren/main.c', chdir=CHDIR)
+ test.built_file_must_exist('Test-copy-bare/main.c', chdir=CHDIR)
+
+ # Env vars in 'actions' filenames and inline actions
+ test.built_file_must_exist('action-copy-brace.txt', chdir=CHDIR)
+ test.built_file_must_exist('action-copy-paren.txt', chdir=CHDIR)
+ test.built_file_must_exist('action-copy-bare.txt', chdir=CHDIR)
+
+ # Env vars in Info.plist.
info_plist = test.built_file_path(INFO_PLIST_PATH, chdir=CHDIR)
test.must_exist(info_plist)
- test.must_contain(info_plist, '>/Source/Project/Test')
- test.must_contain(info_plist, '>DEP:/Source/Project/Test')
- test.must_contain(info_plist,
- '>com.apple.product-type.application:DEP:/Source/Project/Test')
+
+ test.must_contain(info_plist, '''\
+\t<key>BraceProcessedKey1</key>
+\t<string>D:/Source/Project/Test</string>''')
+ test.must_contain(info_plist, '''\
+\t<key>BraceProcessedKey2</key>
+\t<string>/Source/Project/Test</string>''')
+ test.must_contain(info_plist, '''\
+\t<key>BraceProcessedKey3</key>
+\t<string>com.apple.product-type.application:D:/Source/Project/Test</string>''')
+
+ test.must_contain(info_plist, '''\
+\t<key>ParenProcessedKey1</key>
+\t<string>D:/Source/Project/Test</string>''')
+ test.must_contain(info_plist, '''\
+\t<key>ParenProcessedKey2</key>
+\t<string>/Source/Project/Test</string>''')
+ test.must_contain(info_plist, '''\
+\t<key>ParenProcessedKey3</key>
+\t<string>com.apple.product-type.application:D:/Source/Project/Test</string>''')
+
+ test.must_contain(info_plist, '''\
+\t<key>BareProcessedKey1</key>
+\t<string>D:/Source/Project/Test</string>''')
+ test.must_contain(info_plist, '''\
+\t<key>BareProcessedKey2</key>
+\t<string>/Source/Project/Test</string>''')
+ # NOTE: For bare variables, $PRODUCT_TYPE is not replaced! It _is_ replaced
+ # if it's not right at the start of the string (e.g. ':$PRODUCT_TYPE'), so
+ # this looks like an Xcode bug. This bug isn't emulated (yet?), so check this
+ # only for Xcode.
+ if test.format == 'xcode':
+ test.must_contain(info_plist, '''\
+\t<key>BareProcessedKey3</key>
+\t<string>$PRODUCT_TYPE:D:/Source/Project/Test</string>''')
+
+ test.must_contain(info_plist, '''\
+\t<key>MixedProcessedKey</key>
+\t<string>/Source/Project:Test:mh_execute</string>''')
test.pass_test()
diff --git a/tools/gyp/test/mac/libraries/subdir/README.txt b/tools/gyp/test/mac/libraries/subdir/README.txt
new file mode 100644
index 0000000000..4031ded85f
--- /dev/null
+++ b/tools/gyp/test/mac/libraries/subdir/README.txt
@@ -0,0 +1 @@
+Make things live in a subdirectory, to make sure that DEPTH works correctly.
diff --git a/tools/gyp/test/mac/libraries/subdir/hello.cc b/tools/gyp/test/mac/libraries/subdir/hello.cc
new file mode 100644
index 0000000000..a43554c8ca
--- /dev/null
+++ b/tools/gyp/test/mac/libraries/subdir/hello.cc
@@ -0,0 +1,10 @@
+// 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.
+
+#include <iostream>
+
+int main() {
+ std::cout << "Hello, world!" << std::endl;
+ return 0;
+}
diff --git a/tools/gyp/test/mac/libraries/subdir/mylib.c b/tools/gyp/test/mac/libraries/subdir/mylib.c
new file mode 100644
index 0000000000..e771991e83
--- /dev/null
+++ b/tools/gyp/test/mac/libraries/subdir/mylib.c
@@ -0,0 +1,7 @@
+// 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.
+
+int my_foo(int x) {
+ return x + 1;
+}
diff --git a/tools/gyp/test/mac/libraries/subdir/test.gyp b/tools/gyp/test/mac/libraries/subdir/test.gyp
new file mode 100644
index 0000000000..80a0269772
--- /dev/null
+++ b/tools/gyp/test/mac/libraries/subdir/test.gyp
@@ -0,0 +1,66 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'libraries-test',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.cc',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ 'libcrypto.dylib',
+ 'libfl.a',
+ ],
+ },
+ },
+ {
+ # This creates a static library and puts it in a nonstandard location for
+ # libraries-search-path-test.
+ 'target_name': 'mylib',
+ 'type': 'static_library',
+ 'sources': [
+ 'mylib.c',
+ ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Make a secret location',
+ 'action': [
+ 'mkdir',
+ '-p',
+ '${SRCROOT}/../secret_location',
+ ],
+ },
+ {
+ 'postbuild_name': 'Copy to secret location, with secret name',
+ 'action': [
+ 'cp',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ '${SRCROOT}/../secret_location/libmysecretlib.a',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'libraries-search-path-test',
+ 'type': 'executable',
+ 'dependencies': [ 'mylib' ],
+ 'sources': [
+ 'hello.cc',
+ ],
+ 'xcode_settings': {
+ 'LIBRARY_SEARCH_PATHS': [
+ '<(DEPTH)/secret_location',
+ ],
+ },
+ 'link_settings': {
+ 'libraries': [
+ 'libmysecretlib.a',
+ ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/non-strs-flattened-to-env/Info.plist b/tools/gyp/test/mac/non-strs-flattened-to-env/Info.plist
new file mode 100644
index 0000000000..11fc4b660d
--- /dev/null
+++ b/tools/gyp/test/mac/non-strs-flattened-to-env/Info.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <!-- Not a valid plist file since it's missing so much. That's fine. -->
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>My Variable</key>
+ <string>${MY_VAR}</string>
+ <key>CFlags</key>
+ <string>${OTHER_CFLAGS}</string>
+</dict>
+</plist>
diff --git a/tools/gyp/test/mac/non-strs-flattened-to-env/main.c b/tools/gyp/test/mac/non-strs-flattened-to-env/main.c
new file mode 100644
index 0000000000..1711567ef5
--- /dev/null
+++ b/tools/gyp/test/mac/non-strs-flattened-to-env/main.c
@@ -0,0 +1,7 @@
+// 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.
+
+int main() {
+ return 0;
+}
diff --git a/tools/gyp/test/mac/non-strs-flattened-to-env/test.gyp b/tools/gyp/test/mac/non-strs-flattened-to-env/test.gyp
new file mode 100644
index 0000000000..58814b73f6
--- /dev/null
+++ b/tools/gyp/test/mac/non-strs-flattened-to-env/test.gyp
@@ -0,0 +1,24 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'main.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ 'MY_VAR': 'some expansion',
+ 'OTHER_CFLAGS': [
+ # Just some (more than one) random flags.
+ '-fstack-protector-all',
+ '-fno-strict-aliasing',
+ '-DS="A Space"', # Would normally be in 'defines'
+ ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/Framework-Info.plist b/tools/gyp/test/mac/postbuild-copy-bundle/Framework-Info.plist
new file mode 100644
index 0000000000..ec36829c08
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/Framework-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+ <key>RandomKey</key>
+ <string>RandomValue</string>
+</dict>
+</plist>
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/TestApp-Info.plist b/tools/gyp/test/mac/postbuild-copy-bundle/TestApp-Info.plist
new file mode 100644
index 0000000000..98fd515200
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/TestApp-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.google.${PRODUCT_NAME}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/empty.c b/tools/gyp/test/mac/postbuild-copy-bundle/empty.c
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/empty.c
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/main.c b/tools/gyp/test/mac/postbuild-copy-bundle/main.c
new file mode 100644
index 0000000000..21c1963526
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/main.c
@@ -0,0 +1,4 @@
+// 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.
+int main() {}
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/postbuild-copy-framework.sh b/tools/gyp/test/mac/postbuild-copy-bundle/postbuild-copy-framework.sh
new file mode 100755
index 0000000000..930fec6612
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/postbuild-copy-framework.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# 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.
+
+set -e
+
+rsync -acC --delete "$1" "$2"
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/resource_file.sb b/tools/gyp/test/mac/postbuild-copy-bundle/resource_file.sb
new file mode 100644
index 0000000000..42057fa235
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/resource_file.sb
@@ -0,0 +1 @@
+This is included in the framework bundle.
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/test.gyp b/tools/gyp/test/mac/postbuild-copy-bundle/test.gyp
new file mode 100644
index 0000000000..547737ce58
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/test.gyp
@@ -0,0 +1,43 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'test_bundle',
+ 'product_name': 'My Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'empty.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Framework-Info.plist',
+ },
+ 'mac_bundle_resources': [
+ 'resource_file.sb',
+ ],
+ },
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'dependencies': [
+ 'test_bundle',
+ ],
+ 'sources': [ 'main.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestApp-Info.plist',
+ },
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Copy dependent framework into app',
+ 'action': [
+ './postbuild-copy-framework.sh',
+ '${BUILT_PRODUCTS_DIR}/My Framework.framework',
+ '${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/postbuild-defaults/Info.plist b/tools/gyp/test/mac/postbuild-defaults/Info.plist
new file mode 100644
index 0000000000..d3f54d76cd
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-defaults/Info.plist
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <!-- Not a valid plist file since it's missing so much. That's fine. -->
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+</dict>
+</plist>
diff --git a/tools/gyp/test/mac/postbuild-defaults/main.c b/tools/gyp/test/mac/postbuild-defaults/main.c
new file mode 100644
index 0000000000..1711567ef5
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-defaults/main.c
@@ -0,0 +1,7 @@
+// 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.
+
+int main() {
+ return 0;
+}
diff --git a/tools/gyp/test/mac/postbuild-defaults/postbuild-defaults.sh b/tools/gyp/test/mac/postbuild-defaults/postbuild-defaults.sh
new file mode 100755
index 0000000000..56af2a8329
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-defaults/postbuild-defaults.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+# This is the built Info.plist in the output directory.
+PLIST="${BUILT_PRODUCTS_DIR}"/Test.app/Contents/Info # No trailing .plist
+echo $(defaults read "${PLIST}" "CFBundleName") > "${BUILT_PRODUCTS_DIR}/result"
+
+# This is the source Info.plist next to this script file.
+PLIST="${SRCROOT}"/Info # No trailing .plist
+echo $(defaults read "${PLIST}" "CFBundleName") \
+ >> "${BUILT_PRODUCTS_DIR}/result"
diff --git a/tools/gyp/test/mac/postbuild-defaults/test.gyp b/tools/gyp/test/mac/postbuild-defaults/test.gyp
new file mode 100644
index 0000000000..be0a075efc
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-defaults/test.gyp
@@ -0,0 +1,26 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'main.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ },
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild that calls defaults',
+ 'action': [
+ './postbuild-defaults.sh',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh b/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh
index d4113d059e..dc1a60d987 100755
--- a/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh
+++ b/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh
@@ -1,4 +1,4 @@
-#!/usr/bash
+#!/usr/bin/bash
# Copyright (c) 2011 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.
diff --git a/tools/gyp/test/mac/postbuild-multiple-configurations/main.c b/tools/gyp/test/mac/postbuild-multiple-configurations/main.c
new file mode 100644
index 0000000000..21c1963526
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-multiple-configurations/main.c
@@ -0,0 +1,4 @@
+// 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.
+int main() {}
diff --git a/tools/gyp/test/mac/postbuild-multiple-configurations/postbuild-touch-file.sh b/tools/gyp/test/mac/postbuild-multiple-configurations/postbuild-touch-file.sh
new file mode 100755
index 0000000000..b6170cf7a7
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-multiple-configurations/postbuild-touch-file.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# 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.
+
+touch "${BUILT_PRODUCTS_DIR}/postbuild-file"
diff --git a/tools/gyp/test/mac/postbuild-multiple-configurations/test.gyp b/tools/gyp/test/mac/postbuild-multiple-configurations/test.gyp
new file mode 100644
index 0000000000..c350b20d68
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-multiple-configurations/test.gyp
@@ -0,0 +1,26 @@
+# 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.
+{
+ 'target_defaults': {
+ 'configurations': {
+ 'Debug': {},
+ 'Release': {},
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'random_target',
+ 'type': 'executable',
+ 'sources': [ 'main.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Touch a file.',
+ 'action': [
+ './postbuild-touch-file.sh',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/postbuild-static-library/empty.c b/tools/gyp/test/mac/postbuild-static-library/empty.c
new file mode 100644
index 0000000000..9554336c0c
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-static-library/empty.c
@@ -0,0 +1,4 @@
+// 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.
+void f() {}
diff --git a/tools/gyp/test/mac/postbuild-static-library/postbuild-touch-file.sh b/tools/gyp/test/mac/postbuild-static-library/postbuild-touch-file.sh
new file mode 100755
index 0000000000..37de4de4f6
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-static-library/postbuild-touch-file.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# 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.
+
+touch "${BUILT_PRODUCTS_DIR}/$1"
diff --git a/tools/gyp/test/mac/postbuild-static-library/test.gyp b/tools/gyp/test/mac/postbuild-static-library/test.gyp
new file mode 100644
index 0000000000..9ef55a0afa
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-static-library/test.gyp
@@ -0,0 +1,34 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'my_lib',
+ 'type': 'static_library',
+ 'sources': [ 'empty.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild that touches a file',
+ 'action': [
+ './postbuild-touch-file.sh', 'postbuild-file'
+ ],
+ },
+ ],
+ },
+
+ {
+ 'target_name': 'my_sourceless_lib',
+ 'type': 'static_library',
+ 'dependencies': [ 'my_lib' ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild that touches a file',
+ 'action': [
+ './postbuild-touch-file.sh', 'postbuild-file-sourceless'
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/prefixheader/file.cc b/tools/gyp/test/mac/prefixheader/file.cc
new file mode 100644
index 0000000000..d0b39d1f6d
--- /dev/null
+++ b/tools/gyp/test/mac/prefixheader/file.cc
@@ -0,0 +1 @@
+MyInt f() { return 0; }
diff --git a/tools/gyp/test/mac/prefixheader/file.m b/tools/gyp/test/mac/prefixheader/file.m
new file mode 100644
index 0000000000..d0b39d1f6d
--- /dev/null
+++ b/tools/gyp/test/mac/prefixheader/file.m
@@ -0,0 +1 @@
+MyInt f() { return 0; }
diff --git a/tools/gyp/test/mac/prefixheader/file.mm b/tools/gyp/test/mac/prefixheader/file.mm
new file mode 100644
index 0000000000..d0b39d1f6d
--- /dev/null
+++ b/tools/gyp/test/mac/prefixheader/file.mm
@@ -0,0 +1 @@
+MyInt f() { return 0; }
diff --git a/tools/gyp/test/mac/prefixheader/test.gyp b/tools/gyp/test/mac/prefixheader/test.gyp
index ce318dfc71..7e6b1af807 100644
--- a/tools/gyp/test/mac/prefixheader/test.gyp
+++ b/tools/gyp/test/mac/prefixheader/test.gyp
@@ -4,7 +4,7 @@
{
'targets': [
{
- 'target_name': 'prefix_header',
+ 'target_name': 'prefix_header_c',
'type': 'static_library',
'sources': [ 'file.c', ],
'xcode_settings': {
@@ -12,7 +12,7 @@
},
},
{
- 'target_name': 'precompiled_prefix_header',
+ 'target_name': 'precompiled_prefix_header_c',
'type': 'shared_library',
'mac_bundle': 1,
'sources': [ 'file.c', ],
@@ -21,5 +21,62 @@
'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
},
},
+
+ {
+ 'target_name': 'prefix_header_cc',
+ 'type': 'static_library',
+ 'sources': [ 'file.cc', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ },
+ },
+ {
+ 'target_name': 'precompiled_prefix_header_cc',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.cc', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
+ },
+ },
+
+ {
+ 'target_name': 'prefix_header_m',
+ 'type': 'static_library',
+ 'sources': [ 'file.m', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ },
+ },
+ {
+ 'target_name': 'precompiled_prefix_header_m',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.m', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
+ },
+ },
+
+ {
+ 'target_name': 'prefix_header_mm',
+ 'type': 'static_library',
+ 'sources': [ 'file.mm', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ },
+ },
+ {
+ 'target_name': 'precompiled_prefix_header_mm',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.mm', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
+ },
+ },
],
}
diff --git a/tools/gyp/test/mac/rebuild/TestApp-Info.plist b/tools/gyp/test/mac/rebuild/TestApp-Info.plist
new file mode 100644
index 0000000000..98fd515200
--- /dev/null
+++ b/tools/gyp/test/mac/rebuild/TestApp-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.google.${PRODUCT_NAME}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/tools/gyp/test/mac/rebuild/delay-touch.sh b/tools/gyp/test/mac/rebuild/delay-touch.sh
new file mode 100755
index 0000000000..7caf105b6e
--- /dev/null
+++ b/tools/gyp/test/mac/rebuild/delay-touch.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+
+sleep 1 # mtime resolution is 1 sec on unix.
+touch "$1"
diff --git a/tools/gyp/test/mac/rebuild/empty.c b/tools/gyp/test/mac/rebuild/empty.c
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/gyp/test/mac/rebuild/empty.c
diff --git a/tools/gyp/test/mac/rebuild/main.c b/tools/gyp/test/mac/rebuild/main.c
new file mode 100644
index 0000000000..237c8ce181
--- /dev/null
+++ b/tools/gyp/test/mac/rebuild/main.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/rebuild/test.gyp b/tools/gyp/test/mac/rebuild/test.gyp
new file mode 100644
index 0000000000..15b4e4ef2f
--- /dev/null
+++ b/tools/gyp/test/mac/rebuild/test.gyp
@@ -0,0 +1,56 @@
+# Copyright (c) 2011 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': 'test_app',
+ 'product_name': 'Test App',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'main.c',
+ ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestApp-Info.plist',
+ },
+ },
+ {
+ 'target_name': 'test_app_postbuilds',
+ 'product_name': 'Test App 2',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'main.c',
+ ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestApp-Info.plist',
+ },
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild that touches the app binary',
+ 'action': [
+ './delay-touch.sh', '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'test_framework_postbuilds',
+ 'product_name': 'Test Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'empty.c',
+ ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild that touches the framework binary',
+ 'action': [
+ './delay-touch.sh', '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/sourceless-module/empty.c b/tools/gyp/test/mac/sourceless-module/empty.c
new file mode 100644
index 0000000000..237c8ce181
--- /dev/null
+++ b/tools/gyp/test/mac/sourceless-module/empty.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/sourceless-module/test.gyp b/tools/gyp/test/mac/sourceless-module/test.gyp
new file mode 100644
index 0000000000..49dc2af9c6
--- /dev/null
+++ b/tools/gyp/test/mac/sourceless-module/test.gyp
@@ -0,0 +1,39 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'empty_bundle',
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ },
+ {
+ 'target_name': 'resource_bundle',
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ 'actions': [
+ {
+ 'action_name': 'Add Resource',
+ 'inputs': [],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/app_manifest/foo.manifest',
+ ],
+ 'action': [
+ 'touch', '<(INTERMEDIATE_DIR)/app_manifest/foo.manifest',
+ ],
+ 'process_outputs_as_mac_bundle_resources': 1,
+ },
+ ],
+ },
+ {
+ 'target_name': 'dependent_on_resource_bundle',
+ 'type': 'executable',
+ 'sources': [ 'empty.c' ],
+ 'dependencies': [
+ 'resource_bundle',
+ ],
+ },
+ ],
+}
+
diff --git a/tools/gyp/test/mac/type_envvars/test.gyp b/tools/gyp/test/mac/type_envvars/test.gyp
index 4827957f67..465670056b 100644
--- a/tools/gyp/test/mac/type_envvars/test.gyp
+++ b/tools/gyp/test/mac/type_envvars/test.gyp
@@ -40,6 +40,7 @@
},
],
},
+ # Types 'static_library' and 'none' can't exist as bundles.
{
'target_name': 'nonbundle_executable',
@@ -85,5 +86,15 @@
},
],
},
+ {
+ 'target_name': 'nonbundle_none',
+ 'type': 'none',
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'envtest',
+ 'action': [ './test_nonbundle_none.sh', ],
+ },
+ ],
+ },
],
}
diff --git a/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh b/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh
index 4d3a084f07..1cc6e5668f 100755
--- a/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh
+++ b/tools/gyp/test/mac/type_envvars/test_bundle_executable.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,3 +7,9 @@ set -e
test $MACH_O_TYPE = mh_execute
test $PRODUCT_TYPE = com.apple.product-type.application
+test "${PRODUCT_NAME}" = "My App"
+test "${FULL_PRODUCT_NAME}" = "My App.app"
+
+test "${EXECUTABLE_NAME}" = "My App"
+test "${EXECUTABLE_PATH}" = "My App.app/Contents/MacOS/My App"
+test "${WRAPPER_NAME}" = "My App.app"
diff --git a/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh b/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh
index c74c8e435e..86fc5c99d8 100755
--- a/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh
+++ b/tools/gyp/test/mac/type_envvars/test_bundle_loadable_module.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,3 +7,10 @@ set -e
test $MACH_O_TYPE = mh_bundle
test $PRODUCT_TYPE = com.apple.product-type.bundle
+test $PRODUCT_NAME = bundle_loadable_module
+test $FULL_PRODUCT_NAME = bundle_loadable_module.bundle
+
+test $EXECUTABLE_NAME = bundle_loadable_module
+test $EXECUTABLE_PATH = \
+ "bundle_loadable_module.bundle/Contents/MacOS/bundle_loadable_module"
+test $WRAPPER_NAME = bundle_loadable_module.bundle
diff --git a/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh b/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh
index 08ad4fb7f6..5585af4221 100755
--- a/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh
+++ b/tools/gyp/test/mac/type_envvars/test_bundle_shared_library.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,3 +7,10 @@ set -e
test $MACH_O_TYPE = mh_dylib
test $PRODUCT_TYPE = com.apple.product-type.framework
+test $PRODUCT_NAME = bundle_shared_library
+test $FULL_PRODUCT_NAME = bundle_shared_library.framework
+
+test $EXECUTABLE_NAME = bundle_shared_library
+test $EXECUTABLE_PATH = \
+ "bundle_shared_library.framework/Versions/A/bundle_shared_library"
+test $WRAPPER_NAME = bundle_shared_library.framework
diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh
index 02d373de86..e026a1e261 100755
--- a/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh
+++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_executable.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -8,3 +8,9 @@ set -e
# Check for "not set", not just "empty":
[[ ! $MACH_O_TYPE && ${MACH_O_TYPE-_} ]]
test $PRODUCT_TYPE = com.apple.product-type.tool
+test $PRODUCT_NAME = nonbundle_executable
+test $FULL_PRODUCT_NAME = nonbundle_executable
+
+test $EXECUTABLE_NAME = nonbundle_executable
+test $EXECUTABLE_PATH = nonbundle_executable
+[[ ! $WRAPPER_NAME && ${WRAPPER_NAME-_} ]]
diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh
index aa67df3531..317881eda1 100755
--- a/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh
+++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_loadable_module.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,3 +7,9 @@ set -e
test $MACH_O_TYPE = mh_bundle
test $PRODUCT_TYPE = com.apple.product-type.library.dynamic
+test $PRODUCT_NAME = nonbundle_loadable_module
+test $FULL_PRODUCT_NAME = nonbundle_loadable_module.so
+
+test $EXECUTABLE_NAME = nonbundle_loadable_module.so
+test $EXECUTABLE_PATH = nonbundle_loadable_module.so
+[[ ! $WRAPPER_NAME && ${WRAPPER_NAME-_} ]]
diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_none.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_none.sh
new file mode 100755
index 0000000000..11c40bc638
--- /dev/null
+++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_none.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+# Check for "not set", not just "empty":
+[[ ! $MACH_O_TYPE && ${MACH_O_TYPE-_} ]]
+[[ ! $PRODUCT_TYPE && ${PRODUCT_TYPE-_} ]]
+test $PRODUCT_NAME = nonbundle_none
+[[ ! $FULL_PRODUCT_NAME && ${FULL_PRODUCT_NAME-_} ]]
+
+[[ ! $EXECUTABLE_NAME && ${EXECUTABLE_NAME-_} ]]
+[[ ! $EXECUTABLE_PATH && ${EXECUTABLE_PATH-_} ]]
+[[ ! $WRAPPER_NAME && ${WRAPPER_NAME-_} ]]
diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh
index 937a50c4aa..3b9fb72dd3 100755
--- a/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh
+++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_shared_library.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,3 +7,9 @@ set -e
test $MACH_O_TYPE = mh_dylib
test $PRODUCT_TYPE = com.apple.product-type.library.dynamic
+test $PRODUCT_NAME = nonbundle_shared_library
+test $FULL_PRODUCT_NAME = libnonbundle_shared_library.dylib
+
+test $EXECUTABLE_NAME = libnonbundle_shared_library.dylib
+test $EXECUTABLE_PATH = libnonbundle_shared_library.dylib
+[[ ! $WRAPPER_NAME && ${WRAPPER_NAME-_} ]]
diff --git a/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh b/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh
index 892f0d9f11..77cee2c20a 100755
--- a/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh
+++ b/tools/gyp/test/mac/type_envvars/test_nonbundle_static_library.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,3 +7,9 @@ set -e
test $MACH_O_TYPE = staticlib
test $PRODUCT_TYPE = com.apple.product-type.library.static
+test $PRODUCT_NAME = nonbundle_static_library
+test $FULL_PRODUCT_NAME = libnonbundle_static_library.a
+
+test $EXECUTABLE_NAME = libnonbundle_static_library.a
+test $EXECUTABLE_PATH = libnonbundle_static_library.a
+[[ ! $WRAPPER_NAME && ${WRAPPER_NAME-_} ]]
diff --git a/tools/gyp/test/mac/xcode-env-order/Info.plist b/tools/gyp/test/mac/xcode-env-order/Info.plist
index 55db30dba6..e11f21e52d 100644
--- a/tools/gyp/test/mac/xcode-env-order/Info.plist
+++ b/tools/gyp/test/mac/xcode-env-order/Info.plist
@@ -28,11 +28,29 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
- <key>ProcessedKey1</key>
- <string>${DEPENDENT_KEY1}</string>
- <key>ProcessedKey2</key>
- <string>${DEPENDENT_KEY2}</string>
- <key>ProcessedKey3</key>
- <string>${DEPENDENT_KEY3}</string>
+
+ <key>BraceProcessedKey1</key>
+ <string>${BRACE_DEPENDENT_KEY1}</string>
+ <key>BraceProcessedKey2</key>
+ <string>${BRACE_DEPENDENT_KEY2}</string>
+ <key>BraceProcessedKey3</key>
+ <string>${BRACE_DEPENDENT_KEY3}</string>
+
+ <key>ParenProcessedKey1</key>
+ <string>${PAREN_DEPENDENT_KEY1}</string>
+ <key>ParenProcessedKey2</key>
+ <string>${PAREN_DEPENDENT_KEY2}</string>
+ <key>ParenProcessedKey3</key>
+ <string>${PAREN_DEPENDENT_KEY3}</string>
+
+ <key>BareProcessedKey1</key>
+ <string>${BARE_DEPENDENT_KEY1}</string>
+ <key>BareProcessedKey2</key>
+ <string>${BARE_DEPENDENT_KEY2}</string>
+ <key>BareProcessedKey3</key>
+ <string>${BARE_DEPENDENT_KEY3}</string>
+
+ <key>MixedProcessedKey</key>
+ <string>${MIXED_DEPENDENT_KEY}</string>
</dict>
</plist>
diff --git a/tools/gyp/test/mac/xcode-env-order/test.gyp b/tools/gyp/test/mac/xcode-env-order/test.gyp
index d9191aab5e..8433faea3c 100644
--- a/tools/gyp/test/mac/xcode-env-order/test.gyp
+++ b/tools/gyp/test/mac/xcode-env-order/test.gyp
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
@@ -11,12 +11,72 @@
'sources': [
'main.c',
],
+ # Env vars in copies.
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/${PRODUCT_NAME}-copy-brace',
+ 'files': [ 'main.c', ], # ${SOURCE_ROOT} doesn't work with xcode
+ },
+ {
+ 'destination': '<(PRODUCT_DIR)/$(PRODUCT_NAME)-copy-paren',
+ 'files': [ '$(SOURCE_ROOT)/main.c', ],
+ },
+ {
+ 'destination': '<(PRODUCT_DIR)/$PRODUCT_NAME-copy-bare',
+ 'files': [ 'main.c', ], # $SOURCE_ROOT doesn't work with xcode
+ },
+ ],
+ # Env vars in actions.
+ 'actions': [
+ {
+ 'action_name': 'Action copy braces ${PRODUCT_NAME}',
+ 'description': 'Action copy braces ${PRODUCT_NAME}',
+ 'inputs': [ '${SOURCE_ROOT}/main.c' ],
+ # Referencing ${PRODUCT_NAME} in action outputs doesn't work with
+ # the Xcode generator (PRODUCT_NAME expands to "Test Support").
+ 'outputs': [ '<(PRODUCT_DIR)/action-copy-brace.txt' ],
+ 'action': [ 'cp', '${SOURCE_ROOT}/main.c',
+ '<(PRODUCT_DIR)/action-copy-brace.txt' ],
+ },
+ {
+ 'action_name': 'Action copy parens ${PRODUCT_NAME}',
+ 'description': 'Action copy parens ${PRODUCT_NAME}',
+ 'inputs': [ '${SOURCE_ROOT}/main.c' ],
+ # Referencing ${PRODUCT_NAME} in action outputs doesn't work with
+ # the Xcode generator (PRODUCT_NAME expands to "Test Support").
+ 'outputs': [ '<(PRODUCT_DIR)/action-copy-paren.txt' ],
+ 'action': [ 'cp', '${SOURCE_ROOT}/main.c',
+ '<(PRODUCT_DIR)/action-copy-paren.txt' ],
+ },
+ {
+ 'action_name': 'Action copy bare ${PRODUCT_NAME}',
+ 'description': 'Action copy bare ${PRODUCT_NAME}',
+ 'inputs': [ '${SOURCE_ROOT}/main.c' ],
+ # Referencing ${PRODUCT_NAME} in action outputs doesn't work with
+ # the Xcode generator (PRODUCT_NAME expands to "Test Support").
+ 'outputs': [ '<(PRODUCT_DIR)/action-copy-bare.txt' ],
+ 'action': [ 'cp', '${SOURCE_ROOT}/main.c',
+ '<(PRODUCT_DIR)/action-copy-bare.txt' ],
+ },
+ ],
+ # Env vars in copies.
'xcode_settings': {
'INFOPLIST_FILE': 'Info.plist',
'STRING_KEY': '/Source/Project',
- 'DEPENDENT_KEY2': '$(STRING_KEY)/$(PRODUCT_NAME)',
- 'DEPENDENT_KEY1': 'DEP:$(DEPENDENT_KEY2)',
- 'DEPENDENT_KEY3': '$(PRODUCT_TYPE):$(DEPENDENT_KEY1)',
+
+ 'BRACE_DEPENDENT_KEY2': '${STRING_KEY}/${PRODUCT_NAME}',
+ 'BRACE_DEPENDENT_KEY1': 'D:${BRACE_DEPENDENT_KEY2}',
+ 'BRACE_DEPENDENT_KEY3': '${PRODUCT_TYPE}:${BRACE_DEPENDENT_KEY1}',
+
+ 'PAREN_DEPENDENT_KEY2': '$(STRING_KEY)/$(PRODUCT_NAME)',
+ 'PAREN_DEPENDENT_KEY1': 'D:$(PAREN_DEPENDENT_KEY2)',
+ 'PAREN_DEPENDENT_KEY3': '$(PRODUCT_TYPE):$(PAREN_DEPENDENT_KEY1)',
+
+ 'BARE_DEPENDENT_KEY2': '$STRING_KEY/$PRODUCT_NAME',
+ 'BARE_DEPENDENT_KEY1': 'D:$BARE_DEPENDENT_KEY2',
+ 'BARE_DEPENDENT_KEY3': '$PRODUCT_TYPE:$BARE_DEPENDENT_KEY1',
+
+ 'MIXED_DEPENDENT_KEY': '${STRING_KEY}:$(PRODUCT_NAME):$MACH_O_TYPE',
},
},
],
diff --git a/tools/gyp/test/module/src/module.gyp b/tools/gyp/test/module/src/module.gyp
index bb43c30230..cc567ef7b5 100644
--- a/tools/gyp/test/module/src/module.gyp
+++ b/tools/gyp/test/module/src/module.gyp
@@ -15,7 +15,7 @@
'defines': ['PLATFORM_LINUX'],
# Support 64-bit shared libs (also works fine for 32-bit).
'cflags': ['-fPIC'],
- 'ldflags': ['-ldl'],
+ 'libraries': ['-ldl'],
}],
],
},
diff --git a/tools/gyp/test/msvs/list_excluded/gyptest-all.py b/tools/gyp/test/msvs/list_excluded/gyptest-all.py
new file mode 100644
index 0000000000..5a370f6b47
--- /dev/null
+++ b/tools/gyp/test/msvs/list_excluded/gyptest-all.py
@@ -0,0 +1,51 @@
+#!/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.
+
+"""
+Verifies that msvs_list_excluded_files=0 doesn't list files that would
+normally be in _excluded_files, and that if that flag is not set, then they
+are still listed.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['msvs'], workdir='workarea_all')
+
+
+# with the flag set to 0
+try:
+ os.environ['GYP_GENERATOR_FLAGS'] = 'msvs_list_excluded_files=0'
+ test.run_gyp('hello_exclude.gyp')
+finally:
+ del os.environ['GYP_GENERATOR_FLAGS']
+if test.uses_msbuild:
+ test.must_not_contain('hello.vcxproj', 'hello_mac')
+else:
+ test.must_not_contain('hello.vcproj', 'hello_mac')
+
+
+# with the flag not set
+test.run_gyp('hello_exclude.gyp')
+if test.uses_msbuild:
+ test.must_contain('hello.vcxproj', 'hello_mac')
+else:
+ test.must_contain('hello.vcproj', 'hello_mac')
+
+
+# with the flag explicitly set to 1
+try:
+ os.environ['GYP_GENERATOR_FLAGS'] = 'msvs_list_excluded_files=1'
+ test.run_gyp('hello_exclude.gyp')
+finally:
+ del os.environ['GYP_GENERATOR_FLAGS']
+if test.uses_msbuild:
+ test.must_contain('hello.vcxproj', 'hello_mac')
+else:
+ test.must_contain('hello.vcproj', 'hello_mac')
+
+
+test.pass_test()
diff --git a/tools/gyp/test/msvs/list_excluded/hello.cpp b/tools/gyp/test/msvs/list_excluded/hello.cpp
new file mode 100644
index 0000000000..69acc38bd8
--- /dev/null
+++ b/tools/gyp/test/msvs/list_excluded/hello.cpp
@@ -0,0 +1,10 @@
+// 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.
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ printf("Hello, world!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/msvs/list_excluded/hello_exclude.gyp b/tools/gyp/test/msvs/list_excluded/hello_exclude.gyp
new file mode 100644
index 0000000000..aa160f2367
--- /dev/null
+++ b/tools/gyp/test/msvs/list_excluded/hello_exclude.gyp
@@ -0,0 +1,19 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.cpp',
+ 'hello_mac.cpp',
+ ],
+ 'conditions': [
+ ['OS!="mac"', {'sources!': ['hello_mac.cpp']}],
+ ]
+ },
+ ],
+}
diff --git a/tools/gyp/test/msvs/list_excluded/hello_mac.cpp b/tools/gyp/test/msvs/list_excluded/hello_mac.cpp
new file mode 100644
index 0000000000..b9f6242c4b
--- /dev/null
+++ b/tools/gyp/test/msvs/list_excluded/hello_mac.cpp
@@ -0,0 +1,10 @@
+// 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.
+
+#include <stdio.h>
+
+int hello2() {
+ printf("Hello, two!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/msvs/uldi2010/gyptest-all.py b/tools/gyp/test/msvs/uldi2010/gyptest-all.py
new file mode 100644
index 0000000000..cc248fbd63
--- /dev/null
+++ b/tools/gyp/test/msvs/uldi2010/gyptest-all.py
@@ -0,0 +1,20 @@
+#!/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.
+
+"""
+Verifies that uldi can be disabled on a per-project-reference basis in vs2010.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['msvs'], workdir='workarea_all')
+
+test.run_gyp('hello.gyp')
+
+if test.uses_msbuild:
+ test.must_contain('hello.vcxproj', '<UseLibraryDependencyInputs>false')
+
+test.pass_test()
diff --git a/tools/gyp/test/msvs/uldi2010/hello.c b/tools/gyp/test/msvs/uldi2010/hello.c
new file mode 100644
index 0000000000..2769093694
--- /dev/null
+++ b/tools/gyp/test/msvs/uldi2010/hello.c
@@ -0,0 +1,13 @@
+/* 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. */
+
+#include <stdio.h>
+
+extern int hello2();
+
+int main(int argc, char *argv[]) {
+ printf("Hello, world!\n");
+ hello2();
+ return 0;
+}
diff --git a/tools/gyp/test/msvs/uldi2010/hello.gyp b/tools/gyp/test/msvs/uldi2010/hello.gyp
new file mode 100644
index 0000000000..a2bf2badb1
--- /dev/null
+++ b/tools/gyp/test/msvs/uldi2010/hello.gyp
@@ -0,0 +1,26 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ 'dependencies': [
+ 'hellolib',
+ ]
+ },
+ {
+ 'target_name': 'hellolib',
+ 'type': 'static_library',
+ 'sources': [
+ 'hello2.c',
+ ],
+ 'msvs_2010_disable_uldi_when_referenced': 1,
+ },
+ ],
+}
diff --git a/tools/gyp/test/msvs/uldi2010/hello2.c b/tools/gyp/test/msvs/uldi2010/hello2.c
new file mode 100644
index 0000000000..e2f23238d1
--- /dev/null
+++ b/tools/gyp/test/msvs/uldi2010/hello2.c
@@ -0,0 +1,10 @@
+/* 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. */
+
+#include <stdio.h>
+
+int hello2() {
+ printf("Hello, two!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/ninja/chained-dependency/chained-dependency.gyp b/tools/gyp/test/ninja/chained-dependency/chained-dependency.gyp
new file mode 100644
index 0000000000..a08f893d8c
--- /dev/null
+++ b/tools/gyp/test/ninja/chained-dependency/chained-dependency.gyp
@@ -0,0 +1,52 @@
+# Copyright (c) 2010 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': [
+ # This first target generates a header.
+ {
+ 'target_name': 'generate_header',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'generate header',
+ 'inputs': [],
+ 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/generated/header.h'],
+ 'action': [
+ 'python', '-c', 'open(<(_outputs), "w")'
+ ]
+ },
+ ],
+ 'all_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ },
+ },
+
+ # This intermediate target does nothing other than pull in a
+ # dependency on the above generated target.
+ {
+ 'target_name': 'chain',
+ 'type': 'none',
+ 'dependencies': [
+ 'generate_header',
+ ],
+ },
+
+ # This final target is:
+ # - a static library (so gyp doesn't transitively pull in dependencies);
+ # - that relies on the generated file two dependencies away.
+ {
+ 'target_name': 'chained',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'chain',
+ ],
+ 'sources': [
+ 'chained.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/ninja/chained-dependency/chained.c b/tools/gyp/test/ninja/chained-dependency/chained.c
new file mode 100644
index 0000000000..e4eb5e861f
--- /dev/null
+++ b/tools/gyp/test/ninja/chained-dependency/chained.c
@@ -0,0 +1,5 @@
+#include "generated/header.h"
+
+int main(int argc, char** argv) {
+ return 0;
+}
diff --git a/tools/gyp/test/ninja/chained-dependency/gyptest-chained-dependency.py b/tools/gyp/test/ninja/chained-dependency/gyptest-chained-dependency.py
new file mode 100755
index 0000000000..5494c79e09
--- /dev/null
+++ b/tools/gyp/test/ninja/chained-dependency/gyptest-chained-dependency.py
@@ -0,0 +1,23 @@
+#!/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.
+
+"""
+Verifies that files generated by two-steps-removed actions are built before
+dependent compile steps.
+"""
+
+import os
+import TestGyp
+
+# This test is Ninja-specific in that:
+# - the bug only showed nondeterministically in parallel builds;
+# - it relies on a ninja-specific output file path.
+
+test = TestGyp.TestGyp(formats=['ninja'])
+test.run_gyp('chained-dependency.gyp')
+test.build('chained-dependency.gyp', 'obj/chained.chained.o')
+# The test passes if the .o file builds successfully.
+test.pass_test()
diff --git a/tools/gyp/test/restat/gyptest-restat.py b/tools/gyp/test/restat/gyptest-restat.py
new file mode 100644
index 0000000000..87379044dd
--- /dev/null
+++ b/tools/gyp/test/restat/gyptest-restat.py
@@ -0,0 +1,31 @@
+#!/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 dependent rules are executed iff a dependency action modifies its
+outputs.
+"""
+
+import TestGyp
+import os
+
+test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+test.run_gyp('restat.gyp', chdir='src')
+
+chdir = 'relocate/src'
+test.relocate('src', chdir)
+
+# Building 'dependent' the first time generates 'side_effect', but building it
+# the second time doesn't, because 'create_intermediate' doesn't update its
+# output.
+test.build('restat.gyp', 'dependent', chdir=chdir)
+test.built_file_must_exist('side_effect', chdir=chdir)
+os.remove(test.built_file_path('side_effect', chdir=chdir))
+test.build('restat.gyp', 'dependent', chdir=chdir)
+test.built_file_must_not_exist('side_effect', chdir=chdir)
+
+test.pass_test()
diff --git a/tools/gyp/test/restat/src/create_intermediate.py b/tools/gyp/test/restat/src/create_intermediate.py
new file mode 100644
index 0000000000..a4d7450371
--- /dev/null
+++ b/tools/gyp/test/restat/src/create_intermediate.py
@@ -0,0 +1,17 @@
+#!/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.
+
+import os
+import sys
+
+"""
+Create argv[1] iff it doesn't already exist.
+"""
+
+outfile = sys.argv[1]
+if os.path.exists(outfile):
+ sys.exit()
+open(outfile, "wb").close()
diff --git a/tools/gyp/test/restat/src/restat.gyp b/tools/gyp/test/restat/src/restat.gyp
new file mode 100644
index 0000000000..09b3d71fa8
--- /dev/null
+++ b/tools/gyp/test/restat/src/restat.gyp
@@ -0,0 +1,48 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'create_intermediate',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'create_intermediate',
+ 'inputs': [
+ 'create_intermediate.py',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/intermediate',
+ 'ALWAYS.run.ALWAYS',
+ ],
+ 'action': [
+ 'python', 'create_intermediate.py', '<(PRODUCT_DIR)/intermediate',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'dependent',
+ 'type': 'none',
+ 'dependencies': [
+ 'create_intermediate',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'dependent',
+ 'inputs': [
+ '<(PRODUCT_DIR)/intermediate',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/dependent'
+ ],
+ 'action': [
+ 'touch', '<(PRODUCT_DIR)/dependent', '<(PRODUCT_DIR)/side_effect',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/same-gyp-name/gyptest-all.py b/tools/gyp/test/same-gyp-name/gyptest-all.py
index 76456885ed..98732d52fb 100755
--- a/tools/gyp/test/same-gyp-name/gyptest-all.py
+++ b/tools/gyp/test/same-gyp-name/gyptest-all.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2009 Google Inc. All rights reserved.
+# Copyright (c) 2011 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.
@@ -12,7 +12,7 @@ import TestGyp
# This causes a problem on XCode (duplicate ID).
# See http://code.google.com/p/gyp/issues/detail?id=114
-test = TestGyp.TestGyp(formats=['msvs', 'scons', 'make'])
+test = TestGyp.TestGyp(formats=['msvs', 'scons', 'make', 'ninja'])
test.run_gyp('all.gyp', chdir='src')
diff --git a/tools/gyp/test/same-gyp-name/gyptest-default.py b/tools/gyp/test/same-gyp-name/gyptest-default.py
index c1031f86bd..b8fb63d15b 100755
--- a/tools/gyp/test/same-gyp-name/gyptest-default.py
+++ b/tools/gyp/test/same-gyp-name/gyptest-default.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2009 Google Inc. All rights reserved.
+# Copyright (c) 2011 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.
@@ -12,7 +12,7 @@ import TestGyp
# This causes a problem on XCode (duplicate ID).
# See http://code.google.com/p/gyp/issues/detail?id=114
-test = TestGyp.TestGyp(formats=['msvs', 'scons', 'make'])
+test = TestGyp.TestGyp(formats=['msvs', 'scons', 'make', 'ninja'])
test.run_gyp('all.gyp', chdir='src')
diff --git a/tools/gyp/test/settings/gyptest-settings.py b/tools/gyp/test/settings/gyptest-settings.py
deleted file mode 100755
index 0ed81edbf2..0000000000
--- a/tools/gyp/test/settings/gyptest-settings.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) 2011 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.
-
-"""
-Smoke-tests 'settings' blocks.
-"""
-
-import TestGyp
-
-# 'settings' is only supported for make and scons (and will be removed there as
-# well eventually).
-test = TestGyp.TestGyp(formats=['make', 'scons'])
-test.run_gyp('settings.gyp')
-test.build('test.gyp', test.ALL)
-test.pass_test()
diff --git a/tools/gyp/test/settings/settings.gyp b/tools/gyp/test/settings/settings.gyp
deleted file mode 100644
index 5bde13ef27..0000000000
--- a/tools/gyp/test/settings/settings.gyp
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2011 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': 'settings_target',
- 'type': 'settings',
- # In real life, this would set 'cflags' etc and other targets
- # would depend on it.
- },
- {
- # This is needed so scons will actually generate a SConstruct
- # (which it doesn't do for settings targets alone).
- 'target_name': 'junk1',
- 'type': 'none',
- },
- ],
-}
diff --git a/tools/gyp/test/small/gyptest-small.py b/tools/gyp/test/small/gyptest-small.py
index a484cb3667..8d22ccafc9 100755
--- a/tools/gyp/test/small/gyptest-small.py
+++ b/tools/gyp/test/small/gyptest-small.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (c) 2011 Google Inc. All rights reserved.
+# Copyright (c) 2012 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -27,6 +27,7 @@ files_to_test = [
'pylib/gyp/MSVSSettings_test.py',
'pylib/gyp/easy_xml_test.py',
'pylib/gyp/generator/msvs_test.py',
+ 'pylib/gyp/generator/ninja_test.py',
]
# Collect all the suites from the above files.
diff --git a/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py b/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py
index 61986cdaa5..4daa6b2c11 100755
--- a/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py
+++ b/tools/gyp/test/toplevel-dir/gyptest-toplevel-dir.py
@@ -13,7 +13,7 @@ and using the subdirectory's solution or project file as the entry point.
import TestGyp
import errno
-test = TestGyp.TestGyp(formats=['make'])
+test = TestGyp.TestGyp(formats=['ninja', 'make'])
# We want our Makefile to be one dir up from main.gyp.
test.run_gyp('main.gyp', '--toplevel-dir=..', chdir='src/sub1')
diff --git a/tools/gyp/test/variables/commands/commands.gyp b/tools/gyp/test/variables/commands/commands.gyp
index 113e4a279f..985f8bd49d 100644
--- a/tools/gyp/test/variables/commands/commands.gyp
+++ b/tools/gyp/test/variables/commands/commands.gyp
@@ -58,6 +58,8 @@
'var16': '<(not_int_5)',
'var17': '<(negative_int)',
'var18': '<(zero_int)',
+ 'var19': ['<!@(python test.py)'],
+ 'var20': '<!(python test.py)',
},
'actions': [
{
diff --git a/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout b/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout
index b016419fff..4853149b2f 100644
--- a/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout
+++ b/tools/gyp/test/variables/commands/commands.gyp.ignore-env.stdout
@@ -118,6 +118,12 @@ VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print '3.14
VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing.
VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD'
VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")' to '3.14159265359 ABCD'
+VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python test.py', 'is_array': '', 'replace': '<!(python test.py)', 'type': '<!', 'command_string': None}
+VARIABLES:input.py:783:ExpandVariables Expanding 'python test.py' to 'python test.py'
+VARIABLES:input.py:658:ExpandVariables Executing command 'python test.py' in directory 'None'
+VARIABLES:input.py:765:ExpandVariables Found output 'sample\\path\\foo.cpp', recursing.
+VARIABLES:input.py:783:ExpandVariables Expanding 'sample\\path\\foo.cpp' to 'sample\\path\\foo.cpp'
+VARIABLES:input.py:783:ExpandVariables Expanding '<!(python test.py)' to 'sample\\path\\foo.cpp'
VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_str_int', 'is_array': '', 'replace': '<(check_str_int)', 'type': '<', 'command_string': None}
VARIABLES:input.py:783:ExpandVariables Expanding 'check_str_int' to 'check_str_int'
VARIABLES:input.py:765:ExpandVariables Found output '6', recursing.
@@ -175,6 +181,12 @@ VARIABLES:input.py:783:ExpandVariables Expanding 7 to 7
VARIABLES:input.py:783:ExpandVariables Expanding 8 to 8
VARIABLES:input.py:783:ExpandVariables Expanding 9 to 9
VARIABLES:input.py:783:ExpandVariables Expanding '<@(check_list_int)' to [7, 8, 9]
+VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python test.py', 'is_array': '', 'replace': '<!@(python test.py)', 'type': '<!@', 'command_string': None}
+VARIABLES:input.py:783:ExpandVariables Expanding 'python test.py' to 'python test.py'
+VARIABLES:input.py:703:ExpandVariables Had cache value for command 'python test.py' in directory 'None'
+VARIABLES:input.py:765:ExpandVariables Found output ['samplepathfoo.cpp'], recursing.
+VARIABLES:input.py:783:ExpandVariables Expanding 'samplepathfoo.cpp' to 'samplepathfoo.cpp'
+VARIABLES:input.py:783:ExpandVariables Expanding '<!@(python test.py)' to ['samplepathfoo.cpp']
VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo'
VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target'
VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none'
@@ -234,6 +246,7 @@ VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list'
VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359'
VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD'
VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD'
+VARIABLES:input.py:783:ExpandVariables Expanding 'sample\\path\\foo.cpp' to 'sample\\path\\foo.cpp'
VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah'
VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0'
VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012'
@@ -241,6 +254,7 @@ VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14'
VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9'
VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 '
VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10'
+VARIABLES:input.py:783:ExpandVariables Expanding 'samplepathfoo.cpp' to 'samplepathfoo.cpp'
VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo'
VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target'
VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none'
diff --git a/tools/gyp/test/variables/commands/commands.gyp.stdout b/tools/gyp/test/variables/commands/commands.gyp.stdout
index 2debd33fad..6d88f23cb1 100644
--- a/tools/gyp/test/variables/commands/commands.gyp.stdout
+++ b/tools/gyp/test/variables/commands/commands.gyp.stdout
@@ -118,6 +118,12 @@ VARIABLES:input.py:658:ExpandVariables Executing command 'python -c "print '3.14
VARIABLES:input.py:765:ExpandVariables Found output '3.14159265359 ABCD', recursing.
VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD'
VARIABLES:input.py:783:ExpandVariables Expanding '<!(python -c "print \'<!(python -c "<(pi)") <(letters_list)\'")' to '3.14159265359 ABCD'
+VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python test.py', 'is_array': '', 'replace': '<!(python test.py)', 'type': '<!', 'command_string': None}
+VARIABLES:input.py:783:ExpandVariables Expanding 'python test.py' to 'python test.py'
+VARIABLES:input.py:658:ExpandVariables Executing command 'python test.py' in directory 'None'
+VARIABLES:input.py:765:ExpandVariables Found output 'sample\\path\\foo.cpp', recursing.
+VARIABLES:input.py:783:ExpandVariables Expanding 'sample\\path\\foo.cpp' to 'sample\\path\\foo.cpp'
+VARIABLES:input.py:783:ExpandVariables Expanding '<!(python test.py)' to 'sample\\path\\foo.cpp'
VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'check_str_int', 'is_array': '', 'replace': '<(check_str_int)', 'type': '<', 'command_string': None}
VARIABLES:input.py:783:ExpandVariables Expanding 'check_str_int' to 'check_str_int'
VARIABLES:input.py:765:ExpandVariables Found output '6', recursing.
@@ -175,6 +181,12 @@ VARIABLES:input.py:783:ExpandVariables Expanding 7 to 7
VARIABLES:input.py:783:ExpandVariables Expanding 8 to 8
VARIABLES:input.py:783:ExpandVariables Expanding 9 to 9
VARIABLES:input.py:783:ExpandVariables Expanding '<@(check_list_int)' to [7, 8, 9]
+VARIABLES:input.py:544:ExpandVariables Matches: {'content': 'python test.py', 'is_array': '', 'replace': '<!@(python test.py)', 'type': '<!@', 'command_string': None}
+VARIABLES:input.py:783:ExpandVariables Expanding 'python test.py' to 'python test.py'
+VARIABLES:input.py:703:ExpandVariables Had cache value for command 'python test.py' in directory 'None'
+VARIABLES:input.py:765:ExpandVariables Found output ['samplepathfoo.cpp'], recursing.
+VARIABLES:input.py:783:ExpandVariables Expanding 'samplepathfoo.cpp' to 'samplepathfoo.cpp'
+VARIABLES:input.py:783:ExpandVariables Expanding '<!@(python test.py)' to ['samplepathfoo.cpp']
VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo'
VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target'
VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none'
@@ -234,6 +246,7 @@ VARIABLES:input.py:783:ExpandVariables Expanding 'list' to 'list'
VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359' to '3.14159265359'
VARIABLES:input.py:783:ExpandVariables Expanding 'ABCD' to 'ABCD'
VARIABLES:input.py:783:ExpandVariables Expanding '3.14159265359 ABCD' to '3.14159265359 ABCD'
+VARIABLES:input.py:783:ExpandVariables Expanding 'sample\\path\\foo.cpp' to 'sample\\path\\foo.cpp'
VARIABLES:input.py:783:ExpandVariables Expanding '5blah' to '5blah'
VARIABLES:input.py:783:ExpandVariables Expanding '13.0' to '13.0'
VARIABLES:input.py:783:ExpandVariables Expanding '012' to '012'
@@ -241,6 +254,7 @@ VARIABLES:input.py:783:ExpandVariables Expanding '+14' to '+14'
VARIABLES:input.py:783:ExpandVariables Expanding '7 8 9' to '7 8 9'
VARIABLES:input.py:783:ExpandVariables Expanding '11 ' to '11 '
VARIABLES:input.py:783:ExpandVariables Expanding ' 10' to ' 10'
+VARIABLES:input.py:783:ExpandVariables Expanding 'samplepathfoo.cpp' to 'samplepathfoo.cpp'
VARIABLES:input.py:783:ExpandVariables Expanding 'foo' to 'foo'
VARIABLES:input.py:783:ExpandVariables Expanding 'target' to 'target'
VARIABLES:input.py:783:ExpandVariables Expanding 'none' to 'none'
diff --git a/tools/gyp/test/variables/commands/commands.gypd.golden b/tools/gyp/test/variables/commands/commands.gypd.golden
index e9aaf0202d..d5af8b864c 100644
--- a/tools/gyp/test/variables/commands/commands.gypd.golden
+++ b/tools/gyp/test/variables/commands/commands.gypd.golden
@@ -22,7 +22,9 @@
'var16': '+14',
'var17': '-15',
'var18': '0',
+ 'var19': ['samplepathfoo.cpp'],
'var2': '3.14159265359 ABCD',
+ 'var20': 'sample\\path\\foo.cpp',
'var3': 'ABCD',
'var4': 'ABCD',
'var5': 'letters_',
diff --git a/tools/gyp/test/variables/commands/test.py b/tools/gyp/test/variables/commands/test.py
new file mode 100644
index 0000000000..4d9ca6d1ab
--- /dev/null
+++ b/tools/gyp/test/variables/commands/test.py
@@ -0,0 +1 @@
+print "sample\\path\\foo.cpp"