diff --git a/tools/gyp/PRESUBMIT.py b/tools/gyp/PRESUBMIT.py
index 9c474eb2b106b0..abec27b3e33120 100644
--- a/tools/gyp/PRESUBMIT.py
+++ b/tools/gyp/PRESUBMIT.py
@@ -16,8 +16,6 @@
'test/lib/TestCmd.py',
'test/lib/TestCommon.py',
'test/lib/TestGyp.py',
- # Needs style fix.
- 'pylib/gyp/generator/xcode.py',
]
@@ -25,6 +23,10 @@
# TODO: fix me.
# Many tests include modules they don't use.
'W0611',
+ # Possible unbalanced tuple unpacking with sequence.
+ 'W0632',
+ # Attempting to unpack a non-sequence.
+ 'W0633',
# Include order doesn't properly include local files?
'F0401',
# Some use of built-in names.
@@ -40,6 +42,10 @@
'W0613',
# String has no effect (docstring in wrong place).
'W0105',
+ # map/filter on lambda could be replaced by comprehension.
+ 'W0110',
+ # Use of eval.
+ 'W0123',
# Comma not followed by space.
'C0324',
# Access to a protected member.
@@ -56,6 +62,8 @@
'E1101',
# Dangerous default {}.
'W0102',
+ # Cyclic import.
+ 'R0401',
# Others, too many to sort.
'W0201', 'W0232', 'E1103', 'W0621', 'W0108', 'W0223', 'W0231',
'R0201', 'E0101', 'C0321',
@@ -116,5 +124,16 @@ def CheckChangeOnCommit(input_api, output_api):
return report
-def GetPreferredTrySlaves():
- return ['gyp-win32', 'gyp-win64', 'gyp-linux', 'gyp-mac', 'gyp-android']
+TRYBOTS = [
+ 'gyp-win32',
+ 'gyp-win64',
+ 'gyp-linux',
+ 'gyp-mac',
+ 'gyp-android'
+]
+
+
+def GetPreferredTryMasters(_, change):
+ return {
+ 'tryserver.nacl': { t: set(['defaulttests']) for t in TRYBOTS },
+ }
diff --git a/tools/gyp/buildbot/aosp_manifest.xml b/tools/gyp/buildbot/aosp_manifest.xml
new file mode 100644
index 00000000000000..bd73b303c6e2c2
--- /dev/null
+++ b/tools/gyp/buildbot/aosp_manifest.xml
@@ -0,0 +1,466 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/buildbot/buildbot_run.py b/tools/gyp/buildbot/buildbot_run.py
index 979073c773be97..f46ab1822fe321 100755
--- a/tools/gyp/buildbot/buildbot_run.py
+++ b/tools/gyp/buildbot/buildbot_run.py
@@ -7,6 +7,7 @@
"""Argument-less script to select what to run on the buildbots."""
+import filecmp
import os
import shutil
import subprocess
@@ -30,7 +31,8 @@
def CallSubProcess(*args, **kwargs):
"""Wrapper around subprocess.call which treats errors as build exceptions."""
- retcode = subprocess.call(*args, **kwargs)
+ with open(os.devnull) as devnull_fd:
+ retcode = subprocess.call(stdin=devnull_fd, *args, **kwargs)
if retcode != 0:
print '@@@STEP_EXCEPTION@@@'
sys.exit(1)
@@ -49,10 +51,6 @@ def PrepareCmake():
print '@@@BUILD_STEP Initialize CMake checkout@@@'
os.mkdir(CMAKE_DIR)
- CallSubProcess(['git', 'config', '--global', 'user.name', 'trybot'])
- CallSubProcess(['git', 'config', '--global',
- 'user.email', 'chrome-bot@google.com'])
- CallSubProcess(['git', 'config', '--global', 'color.ui', 'false'])
print '@@@BUILD_STEP Sync CMake@@@'
CallSubProcess(
@@ -73,41 +71,96 @@ def PrepareCmake():
CallSubProcess( ['make', 'cmake'], cwd=CMAKE_DIR)
+_ANDROID_SETUP = 'source build/envsetup.sh && lunch full-eng'
+
+
def PrepareAndroidTree():
"""Prepare an Android tree to run 'android' format tests."""
if os.environ['BUILDBOT_CLOBBER'] == '1':
print '@@@BUILD_STEP Clobber Android checkout@@@'
shutil.rmtree(ANDROID_DIR)
- # The release of Android we use is static, so there's no need to do anything
- # if the directory already exists.
- if os.path.isdir(ANDROID_DIR):
+ # (Re)create the directory so that the following steps will succeed.
+ if not os.path.isdir(ANDROID_DIR):
+ os.mkdir(ANDROID_DIR)
+
+ # We use a manifest from the gyp project listing pinned revisions of AOSP to
+ # use, to ensure that we test against a stable target. This needs to be
+ # updated to pick up new build system changes sometimes, so we must test if
+ # it has changed.
+ manifest_filename = 'aosp_manifest.xml'
+ gyp_manifest = os.path.join(BUILDBOT_DIR, manifest_filename)
+ android_manifest = os.path.join(ANDROID_DIR, '.repo', 'manifests',
+ manifest_filename)
+ manifest_is_current = (os.path.isfile(android_manifest) and
+ filecmp.cmp(gyp_manifest, android_manifest))
+ if not manifest_is_current:
+ # It's safe to repeat these steps, so just do them again to make sure we are
+ # in a good state.
+ print '@@@BUILD_STEP Initialize Android checkout@@@'
+ CallSubProcess(
+ ['repo', 'init',
+ '-u', 'https://android.googlesource.com/platform/manifest',
+ '-b', 'master',
+ '-g', 'all,-notdefault,-device,-darwin,-mips,-x86'],
+ cwd=ANDROID_DIR)
+ shutil.copy(gyp_manifest, android_manifest)
+
+ print '@@@BUILD_STEP Sync Android@@@'
+ CallSubProcess(['repo', 'sync', '-j4', '-m', manifest_filename],
+ cwd=ANDROID_DIR)
+
+ # If we already built the system image successfully and didn't sync to a new
+ # version of the source, skip running the build again as it's expensive even
+ # when there's nothing to do.
+ system_img = os.path.join(ANDROID_DIR, 'out', 'target', 'product', 'generic',
+ 'system.img')
+ if manifest_is_current and os.path.isfile(system_img):
return
- print '@@@BUILD_STEP Initialize Android checkout@@@'
- os.mkdir(ANDROID_DIR)
- CallSubProcess(['git', 'config', '--global', 'user.name', 'trybot'])
- CallSubProcess(['git', 'config', '--global',
- 'user.email', 'chrome-bot@google.com'])
- CallSubProcess(['git', 'config', '--global', 'color.ui', 'false'])
+ print '@@@BUILD_STEP Build Android@@@'
CallSubProcess(
- ['repo', 'init',
- '-u', 'https://android.googlesource.com/platform/manifest',
- '-b', 'android-4.2.1_r1',
- '-g', 'all,-notdefault,-device,-darwin,-mips,-x86'],
+ ['/bin/bash',
+ '-c', '%s && make -j4' % _ANDROID_SETUP],
cwd=ANDROID_DIR)
- print '@@@BUILD_STEP Sync Android@@@'
- CallSubProcess(['repo', 'sync', '-j4'], cwd=ANDROID_DIR)
- print '@@@BUILD_STEP Build Android@@@'
+def StartAndroidEmulator():
+ """Start an android emulator from the built android tree."""
+ print '@@@BUILD_STEP Start Android emulator@@@'
+
+ CallSubProcess(['/bin/bash', '-c',
+ '%s && adb kill-server ' % _ANDROID_SETUP],
+ cwd=ANDROID_DIR)
+
+ # If taskset is available, use it to force adbd to run only on one core, as,
+ # sadly, it improves its reliability (see crbug.com/268450).
+ adbd_wrapper = ''
+ with open(os.devnull, 'w') as devnull_fd:
+ if subprocess.call(['which', 'taskset'], stdout=devnull_fd) == 0:
+ adbd_wrapper = 'taskset -c 0'
+ CallSubProcess(['/bin/bash', '-c',
+ '%s && %s adb start-server ' % (_ANDROID_SETUP, adbd_wrapper)],
+ cwd=ANDROID_DIR)
+
+ subprocess.Popen(
+ ['/bin/bash', '-c',
+ '%s && emulator -no-window' % _ANDROID_SETUP],
+ cwd=ANDROID_DIR)
CallSubProcess(
- ['/bin/bash',
- '-c', 'source build/envsetup.sh && lunch full-eng && make -j4'],
+ ['/bin/bash', '-c',
+ '%s && adb wait-for-device' % _ANDROID_SETUP],
cwd=ANDROID_DIR)
-def GypTestFormat(title, format=None, msvs_version=None):
+def StopAndroidEmulator():
+ """Stop all android emulators."""
+ print '@@@BUILD_STEP Stop Android emulator@@@'
+ # If this fails, it's because there is no emulator running.
+ subprocess.call(['pkill', 'emulator.*'])
+
+
+def GypTestFormat(title, format=None, msvs_version=None, tests=[]):
"""Run the gyp tests for a given format, emitting annotator tags.
See annotator docs at:
@@ -126,19 +179,18 @@ def GypTestFormat(title, format=None, msvs_version=None):
if msvs_version:
env['GYP_MSVS_VERSION'] = msvs_version
command = ' '.join(
- [sys.executable, 'trunk/gyptest.py',
+ [sys.executable, 'gyp/gyptest.py',
'--all',
'--passed',
'--format', format,
'--path', CMAKE_BIN_DIR,
- '--chdir', 'trunk'])
+ '--chdir', 'gyp'] + tests)
if format == 'android':
# gyptest needs the environment setup from envsetup/lunch in order to build
# using the 'android' backend, so this is done in a single shell.
retcode = subprocess.call(
['/bin/bash',
- '-c', 'source build/envsetup.sh && lunch full-eng && cd %s && %s'
- % (ROOT_DIR, command)],
+ '-c', '%s && cd %s && %s' % (_ANDROID_SETUP, ROOT_DIR, command)],
cwd=ANDROID_DIR, env=env)
else:
retcode = subprocess.call(command, cwd=ROOT_DIR, env=env, shell=True)
@@ -160,7 +212,11 @@ def GypBuild():
# The Android gyp bot runs on linux so this must be tested first.
if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-android':
PrepareAndroidTree()
- retcode += GypTestFormat('android')
+ StartAndroidEmulator()
+ try:
+ retcode += GypTestFormat('android')
+ finally:
+ StopAndroidEmulator()
elif sys.platform.startswith('linux'):
retcode += GypTestFormat('ninja')
retcode += GypTestFormat('make')
@@ -173,8 +229,13 @@ def GypBuild():
elif sys.platform == 'win32':
retcode += GypTestFormat('ninja')
if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64':
- retcode += GypTestFormat('msvs-2010', format='msvs', msvs_version='2010')
- retcode += GypTestFormat('msvs-2012', format='msvs', msvs_version='2012')
+ retcode += GypTestFormat('msvs-ninja-2013', format='msvs-ninja',
+ msvs_version='2013',
+ tests=[
+ r'test\generator-output\gyptest-actions.py',
+ r'test\generator-output\gyptest-relocate.py',
+ r'test\generator-output\gyptest-rules.py'])
+ retcode += GypTestFormat('msvs-2013', format='msvs', msvs_version='2013')
else:
raise Exception('Unknown platform')
if retcode:
diff --git a/tools/gyp/buildbot/commit_queue/OWNERS b/tools/gyp/buildbot/commit_queue/OWNERS
new file mode 100644
index 00000000000000..b269c198b43e3e
--- /dev/null
+++ b/tools/gyp/buildbot/commit_queue/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+bradnelson@chromium.org
+bradnelson@google.com
+iannucci@chromium.org
+scottmg@chromium.org
+thakis@chromium.org
diff --git a/tools/gyp/buildbot/commit_queue/README b/tools/gyp/buildbot/commit_queue/README
new file mode 100644
index 00000000000000..94284978832702
--- /dev/null
+++ b/tools/gyp/buildbot/commit_queue/README
@@ -0,0 +1,3 @@
+cq_config.json describes the trybots that must pass in order
+to land a change through the commit queue.
+Comments are here as the file is strictly JSON.
diff --git a/tools/gyp/buildbot/commit_queue/cq_config.json b/tools/gyp/buildbot/commit_queue/cq_config.json
new file mode 100644
index 00000000000000..bbf20e394f3bb6
--- /dev/null
+++ b/tools/gyp/buildbot/commit_queue/cq_config.json
@@ -0,0 +1,16 @@
+{
+ "trybots": {
+ "launched": {
+ "tryserver.nacl": {
+ "gyp-presubmit": ["defaulttests"],
+ "gyp-android": ["defaulttests"],
+ "gyp-linux": ["defaulttests"],
+ "gyp-mac": ["defaulttests"],
+ "gyp-win32": ["defaulttests"],
+ "gyp-win64": ["defaulttests"]
+ }
+ },
+ "triggered": {
+ }
+ }
+}
diff --git a/tools/gyp/codereview.settings b/tools/gyp/codereview.settings
index a04a2440df39d1..faf37f1145f173 100644
--- a/tools/gyp/codereview.settings
+++ b/tools/gyp/codereview.settings
@@ -1,10 +1,10 @@
# This file is used by gcl to get repository specific information.
CODE_REVIEW_SERVER: codereview.chromium.org
CC_LIST: gyp-developer@googlegroups.com
-VIEW_VC: http://code.google.com/p/gyp/source/detail?r=
-TRY_ON_UPLOAD: True
+VIEW_VC: https://chromium.googlesource.com/external/gyp/+/
+TRY_ON_UPLOAD: False
TRYSERVER_PROJECT: gyp
-TRYSERVER_PATCHLEVEL: 0
-TRYSERVER_ROOT: trunk
+TRYSERVER_PATCHLEVEL: 1
+TRYSERVER_ROOT: gyp
TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try-nacl
-
+PROJECT: gyp
diff --git a/tools/gyp/gyp b/tools/gyp/gyp
index b53a6dde8f2d5e..1b8b9bdfb05f58 100755
--- a/tools/gyp/gyp
+++ b/tools/gyp/gyp
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
# Copyright 2013 The Chromium Authors. 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/gyptest.py b/tools/gyp/gyptest.py
index 8f3ee0ffb04f29..8e4fc47d5c804b 100755
--- a/tools/gyp/gyptest.py
+++ b/tools/gyp/gyptest.py
@@ -13,7 +13,7 @@
import subprocess
import sys
-class CommandRunner:
+class CommandRunner(object):
"""
Executor class for commands, including "commands" implemented by
Python functions.
@@ -117,7 +117,7 @@ def run(self, command, display=None, stdout=None, stderr=None):
return self.execute(command, stdout, stderr)
-class Unbuffered:
+class Unbuffered(object):
def __init__(self, fp):
self.fp = fp
def write(self, arg):
@@ -224,7 +224,7 @@ def main(argv=None):
'win32': ['msvs', 'ninja'],
'linux2': ['make', 'ninja'],
'linux3': ['make', 'ninja'],
- 'darwin': ['make', 'ninja', 'xcode'],
+ 'darwin': ['make', 'ninja', 'xcode', 'xcode-ninja'],
}[sys.platform]
for format in format_list:
diff --git a/tools/gyp/pylib/gyp/MSVSNew.py b/tools/gyp/pylib/gyp/MSVSNew.py
index 845dcb0639d756..593f0e5b0b2e88 100644
--- a/tools/gyp/pylib/gyp/MSVSNew.py
+++ b/tools/gyp/pylib/gyp/MSVSNew.py
@@ -172,7 +172,7 @@ def set_msbuild_toolset(self, msbuild_toolset):
#------------------------------------------------------------------------------
-class MSVSSolution:
+class MSVSSolution(object):
"""Visual Studio solution."""
def __init__(self, path, version, entries=None, variants=None,
diff --git a/tools/gyp/pylib/gyp/MSVSSettings.py b/tools/gyp/pylib/gyp/MSVSSettings.py
index 0c9532d8e6776c..dde0e07092b167 100644
--- a/tools/gyp/pylib/gyp/MSVSSettings.py
+++ b/tools/gyp/pylib/gyp/MSVSSettings.py
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""Code to validate and convert settings of the Microsoft build tools.
+r"""Code to validate and convert settings of the Microsoft build tools.
This file contains code to validate and convert settings of the Microsoft
build tools. The function ConvertToMSBuildSettings(), ValidateMSVSSettings(),
@@ -314,7 +314,14 @@ def _MSBuildOnly(tool, name, setting_type):
name: the name of the setting.
setting_type: the type of this setting.
"""
+
+ def _Translate(value, msbuild_settings):
+ # Let msbuild-only properties get translated as-is from msvs_settings.
+ tool_settings = msbuild_settings.setdefault(tool.msbuild_name, {})
+ tool_settings[name] = value
+
_msbuild_validators[tool.msbuild_name][name] = setting_type.ValidateMSBuild
+ _msvs_to_msbuild_converters[tool.msvs_name][name] = _Translate
def _ConvertedToAdditionalOption(tool, msvs_name, flag):
@@ -367,6 +374,35 @@ def _Translate(value, msbuild_settings):
r'(\$\((?:%s)\))(?:[\\/]+)' % "|".join(fix_vc_macro_slashes_regex_list)
)
+# Regular expression to detect keys that were generated by exclusion lists
+_EXCLUDED_SUFFIX_RE = re.compile('^(.*)_excluded$')
+
+
+def _ValidateExclusionSetting(setting, settings, error_msg, stderr=sys.stderr):
+ """Verify that 'setting' is valid if it is generated from an exclusion list.
+
+ If the setting appears to be generated from an exclusion list, the root name
+ is checked.
+
+ Args:
+ setting: A string that is the setting name to validate
+ settings: A dictionary where the keys are valid settings
+ error_msg: The message to emit in the event of error
+ stderr: The stream receiving the error messages.
+ """
+ # This may be unrecognized because it's an exclusion list. If the
+ # setting name has the _excluded suffix, then check the root name.
+ unrecognized = True
+ m = re.match(_EXCLUDED_SUFFIX_RE, setting)
+ if m:
+ root_setting = m.group(1)
+ unrecognized = root_setting not in settings
+
+ if unrecognized:
+ # We don't know this setting. Give a warning.
+ print >> stderr, error_msg
+
+
def FixVCMacroSlashes(s):
"""Replace macros which have excessive following slashes.
@@ -388,11 +424,11 @@ def ConvertVCMacrosToMSBuild(s):
if '$' in s:
replace_map = {
'$(ConfigurationName)': '$(Configuration)',
- '$(InputDir)': '%(RootDir)%(Directory)',
+ '$(InputDir)': '%(RelativeDir)',
'$(InputExt)': '%(Extension)',
'$(InputFileName)': '%(Filename)%(Extension)',
'$(InputName)': '%(Filename)',
- '$(InputPath)': '%(FullPath)',
+ '$(InputPath)': '%(Identity)',
'$(ParentName)': '$(ProjectFileName)',
'$(PlatformName)': '$(Platform)',
'$(SafeInputName)': '%(Filename)',
@@ -429,10 +465,12 @@ def ConvertToMSBuildSettings(msvs_settings, stderr=sys.stderr):
print >> stderr, ('Warning: while converting %s/%s to MSBuild, '
'%s' % (msvs_tool_name, msvs_setting, e))
else:
- # We don't know this setting. Give a warning.
- print >> stderr, ('Warning: unrecognized setting %s/%s '
- 'while converting to MSBuild.' %
- (msvs_tool_name, msvs_setting))
+ _ValidateExclusionSetting(msvs_setting,
+ msvs_tool,
+ ('Warning: unrecognized setting %s/%s '
+ 'while converting to MSBuild.' %
+ (msvs_tool_name, msvs_setting)),
+ stderr)
else:
print >> stderr, ('Warning: unrecognized tool %s while converting to '
'MSBuild.' % msvs_tool_name)
@@ -483,8 +521,12 @@ def _ValidateSettings(validators, settings, stderr):
print >> stderr, ('Warning: for %s/%s, %s' %
(tool_name, setting, e))
else:
- print >> stderr, ('Warning: unrecognized setting %s/%s' %
- (tool_name, setting))
+ _ValidateExclusionSetting(setting,
+ tool_validators,
+ ('Warning: unrecognized setting %s/%s' %
+ (tool_name, setting)),
+ stderr)
+
else:
print >> stderr, ('Warning: unrecognized tool %s' % tool_name)
@@ -496,6 +538,7 @@ def _ValidateSettings(validators, settings, stderr):
_rc = _Tool('VCResourceCompilerTool', 'ResourceCompile')
_lib = _Tool('VCLibrarianTool', 'Lib')
_manifest = _Tool('VCManifestTool', 'Manifest')
+_masm = _Tool('MASM', 'MASM')
_AddTool(_compile)
@@ -504,6 +547,7 @@ def _ValidateSettings(validators, settings, stderr):
_AddTool(_rc)
_AddTool(_lib)
_AddTool(_manifest)
+_AddTool(_masm)
# Add sections only found in the MSBuild settings.
_msbuild_validators[''] = {}
_msbuild_validators['ProjectReference'] = {}
@@ -567,7 +611,8 @@ def _ValidateSettings(validators, settings, stderr):
_Same(_compile, 'CallingConvention',
_Enumeration(['Cdecl', # /Gd
'FastCall', # /Gr
- 'StdCall'])) # /Gz
+ 'StdCall', # /Gz
+ 'VectorCall'])) # /Gv
_Same(_compile, 'CompileAs',
_Enumeration(['Default',
'CompileAsC', # /TC
@@ -581,7 +626,12 @@ def _ValidateSettings(validators, settings, stderr):
_Same(_compile, 'EnableEnhancedInstructionSet',
_Enumeration(['NotSet',
'StreamingSIMDExtensions', # /arch:SSE
- 'StreamingSIMDExtensions2'])) # /arch:SSE2
+ 'StreamingSIMDExtensions2', # /arch:SSE2
+ 'AdvancedVectorExtensions', # /arch:AVX (vs2012+)
+ 'NoExtensions', # /arch:IA32 (vs2012+)
+ # This one only exists in the new msbuild format.
+ 'AdvancedVectorExtensions2', # /arch:AVX2 (vs2013r2+)
+ ]))
_Same(_compile, 'ErrorReporting',
_Enumeration(['None', # /errorReport:none
'Prompt', # /errorReport:prompt
@@ -836,13 +886,6 @@ def _ValidateSettings(validators, settings, stderr):
# MSVS options not found in MSBuild.
_MSVSOnly(_link, 'OptimizeForWindows98', _newly_boolean)
_MSVSOnly(_link, 'UseUnicodeResponseFiles', _boolean)
-# These settings generate correctly in the MSVS output files when using
-# e.g. DelayLoadDLLs! or AdditionalDependencies! to exclude files from
-# configuration entries, but result in spurious artifacts which can be
-# safely ignored here. See crbug.com/246570
-_MSVSOnly(_link, 'AdditionalLibraryDirectories_excluded', _folder_list)
-_MSVSOnly(_link, 'DelayLoadDLLs_excluded', _file_list)
-_MSVSOnly(_link, 'AdditionalDependencies_excluded', _file_list)
# MSBuild options not found in MSVS.
_MSBuildOnly(_link, 'BuildingInIDE', _boolean)
@@ -991,9 +1034,6 @@ def _ValidateSettings(validators, settings, stderr):
# ProjectReference. We may want to validate that they are consistent.
_Moved(_lib, 'LinkLibraryDependencies', 'ProjectReference', _boolean)
-# TODO(jeanluc) I don't think these are genuine settings but byproducts of Gyp.
-_MSVSOnly(_lib, 'AdditionalLibraryDirectories_excluded', _folder_list)
-
_MSBuildOnly(_lib, 'DisplayLibrary', _string) # /LIST Visible='false'
_MSBuildOnly(_lib, 'ErrorReporting',
_Enumeration([], new=['PromptImmediately', # /ERRORREPORT:PROMPT
@@ -1049,3 +1089,11 @@ def _ValidateSettings(validators, settings, stderr):
_MSBuildOnly(_manifest, 'OutputResourceManifests', _string) # /outputresource
_MSBuildOnly(_manifest, 'SuppressDependencyElement', _boolean) # /nodependency
_MSBuildOnly(_manifest, 'TrackerLogDirectory', _folder_name)
+
+
+# Directives for MASM.
+# See "$(VCTargetsPath)\BuildCustomizations\masm.xml" for the schema of the
+# MSBuild MASM settings.
+
+# Options that have the same name in MSVS and MSBuild.
+_Same(_masm, 'UseSafeExceptionHandlers', _boolean) # /safeseh
diff --git a/tools/gyp/pylib/gyp/MSVSSettings_test.py b/tools/gyp/pylib/gyp/MSVSSettings_test.py
index 4e06da3bbe3f30..d24dcac4d5e13e 100755
--- a/tools/gyp/pylib/gyp/MSVSSettings_test.py
+++ b/tools/gyp/pylib/gyp/MSVSSettings_test.py
@@ -109,6 +109,7 @@ def testValidateMSVSSettings_settings(self):
'ZZXYZ': 'bogus'},
'VCLinkerTool': {
'AdditionalDependencies': 'file1;file2',
+ 'AdditionalDependencies_excluded': 'file3',
'AdditionalLibraryDirectories': 'folder1;folder2',
'AdditionalManifestDependencies': 'file1;file2',
'AdditionalOptions': 'a string1',
@@ -266,7 +267,7 @@ def testValidateMSVSSettings_settings(self):
'Warning: for VCCLCompilerTool/BrowseInformation, '
"invalid literal for int() with base 10: 'fdkslj'",
'Warning: for VCCLCompilerTool/CallingConvention, '
- 'index value (-1) not in expected range [0, 3)',
+ 'index value (-1) not in expected range [0, 4)',
'Warning: for VCCLCompilerTool/DebugInformationFormat, '
'converted value for 2 not specified.',
'Warning: unrecognized setting VCCLCompilerTool/Enableprefast',
diff --git a/tools/gyp/pylib/gyp/MSVSUtil.py b/tools/gyp/pylib/gyp/MSVSUtil.py
index 62e8d260d444d0..0b32e911807844 100644
--- a/tools/gyp/pylib/gyp/MSVSUtil.py
+++ b/tools/gyp/pylib/gyp/MSVSUtil.py
@@ -8,10 +8,12 @@
import os
-_TARGET_TYPE_EXT = {
- 'executable': '.exe',
- 'loadable_module': '.dll',
- 'shared_library': '.dll',
+# A dictionary mapping supported target types to extensions.
+TARGET_TYPE_EXT = {
+ 'executable': 'exe',
+ 'loadable_module': 'dll',
+ 'shared_library': 'dll',
+ 'static_library': 'lib',
}
@@ -109,15 +111,16 @@ def ShardTargets(target_list, target_dicts):
new_target_dicts[t] = target_dicts[t]
# Shard dependencies.
for t in new_target_dicts:
- dependencies = copy.copy(new_target_dicts[t].get('dependencies', []))
- new_dependencies = []
- for d in dependencies:
- if d in targets_to_shard:
- for i in range(targets_to_shard[d]):
- new_dependencies.append(_ShardName(d, i))
- else:
- new_dependencies.append(d)
- new_target_dicts[t]['dependencies'] = new_dependencies
+ for deptype in ('dependencies', 'dependencies_original'):
+ dependencies = copy.copy(new_target_dicts[t].get(deptype, []))
+ new_dependencies = []
+ for d in dependencies:
+ if d in targets_to_shard:
+ for i in range(targets_to_shard[d]):
+ new_dependencies.append(_ShardName(d, i))
+ else:
+ new_dependencies.append(d)
+ new_target_dicts[t][deptype] = new_dependencies
return (new_target_list, new_target_dicts)
@@ -156,7 +159,7 @@ def _GetPdbPath(target_dict, config_name, vars):
pdb_base = target_dict.get('product_name', target_dict['target_name'])
- pdb_base = '%s%s.pdb' % (pdb_base, _TARGET_TYPE_EXT[target_dict['type']])
+ pdb_base = '%s.%s.pdb' % (pdb_base, TARGET_TYPE_EXT[target_dict['type']])
pdb_path = vars['PRODUCT_DIR'] + '/' + pdb_base
return pdb_path
@@ -264,4 +267,4 @@ def InsertLargePdbShims(target_list, target_dicts, vars):
# Update the original target to depend on the shim target.
target_dict.setdefault('dependencies', []).append(full_shim_target_name)
- return (target_list, target_dicts)
\ No newline at end of file
+ return (target_list, target_dicts)
diff --git a/tools/gyp/pylib/gyp/MSVSVersion.py b/tools/gyp/pylib/gyp/MSVSVersion.py
index 03b6d8ad42cc7f..92e583fd6e27c7 100644
--- a/tools/gyp/pylib/gyp/MSVSVersion.py
+++ b/tools/gyp/pylib/gyp/MSVSVersion.py
@@ -138,7 +138,7 @@ def _RegistryQueryBase(sysdir, key, value):
def _RegistryQuery(key, value=None):
- """Use reg.exe to read a particular key through _RegistryQueryBase.
+ r"""Use reg.exe to read a particular key through _RegistryQueryBase.
First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
that fails, it falls back to System32. Sysnative is available on Vista and
@@ -165,8 +165,33 @@ def _RegistryQuery(key, value=None):
return text
+def _RegistryGetValueUsingWinReg(key, value):
+ """Use the _winreg module to obtain the value of a registry key.
+
+ Args:
+ key: The registry key.
+ value: The particular registry value to read.
+ Return:
+ contents of the registry key's value, or None on failure. Throws
+ ImportError if _winreg is unavailable.
+ """
+ import _winreg
+ try:
+ root, subkey = key.split('\\', 1)
+ assert root == 'HKLM' # Only need HKLM for now.
+ with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
+ return _winreg.QueryValueEx(hkey, value)[0]
+ except WindowsError:
+ return None
+
+
def _RegistryGetValue(key, value):
- """Use reg.exe to obtain the value of a registry key.
+ """Use _winreg or reg.exe to obtain the value of a registry key.
+
+ Using _winreg is preferable because it solves an issue on some corporate
+ environments where access to reg.exe is locked down. However, we still need
+ to fallback to reg.exe for the case where the _winreg module is not available
+ (for example in cygwin python).
Args:
key: The registry key.
@@ -174,6 +199,12 @@ def _RegistryGetValue(key, value):
Return:
contents of the registry key's value, or None on failure.
"""
+ try:
+ return _RegistryGetValueUsingWinReg(key, value)
+ except ImportError:
+ pass
+
+ # Fallback to reg.exe if we fail to import _winreg.
text = _RegistryQuery(key, value)
if not text:
return None
@@ -184,19 +215,6 @@ def _RegistryGetValue(key, value):
return match.group(1)
-def _RegistryKeyExists(key):
- """Use reg.exe to see if a key exists.
-
- Args:
- key: The registry key to check.
- Return:
- True if the key exists
- """
- if not _RegistryQuery(key):
- return False
- return True
-
-
def _CreateVersion(name, path, sdk_based=False):
"""Sets up MSVS project generation.
@@ -207,6 +225,15 @@ def _CreateVersion(name, path, sdk_based=False):
if path:
path = os.path.normpath(path)
versions = {
+ '2015': VisualStudioVersion('2015',
+ 'Visual Studio 2015',
+ solution_version='12.00',
+ project_version='14.0',
+ flat_sln=False,
+ uses_vcxproj=True,
+ path=path,
+ sdk_based=sdk_based,
+ default_toolset='v140'),
'2013': VisualStudioVersion('2013',
'Visual Studio 2013',
solution_version='13.00',
@@ -316,7 +343,8 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
2008(e) - Visual Studio 2008 (9)
2010(e) - Visual Studio 2010 (10)
2012(e) - Visual Studio 2012 (11)
- 2013(e) - Visual Studio 2013 (11)
+ 2013(e) - Visual Studio 2013 (12)
+ 2015 - Visual Studio 2015 (14)
Where (e) is e for express editions of MSVS and blank otherwise.
"""
version_to_year = {
@@ -325,6 +353,7 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
'10.0': '2010',
'11.0': '2012',
'12.0': '2013',
+ '14.0': '2015',
}
versions = []
for version in versions_to_check:
@@ -361,13 +390,14 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
if not path:
continue
path = _ConvertToCygpath(path)
- versions.append(_CreateVersion(version_to_year[version] + 'e',
- os.path.join(path, '..'), sdk_based=True))
+ if version != '14.0': # There is no Express edition for 2015.
+ versions.append(_CreateVersion(version_to_year[version] + 'e',
+ os.path.join(path, '..'), sdk_based=True))
return versions
-def SelectVisualStudioVersion(version='auto'):
+def SelectVisualStudioVersion(version='auto', allow_fallback=True):
"""Select which version of Visual Studio projects to generate.
Arguments:
@@ -379,7 +409,7 @@ def SelectVisualStudioVersion(version='auto'):
if version == 'auto':
version = os.environ.get('GYP_MSVS_VERSION', 'auto')
version_map = {
- 'auto': ('10.0', '12.0', '9.0', '8.0', '11.0'),
+ 'auto': ('14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
'2005': ('8.0',),
'2005e': ('8.0',),
'2008': ('9.0',),
@@ -390,6 +420,7 @@ def SelectVisualStudioVersion(version='auto'):
'2012e': ('11.0',),
'2013': ('12.0',),
'2013e': ('12.0',),
+ '2015': ('14.0',),
}
override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
if override_path:
@@ -401,6 +432,8 @@ def SelectVisualStudioVersion(version='auto'):
version = str(version)
versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
if not versions:
+ if not allow_fallback:
+ raise ValueError('Could not locate Visual Studio installation.')
if version == 'auto':
# Default to 2005 if we couldn't find anything
return _CreateVersion('2005', None)
diff --git a/tools/gyp/pylib/gyp/__init__.py b/tools/gyp/pylib/gyp/__init__.py
index 30edea56752a1e..ac6d918b849922 100755
--- a/tools/gyp/pylib/gyp/__init__.py
+++ b/tools/gyp/pylib/gyp/__init__.py
@@ -59,7 +59,6 @@ def Load(build_files, format, default_variables={},
if params is None:
params = {}
- flavor = None
if '-' in format:
format, params['flavor'] = format.split('-', 1)
@@ -69,6 +68,7 @@ def Load(build_files, format, default_variables={},
# named WITH_CAPITAL_LETTERS to provide a distinct "best practice" namespace,
# avoiding collisions with user and automatic variables.
default_variables['GENERATOR'] = format
+ default_variables['GENERATOR_FLAVOR'] = params.get('flavor', '')
# Format can be a custom python file, or by default the name of a module
# within gyp.generator.
@@ -371,7 +371,7 @@ def gyp_main(args):
if options.use_environment:
generate_formats = os.environ.get('GYP_GENERATORS', [])
if generate_formats:
- generate_formats = re.split('[\s,]', generate_formats)
+ generate_formats = re.split(r'[\s,]', generate_formats)
if generate_formats:
options.formats = generate_formats
else:
@@ -493,14 +493,13 @@ def gyp_main(args):
'gyp_binary': sys.argv[0],
'home_dot_gyp': home_dot_gyp,
'parallel': options.parallel,
- 'root_targets': options.root_targets}
+ 'root_targets': options.root_targets,
+ 'target_arch': cmdline_default_variables.get('target_arch', '')}
# Start with the default variables from the command line.
- [generator, flat_list, targets, data] = Load(build_files, format,
- cmdline_default_variables,
- includes, options.depth,
- params, options.check,
- options.circular_check)
+ [generator, flat_list, targets, data] = Load(
+ build_files, format, cmdline_default_variables, includes, options.depth,
+ params, options.check, options.circular_check)
# TODO(mark): Pass |data| for now because the generator needs a list of
# build files that came in. In the future, maybe it should just accept
diff --git a/tools/gyp/pylib/gyp/common.py b/tools/gyp/pylib/gyp/common.py
index f9c6c6f3a8ccba..b6875e43efcbc4 100644
--- a/tools/gyp/pylib/gyp/common.py
+++ b/tools/gyp/pylib/gyp/common.py
@@ -4,6 +4,7 @@
from __future__ import with_statement
+import collections
import errno
import filecmp
import os.path
@@ -328,7 +329,7 @@ def WriteOnDiff(filename):
the target if it differs (on close).
"""
- class Writer:
+ class Writer(object):
"""Wrapper around file which only covers the target if it differs."""
def __init__(self):
# Pick temporary file.
@@ -472,6 +473,72 @@ def uniquer(seq, idfun=None):
return result
+# Based on http://code.activestate.com/recipes/576694/.
+class OrderedSet(collections.MutableSet):
+ def __init__(self, iterable=None):
+ self.end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.map = {} # key --> [key, prev, next]
+ if iterable is not None:
+ self |= iterable
+
+ def __len__(self):
+ return len(self.map)
+
+ def __contains__(self, key):
+ return key in self.map
+
+ def add(self, key):
+ if key not in self.map:
+ end = self.end
+ curr = end[1]
+ curr[2] = end[1] = self.map[key] = [key, curr, end]
+
+ def discard(self, key):
+ if key in self.map:
+ key, prev_item, next_item = self.map.pop(key)
+ prev_item[2] = next_item
+ next_item[1] = prev_item
+
+ def __iter__(self):
+ end = self.end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ # The second argument is an addition that causes a pylint warning.
+ def pop(self, last=True): # pylint: disable=W0221
+ if not self:
+ raise KeyError('set is empty')
+ key = self.end[1][0] if last else self.end[2][0]
+ self.discard(key)
+ return key
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, list(self))
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedSet):
+ return len(self) == len(other) and list(self) == list(other)
+ return set(self) == set(other)
+
+ # Extensions to the recipe.
+ def update(self, iterable):
+ for i in iterable:
+ if i not in self:
+ self.add(i)
+
+
class CycleError(Exception):
"""An exception raised when an unexpected cycle is detected."""
def __init__(self, nodes):
@@ -481,7 +548,7 @@ def __str__(self):
def TopologicallySorted(graph, get_edges):
- """Topologically sort based on a user provided edge definition.
+ r"""Topologically sort based on a user provided edge definition.
Args:
graph: A list of node names.
@@ -519,3 +586,14 @@ def Visit(node):
for node in sorted(graph):
Visit(node)
return ordered_nodes
+
+def CrossCompileRequested():
+ # TODO: figure out how to not build extra host objects in the
+ # non-cross-compile case when this is enabled, and enable unconditionally.
+ return (os.environ.get('GYP_CROSSCOMPILE') or
+ os.environ.get('AR_host') or
+ os.environ.get('CC_host') or
+ os.environ.get('CXX_host') or
+ os.environ.get('AR_target') or
+ os.environ.get('CC_target') or
+ os.environ.get('CXX_target'))
diff --git a/tools/gyp/pylib/gyp/flock_tool.py b/tools/gyp/pylib/gyp/flock_tool.py
index 3e7efff26ea80f..b38d8660f72853 100755
--- a/tools/gyp/pylib/gyp/flock_tool.py
+++ b/tools/gyp/pylib/gyp/flock_tool.py
@@ -40,7 +40,12 @@ def ExecFlock(self, lockfile, *cmd_list):
# with EBADF, that's why we use this F_SETLK
# hack instead.
fd = os.open(lockfile, os.O_WRONLY|os.O_NOCTTY|os.O_CREAT, 0666)
- op = struct.pack('hhllhhl', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0)
+ if sys.platform.startswith('aix'):
+ # Python on AIX is compiled with LARGEFILE support, which changes the
+ # struct size.
+ op = struct.pack('hhIllqq', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0)
+ else:
+ op = struct.pack('hhllhhl', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0)
fcntl.fcntl(fd, fcntl.F_SETLK, op)
return subprocess.call(cmd_list)
diff --git a/tools/gyp/pylib/gyp/generator/analyzer.py b/tools/gyp/pylib/gyp/generator/analyzer.py
new file mode 100644
index 00000000000000..15b80ef973793c
--- /dev/null
+++ b/tools/gyp/pylib/gyp/generator/analyzer.py
@@ -0,0 +1,569 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+This script is intended for use as a GYP_GENERATOR. It takes as input (by way of
+the generator flag config_path) the path of a json file that dictates the files
+and targets to search for. The following keys are supported:
+files: list of paths (relative) of the files to search for.
+targets: list of targets to search for. The target names are unqualified.
+
+The following is output:
+error: only supplied if there is an error.
+targets: the set of targets passed in via targets that either directly or
+ indirectly depend upon the set of paths supplied in files.
+build_targets: minimal set of targets that directly depend on the changed
+ files and need to be built. The expectation is this set of targets is passed
+ into a build step.
+status: outputs one of three values: none of the supplied files were found,
+ one of the include files changed so that it should be assumed everything
+ changed (in this case targets and build_targets are not output) or at
+ least one file was found.
+invalid_targets: list of supplied targets thare were not found.
+
+If the generator flag analyzer_output_path is specified, output is written
+there. Otherwise output is written to stdout.
+"""
+
+import gyp.common
+import gyp.ninja_syntax as ninja_syntax
+import json
+import os
+import posixpath
+import sys
+
+debug = False
+
+found_dependency_string = 'Found dependency'
+no_dependency_string = 'No dependencies'
+# Status when it should be assumed that everything has changed.
+all_changed_string = 'Found dependency (all)'
+
+# MatchStatus is used indicate if and how a target depends upon the supplied
+# sources.
+# The target's sources contain one of the supplied paths.
+MATCH_STATUS_MATCHES = 1
+# The target has a dependency on another target that contains one of the
+# supplied paths.
+MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2
+# The target's sources weren't in the supplied paths and none of the target's
+# dependencies depend upon a target that matched.
+MATCH_STATUS_DOESNT_MATCH = 3
+# The target doesn't contain the source, but the dependent targets have not yet
+# been visited to determine a more specific status yet.
+MATCH_STATUS_TBD = 4
+
+generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
+
+generator_wants_static_library_dependencies_adjusted = False
+
+generator_default_variables = {
+}
+for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR',
+ 'LIB_DIR', 'SHARED_LIB_DIR']:
+ generator_default_variables[dirname] = '!!!'
+
+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',
+ 'CONFIGURATION_NAME']:
+ generator_default_variables[unused] = ''
+
+
+def _ToGypPath(path):
+ """Converts a path to the format used by gyp."""
+ if os.sep == '\\' and os.altsep == '/':
+ return path.replace('\\', '/')
+ return path
+
+
+def _ResolveParent(path, base_path_components):
+ """Resolves |path|, which starts with at least one '../'. Returns an empty
+ string if the path shouldn't be considered. See _AddSources() for a
+ description of |base_path_components|."""
+ depth = 0
+ while path.startswith('../'):
+ depth += 1
+ path = path[3:]
+ # Relative includes may go outside the source tree. For example, an action may
+ # have inputs in /usr/include, which are not in the source tree.
+ if depth > len(base_path_components):
+ return ''
+ if depth == len(base_path_components):
+ return path
+ return '/'.join(base_path_components[0:len(base_path_components) - depth]) + \
+ '/' + path
+
+
+def _AddSources(sources, base_path, base_path_components, result):
+ """Extracts valid sources from |sources| and adds them to |result|. Each
+ source file is relative to |base_path|, but may contain '..'. To make
+ resolving '..' easier |base_path_components| contains each of the
+ directories in |base_path|. Additionally each source may contain variables.
+ Such sources are ignored as it is assumed dependencies on them are expressed
+ and tracked in some other means."""
+ # NOTE: gyp paths are always posix style.
+ for source in sources:
+ if not len(source) or source.startswith('!!!') or source.startswith('$'):
+ continue
+ # variable expansion may lead to //.
+ org_source = source
+ source = source[0] + source[1:].replace('//', '/')
+ if source.startswith('../'):
+ source = _ResolveParent(source, base_path_components)
+ if len(source):
+ result.append(source)
+ continue
+ result.append(base_path + source)
+ if debug:
+ print 'AddSource', org_source, result[len(result) - 1]
+
+
+def _ExtractSourcesFromAction(action, base_path, base_path_components,
+ results):
+ if 'inputs' in action:
+ _AddSources(action['inputs'], base_path, base_path_components, results)
+
+
+def _ToLocalPath(toplevel_dir, path):
+ """Converts |path| to a path relative to |toplevel_dir|."""
+ if path == toplevel_dir:
+ return ''
+ if path.startswith(toplevel_dir + '/'):
+ return path[len(toplevel_dir) + len('/'):]
+ return path
+
+
+def _ExtractSources(target, target_dict, toplevel_dir):
+ # |target| is either absolute or relative and in the format of the OS. Gyp
+ # source paths are always posix. Convert |target| to a posix path relative to
+ # |toplevel_dir_|. This is done to make it easy to build source paths.
+ base_path = posixpath.dirname(_ToLocalPath(toplevel_dir, _ToGypPath(target)))
+ base_path_components = base_path.split('/')
+
+ # Add a trailing '/' so that _AddSources() can easily build paths.
+ if len(base_path):
+ base_path += '/'
+
+ if debug:
+ print 'ExtractSources', target, base_path
+
+ results = []
+ if 'sources' in target_dict:
+ _AddSources(target_dict['sources'], base_path, base_path_components,
+ results)
+ # Include the inputs from any actions. Any changes to these affect the
+ # resulting output.
+ if 'actions' in target_dict:
+ for action in target_dict['actions']:
+ _ExtractSourcesFromAction(action, base_path, base_path_components,
+ results)
+ if 'rules' in target_dict:
+ for rule in target_dict['rules']:
+ _ExtractSourcesFromAction(rule, base_path, base_path_components, results)
+
+ return results
+
+
+class Target(object):
+ """Holds information about a particular target:
+ deps: set of Targets this Target depends upon. This is not recursive, only the
+ direct dependent Targets.
+ match_status: one of the MatchStatus values.
+ back_deps: set of Targets that have a dependency on this Target.
+ visited: used during iteration to indicate whether we've visited this target.
+ This is used for two iterations, once in building the set of Targets and
+ again in _GetBuildTargets().
+ name: fully qualified name of the target.
+ requires_build: True if the target type is such that it needs to be built.
+ See _DoesTargetTypeRequireBuild for details.
+ added_to_compile_targets: used when determining if the target was added to the
+ set of targets that needs to be built.
+ in_roots: true if this target is a descendant of one of the root nodes.
+ is_executable: true if the type of target is executable."""
+ def __init__(self, name):
+ self.deps = set()
+ self.match_status = MATCH_STATUS_TBD
+ self.back_deps = set()
+ self.name = name
+ # TODO(sky): I don't like hanging this off Target. This state is specific
+ # to certain functions and should be isolated there.
+ self.visited = False
+ self.requires_build = False
+ self.added_to_compile_targets = False
+ self.in_roots = False
+ self.is_executable = False
+
+
+class Config(object):
+ """Details what we're looking for
+ files: set of files to search for
+ targets: see file description for details."""
+ def __init__(self):
+ self.files = []
+ self.targets = set()
+
+ def Init(self, params):
+ """Initializes Config. This is a separate method as it raises an exception
+ if there is a parse error."""
+ generator_flags = params.get('generator_flags', {})
+ config_path = generator_flags.get('config_path', None)
+ if not config_path:
+ return
+ try:
+ f = open(config_path, 'r')
+ config = json.load(f)
+ f.close()
+ except IOError:
+ raise Exception('Unable to open file ' + config_path)
+ except ValueError as e:
+ raise Exception('Unable to parse config file ' + config_path + str(e))
+ if not isinstance(config, dict):
+ raise Exception('config_path must be a JSON file containing a dictionary')
+ self.files = config.get('files', [])
+ self.targets = set(config.get('targets', []))
+
+
+def _WasBuildFileModified(build_file, data, files, toplevel_dir):
+ """Returns true if the build file |build_file| is either in |files| or
+ one of the files included by |build_file| is in |files|. |toplevel_dir| is
+ the root of the source tree."""
+ if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files:
+ if debug:
+ print 'gyp file modified', build_file
+ return True
+
+ # First element of included_files is the file itself.
+ if len(data[build_file]['included_files']) <= 1:
+ return False
+
+ for include_file in data[build_file]['included_files'][1:]:
+ # |included_files| are relative to the directory of the |build_file|.
+ rel_include_file = \
+ _ToGypPath(gyp.common.UnrelativePath(include_file, build_file))
+ if _ToLocalPath(toplevel_dir, rel_include_file) in files:
+ if debug:
+ print 'included gyp file modified, gyp_file=', build_file, \
+ 'included file=', rel_include_file
+ return True
+ return False
+
+
+def _GetOrCreateTargetByName(targets, target_name):
+ """Creates or returns the Target at targets[target_name]. If there is no
+ Target for |target_name| one is created. Returns a tuple of whether a new
+ Target was created and the Target."""
+ if target_name in targets:
+ return False, targets[target_name]
+ target = Target(target_name)
+ targets[target_name] = target
+ return True, target
+
+
+def _DoesTargetTypeRequireBuild(target_dict):
+ """Returns true if the target type is such that it needs to be built."""
+ # If a 'none' target has rules or actions we assume it requires a build.
+ return target_dict['type'] != 'none' or \
+ target_dict.get('actions') or target_dict.get('rules')
+
+
+def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files,
+ build_files):
+ """Returns a tuple of the following:
+ . A dictionary mapping from fully qualified name to Target.
+ . A list of the targets that have a source file in |files|.
+ . Set of root Targets reachable from the the files |build_files|.
+ This sets the |match_status| of the targets that contain any of the source
+ files in |files| to MATCH_STATUS_MATCHES.
+ |toplevel_dir| is the root of the source tree."""
+ # Maps from target name to Target.
+ targets = {}
+
+ # Targets that matched.
+ matching_targets = []
+
+ # Queue of targets to visit.
+ targets_to_visit = target_list[:]
+
+ # Maps from build file to a boolean indicating whether the build file is in
+ # |files|.
+ build_file_in_files = {}
+
+ # Root targets across all files.
+ roots = set()
+
+ # Set of Targets in |build_files|.
+ build_file_targets = set()
+
+ while len(targets_to_visit) > 0:
+ target_name = targets_to_visit.pop()
+ created_target, target = _GetOrCreateTargetByName(targets, target_name)
+ if created_target:
+ roots.add(target)
+ elif target.visited:
+ continue
+
+ target.visited = True
+ target.requires_build = _DoesTargetTypeRequireBuild(
+ target_dicts[target_name])
+ target.is_executable = target_dicts[target_name]['type'] == 'executable'
+
+ build_file = gyp.common.ParseQualifiedTarget(target_name)[0]
+ if not build_file in build_file_in_files:
+ build_file_in_files[build_file] = \
+ _WasBuildFileModified(build_file, data, files, toplevel_dir)
+
+ if build_file in build_files:
+ build_file_targets.add(target)
+
+ # If a build file (or any of its included files) is modified we assume all
+ # targets in the file are modified.
+ if build_file_in_files[build_file]:
+ print 'matching target from modified build file', target_name
+ target.match_status = MATCH_STATUS_MATCHES
+ matching_targets.append(target)
+ else:
+ sources = _ExtractSources(target_name, target_dicts[target_name],
+ toplevel_dir)
+ for source in sources:
+ if source in files:
+ print 'target', target_name, 'matches', source
+ target.match_status = MATCH_STATUS_MATCHES
+ matching_targets.append(target)
+ break
+
+ # Add dependencies to visit as well as updating back pointers for deps.
+ for dep in target_dicts[target_name].get('dependencies', []):
+ targets_to_visit.append(dep)
+
+ created_dep_target, dep_target = _GetOrCreateTargetByName(targets, dep)
+ if not created_dep_target:
+ roots.discard(dep_target)
+
+ target.deps.add(dep_target)
+ dep_target.back_deps.add(target)
+
+ return targets, matching_targets, roots & build_file_targets
+
+
+def _GetUnqualifiedToTargetMapping(all_targets, to_find):
+ """Returns a mapping (dictionary) from unqualified name to Target for all the
+ Targets in |to_find|."""
+ result = {}
+ if not to_find:
+ return result
+ to_find = set(to_find)
+ for target_name in all_targets.keys():
+ extracted = gyp.common.ParseQualifiedTarget(target_name)
+ if len(extracted) > 1 and extracted[1] in to_find:
+ to_find.remove(extracted[1])
+ result[extracted[1]] = all_targets[target_name]
+ if not to_find:
+ return result
+ return result
+
+
+def _DoesTargetDependOn(target):
+ """Returns true if |target| or any of its dependencies matches the supplied
+ set of paths. This updates |matches| of the Targets as it recurses.
+ target: the Target to look for."""
+ if target.match_status == MATCH_STATUS_DOESNT_MATCH:
+ return False
+ if target.match_status == MATCH_STATUS_MATCHES or \
+ target.match_status == MATCH_STATUS_MATCHES_BY_DEPENDENCY:
+ return True
+ for dep in target.deps:
+ if _DoesTargetDependOn(dep):
+ target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
+ return True
+ target.match_status = MATCH_STATUS_DOESNT_MATCH
+ return False
+
+
+def _GetTargetsDependingOn(possible_targets):
+ """Returns the list of Targets in |possible_targets| that depend (either
+ directly on indirectly) on the matched targets.
+ possible_targets: targets to search from."""
+ found = []
+ for target in possible_targets:
+ if _DoesTargetDependOn(target):
+ found.append(target)
+ return found
+
+
+def _AddBuildTargets(target, roots, add_if_no_ancestor, result):
+ """Recurses through all targets that depend on |target|, adding all targets
+ that need to be built (and are in |roots|) to |result|.
+ roots: set of root targets.
+ add_if_no_ancestor: If true and there are no ancestors of |target| then add
+ |target| to |result|. |target| must still be in |roots|.
+ result: targets that need to be built are added here."""
+ if target.visited:
+ return
+
+ target.visited = True
+ target.in_roots = not target.back_deps and target in roots
+
+ for back_dep_target in target.back_deps:
+ _AddBuildTargets(back_dep_target, roots, False, result)
+ target.added_to_compile_targets |= back_dep_target.added_to_compile_targets
+ target.in_roots |= back_dep_target.in_roots
+
+ # Always add 'executable' targets. Even though they may be built by other
+ # targets that depend upon them it makes detection of what is going to be
+ # built easier.
+ if target.in_roots and \
+ (target.is_executable or
+ (not target.added_to_compile_targets and
+ (add_if_no_ancestor or target.requires_build))):
+ result.add(target)
+ target.added_to_compile_targets = True
+
+
+def _GetBuildTargets(matching_targets, roots):
+ """Returns the set of Targets that require a build.
+ matching_targets: targets that changed and need to be built.
+ roots: set of root targets in the build files to search from."""
+ result = set()
+ for target in matching_targets:
+ _AddBuildTargets(target, roots, True, result)
+ return result
+
+
+def _WriteOutput(params, **values):
+ """Writes the output, either to stdout or a file is specified."""
+ if 'error' in values:
+ print 'Error:', values['error']
+ if 'status' in values:
+ print values['status']
+ if 'targets' in values:
+ values['targets'].sort()
+ print 'Supplied targets that depend on changed files:'
+ for target in values['targets']:
+ print '\t', target
+ if 'invalid_targets' in values:
+ values['invalid_targets'].sort()
+ print 'The following targets were not found:'
+ for target in values['invalid_targets']:
+ print '\t', target
+ if 'build_targets' in values:
+ values['build_targets'].sort()
+ print 'Targets that require a build:'
+ for target in values['build_targets']:
+ print '\t', target
+
+ output_path = params.get('generator_flags', {}).get(
+ 'analyzer_output_path', None)
+ if not output_path:
+ print json.dumps(values)
+ return
+ try:
+ f = open(output_path, 'w')
+ f.write(json.dumps(values) + '\n')
+ f.close()
+ except IOError as e:
+ print 'Error writing to output file', output_path, str(e)
+
+
+def _WasGypIncludeFileModified(params, files):
+ """Returns true if one of the files in |files| is in the set of included
+ files."""
+ if params['options'].includes:
+ for include in params['options'].includes:
+ if _ToGypPath(include) in files:
+ print 'Include file modified, assuming all changed', include
+ return True
+ return False
+
+
+def _NamesNotIn(names, mapping):
+ """Returns a list of the values in |names| that are not in |mapping|."""
+ return [name for name in names if name not in mapping]
+
+
+def _LookupTargets(names, mapping):
+ """Returns a list of the mapping[name] for each value in |names| that is in
+ |mapping|."""
+ return [mapping[name] for name in names if name in mapping]
+
+
+def CalculateVariables(default_variables, params):
+ """Calculate additional variables for use in the build (called by gyp)."""
+ flavor = gyp.common.GetFlavor(params)
+ if flavor == 'mac':
+ default_variables.setdefault('OS', 'mac')
+ elif flavor == 'win':
+ default_variables.setdefault('OS', 'win')
+ # Copy additional generator configuration data from VS, which is shared
+ # by the Windows Ninja generator.
+ import gyp.generator.msvs as msvs_generator
+ generator_additional_non_configuration_keys = getattr(msvs_generator,
+ 'generator_additional_non_configuration_keys', [])
+ generator_additional_path_sections = getattr(msvs_generator,
+ 'generator_additional_path_sections', [])
+
+ gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
+ else:
+ operating_system = flavor
+ if flavor == 'android':
+ operating_system = 'linux' # Keep this legacy behavior for now.
+ default_variables.setdefault('OS', operating_system)
+
+
+def GenerateOutput(target_list, target_dicts, data, params):
+ """Called by gyp as the final stage. Outputs results."""
+ config = Config()
+ try:
+ config.Init(params)
+ if not config.files:
+ raise Exception('Must specify files to analyze via config_path generator '
+ 'flag')
+
+ toplevel_dir = _ToGypPath(os.path.abspath(params['options'].toplevel_dir))
+ if debug:
+ print 'toplevel_dir', toplevel_dir
+
+ if _WasGypIncludeFileModified(params, config.files):
+ result_dict = { 'status': all_changed_string,
+ 'targets': list(config.targets) }
+ _WriteOutput(params, **result_dict)
+ return
+
+ all_targets, matching_targets, roots = _GenerateTargets(
+ data, target_list, target_dicts, toplevel_dir, frozenset(config.files),
+ params['build_files'])
+
+ unqualified_mapping = _GetUnqualifiedToTargetMapping(all_targets,
+ config.targets)
+ invalid_targets = None
+ if len(unqualified_mapping) != len(config.targets):
+ invalid_targets = _NamesNotIn(config.targets, unqualified_mapping)
+
+ if matching_targets:
+ search_targets = _LookupTargets(config.targets, unqualified_mapping)
+ matched_search_targets = _GetTargetsDependingOn(search_targets)
+ # Reset the visited status for _GetBuildTargets.
+ for target in all_targets.itervalues():
+ target.visited = False
+ build_targets = _GetBuildTargets(matching_targets, roots)
+ matched_search_targets = [gyp.common.ParseQualifiedTarget(target.name)[1]
+ for target in matched_search_targets]
+ build_targets = [gyp.common.ParseQualifiedTarget(target.name)[1]
+ for target in build_targets]
+ else:
+ matched_search_targets = []
+ build_targets = []
+
+ result_dict = { 'targets': matched_search_targets,
+ 'status': found_dependency_string if matching_targets else
+ no_dependency_string,
+ 'build_targets': build_targets}
+ if invalid_targets:
+ result_dict['invalid_targets'] = invalid_targets
+ _WriteOutput(params, **result_dict)
+
+ except Exception as e:
+ _WriteOutput(params, error=str(e))
diff --git a/tools/gyp/pylib/gyp/generator/android.py b/tools/gyp/pylib/gyp/generator/android.py
index 41346e2b1c35f7..ad6eff6d17b17d 100644
--- a/tools/gyp/pylib/gyp/generator/android.py
+++ b/tools/gyp/pylib/gyp/generator/android.py
@@ -50,12 +50,14 @@
generator_additional_non_configuration_keys = [
# Boolean to declare that this target does not want its name mangled.
'android_unmangled_name',
+ # Map of android build system variables to set.
+ 'aosp_build_settings',
]
generator_additional_path_sections = []
generator_extra_sources_for_rules = []
-SHARED_FOOTER = """\
+ALL_MODULES_FOOTER = """\
# "gyp_all_modules" is a concatenation of the "gyp_all_modules" targets from
# all the included sub-makefiles. This is just here to clarify.
gyp_all_modules:
@@ -66,33 +68,6 @@
"""
-android_standard_include_paths = set([
- # JNI_H_INCLUDE in build/core/binary.mk
- 'dalvik/libnativehelper/include/nativehelper',
- # from SRC_HEADERS in build/core/config.mk
- 'system/core/include',
- 'hardware/libhardware/include',
- 'hardware/libhardware_legacy/include',
- 'hardware/ril/include',
- 'dalvik/libnativehelper/include',
- 'frameworks/native/include',
- 'frameworks/native/opengl/include',
- 'frameworks/base/include',
- 'frameworks/base/opengl/include',
- 'frameworks/base/native/include',
- 'external/skia/include',
- # TARGET_C_INCLUDES in build/core/combo/TARGET_linux-arm.mk
- 'bionic/libc/arch-arm/include',
- 'bionic/libc/include',
- 'bionic/libstdc++/include',
- 'bionic/libc/kernel/common',
- 'bionic/libc/kernel/arch-arm',
- 'bionic/libm/include',
- 'bionic/libm/include/arm',
- 'bionic/libthread_db/include',
- ])
-
-
# Map gyp target types to Android module classes.
MODULE_CLASSES = {
'static_library': 'STATIC_LIBRARIES',
@@ -133,7 +108,7 @@ def __init__(self, android_top_dir):
self.android_top_dir = android_top_dir
def Write(self, qualified_target, relative_target, base_path, output_filename,
- spec, configs, part_of_all):
+ spec, configs, part_of_all, write_alias_target, sdk_version):
"""The main entry point: writes a .mk file for a single target.
Arguments:
@@ -144,6 +119,9 @@ def Write(self, qualified_target, relative_target, base_path, output_filename,
output_filename: output .mk file name to write
spec, configs: gyp info
part_of_all: flag indicating this target is part of 'all'
+ write_alias_target: flag indicating whether to create short aliases for
+ this target
+ sdk_version: what to emit for LOCAL_SDK_VERSION in output
"""
gyp.common.EnsureDirExists(output_filename)
@@ -183,14 +161,23 @@ def Write(self, qualified_target, relative_target, base_path, output_filename,
if self.android_stem != self.android_module:
self.WriteLn('LOCAL_MODULE_STEM := ' + self.android_stem)
self.WriteLn('LOCAL_MODULE_SUFFIX := ' + self.android_suffix)
- self.WriteLn('LOCAL_MODULE_TAGS := optional')
if self.toolset == 'host':
self.WriteLn('LOCAL_IS_HOST_MODULE := true')
+ self.WriteLn('LOCAL_MULTILIB := $(GYP_HOST_MULTILIB)')
+ else:
+ self.WriteLn('LOCAL_MODULE_TARGET_ARCH := '
+ '$(TARGET_$(GYP_VAR_PREFIX)ARCH)')
+ self.WriteLn('LOCAL_SDK_VERSION := %s' % sdk_version)
# Grab output directories; needed for Actions and Rules.
- self.WriteLn('gyp_intermediate_dir := $(call local-intermediates-dir)')
+ if self.toolset == 'host':
+ self.WriteLn('gyp_intermediate_dir := '
+ '$(call local-intermediates-dir,,$(GYP_HOST_VAR_PREFIX))')
+ else:
+ self.WriteLn('gyp_intermediate_dir := '
+ '$(call local-intermediates-dir,,$(GYP_VAR_PREFIX))')
self.WriteLn('gyp_shared_intermediate_dir := '
- '$(call intermediates-dir-for,GYP,shared)')
+ '$(call intermediates-dir-for,GYP,shared,,,$(GYP_VAR_PREFIX))')
self.WriteLn()
# List files this target depends on so that actions/rules/copies/sources
@@ -226,7 +213,8 @@ def Write(self, qualified_target, relative_target, base_path, output_filename,
if spec.get('sources', []) or extra_sources:
self.WriteSources(spec, configs, extra_sources)
- self.WriteTarget(spec, configs, deps, link_deps, part_of_all)
+ self.WriteTarget(spec, configs, deps, link_deps, part_of_all,
+ write_alias_target)
# Update global list of target outputs, used in dependency tracking.
target_outputs[qualified_target] = ('path', self.output_binary)
@@ -291,6 +279,7 @@ def WriteActions(self, actions, extra_sources, extra_outputs):
# writing duplicate dummy rules for those outputs.
main_output = make.QuoteSpaces(self.LocalPathify(outputs[0]))
self.WriteLn('%s: gyp_local_path := $(LOCAL_PATH)' % main_output)
+ self.WriteLn('%s: gyp_var_prefix := $(GYP_VAR_PREFIX)' % main_output)
self.WriteLn('%s: gyp_intermediate_dir := '
'$(abspath $(gyp_intermediate_dir))' % main_output)
self.WriteLn('%s: gyp_shared_intermediate_dir := '
@@ -305,12 +294,19 @@ def WriteActions(self, actions, extra_sources, extra_outputs):
self.WriteLn('%s: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))'
% main_output)
+ # Don't allow spaces in input/output filenames, but make an exception for
+ # filenames which start with '$(' since it's okay for there to be spaces
+ # inside of make function/macro invocations.
for input in inputs:
- assert ' ' not in input, (
- "Spaces in action input filenames not supported (%s)" % input)
+ if not input.startswith('$(') and ' ' in input:
+ raise gyp.common.GypError(
+ 'Action input filename "%s" in target %s contains a space' %
+ (input, self.target))
for output in outputs:
- assert ' ' not in output, (
- "Spaces in action output filenames not supported (%s)" % output)
+ if not output.startswith('$(') and ' ' in output:
+ raise gyp.common.GypError(
+ 'Action output filename "%s" in target %s contains a space' %
+ (output, self.target))
self.WriteLn('%s: %s $(GYP_TARGET_DEPENDENCIES)' %
(main_output, ' '.join(map(self.LocalPathify, inputs))))
@@ -337,13 +333,10 @@ def WriteRules(self, rules, extra_sources, extra_outputs):
"""
if len(rules) == 0:
return
- rule_trigger = '%s_rule_trigger' % self.android_module
- did_write_rule = False
for rule in rules:
if len(rule.get('rule_sources', [])) == 0:
continue
- did_write_rule = True
name = make.StringToMakefileVariable('%s_%s' % (self.relative_target,
rule['rule_name']))
self.WriteLn('\n### Generated for rule "%s":' % name)
@@ -391,6 +384,7 @@ def WriteRules(self, rules, extra_sources, extra_outputs):
outputs = map(self.LocalPathify, outputs)
main_output = outputs[0]
self.WriteLn('%s: gyp_local_path := $(LOCAL_PATH)' % main_output)
+ self.WriteLn('%s: gyp_var_prefix := $(GYP_VAR_PREFIX)' % main_output)
self.WriteLn('%s: gyp_intermediate_dir := '
'$(abspath $(gyp_intermediate_dir))' % main_output)
self.WriteLn('%s: gyp_shared_intermediate_dir := '
@@ -412,13 +406,9 @@ def WriteRules(self, rules, extra_sources, extra_outputs):
# Make each output depend on the main output, with an empty command
# to force make to notice that the mtime has changed.
self.WriteLn('%s: %s ;' % (output, main_output))
- self.WriteLn('.PHONY: %s' % (rule_trigger))
- self.WriteLn('%s: %s' % (rule_trigger, main_output))
- self.WriteLn('')
- if did_write_rule:
- extra_sources.append(rule_trigger) # Force all rules to run.
- self.WriteLn('### Finished generating for all rules')
- self.WriteLn('')
+ self.WriteLn()
+
+ self.WriteLn()
def WriteCopies(self, copies, extra_outputs):
@@ -501,6 +491,9 @@ def WriteSourceFlags(self, spec, configs):
self.WriteLn('LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) '
'$(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))')
self.WriteLn('LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))')
+ # Android uses separate flags for assembly file invocations, but gyp expects
+ # the same CFLAGS to be applied:
+ self.WriteLn('LOCAL_ASFLAGS := $(LOCAL_CFLAGS)')
def WriteSources(self, spec, configs, extra_sources):
@@ -609,16 +602,16 @@ def ComputeAndroidModule(self, spec):
prefix = ''
if spec['toolset'] == 'host':
- suffix = '_host_gyp'
+ suffix = '_$(TARGET_$(GYP_VAR_PREFIX)ARCH)_host_gyp'
else:
suffix = '_gyp'
if self.path:
- name = '%s%s_%s%s' % (prefix, self.path, self.target, suffix)
+ middle = make.StringToMakefileVariable('%s_%s' % (self.path, self.target))
else:
- name = '%s%s%s' % (prefix, self.target, suffix)
+ middle = make.StringToMakefileVariable(self.target)
- return make.StringToMakefileVariable(name)
+ return ''.join([prefix, middle, suffix])
def ComputeOutputParts(self, spec):
@@ -672,32 +665,31 @@ def ComputeOutput(self, spec):
E.g., the loadable module 'foobar' in directory 'baz' will produce
'$(obj)/baz/libfoobar.so'
"""
- if self.type == 'executable' and self.toolset == 'host':
+ if self.type == 'executable':
# We install host executables into shared_intermediate_dir so they can be
# run by gyp rules that refer to PRODUCT_DIR.
path = '$(gyp_shared_intermediate_dir)'
elif self.type == 'shared_library':
if self.toolset == 'host':
- path = '$(HOST_OUT_INTERMEDIATE_LIBRARIES)'
+ path = '$($(GYP_HOST_VAR_PREFIX)HOST_OUT_INTERMEDIATE_LIBRARIES)'
else:
- path = '$(TARGET_OUT_INTERMEDIATE_LIBRARIES)'
+ path = '$($(GYP_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)'
else:
# Other targets just get built into their intermediate dir.
if self.toolset == 'host':
- path = '$(call intermediates-dir-for,%s,%s,true)' % (self.android_class,
- self.android_module)
+ path = ('$(call intermediates-dir-for,%s,%s,true,,'
+ '$(GYP_HOST_VAR_PREFIX))' % (self.android_class,
+ self.android_module))
else:
- path = '$(call intermediates-dir-for,%s,%s)' % (self.android_class,
- self.android_module)
+ path = ('$(call intermediates-dir-for,%s,%s,,,$(GYP_VAR_PREFIX))'
+ % (self.android_class, self.android_module))
assert spec.get('product_dir') is None # TODO: not supported?
return os.path.join(path, self.ComputeOutputBasename(spec))
def NormalizeIncludePaths(self, include_paths):
""" Normalize include_paths.
- Convert absolute paths to relative to the Android top directory;
- filter out include paths that are already brought in by the Android build
- system.
+ Convert absolute paths to relative to the Android top directory.
Args:
include_paths: A list of unprocessed include paths.
@@ -708,10 +700,7 @@ def NormalizeIncludePaths(self, include_paths):
for path in include_paths:
if path[0] == '/':
path = gyp.common.RelativePath(path, self.android_top_dir)
-
- # Filter out the Android standard search path.
- if path not in android_standard_include_paths:
- normalized.append(path)
+ normalized.append(path)
return normalized
def ExtractIncludesFromCFlags(self, cflags):
@@ -732,16 +721,20 @@ def ExtractIncludesFromCFlags(self, cflags):
return (clean_cflags, include_paths)
- def ComputeAndroidLibraryModuleNames(self, libraries):
- """Compute the Android module names from libraries, ie spec.get('libraries')
+ def FilterLibraries(self, libraries):
+ """Filter the 'libraries' key to separate things that shouldn't be ldflags.
+
+ Library entries that look like filenames should be converted to android
+ module names instead of being passed to the linker as flags.
Args:
libraries: the value of spec.get('libraries')
Returns:
- A tuple (static_lib_modules, dynamic_lib_modules)
+ A tuple (static_lib_modules, dynamic_lib_modules, ldflags)
"""
static_lib_modules = []
dynamic_lib_modules = []
+ ldflags = []
for libs in libraries:
# Libs can have multiple words.
for lib in libs.split():
@@ -758,13 +751,9 @@ def ComputeAndroidLibraryModuleNames(self, libraries):
if match:
dynamic_lib_modules.append(match.group(1))
continue
- # "-lstlport" -> libstlport
if lib.startswith('-l'):
- if lib.endswith('_static'):
- static_lib_modules.append('lib' + lib[2:])
- else:
- dynamic_lib_modules.append('lib' + lib[2:])
- return (static_lib_modules, dynamic_lib_modules)
+ ldflags.append(lib)
+ return (static_lib_modules, dynamic_lib_modules, ldflags)
def ComputeDeps(self, spec):
@@ -792,47 +781,74 @@ def WriteTargetFlags(self, spec, configs, link_deps):
spec, configs: input from gyp.
link_deps: link dependency list; see ComputeDeps()
"""
- for configname, config in sorted(configs.iteritems()):
- ldflags = list(config.get('ldflags', []))
- self.WriteLn('')
- self.WriteList(ldflags, 'LOCAL_LDFLAGS_%s' % configname)
- self.WriteLn('\nLOCAL_LDFLAGS := $(LOCAL_LDFLAGS_$(GYP_CONFIGURATION))')
-
# Libraries (i.e. -lfoo)
+ # These must be included even for static libraries as some of them provide
+ # implicit include paths through the build system.
libraries = gyp.common.uniquer(spec.get('libraries', []))
- static_libs, dynamic_libs = self.ComputeAndroidLibraryModuleNames(
- libraries)
-
- # Link dependencies (i.e. libfoo.a, libfoo.so)
- static_link_deps = [x[1] for x in link_deps if x[0] == 'static']
- shared_link_deps = [x[1] for x in link_deps if x[0] == 'shared']
- self.WriteLn('')
- self.WriteList(static_libs + static_link_deps,
- 'LOCAL_STATIC_LIBRARIES')
- self.WriteLn('# Enable grouping to fix circular references')
- self.WriteLn('LOCAL_GROUP_STATIC_LIBRARIES := true')
- self.WriteLn('')
- self.WriteList(dynamic_libs + shared_link_deps,
- 'LOCAL_SHARED_LIBRARIES')
-
-
- def WriteTarget(self, spec, configs, deps, link_deps, part_of_all):
+ static_libs, dynamic_libs, ldflags_libs = self.FilterLibraries(libraries)
+
+ if self.type != 'static_library':
+ for configname, config in sorted(configs.iteritems()):
+ ldflags = list(config.get('ldflags', []))
+ self.WriteLn('')
+ self.WriteList(ldflags, 'LOCAL_LDFLAGS_%s' % configname)
+ self.WriteList(ldflags_libs, 'LOCAL_GYP_LIBS')
+ self.WriteLn('LOCAL_LDFLAGS := $(LOCAL_LDFLAGS_$(GYP_CONFIGURATION)) '
+ '$(LOCAL_GYP_LIBS)')
+
+ # Link dependencies (i.e. other gyp targets this target depends on)
+ # These need not be included for static libraries as within the gyp build
+ # we do not use the implicit include path mechanism.
+ if self.type != 'static_library':
+ static_link_deps = [x[1] for x in link_deps if x[0] == 'static']
+ shared_link_deps = [x[1] for x in link_deps if x[0] == 'shared']
+ else:
+ static_link_deps = []
+ shared_link_deps = []
+
+ # Only write the lists if they are non-empty.
+ if static_libs or static_link_deps:
+ self.WriteLn('')
+ self.WriteList(static_libs + static_link_deps,
+ 'LOCAL_STATIC_LIBRARIES')
+ self.WriteLn('# Enable grouping to fix circular references')
+ self.WriteLn('LOCAL_GROUP_STATIC_LIBRARIES := true')
+ if dynamic_libs or shared_link_deps:
+ self.WriteLn('')
+ self.WriteList(dynamic_libs + shared_link_deps,
+ 'LOCAL_SHARED_LIBRARIES')
+
+
+ def WriteTarget(self, spec, configs, deps, link_deps, part_of_all,
+ write_alias_target):
"""Write Makefile code to produce the final target of the gyp spec.
spec, configs: input from gyp.
deps, link_deps: dependency lists; see ComputeDeps()
part_of_all: flag indicating this target is part of 'all'
+ write_alias_target: flag indicating whether to create short aliases for this
+ target
"""
self.WriteLn('### Rules for final target.')
if self.type != 'none':
self.WriteTargetFlags(spec, configs, link_deps)
+ settings = spec.get('aosp_build_settings', {})
+ if settings:
+ self.WriteLn('### Set directly by aosp_build_settings.')
+ for k, v in settings.iteritems():
+ if isinstance(v, list):
+ self.WriteList(v, k)
+ else:
+ self.WriteLn('%s := %s' % (k, make.QuoteIfNecessary(v)))
+ self.WriteLn('')
+
# Add to the set of targets which represent the gyp 'all' target. We use the
# name 'gyp_all_modules' as the Android build system doesn't allow the use
# of the Make target 'all' and because 'all_modules' is the equivalent of
# the Make target 'all' on Android.
- if part_of_all:
+ if part_of_all and write_alias_target:
self.WriteLn('# Add target alias to "gyp_all_modules" target.')
self.WriteLn('.PHONY: gyp_all_modules')
self.WriteLn('gyp_all_modules: %s' % self.android_module)
@@ -841,7 +857,7 @@ def WriteTarget(self, spec, configs, deps, link_deps, part_of_all):
# Add an alias from the gyp target name to the Android module name. This
# simplifies manual builds of the target, and is required by the test
# framework.
- if self.target != self.android_module:
+ if self.target != self.android_module and write_alias_target:
self.WriteLn('# Alias gyp target name.')
self.WriteLn('.PHONY: %s' % self.target)
self.WriteLn('%s: %s' % (self.target, self.android_module))
@@ -859,17 +875,17 @@ def WriteTarget(self, spec, configs, deps, link_deps, part_of_all):
self.WriteLn('LOCAL_PRELINK_MODULE := false')
self.WriteLn('include $(BUILD_%sSHARED_LIBRARY)' % modifier)
elif self.type == 'executable':
- if self.toolset == 'host':
- self.WriteLn('LOCAL_MODULE_PATH := $(gyp_shared_intermediate_dir)')
- else:
- # Don't install target executables for now, as it results in them being
- # included in ROM. This can be revisited if there's a reason to install
- # them later.
- self.WriteLn('LOCAL_UNINSTALLABLE_MODULE := true')
+ # Executables are for build and test purposes only, so they're installed
+ # to a directory that doesn't get included in the system image.
+ self.WriteLn('LOCAL_MODULE_PATH := $(gyp_shared_intermediate_dir)')
self.WriteLn('include $(BUILD_%sEXECUTABLE)' % modifier)
else:
self.WriteLn('LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp')
self.WriteLn('LOCAL_UNINSTALLABLE_MODULE := true')
+ if self.toolset == 'target':
+ self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX := $(GYP_VAR_PREFIX)')
+ else:
+ self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX := $(GYP_HOST_VAR_PREFIX)')
self.WriteLn()
self.WriteLn('include $(BUILD_SYSTEM)/base_rules.mk')
self.WriteLn()
@@ -877,6 +893,8 @@ def WriteTarget(self, spec, configs, deps, link_deps, part_of_all):
self.WriteLn('\t$(hide) echo "Gyp timestamp: $@"')
self.WriteLn('\t$(hide) mkdir -p $(dir $@)')
self.WriteLn('\t$(hide) touch $@')
+ self.WriteLn()
+ self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX :=')
def WriteList(self, value_list, variable=None, prefix='',
@@ -926,7 +944,7 @@ def ExpandInputRoot(self, template, expansion, dirname):
'INPUT_ROOT': expansion,
'INPUT_DIRNAME': dirname,
}
- return path
+ return os.path.normpath(path)
def PerformBuild(data, configurations, params):
@@ -946,6 +964,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
generator_flags = params.get('generator_flags', {})
builddir_name = generator_flags.get('output_dir', 'out')
limit_to_target_all = generator_flags.get('limit_to_target_all', False)
+ write_alias_targets = generator_flags.get('write_alias_targets', True)
+ sdk_version = generator_flags.get('aosp_sdk_version', 19)
android_top_dir = os.environ.get('ANDROID_BUILD_TOP')
assert android_top_dir, '$ANDROID_BUILD_TOP not set; you need to run lunch.'
@@ -1031,8 +1051,7 @@ def CalculateMakefilePath(build_file, base_name):
spec = target_dicts[qualified_target]
configs = spec['configurations']
- part_of_all = (qualified_target in needed_targets and
- not int(spec.get('suppress_wildcard', False)))
+ part_of_all = qualified_target in needed_targets
if limit_to_target_all and not part_of_all:
continue
@@ -1041,7 +1060,9 @@ def CalculateMakefilePath(build_file, base_name):
writer = AndroidMkWriter(android_top_dir)
android_module = writer.Write(qualified_target, relative_target, base_path,
output_file, spec, configs,
- part_of_all=part_of_all)
+ part_of_all=part_of_all,
+ write_alias_target=write_alias_targets,
+ sdk_version=sdk_version)
if android_module in android_modules:
print ('ERROR: Android module names must be unique. The following '
'targets both generate Android module name %s.\n %s\n %s' %
@@ -1057,6 +1078,9 @@ def CalculateMakefilePath(build_file, base_name):
include_list.add(mkfile_rel_path)
root_makefile.write('GYP_CONFIGURATION ?= %s\n' % default_configuration)
+ root_makefile.write('GYP_VAR_PREFIX ?=\n')
+ root_makefile.write('GYP_HOST_VAR_PREFIX ?=\n')
+ root_makefile.write('GYP_HOST_MULTILIB ?=\n')
# Write out the sorted list of includes.
root_makefile.write('\n')
@@ -1064,6 +1088,7 @@ def CalculateMakefilePath(build_file, base_name):
root_makefile.write('include $(LOCAL_PATH)/' + include_file + '\n')
root_makefile.write('\n')
- root_makefile.write(SHARED_FOOTER)
+ if write_alias_targets:
+ root_makefile.write(ALL_MODULES_FOOTER)
root_makefile.close()
diff --git a/tools/gyp/pylib/gyp/generator/cmake.py b/tools/gyp/pylib/gyp/generator/cmake.py
index 10d015ee83159e..8f5feddee1cb35 100644
--- a/tools/gyp/pylib/gyp/generator/cmake.py
+++ b/tools/gyp/pylib/gyp/generator/cmake.py
@@ -216,7 +216,7 @@ def WriteVariable(output, variable_name, prepend=None):
output.write('}')
-class CMakeTargetType:
+class CMakeTargetType(object):
def __init__(self, command, modifier, property_modifier):
self.command = command
self.modifier = modifier
@@ -464,7 +464,7 @@ def WriteCopies(target_name, copies, extra_deps, path_to_gyp, output):
extra_deps.append(copy_name)
return
- class Copy:
+ class Copy(object):
def __init__(self, ext, command):
self.cmake_inputs = []
self.cmake_outputs = []
@@ -743,7 +743,7 @@ def partition(l, p):
if target_output_directory is None:
if target_type in ('executable', 'loadable_module'):
target_output_directory = generator_default_variables['PRODUCT_DIR']
- elif target_type in ('shared_library'):
+ elif target_type == 'shared_library':
target_output_directory = '${builddir}/lib.${TOOLSET}'
elif spec.get('standalone_static_library', False):
target_output_directory = generator_default_variables['PRODUCT_DIR']
diff --git a/tools/gyp/pylib/gyp/generator/eclipse.py b/tools/gyp/pylib/gyp/generator/eclipse.py
index 84380b04da2444..3544347b3bacd4 100644
--- a/tools/gyp/pylib/gyp/generator/eclipse.py
+++ b/tools/gyp/pylib/gyp/generator/eclipse.py
@@ -24,6 +24,7 @@
import gyp.common
import gyp.msvs_emulation
import shlex
+import xml.etree.cElementTree as ET
generator_wants_static_library_dependencies_adjusted = False
@@ -31,8 +32,8 @@
}
for dirname in ['INTERMEDIATE_DIR', 'PRODUCT_DIR', 'LIB_DIR', 'SHARED_LIB_DIR']:
- # Some gyp steps fail if these are empty(!).
- generator_default_variables[dirname] = 'dir'
+ # Some gyp steps fail if these are empty(!), so we convert them to variables
+ generator_default_variables[dirname] = '$' + dirname
for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
@@ -77,7 +78,8 @@ def CalculateGeneratorInputInfo(params):
def GetAllIncludeDirectories(target_list, target_dicts,
- shared_intermediate_dirs, config_name, params):
+ shared_intermediate_dirs, config_name, params,
+ compiler_path):
"""Calculate the set of include directories to be used.
Returns:
@@ -88,6 +90,33 @@ def GetAllIncludeDirectories(target_list, target_dicts,
gyp_includes_set = set()
compiler_includes_list = []
+ # Find compiler's default include dirs.
+ if compiler_path:
+ command = shlex.split(compiler_path)
+ command.extend(['-E', '-xc++', '-v', '-'])
+ proc = subprocess.Popen(args=command, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output = proc.communicate()[1]
+ # Extract the list of include dirs from the output, which has this format:
+ # ...
+ # #include "..." search starts here:
+ # #include <...> search starts here:
+ # /usr/include/c++/4.6
+ # /usr/local/include
+ # End of search list.
+ # ...
+ in_include_list = False
+ for line in output.splitlines():
+ if line.startswith('#include'):
+ in_include_list = True
+ continue
+ if line.startswith('End of search list.'):
+ break
+ if in_include_list:
+ include_dir = line.strip()
+ if include_dir not in compiler_includes_list:
+ compiler_includes_list.append(include_dir)
+
flavor = gyp.common.GetFlavor(params)
if flavor == 'win':
generator_flags = params.get('generator_flags', {})
@@ -106,11 +135,10 @@ def GetAllIncludeDirectories(target_list, target_dicts,
else:
cflags = config['cflags']
for cflag in cflags:
- include_dir = ''
if cflag.startswith('-I'):
include_dir = cflag[2:]
- if include_dir and not include_dir in compiler_includes_list:
- compiler_includes_list.append(include_dir)
+ if include_dir not in compiler_includes_list:
+ compiler_includes_list.append(include_dir)
# Find standard gyp include dirs.
if config.has_key('include_dirs'):
@@ -125,9 +153,7 @@ def GetAllIncludeDirectories(target_list, target_dicts,
include_dir = base_dir + '/' + include_dir
include_dir = os.path.abspath(include_dir)
- if not include_dir in gyp_includes_set:
- gyp_includes_set.add(include_dir)
-
+ gyp_includes_set.add(include_dir)
# Generate a list that has all the include dirs.
all_includes_list = list(gyp_includes_set)
@@ -140,7 +166,7 @@ def GetAllIncludeDirectories(target_list, target_dicts,
return all_includes_list
-def GetCompilerPath(target_list, target_dicts, data):
+def GetCompilerPath(target_list, data, options):
"""Determine a command that can be used to invoke the compiler.
Returns:
@@ -148,13 +174,12 @@ def GetCompilerPath(target_list, target_dicts, data):
the compiler from that. Otherwise, see if a compiler was specified via the
CC_target environment variable.
"""
-
# First, see if the compiler is configured in make's settings.
build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
make_global_settings_dict = data[build_file].get('make_global_settings', {})
for key, value in make_global_settings_dict:
if key in ['CC', 'CXX']:
- return value
+ return os.path.join(options.toplevel_dir, value)
# Check to see if the compiler was specified as an environment variable.
for key in ['CC_target', 'CC', 'CXX']:
@@ -165,7 +190,8 @@ def GetCompilerPath(target_list, target_dicts, data):
return 'gcc'
-def GetAllDefines(target_list, target_dicts, data, config_name, params):
+def GetAllDefines(target_list, target_dicts, data, config_name, params,
+ compiler_path):
"""Calculate the defines for a project.
Returns:
@@ -202,9 +228,8 @@ def GetAllDefines(target_list, target_dicts, data, config_name, params):
# Get default compiler defines (if possible).
if flavor == 'win':
return all_defines # Default defines already processed in the loop above.
- cc_target = GetCompilerPath(target_list, target_dicts, data)
- if cc_target:
- command = shlex.split(cc_target)
+ if compiler_path:
+ command = shlex.split(compiler_path)
command.extend(['-E', '-dM', '-'])
cpp_proc = subprocess.Popen(args=command, cwd='.',
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
@@ -270,31 +295,123 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
shared_intermediate_dirs = [os.path.join(toplevel_build, 'obj', 'gen'),
os.path.join(toplevel_build, 'gen')]
- out_name = os.path.join(toplevel_build, 'eclipse-cdt-settings.xml')
+ GenerateCdtSettingsFile(target_list,
+ target_dicts,
+ data,
+ params,
+ config_name,
+ os.path.join(toplevel_build,
+ 'eclipse-cdt-settings.xml'),
+ options,
+ shared_intermediate_dirs)
+ GenerateClasspathFile(target_list,
+ target_dicts,
+ options.toplevel_dir,
+ toplevel_build,
+ os.path.join(toplevel_build,
+ 'eclipse-classpath.xml'))
+
+
+def GenerateCdtSettingsFile(target_list, target_dicts, data, params,
+ config_name, out_name, options,
+ shared_intermediate_dirs):
gyp.common.EnsureDirExists(out_name)
- out = open(out_name, 'w')
+ with open(out_name, 'w') as out:
+ out.write('\n')
+ out.write('\n')
+
+ eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File',
+ 'GNU C++', 'GNU C', 'Assembly']
+ compiler_path = GetCompilerPath(target_list, data, options)
+ include_dirs = GetAllIncludeDirectories(target_list, target_dicts,
+ shared_intermediate_dirs,
+ config_name, params, compiler_path)
+ WriteIncludePaths(out, eclipse_langs, include_dirs)
+ defines = GetAllDefines(target_list, target_dicts, data, config_name,
+ params, compiler_path)
+ WriteMacros(out, eclipse_langs, defines)
+
+ out.write('\n')
+
+
+def GenerateClasspathFile(target_list, target_dicts, toplevel_dir,
+ toplevel_build, out_name):
+ '''Generates a classpath file suitable for symbol navigation and code
+ completion of Java code (such as in Android projects) by finding all
+ .java and .jar files used as action inputs.'''
+ gyp.common.EnsureDirExists(out_name)
+ result = ET.Element('classpath')
+
+ def AddElements(kind, paths):
+ # First, we need to normalize the paths so they are all relative to the
+ # toplevel dir.
+ rel_paths = set()
+ for path in paths:
+ if os.path.isabs(path):
+ rel_paths.add(os.path.relpath(path, toplevel_dir))
+ else:
+ rel_paths.add(path)
- out.write('\n')
- out.write('\n')
+ for path in sorted(rel_paths):
+ entry_element = ET.SubElement(result, 'classpathentry')
+ entry_element.set('kind', kind)
+ entry_element.set('path', path)
- eclipse_langs = ['C++ Source File', 'C Source File', 'Assembly Source File',
- 'GNU C++', 'GNU C', 'Assembly']
- include_dirs = GetAllIncludeDirectories(target_list, target_dicts,
- shared_intermediate_dirs, config_name,
- params)
- WriteIncludePaths(out, eclipse_langs, include_dirs)
- defines = GetAllDefines(target_list, target_dicts, data, config_name, params)
- WriteMacros(out, eclipse_langs, defines)
+ AddElements('lib', GetJavaJars(target_list, target_dicts, toplevel_dir))
+ AddElements('src', GetJavaSourceDirs(target_list, target_dicts, toplevel_dir))
+ # Include the standard JRE container and a dummy out folder
+ AddElements('con', ['org.eclipse.jdt.launching.JRE_CONTAINER'])
+ # Include a dummy out folder so that Eclipse doesn't use the default /bin
+ # folder in the root of the project.
+ AddElements('output', [os.path.join(toplevel_build, '.eclipse-java-build')])
- out.write('\n')
- out.close()
+ ET.ElementTree(result).write(out_name)
+
+
+def GetJavaJars(target_list, target_dicts, toplevel_dir):
+ '''Generates a sequence of all .jars used as inputs.'''
+ for target_name in target_list:
+ target = target_dicts[target_name]
+ for action in target.get('actions', []):
+ for input_ in action['inputs']:
+ if os.path.splitext(input_)[1] == '.jar' and not input_.startswith('$'):
+ if os.path.isabs(input_):
+ yield input_
+ else:
+ yield os.path.join(os.path.dirname(target_name), input_)
+
+
+def GetJavaSourceDirs(target_list, target_dicts, toplevel_dir):
+ '''Generates a sequence of all likely java package root directories.'''
+ for target_name in target_list:
+ target = target_dicts[target_name]
+ for action in target.get('actions', []):
+ for input_ in action['inputs']:
+ if (os.path.splitext(input_)[1] == '.java' and
+ not input_.startswith('$')):
+ dir_ = os.path.dirname(os.path.join(os.path.dirname(target_name),
+ input_))
+ # If there is a parent 'src' or 'java' folder, navigate up to it -
+ # these are canonical package root names in Chromium. This will
+ # break if 'src' or 'java' exists in the package structure. This
+ # could be further improved by inspecting the java file for the
+ # package name if this proves to be too fragile in practice.
+ parent_search = dir_
+ while os.path.basename(parent_search) not in ['src', 'java']:
+ parent_search, _ = os.path.split(parent_search)
+ if not parent_search or parent_search == toplevel_dir:
+ # Didn't find a known root, just return the original path
+ yield dir_
+ break
+ else:
+ yield parent_search
def GenerateOutput(target_list, target_dicts, data, params):
"""Generate an XML settings file that can be imported into a CDT project."""
if params['options'].generator_output:
- raise NotImplementedError, "--generator_output not implemented for eclipse"
+ raise NotImplementedError("--generator_output not implemented for eclipse")
user_config = params.get('generator_flags', {}).get('config', None)
if user_config:
diff --git a/tools/gyp/pylib/gyp/generator/gypd.py b/tools/gyp/pylib/gyp/generator/gypd.py
index 22ef57f847d570..3efdb9966a69a1 100644
--- a/tools/gyp/pylib/gyp/generator/gypd.py
+++ b/tools/gyp/pylib/gyp/generator/gypd.py
@@ -39,9 +39,11 @@
# These variables should just be spit back out as variable references.
_generator_identity_variables = [
+ 'CONFIGURATION_NAME',
'EXECUTABLE_PREFIX',
'EXECUTABLE_SUFFIX',
'INTERMEDIATE_DIR',
+ 'LIB_DIR',
'PRODUCT_DIR',
'RULE_INPUT_ROOT',
'RULE_INPUT_DIRNAME',
@@ -49,6 +51,11 @@
'RULE_INPUT_NAME',
'RULE_INPUT_PATH',
'SHARED_INTERMEDIATE_DIR',
+ 'SHARED_LIB_DIR',
+ 'SHARED_LIB_PREFIX',
+ 'SHARED_LIB_SUFFIX',
+ 'STATIC_LIB_PREFIX',
+ 'STATIC_LIB_SUFFIX',
]
# gypd doesn't define a default value for OS like many other generator
diff --git a/tools/gyp/pylib/gyp/generator/make.py b/tools/gyp/pylib/gyp/generator/make.py
index b88a433d3d92de..64b2511f085cff 100644
--- a/tools/gyp/pylib/gyp/generator/make.py
+++ b/tools/gyp/pylib/gyp/generator/make.py
@@ -29,6 +29,7 @@
import gyp.common
import gyp.xcode_emulation
from gyp.common import GetEnvironFallback
+from gyp.common import GypError
generator_default_variables = {
'EXECUTABLE_PREFIX': '',
@@ -280,15 +281,7 @@ def CalculateGeneratorInputInfo(params):
AR.target ?= $(AR)
# C++ apps need to be linked with g++.
-#
-# Note: flock is used to seralize linking. Linking is a memory-intensive
-# process so running parallel links can often lead to thrashing. To disable
-# the serialization, override LINK via an envrionment variable as follows:
-#
-# export LINK=g++
-#
-# This will allow make to invoke N linker processes as specified in -jN.
-LINK ?= %(flock)s $(builddir)/linker.lock $(CXX.target)
+LINK ?= $(CXX.target)
# TODO(evan): move all cross-compilation logic to gyp-time so we don't need
# to replicate this environment fallback in make as well.
@@ -631,6 +624,38 @@ def QuoteSpaces(s, quote=r'\ '):
return s.replace(' ', quote)
+# TODO: Avoid code duplication with _ValidateSourcesForMSVSProject in msvs.py.
+def _ValidateSourcesForOSX(spec, all_sources):
+ """Makes sure if duplicate basenames are not specified in the source list.
+
+ Arguments:
+ spec: The target dictionary containing the properties of the target.
+ """
+ if spec.get('type', None) != 'static_library':
+ return
+
+ basenames = {}
+ for source in all_sources:
+ name, ext = os.path.splitext(source)
+ is_compiled_file = ext in [
+ '.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.s', '.S']
+ if not is_compiled_file:
+ continue
+ basename = os.path.basename(name) # Don't include extension.
+ basenames.setdefault(basename, []).append(source)
+
+ error = ''
+ for basename, files in basenames.iteritems():
+ if len(files) > 1:
+ error += ' %s: %s\n' % (basename, ' '.join(files))
+
+ if error:
+ print('static library %s has several files with the same basename:\n' %
+ spec['target_name'] + error + 'libtool on OS X will generate' +
+ ' warnings for them.')
+ raise GypError('Duplicate basenames in sources section, see list above')
+
+
# Map from qualified target to path to output.
target_outputs = {}
# Map from qualified target to any linkable output. A subset
@@ -640,7 +665,7 @@ def QuoteSpaces(s, quote=r'\ '):
target_link_deps = {}
-class MakefileWriter:
+class MakefileWriter(object):
"""MakefileWriter packages up the writing of one target-specific foobar.mk.
Its only real entry point is Write(), and is mostly used for namespacing.
@@ -758,6 +783,10 @@ def Write(self, qualified_target, base_path, output_filename, spec, configs,
# Sources.
all_sources = spec.get('sources', []) + extra_sources
if all_sources:
+ if self.flavor == 'mac':
+ # libtool on OS X generates warnings for duplicate basenames in the same
+ # target.
+ _ValidateSourcesForOSX(spec, all_sources)
self.WriteSources(
configs, deps, all_sources, extra_outputs,
extra_link_deps, part_of_all,
@@ -1101,9 +1130,12 @@ def WriteMacBundleResources(self, resources, bundle_deps):
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)
+ _, ext = os.path.splitext(output)
+ if ext != '.xcassets':
+ # Make does not supports '.xcassets' emulation.
+ self.WriteDoCmd([output], [res], 'mac_tool,,,copy-bundle-resource',
+ part_of_all=True)
+ bundle_deps.append(output)
def WriteMacInfoPlist(self, bundle_deps):
@@ -2034,7 +2066,6 @@ def CalculateMakefilePath(build_file, base_name):
build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
make_global_settings_array = data[build_file].get('make_global_settings', [])
wrappers = {}
- wrappers['LINK'] = '%s $(builddir)/linker.lock' % flock_command
for key, value in make_global_settings_array:
if key.endswith('_wrapper'):
wrappers[key[:-len('_wrapper')]] = '$(abspath %s)' % value
diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py
index d8e0872c347e97..edf2ae419ef59b 100644
--- a/tools/gyp/pylib/gyp/generator/msvs.py
+++ b/tools/gyp/pylib/gyp/generator/msvs.py
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import collections
import copy
import ntpath
import os
@@ -13,6 +12,7 @@
import gyp.common
import gyp.easy_xml as easy_xml
+import gyp.generator.ninja as ninja_generator
import gyp.MSVSNew as MSVSNew
import gyp.MSVSProject as MSVSProject
import gyp.MSVSSettings as MSVSSettings
@@ -21,6 +21,7 @@
import gyp.MSVSUtil as MSVSUtil
import gyp.MSVSVersion as MSVSVersion
from gyp.common import GypError
+from gyp.common import OrderedSet
# TODO: Remove once bots are on 2.7, http://crbug.com/241769
def _import_OrderedDict():
@@ -41,7 +42,7 @@ def _import_OrderedDict():
# if IncrediBuild is executed from inside Visual Studio. This regex
# validates that the string looks like a GUID with all uppercase hex
# letters.
-VALID_MSVS_GUID_CHARS = re.compile('^[A-F0-9\-]+$')
+VALID_MSVS_GUID_CHARS = re.compile(r'^[A-F0-9\-]+$')
generator_default_variables = {
@@ -81,6 +82,10 @@ def _import_OrderedDict():
'msvs_external_builder_out_dir',
'msvs_external_builder_build_cmd',
'msvs_external_builder_clean_cmd',
+ 'msvs_external_builder_clcompile_cmd',
+ 'msvs_enable_winrt',
+ 'msvs_requires_importlibrary',
+ 'msvs_enable_winphone',
]
@@ -97,46 +102,6 @@ def _import_OrderedDict():
cached_domain = None
-# Based on http://code.activestate.com/recipes/576694/.
-class OrderedSet(collections.MutableSet):
- def __init__(self, iterable=None):
- self.end = end = []
- end += [None, end, end] # sentinel node for doubly linked list
- self.map = {} # key --> [key, prev, next]
- if iterable is not None:
- self |= iterable
-
- def __len__(self):
- return len(self.map)
-
- def discard(self, key):
- if key in self.map:
- key, prev, next = self.map.pop(key)
- prev[2] = next
- next[1] = prev
-
- def __contains__(self, key):
- return key in self.map
-
- def add(self, key):
- if key not in self.map:
- end = self.end
- curr = end[1]
- curr[2] = end[1] = self.map[key] = [key, curr, end]
-
- def update(self, iterable):
- for i in iterable:
- if i not in self:
- self.add(i)
-
- def __iter__(self):
- end = self.end
- curr = end[2]
- while curr is not end:
- yield curr[0]
- curr = curr[2]
-
-
# TODO(gspencer): Switch the os.environ calls to be
# win32api.GetDomainName() and win32api.GetUserName() once the
# python version in depot_tools has been updated to work on Vista
@@ -153,11 +118,11 @@ def _GetDomainAndUserName():
call = subprocess.Popen(['net', 'config', 'Workstation'],
stdout=subprocess.PIPE)
config = call.communicate()[0]
- username_re = re.compile('^User name\s+(\S+)', re.MULTILINE)
+ username_re = re.compile(r'^User name\s+(\S+)', re.MULTILINE)
username_match = username_re.search(config)
if username_match:
username = username_match.group(1)
- domain_re = re.compile('^Logon domain\s+(\S+)', re.MULTILINE)
+ domain_re = re.compile(r'^Logon domain\s+(\S+)', re.MULTILINE)
domain_match = domain_re.search(config)
if domain_match:
domain = domain_match.group(1)
@@ -266,7 +231,8 @@ def _ConvertSourcesToFilterHierarchy(sources, prefix=None, excluded=None,
for f in folders:
contents = _ConvertSourcesToFilterHierarchy(folders[f], prefix=prefix + [f],
excluded=excluded,
- list_excluded=list_excluded)
+ list_excluded=list_excluded,
+ msvs_version=msvs_version)
contents = MSVSProject.Filter(f, contents=contents)
result.append(contents)
return result
@@ -322,7 +288,7 @@ def _BuildCommandLineForRuleRaw(spec, cmd, cygwin_shell, has_input_path,
if [x for x in cmd if '$(InputDir)' in x]:
input_dir_preamble = (
'set INPUTDIR=$(InputDir)\n'
- 'set INPUTDIR=%INPUTDIR:$(ProjectDir)=%\n'
+ 'if NOT DEFINED INPUTDIR set INPUTDIR=.\\\n'
'set INPUTDIR=%INPUTDIR:~0,-1%\n'
)
else:
@@ -851,23 +817,27 @@ def _GenerateRulesForMSVS(p, output_dir, options, spec,
if rules_external:
_GenerateExternalRules(rules_external, output_dir, spec,
sources, options, actions_to_add)
- _AdjustSourcesForRules(spec, rules, sources, excluded_sources)
+ _AdjustSourcesForRules(rules, sources, excluded_sources, False)
-def _AdjustSourcesForRules(spec, rules, sources, excluded_sources):
+def _AdjustSourcesForRules(rules, sources, excluded_sources, is_msbuild):
# Add outputs generated by each rule (if applicable).
for rule in rules:
- # Done if not processing outputs as sources.
- if int(rule.get('process_outputs_as_sources', False)):
- # Add in the outputs from this rule.
- trigger_files = _FindRuleTriggerFiles(rule, sources)
- for trigger_file in trigger_files:
+ # Add in the outputs from this rule.
+ trigger_files = _FindRuleTriggerFiles(rule, sources)
+ for trigger_file in trigger_files:
+ # Remove trigger_file from excluded_sources to let the rule be triggered
+ # (e.g. rule trigger ax_enums.idl is added to excluded_sources
+ # because it's also in an action's inputs in the same project)
+ excluded_sources.discard(_FixPath(trigger_file))
+ # Done if not processing outputs as sources.
+ if int(rule.get('process_outputs_as_sources', False)):
inputs, outputs = _RuleInputsAndOutputs(rule, trigger_file)
inputs = OrderedSet(_FixPaths(inputs))
outputs = OrderedSet(_FixPaths(outputs))
inputs.remove(_FixPath(trigger_file))
sources.update(inputs)
- if not spec.get('msvs_external_builder'):
+ if not is_msbuild:
excluded_sources.update(inputs)
sources.update(outputs)
@@ -954,6 +924,42 @@ def _GenerateProject(project, options, version, generator_flags):
return _GenerateMSVSProject(project, options, version, generator_flags)
+# TODO: Avoid code duplication with _ValidateSourcesForOSX in make.py.
+def _ValidateSourcesForMSVSProject(spec, version):
+ """Makes sure if duplicate basenames are not specified in the source list.
+
+ Arguments:
+ spec: The target dictionary containing the properties of the target.
+ version: The VisualStudioVersion object.
+ """
+ # This validation should not be applied to MSVC2010 and later.
+ assert not version.UsesVcxproj()
+
+ # TODO: Check if MSVC allows this for loadable_module targets.
+ if spec.get('type', None) not in ('static_library', 'shared_library'):
+ return
+ sources = spec.get('sources', [])
+ basenames = {}
+ for source in sources:
+ name, ext = os.path.splitext(source)
+ is_compiled_file = ext in [
+ '.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.s', '.S']
+ if not is_compiled_file:
+ continue
+ basename = os.path.basename(name) # Don't include extension.
+ basenames.setdefault(basename, []).append(source)
+
+ error = ''
+ for basename, files in basenames.iteritems():
+ if len(files) > 1:
+ error += ' %s: %s\n' % (basename, ' '.join(files))
+
+ if error:
+ print('static library %s has several files with the same basename:\n' %
+ spec['target_name'] + error + 'MSVC08 cannot handle that.')
+ raise GypError('Duplicate basenames in sources section, see list above')
+
+
def _GenerateMSVSProject(project, options, version, generator_flags):
"""Generates a .vcproj file. It may create .rules and .user files too.
@@ -979,6 +985,11 @@ def _GenerateMSVSProject(project, options, version, generator_flags):
for config_name, config in spec['configurations'].iteritems():
_AddConfigurationToMSVSProject(p, spec, config_type, config_name, config)
+ # MSVC08 and prior version cannot handle duplicate basenames in the same
+ # target.
+ # TODO: Take excluded sources into consideration if possible.
+ _ValidateSourcesForMSVSProject(spec, version)
+
# Prepare list of sources and excluded sources.
gyp_file = os.path.split(project.build_file)[1]
sources, excluded_sources = _PrepareListOfSources(spec, generator_flags,
@@ -1098,7 +1109,8 @@ def _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config):
for this configuration.
"""
# Get the information for this configuration
- include_dirs, resource_include_dirs = _GetIncludeDirs(config)
+ include_dirs, midl_include_dirs, resource_include_dirs = \
+ _GetIncludeDirs(config)
libraries = _GetLibraries(spec)
library_dirs = _GetLibraryDirs(config)
out_file, vc_tool, _ = _GetOutputFilePathAndTool(spec, msbuild=False)
@@ -1126,6 +1138,8 @@ def _AddConfigurationToMSVSProject(p, spec, config_type, config_name, config):
# Add the information to the appropriate tool
_ToolAppend(tools, 'VCCLCompilerTool',
'AdditionalIncludeDirectories', include_dirs)
+ _ToolAppend(tools, 'VCMIDLTool',
+ 'AdditionalIncludeDirectories', midl_include_dirs)
_ToolAppend(tools, 'VCResourceCompilerTool',
'AdditionalIncludeDirectories', resource_include_dirs)
# Add in libraries.
@@ -1181,10 +1195,14 @@ def _GetIncludeDirs(config):
include_dirs = (
config.get('include_dirs', []) +
config.get('msvs_system_include_dirs', []))
+ midl_include_dirs = (
+ config.get('midl_include_dirs', []) +
+ config.get('msvs_system_include_dirs', []))
resource_include_dirs = config.get('resource_include_dirs', include_dirs)
include_dirs = _FixPaths(include_dirs)
+ midl_include_dirs = _FixPaths(midl_include_dirs)
resource_include_dirs = _FixPaths(resource_include_dirs)
- return include_dirs, resource_include_dirs
+ return include_dirs, midl_include_dirs, resource_include_dirs
def _GetLibraryDirs(config):
@@ -1218,7 +1236,7 @@ def _GetLibraries(spec):
found = OrderedSet()
unique_libraries_list = []
for entry in reversed(libraries):
- library = re.sub('^\-l', '', entry)
+ library = re.sub(r'^\-l', '', entry)
if not os.path.splitext(library)[1]:
library += '.lib'
if library not in found:
@@ -1478,8 +1496,14 @@ def _AdjustSourcesAndConvertToFilterHierarchy(
# Prune filters with a single child to flatten ugly directory structures
# such as ../../src/modules/module1 etc.
- while len(sources) == 1 and isinstance(sources[0], MSVSProject.Filter):
- sources = sources[0].contents
+ if version.UsesVcxproj():
+ while all([isinstance(s, MSVSProject.Filter) for s in sources]) \
+ and len(set([s.name for s in sources])) == 1:
+ assert all([len(s.contents) == 1 for s in sources])
+ sources = [s.contents[0] for s in sources]
+ else:
+ while len(sources) == 1 and isinstance(sources[0], MSVSProject.Filter):
+ sources = sources[0].contents
return sources, excluded_sources, excluded_idl
@@ -1815,7 +1839,7 @@ def _CreateProjectObjects(target_list, target_dicts, options, msvs_version):
return projects
-def _InitNinjaFlavor(options, target_list, target_dicts):
+def _InitNinjaFlavor(params, target_list, target_dicts):
"""Initialize targets for the ninja flavor.
This sets up the necessary variables in the targets to generate msvs projects
@@ -1823,7 +1847,7 @@ def _InitNinjaFlavor(options, target_list, target_dicts):
if they have not been set. This allows individual specs to override the
default values initialized here.
Arguments:
- options: Options provided to the generator.
+ params: Params provided to the generator.
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
"""
@@ -1837,8 +1861,15 @@ def _InitNinjaFlavor(options, target_list, target_dicts):
spec['msvs_external_builder'] = 'ninja'
if not spec.get('msvs_external_builder_out_dir'):
- spec['msvs_external_builder_out_dir'] = \
- options.depth + '/out/$(Configuration)'
+ gyp_file, _, _ = gyp.common.ParseQualifiedTarget(qualified_target)
+ gyp_dir = os.path.dirname(gyp_file)
+ configuration = '$(Configuration)'
+ if params.get('target_arch') == 'x64':
+ configuration += '_x64'
+ spec['msvs_external_builder_out_dir'] = os.path.join(
+ gyp.common.RelativePath(params['options'].toplevel_dir, gyp_dir),
+ ninja_generator.ComputeOutputDir(params),
+ configuration)
if not spec.get('msvs_external_builder_build_cmd'):
spec['msvs_external_builder_build_cmd'] = [
path_to_ninja,
@@ -1851,8 +1882,7 @@ def _InitNinjaFlavor(options, target_list, target_dicts):
path_to_ninja,
'-C',
'$(OutDir)',
- '-t',
- 'clean',
+ '-tclean',
'$(ProjectName)',
]
@@ -1933,7 +1963,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
# Optionally configure each spec to use ninja as the external builder.
if params.get('flavor') == 'ninja':
- _InitNinjaFlavor(options, target_list, target_dicts)
+ _InitNinjaFlavor(params, target_list, target_dicts)
# Prepare the set of configurations.
configs = set()
@@ -1986,7 +2016,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
def _GenerateMSBuildFiltersFile(filters_path, source_files,
- extension_to_rule_name):
+ rule_dependencies, extension_to_rule_name):
"""Generate the filters file.
This file is used by Visual Studio to organize the presentation of source
@@ -1999,8 +2029,8 @@ def _GenerateMSBuildFiltersFile(filters_path, source_files,
"""
filter_group = []
source_group = []
- _AppendFiltersForMSBuild('', source_files, extension_to_rule_name,
- filter_group, source_group)
+ _AppendFiltersForMSBuild('', source_files, rule_dependencies,
+ extension_to_rule_name, filter_group, source_group)
if filter_group:
content = ['Project',
{'ToolsVersion': '4.0',
@@ -2015,7 +2045,7 @@ def _GenerateMSBuildFiltersFile(filters_path, source_files,
os.unlink(filters_path)
-def _AppendFiltersForMSBuild(parent_filter_name, sources,
+def _AppendFiltersForMSBuild(parent_filter_name, sources, rule_dependencies,
extension_to_rule_name,
filter_group, source_group):
"""Creates the list of filters and sources to be added in the filter file.
@@ -2041,11 +2071,12 @@ def _AppendFiltersForMSBuild(parent_filter_name, sources,
['UniqueIdentifier', MSVSNew.MakeGuid(source.name)]])
# Recurse and add its dependents.
_AppendFiltersForMSBuild(filter_name, source.contents,
- extension_to_rule_name,
+ rule_dependencies, extension_to_rule_name,
filter_group, source_group)
else:
# It's a source. Create a source entry.
- _, element = _MapFileToMsBuildSourceType(source, extension_to_rule_name)
+ _, element = _MapFileToMsBuildSourceType(source, rule_dependencies,
+ extension_to_rule_name)
source_entry = [element, {'Include': source}]
# Specify the filter it is part of, if any.
if parent_filter_name:
@@ -2053,7 +2084,8 @@ def _AppendFiltersForMSBuild(parent_filter_name, sources,
source_group.append(source_entry)
-def _MapFileToMsBuildSourceType(source, extension_to_rule_name):
+def _MapFileToMsBuildSourceType(source, rule_dependencies,
+ extension_to_rule_name):
"""Returns the group and element type of the source file.
Arguments:
@@ -2076,9 +2108,15 @@ def _MapFileToMsBuildSourceType(source, extension_to_rule_name):
elif ext == '.rc':
group = 'resource'
element = 'ResourceCompile'
+ elif ext == '.asm':
+ group = 'masm'
+ element = 'MASM'
elif ext == '.idl':
group = 'midl'
element = 'Midl'
+ elif source in rule_dependencies:
+ group = 'rule_dependency'
+ element = 'CustomBuild'
else:
group = 'none'
element = 'None'
@@ -2088,7 +2126,8 @@ def _MapFileToMsBuildSourceType(source, extension_to_rule_name):
def _GenerateRulesForMSBuild(output_dir, options, spec,
sources, excluded_sources,
props_files_of_rules, targets_files_of_rules,
- actions_to_add, extension_to_rule_name):
+ actions_to_add, rule_dependencies,
+ extension_to_rule_name):
# MSBuild rules are implemented using three files: an XML file, a .targets
# file and a .props file.
# See http://blogs.msdn.com/b/vcblog/archive/2010/04/21/quick-help-on-vs2010-custom-build-rule.aspx
@@ -2104,6 +2143,7 @@ def _GenerateRulesForMSBuild(output_dir, options, spec,
continue
msbuild_rule = MSBuildRule(rule, spec)
msbuild_rules.append(msbuild_rule)
+ rule_dependencies.update(msbuild_rule.additional_dependencies.split(';'))
extension_to_rule_name[msbuild_rule.extension] = msbuild_rule.rule_name
if msbuild_rules:
base = spec['target_name'] + options.suffix
@@ -2125,7 +2165,7 @@ def _GenerateRulesForMSBuild(output_dir, options, spec,
if rules_external:
_GenerateExternalRules(rules_external, output_dir, spec,
sources, options, actions_to_add)
- _AdjustSourcesForRules(spec, rules, sources, excluded_sources)
+ _AdjustSourcesForRules(rules, sources, excluded_sources, True)
class MSBuildRule(object):
@@ -2578,14 +2618,30 @@ def _GetMSBuildProjectConfigurations(configurations):
def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
namespace = os.path.splitext(gyp_file_name)[0]
- return [
+ properties = [
['PropertyGroup', {'Label': 'Globals'},
- ['ProjectGuid', guid],
- ['Keyword', 'Win32Proj'],
- ['RootNamespace', namespace],
+ ['ProjectGuid', guid],
+ ['Keyword', 'Win32Proj'],
+ ['RootNamespace', namespace],
+ ['IgnoreWarnCompileDuplicatedFilename', 'true'],
]
- ]
+ ]
+ if os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or \
+ os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64':
+ properties[0].append(['PreferredToolArchitecture', 'x64'])
+
+ if spec.get('msvs_enable_winrt'):
+ properties[0].append(['DefaultLanguage', 'en-US'])
+ properties[0].append(['AppContainerApplication', 'true'])
+ properties[0].append(['ApplicationTypeRevision', '8.1'])
+
+ if spec.get('msvs_enable_winphone'):
+ properties[0].append(['ApplicationType', 'Windows Phone'])
+ else:
+ properties[0].append(['ApplicationType', 'Windows Store'])
+
+ return properties
def _GetMSBuildConfigurationDetails(spec, build_file):
properties = {}
@@ -2596,8 +2652,9 @@ def _GetMSBuildConfigurationDetails(spec, build_file):
_AddConditionalProperty(properties, condition, 'ConfigurationType',
msbuild_attributes['ConfigurationType'])
if character_set:
- _AddConditionalProperty(properties, condition, 'CharacterSet',
- character_set)
+ if 'msvs_enable_winrt' not in spec :
+ _AddConditionalProperty(properties, condition, 'CharacterSet',
+ character_set)
return _GetMSBuildPropertyGroup(spec, 'Configuration', properties)
@@ -2812,7 +2869,7 @@ def _AddConditionalProperty(properties, condition, name, value):
# Regex for msvs variable references ( i.e. $(FOO) ).
-MSVS_VARIABLE_REFERENCE = re.compile('\$\(([a-zA-Z_][a-zA-Z0-9_]*)\)')
+MSVS_VARIABLE_REFERENCE = re.compile(r'\$\(([a-zA-Z_][a-zA-Z0-9_]*)\)')
def _GetMSBuildPropertyGroup(spec, label, properties):
@@ -2896,7 +2953,8 @@ def _FinalizeMSBuildSettings(spec, configuration):
converted = True
msvs_settings = configuration.get('msvs_settings', {})
msbuild_settings = MSVSSettings.ConvertToMSBuildSettings(msvs_settings)
- include_dirs, resource_include_dirs = _GetIncludeDirs(configuration)
+ include_dirs, midl_include_dirs, resource_include_dirs = \
+ _GetIncludeDirs(configuration)
libraries = _GetLibraries(spec)
library_dirs = _GetLibraryDirs(configuration)
out_file, _, msbuild_tool = _GetOutputFilePathAndTool(spec, msbuild=True)
@@ -2906,7 +2964,7 @@ def _FinalizeMSBuildSettings(spec, configuration):
# Visual Studio 2010 has TR1
defines = [d for d in defines if d != '_HAS_TR1=0']
# Warn of ignored settings
- ignored_settings = ['msvs_prebuild', 'msvs_postbuild', 'msvs_tool_files']
+ ignored_settings = ['msvs_tool_files']
for ignored_setting in ignored_settings:
value = configuration.get(ignored_setting)
if value:
@@ -2915,9 +2973,8 @@ def _FinalizeMSBuildSettings(spec, configuration):
defines = [_EscapeCppDefineForMSBuild(d) for d in defines]
disabled_warnings = _GetDisabledWarnings(configuration)
- # TODO(jeanluc) Validate & warn that we don't translate
- # prebuild = configuration.get('msvs_prebuild')
- # postbuild = configuration.get('msvs_postbuild')
+ prebuild = configuration.get('msvs_prebuild')
+ postbuild = configuration.get('msvs_postbuild')
def_file = _GetModuleDefinition(spec)
precompiled_header = configuration.get('msvs_precompiled_header')
@@ -2927,6 +2984,8 @@ def _FinalizeMSBuildSettings(spec, configuration):
# if you don't have any resources.
_ToolAppend(msbuild_settings, 'ClCompile',
'AdditionalIncludeDirectories', include_dirs)
+ _ToolAppend(msbuild_settings, 'Midl',
+ 'AdditionalIncludeDirectories', midl_include_dirs)
_ToolAppend(msbuild_settings, 'ResourceCompile',
'AdditionalIncludeDirectories', resource_include_dirs)
# Add in libraries, note that even for empty libraries, we want this
@@ -2957,6 +3016,13 @@ def _FinalizeMSBuildSettings(spec, configuration):
'PrecompiledHeaderFile', precompiled_header)
_ToolAppend(msbuild_settings, 'ClCompile',
'ForcedIncludeFiles', [precompiled_header])
+ else:
+ _ToolAppend(msbuild_settings, 'ClCompile', 'PrecompiledHeader', 'NotUsing')
+ # Turn off WinRT compilation
+ _ToolAppend(msbuild_settings, 'ClCompile', 'CompileAsWinRT', 'false')
+ # Turn on import libraries if appropriate
+ if spec.get('msvs_requires_importlibrary'):
+ _ToolAppend(msbuild_settings, '', 'IgnoreImportLibrary', 'false')
# Loadable modules don't generate import libraries;
# tell dependent projects to not expect one.
if spec['type'] == 'loadable_module':
@@ -2965,6 +3031,10 @@ def _FinalizeMSBuildSettings(spec, configuration):
if def_file:
_ToolAppend(msbuild_settings, 'Link', 'ModuleDefinitionFile', def_file)
configuration['finalized_msbuild_settings'] = msbuild_settings
+ if prebuild:
+ _ToolAppend(msbuild_settings, 'PreBuildEvent', 'Command', prebuild)
+ if postbuild:
+ _ToolAppend(msbuild_settings, 'PostBuildEvent', 'Command', postbuild)
def _GetValueFormattedForMSBuild(tool_name, name, value):
@@ -3020,15 +3090,18 @@ def _VerifySourcesExist(sources, root_dir):
return missing_sources
-def _GetMSBuildSources(spec, sources, exclusions, extension_to_rule_name,
- actions_spec, sources_handled_by_action, list_excluded):
- groups = ['none', 'midl', 'include', 'compile', 'resource', 'rule']
+def _GetMSBuildSources(spec, sources, exclusions, rule_dependencies,
+ extension_to_rule_name, actions_spec,
+ sources_handled_by_action, list_excluded):
+ groups = ['none', 'masm', 'midl', 'include', 'compile', 'resource', 'rule',
+ 'rule_dependency']
grouped_sources = {}
for g in groups:
grouped_sources[g] = []
_AddSources2(spec, sources, exclusions, grouped_sources,
- extension_to_rule_name, sources_handled_by_action, list_excluded)
+ rule_dependencies, extension_to_rule_name,
+ sources_handled_by_action, list_excluded)
sources = []
for g in groups:
if grouped_sources[g]:
@@ -3039,13 +3112,15 @@ def _GetMSBuildSources(spec, sources, exclusions, extension_to_rule_name,
def _AddSources2(spec, sources, exclusions, grouped_sources,
- extension_to_rule_name, sources_handled_by_action,
+ rule_dependencies, 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,
+ rule_dependencies, extension_to_rule_name,
+ sources_handled_by_action,
list_excluded)
else:
if not source in sources_handled_by_action:
@@ -3088,7 +3163,7 @@ def _AddSources2(spec, sources, exclusions, grouped_sources,
detail.append(['PrecompiledHeader', ''])
detail.append(['ForcedIncludeFiles', ''])
- group, element = _MapFileToMsBuildSourceType(source,
+ group, element = _MapFileToMsBuildSourceType(source, rule_dependencies,
extension_to_rule_name)
grouped_sources[group].append([element, {'Include': source}] + detail)
@@ -3132,6 +3207,7 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
actions_to_add = {}
props_files_of_rules = set()
targets_files_of_rules = set()
+ rule_dependencies = set()
extension_to_rule_name = {}
list_excluded = generator_flags.get('msvs_list_excluded_files', True)
@@ -3140,10 +3216,11 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
_GenerateRulesForMSBuild(project_dir, options, spec,
sources, excluded_sources,
props_files_of_rules, targets_files_of_rules,
- actions_to_add, extension_to_rule_name)
+ actions_to_add, rule_dependencies,
+ extension_to_rule_name)
else:
rules = spec.get('rules', [])
- _AdjustSourcesForRules(spec, rules, sources, excluded_sources)
+ _AdjustSourcesForRules(rules, sources, excluded_sources, True)
sources, excluded_sources, excluded_idl = (
_AdjustSourcesAndConvertToFilterHierarchy(spec, options,
@@ -3166,6 +3243,7 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
spec, actions_to_add)
_GenerateMSBuildFiltersFile(project.path + '.filters', sources,
+ rule_dependencies,
extension_to_rule_name)
missing_sources = _VerifySourcesExist(sources, project_dir)
@@ -3180,6 +3258,12 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.props'}]]
import_cpp_targets_section = [
['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.targets'}]]
+ import_masm_props_section = [
+ ['Import',
+ {'Project': r'$(VCTargetsPath)\BuildCustomizations\masm.props'}]]
+ import_masm_targets_section = [
+ ['Import',
+ {'Project': r'$(VCTargetsPath)\BuildCustomizations\masm.targets'}]]
macro_section = [['PropertyGroup', {'Label': 'UserMacros'}]]
content = [
@@ -3193,8 +3277,12 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
content += _GetMSBuildGlobalProperties(spec, project.guid, project_file_name)
content += import_default_section
content += _GetMSBuildConfigurationDetails(spec, project.build_file)
- content += _GetMSBuildLocalProperties(project.msbuild_toolset)
+ if spec.get('msvs_enable_winphone'):
+ content += _GetMSBuildLocalProperties('v120_wp81')
+ else:
+ content += _GetMSBuildLocalProperties(project.msbuild_toolset)
content += import_cpp_props_section
+ content += import_masm_props_section
content += _GetMSBuildExtensions(props_files_of_rules)
content += _GetMSBuildPropertySheets(configurations)
content += macro_section
@@ -3202,10 +3290,11 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
project.build_file)
content += _GetMSBuildToolSettingsSections(spec, configurations)
content += _GetMSBuildSources(
- spec, sources, exclusions, extension_to_rule_name, actions_spec,
- sources_handled_by_action, list_excluded)
+ spec, sources, exclusions, rule_dependencies, extension_to_rule_name,
+ actions_spec, sources_handled_by_action, list_excluded)
content += _GetMSBuildProjectReferences(project)
content += import_cpp_targets_section
+ content += import_masm_targets_section
content += _GetMSBuildExtensionTargets(targets_files_of_rules)
if spec.get('msvs_external_builder'):
@@ -3222,7 +3311,9 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
def _GetMSBuildExternalBuilderTargets(spec):
"""Return a list of MSBuild targets for external builders.
- Right now, only "Build" and "Clean" targets are generated.
+ The "Build" and "Clean" targets are always generated. If the spec contains
+ 'msvs_external_builder_clcompile_cmd', then the "ClCompile" target will also
+ be generated, to support building selected C/C++ files.
Arguments:
spec: The gyp target spec.
@@ -3241,7 +3332,17 @@ def _GetMSBuildExternalBuilderTargets(spec):
clean_target = ['Target', {'Name': 'Clean'}]
clean_target.append(['Exec', {'Command': clean_cmd}])
- return [build_target, clean_target]
+ targets = [build_target, clean_target]
+
+ if spec.get('msvs_external_builder_clcompile_cmd'):
+ clcompile_cmd = _BuildCommandLineForRuleRaw(
+ spec, spec['msvs_external_builder_clcompile_cmd'],
+ False, False, False, False)
+ clcompile_target = ['Target', {'Name': 'ClCompile'}]
+ clcompile_target.append(['Exec', {'Command': clcompile_cmd}])
+ targets.append(clcompile_target)
+
+ return targets
def _GetMSBuildExtensions(props_files_of_rules):
@@ -3295,8 +3396,8 @@ def _GenerateActionsForMSBuild(spec, actions_to_add):
# get too long. See also _AddActions: cygwin's setup_env mustn't be called
# for every invocation or the command that sets the PATH will grow too
# long.
- command = (
- '\r\nif %errorlevel% neq 0 exit /b %errorlevel%\r\n'.join(commands))
+ command = '\r\n'.join([c + '\r\nif %errorlevel% neq 0 exit /b %errorlevel%'
+ for c in commands])
_AddMSBuildAction(spec,
primary_input,
inputs,
diff --git a/tools/gyp/pylib/gyp/generator/ninja.py b/tools/gyp/pylib/gyp/generator/ninja.py
index 74618147452569..6823f4ffbdaf25 100644
--- a/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/tools/gyp/pylib/gyp/generator/ninja.py
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import collections
import copy
import hashlib
import json
@@ -13,6 +14,7 @@
import sys
import gyp
import gyp.common
+from gyp.common import OrderedSet
import gyp.msvs_emulation
import gyp.MSVSUtil as MSVSUtil
import gyp.xcode_emulation
@@ -60,17 +62,7 @@
generator_extra_sources_for_rules = []
generator_filelist_paths = None
-# TODO: figure out how to not build extra host objects in the non-cross-compile
-# case when this is enabled, and enable unconditionally.
-generator_supports_multiple_toolsets = (
- os.environ.get('GYP_CROSSCOMPILE') or
- os.environ.get('AR_host') or
- os.environ.get('CC_host') or
- os.environ.get('CXX_host') or
- os.environ.get('AR_target') or
- os.environ.get('CC_target') or
- os.environ.get('CXX_target'))
-
+generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
def StripPrefix(arg, prefix):
if arg.startswith(prefix):
@@ -106,7 +98,7 @@ def AddArch(output, arch):
return '%s.%s%s' % (output, arch, extension)
-class Target:
+class Target(object):
"""Target represents the paths used within a single gyp target.
Conceptually, building a single target A is a series of steps:
@@ -210,8 +202,8 @@ def FinalOutput(self):
# an output file; the result can be namespaced such that it is unique
# to the input file name as well as the output target name.
-class NinjaWriter:
- def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
+class NinjaWriter(object):
+ def __init__(self, hash_for_rules, target_outputs, base_dir, build_dir,
output_file, toplevel_build, output_file_name, flavor,
toplevel_dir=None):
"""
@@ -221,7 +213,7 @@ def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
toplevel_dir: path to the toplevel directory
"""
- self.qualified_target = qualified_target
+ self.hash_for_rules = hash_for_rules
self.target_outputs = target_outputs
self.base_dir = base_dir
self.build_dir = build_dir
@@ -338,12 +330,15 @@ def GypPathToUniqueOutput(self, path, qualified=True):
obj += '.' + self.toolset
path_dir, path_basename = os.path.split(path)
+ assert not os.path.isabs(path_dir), (
+ "'%s' can not be absolute path (see crbug.com/462153)." % path_dir)
+
if qualified:
path_basename = self.name + '.' + path_basename
return os.path.normpath(os.path.join(obj, self.base_dir, path_dir,
path_basename))
- def WriteCollapsedDependencies(self, name, targets):
+ def WriteCollapsedDependencies(self, name, targets, order_only=None):
"""Given a list of targets, return a path for a single file
representing the result of building all the targets or None.
@@ -351,10 +346,11 @@ def WriteCollapsedDependencies(self, name, targets):
assert targets == filter(None, targets), targets
if len(targets) == 0:
+ assert not order_only
return None
- if len(targets) > 1:
+ if len(targets) > 1 or order_only:
stamp = self.GypPathToUniqueOutput(name + '.stamp')
- targets = self.ninja.build(stamp, 'stamp', targets)
+ targets = self.ninja.build(stamp, 'stamp', targets, order_only=order_only)
self.ninja.newline()
return targets[0]
@@ -391,6 +387,9 @@ def WriteSpec(self, spec, config_name, generator_flags):
self.ninja.variable('arch', self.win_env[arch])
self.ninja.variable('cc', '$cl_' + arch)
self.ninja.variable('cxx', '$cl_' + arch)
+ self.ninja.variable('cc_host', '$cl_' + arch)
+ self.ninja.variable('cxx_host', '$cl_' + arch)
+ self.ninja.variable('asm', '$ml_' + arch)
if self.flavor == 'mac':
self.archs = self.xcode_settings.GetActiveArchs(config_name)
@@ -472,6 +471,8 @@ def WriteSpec(self, spec, config_name, generator_flags):
else:
print "Warning: Actions/rules writing object files don't work with " \
"multiarch targets, dropping. (target %s)" % spec['target_name']
+ elif self.flavor == 'mac' and len(self.archs) > 1:
+ link_deps = collections.defaultdict(list)
if self.flavor == 'win' and self.target.type == 'static_library':
@@ -523,7 +524,7 @@ def fix_path(path, rel=None):
def WriteWinIdlFiles(self, spec, prebuild):
"""Writes rules to match MSVS's implicit idl handling."""
assert self.flavor == 'win'
- if self.msvs_settings.HasExplicitIdlRules(spec):
+ if self.msvs_settings.HasExplicitIdlRulesOrActions(spec):
return []
outputs = []
for source in filter(lambda x: x.endswith('.idl'), spec['sources']):
@@ -557,9 +558,10 @@ def WriteActionsRulesCopies(self, spec, extra_sources, prebuild,
stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
if self.is_mac_bundle:
- self.WriteMacBundleResources(
+ xcassets = self.WriteMacBundleResources(
extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends)
- self.WriteMacInfoPlist(mac_bundle_depends)
+ partial_info_plist = self.WriteMacXCassets(xcassets, mac_bundle_depends)
+ self.WriteMacInfoPlist(partial_info_plist, mac_bundle_depends)
return stamp
@@ -580,23 +582,24 @@ def GenerateDescription(self, verb, message, fallback):
def WriteActions(self, actions, extra_sources, prebuild,
extra_mac_bundle_resources):
# Actions cd into the base directory.
- env = self.GetSortedXcodeEnv()
- if self.flavor == 'win':
- env = self.msvs_settings.GetVSMacroEnv(
- '$!PRODUCT_DIR', config=self.config_name)
+ env = self.GetToolchainEnv()
all_outputs = []
for action in actions:
# First write out a rule for the action.
- name = '%s_%s' % (action['action_name'],
- hashlib.md5(self.qualified_target).hexdigest())
+ name = '%s_%s' % (action['action_name'], self.hash_for_rules)
description = self.GenerateDescription('ACTION',
action.get('message', None),
name)
is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action)
if self.flavor == 'win' else False)
args = action['action']
+ depfile = action.get('depfile', None)
+ if depfile:
+ depfile = self.ExpandSpecial(depfile, self.base_to_build)
+ pool = 'console' if int(action.get('ninja_use_console', 0)) else None
rule_name, _ = self.WriteNewNinjaRule(name, args, description,
- is_cygwin, env=env)
+ is_cygwin, env, pool,
+ depfile=depfile)
inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
if int(action.get('process_outputs_as_sources', False)):
@@ -616,15 +619,16 @@ def WriteActions(self, actions, extra_sources, prebuild,
def WriteRules(self, rules, extra_sources, prebuild,
mac_bundle_resources, extra_mac_bundle_resources):
- env = self.GetSortedXcodeEnv()
+ env = self.GetToolchainEnv()
all_outputs = []
for rule in rules:
- # First write out a rule for the rule action.
- name = '%s_%s' % (rule['rule_name'],
- hashlib.md5(self.qualified_target).hexdigest())
# Skip a rule with no action and no inputs.
if 'action' not in rule and not rule.get('rule_sources', []):
continue
+
+ # First write out a rule for the rule action.
+ name = '%s_%s' % (rule['rule_name'], self.hash_for_rules)
+
args = rule['action']
description = self.GenerateDescription(
'RULE',
@@ -632,8 +636,9 @@ def WriteRules(self, rules, extra_sources, prebuild,
('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule)
if self.flavor == 'win' else False)
+ pool = 'console' if int(rule.get('ninja_use_console', 0)) else None
rule_name, args = self.WriteNewNinjaRule(
- name, args, description, is_cygwin, env=env)
+ name, args, description, is_cygwin, env, pool)
# TODO: if the command references the outputs directly, we should
# simplify it to just use $out.
@@ -645,16 +650,31 @@ def WriteRules(self, rules, extra_sources, prebuild,
needed_variables = set(['source'])
for argument in args:
for var in special_locals:
- if ('${%s}' % var) in argument:
+ if '${%s}' % var in argument:
needed_variables.add(var)
def cygwin_munge(path):
+ # pylint: disable=cell-var-from-loop
if is_cygwin:
return path.replace('\\', '/')
return path
+ inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])]
+
+ # If there are n source files matching the rule, and m additional rule
+ # inputs, then adding 'inputs' to each build edge written below will
+ # write m * n inputs. Collapsing reduces this to m + n.
+ sources = rule.get('rule_sources', [])
+ num_inputs = len(inputs)
+ if prebuild:
+ num_inputs += 1
+ if num_inputs > 2 and len(sources) > 2:
+ inputs = [self.WriteCollapsedDependencies(
+ rule['rule_name'], inputs, order_only=prebuild)]
+ prebuild = []
+
# For each source file, write an edge that generates all the outputs.
- for source in rule.get('rule_sources', []):
+ for source in sources:
source = os.path.normpath(source)
dirname, basename = os.path.split(source)
root, ext = os.path.splitext(basename)
@@ -663,9 +683,6 @@ def cygwin_munge(path):
outputs = [self.ExpandRuleVariables(o, root, dirname,
source, ext, basename)
for o in rule['outputs']]
- inputs = [self.ExpandRuleVariables(i, root, dirname,
- source, ext, basename)
- for i in rule.get('inputs', [])]
if int(rule.get('process_outputs_as_sources', False)):
extra_sources += outputs
@@ -703,10 +720,11 @@ def cygwin_munge(path):
else:
assert var == None, repr(var)
- inputs = [self.GypPathToNinja(i, env) for i in inputs]
outputs = [self.GypPathToNinja(o, env) for o in outputs]
- extra_bindings.append(('unique_name',
- hashlib.md5(outputs[0]).hexdigest()))
+ if self.flavor == 'win':
+ # WriteNewNinjaRule uses unique_name for creating an rsp file on win.
+ extra_bindings.append(('unique_name',
+ hashlib.md5(outputs[0]).hexdigest()))
self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
implicit=inputs,
order_only=prebuild,
@@ -718,7 +736,7 @@ def cygwin_munge(path):
def WriteCopies(self, copies, prebuild, mac_bundle_depends):
outputs = []
- env = self.GetSortedXcodeEnv()
+ env = self.GetToolchainEnv()
for copy in copies:
for path in copy['files']:
# Normalize the path so trailing slashes don't confuse us.
@@ -742,15 +760,68 @@ def WriteCopies(self, copies, prebuild, mac_bundle_depends):
def WriteMacBundleResources(self, resources, bundle_depends):
"""Writes ninja edges for 'mac_bundle_resources'."""
+ xcassets = []
for output, res in gyp.xcode_emulation.GetMacBundleResources(
generator_default_variables['PRODUCT_DIR'],
self.xcode_settings, map(self.GypPathToNinja, resources)):
output = self.ExpandSpecial(output)
- self.ninja.build(output, 'mac_tool', res,
- variables=[('mactool_cmd', 'copy-bundle-resource')])
- bundle_depends.append(output)
+ if os.path.splitext(output)[-1] != '.xcassets':
+ isBinary = self.xcode_settings.IsBinaryOutputFormat(self.config_name)
+ self.ninja.build(output, 'mac_tool', res,
+ variables=[('mactool_cmd', 'copy-bundle-resource'), \
+ ('binary', isBinary)])
+ bundle_depends.append(output)
+ else:
+ xcassets.append(res)
+ return xcassets
+
+ def WriteMacXCassets(self, xcassets, bundle_depends):
+ """Writes ninja edges for 'mac_bundle_resources' .xcassets files.
+
+ This add an invocation of 'actool' via the 'mac_tool.py' helper script.
+ It assumes that the assets catalogs define at least one imageset and
+ thus an Assets.car file will be generated in the application resources
+ directory. If this is not the case, then the build will probably be done
+ at each invocation of ninja."""
+ if not xcassets:
+ return
+
+ extra_arguments = {}
+ settings_to_arg = {
+ 'XCASSETS_APP_ICON': 'app-icon',
+ 'XCASSETS_LAUNCH_IMAGE': 'launch-image',
+ }
+ settings = self.xcode_settings.xcode_settings[self.config_name]
+ for settings_key, arg_name in settings_to_arg.iteritems():
+ value = settings.get(settings_key)
+ if value:
+ extra_arguments[arg_name] = value
+
+ partial_info_plist = None
+ if extra_arguments:
+ partial_info_plist = self.GypPathToUniqueOutput(
+ 'assetcatalog_generated_info.plist')
+ extra_arguments['output-partial-info-plist'] = partial_info_plist
- def WriteMacInfoPlist(self, bundle_depends):
+ outputs = []
+ outputs.append(
+ os.path.join(
+ self.xcode_settings.GetBundleResourceFolder(),
+ 'Assets.car'))
+ if partial_info_plist:
+ outputs.append(partial_info_plist)
+
+ keys = QuoteShellArgument(json.dumps(extra_arguments), self.flavor)
+ extra_env = self.xcode_settings.GetPerTargetSettings()
+ env = self.GetSortedXcodeEnv(additional_settings=extra_env)
+ env = self.ComputeExportEnvString(env)
+
+ bundle_depends.extend(self.ninja.build(
+ outputs, 'compile_xcassets', xcassets,
+ variables=[('env', env), ('keys', keys)]))
+ return partial_info_plist
+
+ def WriteMacInfoPlist(self, partial_info_plist, bundle_depends):
"""Write build rules for bundle Info.plist files."""
info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
generator_default_variables['PRODUCT_DIR'],
@@ -770,10 +841,18 @@ def WriteMacInfoPlist(self, bundle_depends):
env = self.GetSortedXcodeEnv(additional_settings=extra_env)
env = self.ComputeExportEnvString(env)
+ if partial_info_plist:
+ intermediate_plist = self.GypPathToUniqueOutput('merged_info.plist')
+ info_plist = self.ninja.build(
+ intermediate_plist, 'merge_infoplist',
+ [partial_info_plist, info_plist])
+
keys = self.xcode_settings.GetExtraPlistItems(self.config_name)
keys = QuoteShellArgument(json.dumps(keys), self.flavor)
+ isBinary = self.xcode_settings.IsBinaryOutputFormat(self.config_name)
self.ninja.build(out, 'copy_infoplist', info_plist,
- variables=[('env', env), ('keys', keys)])
+ variables=[('env', env), ('keys', keys),
+ ('binary', isBinary)])
bundle_depends.append(out)
def WriteSources(self, ninja_file, config_name, config, sources, predepends,
@@ -785,6 +864,8 @@ def WriteSources(self, ninja_file, config_name, config, sources, predepends,
self.ninja.variable('cxx', '$cxx_host')
self.ninja.variable('ld', '$ld_host')
self.ninja.variable('ldxx', '$ldxx_host')
+ self.ninja.variable('nm', '$nm_host')
+ self.ninja.variable('readelf', '$readelf_host')
if self.flavor != 'mac' or len(self.archs) == 1:
return self.WriteSourcesForArch(
@@ -810,6 +891,7 @@ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
cflags_objcc = ['$cflags_cc'] + \
self.xcode_settings.GetCflagsObjCC(config_name)
elif self.flavor == 'win':
+ asmflags = self.msvs_settings.GetAsmflags(config_name)
cflags = self.msvs_settings.GetCflags(config_name)
cflags_c = self.msvs_settings.GetCflagsC(config_name)
cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
@@ -844,22 +926,31 @@ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
self.WriteVariableList(ninja_file, 'defines',
[Define(d, self.flavor) for d in defines])
if self.flavor == 'win':
+ self.WriteVariableList(ninja_file, 'asmflags',
+ map(self.ExpandSpecial, asmflags))
self.WriteVariableList(ninja_file, 'rcflags',
[QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
for f in self.msvs_settings.GetRcflags(config_name,
self.GypPathToNinja)])
include_dirs = config.get('include_dirs', [])
- env = self.GetSortedXcodeEnv()
+
+ env = self.GetToolchainEnv()
if self.flavor == 'win':
- env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
- config=config_name)
include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
config_name)
self.WriteVariableList(ninja_file, 'includes',
[QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
for i in include_dirs])
+ if self.flavor == 'win':
+ midl_include_dirs = config.get('midl_include_dirs', [])
+ midl_include_dirs = self.msvs_settings.AdjustMidlIncludeDirs(
+ midl_include_dirs, config_name)
+ self.WriteVariableList(ninja_file, 'midl_includes',
+ [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
+ for i in midl_include_dirs])
+
pch_commands = precompiled_header.GetPchBuildCommands(arch)
if self.flavor == 'mac':
# Most targets use no precompiled headers, so only write these if needed.
@@ -868,6 +959,8 @@ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
include = precompiled_header.GetInclude(ext, arch)
if include: ninja_file.variable(var, include)
+ arflags = config.get('arflags', [])
+
self.WriteVariableList(ninja_file, 'cflags',
map(self.ExpandSpecial, cflags))
self.WriteVariableList(ninja_file, 'cflags_c',
@@ -879,6 +972,8 @@ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
map(self.ExpandSpecial, cflags_objc))
self.WriteVariableList(ninja_file, 'cflags_objcc',
map(self.ExpandSpecial, cflags_objcc))
+ self.WriteVariableList(ninja_file, 'arflags',
+ map(self.ExpandSpecial, arflags))
ninja_file.newline()
outputs = []
has_rc_source = False
@@ -894,9 +989,7 @@ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
elif ext == 's' and self.flavor != 'win': # Doesn't generate .o.d files.
command = 'cc_s'
elif (self.flavor == 'win' and ext == 'asm' and
- self.msvs_settings.GetArch(config_name) == 'x86' and
not self.msvs_settings.HasExplicitAsmRules(spec)):
- # Asm files only get auto assembled for x86 (not x64).
command = 'asm'
# Add the _asm suffix as msvs is capable of handling .cc and
# .asm files of the same name without collision.
@@ -968,9 +1061,19 @@ def WriteLink(self, spec, config_name, config, link_deps):
arch=arch)
for arch in self.archs]
extra_bindings = []
+ build_output = output
if not self.is_mac_bundle:
self.AppendPostbuildVariable(extra_bindings, spec, output, output)
- self.ninja.build(output, 'lipo', inputs, variables=extra_bindings)
+
+ # TODO(yyanagisawa): more work needed to fix:
+ # https://code.google.com/p/gyp/issues/detail?id=411
+ if (spec['type'] in ('shared_library', 'loadable_module') and
+ not self.is_mac_bundle):
+ extra_bindings.append(('lib', output))
+ self.ninja.build([output, output + '.TOC'], 'solipo', inputs,
+ variables=extra_bindings)
+ else:
+ self.ninja.build(build_output, 'lipo', inputs, variables=extra_bindings)
return output
def WriteLinkForArch(self, ninja_file, spec, config_name, config,
@@ -1063,7 +1166,7 @@ def WriteLinkForArch(self, ninja_file, spec, config_name, config,
rpath = 'lib/'
if self.toolset != 'target':
rpath += self.toolset
- ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
+ ldflags.append(r'-Wl,-rpath=\$$ORIGIN/%s' % rpath)
ldflags.append('-Wl,-rpath-link=%s' % rpath)
self.WriteVariableList(ninja_file, 'ldflags',
gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
@@ -1095,9 +1198,27 @@ def WriteLinkForArch(self, ninja_file, spec, config_name, config,
extra_bindings.append(('soname', os.path.split(output)[1]))
extra_bindings.append(('lib',
gyp.common.EncodePOSIXShellArgument(output)))
+ if self.flavor != 'win':
+ link_file_list = output
+ if self.is_mac_bundle:
+ # 'Dependency Framework.framework/Versions/A/Dependency Framework' ->
+ # 'Dependency Framework.framework.rsp'
+ link_file_list = self.xcode_settings.GetWrapperName()
+ if arch:
+ link_file_list += '.' + arch
+ link_file_list += '.rsp'
+ # If an rspfile contains spaces, ninja surrounds the filename with
+ # quotes around it and then passes it to open(), creating a file with
+ # quotes in its name (and when looking for the rsp file, the name
+ # makes it through bash which strips the quotes) :-/
+ link_file_list = link_file_list.replace(' ', '_')
+ extra_bindings.append(
+ ('link_file_list',
+ gyp.common.EncodePOSIXShellArgument(link_file_list)))
if self.flavor == 'win':
extra_bindings.append(('binary', output))
- if '/NOENTRY' not in ldflags:
+ if ('/NOENTRY' not in ldflags and
+ not self.msvs_settings.GetNoImportLibrary(config_name)):
self.target.import_lib = output + '.lib'
extra_bindings.append(('implibflag',
'/IMPLIB:%s' % self.target.import_lib))
@@ -1196,6 +1317,19 @@ def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
self.target.bundle = output
return output
+ def GetToolchainEnv(self, additional_settings=None):
+ """Returns the variables toolchain would set for build steps."""
+ env = self.GetSortedXcodeEnv(additional_settings=additional_settings)
+ if self.flavor == 'win':
+ env = self.GetMsvsToolchainEnv(
+ additional_settings=additional_settings)
+ return env
+
+ def GetMsvsToolchainEnv(self, additional_settings=None):
+ """Returns the variables Visual Studio would set for build steps."""
+ return self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
+ config=self.config_name)
+
def GetSortedXcodeEnv(self, additional_settings=None):
"""Returns the variables Xcode would set for build steps."""
assert self.abs_build_dir
@@ -1377,7 +1511,8 @@ def WriteVariableList(self, ninja_file, var, values):
values = []
ninja_file.variable(var, ' '.join(values))
- def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
+ def WriteNewNinjaRule(self, name, args, description, is_cygwin, env, pool,
+ depfile=None):
"""Write out a new ninja "rule" statement for a given command.
Returns the name of the new rule, and a copy of |args| with variables
@@ -1435,7 +1570,8 @@ def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
# 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.rule(rule_name, command, description, depfile=depfile,
+ restat=True, pool=pool,
rspfile=rspfile, rspfile_content=rspfile_content)
self.ninja.newline()
@@ -1466,12 +1602,13 @@ def CalculateVariables(default_variables, params):
generator_extra_sources_for_rules = getattr(xcode_generator,
'generator_extra_sources_for_rules', [])
elif flavor == 'win':
+ exts = gyp.MSVSUtil.TARGET_TYPE_EXT
default_variables.setdefault('OS', 'win')
- default_variables['EXECUTABLE_SUFFIX'] = '.exe'
+ default_variables['EXECUTABLE_SUFFIX'] = '.' + exts['executable']
default_variables['STATIC_LIB_PREFIX'] = ''
- default_variables['STATIC_LIB_SUFFIX'] = '.lib'
+ default_variables['STATIC_LIB_SUFFIX'] = '.' + exts['static_library']
default_variables['SHARED_LIB_PREFIX'] = ''
- default_variables['SHARED_LIB_SUFFIX'] = '.dll'
+ default_variables['SHARED_LIB_SUFFIX'] = '.' + exts['shared_library']
# Copy additional generator configuration data from VS, which is shared
# by the Windows Ninja generator.
@@ -1535,6 +1672,10 @@ def CommandWithWrapper(cmd, wrappers, prog):
def GetDefaultConcurrentLinks():
"""Returns a best-guess for a number of concurrent links."""
+ pool_size = int(os.getenv('GYP_LINK_CONCURRENCY', 0))
+ if pool_size:
+ return pool_size
+
if sys.platform in ('win32', 'cygwin'):
import ctypes
@@ -1557,19 +1698,17 @@ class MEMORYSTATUSEX(ctypes.Structure):
mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30))) # total / 4GB
hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
- # return min(mem_limit, hard_cap)
- # TODO(scottmg): Temporary speculative fix for OOM on builders
- # See http://crbug.com/333000.
- return 2
+ return min(mem_limit, hard_cap)
elif sys.platform.startswith('linux'):
- with open("/proc/meminfo") as meminfo:
- memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
- for line in meminfo:
- match = memtotal_re.match(line)
- if not match:
- continue
- # Allow 8Gb per link on Linux because Gold is quite memory hungry
- return max(1, int(match.group(1)) / (8 * (2 ** 20)))
+ if os.path.exists("/proc/meminfo"):
+ with open("/proc/meminfo") as meminfo:
+ memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
+ for line in meminfo:
+ match = memtotal_re.match(line)
+ if not match:
+ continue
+ # Allow 8Gb per link on Linux because Gold is quite memory hungry
+ return max(1, int(match.group(1)) / (8 * (2 ** 20)))
return 1
elif sys.platform == 'darwin':
try:
@@ -1666,14 +1805,15 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# 'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
# to cc/cxx.
if flavor == 'win':
- # Overridden by local arch choice in the use_deps case.
- # Chromium's ffmpeg c99conv.py currently looks for a 'cc =' line in
- # build.ninja so needs something valid here. http://crbug.com/233985
- cc = 'cl.exe'
- cxx = 'cl.exe'
+ ar = 'lib.exe'
+ # cc and cxx must be set to the correct architecture by overriding with one
+ # of cl_x86 or cl_x64 below.
+ cc = 'UNSET'
+ cxx = 'UNSET'
ld = 'link.exe'
ld_host = '$ld'
else:
+ ar = 'ar'
cc = 'cc'
cxx = 'c++'
ld = '$cc'
@@ -1681,10 +1821,16 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
ld_host = '$cc_host'
ldxx_host = '$cxx_host'
+ ar_host = 'ar'
cc_host = None
cxx_host = None
cc_host_global_setting = None
cxx_host_global_setting = None
+ clang_cl = None
+ nm = 'nm'
+ nm_host = 'nm'
+ readelf = 'readelf'
+ readelf_host = 'readelf'
build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
make_global_settings = data[build_file].get('make_global_settings', [])
@@ -1692,8 +1838,14 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
options.toplevel_dir)
wrappers = {}
for key, value in make_global_settings:
+ if key == 'AR':
+ ar = os.path.join(build_to_root, value)
+ if key == 'AR.host':
+ ar_host = os.path.join(build_to_root, value)
if key == 'CC':
cc = os.path.join(build_to_root, value)
+ if cc.endswith('clang-cl'):
+ clang_cl = cc
if key == 'CXX':
cxx = os.path.join(build_to_root, value)
if key == 'CC.host':
@@ -1702,6 +1854,18 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
if key == 'CXX.host':
cxx_host = os.path.join(build_to_root, value)
cxx_host_global_setting = value
+ if key == 'LD':
+ ld = os.path.join(build_to_root, value)
+ if key == 'LD.host':
+ ld_host = os.path.join(build_to_root, value)
+ if key == 'NM':
+ nm = os.path.join(build_to_root, value)
+ if key == 'NM.host':
+ nm_host = os.path.join(build_to_root, value)
+ if key == 'READELF':
+ readelf = os.path.join(build_to_root, value)
+ if key == 'READELF.host':
+ readelf_host = os.path.join(build_to_root, value)
if key.endswith('_wrapper'):
wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
@@ -1713,12 +1877,25 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
wrappers[key_prefix] = os.path.join(build_to_root, value)
if flavor == 'win':
+ configs = [target_dicts[qualified_target]['configurations'][config_name]
+ for qualified_target in target_list]
+ shared_system_includes = None
+ if not generator_flags.get('ninja_use_custom_environment_files', 0):
+ shared_system_includes = \
+ gyp.msvs_emulation.ExtractSharedMSVSSystemIncludes(
+ configs, generator_flags)
cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
- toplevel_build, generator_flags, OpenOutput)
+ toplevel_build, generator_flags, shared_system_includes, OpenOutput)
for arch, path in cl_paths.iteritems():
- master_ninja.variable(
- 'cl_' + arch, CommandWithWrapper('CC', wrappers,
- QuoteShellArgument(path, flavor)))
+ if clang_cl:
+ # If we have selected clang-cl, use that instead.
+ path = clang_cl
+ command = CommandWithWrapper('CC', wrappers,
+ QuoteShellArgument(path, 'win'))
+ if clang_cl:
+ # Use clang-cl to cross-compile for x86 or x86_64.
+ command += (' -m32' if arch == 'x86' else ' -m64')
+ master_ninja.variable('cl_' + arch, command)
cc = GetEnvironFallback(['CC_target', 'CC'], cc)
master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
@@ -1728,14 +1905,22 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
if flavor == 'win':
master_ninja.variable('ld', ld)
master_ninja.variable('idl', 'midl.exe')
- master_ninja.variable('ar', 'lib.exe')
+ master_ninja.variable('ar', ar)
master_ninja.variable('rc', 'rc.exe')
- master_ninja.variable('asm', 'ml.exe')
+ master_ninja.variable('ml_x86', 'ml.exe')
+ master_ninja.variable('ml_x64', 'ml64.exe')
master_ninja.variable('mt', 'mt.exe')
else:
master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
- master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
+ master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], ar))
+ if flavor != 'mac':
+ # Mac does not use readelf/nm for .TOC generation, so avoiding polluting
+ # the master ninja with extra unused variables.
+ master_ninja.variable(
+ 'nm', GetEnvironFallback(['NM_target', 'NM'], nm))
+ master_ninja.variable(
+ 'readelf', GetEnvironFallback(['READELF_target', 'READELF'], readelf))
if generator_supports_multiple_toolsets:
if not cc_host:
@@ -1743,7 +1928,10 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
if not cxx_host:
cxx_host = cxx
- master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
+ master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], ar_host))
+ master_ninja.variable('nm_host', GetEnvironFallback(['NM_host'], nm_host))
+ master_ninja.variable('readelf_host',
+ GetEnvironFallback(['READELF_host'], readelf_host))
cc_host = GetEnvironFallback(['CC_host'], cc_host)
cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
@@ -1826,7 +2014,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
description='IDL $in',
command=('%s gyp-win-tool midl-wrapper $arch $outdir '
'$tlb $h $dlldata $iid $proxy $in '
- '$idlflags' % sys.executable))
+ '$midl_includes $idlflags' % sys.executable))
master_ninja.rule(
'rc',
description='RC $in',
@@ -1836,20 +2024,20 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
sys.executable))
master_ninja.rule(
'asm',
- description='ASM $in',
+ description='ASM $out',
command=('%s gyp-win-tool asm-wrapper '
- '$arch $asm $defines $includes /c /Fo $out $in' %
+ '$arch $asm $defines $includes $asmflags /c /Fo $out $in' %
sys.executable))
if flavor != 'mac' and flavor != 'win':
master_ninja.rule(
'alink',
description='AR $out',
- command='rm -f $out && $ar rcs $out $in')
+ command='rm -f $out && $ar rcs $arflags $out $in')
master_ninja.rule(
'alink_thin',
description='AR $out',
- command='rm -f $out && $ar rcsT $out $in')
+ command='rm -f $out && $ar rcsT $arflags $out $in')
# This allows targets that only need to depend on $lib's API to declare an
# order-only dependency on $lib.TOC and avoid relinking such downstream
@@ -1857,38 +2045,39 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# The resulting string leaves an uninterpolated %{suffix} which
# is used in the final substitution below.
mtime_preserving_solink_base = (
- 'if [ ! -e $lib -o ! -e ${lib}.TOC ]; then '
- '%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
- '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
- 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
+ 'if [ ! -e $lib -o ! -e $lib.TOC ]; then '
+ '%(solink)s && %(extract_toc)s > $lib.TOC; else '
+ '%(solink)s && %(extract_toc)s > $lib.tmp && '
+ 'if ! cmp -s $lib.tmp $lib.TOC; then mv $lib.tmp $lib.TOC ; '
'fi; fi'
% { 'solink':
'$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
'extract_toc':
- ('{ readelf -d ${lib} | grep SONAME ; '
- 'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
+ ('{ $readelf -d $lib | grep SONAME ; '
+ '$nm -gD -f p $lib | cut -f1-2 -d\' \'; }')})
master_ninja.rule(
'solink',
description='SOLINK $lib',
restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
- '$libs'}),
+ command=mtime_preserving_solink_base % {'suffix': '@$link_file_list'},
+ rspfile='$link_file_list',
+ rspfile_content=
+ '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive $libs',
pool='link_pool')
master_ninja.rule(
'solink_module',
description='SOLINK(module) $lib',
restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
- '$libs'}),
+ command=mtime_preserving_solink_base % {'suffix': '@$link_file_list'},
+ rspfile='$link_file_list',
+ rspfile_content='-Wl,--start-group $in -Wl,--end-group $solibs $libs',
pool='link_pool')
master_ninja.rule(
'link',
description='LINK $out',
command=('$ld $ldflags -o $out '
- '-Wl,--start-group $in $solibs -Wl,--end-group $libs'),
+ '-Wl,--start-group $in -Wl,--end-group $solibs $libs'),
pool='link_pool')
elif flavor == 'win':
master_ninja.rule(
@@ -1927,21 +2116,31 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
'lipo',
description='LIPO $out, POSTBUILDS',
command='rm -f $out && lipo -create $in -output $out$postbuilds')
+ master_ninja.rule(
+ 'solipo',
+ description='SOLIPO $out, POSTBUILDS',
+ command=(
+ 'rm -f $lib $lib.TOC && lipo -create $in -output $lib$postbuilds &&'
+ '%(extract_toc)s > $lib.TOC'
+ % { 'extract_toc':
+ '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
+ 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'}))
+
# Record the public interface of $lib in $lib.TOC. See the corresponding
# comment in the posix section above for details.
solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
mtime_preserving_solink_base = (
- 'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
+ 'if [ ! -e $lib -o ! -e $lib.TOC ] || '
# Always force dependent targets to relink if this library
# reexports something. Handling this correctly would require
# recursive TOC dumping but this is rare in practice, so punt.
'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
- '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
+ '%(solink)s && %(extract_toc)s > $lib.TOC; '
'else '
- '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
- 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
- 'mv ${lib}.tmp ${lib}.TOC ; '
+ '%(solink)s && %(extract_toc)s > $lib.tmp && '
+ 'if ! cmp -s $lib.tmp $lib.TOC; then '
+ 'mv $lib.tmp $lib.TOC ; '
'fi; '
'fi'
% { 'solink': solink_base,
@@ -1949,34 +2148,42 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
'{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
- solink_suffix = '$in $solibs $libs$postbuilds'
+
+ solink_suffix = '@$link_file_list$postbuilds'
master_ninja.rule(
'solink',
description='SOLINK $lib, POSTBUILDS',
restat=True,
command=mtime_preserving_solink_base % {'suffix': solink_suffix,
'type': '-shared'},
+ rspfile='$link_file_list',
+ rspfile_content='$in $solibs $libs',
pool='link_pool')
master_ninja.rule(
'solink_notoc',
description='SOLINK $lib, POSTBUILDS',
restat=True,
command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
+ rspfile='$link_file_list',
+ rspfile_content='$in $solibs $libs',
pool='link_pool')
- solink_module_suffix = '$in $solibs $libs$postbuilds'
master_ninja.rule(
'solink_module',
description='SOLINK(module) $lib, POSTBUILDS',
restat=True,
- command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
+ command=mtime_preserving_solink_base % {'suffix': solink_suffix,
'type': '-bundle'},
+ rspfile='$link_file_list',
+ rspfile_content='$in $solibs $libs',
pool='link_pool')
master_ninja.rule(
'solink_module_notoc',
description='SOLINK(module) $lib, POSTBUILDS',
restat=True,
- command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
+ command=solink_base % {'suffix': solink_suffix, 'type': '-bundle'},
+ rspfile='$link_file_list',
+ rspfile_content='$in $solibs $libs',
pool='link_pool')
master_ninja.rule(
@@ -1993,11 +2200,19 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
master_ninja.rule(
'copy_infoplist',
description='COPY INFOPLIST $in',
- command='$env ./gyp-mac-tool copy-info-plist $in $out $keys')
+ command='$env ./gyp-mac-tool copy-info-plist $in $out $binary $keys')
+ master_ninja.rule(
+ 'merge_infoplist',
+ description='MERGE INFOPLISTS $in',
+ command='$env ./gyp-mac-tool merge-info-plist $out $in')
+ master_ninja.rule(
+ 'compile_xcassets',
+ description='COMPILE XCASSETS $in',
+ command='$env ./gyp-mac-tool compile-xcassets $keys $in')
master_ninja.rule(
'mac_tool',
description='MACTOOL $mactool_cmd $in',
- command='$env ./gyp-mac-tool $mactool_cmd $in $out')
+ command='$env ./gyp-mac-tool $mactool_cmd $in $out $binary')
master_ninja.rule(
'package_framework',
description='PACKAGE FRAMEWORK $out, POSTBUILDS',
@@ -2037,6 +2252,15 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
# objects.
target_short_names = {}
+ # short name of targets that were skipped because they didn't contain anything
+ # interesting.
+ # NOTE: there may be overlap between this an non_empty_target_names.
+ empty_target_names = set()
+
+ # Set of non-empty short target names.
+ # NOTE: there may be overlap between this an empty_target_names.
+ non_empty_target_names = set()
+
for qualified_target in target_list:
# qualified_target is like: third_party/icu/icu.gyp:icui18n#target
build_file, name, toolset = \
@@ -2053,6 +2277,10 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
+ qualified_target_for_hash = gyp.common.QualifiedTarget(build_file, name,
+ toolset)
+ hash_for_rules = hashlib.md5(qualified_target_for_hash).hexdigest()
+
base_path = os.path.dirname(build_file)
obj = 'obj'
if toolset != 'target':
@@ -2060,7 +2288,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
output_file = os.path.join(obj, base_path, name + '.ninja')
ninja_output = StringIO()
- writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
+ writer = NinjaWriter(hash_for_rules, target_outputs, base_path, build_dir,
ninja_output,
toplevel_build, output_file,
flavor, toplevel_dir=options.toplevel_dir)
@@ -2080,6 +2308,9 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
target_outputs[qualified_target] = target
if qualified_target in all_targets:
all_outputs.add(target.FinalOutput())
+ non_empty_target_names.add(name)
+ else:
+ empty_target_names.add(name)
if target_short_names:
# Write a short name to build this target. This benefits both the
@@ -2091,6 +2322,16 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params,
master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
target_short_names[short_name]])
+ # Write phony targets for any empty targets that weren't written yet. As
+ # short names are not necessarily unique only do this for short names that
+ # haven't already been output for another target.
+ empty_target_names = empty_target_names - non_empty_target_names
+ if empty_target_names:
+ master_ninja.newline()
+ master_ninja.comment('Empty targets (output for completeness).')
+ for name in sorted(empty_target_names):
+ master_ninja.build(name, 'phony')
+
if all_outputs:
master_ninja.newline()
master_ninja.build('all', 'phony', list(all_outputs))
diff --git a/tools/gyp/pylib/gyp/generator/ninja_test.py b/tools/gyp/pylib/gyp/generator/ninja_test.py
index 52661bcdf093ba..1767b2f45a04ca 100644
--- a/tools/gyp/pylib/gyp/generator/ninja_test.py
+++ b/tools/gyp/pylib/gyp/generator/ninja_test.py
@@ -15,15 +15,18 @@
class TestPrefixesAndSuffixes(unittest.TestCase):
def test_BinaryNamesWindows(self):
- writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.',
- 'build.ninja', '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'))
+ # These cannot run on non-Windows as they require a VS installation to
+ # correctly handle variable expansion.
+ if sys.platform.startswith('win'):
+ writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.',
+ 'build.ninja', '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('foo', 'wee', '.', '.', 'build.ninja', '.',
diff --git a/tools/gyp/pylib/gyp/generator/xcode.py b/tools/gyp/pylib/gyp/generator/xcode.py
index 331e78baaa67d0..482b53ac8ad9ec 100644
--- a/tools/gyp/pylib/gyp/generator/xcode.py
+++ b/tools/gyp/pylib/gyp/generator/xcode.py
@@ -5,6 +5,7 @@
import filecmp
import gyp.common
import gyp.xcodeproj_file
+import gyp.xcode_ninja
import errno
import os
import sys
@@ -68,6 +69,9 @@
# The Xcode-specific keys that exist on targets and aren't moved down to
# configurations.
generator_additional_non_configuration_keys = [
+ 'ios_app_extension',
+ 'ios_watch_app',
+ 'ios_watchkit_extension',
'mac_bundle',
'mac_bundle_resources',
'mac_framework_headers',
@@ -484,7 +488,7 @@ def Write(self):
def AddSourceToTarget(source, type, pbxp, xct):
# TODO(mark): Perhaps source_extensions and library_extensions can be made a
# little bit fancier.
- source_extensions = ['c', 'cc', 'cpp', 'cxx', 'm', 'mm', 's']
+ source_extensions = ['c', 'cc', 'cpp', 'cxx', 'm', 'mm', 's', 'swift']
# .o is conceptually more of a "source" than a "library," but Xcode thinks
# of "sources" as things to compile and "libraries" (or "frameworks") as
@@ -520,7 +524,7 @@ def AddHeaderToTarget(header, pbxp, xct, is_public):
xct.HeadersPhase().AddFile(header, settings)
-_xcode_variable_re = re.compile('(\$\((.*?)\))')
+_xcode_variable_re = re.compile(r'(\$\((.*?)\))')
def ExpandXcodeVariables(string, expansions):
"""Expands Xcode-style $(VARIABLES) in string per the expansions dict.
@@ -575,12 +579,17 @@ def PerformBuild(data, configurations, params):
def GenerateOutput(target_list, target_dicts, data, params):
+ # Optionally configure each spec to use ninja as the external builder.
+ ninja_wrapper = params.get('flavor') == 'ninja'
+ if ninja_wrapper:
+ (target_list, target_dicts, data) = \
+ gyp.xcode_ninja.CreateWrapper(target_list, target_dicts, data, params)
+
options = params['options']
generator_flags = params.get('generator_flags', {})
parallel_builds = generator_flags.get('xcode_parallel_builds', True)
serialize_all_tests = \
generator_flags.get('xcode_serialize_all_test_runs', True)
- project_version = generator_flags.get('xcode_project_version', None)
skip_excluded_files = \
not generator_flags.get('xcode_list_excluded_files', True)
xcode_projects = {}
@@ -598,8 +607,6 @@ def GenerateOutput(target_list, target_dicts, data, params):
if parallel_builds:
pbxp.SetProperty('attributes',
{'BuildIndependentTargetsInParallel': 'YES'})
- if project_version:
- xcp.project_file.SetXcodeVersion(project_version)
# Add gyp/gypi files to project
if not generator_flags.get('standalone'):
@@ -637,14 +644,18 @@ def GenerateOutput(target_list, target_dicts, data, params):
# com.googlecode.gyp.xcode.bundle, a pseudo-type that xcode.py interprets
# to create a single-file mh_bundle.
_types = {
- 'executable': 'com.apple.product-type.tool',
- 'loadable_module': 'com.googlecode.gyp.xcode.bundle',
- 'shared_library': 'com.apple.product-type.library.dynamic',
- 'static_library': 'com.apple.product-type.library.static',
- 'executable+bundle': 'com.apple.product-type.application',
- 'loadable_module+bundle': 'com.apple.product-type.bundle',
- 'loadable_module+xctest': 'com.apple.product-type.bundle.unit-test',
- 'shared_library+bundle': 'com.apple.product-type.framework',
+ 'executable': 'com.apple.product-type.tool',
+ 'loadable_module': 'com.googlecode.gyp.xcode.bundle',
+ 'shared_library': 'com.apple.product-type.library.dynamic',
+ 'static_library': 'com.apple.product-type.library.static',
+ 'executable+bundle': 'com.apple.product-type.application',
+ 'loadable_module+bundle': 'com.apple.product-type.bundle',
+ 'loadable_module+xctest': 'com.apple.product-type.bundle.unit-test',
+ 'shared_library+bundle': 'com.apple.product-type.framework',
+ 'executable+extension+bundle': 'com.apple.product-type.app-extension',
+ 'executable+watch+extension+bundle':
+ 'com.apple.product-type.watchkit-extension',
+ 'executable+watch+bundle': 'com.apple.product-type.application.watchapp',
}
target_properties = {
@@ -655,6 +666,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
type = spec['type']
is_xctest = int(spec.get('mac_xctest_bundle', 0))
is_bundle = int(spec.get('mac_bundle', 0)) or is_xctest
+ is_app_extension = int(spec.get('ios_app_extension', 0))
+ is_watchkit_extension = int(spec.get('ios_watchkit_extension', 0))
+ is_watch_app = int(spec.get('ios_watch_app', 0))
if type != 'none':
type_bundle_key = type
if is_xctest:
@@ -662,6 +676,18 @@ def GenerateOutput(target_list, target_dicts, data, params):
assert type == 'loadable_module', (
'mac_xctest_bundle targets must have type loadable_module '
'(target %s)' % target_name)
+ elif is_app_extension:
+ assert is_bundle, ('ios_app_extension flag requires mac_bundle '
+ '(target %s)' % target_name)
+ type_bundle_key += '+extension+bundle'
+ elif is_watchkit_extension:
+ assert is_bundle, ('ios_watchkit_extension flag requires mac_bundle '
+ '(target %s)' % target_name)
+ type_bundle_key += '+watch+extension+bundle'
+ elif is_watch_app:
+ assert is_bundle, ('ios_watch_app flag requires mac_bundle '
+ '(target %s)' % target_name)
+ type_bundle_key += '+watch+bundle'
elif is_bundle:
type_bundle_key += '+bundle'
@@ -703,11 +729,16 @@ def GenerateOutput(target_list, target_dicts, data, params):
# and is made a dependency of this target. This way the work is done
# before the dependency checks for what should be recompiled.
support_xct = None
- if type != 'none' and (spec_actions or spec_rules):
+ # The Xcode "issues" don't affect xcode-ninja builds, since the dependency
+ # logic all happens in ninja. Don't bother creating the extra targets in
+ # that case.
+ if type != 'none' and (spec_actions or spec_rules) and not ninja_wrapper:
support_xccl = CreateXCConfigurationList(configuration_names);
+ support_target_suffix = generator_flags.get(
+ 'support_target_suffix', ' Support')
support_target_properties = {
'buildConfigurationList': support_xccl,
- 'name': target_name + ' Support',
+ 'name': target_name + support_target_suffix,
}
if target_product_name:
support_target_properties['productName'] = \
@@ -1096,6 +1127,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
# Relative paths are relative to $(SRCROOT).
dest = '$(SRCROOT)/' + dest
+ code_sign = int(copy_group.get('xcode_code_sign', 0))
+ settings = (None, '{ATTRIBUTES = (CodeSignOnCopy, ); }')[code_sign];
+
# Coalesce multiple "copies" sections in the same target with the same
# "destination" property into the same PBXCopyFilesBuildPhase, otherwise
# they'll wind up with ID collisions.
@@ -1114,7 +1148,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
pbxcp_dict[dest] = pbxcp
for file in copy_group['files']:
- pbxcp.AddFile(file)
+ pbxcp.AddFile(file, settings)
# Excluded files can also go into the project file.
if not skip_excluded_files:
diff --git a/tools/gyp/pylib/gyp/input.py b/tools/gyp/pylib/gyp/input.py
index 6472912db8eb0e..34fbc54711923c 100644
--- a/tools/gyp/pylib/gyp/input.py
+++ b/tools/gyp/pylib/gyp/input.py
@@ -10,8 +10,8 @@
from compiler.ast import Node
from compiler.ast import Stmt
import compiler
-import copy
import gyp.common
+import gyp.simple_copy
import multiprocessing
import optparse
import os.path
@@ -24,6 +24,7 @@
import time
import traceback
from gyp.common import GypError
+from gyp.common import OrderedSet
# A list of types that are treated as linkable.
@@ -45,18 +46,36 @@
'outputs',
'sources',
]
-path_sections = []
+path_sections = set()
-is_path_section_charset = set('=+?!')
-is_path_section_match_re = re.compile('_(dir|file|path)s?$')
+# These per-process dictionaries are used to cache build file data when loading
+# in parallel mode.
+per_process_data = {}
+per_process_aux_data = {}
def IsPathSection(section):
- # If section ends in one of these characters, it's applied to a section
+ # If section ends in one of the '=+?!' characters, it's applied to a section
# without the trailing characters. '/' is notably absent from this list,
# because there's no way for a regular expression to be treated as a path.
- while section[-1:] in is_path_section_charset:
+ while section[-1:] in '=+?!':
section = section[:-1]
- return section in path_sections or is_path_section_match_re.search(section)
+
+ if section in path_sections:
+ return True
+
+ # Sections mathing the regexp '_(dir|file|path)s?$' are also
+ # considered PathSections. Using manual string matching since that
+ # is much faster than the regexp and this can be called hundreds of
+ # thousands of times so micro performance matters.
+ if "_" in section:
+ tail = section[-6:]
+ if tail[-1] == 's':
+ tail = tail[:-1]
+ if tail[-5:] in ('_file', '_path'):
+ return True
+ return tail[-4:] == '_dir'
+
+ return False
# base_non_configuration_keys is a list of key names that belong in the target
# itself and should not be propagated into its configurations. It is merged
@@ -196,11 +215,11 @@ def CheckNode(node, keypath):
elif isinstance(node, Const):
return node.getChildren()[0]
else:
- raise TypeError, "Unknown AST node at key path '" + '.'.join(keypath) + \
- "': " + repr(node)
+ raise TypeError("Unknown AST node at key path '" + '.'.join(keypath) +
+ "': " + repr(node))
-def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
+def LoadOneBuildFile(build_file_path, data, aux_data, includes,
is_target, check):
if build_file_path in data:
return data[build_file_path]
@@ -224,7 +243,7 @@ def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
gyp.common.ExceptionAppend(e, 'while reading ' + build_file_path)
raise
- if not isinstance(build_file_data, dict):
+ if type(build_file_data) is not dict:
raise GypError("%s does not evaluate to a dictionary." % build_file_path)
data[build_file_path] = build_file_data
@@ -236,10 +255,10 @@ def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
try:
if is_target:
LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data,
- aux_data, variables, includes, check)
+ aux_data, includes, check)
else:
LoadBuildFileIncludesIntoDict(build_file_data, build_file_path, data,
- aux_data, variables, None, check)
+ aux_data, None, check)
except Exception, e:
gyp.common.ExceptionAppend(e,
'while reading includes of ' + build_file_path)
@@ -249,7 +268,7 @@ def LoadOneBuildFile(build_file_path, data, aux_data, variables, includes,
def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data,
- variables, includes, check):
+ includes, check):
includes_list = []
if includes != None:
includes_list.extend(includes)
@@ -273,30 +292,27 @@ def LoadBuildFileIncludesIntoDict(subdict, subdict_path, data, aux_data,
gyp.DebugOutput(gyp.DEBUG_INCLUDES, "Loading Included File: '%s'", include)
MergeDicts(subdict,
- LoadOneBuildFile(include, data, aux_data, variables, None,
- False, check),
+ LoadOneBuildFile(include, data, aux_data, None, False, check),
subdict_path, include)
# Recurse into subdictionaries.
for k, v in subdict.iteritems():
- if v.__class__ == dict:
- LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data, variables,
+ if type(v) is dict:
+ LoadBuildFileIncludesIntoDict(v, subdict_path, data, aux_data,
None, check)
- elif v.__class__ == list:
- LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data, variables,
+ elif type(v) is list:
+ LoadBuildFileIncludesIntoList(v, subdict_path, data, aux_data,
check)
# This recurses into lists so that it can look for dicts.
-def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data,
- variables, check):
+def LoadBuildFileIncludesIntoList(sublist, sublist_path, data, aux_data, check):
for item in sublist:
- if item.__class__ == dict:
+ if type(item) is dict:
LoadBuildFileIncludesIntoDict(item, sublist_path, data, aux_data,
- variables, None, check)
- elif item.__class__ == list:
- LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data,
- variables, check)
+ None, check)
+ elif type(item) is list:
+ LoadBuildFileIncludesIntoList(item, sublist_path, data, aux_data, check)
# Processes toolsets in all the targets. This recurses into condition entries
# since they can contain toolsets as well.
@@ -320,7 +336,7 @@ def ProcessToolsetsInDict(data):
if len(toolsets) > 0:
# Optimization: only do copies if more than one toolset is specified.
for build in toolsets[1:]:
- new_target = copy.deepcopy(target)
+ new_target = gyp.simple_copy.deepcopy(target)
new_target['toolset'] = build
new_target_list.append(new_target)
target['toolset'] = toolsets[0]
@@ -328,9 +344,10 @@ def ProcessToolsetsInDict(data):
data['targets'] = new_target_list
if 'conditions' in data:
for condition in data['conditions']:
- if isinstance(condition, list):
+ if type(condition) is list:
for condition_dict in condition[1:]:
- ProcessToolsetsInDict(condition_dict)
+ if type(condition_dict) is dict:
+ ProcessToolsetsInDict(condition_dict)
# TODO(mark): I don't love this name. It just means that it's going to load
@@ -350,15 +367,22 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
else:
variables['DEPTH'] = d.replace('\\', '/')
- if build_file_path in data['target_build_files']:
- # Already loaded.
- return False
- data['target_build_files'].add(build_file_path)
+ # The 'target_build_files' key is only set when loading target build files in
+ # the non-parallel code path, where LoadTargetBuildFile is called
+ # recursively. In the parallel code path, we don't need to check whether the
+ # |build_file_path| has already been loaded, because the 'scheduled' set in
+ # ParallelState guarantees that we never load the same |build_file_path|
+ # twice.
+ if 'target_build_files' in data:
+ if build_file_path in data['target_build_files']:
+ # Already loaded.
+ return False
+ data['target_build_files'].add(build_file_path)
gyp.DebugOutput(gyp.DEBUG_INCLUDES,
"Loading Target Build File '%s'", build_file_path)
- build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables,
+ build_file_data = LoadOneBuildFile(build_file_path, data, aux_data,
includes, True, check)
# Store DEPTH for later use in generators.
@@ -408,7 +432,8 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
# copy with the target-specific data merged into it as the replacement
# target dict.
old_target_dict = build_file_data['targets'][index]
- new_target_dict = copy.deepcopy(build_file_data['target_defaults'])
+ new_target_dict = gyp.simple_copy.deepcopy(
+ build_file_data['target_defaults'])
MergeDicts(new_target_dict, old_target_dict,
build_file_path, build_file_path)
build_file_data['targets'][index] = new_target_dict
@@ -443,10 +468,8 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
else:
return (build_file_path, dependencies)
-
def CallLoadTargetBuildFile(global_flags,
- build_file_path, data,
- aux_data, variables,
+ build_file_path, variables,
includes, depth, check,
generator_input_info):
"""Wrapper around LoadTargetBuildFile for parallel processing.
@@ -462,35 +485,24 @@ def CallLoadTargetBuildFile(global_flags,
for key, value in global_flags.iteritems():
globals()[key] = value
- # Save the keys so we can return data that changed.
- data_keys = set(data)
- aux_data_keys = set(aux_data)
-
SetGeneratorGlobals(generator_input_info)
- result = LoadTargetBuildFile(build_file_path, data,
- aux_data, variables,
+ result = LoadTargetBuildFile(build_file_path, per_process_data,
+ per_process_aux_data, variables,
includes, depth, check, False)
if not result:
return result
(build_file_path, dependencies) = result
- data_out = {}
- for key in data:
- if key == 'target_build_files':
- continue
- if key not in data_keys:
- data_out[key] = data[key]
- aux_data_out = {}
- for key in aux_data:
- if key not in aux_data_keys:
- aux_data_out[key] = aux_data[key]
+ # We can safely pop the build_file_data from per_process_data because it
+ # will never be referenced by this process again, so we don't need to keep
+ # it in the cache.
+ build_file_data = per_process_data.pop(build_file_path)
# This gets serialized and sent back to the main process via a pipe.
# It's handled in LoadTargetBuildFileCallback.
return (build_file_path,
- data_out,
- aux_data_out,
+ build_file_data,
dependencies)
except GypError, e:
sys.stderr.write("gyp: %s\n" % e)
@@ -521,8 +533,6 @@ def __init__(self):
self.condition = None
# The "data" dict that was passed to LoadTargetBuildFileParallel
self.data = None
- # The "aux_data" dict that was passed to LoadTargetBuildFileParallel
- self.aux_data = None
# The number of parallel calls outstanding; decremented when a response
# was received.
self.pending = 0
@@ -543,12 +553,9 @@ def LoadTargetBuildFileCallback(self, result):
self.condition.notify()
self.condition.release()
return
- (build_file_path0, data0, aux_data0, dependencies0) = result
+ (build_file_path0, build_file_data0, dependencies0) = result
+ self.data[build_file_path0] = build_file_data0
self.data['target_build_files'].add(build_file_path0)
- for key in data0:
- self.data[key] = data0[key]
- for key in aux_data0:
- self.aux_data[key] = aux_data0[key]
for new_dependency in dependencies0:
if new_dependency not in self.scheduled:
self.scheduled.add(new_dependency)
@@ -558,9 +565,8 @@ def LoadTargetBuildFileCallback(self, result):
self.condition.release()
-def LoadTargetBuildFilesParallel(build_files, data, aux_data,
- variables, includes, depth, check,
- generator_input_info):
+def LoadTargetBuildFilesParallel(build_files, data, variables, includes, depth,
+ check, generator_input_info):
parallel_state = ParallelState()
parallel_state.condition = threading.Condition()
# Make copies of the build_files argument that we can modify while working.
@@ -568,7 +574,6 @@ def LoadTargetBuildFilesParallel(build_files, data, aux_data,
parallel_state.scheduled = set(build_files)
parallel_state.pending = 0
parallel_state.data = data
- parallel_state.aux_data = aux_data
try:
parallel_state.condition.acquire()
@@ -582,20 +587,16 @@ def LoadTargetBuildFilesParallel(build_files, data, aux_data,
dependency = parallel_state.dependencies.pop()
parallel_state.pending += 1
- data_in = {}
- data_in['target_build_files'] = data['target_build_files']
- aux_data_in = {}
global_flags = {
'path_sections': globals()['path_sections'],
'non_configuration_keys': globals()['non_configuration_keys'],
'multiple_toolsets': globals()['multiple_toolsets']}
if not parallel_state.pool:
- parallel_state.pool = multiprocessing.Pool(8)
+ parallel_state.pool = multiprocessing.Pool(multiprocessing.cpu_count())
parallel_state.pool.apply_async(
CallLoadTargetBuildFile,
args = (global_flags, dependency,
- data_in, aux_data_in,
variables, includes, depth, check, generator_input_info),
callback = parallel_state.LoadTargetBuildFileCallback)
except KeyboardInterrupt, e:
@@ -636,39 +637,50 @@ def FindEnclosingBracketGroup(input_str):
return (-1, -1)
-canonical_int_re = re.compile('(0|-?[1-9][0-9]*)$')
-
-
def IsStrCanonicalInt(string):
"""Returns True if |string| is in its canonical integer form.
The canonical form is such that str(int(string)) == string.
"""
- return isinstance(string, str) and canonical_int_re.match(string)
+ if type(string) is str:
+ # This function is called a lot so for maximum performance, avoid
+ # involving regexps which would otherwise make the code much
+ # shorter. Regexps would need twice the time of this function.
+ if string:
+ if string == "0":
+ return True
+ if string[0] == "-":
+ string = string[1:]
+ if not string:
+ return False
+ if '1' <= string[0] <= '9':
+ return string.isdigit()
+
+ return False
# This matches things like "<(asdf)", "(?P<(?:(?:!?@?)|\|)?)'
- '(?P[-a-zA-Z0-9_.]+)?'
- '\((?P\s*\[?)'
- '(?P.*?)(\]?)\))')
+ r'(?P(?P<(?:(?:!?@?)|\|)?)'
+ r'(?P[-a-zA-Z0-9_.]+)?'
+ r'\((?P\s*\[?)'
+ r'(?P.*?)(\]?)\))')
# This matches the same as early_variable_re, but with '>' instead of '<'.
late_variable_re = re.compile(
- '(?P(?P>(?:(?:!?@?)|\|)?)'
- '(?P[-a-zA-Z0-9_.]+)?'
- '\((?P\s*\[?)'
- '(?P.*?)(\]?)\))')
+ r'(?P(?P>(?:(?:!?@?)|\|)?)'
+ r'(?P[-a-zA-Z0-9_.]+)?'
+ r'\((?P\s*\[?)'
+ r'(?P.*?)(\]?)\))')
# This matches the same as early_variable_re, but with '^' instead of '<'.
latelate_variable_re = re.compile(
- '(?P(?P[\^](?:(?:!?@?)|\|)?)'
- '(?P[-a-zA-Z0-9_.]+)?'
- '\((?P\s*\[?)'
- '(?P.*?)(\]?)\))')
+ r'(?P(?P[\^](?:(?:!?@?)|\|)?)'
+ r'(?P[-a-zA-Z0-9_.]+)?'
+ r'\((?P\s*\[?)'
+ r'(?P.*?)(\]?)\))')
# Global cache of results from running commands so they don't have to be run
# more then once.
@@ -677,7 +689,7 @@ def IsStrCanonicalInt(string):
def FixupPlatformCommand(cmd):
if sys.platform == 'win32':
- if type(cmd) == list:
+ if type(cmd) is list:
cmd = [re.sub('^cat ', 'type ', cmd[0])] + cmd[1:]
else:
cmd = re.sub('^cat ', 'type ', cmd)
@@ -767,7 +779,7 @@ def ExpandVariables(input, phase, variables, build_file):
# contexts. However, since filtration has no chance to run on <|(),
# this seems like the only obvious way to give them access to filters.
if file_list:
- processed_variables = copy.deepcopy(variables)
+ processed_variables = gyp.simple_copy.deepcopy(variables)
ProcessListFiltersInDict(contents, processed_variables)
# Recurse to expand variables in the contents
contents = ExpandVariables(contents, phase,
@@ -804,7 +816,7 @@ def ExpandVariables(input, phase, variables, build_file):
# This works around actions/rules which have more inputs than will
# fit on the command line.
if file_list:
- if type(contents) == list:
+ if type(contents) is list:
contents_list = contents
else:
contents_list = contents.split(' ')
@@ -837,17 +849,15 @@ def ExpandVariables(input, phase, variables, build_file):
use_shell = False
# Check for a cached value to avoid executing commands, or generating
- # file lists more than once.
- # TODO(http://code.google.com/p/gyp/issues/detail?id=112): It is
- # possible that the command being invoked depends on the current
- # directory. For that case the syntax needs to be extended so that the
- # directory is also used in cache_key (it becomes a tuple).
+ # file lists more than once. The cache key contains the command to be
+ # run as well as the directory to run it from, to account for commands
+ # that depend on their current directory.
# TODO(http://code.google.com/p/gyp/issues/detail?id=111): In theory,
# someone could author a set of GYP files where each time the command
# is invoked it produces different output by design. When the need
# arises, the syntax should be extended to support no caching off a
# command's output so it is run every time.
- cache_key = str(contents)
+ cache_key = (str(contents), build_file_dir)
cached_value = cached_command_results.get(cache_key, None)
if cached_value is None:
gyp.DebugOutput(gyp.DEBUG_VARIABLES,
@@ -925,10 +935,9 @@ def ExpandVariables(input, phase, variables, build_file):
else:
replacement = variables[contents]
- if isinstance(replacement, list):
+ if type(replacement) is list:
for item in replacement:
- if (not contents[-1] == '/' and
- not isinstance(item, str) and not isinstance(item, int)):
+ if not contents[-1] == '/' and type(item) not in (str, int):
raise GypError('Variable ' + contents +
' must expand to a string or list of strings; ' +
'list contains a ' +
@@ -938,8 +947,7 @@ def ExpandVariables(input, phase, variables, build_file):
# with conditions sections.
ProcessVariablesAndConditionsInList(replacement, phase, variables,
build_file)
- elif not isinstance(replacement, str) and \
- not isinstance(replacement, int):
+ elif type(replacement) not in (str, int):
raise GypError('Variable ' + contents +
' must expand to a string or list of strings; ' +
'found a ' + replacement.__class__.__name__)
@@ -948,7 +956,7 @@ def ExpandVariables(input, phase, variables, build_file):
# Expanding in list context. It's guaranteed that there's only one
# replacement to do in |input_str| and that it's this replacement. See
# above.
- if isinstance(replacement, list):
+ if type(replacement) is list:
# If it's already a list, make a copy.
output = replacement[:]
else:
@@ -957,7 +965,7 @@ def ExpandVariables(input, phase, variables, build_file):
else:
# Expanding in string context.
encoded_replacement = ''
- if isinstance(replacement, list):
+ if type(replacement) is list:
# When expanding a list into string context, turn the list items
# into a string in a way that will work with a subprocess call.
#
@@ -975,26 +983,32 @@ def ExpandVariables(input, phase, variables, build_file):
# Prepare for the next match iteration.
input_str = output
- # Look for more matches now that we've replaced some, to deal with
- # expanding local variables (variables defined in the same
- # variables block as this one).
- gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
- if isinstance(output, list):
- if output and isinstance(output[0], list):
- # Leave output alone if it's a list of lists.
- # We don't want such lists to be stringified.
- pass
- else:
- new_output = []
- for item in output:
- new_output.append(
- ExpandVariables(item, phase, variables, build_file))
- output = new_output
+ if output == input:
+ gyp.DebugOutput(gyp.DEBUG_VARIABLES,
+ "Found only identity matches on %r, avoiding infinite "
+ "recursion.",
+ output)
else:
- output = ExpandVariables(output, phase, variables, build_file)
+ # Look for more matches now that we've replaced some, to deal with
+ # expanding local variables (variables defined in the same
+ # variables block as this one).
+ gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
+ if type(output) is list:
+ if output and type(output[0]) is list:
+ # Leave output alone if it's a list of lists.
+ # We don't want such lists to be stringified.
+ pass
+ else:
+ new_output = []
+ for item in output:
+ new_output.append(
+ ExpandVariables(item, phase, variables, build_file))
+ output = new_output
+ else:
+ output = ExpandVariables(output, phase, variables, build_file)
# Convert all strings that are canonically-represented integers into integers.
- if isinstance(output, list):
+ if type(output) is list:
for index in xrange(0, len(output)):
if IsStrCanonicalInt(output[index]):
output[index] = int(output[index])
@@ -1003,6 +1017,80 @@ def ExpandVariables(input, phase, variables, build_file):
return output
+# The same condition is often evaluated over and over again so it
+# makes sense to cache as much as possible between evaluations.
+cached_conditions_asts = {}
+
+def EvalCondition(condition, conditions_key, phase, variables, build_file):
+ """Returns the dict that should be used or None if the result was
+ that nothing should be used."""
+ if type(condition) is not list:
+ raise GypError(conditions_key + ' must be a list')
+ if len(condition) < 2:
+ # It's possible that condition[0] won't work in which case this
+ # attempt will raise its own IndexError. That's probably fine.
+ raise GypError(conditions_key + ' ' + condition[0] +
+ ' must be at least length 2, not ' + str(len(condition)))
+
+ i = 0
+ result = None
+ while i < len(condition):
+ cond_expr = condition[i]
+ true_dict = condition[i + 1]
+ if type(true_dict) is not dict:
+ raise GypError('{} {} must be followed by a dictionary, not {}'.format(
+ conditions_key, cond_expr, type(true_dict)))
+ if len(condition) > i + 2 and type(condition[i + 2]) is dict:
+ false_dict = condition[i + 2]
+ i = i + 3
+ if i != len(condition):
+ raise GypError('{} {} has {} unexpected trailing items'.format(
+ conditions_key, cond_expr, len(condition) - i))
+ else:
+ false_dict = None
+ i = i + 2
+ if result == None:
+ result = EvalSingleCondition(
+ cond_expr, true_dict, false_dict, phase, variables, build_file)
+
+ return result
+
+
+def EvalSingleCondition(
+ cond_expr, true_dict, false_dict, phase, variables, build_file):
+ """Returns true_dict if cond_expr evaluates to true, and false_dict
+ otherwise."""
+ # Do expansions on the condition itself. Since the conditon can naturally
+ # contain variable references without needing to resort to GYP expansion
+ # syntax, this is of dubious value for variables, but someone might want to
+ # use a command expansion directly inside a condition.
+ cond_expr_expanded = ExpandVariables(cond_expr, phase, variables,
+ build_file)
+ if type(cond_expr_expanded) not in (str, int):
+ raise ValueError(
+ 'Variable expansion in this context permits str and int ' + \
+ 'only, found ' + cond_expr_expanded.__class__.__name__)
+
+ try:
+ if cond_expr_expanded in cached_conditions_asts:
+ ast_code = cached_conditions_asts[cond_expr_expanded]
+ else:
+ ast_code = compile(cond_expr_expanded, '', 'eval')
+ cached_conditions_asts[cond_expr_expanded] = ast_code
+ if eval(ast_code, {'__builtins__': None}, variables):
+ return true_dict
+ return false_dict
+ except SyntaxError, e:
+ syntax_error = SyntaxError('%s while evaluating condition \'%s\' in %s '
+ 'at character %d.' %
+ (str(e.args[0]), e.text, build_file, e.offset),
+ e.filename, e.lineno, e.offset, e.text)
+ raise syntax_error
+ except NameError, e:
+ gyp.common.ExceptionAppend(e, 'while evaluating condition \'%s\' in %s' %
+ (cond_expr_expanded, build_file))
+ raise GypError(e)
+
def ProcessConditionsInDict(the_dict, phase, variables, build_file):
# Process a 'conditions' or 'target_conditions' section in the_dict,
@@ -1038,48 +1126,8 @@ def ProcessConditionsInDict(the_dict, phase, variables, build_file):
del the_dict[conditions_key]
for condition in conditions_list:
- if not isinstance(condition, list):
- raise GypError(conditions_key + ' must be a list')
- if len(condition) != 2 and len(condition) != 3:
- # It's possible that condition[0] won't work in which case this
- # attempt will raise its own IndexError. That's probably fine.
- raise GypError(conditions_key + ' ' + condition[0] +
- ' must be length 2 or 3, not ' + str(len(condition)))
-
- [cond_expr, true_dict] = condition[0:2]
- false_dict = None
- if len(condition) == 3:
- false_dict = condition[2]
-
- # Do expansions on the condition itself. Since the conditon can naturally
- # contain variable references without needing to resort to GYP expansion
- # syntax, this is of dubious value for variables, but someone might want to
- # use a command expansion directly inside a condition.
- cond_expr_expanded = ExpandVariables(cond_expr, phase, variables,
- build_file)
- if not isinstance(cond_expr_expanded, str) and \
- not isinstance(cond_expr_expanded, int):
- raise ValueError, \
- 'Variable expansion in this context permits str and int ' + \
- 'only, found ' + expanded.__class__.__name__
-
- try:
- ast_code = compile(cond_expr_expanded, '', 'eval')
-
- if eval(ast_code, {'__builtins__': None}, variables):
- merge_dict = true_dict
- else:
- merge_dict = false_dict
- except SyntaxError, e:
- syntax_error = SyntaxError('%s while evaluating condition \'%s\' in %s '
- 'at character %d.' %
- (str(e.args[0]), e.text, build_file, e.offset),
- e.filename, e.lineno, e.offset, e.text)
- raise syntax_error
- except NameError, e:
- gyp.common.ExceptionAppend(e, 'while evaluating condition \'%s\' in %s' %
- (cond_expr_expanded, build_file))
- raise GypError(e)
+ merge_dict = EvalCondition(condition, conditions_key, phase, variables,
+ build_file)
if merge_dict != None:
# Expand variables and nested conditinals in the merge_dict before
@@ -1094,8 +1142,7 @@ def LoadAutomaticVariablesFromDict(variables, the_dict):
# Any keys with plain string values in the_dict become automatic variables.
# The variable name is the key name with a "_" character prepended.
for key, value in the_dict.iteritems():
- if isinstance(value, str) or isinstance(value, int) or \
- isinstance(value, list):
+ if type(value) in (str, int, list):
variables['_' + key] = value
@@ -1108,8 +1155,7 @@ def LoadVariablesFromVariablesDict(variables, the_dict, the_dict_key):
# (it could be a list or it could be parentless because it is a root dict),
# the_dict_key will be None.
for key, value in the_dict.get('variables', {}).iteritems():
- if not isinstance(value, str) and not isinstance(value, int) and \
- not isinstance(value, list):
+ if type(value) not in (str, int, list):
continue
if key.endswith('%'):
@@ -1162,12 +1208,12 @@ def ProcessVariablesAndConditionsInDict(the_dict, phase, variables_in,
for key, value in the_dict.iteritems():
# Skip "variables", which was already processed if present.
- if key != 'variables' and isinstance(value, str):
+ if key != 'variables' and type(value) is str:
expanded = ExpandVariables(value, phase, variables, build_file)
- if not isinstance(expanded, str) and not isinstance(expanded, int):
- raise ValueError, \
+ if type(expanded) not in (str, int):
+ raise ValueError(
'Variable expansion in this context permits str and int ' + \
- 'only, found ' + expanded.__class__.__name__ + ' for ' + key
+ 'only, found ' + expanded.__class__.__name__ + ' for ' + key)
the_dict[key] = expanded
# Variable expansion may have resulted in changes to automatics. Reload.
@@ -1221,23 +1267,23 @@ def ProcessVariablesAndConditionsInDict(the_dict, phase, variables_in,
for key, value in the_dict.iteritems():
# Skip "variables" and string values, which were already processed if
# present.
- if key == 'variables' or isinstance(value, str):
+ if key == 'variables' or type(value) is str:
continue
- if isinstance(value, dict):
+ if type(value) is dict:
# Pass a copy of the variables dict so that subdicts can't influence
# parents.
ProcessVariablesAndConditionsInDict(value, phase, variables,
build_file, key)
- elif isinstance(value, list):
+ elif type(value) is list:
# The list itself can't influence the variables dict, and
# ProcessVariablesAndConditionsInList will make copies of the variables
# dict if it needs to pass it to something that can influence it. No
# copy is necessary here.
ProcessVariablesAndConditionsInList(value, phase, variables,
build_file)
- elif not isinstance(value, int):
- raise TypeError, 'Unknown type ' + value.__class__.__name__ + \
- ' for ' + key
+ elif type(value) is not int:
+ raise TypeError('Unknown type ' + value.__class__.__name__ + \
+ ' for ' + key)
def ProcessVariablesAndConditionsInList(the_list, phase, variables,
@@ -1246,17 +1292,17 @@ def ProcessVariablesAndConditionsInList(the_list, phase, variables,
index = 0
while index < len(the_list):
item = the_list[index]
- if isinstance(item, dict):
+ if type(item) is dict:
# Make a copy of the variables dict so that it won't influence anything
# outside of its own scope.
ProcessVariablesAndConditionsInDict(item, phase, variables, build_file)
- elif isinstance(item, list):
+ elif type(item) is list:
ProcessVariablesAndConditionsInList(item, phase, variables, build_file)
- elif isinstance(item, str):
+ elif type(item) is str:
expanded = ExpandVariables(item, phase, variables, build_file)
- if isinstance(expanded, str) or isinstance(expanded, int):
+ if type(expanded) in (str, int):
the_list[index] = expanded
- elif isinstance(expanded, list):
+ elif type(expanded) is list:
the_list[index:index+1] = expanded
index += len(expanded)
@@ -1264,13 +1310,13 @@ def ProcessVariablesAndConditionsInList(the_list, phase, variables,
# without falling into the index increment below.
continue
else:
- raise ValueError, \
+ raise ValueError(
'Variable expansion in this context permits strings and ' + \
'lists only, found ' + expanded.__class__.__name__ + ' at ' + \
- index
- elif not isinstance(item, int):
- raise TypeError, 'Unknown type ' + item.__class__.__name__ + \
- ' at index ' + index
+ index)
+ elif type(item) is not int:
+ raise TypeError('Unknown type ' + item.__class__.__name__ + \
+ ' at index ' + index)
index = index + 1
@@ -1443,6 +1489,20 @@ def RemoveSelfDependencies(targets):
target_dict[dependency_key] = Filter(dependencies, target_name)
+def RemoveLinkDependenciesFromNoneTargets(targets):
+ """Remove dependencies having the 'link_dependency' attribute from the 'none'
+ targets."""
+ for target_name, target_dict in targets.iteritems():
+ for dependency_key in dependency_sections:
+ dependencies = target_dict.get(dependency_key, [])
+ if dependencies:
+ for t in dependencies:
+ if target_dict.get('type', None) == 'none':
+ if targets[t].get('variables', {}).get('link_dependency', 0):
+ target_dict[dependency_key] = \
+ Filter(target_dict[dependency_key], t)
+
+
class DependencyGraphNode(object):
"""
@@ -1468,7 +1528,7 @@ def FlattenToList(self):
# are the "ref" attributes of DependencyGraphNodes. Every target will
# appear in flat_list after all of its dependencies, and before all of its
# dependents.
- flat_list = []
+ flat_list = OrderedSet()
# in_degree_zeros is the list of DependencyGraphNodes that have no
# dependencies not in flat_list. Initially, it is a copy of the children
@@ -1482,12 +1542,15 @@ def FlattenToList(self):
# as work progresses, so that the next node to process from the list can
# always be accessed at a consistent position.
node = in_degree_zeros.pop()
- flat_list.append(node.ref)
+ flat_list.add(node.ref)
# Look at dependents of the node just added to flat_list. Some of them
# may now belong in in_degree_zeros.
for node_dependent in node.dependents:
is_in_degree_zero = True
+ # TODO: We want to check through the
+ # node_dependent.dependencies list but if it's long and we
+ # always start at the beginning, then we get O(n^2) behaviour.
for node_dependent_dependency in node_dependent.dependencies:
if not node_dependent_dependency.ref in flat_list:
# The dependent one or more dependencies not in flat_list. There
@@ -1503,28 +1566,27 @@ def FlattenToList(self):
# iteration of the outer loop.
in_degree_zeros.add(node_dependent)
- return flat_list
+ return list(flat_list)
- def FindCycles(self, path=None):
+ def FindCycles(self):
"""
Returns a list of cycles in the graph, where each cycle is its own list.
"""
- if path is None:
- path = [self]
-
results = []
- for node in self.dependents:
- if node in path:
- cycle = [node]
- for part in path:
- cycle.append(part)
- if part == node:
- break
- results.append(tuple(cycle))
- else:
- results.extend(node.FindCycles([node] + path))
+ visited = set()
+
+ def Visit(node, path):
+ for child in node.dependents:
+ if child in path:
+ results.append([child] + path[:path.index(child) + 1])
+ elif not child in visited:
+ visited.add(child)
+ Visit(child, [child] + path)
+
+ visited.add(self)
+ Visit(self, [self])
- return list(set(results))
+ return results
def DirectDependencies(self, dependencies=None):
"""Returns a list of just direct dependencies."""
@@ -1589,21 +1651,26 @@ def DirectAndImportedDependencies(self, targets, dependencies=None):
return self._AddImportedDependencies(targets, dependencies)
def DeepDependencies(self, dependencies=None):
- """Returns a list of all of a target's dependencies, recursively."""
- if dependencies == None:
- dependencies = []
+ """Returns an OrderedSet of all of a target's dependencies, recursively."""
+ if dependencies is None:
+ # Using a list to get ordered output and a set to do fast "is it
+ # already added" checks.
+ dependencies = OrderedSet()
for dependency in self.dependencies:
# Check for None, corresponding to the root node.
- if dependency.ref != None and dependency.ref not in dependencies:
- dependencies.append(dependency.ref)
+ if dependency.ref is None:
+ continue
+ if dependency.ref not in dependencies:
+ dependencies.add(dependency.ref)
dependency.DeepDependencies(dependencies)
return dependencies
def _LinkDependenciesInternal(self, targets, include_shared_libraries,
dependencies=None, initial=True):
- """Returns a list of dependency targets that are linked into this target.
+ """Returns an OrderedSet of dependency targets that are linked
+ into this target.
This function has a split personality, depending on the setting of
|initial|. Outside callers should always leave |initial| at its default
@@ -1616,11 +1683,13 @@ def _LinkDependenciesInternal(self, targets, include_shared_libraries,
If |include_shared_libraries| is False, the resulting dependencies will not
include shared_library targets that are linked into this target.
"""
- if dependencies == None:
- dependencies = []
+ if dependencies is None:
+ # Using a list to get ordered output and a set to do fast "is it
+ # already added" checks.
+ dependencies = OrderedSet()
# Check for None, corresponding to the root node.
- if self.ref == None:
+ if self.ref is None:
return dependencies
# It's kind of sucky that |targets| has to be passed into this function,
@@ -1648,8 +1717,7 @@ def _LinkDependenciesInternal(self, targets, include_shared_libraries,
# Don't traverse 'none' targets if explicitly excluded.
if (target_type == 'none' and
not targets[self.ref].get('dependencies_traverse', True)):
- if self.ref not in dependencies:
- dependencies.append(self.ref)
+ dependencies.add(self.ref)
return dependencies
# Executables and loadable modules are already fully and finally linked.
@@ -1671,7 +1739,7 @@ def _LinkDependenciesInternal(self, targets, include_shared_libraries,
# The target is linkable, add it to the list of link dependencies.
if self.ref not in dependencies:
- dependencies.append(self.ref)
+ dependencies.add(self.ref)
if initial or not is_linkable:
# If this is a subsequent target and it's linkable, don't look any
# further for linkable dependencies, as they'll already be linked into
@@ -1735,12 +1803,22 @@ def BuildDependencyList(targets):
flat_list = root_node.FlattenToList()
# If there's anything left unvisited, there must be a circular dependency
- # (cycle). If you need to figure out what's wrong, look for elements of
- # targets that are not in flat_list.
+ # (cycle).
if len(flat_list) != len(targets):
+ if not root_node.dependents:
+ # If all targets have dependencies, add the first target as a dependent
+ # of root_node so that the cycle can be discovered from root_node.
+ target = targets.keys()[0]
+ target_node = dependency_nodes[target]
+ target_node.dependencies.append(root_node)
+ root_node.dependents.append(target_node)
+
+ cycles = []
+ for cycle in root_node.FindCycles():
+ paths = [node.ref for node in cycle]
+ cycles.append('Cycle: %s' % ' -> '.join(paths))
raise DependencyGraphNode.CircularException(
- 'Some targets not reachable, cycle in dependency graph detected: ' +
- ' '.join(set(flat_list) ^ set(targets)))
+ 'Cycles in dependency graph detected:\n' + '\n'.join(cycles))
return [dependency_nodes, flat_list]
@@ -1790,20 +1868,18 @@ def VerifyNoGYPFileCircularDependencies(targets):
# If there's anything left unvisited, there must be a circular dependency
# (cycle).
if len(flat_list) != len(dependency_nodes):
- bad_files = []
- for file in dependency_nodes.iterkeys():
- if not file in flat_list:
- bad_files.append(file)
- common_path_prefix = os.path.commonprefix(dependency_nodes)
+ if not root_node.dependents:
+ # If all files have dependencies, add the first file as a dependent
+ # of root_node so that the cycle can be discovered from root_node.
+ file_node = dependency_nodes.values()[0]
+ file_node.dependencies.append(root_node)
+ root_node.dependents.append(file_node)
cycles = []
for cycle in root_node.FindCycles():
- simplified_paths = []
- for node in cycle:
- assert(node.ref.startswith(common_path_prefix))
- simplified_paths.append(node.ref[len(common_path_prefix):])
- cycles.append('Cycle: %s' % ' -> '.join(simplified_paths))
- raise DependencyGraphNode.CircularException, \
- 'Cycles in .gyp file dependency graph detected:\n' + '\n'.join(cycles)
+ paths = [node.ref for node in cycle]
+ cycles.append('Cycle: %s' % ' -> '.join(paths))
+ raise DependencyGraphNode.CircularException(
+ 'Cycles in .gyp file dependency graph detected:\n' + '\n'.join(cycles))
def DoDependentSettings(key, flat_list, targets, dependency_nodes):
@@ -1966,25 +2042,25 @@ def is_in_set_or_list(x, s, l):
hashable_to_set = set(x for x in to if is_hashable(x))
for item in fro:
singleton = False
- if isinstance(item, str) or isinstance(item, int):
+ if type(item) in (str, int):
# The cheap and easy case.
if is_paths:
to_item = MakePathRelative(to_file, fro_file, item)
else:
to_item = item
- if not isinstance(item, str) or not item.startswith('-'):
+ if not (type(item) is str and item.startswith('-')):
# Any string that doesn't begin with a "-" is a singleton - it can
# only appear once in a list, to be enforced by the list merge append
# or prepend.
singleton = True
- elif isinstance(item, dict):
+ elif type(item) is dict:
# Make a copy of the dictionary, continuing to look for paths to fix.
# The other intelligent aspects of merge processing won't apply because
# item is being merged into an empty dict.
to_item = {}
MergeDicts(to_item, item, to_file, fro_file)
- elif isinstance(item, list):
+ elif type(item) is list:
# Recurse, making a copy of the list. If the list contains any
# descendant dicts, path fixing will occur. Note that here, custom
# values for is_paths and append are dropped; those are only to be
@@ -1993,9 +2069,9 @@ def is_in_set_or_list(x, s, l):
to_item = []
MergeLists(to_item, item, to_file, fro_file)
else:
- raise TypeError, \
+ raise TypeError(
'Attempt to merge list item of unsupported type ' + \
- item.__class__.__name__
+ item.__class__.__name__)
if append:
# If appending a singleton that's already in the list, don't append.
@@ -2030,30 +2106,30 @@ def MergeDicts(to, fro, to_file, fro_file):
# modified.
if k in to:
bad_merge = False
- if isinstance(v, str) or isinstance(v, int):
- if not (isinstance(to[k], str) or isinstance(to[k], int)):
+ if type(v) in (str, int):
+ if type(to[k]) not in (str, int):
bad_merge = True
- elif v.__class__ != to[k].__class__:
+ elif type(v) is not type(to[k]):
bad_merge = True
if bad_merge:
- raise TypeError, \
+ raise TypeError(
'Attempt to merge dict value of type ' + v.__class__.__name__ + \
' into incompatible type ' + to[k].__class__.__name__ + \
- ' for key ' + k
- if isinstance(v, str) or isinstance(v, int):
+ ' for key ' + k)
+ if type(v) in (str, int):
# Overwrite the existing value, if any. Cheap and easy.
is_path = IsPathSection(k)
if is_path:
to[k] = MakePathRelative(to_file, fro_file, v)
else:
to[k] = v
- elif isinstance(v, dict):
+ elif type(v) is dict:
# Recurse, guaranteeing copies will be made of objects that require it.
if not k in to:
to[k] = {}
MergeDicts(to[k], v, to_file, fro_file)
- elif isinstance(v, list):
+ elif type(v) is list:
# Lists in dicts can be merged with different policies, depending on
# how the key in the "from" dict (k, the from-key) is written.
#
@@ -2096,13 +2172,13 @@ def MergeDicts(to, fro, to_file, fro_file):
# If the key ends in "?", the list will only be merged if it doesn't
# already exist.
continue
- if not isinstance(to[list_base], list):
+ elif type(to[list_base]) is not list:
# This may not have been checked above if merging in a list with an
# extension character.
- raise TypeError, \
+ raise TypeError(
'Attempt to merge dict value of type ' + v.__class__.__name__ + \
' into incompatible type ' + to[list_base].__class__.__name__ + \
- ' for key ' + list_base + '(' + k + ')'
+ ' for key ' + list_base + '(' + k + ')')
else:
to[list_base] = []
@@ -2114,9 +2190,9 @@ def MergeDicts(to, fro, to_file, fro_file):
is_paths = IsPathSection(list_base)
MergeLists(to[list_base], v, to_file, fro_file, is_paths, append)
else:
- raise TypeError, \
+ raise TypeError(
'Attempt to merge dict value of unsupported type ' + \
- v.__class__.__name__ + ' for key ' + k
+ v.__class__.__name__ + ' for key ' + k)
def MergeConfigWithInheritance(new_configuration_dict, build_file,
@@ -2157,43 +2233,39 @@ def SetUpConfigurations(target, target_dict):
if not 'configurations' in target_dict:
target_dict['configurations'] = {'Default': {}}
if not 'default_configuration' in target_dict:
- concrete = [i for i in target_dict['configurations'].iterkeys()
- if not target_dict['configurations'][i].get('abstract')]
+ concrete = [i for (i, config) in target_dict['configurations'].iteritems()
+ if not config.get('abstract')]
target_dict['default_configuration'] = sorted(concrete)[0]
- for configuration in target_dict['configurations'].keys():
- old_configuration_dict = target_dict['configurations'][configuration]
+ merged_configurations = {}
+ configs = target_dict['configurations']
+ for (configuration, old_configuration_dict) in configs.iteritems():
# Skip abstract configurations (saves work only).
if old_configuration_dict.get('abstract'):
continue
# Configurations inherit (most) settings from the enclosing target scope.
# Get the inheritance relationship right by making a copy of the target
# dict.
- new_configuration_dict = copy.deepcopy(target_dict)
-
- # Take out the bits that don't belong in a "configurations" section.
- # Since configuration setup is done before conditional, exclude, and rules
- # processing, be careful with handling of the suffix characters used in
- # those phases.
- delete_keys = []
- for key in new_configuration_dict:
+ new_configuration_dict = {}
+ for (key, target_val) in target_dict.iteritems():
key_ext = key[-1:]
if key_ext in key_suffixes:
key_base = key[:-1]
else:
key_base = key
- if key_base in non_configuration_keys:
- delete_keys.append(key)
-
- for key in delete_keys:
- del new_configuration_dict[key]
+ if not key_base in non_configuration_keys:
+ new_configuration_dict[key] = gyp.simple_copy.deepcopy(target_val)
# Merge in configuration (with all its parents first).
MergeConfigWithInheritance(new_configuration_dict, build_file,
target_dict, configuration, [])
- # Put the new result back into the target dict as a configuration.
- target_dict['configurations'][configuration] = new_configuration_dict
+ merged_configurations[configuration] = new_configuration_dict
+
+ # Put the new configurations back into the target dict as a configuration.
+ for configuration in merged_configurations.keys():
+ target_dict['configurations'][configuration] = (
+ merged_configurations[configuration])
# Now drop all the abstract ones.
for configuration in target_dict['configurations'].keys():
@@ -2264,9 +2336,9 @@ def ProcessListFiltersInDict(name, the_dict):
if operation != '!' and operation != '/':
continue
- if not isinstance(value, list):
- raise ValueError, name + ' key ' + key + ' must be list, not ' + \
- value.__class__.__name__
+ if type(value) is not list:
+ raise ValueError(name + ' key ' + key + ' must be list, not ' + \
+ value.__class__.__name__)
list_key = key[:-1]
if list_key not in the_dict:
@@ -2276,12 +2348,12 @@ def ProcessListFiltersInDict(name, the_dict):
del_lists.append(key)
continue
- if not isinstance(the_dict[list_key], list):
+ if type(the_dict[list_key]) is not list:
value = the_dict[list_key]
- raise ValueError, name + ' key ' + list_key + \
- ' must be list, not ' + \
- value.__class__.__name__ + ' when applying ' + \
- {'!': 'exclusion', '/': 'regex'}[operation]
+ raise ValueError(name + ' key ' + list_key + \
+ ' must be list, not ' + \
+ value.__class__.__name__ + ' when applying ' + \
+ {'!': 'exclusion', '/': 'regex'}[operation])
if not list_key in lists:
lists.append(list_key)
@@ -2330,8 +2402,8 @@ def ProcessListFiltersInDict(name, the_dict):
action_value = 1
else:
# This is an action that doesn't make any sense.
- raise ValueError, 'Unrecognized action ' + action + ' in ' + name + \
- ' key ' + regex_key
+ raise ValueError('Unrecognized action ' + action + ' in ' + name + \
+ ' key ' + regex_key)
for index in xrange(0, len(the_list)):
list_item = the_list[index]
@@ -2378,17 +2450,17 @@ def ProcessListFiltersInDict(name, the_dict):
# Now recurse into subdicts and lists that may contain dicts.
for key, value in the_dict.iteritems():
- if isinstance(value, dict):
+ if type(value) is dict:
ProcessListFiltersInDict(key, value)
- elif isinstance(value, list):
+ elif type(value) is list:
ProcessListFiltersInList(key, value)
def ProcessListFiltersInList(name, the_list):
for item in the_list:
- if isinstance(item, dict):
+ if type(item) is dict:
ProcessListFiltersInDict(name, item)
- elif isinstance(item, list):
+ elif type(item) is list:
ProcessListFiltersInList(name, item)
@@ -2416,33 +2488,6 @@ def ValidateTargetType(target, target_dict):
target_type))
-def ValidateSourcesInTarget(target, target_dict, build_file):
- # TODO: Check if MSVC allows this for loadable_module targets.
- if target_dict.get('type', None) not in ('static_library', 'shared_library'):
- return
- sources = target_dict.get('sources', [])
- basenames = {}
- for source in sources:
- name, ext = os.path.splitext(source)
- is_compiled_file = ext in [
- '.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.s', '.S']
- if not is_compiled_file:
- continue
- basename = os.path.basename(name) # Don't include extension.
- basenames.setdefault(basename, []).append(source)
-
- error = ''
- for basename, files in basenames.iteritems():
- if len(files) > 1:
- error += ' %s: %s\n' % (basename, ' '.join(files))
-
- if error:
- print('static library %s has several files with the same basename:\n' %
- target + error + 'Some build systems, e.g. MSVC08, '
- 'cannot handle that.')
- raise GypError('Duplicate basenames in sources section, see list above')
-
-
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.
@@ -2506,7 +2551,7 @@ def ValidateRunAsInTarget(target, target_dict, build_file):
run_as = target_dict.get('run_as')
if not run_as:
return
- if not isinstance(run_as, dict):
+ if type(run_as) is not dict:
raise GypError("The 'run_as' in target %s from file %s should be a "
"dictionary." %
(target_name, build_file))
@@ -2515,17 +2560,17 @@ def ValidateRunAsInTarget(target, target_dict, build_file):
raise GypError("The 'run_as' in target %s from file %s must have an "
"'action' section." %
(target_name, build_file))
- if not isinstance(action, list):
+ if type(action) is not list:
raise GypError("The 'action' for 'run_as' in target %s from file %s "
"must be a list." %
(target_name, build_file))
working_directory = run_as.get('working_directory')
- if working_directory and not isinstance(working_directory, str):
+ if working_directory and type(working_directory) is not str:
raise GypError("The 'working_directory' for 'run_as' in target %s "
"in file %s should be a string." %
(target_name, build_file))
environment = run_as.get('environment')
- if environment and not isinstance(environment, dict):
+ if environment and type(environment) is not dict:
raise GypError("The 'environment' for 'run_as' in target %s "
"in file %s should be a dictionary." %
(target_name, build_file))
@@ -2555,17 +2600,17 @@ def TurnIntIntoStrInDict(the_dict):
# Use items instead of iteritems because there's no need to try to look at
# reinserted keys and their associated values.
for k, v in the_dict.items():
- if isinstance(v, int):
+ if type(v) is int:
v = str(v)
the_dict[k] = v
- elif isinstance(v, dict):
+ elif type(v) is dict:
TurnIntIntoStrInDict(v)
- elif isinstance(v, list):
+ elif type(v) is list:
TurnIntIntoStrInList(v)
- if isinstance(k, int):
- the_dict[str(k)] = v
+ if type(k) is int:
del the_dict[k]
+ the_dict[str(k)] = v
def TurnIntIntoStrInList(the_list):
@@ -2573,11 +2618,11 @@ def TurnIntIntoStrInList(the_list):
"""
for index in xrange(0, len(the_list)):
item = the_list[index]
- if isinstance(item, int):
+ if type(item) is int:
the_list[index] = str(item)
- elif isinstance(item, dict):
+ elif type(item) is dict:
TurnIntIntoStrInDict(item)
- elif isinstance(item, list):
+ elif type(item) is list:
TurnIntIntoStrInList(item)
@@ -2647,8 +2692,8 @@ def SetGeneratorGlobals(generator_input_info):
# Set up path_sections and non_configuration_keys with the default data plus
# the generator-specific data.
global path_sections
- path_sections = base_path_sections[:]
- path_sections.extend(generator_input_info['path_sections'])
+ path_sections = set(base_path_sections)
+ path_sections.update(generator_input_info['path_sections'])
global non_configuration_keys
non_configuration_keys = base_non_configuration_keys[:]
@@ -2677,15 +2722,14 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
# well as meta-data (e.g. 'included_files' key). 'target_build_files' keeps
# track of the keys corresponding to "target" files.
data = {'target_build_files': set()}
- aux_data = {}
# Normalize paths everywhere. This is important because paths will be
# used as keys to the data dict and for references between input files.
build_files = set(map(os.path.normpath, build_files))
if parallel:
- LoadTargetBuildFilesParallel(build_files, data, aux_data,
- variables, includes, depth, check,
- generator_input_info)
+ LoadTargetBuildFilesParallel(build_files, data, variables, includes, depth,
+ check, generator_input_info)
else:
+ aux_data = {}
for build_file in build_files:
try:
LoadTargetBuildFile(build_file, data, aux_data,
@@ -2707,6 +2751,10 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
# Expand dependencies specified as build_file:*.
ExpandWildcardDependencies(targets, data)
+ # Remove all dependencies marked as 'link_dependency' from the targets of
+ # type 'none'.
+ RemoveLinkDependenciesFromNoneTargets(targets)
+
# Apply exclude (!) and regex (/) list filters only for dependency_sections.
for target_name, target_dict in targets.iteritems():
tmp_dict = {}
@@ -2792,10 +2840,6 @@ def Load(build_files, variables, includes, depth, generator_input_info, check,
target_dict = targets[target]
build_file = gyp.common.BuildFile(target)
ValidateTargetType(target, target_dict)
- # TODO(thakis): Get vpx_scale/arm/scalesystemdependent.c to be renamed to
- # scalesystemdependent_arm_additions.c or similar.
- if 'arm' not in variables.get('target_arch', ''):
- ValidateSourcesInTarget(target, target_dict, build_file)
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/input_test.py b/tools/gyp/pylib/gyp/input_test.py
index cdbf6b2fad65fa..4234fbb8302eb9 100755
--- a/tools/gyp/pylib/gyp/input_test.py
+++ b/tools/gyp/pylib/gyp/input_test.py
@@ -44,16 +44,16 @@ def test_no_cycle_dag(self):
def test_cycle_self_reference(self):
self._create_dependency(self.nodes['a'], self.nodes['a'])
- self.assertEquals([(self.nodes['a'], self.nodes['a'])],
+ self.assertEquals([[self.nodes['a'], self.nodes['a']]],
self.nodes['a'].FindCycles())
def test_cycle_two_nodes(self):
self._create_dependency(self.nodes['a'], self.nodes['b'])
self._create_dependency(self.nodes['b'], self.nodes['a'])
- self.assertEquals([(self.nodes['a'], self.nodes['b'], self.nodes['a'])],
+ self.assertEquals([[self.nodes['a'], self.nodes['b'], self.nodes['a']]],
self.nodes['a'].FindCycles())
- self.assertEquals([(self.nodes['b'], self.nodes['a'], self.nodes['b'])],
+ self.assertEquals([[self.nodes['b'], self.nodes['a'], self.nodes['b']]],
self.nodes['b'].FindCycles())
def test_two_cycles(self):
@@ -65,9 +65,9 @@ def test_two_cycles(self):
cycles = self.nodes['a'].FindCycles()
self.assertTrue(
- (self.nodes['a'], self.nodes['b'], self.nodes['a']) in cycles)
+ [self.nodes['a'], self.nodes['b'], self.nodes['a']] in cycles)
self.assertTrue(
- (self.nodes['b'], self.nodes['c'], self.nodes['b']) in cycles)
+ [self.nodes['b'], self.nodes['c'], self.nodes['b']] in cycles)
self.assertEquals(2, len(cycles))
def test_big_cycle(self):
@@ -77,12 +77,12 @@ def test_big_cycle(self):
self._create_dependency(self.nodes['d'], self.nodes['e'])
self._create_dependency(self.nodes['e'], self.nodes['a'])
- self.assertEquals([(self.nodes['a'],
+ self.assertEquals([[self.nodes['a'],
self.nodes['b'],
self.nodes['c'],
self.nodes['d'],
self.nodes['e'],
- self.nodes['a'])],
+ self.nodes['a']]],
self.nodes['a'].FindCycles())
diff --git a/tools/gyp/pylib/gyp/mac_tool.py b/tools/gyp/pylib/gyp/mac_tool.py
index ac19b6dc81fa6e..eeeaceb0c7aa23 100755
--- a/tools/gyp/pylib/gyp/mac_tool.py
+++ b/tools/gyp/pylib/gyp/mac_tool.py
@@ -45,7 +45,7 @@ def _CommandifyName(self, name_string):
"""Transforms a tool name like copy-info-plist to CopyInfoPlist"""
return name_string.title().replace('-', '')
- def ExecCopyBundleResource(self, source, dest):
+ def ExecCopyBundleResource(self, source, dest, convert_to_binary):
"""Copies a resource file to the bundle/Resources directory, performing any
necessary compilation on each resource."""
extension = os.path.splitext(source)[1].lower()
@@ -62,7 +62,7 @@ def ExecCopyBundleResource(self, source, dest):
elif extension == '.storyboard':
return self._CopyXIBFile(source, dest)
elif extension == '.strings':
- self._CopyStringsFile(source, dest)
+ self._CopyStringsFile(source, dest, convert_to_binary)
else:
shutil.copy(source, dest)
@@ -92,7 +92,11 @@ def _CopyXIBFile(self, source, dest):
sys.stdout.write(line)
return ibtoolout.returncode
- def _CopyStringsFile(self, source, dest):
+ def _ConvertToBinary(self, dest):
+ subprocess.check_call([
+ 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest])
+
+ def _CopyStringsFile(self, source, dest, convert_to_binary):
"""Copies a .strings file using iconv to reconvert the input into UTF-16."""
input_code = self._DetectInputEncoding(source) or "UTF-8"
@@ -112,6 +116,9 @@ def _CopyStringsFile(self, source, dest):
fp.write(s.decode(input_code).encode('UTF-16'))
fp.close()
+ if convert_to_binary == 'True':
+ self._ConvertToBinary(dest)
+
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."""
@@ -131,7 +138,7 @@ def _DetectInputEncoding(self, file_name):
else:
return None
- def ExecCopyInfoPlist(self, source, dest, *keys):
+ def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys):
"""Copies the |source| Info.plist to the destination directory |dest|."""
# Read the source Info.plist into memory.
fd = open(source, 'r')
@@ -146,7 +153,7 @@ def ExecCopyInfoPlist(self, source, dest, *keys):
# Go through all the environment variables and replace them as variables in
# the file.
- IDENT_RE = re.compile('[/\s]')
+ IDENT_RE = re.compile(r'[/\s]')
for key in os.environ:
if key.startswith('_'):
continue
@@ -185,6 +192,9 @@ def ExecCopyInfoPlist(self, source, dest, *keys):
# "compiled".
self._WritePkgInfo(dest)
+ if convert_to_binary == 'True':
+ self._ConvertToBinary(dest)
+
def _WritePkgInfo(self, info_plist):
"""This writes the PkgInfo file from the data stored in Info.plist."""
plist = plistlib.readPlist(info_plist)
@@ -219,11 +229,28 @@ def ExecFilterLibtool(self, *cmd_list):
"""Calls libtool and filters out '/path/to/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)
+ libtool_re5 = re.compile(
+ r'^.*libtool: warning for library: ' +
+ r'.* the table of contents is empty ' +
+ r'\(no object file members in the library define global symbols\)$')
+ env = os.environ.copy()
+ # Ref:
+ # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c
+ # The problem with this flag is that it resets the file mtime on the file to
+ # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone.
+ env['ZERO_AR_DATE'] = '1'
+ libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env)
_, err = libtoolout.communicate()
for line in err.splitlines():
- if not libtool_re.match(line):
+ if not libtool_re.match(line) and not libtool_re5.match(line):
print >>sys.stderr, line
+ # Unconditionally touch the output .a file on the command line if present
+ # and the command succeeded. A bit hacky.
+ if not libtoolout.returncode:
+ for i in range(len(cmd_list) - 1):
+ if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'):
+ os.utime(cmd_list[i+1], None)
+ break
return libtoolout.returncode
def ExecPackageFramework(self, framework, version):
@@ -262,6 +289,66 @@ def _Relink(self, dest, link):
os.remove(link)
os.symlink(dest, link)
+ def ExecCompileXcassets(self, keys, *inputs):
+ """Compiles multiple .xcassets files into a single .car file.
+
+ This invokes 'actool' to compile all the inputs .xcassets files. The
+ |keys| arguments is a json-encoded dictionary of extra arguments to
+ pass to 'actool' when the asset catalogs contains an application icon
+ or a launch image.
+
+ Note that 'actool' does not create the Assets.car file if the asset
+ catalogs does not contains imageset.
+ """
+ command_line = [
+ 'xcrun', 'actool', '--output-format', 'human-readable-text',
+ '--compress-pngs', '--notices', '--warnings', '--errors',
+ ]
+ is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ
+ if is_iphone_target:
+ platform = os.environ['CONFIGURATION'].split('-')[-1]
+ if platform not in ('iphoneos', 'iphonesimulator'):
+ platform = 'iphonesimulator'
+ command_line.extend([
+ '--platform', platform, '--target-device', 'iphone',
+ '--target-device', 'ipad', '--minimum-deployment-target',
+ os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile',
+ os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']),
+ ])
+ else:
+ command_line.extend([
+ '--platform', 'macosx', '--target-device', 'mac',
+ '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'],
+ '--compile',
+ os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']),
+ ])
+ if keys:
+ keys = json.loads(keys)
+ for key, value in keys.iteritems():
+ arg_name = '--' + key
+ if isinstance(value, bool):
+ if value:
+ command_line.append(arg_name)
+ elif isinstance(value, list):
+ for v in value:
+ command_line.append(arg_name)
+ command_line.append(str(v))
+ else:
+ command_line.append(arg_name)
+ command_line.append(str(value))
+ # Note: actool crashes if inputs path are relative, so use os.path.abspath
+ # to get absolute path name for inputs.
+ command_line.extend(map(os.path.abspath, inputs))
+ subprocess.check_call(command_line)
+
+ def ExecMergeInfoPlist(self, output, *inputs):
+ """Merge multiple .plist files into a single .plist file."""
+ merged_plist = {}
+ for path in inputs:
+ plist = self._LoadPlistMaybeBinary(path)
+ self._MergePlist(merged_plist, plist)
+ plistlib.writePlist(merged_plist, output)
+
def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning):
"""Code sign a bundle.
@@ -398,6 +485,19 @@ def _LoadProvisioningProfile(self, profile_path):
'security', 'cms', '-D', '-i', profile_path, '-o', temp.name])
return self._LoadPlistMaybeBinary(temp.name)
+ def _MergePlist(self, merged_plist, plist):
+ """Merge |plist| into |merged_plist|."""
+ for key, value in plist.iteritems():
+ if isinstance(value, dict):
+ merged_value = merged_plist.get(key, {})
+ if isinstance(merged_value, dict):
+ self._MergePlist(merged_value, value)
+ merged_plist[key] = merged_value
+ else:
+ merged_plist[key] = value
+ else:
+ merged_plist[key] = value
+
def _LoadPlistMaybeBinary(self, plist_path):
"""Loads into a memory a plist possibly encoded in binary format.
@@ -503,8 +603,7 @@ def _ExpandVariables(self, data, substitutions):
if isinstance(data, list):
return [self._ExpandVariables(v, substitutions) for v in data]
if isinstance(data, dict):
- return dict((k, self._ExpandVariables(data[k],
- substitutions)) for k in data)
+ return {k: self._ExpandVariables(data[k], substitutions) for k in data}
return data
if __name__ == '__main__':
diff --git a/tools/gyp/pylib/gyp/msvs_emulation.py b/tools/gyp/pylib/gyp/msvs_emulation.py
index 6428fced00552e..ce5c46ea5b3d95 100644
--- a/tools/gyp/pylib/gyp/msvs_emulation.py
+++ b/tools/gyp/pylib/gyp/msvs_emulation.py
@@ -12,10 +12,14 @@
import subprocess
import sys
+from gyp.common import OrderedSet
+import gyp.MSVSUtil
import gyp.MSVSVersion
+
windows_quoter_regex = re.compile(r'(\\*)"')
+
def QuoteForRspFile(arg):
"""Quote a command line argument so that it appears as one argument when
processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
@@ -131,6 +135,54 @@ def _FindDirectXInstallation():
return dxsdk_dir
+def GetGlobalVSMacroEnv(vs_version):
+ """Get a dict of variables mapping internal VS macro names to their gyp
+ equivalents. Returns all variables that are independent of the target."""
+ env = {}
+ # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
+ # Visual Studio is actually installed.
+ if vs_version.Path():
+ env['$(VSInstallDir)'] = vs_version.Path()
+ env['$(VCInstallDir)'] = os.path.join(vs_version.Path(), 'VC') + '\\'
+ # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
+ # set. This happens when the SDK is sync'd via src-internal, rather than
+ # by typical end-user installation of the SDK. If it's not set, we don't
+ # want to leave the unexpanded variable in the path, so simply strip it.
+ dxsdk_dir = _FindDirectXInstallation()
+ env['$(DXSDK_DIR)'] = dxsdk_dir if dxsdk_dir else ''
+ # Try to find an installation location for the Windows DDK by checking
+ # the WDK_DIR environment variable, may be None.
+ env['$(WDK_DIR)'] = os.environ.get('WDK_DIR', '')
+ return env
+
+def ExtractSharedMSVSSystemIncludes(configs, generator_flags):
+ """Finds msvs_system_include_dirs that are common to all targets, removes
+ them from all targets, and returns an OrderedSet containing them."""
+ all_system_includes = OrderedSet(
+ configs[0].get('msvs_system_include_dirs', []))
+ for config in configs[1:]:
+ system_includes = config.get('msvs_system_include_dirs', [])
+ all_system_includes = all_system_includes & OrderedSet(system_includes)
+ if not all_system_includes:
+ return None
+ # Expand macros in all_system_includes.
+ env = GetGlobalVSMacroEnv(GetVSVersion(generator_flags))
+ expanded_system_includes = OrderedSet([ExpandMacros(include, env)
+ for include in all_system_includes])
+ if any(['$' in include for include in expanded_system_includes]):
+ # Some path relies on target-specific variables, bail.
+ return None
+
+ # Remove system includes shared by all targets from the targets.
+ for config in configs:
+ includes = config.get('msvs_system_include_dirs', [])
+ if includes: # Don't insert a msvs_system_include_dirs key if not needed.
+ # This must check the unexpanded includes list:
+ new_includes = [i for i in includes if i not in all_system_includes]
+ config['msvs_system_include_dirs'] = new_includes
+ return expanded_system_includes
+
+
class MsvsSettings(object):
"""A class that understands the gyp 'msvs_...' values (especially the
msvs_settings field). They largely correpond to the VS2008 IDE DOM. This
@@ -139,11 +191,6 @@ class helps map those settings to command line options."""
def __init__(self, spec, generator_flags):
self.spec = spec
self.vs_version = GetVSVersion(generator_flags)
- self.dxsdk_dir = _FindDirectXInstallation()
-
- # Try to find an installation location for the Windows DDK by checking
- # the WDK_DIR environment variable, may be None.
- self.wdk_dir = os.environ.get('WDK_DIR')
supported_fields = [
('msvs_configuration_attributes', dict),
@@ -163,6 +210,30 @@ def __init__(self, spec, generator_flags):
self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.'])
+ unsupported_fields = [
+ 'msvs_prebuild',
+ 'msvs_postbuild',
+ ]
+ unsupported = []
+ for field in unsupported_fields:
+ for config in configs.values():
+ if field in config:
+ unsupported += ["%s not supported (target %s)." %
+ (field, spec['target_name'])]
+ if unsupported:
+ raise Exception('\n'.join(unsupported))
+
+ def GetExtension(self):
+ """Returns the extension for the target, with no leading dot.
+
+ Uses 'product_extension' if specified, otherwise uses MSVS defaults based on
+ the target type.
+ """
+ ext = self.spec.get('product_extension', None)
+ if ext:
+ return ext
+ return gyp.MSVSUtil.TARGET_TYPE_EXT.get(self.spec['type'], '')
+
def GetVSMacroEnv(self, base_to_build=None, config=None):
"""Get a dict of variables mapping internal VS macro names to their gyp
equivalents."""
@@ -170,29 +241,24 @@ def GetVSMacroEnv(self, base_to_build=None, config=None):
target_name = self.spec.get('product_prefix', '') + \
self.spec.get('product_name', self.spec['target_name'])
target_dir = base_to_build + '\\' if base_to_build else ''
+ target_ext = '.' + self.GetExtension()
+ target_file_name = target_name + target_ext
+
replacements = {
- '$(OutDir)\\': target_dir,
- '$(TargetDir)\\': target_dir,
- '$(IntDir)': '$!INTERMEDIATE_DIR',
- '$(InputPath)': '${source}',
'$(InputName)': '${root}',
- '$(ProjectName)': self.spec['target_name'],
- '$(TargetName)': target_name,
+ '$(InputPath)': '${source}',
+ '$(IntDir)': '$!INTERMEDIATE_DIR',
+ '$(OutDir)\\': target_dir,
'$(PlatformName)': target_platform,
'$(ProjectDir)\\': '',
+ '$(ProjectName)': self.spec['target_name'],
+ '$(TargetDir)\\': target_dir,
+ '$(TargetExt)': target_ext,
+ '$(TargetFileName)': target_file_name,
+ '$(TargetName)': target_name,
+ '$(TargetPath)': os.path.join(target_dir, target_file_name),
}
- # '$(VSInstallDir)' and '$(VCInstallDir)' are available when and only when
- # Visual Studio is actually installed.
- if self.vs_version.Path():
- replacements['$(VSInstallDir)'] = self.vs_version.Path()
- replacements['$(VCInstallDir)'] = os.path.join(self.vs_version.Path(),
- 'VC') + '\\'
- # Chromium uses DXSDK_DIR in include/lib paths, but it may or may not be
- # set. This happens when the SDK is sync'd via src-internal, rather than
- # by typical end-user installation of the SDK. If it's not set, we don't
- # want to leave the unexpanded variable in the path, so simply strip it.
- replacements['$(DXSDK_DIR)'] = self.dxsdk_dir if self.dxsdk_dir else ''
- replacements['$(WDK_DIR)'] = self.wdk_dir if self.wdk_dir else ''
+ replacements.update(GetGlobalVSMacroEnv(self.vs_version))
return replacements
def ConvertVSMacros(self, s, base_to_build=None, config=None):
@@ -272,6 +338,15 @@ def AdjustIncludeDirs(self, include_dirs, config):
('VCCLCompilerTool', 'AdditionalIncludeDirectories'), config, default=[]))
return [self.ConvertVSMacros(p, config=config) for p in includes]
+ def AdjustMidlIncludeDirs(self, midl_include_dirs, config):
+ """Updates midl_include_dirs to expand VS specific paths, and adds the
+ system include dirs used for platform SDK and similar."""
+ config = self._TargetConfig(config)
+ includes = midl_include_dirs + self.msvs_system_include_dirs[config]
+ includes.extend(self._Setting(
+ ('VCMIDLTool', 'AdditionalIncludeDirectories'), config, default=[]))
+ return [self.ConvertVSMacros(p, config=config) for p in includes]
+
def GetComputedDefines(self, config):
"""Returns the set of defines that are injected to the defines list based
on other VS settings."""
@@ -324,7 +399,7 @@ def GetPDBName(self, config, expand_special, default):
output_file = self._Setting(('VCLinkerTool', 'ProgramDatabaseFile'), config)
generate_debug_info = self._Setting(
('VCLinkerTool', 'GenerateDebugInformation'), config)
- if generate_debug_info:
+ if generate_debug_info == 'true':
if output_file:
return expand_special(self.ConvertVSMacros(output_file, config=config))
else:
@@ -332,6 +407,22 @@ def GetPDBName(self, config, expand_special, default):
else:
return None
+ def GetNoImportLibrary(self, config):
+ """If NoImportLibrary: true, ninja will not expect the output to include
+ an import library."""
+ config = self._TargetConfig(config)
+ noimplib = self._Setting(('NoImportLibrary',), config)
+ return noimplib == 'true'
+
+ def GetAsmflags(self, config):
+ """Returns the flags that need to be added to ml invocations."""
+ config = self._TargetConfig(config)
+ asmflags = []
+ safeseh = self._Setting(('MASM', 'UseSafeExceptionHandlers'), config)
+ if safeseh == 'true':
+ asmflags.append('/safeseh')
+ return asmflags
+
def GetCflags(self, config):
"""Returns the flags that need to be added to .c and .cc compilations."""
config = self._TargetConfig(config)
@@ -348,9 +439,14 @@ def GetCflags(self, config):
cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy')
cl('EnableIntrinsicFunctions', map={'false': '-', 'true': ''}, prefix='/Oi')
cl('FavorSizeOrSpeed', map={'1': 't', '2': 's'}, prefix='/O')
+ cl('FloatingPointModel',
+ map={'0': 'precise', '1': 'strict', '2': 'fast'}, prefix='/fp:',
+ default='0')
cl('WholeProgramOptimization', map={'true': '/GL'})
cl('WarningLevel', prefix='/W')
cl('WarnAsError', map={'true': '/WX'})
+ cl('CallingConvention',
+ map={'0': 'd', '1': 'r', '2': 'z', '3': 'v'}, prefix='/G')
cl('DebugInformationFormat',
map={'1': '7', '3': 'i', '4': 'I'}, prefix='/Z')
cl('RuntimeTypeInfo', map={'true': '/GR', 'false': '/GR-'})
@@ -366,21 +462,18 @@ def GetCflags(self, config):
map={'false': '-', 'true': ''}, prefix='/Zc:wchar_t')
cl('EnablePREfast', map={'true': '/analyze'})
cl('AdditionalOptions', prefix='')
+ cl('EnableEnhancedInstructionSet',
+ map={'1': 'SSE', '2': 'SSE2', '3': 'AVX', '4': 'IA32', '5': 'AVX2'},
+ prefix='/arch:')
cflags.extend(['/FI' + f for f in self._Setting(
('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])])
- if self.vs_version.short_name in ('2013', '2013e'):
+ if self.vs_version.short_name in ('2013', '2013e', '2015'):
# New flag required in 2013 to maintain previous PDB behavior.
cflags.append('/FS')
# ninja handles parallelism by itself, don't have the compiler do it too.
cflags = filter(lambda x: not x.startswith('/MP'), cflags)
return cflags
- def GetPrecompiledHeader(self, config, gyp_to_build_path):
- """Returns an object that handles the generation of precompiled header
- build steps."""
- config = self._TargetConfig(config)
- return _PchHelper(self, config, gyp_to_build_path)
-
def _GetPchFlags(self, config, extension):
"""Get the flags to be added to the cflags for precompiled header support.
"""
@@ -425,7 +518,8 @@ def GetLibFlags(self, config, gyp_to_build_path):
libflags.extend(self._GetAdditionalLibraryDirectories(
'VCLibrarianTool', config, gyp_to_build_path))
lib('LinkTimeCodeGeneration', map={'true': '/LTCG'})
- lib('TargetMachine', map={'1': 'X86', '17': 'X64'}, prefix='/MACHINE:')
+ lib('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
+ prefix='/MACHINE:')
lib('AdditionalOptions')
return libflags
@@ -468,7 +562,8 @@ def GetLdflags(self, config, gyp_to_build_path, expand_special,
'VCLinkerTool', append=ldflags)
self._GetDefFileAsLdflags(ldflags, gyp_to_build_path)
ld('GenerateDebugInformation', map={'true': '/DEBUG'})
- ld('TargetMachine', map={'1': 'X86', '17': 'X64'}, prefix='/MACHINE:')
+ ld('TargetMachine', map={'1': 'X86', '17': 'X64', '3': 'ARM'},
+ prefix='/MACHINE:')
ldflags.extend(self._GetAdditionalLibraryDirectories(
'VCLinkerTool', config, gyp_to_build_path))
ld('DelayLoadDLLs', prefix='/DELAYLOAD:')
@@ -522,6 +617,14 @@ def GetLdflags(self, config, gyp_to_build_path, expand_special,
# TODO(scottmg): This should sort of be somewhere else (not really a flag).
ld('AdditionalDependencies', prefix='')
+ if self.GetArch(config) == 'x86':
+ safeseh_default = 'true'
+ else:
+ safeseh_default = None
+ ld('ImageHasSafeExceptionHandlers',
+ map={'false': ':NO', 'true': ''}, prefix='/SAFESEH',
+ default=safeseh_default)
+
# If the base address is not specifically controlled, DYNAMICBASE should
# be on by default.
base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED',
@@ -708,10 +811,16 @@ def _HasExplicitRuleForExtension(self, spec, extension):
return True
return False
- def HasExplicitIdlRules(self, spec):
- """Determine if there's an explicit rule for idl files. When there isn't we
- need to generate implicit rules to build MIDL .idl files."""
- return self._HasExplicitRuleForExtension(spec, 'idl')
+ def _HasExplicitIdlActions(self, spec):
+ """Determine if an action should not run midl for .idl files."""
+ return any([action.get('explicit_idl_action', 0)
+ for action in spec.get('actions', [])])
+
+ def HasExplicitIdlRulesOrActions(self, spec):
+ """Determine if there's an explicit rule or action for idl files. When
+ there isn't we need to generate implicit rules to build MIDL .idl files."""
+ return (self._HasExplicitRuleForExtension(spec, 'idl') or
+ self._HasExplicitIdlActions(spec))
def HasExplicitAsmRules(self, spec):
"""Determine if there's an explicit rule for asm files. When there isn't we
@@ -774,7 +883,7 @@ def _PchHeader(self):
def GetObjDependencies(self, sources, objs, arch):
"""Given a list of sources files and the corresponding object files,
returns a list of the pch files that should be depended upon. The
- additional wrapping in the return value is for interface compatability
+ additional wrapping in the return value is for interface compatibility
with make.py on Mac, and xcode_emulation.py."""
assert arch is None
if not self._PchHeader():
@@ -810,7 +919,8 @@ def GetVSVersion(generator_flags):
global vs_version
if not vs_version:
vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
- generator_flags.get('msvs_version', 'auto'))
+ generator_flags.get('msvs_version', 'auto'),
+ allow_fallback=False)
return vs_version
def _GetVsvarsSetupArgs(generator_flags, arch):
@@ -878,7 +988,8 @@ def _ExtractCLPath(output_of_where):
if line.startswith('LOC:'):
return line[len('LOC:'):].strip()
-def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
+def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags,
+ system_includes, open_out):
"""It's not sufficient to have the absolute path to the compiler, linker,
etc. on Windows, as those tools rely on .dlls being in the PATH. We also
need to support both x86 and x64 compilers within the same build (to support
@@ -909,6 +1020,13 @@ def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, open_out):
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
variables, _ = popen.communicate()
env = _ExtractImportantEnvironment(variables)
+
+ # Inject system includes from gyp files into INCLUDE.
+ if system_includes:
+ system_includes = system_includes | OrderedSet(
+ env.get('INCLUDE', '').split(';'))
+ env['INCLUDE'] = ';'.join(system_includes)
+
env_block = _FormatAsEnvironmentBlock(env)
f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
f.write(env_block)
diff --git a/tools/gyp/pylib/gyp/simple_copy.py b/tools/gyp/pylib/gyp/simple_copy.py
new file mode 100644
index 00000000000000..74c98c5a79594b
--- /dev/null
+++ b/tools/gyp/pylib/gyp/simple_copy.py
@@ -0,0 +1,46 @@
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A clone of the default copy.deepcopy that doesn't handle cyclic
+structures or complex types except for dicts and lists. This is
+because gyp copies so large structure that small copy overhead ends up
+taking seconds in a project the size of Chromium."""
+
+class Error(Exception):
+ pass
+
+__all__ = ["Error", "deepcopy"]
+
+def deepcopy(x):
+ """Deep copy operation on gyp objects such as strings, ints, dicts
+ and lists. More than twice as fast as copy.deepcopy but much less
+ generic."""
+
+ try:
+ return _deepcopy_dispatch[type(x)](x)
+ except KeyError:
+ raise Error('Unsupported type %s for deepcopy. Use copy.deepcopy ' +
+ 'or expand simple_copy support.' % type(x))
+
+_deepcopy_dispatch = d = {}
+
+def _deepcopy_atomic(x):
+ return x
+
+for x in (type(None), int, long, float,
+ bool, str, unicode, type):
+ d[x] = _deepcopy_atomic
+
+def _deepcopy_list(x):
+ return [deepcopy(a) for a in x]
+d[list] = _deepcopy_list
+
+def _deepcopy_dict(x):
+ y = {}
+ for key, value in x.iteritems():
+ y[deepcopy(key)] = deepcopy(value)
+ return y
+d[dict] = _deepcopy_dict
+
+del d
diff --git a/tools/gyp/pylib/gyp/win_tool.py b/tools/gyp/pylib/gyp/win_tool.py
index e9d7df0bb771b5..417e465f7853f4 100755
--- a/tools/gyp/pylib/gyp/win_tool.py
+++ b/tools/gyp/pylib/gyp/win_tool.py
@@ -13,6 +13,7 @@
import re
import shutil
import subprocess
+import stat
import string
import sys
@@ -48,7 +49,8 @@ def _UseSeparateMspdbsrv(self, env, args):
for arg in args:
m = _LINK_EXE_OUT_ARG.match(arg)
if m:
- endpoint_name = '%s_%d' % (m.group('out'), os.getpid())
+ endpoint_name = re.sub(r'\W+', '',
+ '%s_%d' % (m.group('out'), os.getpid()))
break
if endpoint_name is None:
@@ -88,9 +90,19 @@ def ExecRecursiveMirror(self, source, dest):
"""Emulation of rm -rf out && cp -af in out."""
if os.path.exists(dest):
if os.path.isdir(dest):
- shutil.rmtree(dest)
+ def _on_error(fn, path, excinfo):
+ # The operation failed, possibly because the file is set to
+ # read-only. If that's why, make it writable and try the op again.
+ if not os.access(path, os.W_OK):
+ os.chmod(path, stat.S_IWRITE)
+ fn(path)
+ shutil.rmtree(dest, onerror=_on_error)
else:
+ if not os.access(dest, os.W_OK):
+ # Attempt to make the file writable before deleting it.
+ os.chmod(dest, stat.S_IWRITE)
os.unlink(dest)
+
if os.path.isdir(source):
shutil.copytree(source, dest)
else:
@@ -104,7 +116,7 @@ def ExecLinkWrapper(self, arch, use_separate_mspdbsrv, *args):
env = self._GetEnv(arch)
if use_separate_mspdbsrv == 'True':
self._UseSeparateMspdbsrv(env, args)
- link = subprocess.Popen(args,
+ link = subprocess.Popen([args[0].replace('/', '\\')] + list(args[1:]),
shell=True,
env=env,
stdout=subprocess.PIPE,
@@ -236,19 +248,17 @@ def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
# Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl
# objidl.idl
lines = out.splitlines()
- prefix = 'Processing '
- processing = set(os.path.basename(x) for x in lines if x.startswith(prefix))
+ prefixes = ('Processing ', '64 bit Processing ')
+ processing = set(os.path.basename(x)
+ for x in lines if x.startswith(prefixes))
for line in lines:
- if not line.startswith(prefix) and line not in processing:
+ if not line.startswith(prefixes) and line not in processing:
print line
return popen.returncode
def ExecAsmWrapper(self, arch, *args):
"""Filter logo banner from invocations of asm.exe."""
env = self._GetEnv(arch)
- # MSVS doesn't assemble x64 asm files.
- if arch == 'environment.x64':
- return 0
popen = subprocess.Popen(args, shell=True, env=env,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, _ = popen.communicate()
@@ -287,5 +297,16 @@ def ExecActionWrapper(self, arch, rspfile, *dir):
dir = dir[0] if dir else None
return subprocess.call(args, shell=True, env=env, cwd=dir)
+ def ExecClCompile(self, project_dir, selected_files):
+ """Executed by msvs-ninja projects when the 'ClCompile' target is used to
+ build selected C/C++ files."""
+ project_dir = os.path.relpath(project_dir, BASE_DIR)
+ selected_files = selected_files.split(';')
+ ninja_targets = [os.path.join(project_dir, filename) + '^^'
+ for filename in selected_files]
+ cmd = ['ninja.exe']
+ cmd.extend(ninja_targets)
+ return subprocess.call(cmd, shell=True, cwd=BASE_DIR)
+
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/tools/gyp/pylib/gyp/xcode_emulation.py b/tools/gyp/pylib/gyp/xcode_emulation.py
index 30f27d5832d99b..72bb53a40dbb9f 100644
--- a/tools/gyp/pylib/gyp/xcode_emulation.py
+++ b/tools/gyp/pylib/gyp/xcode_emulation.py
@@ -18,6 +18,129 @@
import tempfile
from gyp.common import GypError
+# Populated lazily by XcodeVersion, for efficiency, and to fix an issue when
+# "xcodebuild" is called too quickly (it has been found to return incorrect
+# version number).
+XCODE_VERSION_CACHE = None
+
+# Populated lazily by GetXcodeArchsDefault, to an |XcodeArchsDefault| instance
+# corresponding to the installed version of Xcode.
+XCODE_ARCHS_DEFAULT_CACHE = None
+
+
+def XcodeArchsVariableMapping(archs, archs_including_64_bit=None):
+ """Constructs a dictionary with expansion for $(ARCHS_STANDARD) variable,
+ and optionally for $(ARCHS_STANDARD_INCLUDING_64_BIT)."""
+ mapping = {'$(ARCHS_STANDARD)': archs}
+ if archs_including_64_bit:
+ mapping['$(ARCHS_STANDARD_INCLUDING_64_BIT)'] = archs_including_64_bit
+ return mapping
+
+class XcodeArchsDefault(object):
+ """A class to resolve ARCHS variable from xcode_settings, resolving Xcode
+ macros and implementing filtering by VALID_ARCHS. The expansion of macros
+ depends on the SDKROOT used ("macosx", "iphoneos", "iphonesimulator") and
+ on the version of Xcode.
+ """
+
+ # Match variable like $(ARCHS_STANDARD).
+ variable_pattern = re.compile(r'\$\([a-zA-Z_][a-zA-Z0-9_]*\)$')
+
+ def __init__(self, default, mac, iphonesimulator, iphoneos):
+ self._default = (default,)
+ self._archs = {'mac': mac, 'ios': iphoneos, 'iossim': iphonesimulator}
+
+ def _VariableMapping(self, sdkroot):
+ """Returns the dictionary of variable mapping depending on the SDKROOT."""
+ sdkroot = sdkroot.lower()
+ if 'iphoneos' in sdkroot:
+ return self._archs['ios']
+ elif 'iphonesimulator' in sdkroot:
+ return self._archs['iossim']
+ else:
+ return self._archs['mac']
+
+ def _ExpandArchs(self, archs, sdkroot):
+ """Expands variables references in ARCHS, and remove duplicates."""
+ variable_mapping = self._VariableMapping(sdkroot)
+ expanded_archs = []
+ for arch in archs:
+ if self.variable_pattern.match(arch):
+ variable = arch
+ try:
+ variable_expansion = variable_mapping[variable]
+ for arch in variable_expansion:
+ if arch not in expanded_archs:
+ expanded_archs.append(arch)
+ except KeyError as e:
+ print 'Warning: Ignoring unsupported variable "%s".' % variable
+ elif arch not in expanded_archs:
+ expanded_archs.append(arch)
+ return expanded_archs
+
+ def ActiveArchs(self, archs, valid_archs, sdkroot):
+ """Expands variables references in ARCHS, and filter by VALID_ARCHS if it
+ is defined (if not set, Xcode accept any value in ARCHS, otherwise, only
+ values present in VALID_ARCHS are kept)."""
+ expanded_archs = self._ExpandArchs(archs or self._default, sdkroot or '')
+ if valid_archs:
+ filtered_archs = []
+ for arch in expanded_archs:
+ if arch in valid_archs:
+ filtered_archs.append(arch)
+ expanded_archs = filtered_archs
+ return expanded_archs
+
+
+def GetXcodeArchsDefault():
+ """Returns the |XcodeArchsDefault| object to use to expand ARCHS for the
+ installed version of Xcode. The default values used by Xcode for ARCHS
+ and the expansion of the variables depends on the version of Xcode used.
+
+ For all version anterior to Xcode 5.0 or posterior to Xcode 5.1 included
+ uses $(ARCHS_STANDARD) if ARCHS is unset, while Xcode 5.0 to 5.0.2 uses
+ $(ARCHS_STANDARD_INCLUDING_64_BIT). This variable was added to Xcode 5.0
+ and deprecated with Xcode 5.1.
+
+ For "macosx" SDKROOT, all version starting with Xcode 5.0 includes 64-bit
+ architecture as part of $(ARCHS_STANDARD) and default to only building it.
+
+ For "iphoneos" and "iphonesimulator" SDKROOT, 64-bit architectures are part
+ of $(ARCHS_STANDARD_INCLUDING_64_BIT) from Xcode 5.0. From Xcode 5.1, they
+ are also part of $(ARCHS_STANDARD).
+
+ All thoses rules are coded in the construction of the |XcodeArchsDefault|
+ object to use depending on the version of Xcode detected. The object is
+ for performance reason."""
+ global XCODE_ARCHS_DEFAULT_CACHE
+ if XCODE_ARCHS_DEFAULT_CACHE:
+ return XCODE_ARCHS_DEFAULT_CACHE
+ xcode_version, _ = XcodeVersion()
+ if xcode_version < '0500':
+ XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
+ '$(ARCHS_STANDARD)',
+ XcodeArchsVariableMapping(['i386']),
+ XcodeArchsVariableMapping(['i386']),
+ XcodeArchsVariableMapping(['armv7']))
+ elif xcode_version < '0510':
+ XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
+ '$(ARCHS_STANDARD_INCLUDING_64_BIT)',
+ XcodeArchsVariableMapping(['x86_64'], ['x86_64']),
+ XcodeArchsVariableMapping(['i386'], ['i386', 'x86_64']),
+ XcodeArchsVariableMapping(
+ ['armv7', 'armv7s'],
+ ['armv7', 'armv7s', 'arm64']))
+ else:
+ XCODE_ARCHS_DEFAULT_CACHE = XcodeArchsDefault(
+ '$(ARCHS_STANDARD)',
+ XcodeArchsVariableMapping(['x86_64'], ['x86_64']),
+ XcodeArchsVariableMapping(['i386', 'x86_64'], ['i386', 'x86_64']),
+ XcodeArchsVariableMapping(
+ ['armv7', 'armv7s', 'arm64'],
+ ['armv7', 'armv7s', 'arm64']))
+ return XCODE_ARCHS_DEFAULT_CACHE
+
+
class XcodeSettings(object):
"""A class that understands the gyp 'xcode_settings' object."""
@@ -34,10 +157,6 @@ class XcodeSettings(object):
# cached at class-level for efficiency.
_codesigning_key_cache = {}
- # Populated lazily by _XcodeVersion. Shared by all XcodeSettings, so cached
- # at class-level for efficiency.
- _xcode_version_cache = ()
-
def __init__(self, spec):
self.spec = spec
@@ -96,9 +215,24 @@ def _WarnUnimplemented(self, test_key):
if test_key in self._Settings():
print 'Warning: Ignoring not yet implemented key "%s".' % test_key
+ def IsBinaryOutputFormat(self, configname):
+ default = "binary" if self.isIOS else "xml"
+ format = self.xcode_settings[configname].get('INFOPLIST_OUTPUT_FORMAT',
+ default)
+ return format == "binary"
+
def _IsBundle(self):
return int(self.spec.get('mac_bundle', 0)) != 0
+ def _IsIosAppExtension(self):
+ return int(self.spec.get('ios_app_extension', 0)) != 0
+
+ def _IsIosWatchKitExtension(self):
+ return int(self.spec.get('ios_watchkit_extension', 0)) != 0
+
+ def _IsIosWatchApp(self):
+ return int(self.spec.get('ios_watch_app', 0)) != 0
+
def GetFrameworkVersion(self):
"""Returns the framework version of the current target. Only valid for
bundles."""
@@ -118,7 +252,10 @@ def GetWrapperExtension(self):
'WRAPPER_EXTENSION', default=default_wrapper_extension)
return '.' + self.spec.get('product_extension', wrapper_extension)
elif self.spec['type'] == 'executable':
- return '.' + self.spec.get('product_extension', 'app')
+ if self._IsIosAppExtension() or self._IsIosWatchKitExtension():
+ return '.' + self.spec.get('product_extension', 'appex')
+ else:
+ return '.' + self.spec.get('product_extension', 'app')
else:
assert False, "Don't know extension for '%s', target '%s'" % (
self.spec['type'], self.spec['target_name'])
@@ -173,6 +310,18 @@ def GetBundlePlistPath(self):
def GetProductType(self):
"""Returns the PRODUCT_TYPE of this target."""
+ if self._IsIosAppExtension():
+ assert self._IsBundle(), ('ios_app_extension flag requires mac_bundle '
+ '(target %s)' % self.spec['target_name'])
+ return 'com.apple.product-type.app-extension'
+ if self._IsIosWatchKitExtension():
+ assert self._IsBundle(), ('ios_watchkit_extension flag requires '
+ 'mac_bundle (target %s)' % self.spec['target_name'])
+ return 'com.apple.product-type.watchkit-extension'
+ if self._IsIosWatchApp():
+ assert self._IsBundle(), ('ios_watch_app flag requires mac_bundle '
+ '(target %s)' % self.spec['target_name'])
+ return 'com.apple.product-type.application.watchapp'
if self._IsBundle():
return {
'executable': 'com.apple.product-type.application',
@@ -267,17 +416,12 @@ def GetExecutablePath(self):
def GetActiveArchs(self, configname):
"""Returns the architectures this target should be built for."""
- # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set
- # CURRENT_ARCH / NATIVE_ARCH env vars?
- return self.xcode_settings[configname].get('ARCHS', [self._DefaultArch()])
-
- def _GetStdout(self, cmdlist):
- job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
- out = job.communicate()[0]
- if job.returncode != 0:
- sys.stderr.write(out + '\n')
- raise GypError('Error %d running %s' % (job.returncode, cmdlist[0]))
- return out.rstrip('\n')
+ config_settings = self.xcode_settings[configname]
+ xcode_archs_default = GetXcodeArchsDefault()
+ return xcode_archs_default.ActiveArchs(
+ config_settings.get('ARCHS'),
+ config_settings.get('VALID_ARCHS'),
+ config_settings.get('SDKROOT'))
def _GetSdkVersionInfoItem(self, sdk, infoitem):
# xcodebuild requires Xcode and can't run on Command Line Tools-only
@@ -285,7 +429,7 @@ def _GetSdkVersionInfoItem(self, sdk, infoitem):
# Since the CLT has no SDK paths anyway, returning None is the
# most sensible route and should still do the right thing.
try:
- return self._GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem])
+ return GetStdout(['xcodebuild', '-version', '-sdk', sdk, infoitem])
except:
pass
@@ -396,7 +540,8 @@ def GetCflags(self, configname, arch=None):
if arch is not None:
archs = [arch]
else:
- archs = self._Settings().get('ARCHS', [self._DefaultArch()])
+ assert self.configname
+ archs = self.GetActiveArchs(self.configname)
if len(archs) != 1:
# TODO: Supporting fat binaries will be annoying.
self._WarnUnimplemented('ARCHS')
@@ -588,8 +733,8 @@ def _MapLinkerFlagFilename(self, ldflag, gyp_to_build_path):
# -exported_symbols_list file
# -Wl,exported_symbols_list file
# -Wl,exported_symbols_list,file
- LINKER_FILE = '(\S+)'
- WORD = '\S+'
+ LINKER_FILE = r'(\S+)'
+ WORD = r'\S+'
linker_flags = [
['-exported_symbols_list', LINKER_FILE], # Needed for NaCl.
['-unexported_symbols_list', LINKER_FILE],
@@ -653,7 +798,8 @@ def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None):
if arch is not None:
archs = [arch]
else:
- archs = self._Settings().get('ARCHS', [self._DefaultArch()])
+ assert self.configname
+ archs = self.GetActiveArchs(self.configname)
if len(archs) != 1:
# TODO: Supporting fat binaries will be annoying.
self._WarnUnimplemented('ARCHS')
@@ -678,6 +824,21 @@ def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None):
for directory in framework_dirs:
ldflags.append('-F' + directory.replace('$(SDKROOT)', sdk_root))
+ is_extension = self._IsIosAppExtension() or self._IsIosWatchKitExtension()
+ if sdk_root and is_extension:
+ # Adds the link flags for extensions. These flags are common for all
+ # extensions and provide loader and main function.
+ # These flags reflect the compilation options used by xcode to compile
+ # extensions.
+ ldflags.append('-lpkstart')
+ ldflags.append(sdk_root +
+ '/System/Library/PrivateFrameworks/PlugInKit.framework/PlugInKit')
+ ldflags.append('-fapplication-extension')
+ ldflags.append('-Xlinker -rpath '
+ '-Xlinker @executable_path/../../Frameworks')
+
+ self._Appendf(ldflags, 'CLANG_CXX_LIBRARY', '-stdlib=%s')
+
self.configname = None
return ldflags
@@ -803,7 +964,7 @@ def _GetIOSPostbuilds(self, configname, output_binary):
"""Return a shell command to codesign the iOS output binary so it can
be deployed to a device. This should be run as the very last step of the
build."""
- if not (self.isIOS and self.spec['type'] == "executable"):
+ if not (self.isIOS and self.spec['type'] == 'executable'):
return []
settings = self.xcode_settings[configname]
@@ -874,65 +1035,7 @@ def AdjustLibraries(self, libraries, config_name=None):
return libraries
def _BuildMachineOSBuild(self):
- return self._GetStdout(['sw_vers', '-buildVersion'])
-
- # This method ported from the logic in Homebrew's CLT version check
- def _CLTVersion(self):
- # pkgutil output looks like
- # package-id: com.apple.pkg.CLTools_Executables
- # version: 5.0.1.0.1.1382131676
- # volume: /
- # location: /
- # install-time: 1382544035
- # groups: com.apple.FindSystemFiles.pkg-group com.apple.DevToolsBoth.pkg-group com.apple.DevToolsNonRelocatableShared.pkg-group
- STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo"
- FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI"
- MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables"
-
- regex = re.compile('version: (?P.+)')
- for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]:
- try:
- output = self._GetStdout(['/usr/sbin/pkgutil', '--pkg-info', key])
- return re.search(regex, output).groupdict()['version']
- except:
- continue
-
- def _XcodeVersion(self):
- # `xcodebuild -version` output looks like
- # Xcode 4.6.3
- # Build version 4H1503
- # or like
- # Xcode 3.2.6
- # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0
- # BuildVersion: 10M2518
- # Convert that to '0463', '4H1503'.
- if len(XcodeSettings._xcode_version_cache) == 0:
- try:
- version_list = self._GetStdout(['xcodebuild', '-version']).splitlines()
- # In some circumstances xcodebuild exits 0 but doesn't return
- # the right results; for example, a user on 10.7 or 10.8 with
- # a bogus path set via xcode-select
- # In that case this may be a CLT-only install so fall back to
- # checking that version.
- if len(version_list) < 2:
- raise GypError, "xcodebuild returned unexpected results"
- except:
- version = self._CLTVersion()
- if version:
- version = re.match('(\d\.\d\.?\d*)', version).groups()[0]
- else:
- raise GypError, "No Xcode or CLT version detected!"
- # The CLT has no build information, so we return an empty string.
- version_list = [version, '']
- version = version_list[0]
- build = version_list[-1]
- # Be careful to convert "4.2" to "0420":
- version = version.split()[-1].replace('.', '')
- version = (version + '0' * (3 - len(version))).zfill(4)
- if build:
- build = build.split()[-1]
- XcodeSettings._xcode_version_cache = (version, build)
- return XcodeSettings._xcode_version_cache
+ return GetStdout(['sw_vers', '-buildVersion'])
def _XcodeIOSDeviceFamily(self, configname):
family = self.xcode_settings[configname].get('TARGETED_DEVICE_FAMILY', '1')
@@ -944,7 +1047,7 @@ def GetExtraPlistItems(self, configname=None):
cache = {}
cache['BuildMachineOSBuild'] = self._BuildMachineOSBuild()
- xcode, xcode_build = self._XcodeVersion()
+ xcode, xcode_build = XcodeVersion()
cache['DTXcode'] = xcode
cache['DTXcodeBuild'] = xcode_build
@@ -982,14 +1085,15 @@ def _DefaultSdkRoot(self):
project, then the environment variable was empty. Starting with this
version, Xcode uses the name of the newest SDK installed.
"""
- if self._XcodeVersion() < '0500':
+ xcode_version, xcode_build = XcodeVersion()
+ if xcode_version < '0500':
return ''
default_sdk_path = self._XcodeSdkPath('')
default_sdk_root = XcodeSettings._sdk_root_cache.get(default_sdk_path)
if default_sdk_root:
return default_sdk_root
try:
- all_sdks = self._GetStdout(['xcodebuild', '-showsdks'])
+ all_sdks = GetStdout(['xcodebuild', '-showsdks'])
except:
# If xcodebuild fails, there will be no valid SDKs
return ''
@@ -1002,28 +1106,6 @@ def _DefaultSdkRoot(self):
return sdk_root
return ''
- def _DefaultArch(self):
- # For Mac projects, Xcode changed the default value used when ARCHS is not
- # set from "i386" to "x86_64".
- #
- # For iOS projects, if ARCHS is unset, it defaults to "armv7 armv7s" when
- # building for a device, and the simulator binaries are always build for
- # "i386".
- #
- # For new projects, ARCHS is set to $(ARCHS_STANDARD_INCLUDING_64_BIT),
- # which correspond to "armv7 armv7s arm64", and when building the simulator
- # the architecture is either "i386" or "x86_64" depending on the simulated
- # device (respectively 32-bit or 64-bit device).
- #
- # Since the value returned by this function is only used when ARCHS is not
- # set, then on iOS we return "i386", as the default xcode project generator
- # does not set ARCHS if it is not set in the .gyp file.
- if self.isIOS:
- return 'i386'
- version, build = self._XcodeVersion()
- if version >= '0500':
- return 'x86_64'
- return 'i386'
class MacPrefixHeader(object):
"""A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature.
@@ -1131,6 +1213,81 @@ def GetPchBuildCommands(self, arch=None):
]
+def XcodeVersion():
+ """Returns a tuple of version and build version of installed Xcode."""
+ # `xcodebuild -version` output looks like
+ # Xcode 4.6.3
+ # Build version 4H1503
+ # or like
+ # Xcode 3.2.6
+ # Component versions: DevToolsCore-1809.0; DevToolsSupport-1806.0
+ # BuildVersion: 10M2518
+ # Convert that to '0463', '4H1503'.
+ global XCODE_VERSION_CACHE
+ if XCODE_VERSION_CACHE:
+ return XCODE_VERSION_CACHE
+ try:
+ version_list = GetStdout(['xcodebuild', '-version']).splitlines()
+ # In some circumstances xcodebuild exits 0 but doesn't return
+ # the right results; for example, a user on 10.7 or 10.8 with
+ # a bogus path set via xcode-select
+ # In that case this may be a CLT-only install so fall back to
+ # checking that version.
+ if len(version_list) < 2:
+ raise GypError("xcodebuild returned unexpected results")
+ except:
+ version = CLTVersion()
+ if version:
+ version = re.match(r'(\d\.\d\.?\d*)', version).groups()[0]
+ else:
+ raise GypError("No Xcode or CLT version detected!")
+ # The CLT has no build information, so we return an empty string.
+ version_list = [version, '']
+ version = version_list[0]
+ build = version_list[-1]
+ # Be careful to convert "4.2" to "0420":
+ version = version.split()[-1].replace('.', '')
+ version = (version + '0' * (3 - len(version))).zfill(4)
+ if build:
+ build = build.split()[-1]
+ XCODE_VERSION_CACHE = (version, build)
+ return XCODE_VERSION_CACHE
+
+
+# This function ported from the logic in Homebrew's CLT version check
+def CLTVersion():
+ """Returns the version of command-line tools from pkgutil."""
+ # pkgutil output looks like
+ # package-id: com.apple.pkg.CLTools_Executables
+ # version: 5.0.1.0.1.1382131676
+ # volume: /
+ # location: /
+ # install-time: 1382544035
+ # groups: com.apple.FindSystemFiles.pkg-group com.apple.DevToolsBoth.pkg-group com.apple.DevToolsNonRelocatableShared.pkg-group
+ STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo"
+ FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI"
+ MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables"
+
+ regex = re.compile('version: (?P.+)')
+ for key in [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID]:
+ try:
+ output = GetStdout(['/usr/sbin/pkgutil', '--pkg-info', key])
+ return re.search(regex, output).groupdict()['version']
+ except:
+ continue
+
+
+def GetStdout(cmdlist):
+ """Returns the content of standard output returned by invoking |cmdlist|.
+ Raises |GypError| if the command return with a non-zero return code."""
+ job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
+ out = job.communicate()[0]
+ if job.returncode != 0:
+ sys.stderr.write(out + '\n')
+ raise GypError('Error %d running %s' % (job.returncode, cmdlist[0]))
+ return out.rstrip('\n')
+
+
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
@@ -1310,6 +1467,11 @@ def _GetXcodeEnv(xcode_settings, built_products_dir, srcroot, configuration,
install_name_base = xcode_settings.GetInstallNameBase()
if install_name_base:
env['DYLIB_INSTALL_NAME_BASE'] = install_name_base
+ if XcodeVersion() >= '0500' and not env.get('SDKROOT'):
+ sdk_root = xcode_settings._SdkRoot(configuration)
+ if not sdk_root:
+ sdk_root = xcode_settings._XcodeSdkPath('')
+ env['SDKROOT'] = sdk_root
if not additional_settings:
additional_settings = {}
@@ -1420,16 +1582,16 @@ def _HasIOSTarget(targets):
def _AddIOSDeviceConfigurations(targets):
"""Clone all targets and append -iphoneos to the name. Configure these targets
- to build for iOS devices."""
- for target_dict in targets.values():
- for config_name in target_dict['configurations'].keys():
- config = target_dict['configurations'][config_name]
- new_config_name = config_name + '-iphoneos'
- new_config_dict = copy.deepcopy(config)
- if target_dict['toolset'] == 'target':
- new_config_dict['xcode_settings']['ARCHS'] = ['armv7']
- new_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos'
- target_dict['configurations'][new_config_name] = new_config_dict
+ to build for iOS devices and use correct architectures for those builds."""
+ for target_dict in targets.itervalues():
+ toolset = target_dict['toolset']
+ configs = target_dict['configurations']
+ for config_name, config_dict in dict(configs).iteritems():
+ iphoneos_config_dict = copy.deepcopy(config_dict)
+ configs[config_name + '-iphoneos'] = iphoneos_config_dict
+ configs[config_name + '-iphonesimulator'] = config_dict
+ if toolset == 'target':
+ iphoneos_config_dict['xcode_settings']['SDKROOT'] = 'iphoneos'
return targets
def CloneConfigurationForDeviceAndEmulator(target_dicts):
diff --git a/tools/gyp/pylib/gyp/xcode_ninja.py b/tools/gyp/pylib/gyp/xcode_ninja.py
new file mode 100644
index 00000000000000..3820d6bf04817f
--- /dev/null
+++ b/tools/gyp/pylib/gyp/xcode_ninja.py
@@ -0,0 +1,270 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Xcode-ninja wrapper project file generator.
+
+This updates the data structures passed to the Xcode gyp generator to build
+with ninja instead. The Xcode project itself is transformed into a list of
+executable targets, each with a build step to build with ninja, and a target
+with every source and resource file. This appears to sidestep some of the
+major performance headaches experienced using complex projects and large number
+of targets within Xcode.
+"""
+
+import errno
+import gyp.generator.ninja
+import os
+import re
+import xml.sax.saxutils
+
+
+def _WriteWorkspace(main_gyp, sources_gyp, params):
+ """ Create a workspace to wrap main and sources gyp paths. """
+ (build_file_root, build_file_ext) = os.path.splitext(main_gyp)
+ workspace_path = build_file_root + '.xcworkspace'
+ options = params['options']
+ if options.generator_output:
+ workspace_path = os.path.join(options.generator_output, workspace_path)
+ try:
+ os.makedirs(workspace_path)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+ output_string = '\n' + \
+ '\n'
+ for gyp_name in [main_gyp, sources_gyp]:
+ name = os.path.splitext(os.path.basename(gyp_name))[0] + '.xcodeproj'
+ name = xml.sax.saxutils.quoteattr("group:" + name)
+ output_string += ' \n' % name
+ output_string += '\n'
+
+ workspace_file = os.path.join(workspace_path, "contents.xcworkspacedata")
+
+ try:
+ with open(workspace_file, 'r') as input_file:
+ input_string = input_file.read()
+ if input_string == output_string:
+ return
+ except IOError:
+ # Ignore errors if the file doesn't exist.
+ pass
+
+ with open(workspace_file, 'w') as output_file:
+ output_file.write(output_string)
+
+def _TargetFromSpec(old_spec, params):
+ """ Create fake target for xcode-ninja wrapper. """
+ # Determine ninja top level build dir (e.g. /path/to/out).
+ ninja_toplevel = None
+ jobs = 0
+ if params:
+ options = params['options']
+ ninja_toplevel = \
+ os.path.join(options.toplevel_dir,
+ gyp.generator.ninja.ComputeOutputDir(params))
+ jobs = params.get('generator_flags', {}).get('xcode_ninja_jobs', 0)
+
+ target_name = old_spec.get('target_name')
+ product_name = old_spec.get('product_name', target_name)
+ product_extension = old_spec.get('product_extension')
+
+ ninja_target = {}
+ ninja_target['target_name'] = target_name
+ ninja_target['product_name'] = product_name
+ if product_extension:
+ ninja_target['product_extension'] = product_extension
+ ninja_target['toolset'] = old_spec.get('toolset')
+ ninja_target['default_configuration'] = old_spec.get('default_configuration')
+ ninja_target['configurations'] = {}
+
+ # Tell Xcode to look in |ninja_toplevel| for build products.
+ new_xcode_settings = {}
+ if ninja_toplevel:
+ new_xcode_settings['CONFIGURATION_BUILD_DIR'] = \
+ "%s/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)" % ninja_toplevel
+
+ if 'configurations' in old_spec:
+ for config in old_spec['configurations'].iterkeys():
+ old_xcode_settings = \
+ old_spec['configurations'][config].get('xcode_settings', {})
+ if 'IPHONEOS_DEPLOYMENT_TARGET' in old_xcode_settings:
+ new_xcode_settings['CODE_SIGNING_REQUIRED'] = "NO"
+ new_xcode_settings['IPHONEOS_DEPLOYMENT_TARGET'] = \
+ old_xcode_settings['IPHONEOS_DEPLOYMENT_TARGET']
+ ninja_target['configurations'][config] = {}
+ ninja_target['configurations'][config]['xcode_settings'] = \
+ new_xcode_settings
+
+ ninja_target['mac_bundle'] = old_spec.get('mac_bundle', 0)
+ ninja_target['ios_app_extension'] = old_spec.get('ios_app_extension', 0)
+ ninja_target['ios_watchkit_extension'] = \
+ old_spec.get('ios_watchkit_extension', 0)
+ ninja_target['ios_watchkit_app'] = old_spec.get('ios_watchkit_app', 0)
+ ninja_target['type'] = old_spec['type']
+ if ninja_toplevel:
+ ninja_target['actions'] = [
+ {
+ 'action_name': 'Compile and copy %s via ninja' % target_name,
+ 'inputs': [],
+ 'outputs': [],
+ 'action': [
+ 'env',
+ 'PATH=%s' % os.environ['PATH'],
+ 'ninja',
+ '-C',
+ new_xcode_settings['CONFIGURATION_BUILD_DIR'],
+ target_name,
+ ],
+ 'message': 'Compile and copy %s via ninja' % target_name,
+ },
+ ]
+ if jobs > 0:
+ ninja_target['actions'][0]['action'].extend(('-j', jobs))
+ return ninja_target
+
+def IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
+ """Limit targets for Xcode wrapper.
+
+ Xcode sometimes performs poorly with too many targets, so only include
+ proper executable targets, with filters to customize.
+ Arguments:
+ target_extras: Regular expression to always add, matching any target.
+ executable_target_pattern: Regular expression limiting executable targets.
+ spec: Specifications for target.
+ """
+ target_name = spec.get('target_name')
+ # Always include targets matching target_extras.
+ if target_extras is not None and re.search(target_extras, target_name):
+ return True
+
+ # Otherwise just show executable targets.
+ if spec.get('type', '') == 'executable' and \
+ spec.get('product_extension', '') != 'bundle':
+
+ # If there is a filter and the target does not match, exclude the target.
+ if executable_target_pattern is not None:
+ if not re.search(executable_target_pattern, target_name):
+ return False
+ return True
+ return False
+
+def CreateWrapper(target_list, target_dicts, data, params):
+ """Initialize targets for the ninja wrapper.
+
+ This sets up the necessary variables in the targets to generate Xcode projects
+ that use ninja as an external builder.
+ Arguments:
+ target_list: List of target pairs: 'base/base.gyp:base'.
+ target_dicts: Dict of target properties keyed on target pair.
+ data: Dict of flattened build files keyed on gyp path.
+ params: Dict of global options for gyp.
+ """
+ orig_gyp = params['build_files'][0]
+ for gyp_name, gyp_dict in data.iteritems():
+ if gyp_name == orig_gyp:
+ depth = gyp_dict['_DEPTH']
+
+ # Check for custom main gyp name, otherwise use the default CHROMIUM_GYP_FILE
+ # and prepend .ninja before the .gyp extension.
+ generator_flags = params.get('generator_flags', {})
+ main_gyp = generator_flags.get('xcode_ninja_main_gyp', None)
+ if main_gyp is None:
+ (build_file_root, build_file_ext) = os.path.splitext(orig_gyp)
+ main_gyp = build_file_root + ".ninja" + build_file_ext
+
+ # Create new |target_list|, |target_dicts| and |data| data structures.
+ new_target_list = []
+ new_target_dicts = {}
+ new_data = {}
+
+ # Set base keys needed for |data|.
+ new_data[main_gyp] = {}
+ new_data[main_gyp]['included_files'] = []
+ new_data[main_gyp]['targets'] = []
+ new_data[main_gyp]['xcode_settings'] = \
+ data[orig_gyp].get('xcode_settings', {})
+
+ # Normally the xcode-ninja generator includes only valid executable targets.
+ # If |xcode_ninja_executable_target_pattern| is set, that list is reduced to
+ # executable targets that match the pattern. (Default all)
+ executable_target_pattern = \
+ generator_flags.get('xcode_ninja_executable_target_pattern', None)
+
+ # For including other non-executable targets, add the matching target name
+ # to the |xcode_ninja_target_pattern| regular expression. (Default none)
+ target_extras = generator_flags.get('xcode_ninja_target_pattern', None)
+
+ for old_qualified_target in target_list:
+ spec = target_dicts[old_qualified_target]
+ if IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
+ # Add to new_target_list.
+ target_name = spec.get('target_name')
+ new_target_name = '%s:%s#target' % (main_gyp, target_name)
+ new_target_list.append(new_target_name)
+
+ # Add to new_target_dicts.
+ new_target_dicts[new_target_name] = _TargetFromSpec(spec, params)
+
+ # Add to new_data.
+ for old_target in data[old_qualified_target.split(':')[0]]['targets']:
+ if old_target['target_name'] == target_name:
+ new_data_target = {}
+ new_data_target['target_name'] = old_target['target_name']
+ new_data_target['toolset'] = old_target['toolset']
+ new_data[main_gyp]['targets'].append(new_data_target)
+
+ # Create sources target.
+ sources_target_name = 'sources_for_indexing'
+ sources_target = _TargetFromSpec(
+ { 'target_name' : sources_target_name,
+ 'toolset': 'target',
+ 'default_configuration': 'Default',
+ 'mac_bundle': '0',
+ 'type': 'executable'
+ }, None)
+
+ # Tell Xcode to look everywhere for headers.
+ sources_target['configurations'] = {'Default': { 'include_dirs': [ depth ] } }
+
+ sources = []
+ for target, target_dict in target_dicts.iteritems():
+ base = os.path.dirname(target)
+ files = target_dict.get('sources', []) + \
+ target_dict.get('mac_bundle_resources', [])
+ for action in target_dict.get('actions', []):
+ files.extend(action.get('inputs', []))
+ # Remove files starting with $. These are mostly intermediate files for the
+ # build system.
+ files = [ file for file in files if not file.startswith('$')]
+
+ # Make sources relative to root build file.
+ relative_path = os.path.dirname(main_gyp)
+ sources += [ os.path.relpath(os.path.join(base, file), relative_path)
+ for file in files ]
+
+ sources_target['sources'] = sorted(set(sources))
+
+ # Put sources_to_index in it's own gyp.
+ sources_gyp = \
+ os.path.join(os.path.dirname(main_gyp), sources_target_name + ".gyp")
+ fully_qualified_target_name = \
+ '%s:%s#target' % (sources_gyp, sources_target_name)
+
+ # Add to new_target_list, new_target_dicts and new_data.
+ new_target_list.append(fully_qualified_target_name)
+ new_target_dicts[fully_qualified_target_name] = sources_target
+ new_data_target = {}
+ new_data_target['target_name'] = sources_target['target_name']
+ new_data_target['_DEPTH'] = depth
+ new_data_target['toolset'] = "target"
+ new_data[sources_gyp] = {}
+ new_data[sources_gyp]['targets'] = []
+ new_data[sources_gyp]['included_files'] = []
+ new_data[sources_gyp]['xcode_settings'] = \
+ data[orig_gyp].get('xcode_settings', {})
+ new_data[sources_gyp]['targets'].append(new_data_target)
+
+ # Write workspace to file.
+ _WriteWorkspace(main_gyp, sources_gyp, params)
+ return (new_target_list, new_target_dicts, new_data)
diff --git a/tools/gyp/pylib/gyp/xcodeproj_file.py b/tools/gyp/pylib/gyp/xcodeproj_file.py
index 6e4a1dc69dce17..034a0d2d4fcc23 100644
--- a/tools/gyp/pylib/gyp/xcodeproj_file.py
+++ b/tools/gyp/pylib/gyp/xcodeproj_file.py
@@ -173,7 +173,7 @@
# Used by SourceTreeAndPathFromPath
-_path_leading_variable = re.compile('^\$\((.*?)\)(/(.*))?$')
+_path_leading_variable = re.compile(r'^\$\((.*?)\)(/(.*))?$')
def SourceTreeAndPathFromPath(input_path):
"""Given input_path, returns a tuple with sourceTree and path values.
@@ -196,7 +196,7 @@ def SourceTreeAndPathFromPath(input_path):
return (source_tree, output_path)
def ConvertVariablesToShellSyntax(input_string):
- return re.sub('\$\((.*?)\)', '${\\1}', input_string)
+ return re.sub(r'\$\((.*?)\)', '${\\1}', input_string)
class XCObject(object):
"""The abstract base of all class types used in Xcode project files.
@@ -341,13 +341,13 @@ def Copy(self):
elif isinstance(value, dict):
# dicts are never strong.
if is_strong:
- raise TypeError, 'Strong dict for key ' + key + ' in ' + \
- self.__class__.__name__
+ raise TypeError('Strong dict for key ' + key + ' in ' + \
+ self.__class__.__name__)
else:
that._properties[key] = value.copy()
else:
- raise TypeError, 'Unexpected type ' + value.__class__.__name__ + \
- ' for key ' + key + ' in ' + self.__class__.__name__
+ raise TypeError('Unexpected type ' + value.__class__.__name__ + \
+ ' for key ' + key + ' in ' + self.__class__.__name__)
return that
@@ -366,8 +366,7 @@ def Name(self):
('name' in self._schema and self._schema['name'][3]):
return self._properties['name']
- raise NotImplementedError, \
- self.__class__.__name__ + ' must implement Name'
+ raise NotImplementedError(self.__class__.__name__ + ' must implement Name')
def Comment(self):
"""Return a comment string for the object.
@@ -466,10 +465,10 @@ def EnsureNoIDCollisions(self):
for descendant in descendants:
if descendant.id in ids:
other = ids[descendant.id]
- raise KeyError, \
+ raise KeyError(
'Duplicate ID %s, objects "%s" and "%s" in "%s"' % \
(descendant.id, str(descendant._properties),
- str(other._properties), self._properties['rootObject'].Name())
+ str(other._properties), self._properties['rootObject'].Name()))
ids[descendant.id] = descendant
def Children(self):
@@ -630,7 +629,7 @@ def _XCPrintableValue(self, tabs, value, flatten_list=False):
sep
printable += end_tabs + '}'
else:
- raise TypeError, "Can't make " + value.__class__.__name__ + ' printable'
+ raise TypeError("Can't make " + value.__class__.__name__ + ' printable')
if comment != None:
printable += ' ' + self._EncodeComment(comment)
@@ -756,31 +755,31 @@ def UpdateProperties(self, properties, do_copy=False):
for property, value in properties.iteritems():
# Make sure the property is in the schema.
if not property in self._schema:
- raise KeyError, property + ' not in ' + self.__class__.__name__
+ raise KeyError(property + ' not in ' + self.__class__.__name__)
# Make sure the property conforms to the schema.
(is_list, property_type, is_strong) = self._schema[property][0:3]
if is_list:
if value.__class__ != list:
- raise TypeError, \
+ raise TypeError(
property + ' of ' + self.__class__.__name__ + \
- ' must be list, not ' + value.__class__.__name__
+ ' must be list, not ' + value.__class__.__name__)
for item in value:
if not isinstance(item, property_type) and \
not (item.__class__ == unicode and property_type == str):
# Accept unicode where str is specified. str is treated as
# UTF-8-encoded.
- raise TypeError, \
+ raise TypeError(
'item of ' + property + ' of ' + self.__class__.__name__ + \
' must be ' + property_type.__name__ + ', not ' + \
- item.__class__.__name__
+ item.__class__.__name__)
elif not isinstance(value, property_type) and \
not (value.__class__ == unicode and property_type == str):
# Accept unicode where str is specified. str is treated as
# UTF-8-encoded.
- raise TypeError, \
+ raise TypeError(
property + ' of ' + self.__class__.__name__ + ' must be ' + \
- property_type.__name__ + ', not ' + value.__class__.__name__
+ property_type.__name__ + ', not ' + value.__class__.__name__)
# Checks passed, perform the assignment.
if do_copy:
@@ -804,9 +803,9 @@ def UpdateProperties(self, properties, do_copy=False):
elif isinstance(value, dict):
self._properties[property] = value.copy()
else:
- raise TypeError, "Don't know how to copy a " + \
- value.__class__.__name__ + ' object for ' + \
- property + ' in ' + self.__class__.__name__
+ raise TypeError("Don't know how to copy a " + \
+ value.__class__.__name__ + ' object for ' + \
+ property + ' in ' + self.__class__.__name__)
else:
self._properties[property] = value
@@ -837,15 +836,15 @@ def AppendProperty(self, key, value):
# Schema validation.
if not key in self._schema:
- raise KeyError, key + ' not in ' + self.__class__.__name__
+ raise KeyError(key + ' not in ' + self.__class__.__name__)
(is_list, property_type, is_strong) = self._schema[key][0:3]
if not is_list:
- raise TypeError, key + ' of ' + self.__class__.__name__ + ' must be list'
+ raise TypeError(key + ' of ' + self.__class__.__name__ + ' must be list')
if not isinstance(value, property_type):
- raise TypeError, 'item of ' + key + ' of ' + self.__class__.__name__ + \
- ' must be ' + property_type.__name__ + ', not ' + \
- value.__class__.__name__
+ raise TypeError('item of ' + key + ' of ' + self.__class__.__name__ + \
+ ' must be ' + property_type.__name__ + ', not ' + \
+ value.__class__.__name__)
# If the property doesn't exist yet, create a new empty list to receive the
# item.
@@ -869,7 +868,7 @@ def VerifyHasRequiredProperties(self):
for property, attributes in self._schema.iteritems():
(is_list, property_type, is_strong, is_required) = attributes[0:4]
if is_required and not property in self._properties:
- raise KeyError, self.__class__.__name__ + ' requires ' + property
+ raise KeyError(self.__class__.__name__ + ' requires ' + property)
def _SetDefaultsFromSchema(self):
"""Assign object default values according to the schema. This will not
@@ -1143,16 +1142,16 @@ def _AddChildToDicts(self, child):
child_path = child.PathFromSourceTreeAndPath()
if child_path:
if child_path in self._children_by_path:
- raise ValueError, 'Found multiple children with path ' + child_path
+ raise ValueError('Found multiple children with path ' + child_path)
self._children_by_path[child_path] = child
if isinstance(child, PBXVariantGroup):
child_name = child._properties.get('name', None)
key = (child_name, child_path)
if key in self._variant_children_by_name_and_path:
- raise ValueError, 'Found multiple PBXVariantGroup children with ' + \
- 'name ' + str(child_name) + ' and path ' + \
- str(child_path)
+ raise ValueError('Found multiple PBXVariantGroup children with ' + \
+ 'name ' + str(child_name) + ' and path ' + \
+ str(child_path))
self._variant_children_by_name_and_path[key] = child
def AppendChild(self, child):
@@ -1508,9 +1507,12 @@ def __init__(self, properties=None, id=None, parent=None):
's': 'sourcecode.asm',
'storyboard': 'file.storyboard',
'strings': 'text.plist.strings',
+ 'swift': 'sourcecode.swift',
'ttf': 'file',
+ 'xcassets': 'folder.assetcatalog',
'xcconfig': 'text.xcconfig',
'xcdatamodel': 'wrapper.xcdatamodel',
+ 'xcdatamodeld':'wrapper.xcdatamodeld',
'xib': 'file.xib',
'y': 'sourcecode.yacc',
}
@@ -1605,7 +1607,7 @@ def ConfigurationNamed(self, name):
if configuration._properties['name'] == name:
return configuration
- raise KeyError, name
+ raise KeyError(name)
def DefaultConfiguration(self):
"""Convenience accessor to obtain the default XCBuildConfiguration."""
@@ -1662,7 +1664,7 @@ def GetBuildSetting(self, key):
value = configuration_value
else:
if value != configuration_value:
- raise ValueError, 'Variant values for ' + key
+ raise ValueError('Variant values for ' + key)
return value
@@ -1769,8 +1771,8 @@ def FileGroup(self, path):
# added, either as a child or deeper descendant. The second item should
# be a boolean indicating whether files should be added into hierarchical
# groups or one single flat group.
- raise NotImplementedError, \
- self.__class__.__name__ + ' must implement FileGroup'
+ raise NotImplementedError(
+ self.__class__.__name__ + ' must implement FileGroup')
def _AddPathToDict(self, pbxbuildfile, path):
"""Adds path to the dict tracking paths belonging to this build phase.
@@ -1779,7 +1781,7 @@ def _AddPathToDict(self, pbxbuildfile, path):
"""
if path in self._files_by_path:
- raise ValueError, 'Found multiple build files with path ' + path
+ raise ValueError('Found multiple build files with path ' + path)
self._files_by_path[path] = pbxbuildfile
def _AddBuildFileToDicts(self, pbxbuildfile, path=None):
@@ -1834,8 +1836,8 @@ def _AddBuildFileToDicts(self, pbxbuildfile, path=None):
# problem.
if xcfilelikeelement in self._files_by_xcfilelikeelement and \
self._files_by_xcfilelikeelement[xcfilelikeelement] != pbxbuildfile:
- raise ValueError, 'Found multiple build files for ' + \
- xcfilelikeelement.Name()
+ raise ValueError('Found multiple build files for ' + \
+ xcfilelikeelement.Name())
self._files_by_xcfilelikeelement[xcfilelikeelement] = pbxbuildfile
def AppendBuildFile(self, pbxbuildfile, path=None):
@@ -1999,8 +2001,8 @@ def SetDestination(self, path):
subfolder = 0
relative_path = path[1:]
else:
- raise ValueError, 'Can\'t use path %s in a %s' % \
- (path, self.__class__.__name__)
+ raise ValueError('Can\'t use path %s in a %s' % \
+ (path, self.__class__.__name__))
self._properties['dstPath'] = relative_path
self._properties['dstSubfolderSpec'] = subfolder
@@ -2236,10 +2238,16 @@ class PBXNativeTarget(XCTarget):
# Mapping from Xcode product-types to settings. The settings are:
# filetype : used for explicitFileType in the project file
# prefix : the prefix for the file name
- # suffix : the suffix for the filen ame
+ # suffix : the suffix for the file name
_product_filetypes = {
- 'com.apple.product-type.application': ['wrapper.application',
- '', '.app'],
+ 'com.apple.product-type.application': ['wrapper.application',
+ '', '.app'],
+ 'com.apple.product-type.application.watchapp': ['wrapper.application',
+ '', '.app'],
+ 'com.apple.product-type.watchkit-extension': ['wrapper.app-extension',
+ '', '.appex'],
+ 'com.apple.product-type.app-extension': ['wrapper.app-extension',
+ '', '.appex'],
'com.apple.product-type.bundle': ['wrapper.cfbundle',
'', '.bundle'],
'com.apple.product-type.framework': ['wrapper.framework',
@@ -2312,11 +2320,11 @@ def __init__(self, properties=None, id=None, parent=None,
if force_extension is not None:
# If it's a wrapper (bundle), set WRAPPER_EXTENSION.
+ # Extension override.
+ suffix = '.' + force_extension
if filetype.startswith('wrapper.'):
self.SetBuildSetting('WRAPPER_EXTENSION', force_extension)
else:
- # Extension override.
- suffix = '.' + force_extension
self.SetBuildSetting('EXECUTABLE_EXTENSION', force_extension)
if filetype.startswith('compiled.mach-o.executable'):
@@ -2732,8 +2740,53 @@ def AddOrGetProjectReference(self, other_pbxproject):
self._SetUpProductReferences(other_pbxproject, product_group, project_ref)
+ inherit_unique_symroot = self._AllSymrootsUnique(other_pbxproject, False)
+ targets = other_pbxproject.GetProperty('targets')
+ if all(self._AllSymrootsUnique(t, inherit_unique_symroot) for t in targets):
+ dir_path = project_ref._properties['path']
+ product_group._hashables.extend(dir_path)
+
return [product_group, project_ref]
+ def _AllSymrootsUnique(self, target, inherit_unique_symroot):
+ # Returns True if all configurations have a unique 'SYMROOT' attribute.
+ # The value of inherit_unique_symroot decides, if a configuration is assumed
+ # to inherit a unique 'SYMROOT' attribute from its parent, if it doesn't
+ # define an explicit value for 'SYMROOT'.
+ symroots = self._DefinedSymroots(target)
+ for s in self._DefinedSymroots(target):
+ if (s is not None and not self._IsUniqueSymrootForTarget(s) or
+ s is None and not inherit_unique_symroot):
+ return False
+ return True if symroots else inherit_unique_symroot
+
+ def _DefinedSymroots(self, target):
+ # Returns all values for the 'SYMROOT' attribute defined in all
+ # configurations for this target. If any configuration doesn't define the
+ # 'SYMROOT' attribute, None is added to the returned set. If all
+ # configurations don't define the 'SYMROOT' attribute, an empty set is
+ # returned.
+ config_list = target.GetProperty('buildConfigurationList')
+ symroots = set()
+ for config in config_list.GetProperty('buildConfigurations'):
+ setting = config.GetProperty('buildSettings')
+ if 'SYMROOT' in setting:
+ symroots.add(setting['SYMROOT'])
+ else:
+ symroots.add(None)
+ if len(symroots) == 1 and None in symroots:
+ return set()
+ return symroots
+
+ def _IsUniqueSymrootForTarget(self, symroot):
+ # This method returns True if all configurations in target contain a
+ # 'SYMROOT' attribute that is unique for the given target. A value is
+ # unique, if the Xcode macro '$SRCROOT' appears in it in any form.
+ uniquifier = ['$SRCROOT', '$(SRCROOT)']
+ if any(x in symroot for x in uniquifier):
+ return True
+ return False
+
def _SetUpProductReferences(self, other_pbxproject, product_group,
project_ref):
# TODO(mark): This only adds references to products in other_pbxproject
@@ -2802,7 +2855,7 @@ def CompareProducts(x, y, remote_products):
product_group = ref_dict['ProductGroup']
product_group._properties['children'] = sorted(
product_group._properties['children'],
- cmp=lambda x, y: CompareProducts(x, y, remote_products))
+ cmp=lambda x, y, rp=remote_products: CompareProducts(x, y, rp))
class XCProjectFile(XCObject):
@@ -2810,27 +2863,10 @@ class XCProjectFile(XCObject):
_schema.update({
'archiveVersion': [0, int, 0, 1, 1],
'classes': [0, dict, 0, 1, {}],
- 'objectVersion': [0, int, 0, 1, 45],
+ 'objectVersion': [0, int, 0, 1, 46],
'rootObject': [0, PBXProject, 1, 1],
})
- def SetXcodeVersion(self, version):
- version_to_object_version = {
- '2.4': 45,
- '3.0': 45,
- '3.1': 45,
- '3.2': 46,
- }
- if not version in version_to_object_version:
- supported_str = ', '.join(sorted(version_to_object_version.keys()))
- raise Exception(
- 'Unsupported Xcode version %s (supported: %s)' %
- ( version, supported_str ) )
- compatibility_version = 'Xcode %s' % version
- self._properties['rootObject'].SetProperty('compatibilityVersion',
- compatibility_version)
- self.SetProperty('objectVersion', version_to_object_version[version]);
-
def ComputeIDs(self, recursive=True, overwrite=True, hash=None):
# Although XCProjectFile is implemented here as an XCObject, it's not a
# proper object in the Xcode sense, and it certainly doesn't have its own
diff --git a/tools/gyp/pylintrc b/tools/gyp/pylintrc
deleted file mode 100644
index d7c23d2a211d0a..00000000000000
--- a/tools/gyp/pylintrc
+++ /dev/null
@@ -1,307 +0,0 @@
-[MASTER]
-
-# Specify a configuration file.
-#rcfile=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Profiled execution.
-profile=no
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-
-[MESSAGES CONTROL]
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once).
-# C0103: Invalid name "NN" (should match [a-z_][a-z0-9_]{2,30}$)
-# C0111: Missing docstring
-# C0302: Too many lines in module (NN)
-# R0902: Too many instance attributes (N/7)
-# R0903: Too few public methods (N/2)
-# R0904: Too many public methods (NN/20)
-# R0912: Too many branches (NN/12)
-# R0913: Too many arguments (N/5)
-# R0914: Too many local variables (NN/15)
-# R0915: Too many statements (NN/50)
-# W0141: Used builtin function 'map'
-# W0142: Used * or ** magic
-# W0232: Class has no __init__ method
-# W0511: TODO
-# W0603: Using the global statement
-#
-# These should be enabled eventually:
-# C0112: Empty docstring
-# C0301: Line too long (NN/80)
-# C0321: More than one statement on single line
-# C0322: Operator not preceded by a space
-# C0323: Operator not followed by a space
-# C0324: Comma not followed by a space
-# E0101: Explicit return in __init__
-# E0102: function already defined line NN
-# E1002: Use of super on an old style class
-# E1101: Instance of 'XX' has no 'YY' member
-# E1103: Instance of 'XX' has no 'XX' member (but some types could not be inferred)
-# E0602: Undefined variable 'XX'
-# F0401: Unable to import 'XX'
-# R0201: Method could be a function
-# R0801: Similar lines in N files
-# W0102: Dangerous default value {} as argument
-# W0104: Statement seems to have no effect
-# W0105: String statement has no effect
-# W0108: Lambda may not be necessary
-# W0201: Attribute 'XX' defined outside __init__
-# W0212: Access to a protected member XX of a client class
-# W0221: Arguments number differs from overridden method
-# W0223: Method 'XX' is abstract in class 'YY' but is not overridden
-# W0231: __init__ method from base class 'XX' is not called
-# W0301: Unnecessary semicolon
-# W0311: Bad indentation. Found NN spaces, expected NN
-# W0401: Wildcard import XX
-# W0402: Uses of a deprecated module 'string'
-# W0403: Relative import 'XX', should be 'YY.XX'
-# W0404: Reimport 'XX' (imported line NN)
-# W0601: Global variable 'XX' undefined at the module level
-# W0602: Using global for 'XX' but no assignment is done
-# W0611: Unused import pprint
-# W0612: Unused variable 'XX'
-# W0613: Unused argument 'XX'
-# W0614: Unused import XX from wildcard import
-# W0621: Redefining name 'XX' from outer scope (line NN)
-# W0622: Redefining built-in 'NN'
-# W0631: Using possibly undefined loop variable 'XX'
-# W0701: Raising a string exception
-# W0702: No exception type(s) specified
-disable=C0103,C0111,C0302,R0902,R0903,R0904,R0912,R0913,R0914,R0915,W0141,W0142,W0232,W0511,W0603,C0112,C0301,C0321,C0322,C0323,C0324,E0101,E0102,E1002,E1101,E1103,E0602,F0401,R0201,R0801,W0102,W0104,W0105,W0108,W0201,W0212,W0221,W0223,W0231,W0301,W0311,W0401,W0402,W0403,W0404,W0601,W0602,W0611,W0612,W0613,W0614,W0621,W0622,W0631,W0701,W0702
-
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html
-output-format=text
-
-# Include message's id in output
-include-ids=yes
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]".
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=no
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Add a comment according to your evaluation note. This is used by the global
-# evaluation report (RP0004).
-comment=no
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the beginning of the name of dummy variables
-# (i.e. not used).
-dummy-variables-rgx=_|dummy
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-
-[TYPECHECK]
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of classes names for which member attributes should not be checked
-# (useful for classes with attributes dynamically set).
-ignored-classes=SQLObject
-
-# When zope mode is activated, add a predefined set of Zope acquired attributes
-# to generated-members.
-zope=no
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E0201 when accessed. Python regular
-# expressions are accepted.
-generated-members=REQUEST,acl_users,aq_parent
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,XXX,TODO
-
-
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=80
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
-
-
-[BASIC]
-
-# Required attributes for module, separated by a comma
-required-attributes=
-
-# List of builtins function names that should not be used, separated by a comma
-bad-functions=map,filter,apply,input
-
-# Regular expression which should only match correct module names
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Regular expression which should only match correct module level names
-const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Regular expression which should only match correct class names
-class-rgx=[A-Z_][a-zA-Z0-9]+$
-
-# Regular expression which should only match correct function names
-function-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct method names
-method-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct instance attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct argument names
-argument-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct variable names
-variable-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct list comprehension /
-# generator expression variable names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=i,j,k,ex,Run,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata
-
-# Regular expression which should only match functions or classes name which do
-# not require a docstring
-no-docstring-rgx=__.*__
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=5
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore
-ignored-argument-names=_.*
-
-# Maximum number of locals for function / method body
-max-locals=15
-
-# Maximum number of return / yield for function / method body
-max-returns=6
-
-# Maximum number of branch for function / method body
-max-branchs=12
-
-# Maximum number of statements in function / method body
-max-statements=50
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=2
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-
-[CLASSES]
-
-# List of interface methods to ignore, separated by a comma. This is used for
-# instance to not check methods defines in Zope's Interface base class.
-ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=Exception
diff --git a/tools/gyp/test/actions-bare/gyptest-bare.py b/tools/gyp/test/actions-bare/gyptest-bare.py
new file mode 100755
index 00000000000000..e3d6db1029b39f
--- /dev/null
+++ b/tools/gyp/test/actions-bare/gyptest-bare.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 actions which are not depended on by other targets get executed.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('bare.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+test.build('bare.gyp', chdir='relocate/src')
+
+file_content = 'Hello from bare.py\n'
+
+test.built_file_must_match('out.txt', file_content, chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/actions-bare/src/bare.gyp b/tools/gyp/test/actions-bare/src/bare.gyp
new file mode 100644
index 00000000000000..3d28f099d44789
--- /dev/null
+++ b/tools/gyp/test/actions-bare/src/bare.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2009 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': 'bare',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'action1',
+ 'inputs': [
+ 'bare.py',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/out.txt',
+ ],
+ 'action': ['python', 'bare.py', '<(PRODUCT_DIR)/out.txt'],
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions-bare/src/bare.py b/tools/gyp/test/actions-bare/src/bare.py
new file mode 100755
index 00000000000000..12307500f2dbc4
--- /dev/null
+++ b/tools/gyp/test/actions-bare/src/bare.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+f = open(sys.argv[1], 'wb')
+f.write('Hello from bare.py\n')
+f.close()
diff --git a/tools/gyp/test/actions-depfile/depfile.gyp b/tools/gyp/test/actions-depfile/depfile.gyp
new file mode 100644
index 00000000000000..dc2397de0544c7
--- /dev/null
+++ b/tools/gyp/test/actions-depfile/depfile.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2015 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': 'depfile_target',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'depfile_action',
+ 'inputs': [
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'output.txt',
+ ],
+ 'depfile': 'depfile.d',
+ 'action': [ ]
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions-depfile/gyptest-all.py b/tools/gyp/test/actions-depfile/gyptest-all.py
new file mode 100644
index 00000000000000..23f6f4a056707b
--- /dev/null
+++ b/tools/gyp/test/actions-depfile/gyptest-all.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Verifies that depfile fields are output in ninja rules."""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+if test.format == 'ninja':
+ test.run_gyp('depfile.gyp')
+ contents = open(test.built_file_path('obj/depfile_target.ninja')).read()
+
+ expected = 'depfile = depfile.d'
+ if expected not in contents:
+ test.fail_test()
+ test.pass_test()
diff --git a/tools/gyp/test/actions-depfile/input.txt b/tools/gyp/test/actions-depfile/input.txt
new file mode 100644
index 00000000000000..3f9177e45e805c
--- /dev/null
+++ b/tools/gyp/test/actions-depfile/input.txt
@@ -0,0 +1 @@
+input
diff --git a/tools/gyp/test/actions-multiple/gyptest-all.py b/tools/gyp/test/actions-multiple/gyptest-all.py
new file mode 100755
index 00000000000000..2a083de9b054a4
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/gyptest-all.py
@@ -0,0 +1,72 @@
+#!/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 two actions can be attached to the same input files.
+"""
+
+import sys
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('actions.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+# Test of fine-grained dependencies for generators that can build individual
+# files on demand.
+# In particular:
+# - TargetA depends on TargetB.
+# - TargetA and TargetB are 'none' type with actions attached.
+# - TargetA has multiple actions.
+# - An output from one of the actions in TargetA (not the first listed),
+# is requested as the build target.
+# Ensure that TargetB gets built.
+#
+# This sub-test can only be done with generators/build tools that can
+# be asked to build individual files rather than whole targets (make, ninja).
+if test.format in ['make', 'ninja']:
+ # Select location of target based on generator.
+ if test.format == 'make':
+ target = 'multi2.txt'
+ elif test.format == 'ninja':
+ if sys.platform in ['win32', 'cygwin']:
+ target = '..\\..\\multi2.txt'
+ else:
+ target = '../../multi2.txt'
+ else:
+ assert False
+ test.build('actions.gyp', chdir='relocate/src', target=target)
+ test.must_contain('relocate/src/multi2.txt', 'hello there')
+ test.must_contain('relocate/src/multi_dep.txt', 'hello there')
+
+
+# Test that two actions can be attached to the same inputs.
+test.build('actions.gyp', test.ALL, chdir='relocate/src')
+test.must_contain('relocate/src/output1.txt', 'hello there')
+test.must_contain('relocate/src/output2.txt', 'hello there')
+test.must_contain('relocate/src/output3.txt', 'hello there')
+test.must_contain('relocate/src/output4.txt', 'hello there')
+
+# Test that process_outputs_as_sources works in conjuction with merged
+# actions.
+test.run_built_executable(
+ 'multiple_action_source_filter',
+ chdir='relocate/src',
+ stdout=(
+ '{\n'
+ 'bar\n'
+ 'car\n'
+ 'dar\n'
+ 'ear\n'
+ '}\n'
+ ),
+)
+
+
+test.pass_test()
diff --git a/tools/gyp/test/actions-multiple/src/actions.gyp b/tools/gyp/test/actions-multiple/src/actions.gyp
new file mode 100644
index 00000000000000..c70a58f702ff38
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/src/actions.gyp
@@ -0,0 +1,226 @@
+# 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.
+
+{
+ 'variables': {
+ # Have a long string so that actions will exceed xp 512 character
+ # command limit on xp.
+ 'long_string':
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ 'abcdefghijklmnopqrstuvwxyz0123456789'
+ },
+ 'targets': [
+ {
+ 'target_name': 'multiple_action_target',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'action1',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'output1.txt',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action2',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'output2.txt',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action3',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'output3.txt',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action4',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'output4.txt',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ {
+ 'target_name': 'multiple_action_source_filter',
+ 'type': 'executable',
+ 'sources': [
+ 'main.c',
+ # TODO(bradnelson): add foo.c here once this issue is fixed:
+ # http://code.google.com/p/gyp/issues/detail?id=175
+ ],
+ 'actions': [
+ {
+ 'action_name': 'action1',
+ 'inputs': [
+ 'foo.c',
+ 'filter.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/output1.c',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'action': [
+ 'python', 'filter.py', 'foo', 'bar', 'foo.c', '<@(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action2',
+ 'inputs': [
+ 'foo.c',
+ 'filter.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/output2.c',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'action': [
+ 'python', 'filter.py', 'foo', 'car', 'foo.c', '<@(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action3',
+ 'inputs': [
+ 'foo.c',
+ 'filter.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/output3.c',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'action': [
+ 'python', 'filter.py', 'foo', 'dar', 'foo.c', '<@(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action4',
+ 'inputs': [
+ 'foo.c',
+ 'filter.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/output4.c',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'action': [
+ 'python', 'filter.py', 'foo', 'ear', 'foo.c', '<@(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ {
+ 'target_name': 'multiple_dependent_target',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'action1',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'multi1.txt',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'action2',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'multi2.txt',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ 'dependencies': [
+ 'multiple_required_target',
+ ],
+ },
+ {
+ 'target_name': 'multiple_required_target',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'multi_dep',
+ 'inputs': [
+ 'copy.py',
+ 'input.txt',
+ ],
+ 'outputs': [
+ 'multi_dep.txt',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'action': [
+ 'python', '<@(_inputs)', '<(_outputs)', '<(long_string)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions-multiple/src/copy.py b/tools/gyp/test/actions-multiple/src/copy.py
new file mode 100755
index 00000000000000..07746793806493
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/src/copy.py
@@ -0,0 +1,9 @@
+#!/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.
+
+import shutil
+import sys
+
+shutil.copyfile(sys.argv[1], sys.argv[2])
diff --git a/tools/gyp/test/actions-multiple/src/filter.py b/tools/gyp/test/actions-multiple/src/filter.py
new file mode 100755
index 00000000000000..f61a5fa59a9f1f
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/src/filter.py
@@ -0,0 +1,12 @@
+#!/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.
+
+
+import sys
+
+data = open(sys.argv[3], 'r').read()
+fh = open(sys.argv[4], 'w')
+fh.write(data.replace(sys.argv[1], sys.argv[2]))
+fh.close()
diff --git a/tools/gyp/test/actions-multiple/src/foo.c b/tools/gyp/test/actions-multiple/src/foo.c
new file mode 100644
index 00000000000000..23c4ef7f26fc47
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/src/foo.c
@@ -0,0 +1,11 @@
+/*
+ * 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.
+ */
+
+#include
+
+void foo(void) {
+ printf("foo\n");
+}
diff --git a/tools/gyp/test/actions-multiple/src/input.txt b/tools/gyp/test/actions-multiple/src/input.txt
new file mode 100644
index 00000000000000..c7c7da3c64e86c
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/src/input.txt
@@ -0,0 +1 @@
+hello there
diff --git a/tools/gyp/test/actions-multiple/src/main.c b/tools/gyp/test/actions-multiple/src/main.c
new file mode 100644
index 00000000000000..0a420b9034369f
--- /dev/null
+++ b/tools/gyp/test/actions-multiple/src/main.c
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#include
+
+void bar(void);
+void car(void);
+void dar(void);
+void ear(void);
+
+int main() {
+ printf("{\n");
+ bar();
+ car();
+ dar();
+ ear();
+ printf("}\n");
+ return 0;
+}
diff --git a/tools/gyp/test/actions-none/gyptest-none.py b/tools/gyp/test/actions-none/gyptest-none.py
new file mode 100755
index 00000000000000..933cfad30c6619
--- /dev/null
+++ b/tools/gyp/test/actions-none/gyptest-none.py
@@ -0,0 +1,24 @@
+#!/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 actions can be in 'none' type targets with source files.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('none_with_source_files.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+test.build('none_with_source_files.gyp', chdir='relocate/src')
+
+file_content = 'foo.cc\n'
+
+test.built_file_must_match('fake.out', file_content, chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/actions-none/src/fake_cross.py b/tools/gyp/test/actions-none/src/fake_cross.py
new file mode 100644
index 00000000000000..2913f66a68d229
--- /dev/null
+++ b/tools/gyp/test/actions-none/src/fake_cross.py
@@ -0,0 +1,12 @@
+#!/usr/bin/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 sys
+
+fh = open(sys.argv[-1], 'wb')
+for filename in sys.argv[1:-1]:
+ fh.write(open(filename).read())
+fh.close()
diff --git a/tools/gyp/test/actions-none/src/foo.cc b/tools/gyp/test/actions-none/src/foo.cc
new file mode 100644
index 00000000000000..c6c61745badb78
--- /dev/null
+++ b/tools/gyp/test/actions-none/src/foo.cc
@@ -0,0 +1 @@
+foo.cc
diff --git a/tools/gyp/test/actions-none/src/none_with_source_files.gyp b/tools/gyp/test/actions-none/src/none_with_source_files.gyp
new file mode 100644
index 00000000000000..e2aaebc10a6d5e
--- /dev/null
+++ b/tools/gyp/test/actions-none/src/none_with_source_files.gyp
@@ -0,0 +1,35 @@
+# 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.
+
+# Test that 'none' type targets can have .cc files in them.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'none_with_sources',
+ 'type': 'none',
+ 'msvs_cygwin_shell': 0,
+ 'sources': [
+ 'foo.cc',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'fake_cross',
+ 'inputs': [
+ 'fake_cross.py',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/fake.out',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<@(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions-subdir/gyptest-action.py b/tools/gyp/test/actions-subdir/gyptest-action.py
new file mode 100755
index 00000000000000..09cfef1893d56f
--- /dev/null
+++ b/tools/gyp/test/actions-subdir/gyptest-action.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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.
+
+"""
+Test actions that output to PRODUCT_DIR.
+"""
+
+import TestGyp
+
+# TODO fix this for xcode: http://code.google.com/p/gyp/issues/detail?id=88
+test = TestGyp.TestGyp(formats=['!xcode'])
+
+test.run_gyp('none.gyp', chdir='src')
+
+test.build('none.gyp', test.ALL, chdir='src')
+
+file_content = 'Hello from make-file.py\n'
+subdir_file_content = 'Hello from make-subdir-file.py\n'
+
+test.built_file_must_match('file.out', file_content, chdir='src')
+test.built_file_must_match('subdir_file.out', subdir_file_content, chdir='src')
+
+test.pass_test()
diff --git a/tools/gyp/test/actions-subdir/src/make-file.py b/tools/gyp/test/actions-subdir/src/make-file.py
new file mode 100755
index 00000000000000..74e55811d233b0
--- /dev/null
+++ b/tools/gyp/test/actions-subdir/src/make-file.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = 'Hello from make-file.py\n'
+
+open(sys.argv[1], 'wb').write(contents)
diff --git a/tools/gyp/test/actions-subdir/src/none.gyp b/tools/gyp/test/actions-subdir/src/none.gyp
new file mode 100644
index 00000000000000..23f8d25a53e44a
--- /dev/null
+++ b/tools/gyp/test/actions-subdir/src/none.gyp
@@ -0,0 +1,31 @@
+# Copyright (c) 2009 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': 'file',
+ 'type': 'none',
+ 'msvs_cygwin_shell': 0,
+ 'actions': [
+ {
+ 'action_name': 'make-file',
+ 'inputs': [
+ 'make-file.py',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/file.out',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ }
+ ],
+ 'dependencies': [
+ 'subdir/subdir.gyp:subdir_file',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py b/tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py
new file mode 100755
index 00000000000000..80ce19ae0ea0b7
--- /dev/null
+++ b/tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = 'Hello from make-subdir-file.py\n'
+
+open(sys.argv[1], 'wb').write(contents)
diff --git a/tools/gyp/test/actions-subdir/src/subdir/subdir.gyp b/tools/gyp/test/actions-subdir/src/subdir/subdir.gyp
new file mode 100644
index 00000000000000..0315d4eb83fefc
--- /dev/null
+++ b/tools/gyp/test/actions-subdir/src/subdir/subdir.gyp
@@ -0,0 +1,28 @@
+# Copyright (c) 2009 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': 'subdir_file',
+ 'type': 'none',
+ 'msvs_cygwin_shell': 0,
+ 'actions': [
+ {
+ 'action_name': 'make-subdir-file',
+ 'inputs': [
+ 'make-subdir-file.py',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/subdir_file.out',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions/generated-header/action.py b/tools/gyp/test/actions/generated-header/action.py
new file mode 100644
index 00000000000000..9be98798d629cd
--- /dev/null
+++ b/tools/gyp/test/actions/generated-header/action.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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
+
+outfile = sys.argv[1]
+open(outfile, 'w').write('const char kFoo[] = "%s";' % sys.argv[2])
diff --git a/tools/gyp/test/actions/generated-header/main.cc b/tools/gyp/test/actions/generated-header/main.cc
new file mode 100644
index 00000000000000..7973781bc6e500
--- /dev/null
+++ b/tools/gyp/test/actions/generated-header/main.cc
@@ -0,0 +1,7 @@
+#include
+
+#include "MyHeader.h"
+
+int main() {
+ printf("%s\n", kFoo);
+}
diff --git a/tools/gyp/test/actions/generated-header/test.gyp b/tools/gyp/test/actions/generated-header/test.gyp
new file mode 100644
index 00000000000000..209b951ef6cb62
--- /dev/null
+++ b/tools/gyp/test/actions/generated-header/test.gyp
@@ -0,0 +1,34 @@
+# Copyright (c) 2013 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': 'generate_header',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'inputs': [ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/MyHeader.h',
+ ],
+ 'action_name': 'generate header',
+ 'action': ['python', './action.py',
+ '<(SHARED_INTERMEDIATE_DIR)/MyHeader.h', 'foobar output' ],
+ },
+ ],
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'dependencies': [
+ 'generate_header',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'sources': [ 'main.cc' ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions/gyptest-all.py b/tools/gyp/test/actions/gyptest-all.py
new file mode 100755
index 00000000000000..ea555550a20804
--- /dev/null
+++ b/tools/gyp/test/actions/gyptest-all.py
@@ -0,0 +1,102 @@
+#!/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 simple actions when using an explicit build target of 'all'.
+"""
+
+import glob
+import os
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_all')
+
+test.run_gyp('actions.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+# Some gyp files use an action that mentions an output but never
+# writes it as a means to making the action run on every build. That
+# doesn't mesh well with ninja's semantics. TODO(evan): figure out
+# how to work always-run actions in to ninja.
+# Android also can't do this as it doesn't have order-only dependencies.
+if test.format in ['ninja', 'android', 'xcode-ninja']:
+ test.build('actions.gyp', test.ALL, chdir='relocate/src')
+else:
+ # Test that an "always run" action increases a counter on multiple
+ # invocations, and that a dependent action updates in step.
+ test.build('actions.gyp', test.ALL, chdir='relocate/src')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '1')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '1')
+ test.build('actions.gyp', test.ALL, chdir='relocate/src')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2')
+
+ # The "always run" action only counts to 2, but the dependent target
+ # will count forever if it's allowed to run. This verifies that the
+ # dependent target only runs when the "always run" action generates
+ # new output, not just because the "always run" ran.
+ test.build('actions.gyp', test.ALL, chdir='relocate/src')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2')
+
+expect = """\
+Hello from program.c
+Hello from make-prog1.py
+Hello from make-prog2.py
+"""
+
+if test.format == 'xcode':
+ chdir = 'relocate/src/subdir1'
+else:
+ chdir = 'relocate/src'
+test.run_built_executable('program', chdir=chdir, stdout=expect)
+
+
+test.must_match('relocate/src/subdir2/file.out', "Hello from make-file.py\n")
+
+
+expect = "Hello from generate_main.py\n"
+
+if test.format == 'xcode':
+ chdir = 'relocate/src/subdir3'
+else:
+ chdir = 'relocate/src'
+test.run_built_executable('null_input', chdir=chdir, stdout=expect)
+
+
+# Clean out files which may have been created if test.ALL was run.
+def clean_dep_files():
+ for file in (glob.glob('relocate/src/dep_*.txt') +
+ glob.glob('relocate/src/deps_all_done_*.txt')):
+ if os.path.exists(file):
+ os.remove(file)
+
+# Confirm our clean.
+clean_dep_files()
+test.must_not_exist('relocate/src/dep_1.txt')
+test.must_not_exist('relocate/src/deps_all_done_first_123.txt')
+
+# Make sure all deps finish before an action is run on a 'None' target.
+# If using the Make builder, add -j to make things more difficult.
+arguments = []
+if test.format == 'make':
+ arguments = ['-j']
+test.build('actions.gyp', 'action_with_dependencies_123', chdir='relocate/src',
+ arguments=arguments)
+test.must_exist('relocate/src/deps_all_done_first_123.txt')
+
+# Try again with a target that has deps in reverse. Output files from
+# previous tests deleted. Confirm this execution did NOT run the ALL
+# target which would mess up our dep tests.
+clean_dep_files()
+test.build('actions.gyp', 'action_with_dependencies_321', chdir='relocate/src',
+ arguments=arguments)
+test.must_exist('relocate/src/deps_all_done_first_321.txt')
+test.must_not_exist('relocate/src/deps_all_done_first_123.txt')
+
+
+test.pass_test()
diff --git a/tools/gyp/test/actions/gyptest-default.py b/tools/gyp/test/actions/gyptest-default.py
new file mode 100755
index 00000000000000..ae8d6b3f19f8dc
--- /dev/null
+++ b/tools/gyp/test/actions/gyptest-default.py
@@ -0,0 +1,69 @@
+#!/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 simple actions when using the default build target.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_default')
+
+test.run_gyp('actions.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+# Some gyp files use an action that mentions an output but never
+# writes it as a means to making the action run on every build. That
+# doesn't mesh well with ninja's semantics. TODO(evan): figure out
+# how to work always-run actions in to ninja.
+# Android also can't do this as it doesn't have order-only dependencies.
+if test.format in ['ninja', 'android', 'xcode-ninja']:
+ test.build('actions.gyp', test.ALL, chdir='relocate/src')
+else:
+ # Test that an "always run" action increases a counter on multiple
+ # invocations, and that a dependent action updates in step.
+ test.build('actions.gyp', chdir='relocate/src')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '1')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '1')
+ test.build('actions.gyp', chdir='relocate/src')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2')
+
+ # The "always run" action only counts to 2, but the dependent target
+ # will count forever if it's allowed to run. This verifies that the
+ # dependent target only runs when the "always run" action generates
+ # new output, not just because the "always run" ran.
+ test.build('actions.gyp', test.ALL, chdir='relocate/src')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter.txt', '2')
+ test.must_match('relocate/src/subdir1/actions-out/action-counter_2.txt', '2')
+
+expect = """\
+Hello from program.c
+Hello from make-prog1.py
+Hello from make-prog2.py
+"""
+
+if test.format == 'xcode':
+ chdir = 'relocate/src/subdir1'
+else:
+ chdir = 'relocate/src'
+test.run_built_executable('program', chdir=chdir, stdout=expect)
+
+
+test.must_match('relocate/src/subdir2/file.out', "Hello from make-file.py\n")
+
+
+expect = "Hello from generate_main.py\n"
+
+if test.format == 'xcode':
+ chdir = 'relocate/src/subdir3'
+else:
+ chdir = 'relocate/src'
+test.run_built_executable('null_input', chdir=chdir, stdout=expect)
+
+
+test.pass_test()
diff --git a/tools/gyp/test/actions/gyptest-errors.py b/tools/gyp/test/actions/gyptest-errors.py
new file mode 100755
index 00000000000000..e1ef883e1e852d
--- /dev/null
+++ b/tools/gyp/test/actions/gyptest-errors.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 behavior for different action configuration errors:
+exit status of 1, and the expected error message must be in stderr.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_errors')
+
+
+test.run_gyp('action_missing_name.gyp', chdir='src', status=1, stderr=None)
+expect = [
+ "Anonymous action in target broken_actions2. An action must have an 'action_name' field.",
+]
+test.must_contain_all_lines(test.stderr(), expect)
+
+
+test.pass_test()
diff --git a/tools/gyp/test/actions/gyptest-generated-header.py b/tools/gyp/test/actions/gyptest-generated-header.py
new file mode 100644
index 00000000000000..fa680ab3bea477
--- /dev/null
+++ b/tools/gyp/test/actions/gyptest-generated-header.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 dependencies on generated headers work, even if the header has
+a mixed-case file name.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+if test.format == 'android':
+ # This test currently fails on android. Investigate why, fix the issues
+ # responsible, and reenable this test on android. See bug:
+ # https://code.google.com/p/gyp/issues/detail?id=436
+ test.skip_test(message='Test fails on android. Fix and reenable.\n')
+
+CHDIR = 'generated-header'
+
+test.run_gyp('test.gyp', chdir=CHDIR)
+test.build('test.gyp', 'program', chdir=CHDIR)
+test.up_to_date('test.gyp', 'program', chdir=CHDIR)
+
+expect = 'foobar output\n'
+test.run_built_executable('program', chdir=CHDIR, stdout=expect)
+
+# Change what's written to the generated header, regyp and rebuild, and check
+# that the change makes it to the executable and that the build is clean.
+test.sleep()
+test.write('generated-header/test.gyp',
+ test.read('generated-header/test.gyp').replace('foobar', 'barbaz'))
+
+test.run_gyp('test.gyp', chdir=CHDIR)
+test.build('test.gyp', 'program', chdir=CHDIR)
+test.up_to_date('test.gyp', 'program', chdir=CHDIR)
+
+expect = 'barbaz output\n'
+test.run_built_executable('program', chdir=CHDIR, stdout=expect)
+
+test.pass_test()
diff --git a/tools/gyp/test/actions/src/action_missing_name.gyp b/tools/gyp/test/actions/src/action_missing_name.gyp
new file mode 100644
index 00000000000000..00424c35a11502
--- /dev/null
+++ b/tools/gyp/test/actions/src/action_missing_name.gyp
@@ -0,0 +1,24 @@
+# Copyright (c) 2009 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': 'broken_actions2',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'inputs': [
+ 'no_name.input',
+ ],
+ 'action': [
+ 'python',
+ '-c',
+ 'print \'missing name\'',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions/src/actions.gyp b/tools/gyp/test/actions/src/actions.gyp
new file mode 100644
index 00000000000000..5d2db1955e2db1
--- /dev/null
+++ b/tools/gyp/test/actions/src/actions.gyp
@@ -0,0 +1,114 @@
+# Copyright (c) 2009 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': 'pull_in_all_actions',
+ 'type': 'none',
+ 'dependencies': [
+ 'subdir1/executable.gyp:*',
+ 'subdir2/none.gyp:*',
+ 'subdir3/null_input.gyp:*',
+ ],
+ },
+ {
+ 'target_name': 'depend_on_always_run_action',
+ 'type': 'none',
+ 'dependencies': [ 'subdir1/executable.gyp:counter' ],
+ 'actions': [
+ {
+ 'action_name': 'use_always_run_output',
+ 'inputs': [
+ 'subdir1/actions-out/action-counter.txt',
+ 'subdir1/counter.py',
+ ],
+ 'outputs': [
+ 'subdir1/actions-out/action-counter_2.txt',
+ ],
+ 'action': [
+ 'python', 'subdir1/counter.py', '<(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+
+ # Three deps which don't finish immediately.
+ # Each one has a small delay then creates a file.
+ # Delays are 1.0, 1.1, and 2.0 seconds.
+ {
+ 'target_name': 'dep_1',
+ 'type': 'none',
+ 'actions': [{
+ 'inputs': [ 'actions.gyp' ],
+ 'outputs': [ 'dep_1.txt' ],
+ 'action_name': 'dep_1',
+ 'action': [ 'python', '-c',
+ 'import time; time.sleep(1); open(\'dep_1.txt\', \'w\')' ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }],
+ },
+ {
+ 'target_name': 'dep_2',
+ 'type': 'none',
+ 'actions': [{
+ 'inputs': [ 'actions.gyp' ],
+ 'outputs': [ 'dep_2.txt' ],
+ 'action_name': 'dep_2',
+ 'action': [ 'python', '-c',
+ 'import time; time.sleep(1.1); open(\'dep_2.txt\', \'w\')' ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }],
+ },
+ {
+ 'target_name': 'dep_3',
+ 'type': 'none',
+ 'actions': [{
+ 'inputs': [ 'actions.gyp' ],
+ 'outputs': [ 'dep_3.txt' ],
+ 'action_name': 'dep_3',
+ 'action': [ 'python', '-c',
+ 'import time; time.sleep(2.0); open(\'dep_3.txt\', \'w\')' ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }],
+ },
+
+ # An action which assumes the deps have completed.
+ # Does NOT list the output files of it's deps as inputs.
+ # On success create the file deps_all_done_first.txt.
+ {
+ 'target_name': 'action_with_dependencies_123',
+ 'type': 'none',
+ 'dependencies': [ 'dep_1', 'dep_2', 'dep_3' ],
+ 'actions': [{
+ 'inputs': [ 'actions.gyp' ],
+ 'outputs': [ 'deps_all_done_first_123.txt' ],
+ 'action_name': 'action_with_dependencies_123',
+ 'action': [ 'python', 'confirm-dep-files.py', '<(_outputs)' ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }],
+ },
+ # Same as above but with deps in reverse.
+ {
+ 'target_name': 'action_with_dependencies_321',
+ 'type': 'none',
+ 'dependencies': [ 'dep_3', 'dep_2', 'dep_1' ],
+ 'actions': [{
+ 'inputs': [ 'actions.gyp' ],
+ 'outputs': [ 'deps_all_done_first_321.txt' ],
+ 'action_name': 'action_with_dependencies_321',
+ 'action': [ 'python', 'confirm-dep-files.py', '<(_outputs)' ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }],
+ },
+
+ ],
+}
diff --git a/tools/gyp/test/actions/src/confirm-dep-files.py b/tools/gyp/test/actions/src/confirm-dep-files.py
new file mode 100755
index 00000000000000..3b8463057df50d
--- /dev/null
+++ b/tools/gyp/test/actions/src/confirm-dep-files.py
@@ -0,0 +1,21 @@
+#!/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.
+
+"""Confirms presence of files generated by our targets we depend on.
+If they exist, create a new file.
+
+Note target's input files are explicitly NOT defined in the gyp file
+so they can't easily be passed to this script as args.
+"""
+
+import os
+import sys
+
+outfile = sys.argv[1] # Example value we expect: deps_all_done_first_123.txt
+if (os.path.exists("dep_1.txt") and
+ os.path.exists("dep_2.txt") and
+ os.path.exists("dep_3.txt")):
+ open(outfile, "w")
diff --git a/tools/gyp/test/actions/src/subdir1/counter.py b/tools/gyp/test/actions/src/subdir1/counter.py
new file mode 100755
index 00000000000000..d888f2e803bc94
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir1/counter.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# 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.
+
+import sys
+import time
+
+output = sys.argv[1]
+persistoutput = "%s.persist" % sys.argv[1]
+
+count = 0
+try:
+ count = open(persistoutput, 'r').read()
+except:
+ pass
+count = int(count) + 1
+
+if len(sys.argv) > 2:
+ max_count = int(sys.argv[2])
+ if count > max_count:
+ count = max_count
+
+oldcount = 0
+try:
+ oldcount = open(output, 'r').read()
+except:
+ pass
+
+# Save the count in a file that is undeclared, and thus hidden, to gyp. We need
+# to do this because, prior to running commands, some build systems deletes
+# any declared outputs, so we would lose our count if we just wrote to the
+# given output file.
+open(persistoutput, 'w').write('%d' % (count))
+
+# Only write the given output file if the count has changed.
+if int(oldcount) != count:
+ open(output, 'w').write('%d' % (count))
+ # Sleep so the next run changes the file time sufficiently to make the build
+ # detect the file as changed.
+ time.sleep(1)
+
+sys.exit(0)
diff --git a/tools/gyp/test/actions/src/subdir1/executable.gyp b/tools/gyp/test/actions/src/subdir1/executable.gyp
new file mode 100644
index 00000000000000..6a1ce4f91e0faa
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir1/executable.gyp
@@ -0,0 +1,74 @@
+# Copyright (c) 2009 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': 'program',
+ 'type': 'executable',
+ 'msvs_cygwin_shell': 0,
+ 'sources': [
+ 'program.c',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'make-prog1',
+ 'inputs': [
+ 'make-prog1.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/prog1.c',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ },
+ {
+ 'action_name': 'make-prog2',
+ 'inputs': [
+ 'make-prog2.py',
+ ],
+ 'outputs': [
+ 'actions-out/prog2.c',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ {
+ 'target_name': 'counter',
+ 'type': 'none',
+ 'actions': [
+ {
+ # This action should always run, regardless of whether or not it's
+ # inputs or the command-line change. We do this by creating a dummy
+ # first output, which is always missing, thus causing the build to
+ # always try to recreate it. Actual output files should be listed
+ # after the dummy one, and dependent targets should list the real
+ # output(s) in their inputs
+ # (see '../actions.gyp:depend_on_always_run_action').
+ 'action_name': 'action_counter',
+ 'inputs': [
+ 'counter.py',
+ ],
+ 'outputs': [
+ 'actions-out/action-counter.txt.always',
+ 'actions-out/action-counter.txt',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', 'actions-out/action-counter.txt', '2',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions/src/subdir1/make-prog1.py b/tools/gyp/test/actions/src/subdir1/make-prog1.py
new file mode 100755
index 00000000000000..7ea1d8a2d48708
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir1/make-prog1.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = r"""
+#include
+
+void prog1(void)
+{
+ printf("Hello from make-prog1.py\n");
+}
+"""
+
+open(sys.argv[1], 'w').write(contents)
+
+sys.exit(0)
diff --git a/tools/gyp/test/actions/src/subdir1/make-prog2.py b/tools/gyp/test/actions/src/subdir1/make-prog2.py
new file mode 100755
index 00000000000000..0bfe4973c24c15
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir1/make-prog2.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = r"""
+#include
+
+void prog2(void)
+{
+ printf("Hello from make-prog2.py\n");
+}
+"""
+
+open(sys.argv[1], 'w').write(contents)
+
+sys.exit(0)
diff --git a/tools/gyp/test/actions/src/subdir1/program.c b/tools/gyp/test/actions/src/subdir1/program.c
new file mode 100644
index 00000000000000..15f0f94ee57f47
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir1/program.c
@@ -0,0 +1,12 @@
+#include
+
+extern void prog1(void);
+extern void prog2(void);
+
+int main(void)
+{
+ printf("Hello from program.c\n");
+ prog1();
+ prog2();
+ return 0;
+}
diff --git a/tools/gyp/test/actions/src/subdir2/make-file.py b/tools/gyp/test/actions/src/subdir2/make-file.py
new file mode 100755
index 00000000000000..fff0653144745a
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir2/make-file.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = "Hello from make-file.py\n"
+
+open(sys.argv[1], 'wb').write(contents)
diff --git a/tools/gyp/test/actions/src/subdir2/none.gyp b/tools/gyp/test/actions/src/subdir2/none.gyp
new file mode 100644
index 00000000000000..2caa97d55c90e0
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir2/none.gyp
@@ -0,0 +1,33 @@
+# Copyright (c) 2009 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': 'file',
+ 'type': 'none',
+ 'msvs_cygwin_shell': 0,
+ 'actions': [
+ {
+ 'action_name': 'make-file',
+ 'inputs': [
+ 'make-file.py',
+ ],
+ 'outputs': [
+ 'file.out',
+ # TODO: enhance testing infrastructure to test this
+ # without having to hard-code the intermediate dir paths.
+ #'<(INTERMEDIATE_DIR)/file.out',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/actions/src/subdir3/generate_main.py b/tools/gyp/test/actions/src/subdir3/generate_main.py
new file mode 100755
index 00000000000000..804d38df3181ac
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir3/generate_main.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = """
+#include
+
+int main(void)
+{
+ printf("Hello from generate_main.py\\n");
+ return 0;
+}
+"""
+
+open(sys.argv[1], 'w').write(contents)
+
+sys.exit(0)
diff --git a/tools/gyp/test/actions/src/subdir3/null_input.gyp b/tools/gyp/test/actions/src/subdir3/null_input.gyp
new file mode 100644
index 00000000000000..9b0bea5fdb40b8
--- /dev/null
+++ b/tools/gyp/test/actions/src/subdir3/null_input.gyp
@@ -0,0 +1,29 @@
+# Copyright (c) 2009 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': 'null_input',
+ 'type': 'executable',
+ 'msvs_cygwin_shell': 0,
+ 'actions': [
+ {
+ 'action_name': 'generate_main',
+ 'process_outputs_as_sources': 1,
+ 'inputs': [],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/main.c',
+ ],
+ 'action': [
+ # TODO: we can't just use <(_outputs) here?!
+ 'python', 'generate_main.py', '<(INTERMEDIATE_DIR)/main.c',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/additional-targets/gyptest-additional.py b/tools/gyp/test/additional-targets/gyptest-additional.py
new file mode 100755
index 00000000000000..fcea0d573c10ff
--- /dev/null
+++ b/tools/gyp/test/additional-targets/gyptest-additional.py
@@ -0,0 +1,63 @@
+#!/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 simple actions when using an explicit build target of 'all'.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('all.gyp',
+ '-G', 'xcode_ninja_target_pattern=^all_targets$',
+ chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+# Build all.
+test.build('all.gyp', chdir='relocate/src')
+
+if test.format=='xcode':
+ chdir = 'relocate/src/dir1'
+else:
+ chdir = 'relocate/src'
+
+# Output is as expected.
+file_content = 'Hello from emit.py\n'
+test.built_file_must_match('out2.txt', file_content, chdir=chdir)
+
+test.built_file_must_not_exist('out.txt', chdir='relocate/src')
+test.built_file_must_not_exist('foolib1',
+ type=test.SHARED_LIB,
+ chdir=chdir)
+
+# xcode-ninja doesn't generate separate workspaces for sub-gyps by design
+if test.format == 'xcode-ninja':
+ test.pass_test()
+
+# TODO(mmoss) Make consistent with msvs, with 'dir1' before 'out/Default'?
+if test.format in ('make', 'ninja', 'android', 'cmake'):
+ chdir='relocate/src'
+else:
+ chdir='relocate/src/dir1'
+
+# Build the action explicitly.
+test.build('actions.gyp', 'action1_target', chdir=chdir)
+
+# Check that things got run.
+file_content = 'Hello from emit.py\n'
+test.built_file_must_exist('out.txt', chdir=chdir)
+
+# Build the shared library explicitly.
+test.build('actions.gyp', 'foolib1', chdir=chdir)
+
+test.built_file_must_exist('foolib1',
+ type=test.SHARED_LIB,
+ chdir=chdir,
+ subdir='dir1')
+
+test.pass_test()
diff --git a/tools/gyp/test/additional-targets/src/all.gyp b/tools/gyp/test/additional-targets/src/all.gyp
new file mode 100644
index 00000000000000..21c83080aaffef
--- /dev/null
+++ b/tools/gyp/test/additional-targets/src/all.gyp
@@ -0,0 +1,13 @@
+# Copyright (c) 2009 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': 'all_targets',
+ 'type': 'none',
+ 'dependencies': ['dir1/actions.gyp:*'],
+ },
+ ],
+}
diff --git a/tools/gyp/test/additional-targets/src/dir1/actions.gyp b/tools/gyp/test/additional-targets/src/dir1/actions.gyp
new file mode 100644
index 00000000000000..5089c809131cb9
--- /dev/null
+++ b/tools/gyp/test/additional-targets/src/dir1/actions.gyp
@@ -0,0 +1,56 @@
+# Copyright (c) 2009 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': 'action1_target',
+ 'type': 'none',
+ 'suppress_wildcard': 1,
+ 'actions': [
+ {
+ 'action_name': 'action1',
+ 'inputs': [
+ 'emit.py',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/out.txt',
+ ],
+ 'action': ['python', 'emit.py', '<(PRODUCT_DIR)/out.txt'],
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ {
+ 'target_name': 'action2_target',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'action2',
+ 'inputs': [
+ 'emit.py',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/out2.txt',
+ ],
+ 'action': ['python', 'emit.py', '<(PRODUCT_DIR)/out2.txt'],
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ {
+ 'target_name': 'foolib1',
+ 'type': 'shared_library',
+ 'suppress_wildcard': 1,
+ 'sources': ['lib1.c'],
+ },
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'target_defaults': {
+ 'cflags': ['-fPIC'],
+ },
+ }],
+ ],
+}
diff --git a/tools/gyp/test/additional-targets/src/dir1/emit.py b/tools/gyp/test/additional-targets/src/dir1/emit.py
new file mode 100755
index 00000000000000..fd3138738ed4a8
--- /dev/null
+++ b/tools/gyp/test/additional-targets/src/dir1/emit.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+f = open(sys.argv[1], 'wb')
+f.write('Hello from emit.py\n')
+f.close()
diff --git a/tools/gyp/test/additional-targets/src/dir1/lib1.c b/tools/gyp/test/additional-targets/src/dir1/lib1.c
new file mode 100644
index 00000000000000..df4cb10f796e29
--- /dev/null
+++ b/tools/gyp/test/additional-targets/src/dir1/lib1.c
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int func1(void) {
+ return 42;
+}
diff --git a/tools/gyp/test/analyzer/common.gypi b/tools/gyp/test/analyzer/common.gypi
new file mode 100644
index 00000000000000..7c664e40dabf4d
--- /dev/null
+++ b/tools/gyp/test/analyzer/common.gypi
@@ -0,0 +1,6 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+}
diff --git a/tools/gyp/test/analyzer/gyptest-analyzer.py b/tools/gyp/test/analyzer/gyptest-analyzer.py
new file mode 100644
index 00000000000000..537d1c80b6bfef
--- /dev/null
+++ b/tools/gyp/test/analyzer/gyptest-analyzer.py
@@ -0,0 +1,356 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for analyzer
+"""
+
+import json
+import TestGyp
+
+found = 'Found dependency'
+found_all = 'Found dependency (all)'
+not_found = 'No dependencies'
+
+
+def _CreateConfigFile(files, targets):
+ """Creates the analyzer conflig file, which is used as the input to analyzer.
+ See description of analyzer.py for description of the arguments."""
+ f = open('test_file', 'w')
+ to_write = {'files': files, 'targets': targets }
+ json.dump(to_write, f)
+ f.close()
+
+
+def _CreateBogusConfigFile():
+ f = open('test_file','w')
+ f.write('bogus')
+ f.close()
+
+
+def _ReadOutputFileContents():
+ f = open('analyzer_output', 'r')
+ result = json.load(f)
+ f.close()
+ return result
+
+
+# NOTE: this would be clearer if it subclassed TestGypCustom, but that trips
+# over a bug in pylint (E1002).
+test = TestGyp.TestGypCustom(format='analyzer')
+
+def CommonArgs():
+ return ('-Gconfig_path=test_file',
+ '-Ganalyzer_output_path=analyzer_output')
+
+
+def run_analyzer(*args, **kw):
+ """Runs the test specifying a particular config and output path."""
+ args += CommonArgs()
+ test.run_gyp('test.gyp', *args, **kw)
+
+
+def run_analyzer2(*args, **kw):
+ """Same as run_analyzer(), but passes in test2.gyp instead of test.gyp."""
+ args += CommonArgs()
+ test.run_gyp('test2.gyp', *args, **kw)
+
+
+def run_analyzer3(*args, **kw):
+ """Same as run_analyzer(), but passes in test3.gyp instead of test.gyp."""
+ args += CommonArgs()
+ test.run_gyp('test3.gyp', *args, **kw)
+
+
+def run_analyzer4(*args, **kw):
+ """Same as run_analyzer(), but passes in test3.gyp instead of test.gyp."""
+ args += CommonArgs()
+ test.run_gyp('test4.gyp', *args, **kw)
+
+
+def EnsureContains(targets=set(), matched=False, build_targets=set()):
+ """Verifies output contains |targets|."""
+ result = _ReadOutputFileContents()
+ if result.get('error', None):
+ print 'unexpected error', result.get('error')
+ test.fail_test()
+
+ if result.get('invalid_targets', None):
+ print 'unexpected invalid_targets', result.get('invalid_targets')
+ test.fail_test()
+
+ actual_targets = set(result['targets'])
+ if actual_targets != targets:
+ print 'actual targets:', actual_targets, '\nexpected targets:', targets
+ test.fail_test()
+
+ actual_build_targets = set(result['build_targets'])
+ if actual_build_targets != build_targets:
+ print 'actual build_targets:', actual_build_targets, \
+ '\nexpected build_targets:', build_targets
+ test.fail_test()
+
+ if matched and result['status'] != found:
+ print 'expected', found, 'got', result['status']
+ test.fail_test()
+ elif not matched and result['status'] != not_found:
+ print 'expected', not_found, 'got', result['status']
+ test.fail_test()
+
+
+def EnsureMatchedAll(targets):
+ result = _ReadOutputFileContents()
+ if result.get('error', None):
+ print 'unexpected error', result.get('error')
+ test.fail_test()
+
+ if result.get('invalid_targets', None):
+ print 'unexpected invalid_targets', result.get('invalid_targets')
+ test.fail_test()
+
+ if result['status'] != found_all:
+ print 'expected', found_all, 'got', result['status']
+ test.fail_test()
+
+ actual_targets = set(result['targets'])
+ if actual_targets != targets:
+ print 'actual targets:', actual_targets, '\nexpected targets:', targets
+ test.fail_test()
+
+
+def EnsureError(expected_error_string):
+ """Verifies output contains the error string."""
+ result = _ReadOutputFileContents()
+ if result.get('error', '').find(expected_error_string) == -1:
+ print 'actual error:', result.get('error', ''), '\nexpected error:', \
+ expected_error_string
+ test.fail_test()
+
+
+def EnsureStdoutContains(expected_error_string):
+ if test.stdout().find(expected_error_string) == -1:
+ print 'actual stdout:', test.stdout(), '\nexpected stdout:', \
+ expected_error_string
+ test.fail_test()
+
+
+def EnsureInvalidTargets(expected_invalid_targets):
+ """Verifies output contains invalid_targets."""
+ result = _ReadOutputFileContents()
+ actual_invalid_targets = set(result['invalid_targets'])
+ if actual_invalid_targets != expected_invalid_targets:
+ print 'actual invalid_targets:', actual_invalid_targets, \
+ '\nexpected :', expected_invalid_targets
+ test.fail_test()
+
+# Verifies config_path must be specified.
+test.run_gyp('test.gyp')
+EnsureStdoutContains('Must specify files to analyze via config_path')
+
+# Verifies config_path must point to a valid file.
+test.run_gyp('test.gyp', '-Gconfig_path=bogus_file',
+ '-Ganalyzer_output_path=analyzer_output')
+EnsureError('Unable to open file bogus_file')
+
+# Verify 'invalid_targets' is present when bad target is specified.
+_CreateConfigFile(['exe2.c'], ['bad_target'])
+run_analyzer()
+EnsureInvalidTargets({'bad_target'})
+
+# Verifies config_path must point to a valid json file.
+_CreateBogusConfigFile()
+run_analyzer()
+EnsureError('Unable to parse config file test_file')
+
+# Trivial test of a source.
+_CreateConfigFile(['foo.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Conditional source that is excluded.
+_CreateConfigFile(['conditional_source.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Conditional source that is included by way of argument.
+_CreateConfigFile(['conditional_source.c'], [])
+run_analyzer('-Dtest_variable=1')
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Two unknown files.
+_CreateConfigFile(['unknown1.c', 'unoknow2.cc'], [])
+run_analyzer()
+EnsureContains()
+
+# Two unknown files.
+_CreateConfigFile(['unknown1.c', 'subdir/subdir_sourcex.c'], [])
+run_analyzer()
+EnsureContains()
+
+# Included dependency
+_CreateConfigFile(['unknown1.c', 'subdir/subdir_source.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe', 'exe3'})
+
+# Included inputs to actions.
+_CreateConfigFile(['action_input.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Don't consider outputs.
+_CreateConfigFile(['action_output.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Rule inputs.
+_CreateConfigFile(['rule_input.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Ignore path specified with PRODUCT_DIR.
+_CreateConfigFile(['product_dir_input.c'], [])
+run_analyzer()
+EnsureContains(matched=False)
+
+# Path specified via a variable.
+_CreateConfigFile(['subdir/subdir_source2.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Verifies paths with // are fixed up correctly.
+_CreateConfigFile(['parent_source.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe', 'exe3'})
+
+# Verifies relative paths are resolved correctly.
+_CreateConfigFile(['subdir/subdir_source.h'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Various permutations when passing in targets.
+_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe3'])
+run_analyzer()
+EnsureContains(matched=True, targets={'exe3'}, build_targets={'exe2', 'exe3'})
+
+_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe'])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe2', 'exe3'})
+
+# Verifies duplicates are ignored.
+_CreateConfigFile(['exe2.c', 'subdir/subdir2b_source.c'], ['exe', 'exe'])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe2', 'exe3'})
+
+_CreateConfigFile(['exe2.c'], ['exe'])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe2'})
+
+_CreateConfigFile(['exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe2'})
+
+_CreateConfigFile(['subdir/subdir2b_source.c', 'exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe2', 'exe3'})
+
+_CreateConfigFile(['subdir/subdir2b_source.c'], ['exe3'])
+run_analyzer()
+EnsureContains(matched=True, targets={'exe3'}, build_targets={'exe3'})
+
+_CreateConfigFile(['exe2.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe2'})
+
+_CreateConfigFile(['foo.c'], [])
+run_analyzer()
+EnsureContains(matched=True, build_targets={'exe'})
+
+# Assertions when modifying build (gyp/gypi) files, especially when said files
+# are included.
+_CreateConfigFile(['subdir2/d.cc'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'})
+
+_CreateConfigFile(['subdir2/subdir.includes.gypi'],
+ ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'})
+
+_CreateConfigFile(['subdir2/subdir.gyp'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'foo'}, build_targets={'exe'})
+
+_CreateConfigFile(['test2.includes.gypi'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2()
+EnsureContains(matched=True, targets={'exe', 'exe2', 'exe3'},
+ build_targets={'exe', 'exe2', 'exe3'})
+
+# Verify modifying a file included makes all targets dirty.
+_CreateConfigFile(['common.gypi'], ['exe', 'exe2', 'foo', 'exe3'])
+run_analyzer2('-Icommon.gypi')
+EnsureMatchedAll({'exe', 'exe2', 'foo', 'exe3'})
+
+# Assertions from test3.gyp.
+_CreateConfigFile(['d.c', 'f.c'], ['a'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'})
+
+_CreateConfigFile(['f.c'], ['a'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'})
+
+_CreateConfigFile(['f.c'], [])
+run_analyzer3()
+EnsureContains(matched=True, build_targets={'a', 'b'})
+
+_CreateConfigFile(['c.c', 'e.c'], [])
+run_analyzer3()
+EnsureContains(matched=True, build_targets={'a', 'b', 'c', 'e'})
+
+_CreateConfigFile(['d.c'], ['a'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'})
+
+_CreateConfigFile(['a.c'], ['a', 'b'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a'})
+
+_CreateConfigFile(['a.c'], ['a', 'b'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a'})
+
+_CreateConfigFile(['d.c'], ['a', 'b'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a', 'b'}, build_targets={'a', 'b'})
+
+_CreateConfigFile(['f.c'], ['a'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a', 'b'})
+
+_CreateConfigFile(['a.c'], ['a'])
+run_analyzer3()
+EnsureContains(matched=True, targets={'a'}, build_targets={'a'})
+
+_CreateConfigFile(['a.c'], [])
+run_analyzer3()
+EnsureContains(matched=True, build_targets={'a'})
+
+_CreateConfigFile(['d.c'], [])
+run_analyzer3()
+EnsureContains(matched=True, build_targets={'a', 'b'})
+
+# Assertions around test4.gyp.
+_CreateConfigFile(['f.c'], [])
+run_analyzer4()
+EnsureContains(matched=True, build_targets={'e', 'f'})
+
+_CreateConfigFile(['d.c'], [])
+run_analyzer4()
+EnsureContains(matched=True, build_targets={'a', 'b', 'c', 'd'})
+
+_CreateConfigFile(['i.c'], [])
+run_analyzer4()
+EnsureContains(matched=True, build_targets={'h'})
+
+test.pass_test()
diff --git a/tools/gyp/test/analyzer/subdir/subdir.gyp b/tools/gyp/test/analyzer/subdir/subdir.gyp
new file mode 100644
index 00000000000000..bfa2df48e15b07
--- /dev/null
+++ b/tools/gyp/test/analyzer/subdir/subdir.gyp
@@ -0,0 +1,36 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'trailing_dir_path': '../',
+ },
+ 'targets': [
+ {
+ 'target_name': 'foo',
+ 'type': 'static_library',
+ 'sources': [
+ 'subdir_source.c',
+ '<(trailing_dir_path)/parent_source.c',
+ ],
+ },
+ {
+ 'target_name': 'subdir2a',
+ 'type': 'static_library',
+ 'sources': [
+ 'subdir2_source.c',
+ ],
+ 'dependencies': [
+ 'subdir2b',
+ ],
+ },
+ {
+ 'target_name': 'subdir2b',
+ 'type': 'static_library',
+ 'sources': [
+ 'subdir2b_source.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/subdir/subdir2/subdir2.gyp b/tools/gyp/test/analyzer/subdir/subdir2/subdir2.gyp
new file mode 100644
index 00000000000000..e5aaa92b18c6b4
--- /dev/null
+++ b/tools/gyp/test/analyzer/subdir/subdir2/subdir2.gyp
@@ -0,0 +1,15 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'subdir2',
+ 'type': 'static_library',
+ 'sources': [
+ '../subdir_source.h',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/subdir2/subdir.gyp b/tools/gyp/test/analyzer/subdir2/subdir.gyp
new file mode 100644
index 00000000000000..d6c709c9ef4408
--- /dev/null
+++ b/tools/gyp/test/analyzer/subdir2/subdir.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'foo',
+ 'type': 'static_library',
+ 'sources': [
+ 'subdir_source.c',
+ ],
+ 'includes': [
+ 'subdir.includes.gypi',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/subdir2/subdir.includes.gypi b/tools/gyp/test/analyzer/subdir2/subdir.includes.gypi
new file mode 100644
index 00000000000000..324e92bcd43c02
--- /dev/null
+++ b/tools/gyp/test/analyzer/subdir2/subdir.includes.gypi
@@ -0,0 +1,9 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'sources': [
+ 'd.cc'
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test.gyp b/tools/gyp/test/analyzer/test.gyp
new file mode 100644
index 00000000000000..cb3612fc436a41
--- /dev/null
+++ b/tools/gyp/test/analyzer/test.gyp
@@ -0,0 +1,113 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# These gyp files create the following dependencies:
+#
+# test.gyp:
+# #exe -> subdir/subdir.gyp#foo, subdir/subdir2/subdir2.gyp#subdir2
+# foo.c
+# subdir/subdir_source2.c
+# conditional_source.c (if test_variable==1)
+# action_input.c
+# action_output.c
+# rule_input.c
+# rule_output.pdf
+# #exe2
+# exe2.c
+# #exe3 -> subdir/subdir.gyp#foo, subdir/subdir.gyp#subdir2a
+# exe3.c
+# #all (type none) -> exe, exe3
+#
+# subdir/subdir.gyp
+# #foo
+# subdir/subdir_source.c
+# parent_source.c
+# #subdir2a -> subdir2b
+# subdir/subdir2_source.c
+# #subdir2b
+# subdir/subdir2b_source.c
+#
+# subdir/subdir2/subdir2.gyp
+# #subdir2
+# subdir/subdir_source.h
+
+{
+ 'variables': {
+ 'test_variable%': 0,
+ 'variable_path': 'subdir',
+ },
+ 'targets': [
+ {
+ 'target_name': 'exe',
+ 'type': 'executable',
+ 'dependencies': [
+ 'subdir/subdir.gyp:foo',
+ 'subdir/subdir2/subdir2.gyp:subdir2',
+ ],
+ 'sources': [
+ 'foo.c',
+ '<(variable_path)/subdir_source2.c',
+ ],
+ 'conditions': [
+ ['test_variable==1', {
+ 'sources': [
+ 'conditional_source.c',
+ ],
+ }],
+ ],
+ 'actions': [
+ {
+ 'action_name': 'action',
+ 'inputs': [
+ '<(PRODUCT_DIR)/product_dir_input.c',
+ 'action_input.c',
+ '../bad_path1.h',
+ '../../bad_path2.h',
+ ],
+ 'outputs': [
+ 'action_output.c',
+ ],
+ },
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'rule',
+ 'extension': 'pdf',
+ 'inputs': [
+ 'rule_input.c',
+ ],
+ 'outputs': [
+ 'rule_output.pdf',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'exe2',
+ 'type': 'executable',
+ 'sources': [
+ 'exe2.c',
+ ],
+ },
+ {
+ 'target_name': 'exe3',
+ 'type': 'executable',
+ 'dependencies': [
+ 'subdir/subdir.gyp:foo',
+ 'subdir/subdir.gyp:subdir2a',
+ ],
+ 'sources': [
+ 'exe3.c',
+ ],
+ },
+ {
+ 'target_name': 'all',
+ 'type': 'none',
+ 'dependencies': [
+ 'exe',
+ 'exe3',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test2.gyp b/tools/gyp/test/analyzer/test2.gyp
new file mode 100644
index 00000000000000..782b6e6428a7bf
--- /dev/null
+++ b/tools/gyp/test/analyzer/test2.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'exe',
+ 'type': 'executable',
+ 'dependencies': [
+ 'subdir2/subdir.gyp:foo',
+ ],
+ },
+ {
+ 'target_name': 'exe2',
+ 'type': 'executable',
+ 'includes': [
+ 'test2.includes.gypi',
+ ],
+ },
+ ],
+ 'includes': [
+ 'test2.toplevel_includes.gypi',
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test2.includes.gypi b/tools/gyp/test/analyzer/test2.includes.gypi
new file mode 100644
index 00000000000000..3e21de23cbe64d
--- /dev/null
+++ b/tools/gyp/test/analyzer/test2.includes.gypi
@@ -0,0 +1,13 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'sources': [
+ 'a.cc',
+ 'b.cc'
+ ],
+ 'includes': [
+ 'test2.includes.includes.gypi',
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test2.includes.includes.gypi b/tools/gyp/test/analyzer/test2.includes.includes.gypi
new file mode 100644
index 00000000000000..de3a025dbb607d
--- /dev/null
+++ b/tools/gyp/test/analyzer/test2.includes.includes.gypi
@@ -0,0 +1,9 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'sources': [
+ 'c.cc'
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test2.toplevel_includes.gypi b/tools/gyp/test/analyzer/test2.toplevel_includes.gypi
new file mode 100644
index 00000000000000..54fa453b085691
--- /dev/null
+++ b/tools/gyp/test/analyzer/test2.toplevel_includes.gypi
@@ -0,0 +1,15 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'exe3',
+ 'type': 'executable',
+ 'sources': [
+ 'e.cc',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test3.gyp b/tools/gyp/test/analyzer/test3.gyp
new file mode 100644
index 00000000000000..40c7534ca4ec79
--- /dev/null
+++ b/tools/gyp/test/analyzer/test3.gyp
@@ -0,0 +1,77 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'all',
+ 'type': 'none',
+ 'dependencies': [
+ 'a',
+ 'b',
+ ],
+ },
+ {
+ 'target_name': 'a',
+ 'type': 'executable',
+ 'sources': [
+ 'a.c',
+ ],
+ 'dependencies': [
+ 'c',
+ 'd',
+ ],
+ },
+ {
+ 'target_name': 'b',
+ 'type': 'executable',
+ 'sources': [
+ 'b.c',
+ ],
+ 'dependencies': [
+ 'd',
+ 'e',
+ ],
+ },
+ {
+ 'target_name': 'c',
+ 'type': 'executable',
+ 'sources': [
+ 'c.c',
+ ],
+ },
+ {
+ 'target_name': 'd',
+ 'type': 'none',
+ 'sources': [
+ 'd.c',
+ ],
+ 'dependencies': [
+ 'f',
+ 'g',
+ ],
+ },
+ {
+ 'target_name': 'e',
+ 'type': 'executable',
+ 'sources': [
+ 'e.c',
+ ],
+ },
+ {
+ 'target_name': 'f',
+ 'type': 'static_library',
+ 'sources': [
+ 'f.c',
+ ],
+ },
+ {
+ 'target_name': 'g',
+ 'type': 'executable',
+ 'sources': [
+ 'g.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test4.gyp b/tools/gyp/test/analyzer/test4.gyp
new file mode 100644
index 00000000000000..91cea56c1fe35f
--- /dev/null
+++ b/tools/gyp/test/analyzer/test4.gyp
@@ -0,0 +1,80 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'a',
+ 'type': 'executable',
+ 'sources': [
+ 'a.c',
+ ],
+ 'dependencies': [
+ 'b',
+ 'c',
+ ],
+ },
+ {
+ 'target_name': 'b',
+ 'type': 'executable',
+ 'sources': [
+ 'b.c',
+ ],
+ 'dependencies': [
+ 'd',
+ ],
+ },
+ {
+ 'target_name': 'c',
+ 'type': 'executable',
+ 'sources': [
+ 'c.c',
+ ],
+ 'dependencies': [
+ 'b',
+ 'd',
+ ],
+ },
+ {
+ 'target_name': 'd',
+ 'type': 'executable',
+ 'sources': [
+ 'd.c',
+ ],
+ },
+ {
+ 'target_name': 'e',
+ 'type': 'executable',
+ 'dependencies': [
+ 'test5.gyp:f',
+ ],
+ },
+ {
+ 'target_name': 'h',
+ 'type': 'none',
+ 'dependencies': [
+ 'i',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'rule',
+ 'extension': 'pdf',
+ 'inputs': [
+ 'rule_input.c',
+ ],
+ 'outputs': [
+ 'rule_output.pdf',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'i',
+ 'type': 'static_library',
+ 'sources': [
+ 'i.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/analyzer/test5.gyp b/tools/gyp/test/analyzer/test5.gyp
new file mode 100644
index 00000000000000..f3ea5b00612d08
--- /dev/null
+++ b/tools/gyp/test/analyzer/test5.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'f',
+ 'type': 'executable',
+ 'sources': [
+ 'f.c',
+ ],
+ },
+ {
+ 'target_name': 'g',
+ 'type': 'executable',
+ 'sources': [
+ 'g.c',
+ ],
+ 'dependencies': [
+ 'f',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/32or64.c b/tools/gyp/test/android/32or64.c
new file mode 100644
index 00000000000000..3ea81692bde47a
--- /dev/null
+++ b/tools/gyp/test/android/32or64.c
@@ -0,0 +1,13 @@
+/* Copyright (c) 2014 Google Inc. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+const char* getString()
+{
+#ifdef __LP64__
+ return "Hello, 64-bit world!\n";
+#else
+ return "Hello, 32-bit world!\n";
+#endif
+}
diff --git a/tools/gyp/test/android/file.in b/tools/gyp/test/android/file.in
new file mode 100644
index 00000000000000..68016f01c00f7d
--- /dev/null
+++ b/tools/gyp/test/android/file.in
@@ -0,0 +1 @@
+A boring test file
diff --git a/tools/gyp/test/android/gyptest-host-multilib.py b/tools/gyp/test/android/gyptest-host-multilib.py
new file mode 100755
index 00000000000000..4fb7919540e6eb
--- /dev/null
+++ b/tools/gyp/test/android/gyptest-host-multilib.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that it's possible to build host targets correctly in a multilib
+configuration, explicitly forcing either 32 or 64 bit.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+test.run_gyp('host_32or64.gyp')
+
+# Force building as 32-bit
+test.build('host_32or64.gyp', 'generate_output',
+ arguments=['GYP_HOST_VAR_PREFIX=$(HOST_2ND_ARCH_VAR_PREFIX)',
+ 'GYP_HOST_MULTILIB=32'])
+
+test.built_file_must_match('host_32or64.output', 'Hello, 32-bit world!\n')
+
+# Force building as 64-bit
+test.build('host_32or64.gyp', 'generate_output',
+ arguments=['GYP_HOST_VAR_PREFIX=',
+ 'GYP_HOST_MULTILIB=64'])
+
+test.built_file_must_match('host_32or64.output', 'Hello, 64-bit world!\n')
+
+test.pass_test()
diff --git a/tools/gyp/test/android/gyptest-make-functions.py b/tools/gyp/test/android/gyptest-make-functions.py
new file mode 100755
index 00000000000000..cdf0e0ed29b44c
--- /dev/null
+++ b/tools/gyp/test/android/gyptest-make-functions.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that it's possible for gyp actions to use the result of calling a make
+function with "$()".
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+test.run_gyp('make_functions.gyp')
+
+test.build('make_functions.gyp', test.ALL)
+
+file_content = 'A boring test file\n'
+test.built_file_must_match('file.in', file_content)
+test.built_file_must_match('file.out', file_content)
+
+test.pass_test()
diff --git a/tools/gyp/test/android/gyptest-noalias.py b/tools/gyp/test/android/gyptest-noalias.py
new file mode 100755
index 00000000000000..4840c4c79f2597
--- /dev/null
+++ b/tools/gyp/test/android/gyptest-noalias.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that disabling target aliases works.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+test.run_gyp('hello.gyp', '-G', 'write_alias_targets=0')
+
+test.build('hello.gyp', 'hello', status=2, stderr=None)
+
+test.build('hello.gyp', 'gyp_all_modules', status=2, stderr=None)
+
+test.pass_test()
diff --git a/tools/gyp/test/android/gyptest-settings-list.py b/tools/gyp/test/android/gyptest-settings-list.py
new file mode 100755
index 00000000000000..5850d47ad92e48
--- /dev/null
+++ b/tools/gyp/test/android/gyptest-settings-list.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that it's possible to set Android build system settings using the
+aosp_build_settings key, with list values.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+test.run_gyp('settings-list.gyp')
+
+test.build('settings-list.gyp', 'hello')
+
+test.run_built_executable('hello.foo', stdout="Hello, world!\n")
+
+test.up_to_date('settings-list.gyp', test.DEFAULT)
+
+test.pass_test()
diff --git a/tools/gyp/test/android/gyptest-settings.py b/tools/gyp/test/android/gyptest-settings.py
new file mode 100755
index 00000000000000..f4e89d1bb48b8a
--- /dev/null
+++ b/tools/gyp/test/android/gyptest-settings.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that it's possible to set Android build system settings using the
+aosp_build_settings key.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+test.run_gyp('settings.gyp')
+
+test.build('settings.gyp', 'hello')
+
+test.run_built_executable('hello.foo', stdout="Hello, world!\n")
+
+test.up_to_date('settings.gyp', test.DEFAULT)
+
+test.pass_test()
diff --git a/tools/gyp/test/android/gyptest-space-filenames.py b/tools/gyp/test/android/gyptest-space-filenames.py
new file mode 100755
index 00000000000000..c6caf264665a2b
--- /dev/null
+++ b/tools/gyp/test/android/gyptest-space-filenames.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that action input/output filenames with spaces are rejected.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['android'])
+
+stderr = ('gyp: Action input filename "name with spaces" in target do_actions '
+ 'contains a space\n')
+test.run_gyp('space_filenames.gyp', status=1, stderr=stderr)
+
+test.pass_test()
diff --git a/tools/gyp/test/android/hello.c b/tools/gyp/test/android/hello.c
new file mode 100644
index 00000000000000..e6bf622aaa411c
--- /dev/null
+++ b/tools/gyp/test/android/hello.c
@@ -0,0 +1,12 @@
+/* Copyright (c) 2014 Google Inc. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include
+
+int main()
+{
+ printf("Hello, world!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/android/hello.gyp b/tools/gyp/test/android/hello.gyp
new file mode 100644
index 00000000000000..da58a2b707f8f1
--- /dev/null
+++ b/tools/gyp/test/android/hello.gyp
@@ -0,0 +1,15 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/host_32or64.gyp b/tools/gyp/test/android/host_32or64.gyp
new file mode 100644
index 00000000000000..e8ca79f5dbc815
--- /dev/null
+++ b/tools/gyp/test/android/host_32or64.gyp
@@ -0,0 +1,38 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lib32or64',
+ 'type': 'static_library',
+ 'toolsets': [ 'host' ],
+ 'sources': [
+ '32or64.c',
+ ],
+ },
+ {
+ 'target_name': 'host_32or64',
+ 'type': 'executable',
+ 'toolsets': [ 'host' ],
+ 'dependencies': [ 'lib32or64#host' ],
+ 'sources': [
+ 'writefile.c',
+ ],
+ },
+ {
+ 'target_name': 'generate_output',
+ 'type': 'none',
+ 'dependencies': [ 'host_32or64#host' ],
+ 'actions': [
+ {
+ 'action_name': 'generate',
+ 'inputs': [ '<(PRODUCT_DIR)/host_32or64' ],
+ 'outputs': [ '<(PRODUCT_DIR)/host_32or64.output' ],
+ 'action': [ '<@(_inputs)', '<@(_outputs)' ],
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/make_functions.gyp b/tools/gyp/test/android/make_functions.gyp
new file mode 100644
index 00000000000000..4b617cc932d349
--- /dev/null
+++ b/tools/gyp/test/android/make_functions.gyp
@@ -0,0 +1,31 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'file-in',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [ 'file.in' ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'file-out',
+ 'type': 'none',
+ 'dependencies': [ 'file-in' ],
+ 'actions': [
+ {
+ 'action_name': 'copy-file',
+ 'inputs': [ '$(strip <(PRODUCT_DIR)/file.in)' ],
+ 'outputs': [ '<(PRODUCT_DIR)/file.out' ],
+ 'action': [ 'cp', '$(strip <(PRODUCT_DIR)/file.in)', '<(PRODUCT_DIR)/file.out' ],
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/settings-list.gyp b/tools/gyp/test/android/settings-list.gyp
new file mode 100644
index 00000000000000..b392ce2bda3770
--- /dev/null
+++ b/tools/gyp/test/android/settings-list.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ 'aosp_build_settings': {
+ 'LOCAL_MODULE_SUFFIX': ['.foo'],
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/settings.gyp b/tools/gyp/test/android/settings.gyp
new file mode 100644
index 00000000000000..2f8ce7f5404af7
--- /dev/null
+++ b/tools/gyp/test/android/settings.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ 'aosp_build_settings': {
+ 'LOCAL_MODULE_SUFFIX': '.foo',
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/space_filenames.gyp b/tools/gyp/test/android/space_filenames.gyp
new file mode 100644
index 00000000000000..487ac551db2131
--- /dev/null
+++ b/tools/gyp/test/android/space_filenames.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'do_actions',
+ 'type': 'none',
+ 'actions': [{
+ 'action_name': 'should_be_forbidden',
+ 'inputs': [ 'name with spaces' ],
+ 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/name with spaces' ],
+ 'action': [ 'true' ],
+ }],
+ },
+ ],
+}
diff --git a/tools/gyp/test/android/writefile.c b/tools/gyp/test/android/writefile.c
new file mode 100644
index 00000000000000..3f5960f8915e68
--- /dev/null
+++ b/tools/gyp/test/android/writefile.c
@@ -0,0 +1,18 @@
+/* Copyright (c) 2014 Google Inc. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include
+
+extern const char* getString(void);
+
+int main(int argc, char* argv[])
+{
+ if (argc < 2) return 2;
+ FILE* f = fopen(argv[1], "w");
+ if (f == NULL) return 1;
+ fprintf(f, "%s", getString());
+ fclose(f);
+ return 0;
+}
diff --git a/tools/gyp/test/arflags/gyptest-arflags.py b/tools/gyp/test/arflags/gyptest-arflags.py
new file mode 100644
index 00000000000000..5ee1339a41668c
--- /dev/null
+++ b/tools/gyp/test/arflags/gyptest-arflags.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that building a target with invalid arflags fails.
+"""
+
+import os
+import sys
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['ninja'])
+test.run_gyp('test.gyp')
+expected_status = 0 if sys.platform in ['darwin', 'win32'] else 1
+test.build('test.gyp', target='lib', status=expected_status)
+test.pass_test()
diff --git a/tools/gyp/test/arflags/lib.cc b/tools/gyp/test/arflags/lib.cc
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/tools/gyp/test/arflags/test.gyp b/tools/gyp/test/arflags/test.gyp
new file mode 100644
index 00000000000000..f7430fae2d1359
--- /dev/null
+++ b/tools/gyp/test/arflags/test.gyp
@@ -0,0 +1,10 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'lib',
+ 'type': 'static_library',
+ 'sources': ['lib.cc'],
+ 'arflags': ['--nonexistent'],
+ },
+ ],
+}
diff --git a/tools/gyp/test/assembly/gyptest-assembly.py b/tools/gyp/test/assembly/gyptest-assembly.py
new file mode 100755
index 00000000000000..8a84310544afc9
--- /dev/null
+++ b/tools/gyp/test/assembly/gyptest-assembly.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.
+
+"""
+A basic test of compiling assembler files.
+"""
+
+import sys
+import TestGyp
+
+if sys.platform != 'win32':
+ # TODO(bradnelson): get this working for windows.
+ test = TestGyp.TestGyp(formats=['!msvs'])
+
+ test.run_gyp('assembly.gyp', chdir='src')
+
+ test.relocate('src', 'relocate/src')
+
+ test.build('assembly.gyp', test.ALL, chdir='relocate/src')
+
+ expect = """\
+Hello from program.c
+Got 42.
+"""
+ test.run_built_executable('program', chdir='relocate/src', stdout=expect)
+
+
+ test.pass_test()
diff --git a/tools/gyp/test/assembly/gyptest-override.py b/tools/gyp/test/assembly/gyptest-override.py
new file mode 100644
index 00000000000000..e84a23e855ac21
--- /dev/null
+++ b/tools/gyp/test/assembly/gyptest-override.py
@@ -0,0 +1,24 @@
+#!/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.
+
+"""
+Make sure that manual rules on Windows override the built in ones.
+"""
+
+import sys
+import TestGyp
+
+if sys.platform == 'win32':
+ test = TestGyp.TestGyp(formats=['msvs', 'ninja'])
+ CHDIR = 'src'
+ test.run_gyp('override.gyp', chdir=CHDIR)
+ test.build('override.gyp', test.ALL, chdir=CHDIR)
+ expect = """\
+Hello from program.c
+Got 42.
+"""
+ test.run_built_executable('program', chdir=CHDIR, stdout=expect)
+ test.pass_test()
diff --git a/tools/gyp/test/assembly/src/as.bat b/tools/gyp/test/assembly/src/as.bat
new file mode 100644
index 00000000000000..5ac229fbd2e1cc
--- /dev/null
+++ b/tools/gyp/test/assembly/src/as.bat
@@ -0,0 +1,3 @@
+@echo off
+:: Mock windows assembler.
+cl /MD /c %1 /Fo"%2"
diff --git a/tools/gyp/test/assembly/src/assembly.gyp b/tools/gyp/test/assembly/src/assembly.gyp
new file mode 100644
index 00000000000000..565cb0fa0e3888
--- /dev/null
+++ b/tools/gyp/test/assembly/src/assembly.gyp
@@ -0,0 +1,62 @@
+# Copyright (c) 2009 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': {
+ 'conditions': [
+ ['OS=="win"', {
+ 'defines': ['PLATFORM_WIN'],
+ }],
+ ['OS=="mac" or OS=="ios"', {
+ 'defines': ['PLATFORM_MAC'],
+ }],
+ ['OS=="linux"', {
+ 'defines': ['PLATFORM_LINUX'],
+ }],
+ ['OS=="android"', {
+ 'defines': ['PLATFORM_ANDROID'],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'dependencies': ['lib1'],
+ 'sources': [
+ 'program.c',
+ ],
+ },
+ {
+ 'target_name': 'lib1',
+ 'type': 'static_library',
+ 'sources': [
+ 'lib1.S',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'target_defaults': {
+ 'rules': [
+ {
+ 'rule_name': 'assembler',
+ 'msvs_cygwin_shell': 0,
+ 'extension': 'S',
+ 'inputs': [
+ 'as.bat',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj',
+ ],
+ 'action':
+ ['as.bat', 'lib1.c', '<(_outputs)'],
+ 'message': 'Building assembly file <(RULE_INPUT_PATH)',
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ },
+ },],
+ ],
+}
diff --git a/tools/gyp/test/assembly/src/lib1.S b/tools/gyp/test/assembly/src/lib1.S
new file mode 100644
index 00000000000000..7de9f19cf97383
--- /dev/null
+++ b/tools/gyp/test/assembly/src/lib1.S
@@ -0,0 +1,15 @@
+#if PLATFORM_WINDOWS || PLATFORM_MAC
+# define IDENTIFIER(n) _##n
+#else /* Linux */
+# define IDENTIFIER(n) n
+#endif
+
+.globl IDENTIFIER(lib1_function)
+IDENTIFIER(lib1_function):
+#if !defined(PLATFORM_ANDROID)
+ movl $42, %eax
+ ret
+#else /* Android (assuming ARM) */
+ mov r0, #42
+ bx lr
+#endif
diff --git a/tools/gyp/test/assembly/src/lib1.c b/tools/gyp/test/assembly/src/lib1.c
new file mode 100644
index 00000000000000..be21ecd5f6a765
--- /dev/null
+++ b/tools/gyp/test/assembly/src/lib1.c
@@ -0,0 +1,3 @@
+int lib1_function(void) {
+ return 42;
+}
diff --git a/tools/gyp/test/assembly/src/override.gyp b/tools/gyp/test/assembly/src/override.gyp
new file mode 100644
index 00000000000000..39a4072effc5cf
--- /dev/null
+++ b/tools/gyp/test/assembly/src/override.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': 'program',
+ 'type': 'executable',
+ 'sources': [
+ 'program.c',
+ 'override_asm.asm',
+ ],
+ 'rules': [
+ {
+ # Test that if there's a specific .asm rule, it overrides the
+ # built in one on Windows.
+ 'rule_name': 'assembler',
+ 'msvs_cygwin_shell': 0,
+ 'extension': 'asm',
+ 'inputs': [
+ 'as.bat',
+ ],
+ 'outputs': [
+ 'output.obj',
+ ],
+ 'action': ['as.bat', 'lib1.c', '<(_outputs)'],
+ 'message': 'Building assembly file <(RULE_INPUT_PATH)',
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/assembly/src/override_asm.asm b/tools/gyp/test/assembly/src/override_asm.asm
new file mode 100644
index 00000000000000..be93b23baac3f5
--- /dev/null
+++ b/tools/gyp/test/assembly/src/override_asm.asm
@@ -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.
+
+; This is a placeholder. It should not be referenced if overrides work
+; correctly.
+
+Bad stuff that shouldn't assemble.
diff --git a/tools/gyp/test/assembly/src/program.c b/tools/gyp/test/assembly/src/program.c
new file mode 100644
index 00000000000000..eee862712e1916
--- /dev/null
+++ b/tools/gyp/test/assembly/src/program.c
@@ -0,0 +1,12 @@
+#include
+
+extern int lib1_function(void);
+
+int main(void)
+{
+ fprintf(stdout, "Hello from program.c\n");
+ fflush(stdout);
+ fprintf(stdout, "Got %d.\n", lib1_function());
+ fflush(stdout);
+ return 0;
+}
diff --git a/tools/gyp/test/build-option/gyptest-build.py b/tools/gyp/test/build-option/gyptest-build.py
new file mode 100755
index 00000000000000..773d409ce5efc1
--- /dev/null
+++ b/tools/gyp/test/build-option/gyptest-build.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 simplest-possible build of a "Hello, world!" program
+using the default build target.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_default')
+
+if test.format == 'android':
+ # This test currently fails on android. Investigate why, fix the issues
+ # responsible, and reenable this test on android. See bug:
+ # https://code.google.com/p/gyp/issues/detail?id=436
+ test.skip_test(message='Test fails on android. Fix and reenable.\n')
+
+if test.format == 'xcode-ninja':
+ # The xcode-ninja generator doesn't support --build
+ # cf. https://code.google.com/p/gyp/issues/detail?id=453
+ test.skip_test()
+
+test.run_gyp('hello.gyp', '--build=Default')
+
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+test.up_to_date('hello.gyp', test.DEFAULT)
+
+test.pass_test()
diff --git a/tools/gyp/test/build-option/hello.c b/tools/gyp/test/build-option/hello.c
new file mode 100644
index 00000000000000..f6ad129fd7a3db
--- /dev/null
+++ b/tools/gyp/test/build-option/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
+
+int main(void)
+{
+ printf("Hello, world!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/build-option/hello.gyp b/tools/gyp/test/build-option/hello.gyp
new file mode 100644
index 00000000000000..1974d51ccd1934
--- /dev/null
+++ b/tools/gyp/test/build-option/hello.gyp
@@ -0,0 +1,15 @@
+# Copyright (c) 2009 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/builddir/gyptest-all.py b/tools/gyp/test/builddir/gyptest-all.py
new file mode 100755
index 00000000000000..f7294b570e2d19
--- /dev/null
+++ b/tools/gyp/test/builddir/gyptest-all.py
@@ -0,0 +1,85 @@
+#!/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 the settings that cause a set of programs to be created in
+a specific build directory, and that no intermediate built files
+get created outside of that build directory hierarchy even when
+referred to with deeply-nested ../../.. paths.
+"""
+
+import TestGyp
+
+# TODO(mmoss): Make only supports (theoretically) a single, global build
+# directory (through GYP_GENERATOR_FLAGS 'output_dir'), rather than
+# gyp-file-specific settings (e.g. the stuff in builddir.gypi) that the other
+# generators support, so this doesn't work yet for make.
+# TODO(mmoss) Make also has the issue that the top-level Makefile is written to
+# the "--depth" location, which is one level above 'src', but then this test
+# moves 'src' somewhere else, leaving the Makefile behind, so make can't find
+# its sources. I'm not sure if make is wrong for writing outside the current
+# directory, or if the test is wrong for assuming everything generated is under
+# the current directory.
+# Android, Ninja, and CMake do not support setting the build directory.
+test = TestGyp.TestGyp(formats=['!make', '!ninja', '!android', '!cmake'])
+
+test.run_gyp('prog1.gyp', '--depth=..', chdir='src')
+if test.format == 'msvs':
+ if test.uses_msbuild:
+ test.must_contain('src/prog1.vcxproj',
+ '..\\builddir\\Default\\')
+ else:
+ test.must_contain('src/prog1.vcproj',
+ 'OutputDirectory="..\\builddir\\Default\\"')
+
+test.relocate('src', 'relocate/src')
+
+test.subdir('relocate/builddir')
+
+# Make sure that all the built ../../etc. files only get put under builddir,
+# by making all of relocate read-only and then making only builddir writable.
+test.writable('relocate', False)
+test.writable('relocate/builddir', True)
+
+# Suppress the test infrastructure's setting SYMROOT on the command line.
+test.build('prog1.gyp', test.ALL, SYMROOT=None, chdir='relocate/src')
+
+expect1 = """\
+Hello from prog1.c
+Hello from func1.c
+"""
+
+expect2 = """\
+Hello from subdir2/prog2.c
+Hello from func2.c
+"""
+
+expect3 = """\
+Hello from subdir2/subdir3/prog3.c
+Hello from func3.c
+"""
+
+expect4 = """\
+Hello from subdir2/subdir3/subdir4/prog4.c
+Hello from func4.c
+"""
+
+expect5 = """\
+Hello from subdir2/subdir3/subdir4/subdir5/prog5.c
+Hello from func5.c
+"""
+
+def run_builddir(prog, expect):
+ dir = 'relocate/builddir/Default/'
+ test.run(program=test.workpath(dir + prog), stdout=expect)
+
+run_builddir('prog1', expect1)
+run_builddir('prog2', expect2)
+run_builddir('prog3', expect3)
+run_builddir('prog4', expect4)
+run_builddir('prog5', expect5)
+
+test.pass_test()
diff --git a/tools/gyp/test/builddir/gyptest-default.py b/tools/gyp/test/builddir/gyptest-default.py
new file mode 100755
index 00000000000000..1b47443fff46c1
--- /dev/null
+++ b/tools/gyp/test/builddir/gyptest-default.py
@@ -0,0 +1,85 @@
+#!/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 the settings that cause a set of programs to be created in
+a specific build directory, and that no intermediate built files
+get created outside of that build directory hierarchy even when
+referred to with deeply-nested ../../.. paths.
+"""
+
+import TestGyp
+
+# TODO(mmoss): Make only supports (theoretically) a single, global build
+# directory (through GYP_GENERATOR_FLAGS 'output_dir'), rather than
+# gyp-file-specific settings (e.g. the stuff in builddir.gypi) that the other
+# generators support, so this doesn't work yet for make.
+# TODO(mmoss) Make also has the issue that the top-level Makefile is written to
+# the "--depth" location, which is one level above 'src', but then this test
+# moves 'src' somewhere else, leaving the Makefile behind, so make can't find
+# its sources. I'm not sure if make is wrong for writing outside the current
+# directory, or if the test is wrong for assuming everything generated is under
+# the current directory.
+# Android, Ninja, and CMake do not support setting the build directory.
+test = TestGyp.TestGyp(formats=['!make', '!ninja', '!android', '!cmake'])
+
+test.run_gyp('prog1.gyp', '--depth=..', chdir='src')
+if test.format == 'msvs':
+ if test.uses_msbuild:
+ test.must_contain('src/prog1.vcxproj',
+ '..\\builddir\\Default\\')
+ else:
+ test.must_contain('src/prog1.vcproj',
+ 'OutputDirectory="..\\builddir\\Default\\"')
+
+test.relocate('src', 'relocate/src')
+
+test.subdir('relocate/builddir')
+
+# Make sure that all the built ../../etc. files only get put under builddir,
+# by making all of relocate read-only and then making only builddir writable.
+test.writable('relocate', False)
+test.writable('relocate/builddir', True)
+
+# Suppress the test infrastructure's setting SYMROOT on the command line.
+test.build('prog1.gyp', SYMROOT=None, chdir='relocate/src')
+
+expect1 = """\
+Hello from prog1.c
+Hello from func1.c
+"""
+
+expect2 = """\
+Hello from subdir2/prog2.c
+Hello from func2.c
+"""
+
+expect3 = """\
+Hello from subdir2/subdir3/prog3.c
+Hello from func3.c
+"""
+
+expect4 = """\
+Hello from subdir2/subdir3/subdir4/prog4.c
+Hello from func4.c
+"""
+
+expect5 = """\
+Hello from subdir2/subdir3/subdir4/subdir5/prog5.c
+Hello from func5.c
+"""
+
+def run_builddir(prog, expect):
+ dir = 'relocate/builddir/Default/'
+ test.run(program=test.workpath(dir + prog), stdout=expect)
+
+run_builddir('prog1', expect1)
+run_builddir('prog2', expect2)
+run_builddir('prog3', expect3)
+run_builddir('prog4', expect4)
+run_builddir('prog5', expect5)
+
+test.pass_test()
diff --git a/tools/gyp/test/builddir/src/builddir.gypi b/tools/gyp/test/builddir/src/builddir.gypi
new file mode 100644
index 00000000000000..ce175db8f84dcb
--- /dev/null
+++ b/tools/gyp/test/builddir/src/builddir.gypi
@@ -0,0 +1,18 @@
+# Copyright (c) 2009 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': {
+ 'Default': {
+ 'msvs_configuration_attributes': {
+ 'OutputDirectory': '<(DEPTH)\\builddir/Default',
+ },
+ },
+ },
+ },
+ 'xcode_settings': {
+ 'SYMROOT': '<(DEPTH)/builddir',
+ },
+}
diff --git a/tools/gyp/test/builddir/src/func1.c b/tools/gyp/test/builddir/src/func1.c
new file mode 100644
index 00000000000000..b8e6a06951c987
--- /dev/null
+++ b/tools/gyp/test/builddir/src/func1.c
@@ -0,0 +1,6 @@
+#include
+
+void func1(void)
+{
+ printf("Hello from func1.c\n");
+}
diff --git a/tools/gyp/test/builddir/src/func2.c b/tools/gyp/test/builddir/src/func2.c
new file mode 100644
index 00000000000000..14aabac475ad2f
--- /dev/null
+++ b/tools/gyp/test/builddir/src/func2.c
@@ -0,0 +1,6 @@
+#include
+
+void func2(void)
+{
+ printf("Hello from func2.c\n");
+}
diff --git a/tools/gyp/test/builddir/src/func3.c b/tools/gyp/test/builddir/src/func3.c
new file mode 100644
index 00000000000000..3b4edeae6ddbfc
--- /dev/null
+++ b/tools/gyp/test/builddir/src/func3.c
@@ -0,0 +1,6 @@
+#include
+
+void func3(void)
+{
+ printf("Hello from func3.c\n");
+}
diff --git a/tools/gyp/test/builddir/src/func4.c b/tools/gyp/test/builddir/src/func4.c
new file mode 100644
index 00000000000000..732891b79a999c
--- /dev/null
+++ b/tools/gyp/test/builddir/src/func4.c
@@ -0,0 +1,6 @@
+#include
+
+void func4(void)
+{
+ printf("Hello from func4.c\n");
+}
diff --git a/tools/gyp/test/builddir/src/func5.c b/tools/gyp/test/builddir/src/func5.c
new file mode 100644
index 00000000000000..18fdfabbbed858
--- /dev/null
+++ b/tools/gyp/test/builddir/src/func5.c
@@ -0,0 +1,6 @@
+#include
+
+void func5(void)
+{
+ printf("Hello from func5.c\n");
+}
diff --git a/tools/gyp/test/builddir/src/prog1.c b/tools/gyp/test/builddir/src/prog1.c
new file mode 100644
index 00000000000000..a32aaf04f9dcc7
--- /dev/null
+++ b/tools/gyp/test/builddir/src/prog1.c
@@ -0,0 +1,10 @@
+#include
+
+extern void func1(void);
+
+int main(void)
+{
+ printf("Hello from prog1.c\n");
+ func1();
+ return 0;
+}
diff --git a/tools/gyp/test/builddir/src/prog1.gyp b/tools/gyp/test/builddir/src/prog1.gyp
new file mode 100644
index 00000000000000..5b96f035ec707c
--- /dev/null
+++ b/tools/gyp/test/builddir/src/prog1.gyp
@@ -0,0 +1,30 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ 'builddir.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'pull_in_all',
+ 'type': 'none',
+ 'dependencies': [
+ 'prog1',
+ 'subdir2/prog2.gyp:prog2',
+ 'subdir2/subdir3/prog3.gyp:prog3',
+ 'subdir2/subdir3/subdir4/prog4.gyp:prog4',
+ 'subdir2/subdir3/subdir4/subdir5/prog5.gyp:prog5',
+ ],
+ },
+ {
+ 'target_name': 'prog1',
+ 'type': 'executable',
+ 'sources': [
+ 'prog1.c',
+ 'func1.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/prog2.c b/tools/gyp/test/builddir/src/subdir2/prog2.c
new file mode 100644
index 00000000000000..9d682cd783c8ad
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/prog2.c
@@ -0,0 +1,10 @@
+#include
+
+extern void func2(void);
+
+int main(void)
+{
+ printf("Hello from subdir2/prog2.c\n");
+ func2();
+ return 0;
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/prog2.gyp b/tools/gyp/test/builddir/src/subdir2/prog2.gyp
new file mode 100644
index 00000000000000..96299b646d2116
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/prog2.gyp
@@ -0,0 +1,19 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../builddir.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog2',
+ 'type': 'executable',
+ 'sources': [
+ 'prog2.c',
+ '../func2.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c
new file mode 100644
index 00000000000000..da74965985e56c
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.c
@@ -0,0 +1,10 @@
+#include
+
+extern void func3(void);
+
+int main(void)
+{
+ printf("Hello from subdir2/subdir3/prog3.c\n");
+ func3();
+ return 0;
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp
new file mode 100644
index 00000000000000..d7df43c7bde92f
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/subdir3/prog3.gyp
@@ -0,0 +1,19 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../../builddir.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog3',
+ 'type': 'executable',
+ 'sources': [
+ 'prog3.c',
+ '../../func3.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c
new file mode 100644
index 00000000000000..5787d5fa4368bc
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.c
@@ -0,0 +1,10 @@
+#include
+
+extern void func4(void);
+
+int main(void)
+{
+ printf("Hello from subdir2/subdir3/subdir4/prog4.c\n");
+ func4();
+ return 0;
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp
new file mode 100644
index 00000000000000..862a8a18cd70f8
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/prog4.gyp
@@ -0,0 +1,19 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../../../builddir.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog4',
+ 'type': 'executable',
+ 'sources': [
+ 'prog4.c',
+ '../../../func4.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c
new file mode 100644
index 00000000000000..c6e2ab521f4333
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.c
@@ -0,0 +1,10 @@
+#include
+
+extern void func5(void);
+
+int main(void)
+{
+ printf("Hello from subdir2/subdir3/subdir4/subdir5/prog5.c\n");
+ func5();
+ return 0;
+}
diff --git a/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp
new file mode 100644
index 00000000000000..fe1c9cbf50f49a
--- /dev/null
+++ b/tools/gyp/test/builddir/src/subdir2/subdir3/subdir4/subdir5/prog5.gyp
@@ -0,0 +1,19 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../../../../builddir.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog5',
+ 'type': 'executable',
+ 'sources': [
+ 'prog5.c',
+ '../../../../func5.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/cflags/cflags.c b/tools/gyp/test/cflags/cflags.c
new file mode 100644
index 00000000000000..0a02ba90745e80
--- /dev/null
+++ b/tools/gyp/test/cflags/cflags.c
@@ -0,0 +1,15 @@
+/* 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. */
+
+#include
+
+int main(void)
+{
+#ifdef FOO
+ printf("FOO defined\n");
+#else
+ printf("FOO not defined\n");
+#endif
+ return 0;
+}
diff --git a/tools/gyp/test/cflags/cflags.gyp b/tools/gyp/test/cflags/cflags.gyp
new file mode 100644
index 00000000000000..2840dc63185d04
--- /dev/null
+++ b/tools/gyp/test/cflags/cflags.gyp
@@ -0,0 +1,23 @@
+# 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': [
+ {
+ 'target_name': 'cflags',
+ 'type': 'executable',
+ 'sources': [
+ 'cflags.c',
+ ],
+ },
+ {
+ 'target_name': 'cflags_host',
+ 'toolsets': ['host'],
+ 'type': 'executable',
+ 'sources': [
+ 'cflags.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/cflags/gyptest-cflags.py b/tools/gyp/test/cflags/gyptest-cflags.py
new file mode 100755
index 00000000000000..0a87ed871bec68
--- /dev/null
+++ b/tools/gyp/test/cflags/gyptest-cflags.py
@@ -0,0 +1,64 @@
+#!/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 the use of the environment during regeneration when the gyp file
+changes, specifically via build of an executable with C preprocessor
+definition specified by CFLAGS.
+
+In this test, gyp and build both run in same local environment.
+"""
+
+import TestGyp
+
+# CPPFLAGS works in ninja but not make; CFLAGS works in both
+FORMATS = ('make', 'ninja')
+
+test = TestGyp.TestGyp(formats=FORMATS)
+
+# First set CFLAGS to blank in case the platform doesn't support unsetenv.
+with TestGyp.LocalEnv({'CFLAGS': '',
+ 'GYP_CROSSCOMPILE': '1'}):
+ test.run_gyp('cflags.gyp')
+ test.build('cflags.gyp')
+
+expect = """FOO not defined\n"""
+test.run_built_executable('cflags', stdout=expect)
+test.run_built_executable('cflags_host', stdout=expect)
+
+test.sleep()
+
+with TestGyp.LocalEnv({'CFLAGS': '-DFOO=1',
+ 'GYP_CROSSCOMPILE': '1'}):
+ test.run_gyp('cflags.gyp')
+ test.build('cflags.gyp')
+
+expect = """FOO defined\n"""
+test.run_built_executable('cflags', stdout=expect)
+
+# Environment variables shouldn't influence the flags for the host.
+expect = """FOO not defined\n"""
+test.run_built_executable('cflags_host', stdout=expect)
+
+test.sleep()
+
+with TestGyp.LocalEnv({'CFLAGS': ''}):
+ test.run_gyp('cflags.gyp')
+ test.build('cflags.gyp')
+
+expect = """FOO not defined\n"""
+test.run_built_executable('cflags', stdout=expect)
+
+test.sleep()
+
+with TestGyp.LocalEnv({'CFLAGS': '-DFOO=1'}):
+ test.run_gyp('cflags.gyp')
+ test.build('cflags.gyp')
+
+expect = """FOO defined\n"""
+test.run_built_executable('cflags', stdout=expect)
+
+test.pass_test()
diff --git a/tools/gyp/test/compilable/gyptest-headers.py b/tools/gyp/test/compilable/gyptest-headers.py
new file mode 100755
index 00000000000000..91760216fbb55f
--- /dev/null
+++ b/tools/gyp/test/compilable/gyptest-headers.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 .hpp files are ignored when included in the source list on all
+platforms.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('headers.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+test.build('headers.gyp', test.ALL, chdir='relocate/src')
+
+expect = """\
+Hello from program.c
+Hello from lib1.c
+"""
+test.run_built_executable('program', chdir='relocate/src', stdout=expect)
+
+
+test.pass_test()
diff --git a/tools/gyp/test/compilable/src/headers.gyp b/tools/gyp/test/compilable/src/headers.gyp
new file mode 100644
index 00000000000000..b6c2a8857be940
--- /dev/null
+++ b/tools/gyp/test/compilable/src/headers.gyp
@@ -0,0 +1,26 @@
+# Copyright (c) 2009 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': 'program',
+ 'type': 'executable',
+ 'dependencies': [
+ 'lib1'
+ ],
+ 'sources': [
+ 'program.cpp',
+ ],
+ },
+ {
+ 'target_name': 'lib1',
+ 'type': 'static_library',
+ 'sources': [
+ 'lib1.hpp',
+ 'lib1.cpp',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/compilable/src/lib1.cpp b/tools/gyp/test/compilable/src/lib1.cpp
new file mode 100644
index 00000000000000..51bc31a40b86fa
--- /dev/null
+++ b/tools/gyp/test/compilable/src/lib1.cpp
@@ -0,0 +1,7 @@
+#include
+#include "lib1.hpp"
+
+void lib1_function(void) {
+ fprintf(stdout, "Hello from lib1.c\n");
+ fflush(stdout);
+}
diff --git a/tools/gyp/test/compilable/src/lib1.hpp b/tools/gyp/test/compilable/src/lib1.hpp
new file mode 100644
index 00000000000000..72e63e8acdbd70
--- /dev/null
+++ b/tools/gyp/test/compilable/src/lib1.hpp
@@ -0,0 +1,6 @@
+#ifndef _lib1_hpp
+#define _lib1_hpp
+
+extern void lib1_function(void);
+
+#endif
diff --git a/tools/gyp/test/compilable/src/program.cpp b/tools/gyp/test/compilable/src/program.cpp
new file mode 100644
index 00000000000000..8af2c9b6ffcb22
--- /dev/null
+++ b/tools/gyp/test/compilable/src/program.cpp
@@ -0,0 +1,9 @@
+#include
+#include "lib1.hpp"
+
+int main(void) {
+ fprintf(stdout, "Hello from program.c\n");
+ fflush(stdout);
+ lib1_function();
+ return 0;
+}
diff --git a/tools/gyp/test/compiler-override/compiler-exe.gyp b/tools/gyp/test/compiler-override/compiler-exe.gyp
new file mode 100644
index 00000000000000..c2f3002f20724d
--- /dev/null
+++ b/tools/gyp/test/compiler-override/compiler-exe.gyp
@@ -0,0 +1,16 @@
+# 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': [
+ 'test.c',
+ 'cxxtest.cc',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/compiler-override/compiler-global-settings.gyp.in b/tools/gyp/test/compiler-override/compiler-global-settings.gyp.in
new file mode 100644
index 00000000000000..ca13a53e8d88bb
--- /dev/null
+++ b/tools/gyp/test/compiler-override/compiler-global-settings.gyp.in
@@ -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.
+
+{
+ # PYTHON and PWD are replaced by the test code before this
+ # gyp file runs
+ 'make_global_settings': [
+ ['CC', r'$PYTHON $PWD/my_cc.py FOO'],
+ ['CXX', r'$PYTHON $PWD/my_cxx.py FOO'],
+ ['CC.host', r'$PYTHON $PWD/my_cc.py BAR'],
+ ['CXX.host', r'$PYTHON $PWD/my_cxx.py BAR'],
+
+ ['LD', r'$PYTHON $PWD/my_ld.py FOO_LINK'],
+ ['LD.host', r'$PYTHON $PWD/my_ld.py BAR_LINK'],
+ ['LINK', r'$PYTHON $PWD/my_ld.py FOO_LINK'],
+ ['LINK.host', r'$PYTHON $PWD/my_ld.py BAR_LINK'],
+ ],
+
+ # The above global settings should mean that
+ # that these targets are built using the fake
+ # toolchain above.
+ 'targets': [
+ {
+ 'toolset': '$TOOLSET',
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'test.c',
+ 'cxxtest.cc',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/compiler-override/compiler-host.gyp b/tools/gyp/test/compiler-override/compiler-host.gyp
new file mode 100644
index 00000000000000..ab3d247e0bfe19
--- /dev/null
+++ b/tools/gyp/test/compiler-override/compiler-host.gyp
@@ -0,0 +1,17 @@
+# 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': [
+ {
+ 'toolset': 'host',
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'test.c',
+ 'cxxtest.cc',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/compiler-override/compiler-shared-lib.gyp b/tools/gyp/test/compiler-override/compiler-shared-lib.gyp
new file mode 100644
index 00000000000000..d3e4316135db06
--- /dev/null
+++ b/tools/gyp/test/compiler-override/compiler-shared-lib.gyp
@@ -0,0 +1,16 @@
+# 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-lib',
+ 'type': 'shared_library',
+ 'sources': [
+ 'test.c',
+ 'cxxtest.cc',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/compiler-override/cxxtest.cc b/tools/gyp/test/compiler-override/cxxtest.cc
new file mode 100644
index 00000000000000..517a3536196283
--- /dev/null
+++ b/tools/gyp/test/compiler-override/cxxtest.cc
@@ -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.
+
+// Deliberate C syntax error as this file should never be passed to
+// the actual compiler
+#error Should not be passed to a real compiler
diff --git a/tools/gyp/test/compiler-override/gyptest-compiler-env-toolchain.py b/tools/gyp/test/compiler-override/gyptest-compiler-env-toolchain.py
new file mode 100644
index 00000000000000..2361d0c7c2a4c4
--- /dev/null
+++ b/tools/gyp/test/compiler-override/gyptest-compiler-env-toolchain.py
@@ -0,0 +1,78 @@
+#!/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 user can override the compiler and linker using
+CC/CXX/NM/READELF environment variables.
+"""
+
+import TestGyp
+import os
+import copy
+import sys
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+if sys.platform == 'win32':
+ # cross compiling not supported by ninja on windows
+ # and make not supported on windows at all.
+ sys.exit(0)
+
+# Clear any existing compiler related env vars.
+for key in ['CC', 'CXX', 'LINK', 'CC_host', 'CXX_host', 'LINK_host',
+ 'NM_target', 'READELF_target']:
+ if key in os.environ:
+ del os.environ[key]
+
+
+def CheckCompiler(test, gypfile, check_for, run_gyp):
+ if run_gyp:
+ test.run_gyp(gypfile)
+ test.build(gypfile)
+
+ test.must_contain_all_lines(test.stdout(), check_for)
+
+
+test = TestGyp.TestGyp(formats=['ninja'])
+# Must set the test format to something with a flavor (the part after the '-')
+# in order to test the desired behavior. Since we want to run a non-host
+# toolchain, we have to set the flavor to something that the ninja generator
+# doesn't know about, so it doesn't default to the host-specific tools (e.g.,
+# 'otool' on mac to generate the .TOC).
+#
+# Note that we can't just pass format=['ninja-some_toolchain'] to the
+# constructor above, because then this test wouldn't be recognized as a ninja
+# format test.
+test.formats = ['ninja-my_flavor' if f == 'ninja' else f for f in test.formats]
+
+
+def TestTargetOverideSharedLib():
+ # The std output from nm and readelf is redirected to files, so we can't
+ # expect their output to appear. Instead, check for the files they create to
+ # see if they actually ran.
+ expected = ['my_cc.py', 'my_cxx.py', 'FOO']
+
+ # Check that CC, CXX, NM, READELF, set target compiler
+ env = {'CC': 'python %s/my_cc.py FOO' % here,
+ 'CXX': 'python %s/my_cxx.py FOO' % here,
+ 'NM': 'python %s/my_nm.py' % here,
+ 'READELF': 'python %s/my_readelf.py' % here}
+
+ with TestGyp.LocalEnv(env):
+ CheckCompiler(test, 'compiler-shared-lib.gyp', expected, True)
+ test.must_contain(test.built_file_path('RAN_MY_NM'), 'RAN_MY_NM')
+ test.must_contain(test.built_file_path('RAN_MY_READELF'), 'RAN_MY_READELF')
+ test.unlink(test.built_file_path('RAN_MY_NM'))
+ test.unlink(test.built_file_path('RAN_MY_READELF'))
+
+ # Run the same tests once the eviron has been restored. The generated
+ # projects should have embedded all the settings in the project files so the
+ # results should be the same.
+ CheckCompiler(test, 'compiler-shared-lib.gyp', expected, False)
+ test.must_contain(test.built_file_path('RAN_MY_NM'), 'RAN_MY_NM')
+ test.must_contain(test.built_file_path('RAN_MY_READELF'), 'RAN_MY_READELF')
+
+
+TestTargetOverideSharedLib()
+test.pass_test()
diff --git a/tools/gyp/test/compiler-override/gyptest-compiler-env.py b/tools/gyp/test/compiler-override/gyptest-compiler-env.py
new file mode 100755
index 00000000000000..bb38b6e55bea9b
--- /dev/null
+++ b/tools/gyp/test/compiler-override/gyptest-compiler-env.py
@@ -0,0 +1,110 @@
+#!/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 user can override the compiler and linker using CC/CXX/LD
+environment variables.
+"""
+
+import TestGyp
+import os
+import copy
+import sys
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+if sys.platform == 'win32':
+ # cross compiling not supported by ninja on windows
+ # and make not supported on windows at all.
+ sys.exit(0)
+
+# Clear any existing compiler related env vars.
+for key in ['CC', 'CXX', 'LINK', 'CC_host', 'CXX_host', 'LINK_host']:
+ if key in os.environ:
+ del os.environ[key]
+
+
+def CheckCompiler(test, gypfile, check_for, run_gyp):
+ if run_gyp:
+ test.run_gyp(gypfile)
+ test.build(gypfile)
+
+ test.must_contain_all_lines(test.stdout(), check_for)
+
+
+test = TestGyp.TestGyp(formats=['ninja', 'make'])
+
+def TestTargetOveride():
+ expected = ['my_cc.py', 'my_cxx.py', 'FOO' ]
+
+ # ninja just uses $CC / $CXX as linker.
+ if test.format not in ['ninja', 'xcode-ninja']:
+ expected.append('FOO_LINK')
+
+ # Check that CC, CXX and LD set target compiler
+ oldenv = os.environ.copy()
+ try:
+ os.environ['CC'] = 'python %s/my_cc.py FOO' % here
+ os.environ['CXX'] = 'python %s/my_cxx.py FOO' % here
+ os.environ['LINK'] = 'python %s/my_ld.py FOO_LINK' % here
+
+ CheckCompiler(test, 'compiler-exe.gyp', expected, True)
+ finally:
+ os.environ.clear()
+ os.environ.update(oldenv)
+
+ # Run the same tests once the eviron has been restored. The
+ # generated should have embedded all the settings in the
+ # project files so the results should be the same.
+ CheckCompiler(test, 'compiler-exe.gyp', expected, False)
+
+
+def TestTargetOverideCompilerOnly():
+ # Same test again but with that CC, CXX and not LD
+ oldenv = os.environ.copy()
+ try:
+ os.environ['CC'] = 'python %s/my_cc.py FOO' % here
+ os.environ['CXX'] = 'python %s/my_cxx.py FOO' % here
+
+ CheckCompiler(test, 'compiler-exe.gyp',
+ ['my_cc.py', 'my_cxx.py', 'FOO'],
+ True)
+ finally:
+ os.environ.clear()
+ os.environ.update(oldenv)
+
+ # Run the same tests once the eviron has been restored. The
+ # generated should have embedded all the settings in the
+ # project files so the results should be the same.
+ CheckCompiler(test, 'compiler-exe.gyp',
+ ['my_cc.py', 'my_cxx.py', 'FOO'],
+ False)
+
+
+def TestHostOveride():
+ expected = ['my_cc.py', 'my_cxx.py', 'HOST' ]
+ if test.format != 'ninja': # ninja just uses $CC / $CXX as linker.
+ expected.append('HOST_LINK')
+
+ # Check that CC_host sets host compilee
+ oldenv = os.environ.copy()
+ try:
+ os.environ['CC_host'] = 'python %s/my_cc.py HOST' % here
+ os.environ['CXX_host'] = 'python %s/my_cxx.py HOST' % here
+ os.environ['LINK_host'] = 'python %s/my_ld.py HOST_LINK' % here
+ CheckCompiler(test, 'compiler-host.gyp', expected, True)
+ finally:
+ os.environ.clear()
+ os.environ.update(oldenv)
+
+ # Run the same tests once the eviron has been restored. The
+ # generated should have embedded all the settings in the
+ # project files so the results should be the same.
+ CheckCompiler(test, 'compiler-host.gyp', expected, False)
+
+
+TestTargetOveride()
+TestTargetOverideCompilerOnly()
+
+test.pass_test()
diff --git a/tools/gyp/test/compiler-override/gyptest-compiler-global-settings.py b/tools/gyp/test/compiler-override/gyptest-compiler-global-settings.py
new file mode 100755
index 00000000000000..d58fc7c2f995d6
--- /dev/null
+++ b/tools/gyp/test/compiler-override/gyptest-compiler-global-settings.py
@@ -0,0 +1,78 @@
+#!/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 make_global_settings can be used to override the
+compiler settings.
+"""
+
+import TestGyp
+import os
+import copy
+import sys
+from string import Template
+
+
+if sys.platform == 'win32':
+ # cross compiling not support by ninja on windows
+ # and make not supported on windows at all.
+ sys.exit(0)
+
+test = TestGyp.TestGyp(formats=['ninja', 'make'])
+
+gypfile = 'compiler-global-settings.gyp'
+
+replacements = { 'PYTHON': '/usr/bin/python', 'PWD': os.getcwd()}
+
+# Process the .in gyp file to produce the final gyp file
+# since we need to include absolute paths in the make_global_settings
+# section.
+replacements['TOOLSET'] = 'target'
+s = Template(open(gypfile + '.in').read())
+output = open(gypfile, 'w')
+output.write(s.substitute(replacements))
+output.close()
+
+old_env = dict(os.environ)
+os.environ['GYP_CROSSCOMPILE'] = '1'
+test.run_gyp(gypfile)
+os.environ.clear()
+os.environ.update(old_env)
+
+test.build(gypfile)
+test.must_contain_all_lines(test.stdout(), ['my_cc.py', 'my_cxx.py', 'FOO'])
+
+# The xcode generator chokes on the 'host' toolset. Skip the rest of
+# this test (cf. https://code.google.com/p/gyp/issues/detail?id=454).
+if test.format == 'xcode-ninja':
+ test.pass_test()
+
+# Same again but with the host toolset.
+replacements['TOOLSET'] = 'host'
+s = Template(open(gypfile + '.in').read())
+output = open(gypfile, 'w')
+output.write(s.substitute(replacements))
+output.close()
+
+old_env = dict(os.environ)
+os.environ['GYP_CROSSCOMPILE'] = '1'
+test.run_gyp(gypfile)
+os.environ.clear()
+os.environ.update(old_env)
+
+test.build(gypfile)
+test.must_contain_all_lines(test.stdout(), ['my_cc.py', 'my_cxx.py', 'BAR'])
+
+# Check that CC_host overrides make_global_settings
+old_env = dict(os.environ)
+os.environ['CC_host'] = '%s %s/my_cc.py SECRET' % (replacements['PYTHON'],
+ replacements['PWD'])
+test.run_gyp(gypfile)
+os.environ.clear()
+os.environ.update(old_env)
+
+test.build(gypfile)
+test.must_contain_all_lines(test.stdout(), ['SECRET', 'my_cxx.py', 'BAR'])
+
+test.pass_test()
diff --git a/tools/gyp/test/compiler-override/my_cc.py b/tools/gyp/test/compiler-override/my_cc.py
new file mode 100755
index 00000000000000..e2f0bdd51c7c55
--- /dev/null
+++ b/tools/gyp/test/compiler-override/my_cc.py
@@ -0,0 +1,6 @@
+#!/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 sys
+print sys.argv
diff --git a/tools/gyp/test/compiler-override/my_cxx.py b/tools/gyp/test/compiler-override/my_cxx.py
new file mode 100755
index 00000000000000..e2f0bdd51c7c55
--- /dev/null
+++ b/tools/gyp/test/compiler-override/my_cxx.py
@@ -0,0 +1,6 @@
+#!/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 sys
+print sys.argv
diff --git a/tools/gyp/test/compiler-override/my_ld.py b/tools/gyp/test/compiler-override/my_ld.py
new file mode 100755
index 00000000000000..e2f0bdd51c7c55
--- /dev/null
+++ b/tools/gyp/test/compiler-override/my_ld.py
@@ -0,0 +1,6 @@
+#!/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 sys
+print sys.argv
diff --git a/tools/gyp/test/compiler-override/my_nm.py b/tools/gyp/test/compiler-override/my_nm.py
new file mode 100755
index 00000000000000..f0f1efcf73bd7c
--- /dev/null
+++ b/tools/gyp/test/compiler-override/my_nm.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import sys
+print sys.argv
+with open('RAN_MY_NM', 'w') as f:
+ f.write('RAN_MY_NM')
diff --git a/tools/gyp/test/compiler-override/my_readelf.py b/tools/gyp/test/compiler-override/my_readelf.py
new file mode 100755
index 00000000000000..40e303cd76f74e
--- /dev/null
+++ b/tools/gyp/test/compiler-override/my_readelf.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import sys
+print sys.argv
+with open('RAN_MY_READELF', 'w') as f:
+ f.write('RAN_MY_READELF')
diff --git a/tools/gyp/test/compiler-override/test.c b/tools/gyp/test/compiler-override/test.c
new file mode 100644
index 00000000000000..517a3536196283
--- /dev/null
+++ b/tools/gyp/test/compiler-override/test.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.
+
+// Deliberate C syntax error as this file should never be passed to
+// the actual compiler
+#error Should not be passed to a real compiler
diff --git a/tools/gyp/test/conditions/elseif/elseif.gyp b/tools/gyp/test/conditions/elseif/elseif.gyp
new file mode 100644
index 00000000000000..6367ff7d7afef5
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/elseif.gyp
@@ -0,0 +1,43 @@
+# Copyright (c) 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'variables': { 'test_var': 0 },
+ 'target_name': 'program0',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'includes': [ 'elseif_conditions.gypi' ],
+ },
+ {
+ 'variables': { 'test_var': 1 },
+ 'target_name': 'program1',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'includes': [ 'elseif_conditions.gypi' ],
+ },
+ {
+ 'variables': { 'test_var': 2 },
+ 'target_name': 'program2',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'includes': [ 'elseif_conditions.gypi' ],
+ },
+ {
+ 'variables': { 'test_var': 3 },
+ 'target_name': 'program3',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'includes': [ 'elseif_conditions.gypi' ],
+ },
+ {
+ 'variables': { 'test_var': 4 },
+ 'target_name': 'program4',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'includes': [ 'elseif_conditions.gypi' ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/conditions/elseif/elseif_bad1.gyp b/tools/gyp/test/conditions/elseif/elseif_bad1.gyp
new file mode 100644
index 00000000000000..35c8455ccae177
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/elseif_bad1.gyp
@@ -0,0 +1,20 @@
+# Copyright (c) 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Trigger an error because of two consecutive string conditions.
+
+{
+ 'targets': [
+ {
+ 'variables': { 'test_var': 0 },
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'conditions': [
+ ['test_var==0', 'test_var==1', {
+ }],
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/conditions/elseif/elseif_bad2.gyp b/tools/gyp/test/conditions/elseif/elseif_bad2.gyp
new file mode 100644
index 00000000000000..b529f292c06f98
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/elseif_bad2.gyp
@@ -0,0 +1,22 @@
+# Copyright (c) 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Trigger an error because of two consecutive string conditions, even if the
+# conditions are not actually evaluated.
+
+{
+ 'targets': [
+ {
+ 'variables': { 'test_var': 0 },
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'conditions': [
+ ['test_var==0', {
+ }, 'test_var==1', 'test_var==2', {
+ }],
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/conditions/elseif/elseif_bad3.gyp b/tools/gyp/test/conditions/elseif/elseif_bad3.gyp
new file mode 100644
index 00000000000000..126e18605373b7
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/elseif_bad3.gyp
@@ -0,0 +1,23 @@
+# Copyright (c) 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Trigger an error because there are unexpected trailing items in a condition.
+
+{
+ 'targets': [
+ {
+ 'variables': { 'test_var': 0 },
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'conditions': [
+ ['test_var==0' {
+ }, 'test_var==1', {
+ }, {
+ }, 'test_var==2', {
+ }],
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/conditions/elseif/elseif_conditions.gypi b/tools/gyp/test/conditions/elseif/elseif_conditions.gypi
new file mode 100644
index 00000000000000..4310ccc0312890
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/elseif_conditions.gypi
@@ -0,0 +1,15 @@
+{
+ 'conditions': [
+ ['test_var==0', {
+ 'defines': ['FOO="first_if"'],
+ }, 'test_var==1', {
+ 'defines': ['FOO="first_else_if"'],
+ }, 'test_var==2', {
+ 'defines': ['FOO="second_else_if"'],
+ }, 'test_var==3', {
+ 'defines': ['FOO="third_else_if"'],
+ }, {
+ 'defines': ['FOO="last_else"'],
+ }],
+ ],
+}
diff --git a/tools/gyp/test/conditions/elseif/gyptest_elseif.py b/tools/gyp/test/conditions/elseif/gyptest_elseif.py
new file mode 100644
index 00000000000000..9d030cf3fe0a8a
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/gyptest_elseif.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verify that "else-if" conditions work.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('elseif.gyp')
+test.build('elseif.gyp', test.ALL)
+test.run_built_executable(
+ 'program0', stdout='first_if\n')
+test.run_built_executable(
+ 'program1', stdout='first_else_if\n')
+test.run_built_executable(
+ 'program2', stdout='second_else_if\n')
+test.run_built_executable(
+ 'program3', stdout='third_else_if\n')
+test.run_built_executable(
+ 'program4', stdout='last_else\n')
+
+# Verify that bad condition blocks fail at gyp time.
+test.run_gyp('elseif_bad1.gyp', status=1, stderr=None)
+test.run_gyp('elseif_bad2.gyp', status=1, stderr=None)
+test.run_gyp('elseif_bad3.gyp', status=1, stderr=None)
+
+test.pass_test()
diff --git a/tools/gyp/test/conditions/elseif/program.cc b/tools/gyp/test/conditions/elseif/program.cc
new file mode 100644
index 00000000000000..147fe2f75e4694
--- /dev/null
+++ b/tools/gyp/test/conditions/elseif/program.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include
+
+int main() {
+ printf("%s\n", FOO);
+ return 0;
+}
diff --git a/tools/gyp/test/configurations/basics/configurations.c b/tools/gyp/test/configurations/basics/configurations.c
new file mode 100644
index 00000000000000..39e13c9c83fb0f
--- /dev/null
+++ b/tools/gyp/test/configurations/basics/configurations.c
@@ -0,0 +1,15 @@
+#include
+
+int main(void)
+{
+#ifdef FOO
+ printf("Foo configuration\n");
+#endif
+#ifdef DEBUG
+ printf("Debug configuration\n");
+#endif
+#ifdef RELEASE
+ printf("Release configuration\n");
+#endif
+ return 0;
+}
diff --git a/tools/gyp/test/configurations/basics/configurations.gyp b/tools/gyp/test/configurations/basics/configurations.gyp
new file mode 100644
index 00000000000000..93f1d8d5c7fe09
--- /dev/null
+++ b/tools/gyp/test/configurations/basics/configurations.gyp
@@ -0,0 +1,32 @@
+# Copyright (c) 2009 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': 'configurations',
+ 'type': 'executable',
+ 'sources': [
+ 'configurations.c',
+ ],
+ 'configurations': {
+ 'Debug': {
+ 'defines': [
+ 'DEBUG',
+ ],
+ },
+ 'Release': {
+ 'defines': [
+ 'RELEASE',
+ ],
+ },
+ 'Foo': {
+ 'defines': [
+ 'FOO',
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/basics/gyptest-configurations.py b/tools/gyp/test/configurations/basics/gyptest-configurations.py
new file mode 100755
index 00000000000000..1cf1995db78c16
--- /dev/null
+++ b/tools/gyp/test/configurations/basics/gyptest-configurations.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 build of an executable in three different configurations.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+if test.format == 'android':
+ # This test currently fails on android. Investigate why, fix the issues
+ # responsible, and reenable this test on android. See bug:
+ # https://code.google.com/p/gyp/issues/detail?id=436
+ test.skip_test(message='Test fails on android. Fix and reenable.\n')
+
+test.run_gyp('configurations.gyp')
+
+test.set_configuration('Release')
+test.build('configurations.gyp')
+test.run_built_executable('configurations', stdout="Release configuration\n")
+
+test.set_configuration('Debug')
+test.build('configurations.gyp')
+test.run_built_executable('configurations', stdout="Debug configuration\n")
+
+test.set_configuration('Foo')
+test.build('configurations.gyp')
+test.run_built_executable('configurations', stdout="Foo configuration\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/configurations/inheritance/configurations.c b/tools/gyp/test/configurations/inheritance/configurations.c
new file mode 100644
index 00000000000000..ebb9f8450e2722
--- /dev/null
+++ b/tools/gyp/test/configurations/inheritance/configurations.c
@@ -0,0 +1,21 @@
+#include
+
+int main(void)
+{
+#ifdef BASE
+ printf("Base configuration\n");
+#endif
+#ifdef COMMON
+ printf("Common configuration\n");
+#endif
+#ifdef COMMON2
+ printf("Common2 configuration\n");
+#endif
+#ifdef DEBUG
+ printf("Debug configuration\n");
+#endif
+#ifdef RELEASE
+ printf("Release configuration\n");
+#endif
+ return 0;
+}
diff --git a/tools/gyp/test/configurations/inheritance/configurations.gyp b/tools/gyp/test/configurations/inheritance/configurations.gyp
new file mode 100644
index 00000000000000..9441376b4d1b2b
--- /dev/null
+++ b/tools/gyp/test/configurations/inheritance/configurations.gyp
@@ -0,0 +1,40 @@
+# Copyright (c) 2009 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': {
+ 'Base': {
+ 'abstract': 1,
+ 'defines': ['BASE'],
+ },
+ 'Common': {
+ 'abstract': 1,
+ 'inherit_from': ['Base'],
+ 'defines': ['COMMON'],
+ },
+ 'Common2': {
+ 'abstract': 1,
+ 'defines': ['COMMON2'],
+ },
+ 'Debug': {
+ 'inherit_from': ['Common', 'Common2'],
+ 'defines': ['DEBUG'],
+ },
+ 'Release': {
+ 'inherit_from': ['Common', 'Common2'],
+ 'defines': ['RELEASE'],
+ },
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'executable',
+ 'sources': [
+ 'configurations.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/inheritance/duplicates.gyp b/tools/gyp/test/configurations/inheritance/duplicates.gyp
new file mode 100644
index 00000000000000..6930ce3b39a12b
--- /dev/null
+++ b/tools/gyp/test/configurations/inheritance/duplicates.gyp
@@ -0,0 +1,27 @@
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'A',
+ 'configurations': {
+ 'A': {
+ 'defines': ['SOMETHING'],
+ },
+ 'B': {
+ 'inherit_from': ['A'],
+ },
+ },
+ 'cflags': ['-g'],
+ },
+ 'targets': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'executable',
+ 'sources': [
+ 'configurations.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/inheritance/duplicates.gypd.golden b/tools/gyp/test/configurations/inheritance/duplicates.gypd.golden
new file mode 100644
index 00000000000000..719b70861e326f
--- /dev/null
+++ b/tools/gyp/test/configurations/inheritance/duplicates.gypd.golden
@@ -0,0 +1,12 @@
+{'_DEPTH': '.',
+ 'included_files': ['duplicates.gyp'],
+ 'targets': [{'configurations': {'A': {'cflags': ['-g'],
+ 'defines': ['SOMETHING']},
+ 'B': {'cflags': ['-g'],
+ 'defines': ['SOMETHING'],
+ 'inherit_from': ['A']}},
+ 'default_configuration': 'A',
+ 'sources': ['configurations.c'],
+ 'target_name': 'configurations',
+ 'toolset': 'target',
+ 'type': 'executable'}]}
diff --git a/tools/gyp/test/configurations/inheritance/gyptest-duplicates.py b/tools/gyp/test/configurations/inheritance/gyptest-duplicates.py
new file mode 100755
index 00000000000000..46687b4549b3a5
--- /dev/null
+++ b/tools/gyp/test/configurations/inheritance/gyptest-duplicates.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that configurations do not duplicate other settings.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(format='gypd')
+
+test.run_gyp('duplicates.gyp')
+
+# Verify the duplicates.gypd against the checked-in expected contents.
+#
+# Normally, we should canonicalize line endings in the expected
+# contents file setting the Subversion svn:eol-style to native,
+# but that would still fail if multiple systems are sharing a single
+# workspace on a network-mounted file system. Consequently, we
+# massage the Windows line endings ('\r\n') in the output to the
+# checked-in UNIX endings ('\n').
+
+contents = test.read('duplicates.gypd').replace(
+ '\r', '').replace('\\\\', '/')
+expect = test.read('duplicates.gypd.golden').replace('\r', '')
+if not test.match(contents, expect):
+ print "Unexpected contents of `duplicates.gypd'"
+ test.diff(expect, contents, 'duplicates.gypd ')
+ test.fail_test()
+
+test.pass_test()
diff --git a/tools/gyp/test/configurations/inheritance/gyptest-inheritance.py b/tools/gyp/test/configurations/inheritance/gyptest-inheritance.py
new file mode 100755
index 00000000000000..ecc9d08345864a
--- /dev/null
+++ b/tools/gyp/test/configurations/inheritance/gyptest-inheritance.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 build of an executable in three different configurations.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+if test.format == 'android':
+ # This test currently fails on android. Investigate why, fix the issues
+ # responsible, and reenable this test on android. See bug:
+ # https://code.google.com/p/gyp/issues/detail?id=436
+ test.skip_test(message='Test fails on android. Fix and reenable.\n')
+
+test.run_gyp('configurations.gyp')
+
+test.set_configuration('Release')
+test.build('configurations.gyp')
+test.run_built_executable('configurations',
+ stdout=('Base configuration\n'
+ 'Common configuration\n'
+ 'Common2 configuration\n'
+ 'Release configuration\n'))
+
+test.set_configuration('Debug')
+test.build('configurations.gyp')
+test.run_built_executable('configurations',
+ stdout=('Base configuration\n'
+ 'Common configuration\n'
+ 'Common2 configuration\n'
+ 'Debug configuration\n'))
+
+test.pass_test()
diff --git a/tools/gyp/test/configurations/invalid/actions.gyp b/tools/gyp/test/configurations/invalid/actions.gyp
new file mode 100644
index 00000000000000..a6e42089ebaf3c
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/actions.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'actions': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/all_dependent_settings.gyp b/tools/gyp/test/configurations/invalid/all_dependent_settings.gyp
new file mode 100644
index 00000000000000..b16a245df5468c
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/all_dependent_settings.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'all_dependent_settings': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/configurations.gyp b/tools/gyp/test/configurations/invalid/configurations.gyp
new file mode 100644
index 00000000000000..2cfc9600498ec8
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/configurations.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'configurations': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/dependencies.gyp b/tools/gyp/test/configurations/invalid/dependencies.gyp
new file mode 100644
index 00000000000000..74633f3f111337
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/dependencies.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'dependencies': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp b/tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp
new file mode 100644
index 00000000000000..8a0f2e95ea7138
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/direct_dependent_settings.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'direct_dependent_settings': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/gyptest-configurations.py b/tools/gyp/test/configurations/invalid/gyptest-configurations.py
new file mode 100755
index 00000000000000..bd844b95dd8a33
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/gyptest-configurations.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+Verifies build of an executable in three different configurations.
+"""
+
+import TestGyp
+
+# Keys that do not belong inside a configuration dictionary.
+invalid_configuration_keys = [
+ 'actions',
+ 'all_dependent_settings',
+ 'configurations',
+ 'dependencies',
+ 'direct_dependent_settings',
+ 'libraries',
+ 'link_settings',
+ 'sources',
+ 'standalone_static_library',
+ 'target_name',
+ 'type',
+]
+
+test = TestGyp.TestGyp()
+
+for test_key in invalid_configuration_keys:
+ test.run_gyp('%s.gyp' % test_key, status=1, stderr=None)
+ expect = ['%s not allowed in the Debug configuration, found in target '
+ '%s.gyp:configurations#target' % (test_key, test_key)]
+ test.must_contain_all_lines(test.stderr(), expect)
+
+test.pass_test()
diff --git a/tools/gyp/test/configurations/invalid/libraries.gyp b/tools/gyp/test/configurations/invalid/libraries.gyp
new file mode 100644
index 00000000000000..c4014ed4065dc0
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/libraries.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'libraries': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/link_settings.gyp b/tools/gyp/test/configurations/invalid/link_settings.gyp
new file mode 100644
index 00000000000000..2f0e1c46f5c98c
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/link_settings.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'link_settings': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/sources.gyp b/tools/gyp/test/configurations/invalid/sources.gyp
new file mode 100644
index 00000000000000..b38cca03818967
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/sources.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'sources': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/standalone_static_library.gyp b/tools/gyp/test/configurations/invalid/standalone_static_library.gyp
new file mode 100644
index 00000000000000..2edb9febd64ab5
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/standalone_static_library.gyp
@@ -0,0 +1,17 @@
+# 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': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'standalone_static_library': 1,
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/target_name.gyp b/tools/gyp/test/configurations/invalid/target_name.gyp
new file mode 100644
index 00000000000000..83baad95d60069
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/target_name.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'target_name': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/invalid/type.gyp b/tools/gyp/test/configurations/invalid/type.gyp
new file mode 100644
index 00000000000000..bc55898b89e063
--- /dev/null
+++ b/tools/gyp/test/configurations/invalid/type.gyp
@@ -0,0 +1,18 @@
+# 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': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'none',
+ 'configurations': {
+ 'Debug': {
+ 'type': [
+ ],
+ },
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/target_platform/configurations.gyp b/tools/gyp/test/configurations/target_platform/configurations.gyp
new file mode 100644
index 00000000000000..d15429f4e5d7eb
--- /dev/null
+++ b/tools/gyp/test/configurations/target_platform/configurations.gyp
@@ -0,0 +1,58 @@
+# Copyright (c) 2009 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_Win32': {
+ 'msvs_configuration_platform': 'Win32',
+ },
+ 'Debug_x64': {
+ 'msvs_configuration_platform': 'x64',
+ },
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'left',
+ 'type': 'static_library',
+ 'sources': [
+ 'left.c',
+ ],
+ 'configurations': {
+ 'Debug_Win32': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ 'target_name': 'right',
+ 'type': 'static_library',
+ 'sources': [
+ 'right.c',
+ ],
+ },
+ {
+ 'target_name': 'front_left',
+ 'type': 'executable',
+ 'dependencies': ['left'],
+ 'sources': [
+ 'front.c',
+ ],
+ 'configurations': {
+ 'Debug_Win32': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ 'target_name': 'front_right',
+ 'type': 'executable',
+ 'dependencies': ['right'],
+ 'sources': [
+ 'front.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/target_platform/front.c b/tools/gyp/test/configurations/target_platform/front.c
new file mode 100644
index 00000000000000..7a91689ff5c130
--- /dev/null
+++ b/tools/gyp/test/configurations/target_platform/front.c
@@ -0,0 +1,8 @@
+#include
+
+const char *message(void);
+
+int main(void) {
+ printf("%s\n", message());
+ return 0;
+}
diff --git a/tools/gyp/test/configurations/target_platform/gyptest-target_platform.py b/tools/gyp/test/configurations/target_platform/gyptest-target_platform.py
new file mode 100755
index 00000000000000..ae4e9e5a2dffe2
--- /dev/null
+++ b/tools/gyp/test/configurations/target_platform/gyptest-target_platform.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 the msvs specific msvs_target_platform option.
+"""
+
+import TestGyp
+import TestCommon
+
+
+def RunX64(exe, stdout):
+ try:
+ test.run_built_executable(exe, stdout=stdout)
+ except WindowsError, e:
+ # Assume the exe is 64-bit if it can't load on 32-bit systems.
+ # Both versions of the error are required because different versions
+ # of python seem to return different errors for invalid exe type.
+ if e.errno != 193 and '[Error 193]' not in str(e):
+ raise
+
+
+test = TestGyp.TestGyp(formats=['msvs'])
+
+test.run_gyp('configurations.gyp')
+
+test.set_configuration('Debug|x64')
+test.build('configurations.gyp', rebuild=True)
+RunX64('front_left', stdout=('left\n'))
+RunX64('front_right', stdout=('right\n'))
+
+test.set_configuration('Debug|Win32')
+test.build('configurations.gyp', rebuild=True)
+RunX64('front_left', stdout=('left\n'))
+test.run_built_executable('front_right', stdout=('right\n'))
+
+test.pass_test()
diff --git a/tools/gyp/test/configurations/target_platform/left.c b/tools/gyp/test/configurations/target_platform/left.c
new file mode 100644
index 00000000000000..1ce2ea1227218f
--- /dev/null
+++ b/tools/gyp/test/configurations/target_platform/left.c
@@ -0,0 +1,3 @@
+const char *message(void) {
+ return "left";
+}
diff --git a/tools/gyp/test/configurations/target_platform/right.c b/tools/gyp/test/configurations/target_platform/right.c
new file mode 100644
index 00000000000000..b1578492fe407d
--- /dev/null
+++ b/tools/gyp/test/configurations/target_platform/right.c
@@ -0,0 +1,3 @@
+const char *message(void) {
+ return "right";
+}
diff --git a/tools/gyp/test/configurations/x64/configurations.c b/tools/gyp/test/configurations/x64/configurations.c
new file mode 100644
index 00000000000000..37018438fc7bfe
--- /dev/null
+++ b/tools/gyp/test/configurations/x64/configurations.c
@@ -0,0 +1,12 @@
+#include
+
+int main(void) {
+ if (sizeof(void*) == 4) {
+ printf("Running Win32\n");
+ } else if (sizeof(void*) == 8) {
+ printf("Running x64\n");
+ } else {
+ printf("Unexpected platform\n");
+ }
+ return 0;
+}
diff --git a/tools/gyp/test/configurations/x64/configurations.gyp b/tools/gyp/test/configurations/x64/configurations.gyp
new file mode 100644
index 00000000000000..8b0139f1418904
--- /dev/null
+++ b/tools/gyp/test/configurations/x64/configurations.gyp
@@ -0,0 +1,38 @@
+# Copyright (c) 2009 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': {
+ 'msvs_configuration_platform': 'Win32',
+ },
+ 'Debug_x64': {
+ 'inherit_from': ['Debug'],
+ 'msvs_configuration_platform': 'x64',
+ },
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'configurations',
+ 'type': 'executable',
+ 'sources': [
+ 'configurations.c',
+ ],
+ },
+ {
+ 'target_name': 'configurations64',
+ 'type': 'executable',
+ 'sources': [
+ 'configurations.c',
+ ],
+ 'configurations': {
+ 'Debug': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/configurations/x64/gyptest-x86.py b/tools/gyp/test/configurations/x64/gyptest-x86.py
new file mode 100755
index 00000000000000..8675d8f7e76b99
--- /dev/null
+++ b/tools/gyp/test/configurations/x64/gyptest-x86.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.
+
+"""
+Verifies build of an executable in three different configurations.
+"""
+
+import TestGyp
+
+import sys
+
+formats = ['msvs']
+if sys.platform == 'win32':
+ formats += ['ninja']
+test = TestGyp.TestGyp(formats=formats)
+
+test.run_gyp('configurations.gyp')
+test.set_configuration('Debug|Win32')
+test.build('configurations.gyp', test.ALL)
+
+for machine, suffix in [('14C machine (x86)', ''),
+ ('8664 machine (x64)', '64')]:
+ output = test.run_dumpbin(
+ '/headers', test.built_file_path('configurations%s.exe' % suffix))
+ if machine not in output:
+ test.fail_test()
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/gyptest-all.py b/tools/gyp/test/copies/gyptest-all.py
new file mode 100755
index 00000000000000..aeccf3324c1c2e
--- /dev/null
+++ b/tools/gyp/test/copies/gyptest-all.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 file copies using an explicit build target of 'all'.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('copies.gyp',
+ '-G', 'xcode_ninja_target_pattern=^(?!copies_null)',
+ chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+test.build('copies.gyp', test.ALL, chdir='relocate/src')
+
+test.must_match(['relocate', 'src', 'copies-out', 'file1'], 'file1 contents\n')
+
+test.built_file_must_match('copies-out/file2',
+ 'file2 contents\n',
+ chdir='relocate/src')
+
+test.built_file_must_match('copies-out/directory/file3',
+ 'file3 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out/directory/file4',
+ 'file4 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out/directory/subdir/file5',
+ 'file5 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out/subdir/file6',
+ 'file6 contents\n',
+ chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/gyptest-attribs.py b/tools/gyp/test/copies/gyptest-attribs.py
new file mode 100644
index 00000000000000..70d717a45ecf60
--- /dev/null
+++ b/tools/gyp/test/copies/gyptest-attribs.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 copying files preserves file attributes.
+"""
+
+import TestGyp
+
+import os
+import stat
+import sys
+
+
+def check_attribs(path, expected_exec_bit):
+ out_path = test.built_file_path(path, chdir='src')
+
+ in_stat = os.stat(os.path.join('src', path))
+ out_stat = os.stat(out_path)
+ if out_stat.st_mode & stat.S_IXUSR != expected_exec_bit:
+ test.fail_test()
+
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('copies-attribs.gyp', chdir='src')
+
+test.build('copies-attribs.gyp', chdir='src')
+
+if sys.platform != 'win32':
+ out_path = test.built_file_path('executable-file.sh', chdir='src')
+ test.must_contain(out_path,
+ '#!/bin/bash\n'
+ '\n'
+ 'echo echo echo echo cho ho o o\n')
+ check_attribs('executable-file.sh', expected_exec_bit=stat.S_IXUSR)
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/gyptest-default.py b/tools/gyp/test/copies/gyptest-default.py
new file mode 100755
index 00000000000000..a916869f0dbd3c
--- /dev/null
+++ b/tools/gyp/test/copies/gyptest-default.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 file copies using the build tool default.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('copies.gyp',
+ '-G', 'xcode_ninja_target_pattern=^(?!copies_null)',
+ chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+test.build('copies.gyp', chdir='relocate/src')
+
+test.must_match(['relocate', 'src', 'copies-out', 'file1'], 'file1 contents\n')
+
+test.built_file_must_match('copies-out/file2',
+ 'file2 contents\n',
+ chdir='relocate/src')
+
+test.built_file_must_match('copies-out/directory/file3',
+ 'file3 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out/directory/file4',
+ 'file4 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out/directory/subdir/file5',
+ 'file5 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out/subdir/file6',
+ 'file6 contents\n',
+ chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/gyptest-samedir.py b/tools/gyp/test/copies/gyptest-samedir.py
new file mode 100755
index 00000000000000..16547f48d8b70d
--- /dev/null
+++ b/tools/gyp/test/copies/gyptest-samedir.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 file copies where two copies sections in the same target have the
+same destination directory.
+"""
+
+import TestGyp
+
+# The Android build system doesn't allow output to go to arbitrary places.
+test = TestGyp.TestGyp(formats=['!android'])
+test.run_gyp('copies-samedir.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+test.build('copies-samedir.gyp', 'copies_samedir', chdir='relocate/src')
+
+test.built_file_must_match('copies-out-samedir/file1',
+ 'file1 contents\n',
+ chdir='relocate/src')
+
+test.built_file_must_match('copies-out-samedir/file2',
+ 'file2 contents\n',
+ chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/gyptest-slash.py b/tools/gyp/test/copies/gyptest-slash.py
new file mode 100755
index 00000000000000..f7a2e549ebe65f
--- /dev/null
+++ b/tools/gyp/test/copies/gyptest-slash.py
@@ -0,0 +1,39 @@
+#!/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.
+
+"""
+Verifies file copies with a trailing slash in the destination directory.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+test.run_gyp('copies-slash.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+test.build('copies-slash.gyp', chdir='relocate/src')
+
+test.built_file_must_match('copies-out-slash/directory/file3',
+ 'file3 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out-slash/directory/file4',
+ 'file4 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out-slash/directory/subdir/file5',
+ 'file5 contents\n',
+ chdir='relocate/src')
+
+test.built_file_must_match('copies-out-slash-2/directory/file3',
+ 'file3 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out-slash-2/directory/file4',
+ 'file4 contents\n',
+ chdir='relocate/src')
+test.built_file_must_match('copies-out-slash-2/directory/subdir/file5',
+ 'file5 contents\n',
+ chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/gyptest-updir.py b/tools/gyp/test/copies/gyptest-updir.py
new file mode 100755
index 00000000000000..afd3abcc76c24f
--- /dev/null
+++ b/tools/gyp/test/copies/gyptest-updir.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 file copies where the destination is one level above an expansion that
+yields a make variable.
+"""
+
+import TestGyp
+
+# The Android build system doesn't allow output to go to arbitrary places.
+test = TestGyp.TestGyp(formats=['!android'])
+test.run_gyp('copies-updir.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+test.build('copies-updir.gyp', 'copies_up', chdir='relocate/src')
+
+test.built_file_must_match('../copies-out-updir/file1',
+ 'file1 contents\n',
+ chdir='relocate/src')
+
+test.pass_test()
diff --git a/tools/gyp/test/copies/src/copies-attribs.gyp b/tools/gyp/test/copies/src/copies-attribs.gyp
new file mode 100644
index 00000000000000..073e0d0cf677d4
--- /dev/null
+++ b/tools/gyp/test/copies/src/copies-attribs.gyp
@@ -0,0 +1,20 @@
+# Copyright (c) 2013 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': 'copies1',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [
+ 'executable-file.sh',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/copies/src/copies-samedir.gyp b/tools/gyp/test/copies/src/copies-samedir.gyp
new file mode 100644
index 00000000000000..2919ce503eda42
--- /dev/null
+++ b/tools/gyp/test/copies/src/copies-samedir.gyp
@@ -0,0 +1,37 @@
+# Copyright (c) 2013 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': 'copies_samedir',
+ 'type': 'none',
+ 'dependencies': [
+ 'copies_samedir_dependency',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out-samedir',
+ 'files': [
+ 'file1',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'copies_samedir_dependency',
+ 'type': 'none',
+ 'direct_dependent_settings': {
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out-samedir',
+ 'files': [
+ 'file2',
+ ],
+ },
+ ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/copies/src/copies-slash.gyp b/tools/gyp/test/copies/src/copies-slash.gyp
new file mode 100644
index 00000000000000..9b8ac4239d54f2
--- /dev/null
+++ b/tools/gyp/test/copies/src/copies-slash.gyp
@@ -0,0 +1,35 @@
+# 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': [
+ # A trailing slash on the destination directory should be ignored.
+ {
+ 'target_name': 'copies_recursive_trailing_slash',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out-slash/',
+ 'files': [
+ 'directory/',
+ ],
+ },
+ ],
+ },
+ # Even if the source directory is below <(PRODUCT_DIR).
+ {
+ 'target_name': 'copies_recursive_trailing_slash_in_product_dir',
+ 'type': 'none',
+ 'dependencies': [ ':copies_recursive_trailing_slash' ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out-slash-2/',
+ 'files': [
+ '<(PRODUCT_DIR)/copies-out-slash/directory/',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/copies/src/copies-updir.gyp b/tools/gyp/test/copies/src/copies-updir.gyp
new file mode 100644
index 00000000000000..ee8f51d91ed5df
--- /dev/null
+++ b/tools/gyp/test/copies/src/copies-updir.gyp
@@ -0,0 +1,20 @@
+# 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': 'copies_up',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/../copies-out-updir',
+ 'files': [
+ 'file1',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/copies/src/copies.gyp b/tools/gyp/test/copies/src/copies.gyp
new file mode 100644
index 00000000000000..ce2e0cabca5431
--- /dev/null
+++ b/tools/gyp/test/copies/src/copies.gyp
@@ -0,0 +1,70 @@
+# Copyright (c) 2009 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': 'copies1',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': 'copies-out',
+ 'files': [
+ 'file1',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'copies2',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out',
+ 'files': [
+ 'file2',
+ ],
+ },
+ ],
+ },
+ # Copy a directory tree.
+ {
+ 'target_name': 'copies_recursive',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out',
+ 'files': [
+ 'directory/',
+ ],
+ },
+ ],
+ },
+ # Copy a directory from deeper in the tree (this should not reproduce the
+ # entire directory path in the destination, only the final directory).
+ {
+ 'target_name': 'copies_recursive_depth',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out',
+ 'files': [
+ 'parentdir/subdir/',
+ ],
+ },
+ ],
+ },
+ # Verify that a null 'files' list doesn't gag the generators.
+ {
+ 'target_name': 'copies_null',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-null',
+ 'files': [],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/copies/src/directory/file3 b/tools/gyp/test/copies/src/directory/file3
new file mode 100644
index 00000000000000..43f16f35220657
--- /dev/null
+++ b/tools/gyp/test/copies/src/directory/file3
@@ -0,0 +1 @@
+file3 contents
diff --git a/tools/gyp/test/copies/src/directory/file4 b/tools/gyp/test/copies/src/directory/file4
new file mode 100644
index 00000000000000..5f7270a0847cc6
--- /dev/null
+++ b/tools/gyp/test/copies/src/directory/file4
@@ -0,0 +1 @@
+file4 contents
diff --git a/tools/gyp/test/copies/src/directory/subdir/file5 b/tools/gyp/test/copies/src/directory/subdir/file5
new file mode 100644
index 00000000000000..41f47186bd2197
--- /dev/null
+++ b/tools/gyp/test/copies/src/directory/subdir/file5
@@ -0,0 +1 @@
+file5 contents
diff --git a/tools/gyp/test/copies/src/executable-file.sh b/tools/gyp/test/copies/src/executable-file.sh
new file mode 100755
index 00000000000000..796953a1a2cf23
--- /dev/null
+++ b/tools/gyp/test/copies/src/executable-file.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+echo echo echo echo cho ho o o
diff --git a/tools/gyp/test/copies/src/file1 b/tools/gyp/test/copies/src/file1
new file mode 100644
index 00000000000000..84d55c5759cf6b
--- /dev/null
+++ b/tools/gyp/test/copies/src/file1
@@ -0,0 +1 @@
+file1 contents
diff --git a/tools/gyp/test/copies/src/file2 b/tools/gyp/test/copies/src/file2
new file mode 100644
index 00000000000000..af1b8ae35db461
--- /dev/null
+++ b/tools/gyp/test/copies/src/file2
@@ -0,0 +1 @@
+file2 contents
diff --git a/tools/gyp/test/copies/src/parentdir/subdir/file6 b/tools/gyp/test/copies/src/parentdir/subdir/file6
new file mode 100644
index 00000000000000..f5d57573486772
--- /dev/null
+++ b/tools/gyp/test/copies/src/parentdir/subdir/file6
@@ -0,0 +1 @@
+file6 contents
diff --git a/tools/gyp/test/custom-generator/gyptest-custom-generator.py b/tools/gyp/test/custom-generator/gyptest-custom-generator.py
new file mode 100755
index 00000000000000..85fd0724a13fc5
--- /dev/null
+++ b/tools/gyp/test/custom-generator/gyptest-custom-generator.py
@@ -0,0 +1,18 @@
+#!/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.
+
+"""Test that custom generators can be passed to --format
+"""
+
+import TestGyp
+
+test = TestGyp.TestGypCustom(format='mygenerator.py')
+test.run_gyp('test.gyp')
+
+# mygenerator.py should generate a file called MyBuildFile containing
+# "Testing..." alongside the gyp file.
+test.must_match('MyBuildFile', 'Testing...\n')
+
+test.pass_test()
diff --git a/tools/gyp/test/custom-generator/mygenerator.py b/tools/gyp/test/custom-generator/mygenerator.py
new file mode 100644
index 00000000000000..8eb4c2de1b3a02
--- /dev/null
+++ b/tools/gyp/test/custom-generator/mygenerator.py
@@ -0,0 +1,14 @@
+# 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.
+
+"""Custom gyp generator that doesn't do much."""
+
+import gyp.common
+
+generator_default_variables = {}
+
+def GenerateOutput(target_list, target_dicts, data, params):
+ f = open("MyBuildFile", "wb")
+ f.write("Testing...\n")
+ f.close()
diff --git a/tools/gyp/test/custom-generator/test.gyp b/tools/gyp/test/custom-generator/test.gyp
new file mode 100644
index 00000000000000..aa5f864a3b7bfa
--- /dev/null
+++ b/tools/gyp/test/custom-generator/test.gyp
@@ -0,0 +1,15 @@
+# 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': [
+ {
+ 'target_name': 'exe',
+ 'type': 'executable',
+ 'sources': [
+ 'main.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/cxxflags/cxxflags.cc b/tools/gyp/test/cxxflags/cxxflags.cc
new file mode 100644
index 00000000000000..e70e39dfd35d02
--- /dev/null
+++ b/tools/gyp/test/cxxflags/cxxflags.cc
@@ -0,0 +1,15 @@
+/* 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. */
+
+#include
+
+int main(void)
+{
+#ifdef ABC
+ printf("With define\n");
+#else
+ printf("No define\n");
+#endif
+ return 0;
+}
diff --git a/tools/gyp/test/cxxflags/cxxflags.gyp b/tools/gyp/test/cxxflags/cxxflags.gyp
new file mode 100644
index 00000000000000..a082d494924f94
--- /dev/null
+++ b/tools/gyp/test/cxxflags/cxxflags.gyp
@@ -0,0 +1,15 @@
+# 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': [
+ {
+ 'target_name': 'cxxflags',
+ 'type': 'executable',
+ 'sources': [
+ 'cxxflags.cc',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/cxxflags/gyptest-cxxflags.py b/tools/gyp/test/cxxflags/gyptest-cxxflags.py
new file mode 100755
index 00000000000000..117a1800de49d3
--- /dev/null
+++ b/tools/gyp/test/cxxflags/gyptest-cxxflags.py
@@ -0,0 +1,45 @@
+#!/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 the use of the environment during regeneration when the gyp file
+changes, specifically via build of an executable with C++ flags specified by
+CXXFLAGS.
+
+In this test, gyp happens within a local environment, but build outside of it.
+"""
+
+import TestGyp
+
+FORMATS = ('ninja',)
+
+test = TestGyp.TestGyp(formats=FORMATS)
+
+# We reset the environ after calling gyp. When the auto-regeneration happens,
+# the same define should be reused anyway.
+with TestGyp.LocalEnv({'CXXFLAGS': ''}):
+ test.run_gyp('cxxflags.gyp')
+
+test.build('cxxflags.gyp')
+
+expect = """\
+No define
+"""
+test.run_built_executable('cxxflags', stdout=expect)
+
+test.sleep()
+
+with TestGyp.LocalEnv({'CXXFLAGS': '-DABC'}):
+ test.run_gyp('cxxflags.gyp')
+
+test.build('cxxflags.gyp')
+
+expect = """\
+With define
+"""
+test.run_built_executable('cxxflags', stdout=expect)
+
+test.pass_test()
diff --git a/tools/gyp/test/defines-escaping/defines-escaping.c b/tools/gyp/test/defines-escaping/defines-escaping.c
new file mode 100644
index 00000000000000..a0aa4c286d9fad
--- /dev/null
+++ b/tools/gyp/test/defines-escaping/defines-escaping.c
@@ -0,0 +1,11 @@
+/* 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. */
+
+#include
+
+int main(void)
+{
+ printf(TEST_FORMAT, TEST_ARGS);
+ return 0;
+}
diff --git a/tools/gyp/test/defines-escaping/defines-escaping.gyp b/tools/gyp/test/defines-escaping/defines-escaping.gyp
new file mode 100644
index 00000000000000..6f0f3fde41fdcd
--- /dev/null
+++ b/tools/gyp/test/defines-escaping/defines-escaping.gyp
@@ -0,0 +1,19 @@
+# Copyright (c) 2009 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': 'defines_escaping',
+ 'type': 'executable',
+ 'sources': [
+ 'defines-escaping.c',
+ ],
+ 'defines': [
+ 'TEST_FORMAT="<(test_format)"',
+ 'TEST_ARGS=<(test_args)',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/defines-escaping/gyptest-defines-escaping.py b/tools/gyp/test/defines-escaping/gyptest-defines-escaping.py
new file mode 100755
index 00000000000000..eb18a3d369f48d
--- /dev/null
+++ b/tools/gyp/test/defines-escaping/gyptest-defines-escaping.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+Verifies build of an executable with C++ define specified by a gyp define using
+various special characters such as quotes, commas, etc.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+# Tests string literals, percents, and backslash escapes.
+try:
+ os.environ['GYP_DEFINES'] = (
+ r"""test_format='\n%s\n' """
+ r"""test_args='"Simple test of %s with a literal"'""")
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.build('defines-escaping.gyp')
+
+expect = """
+Simple test of %s with a literal
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# Test multiple comma-and-space-separated string literals.
+try:
+ os.environ['GYP_DEFINES'] = \
+ r"""test_format='\n%s and %s\n' test_args='"foo", "bar"'"""
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines-escaping.c')
+test.build('defines-escaping.gyp')
+
+expect = """
+foo and bar
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# Test string literals containing quotes.
+try:
+ os.environ['GYP_DEFINES'] = (
+ r"""test_format='\n%s %s %s %s %s\n' """
+ r"""test_args='"\"These,\"","""
+ r""" "\"words,\"","""
+ r""" "\"are,\"","""
+ r""" "\"in,\"","""
+ r""" "\"quotes.\""'""")
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines-escaping.c')
+test.build('defines-escaping.gyp')
+
+expect = """
+"These," "words," "are," "in," "quotes."
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# Test string literals containing single quotes.
+try:
+ os.environ['GYP_DEFINES'] = (
+ r"""test_format='\n%s %s %s %s %s\n' """
+ r"""test_args="\"'These,'\","""
+ r""" \"'words,'\","""
+ r""" \"'are,'\","""
+ r""" \"'in,'\","""
+ r""" \"'quotes.'\"" """)
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines-escaping.c')
+test.build('defines-escaping.gyp')
+
+expect = """
+'These,' 'words,' 'are,' 'in,' 'quotes.'
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# Test string literals containing different numbers of backslashes before quotes
+# (to exercise Windows' quoting behaviour).
+try:
+ os.environ['GYP_DEFINES'] = (
+ r"""test_format='\n%s\n%s\n%s\n' """
+ r"""test_args='"\\\"1 visible slash\\\"","""
+ r""" "\\\\\"2 visible slashes\\\\\"","""
+ r""" "\\\\\\\"3 visible slashes\\\\\\\""'""")
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines-escaping.c')
+test.build('defines-escaping.gyp')
+
+expect = r"""
+\"1 visible slash\"
+\\"2 visible slashes\\"
+\\\"3 visible slashes\\\"
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# Test that various scary sequences are passed unfettered.
+try:
+ os.environ['GYP_DEFINES'] = (
+ r"""test_format='\n%s\n' """
+ r"""test_args='"$foo, " `foo`;"'""")
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines-escaping.c')
+test.build('defines-escaping.gyp')
+
+expect = """
+$foo, " `foo`;
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# VisualStudio 2010 can't handle passing %PATH%
+if not (test.format == 'msvs' and test.uses_msbuild):
+ try:
+ os.environ['GYP_DEFINES'] = (
+ """test_format='%s' """
+ """test_args='"%PATH%"'""")
+ test.run_gyp('defines-escaping.gyp')
+ finally:
+ del os.environ['GYP_DEFINES']
+
+ test.sleep()
+ test.touch('defines-escaping.c')
+ test.build('defines-escaping.gyp')
+
+ expect = "%PATH%"
+ test.run_built_executable('defines_escaping', stdout=expect)
+
+
+# Test commas and semi-colons preceded by backslashes (to exercise Windows'
+# quoting behaviour).
+try:
+ os.environ['GYP_DEFINES'] = (
+ r"""test_format='\n%s\n%s\n' """
+ r"""test_args='"\\, \\\\;","""
+ # Same thing again, but enclosed in visible quotes.
+ r""" "\"\\, \\\\;\""'""")
+ test.run_gyp('defines-escaping.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines-escaping.c')
+test.build('defines-escaping.gyp')
+
+expect = r"""
+\, \\;
+"\, \\;"
+"""
+test.run_built_executable('defines_escaping', stdout=expect)
+
+# We deliberately do not test having an odd number of quotes in a string
+# literal because that isn't feasible in MSVS.
+
+test.pass_test()
diff --git a/tools/gyp/test/defines/defines-env.gyp b/tools/gyp/test/defines/defines-env.gyp
new file mode 100644
index 00000000000000..5d3c3b1e4c2d6d
--- /dev/null
+++ b/tools/gyp/test/defines/defines-env.gyp
@@ -0,0 +1,21 @@
+# Copyright (c) 2009 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.
+
+{
+ 'variables': {
+ 'value%': '5',
+ },
+ 'targets': [
+ {
+ 'target_name': 'defines',
+ 'type': 'executable',
+ 'sources': [
+ 'defines.c',
+ ],
+ 'defines': [
+ 'VALUE=<(value)',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/defines/defines.c b/tools/gyp/test/defines/defines.c
new file mode 100644
index 00000000000000..dda139275df945
--- /dev/null
+++ b/tools/gyp/test/defines/defines.c
@@ -0,0 +1,23 @@
+/* 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. */
+
+#include
+
+int main(void)
+{
+#ifdef FOO
+ printf("FOO is defined\n");
+#endif
+ printf("VALUE is %d\n", VALUE);
+
+#ifdef PAREN_VALUE
+ printf("2*PAREN_VALUE is %d\n", 2*PAREN_VALUE);
+#endif
+
+#ifdef HASH_VALUE
+ printf("HASH_VALUE is %s\n", HASH_VALUE);
+#endif
+
+ return 0;
+}
diff --git a/tools/gyp/test/defines/defines.gyp b/tools/gyp/test/defines/defines.gyp
new file mode 100644
index 00000000000000..90a755eb84afe8
--- /dev/null
+++ b/tools/gyp/test/defines/defines.gyp
@@ -0,0 +1,38 @@
+# Copyright (c) 2009 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': 'defines',
+ 'type': 'executable',
+ 'sources': [
+ 'defines.c',
+ ],
+ 'defines': [
+ 'FOO',
+ 'VALUE=1',
+ 'PAREN_VALUE=(1+2+3)',
+ 'HASH_VALUE="a#1"',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="fakeos"', {
+ 'targets': [
+ {
+ 'target_name': 'fakeosprogram',
+ 'type': 'executable',
+ 'sources': [
+ 'defines.c',
+ ],
+ 'defines': [
+ 'FOO',
+ 'VALUE=1',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/tools/gyp/test/defines/gyptest-define-override.py b/tools/gyp/test/defines/gyptest-define-override.py
new file mode 100755
index 00000000000000..9730455b67163f
--- /dev/null
+++ b/tools/gyp/test/defines/gyptest-define-override.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 default gyp define can be overridden.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+# CMake loudly warns about passing '#' to the compiler and drops the define.
+expect_stderr = ''
+if test.format == 'cmake':
+ expect_stderr = (
+"""WARNING: Preprocessor definitions containing '#' may not be passed on the"""
+""" compiler command line because many compilers do not support it.\n"""
+"""CMake is dropping a preprocessor definition: HASH_VALUE="a#1"\n"""
+"""Consider defining the macro in a (configured) header file.\n\n""")
+
+# Command-line define
+test.run_gyp('defines.gyp', '-D', 'OS=fakeos')
+test.build('defines.gyp', stderr=expect_stderr)
+test.built_file_must_exist('fakeosprogram', type=test.EXECUTABLE)
+# Clean up the exe so subsequent tests don't find an old exe.
+os.remove(test.built_file_path('fakeosprogram', type=test.EXECUTABLE))
+
+# Without "OS" override, fokeosprogram shouldn't be built.
+test.run_gyp('defines.gyp')
+test.build('defines.gyp', stderr=expect_stderr)
+test.built_file_must_not_exist('fakeosprogram', type=test.EXECUTABLE)
+
+# Environment define
+os.environ['GYP_DEFINES'] = 'OS=fakeos'
+test.run_gyp('defines.gyp')
+test.build('defines.gyp', stderr=expect_stderr)
+test.built_file_must_exist('fakeosprogram', type=test.EXECUTABLE)
+
+test.pass_test()
diff --git a/tools/gyp/test/defines/gyptest-defines-env-regyp.py b/tools/gyp/test/defines/gyptest-defines-env-regyp.py
new file mode 100755
index 00000000000000..f2d931c2f7d768
--- /dev/null
+++ b/tools/gyp/test/defines/gyptest-defines-env-regyp.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 build of an executable with C++ define specified by a gyp define, and
+the use of the environment during regeneration when the gyp file changes.
+"""
+
+import os
+import TestGyp
+
+# Regenerating build files when a gyp file changes is currently only supported
+# by the make generator.
+test = TestGyp.TestGyp(formats=['make'])
+
+try:
+ os.environ['GYP_DEFINES'] = 'value=50'
+ test.run_gyp('defines.gyp')
+finally:
+ # We clear the environ after calling gyp. When the auto-regeneration happens,
+ # the same define should be reused anyway. Reset to empty string first in
+ # case the platform doesn't support unsetenv.
+ os.environ['GYP_DEFINES'] = ''
+ del os.environ['GYP_DEFINES']
+
+test.build('defines.gyp')
+
+expect = """\
+FOO is defined
+VALUE is 1
+2*PAREN_VALUE is 12
+HASH_VALUE is a#1
+"""
+test.run_built_executable('defines', stdout=expect)
+
+# Sleep so that the changed gyp file will have a newer timestamp than the
+# previously generated build files.
+test.sleep()
+test.write('defines.gyp', test.read('defines-env.gyp'))
+
+test.build('defines.gyp', test.ALL)
+
+expect = """\
+VALUE is 50
+"""
+test.run_built_executable('defines', stdout=expect)
+
+test.pass_test()
diff --git a/tools/gyp/test/defines/gyptest-defines-env.py b/tools/gyp/test/defines/gyptest-defines-env.py
new file mode 100755
index 00000000000000..6b4e7175a6d4d7
--- /dev/null
+++ b/tools/gyp/test/defines/gyptest-defines-env.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 build of an executable with C++ define specified by a gyp define.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+# With the value only given in environment, it should be used.
+try:
+ os.environ['GYP_DEFINES'] = 'value=10'
+ test.run_gyp('defines-env.gyp')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.build('defines-env.gyp')
+
+expect = """\
+VALUE is 10
+"""
+test.run_built_executable('defines', stdout=expect)
+
+
+# With the value given in both command line and environment,
+# command line should take precedence.
+try:
+ os.environ['GYP_DEFINES'] = 'value=20'
+ test.run_gyp('defines-env.gyp', '-Dvalue=25')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines.c')
+test.build('defines-env.gyp')
+
+expect = """\
+VALUE is 25
+"""
+test.run_built_executable('defines', stdout=expect)
+
+
+# With the value only given in environment, it should be ignored if
+# --ignore-environment is specified.
+try:
+ os.environ['GYP_DEFINES'] = 'value=30'
+ test.run_gyp('defines-env.gyp', '--ignore-environment')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines.c')
+test.build('defines-env.gyp')
+
+expect = """\
+VALUE is 5
+"""
+test.run_built_executable('defines', stdout=expect)
+
+
+# With the value given in both command line and environment, and
+# --ignore-environment also specified, command line should still be used.
+try:
+ os.environ['GYP_DEFINES'] = 'value=40'
+ test.run_gyp('defines-env.gyp', '--ignore-environment', '-Dvalue=45')
+finally:
+ del os.environ['GYP_DEFINES']
+
+test.sleep()
+test.touch('defines.c')
+test.build('defines-env.gyp')
+
+expect = """\
+VALUE is 45
+"""
+test.run_built_executable('defines', stdout=expect)
+
+
+test.pass_test()
diff --git a/tools/gyp/test/defines/gyptest-defines.py b/tools/gyp/test/defines/gyptest-defines.py
new file mode 100755
index 00000000000000..77a3af53b989bb
--- /dev/null
+++ b/tools/gyp/test/defines/gyptest-defines.py
@@ -0,0 +1,39 @@
+#!/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.
+
+"""
+Verifies build of an executable with C++ defines.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('defines.gyp')
+
+expect = """\
+FOO is defined
+VALUE is 1
+2*PAREN_VALUE is 12
+"""
+
+#CMake loudly warns about passing '#' to the compiler and drops the define.
+expect_stderr = ''
+if test.format == 'cmake':
+ expect_stderr = (
+"""WARNING: Preprocessor definitions containing '#' may not be passed on the"""
+""" compiler command line because many compilers do not support it.\n"""
+"""CMake is dropping a preprocessor definition: HASH_VALUE="a#1"\n"""
+"""Consider defining the macro in a (configured) header file.\n\n""")
+else:
+ expect += """HASH_VALUE is a#1
+"""
+
+test.build('defines.gyp', stderr=expect_stderr)
+
+test.run_built_executable('defines', stdout=expect)
+
+test.pass_test()
diff --git a/tools/gyp/test/dependencies/a.c b/tools/gyp/test/dependencies/a.c
new file mode 100755
index 00000000000000..3bba111d24eefd
--- /dev/null
+++ b/tools/gyp/test/dependencies/a.c
@@ -0,0 +1,9 @@
+/* Copyright (c) 2009 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. */
+
+extern int funcB();
+
+int funcA() {
+ return funcB();
+}
diff --git a/tools/gyp/test/dependencies/b/b.c b/tools/gyp/test/dependencies/b/b.c
new file mode 100755
index 00000000000000..b5e771bcc77b0b
--- /dev/null
+++ b/tools/gyp/test/dependencies/b/b.c
@@ -0,0 +1,3 @@
+int funcB() {
+ return 2;
+}
diff --git a/tools/gyp/test/dependencies/b/b.gyp b/tools/gyp/test/dependencies/b/b.gyp
new file mode 100755
index 00000000000000..893dc64d652418
--- /dev/null
+++ b/tools/gyp/test/dependencies/b/b.gyp
@@ -0,0 +1,22 @@
+# Copyright (c) 2009 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': 'b',
+ 'type': 'static_library',
+ 'sources': [
+ 'b.c',
+ ],
+ },
+ {
+ 'target_name': 'b3',
+ 'type': 'static_library',
+ 'sources': [
+ 'b3.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/b/b3.c b/tools/gyp/test/dependencies/b/b3.c
new file mode 100755
index 00000000000000..287f67ff3151e3
--- /dev/null
+++ b/tools/gyp/test/dependencies/b/b3.c
@@ -0,0 +1,9 @@
+/*
+ * 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.
+ */
+
+int funcB() {
+ return 3;
+}
diff --git a/tools/gyp/test/dependencies/c/c.c b/tools/gyp/test/dependencies/c/c.c
new file mode 100644
index 00000000000000..4949daf3ee1fe1
--- /dev/null
+++ b/tools/gyp/test/dependencies/c/c.c
@@ -0,0 +1,4 @@
+int funcC() {
+ return 3
+ // Intentional syntax error. This file should never be compiled, so this
+ // shouldn't be a problem.
diff --git a/tools/gyp/test/dependencies/c/c.gyp b/tools/gyp/test/dependencies/c/c.gyp
new file mode 100644
index 00000000000000..eabebea9efbf41
--- /dev/null
+++ b/tools/gyp/test/dependencies/c/c.gyp
@@ -0,0 +1,22 @@
+# Copyright (c) 2009 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': 'c_unused',
+ 'type': 'static_library',
+ 'sources': [
+ 'c.c',
+ ],
+ },
+ {
+ 'target_name': 'd',
+ 'type': 'static_library',
+ 'sources': [
+ 'd.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/c/d.c b/tools/gyp/test/dependencies/c/d.c
new file mode 100644
index 00000000000000..05465fc1af7d0c
--- /dev/null
+++ b/tools/gyp/test/dependencies/c/d.c
@@ -0,0 +1,3 @@
+int funcD() {
+ return 4;
+}
diff --git a/tools/gyp/test/dependencies/double_dependency.gyp b/tools/gyp/test/dependencies/double_dependency.gyp
new file mode 100644
index 00000000000000..ca2bf1cd892fd9
--- /dev/null
+++ b/tools/gyp/test/dependencies/double_dependency.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': 'double_dependency',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'double_dependent.gyp:double_dependent',
+ ],
+ 'conditions': [
+ ['1==1', {
+ 'dependencies': [
+ 'double_dependent.gyp:*',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/double_dependent.gyp b/tools/gyp/test/dependencies/double_dependent.gyp
new file mode 100644
index 00000000000000..334caff723ce8e
--- /dev/null
+++ b/tools/gyp/test/dependencies/double_dependent.gyp
@@ -0,0 +1,12 @@
+# 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': 'double_dependent',
+ 'type': 'none',
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/extra_targets.gyp b/tools/gyp/test/dependencies/extra_targets.gyp
new file mode 100644
index 00000000000000..c1a26de42281ba
--- /dev/null
+++ b/tools/gyp/test/dependencies/extra_targets.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2009 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': 'a',
+ 'type': 'static_library',
+ 'sources': [
+ 'a.c',
+ ],
+ # This only depends on the "d" target; other targets in c.gyp
+ # should not become part of the build (unlike with 'c/c.gyp:*').
+ 'dependencies': ['c/c.gyp:d'],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/gyptest-double-dependency.py b/tools/gyp/test/dependencies/gyptest-double-dependency.py
new file mode 100644
index 00000000000000..7692740c54deb4
--- /dev/null
+++ b/tools/gyp/test/dependencies/gyptest-double-dependency.py
@@ -0,0 +1,19 @@
+#!/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 pulling in a dependency a second time in a conditional works for
+shared_library targets. Regression test for http://crbug.com/122588
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('double_dependency.gyp')
+
+# If running gyp worked, all is well.
+test.pass_test()
diff --git a/tools/gyp/test/dependencies/gyptest-extra-targets.py b/tools/gyp/test/dependencies/gyptest-extra-targets.py
new file mode 100755
index 00000000000000..09b00d958ba175
--- /dev/null
+++ b/tools/gyp/test/dependencies/gyptest-extra-targets.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 dependencies don't pull unused targets into the build.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('extra_targets.gyp',
+ '-G', 'xcode_ninja_target_pattern=^a$')
+
+# This should fail if it tries to build 'c_unused' since 'c/c.c' has a syntax
+# error and won't compile.
+test.build('extra_targets.gyp', test.ALL)
+
+test.pass_test()
diff --git a/tools/gyp/test/dependencies/gyptest-lib-only.py b/tools/gyp/test/dependencies/gyptest-lib-only.py
new file mode 100755
index 00000000000000..3a99a7f4aa61e0
--- /dev/null
+++ b/tools/gyp/test/dependencies/gyptest-lib-only.py
@@ -0,0 +1,39 @@
+#!/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.
+
+"""
+Verify that a link time only dependency will get pulled into the set of built
+targets, even if no executable uses it.
+"""
+
+import TestGyp
+
+import sys
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('lib_only.gyp')
+
+test.build('lib_only.gyp', test.ALL)
+
+test.built_file_must_exist('a', type=test.STATIC_LIB)
+
+# TODO(bradnelson/mark):
+# On linux and windows a library target will at least pull its link dependencies
+# into the generated project, since not doing so confuses users.
+# This is not currently implemented on mac, which has the opposite behavior.
+if sys.platform == 'darwin':
+ if test.format == 'xcode':
+ test.built_file_must_not_exist('b', type=test.STATIC_LIB)
+ else:
+ assert test.format in ('make', 'ninja', 'xcode-ninja')
+ test.built_file_must_exist('b', type=test.STATIC_LIB)
+else:
+ # Make puts the resulting library in a directory matching the input gyp file;
+ # for the 'b' library, that is in the 'b' subdirectory.
+ test.built_file_must_exist('b', type=test.STATIC_LIB, subdir='b')
+
+test.pass_test()
diff --git a/tools/gyp/test/dependencies/gyptest-none-traversal.py b/tools/gyp/test/dependencies/gyptest-none-traversal.py
new file mode 100755
index 00000000000000..c09063dad3acbf
--- /dev/null
+++ b/tools/gyp/test/dependencies/gyptest-none-traversal.py
@@ -0,0 +1,25 @@
+#!/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.
+
+"""
+Verify that static library dependencies don't traverse none targets, unless
+explicitly specified.
+"""
+
+import TestGyp
+
+import sys
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('none_traversal.gyp')
+
+test.build('none_traversal.gyp', test.ALL)
+
+test.run_built_executable('needs_chain', stdout="2\n")
+test.run_built_executable('doesnt_need_chain', stdout="3\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/dependencies/gyptest-sharedlib-linksettings.py b/tools/gyp/test/dependencies/gyptest-sharedlib-linksettings.py
new file mode 100644
index 00000000000000..87428af459126a
--- /dev/null
+++ b/tools/gyp/test/dependencies/gyptest-sharedlib-linksettings.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 link_settings in a shared_library are not propagated to targets
+that depend on the shared_library, but are used in the shared_library itself.
+"""
+
+import TestGyp
+import sys
+
+CHDIR='sharedlib-linksettings'
+
+test = TestGyp.TestGyp()
+test.run_gyp('test.gyp', chdir=CHDIR)
+test.build('test.gyp', test.ALL, chdir=CHDIR)
+test.run_built_executable('program', stdout="1\n2\n", chdir=CHDIR)
+test.pass_test()
diff --git a/tools/gyp/test/dependencies/lib_only.gyp b/tools/gyp/test/dependencies/lib_only.gyp
new file mode 100755
index 00000000000000..f6c84dea64ffb8
--- /dev/null
+++ b/tools/gyp/test/dependencies/lib_only.gyp
@@ -0,0 +1,16 @@
+# Copyright (c) 2009 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': 'a',
+ 'type': 'static_library',
+ 'sources': [
+ 'a.c',
+ ],
+ 'dependencies': ['b/b.gyp:b'],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/main.c b/tools/gyp/test/dependencies/main.c
new file mode 100644
index 00000000000000..185bd482f2b784
--- /dev/null
+++ b/tools/gyp/test/dependencies/main.c
@@ -0,0 +1,14 @@
+/*
+ * 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.
+ */
+
+#include
+
+extern int funcA();
+
+int main() {
+ printf("%d\n", funcA());
+ return 0;
+}
diff --git a/tools/gyp/test/dependencies/none_traversal.gyp b/tools/gyp/test/dependencies/none_traversal.gyp
new file mode 100755
index 00000000000000..3d8ab30aff5a19
--- /dev/null
+++ b/tools/gyp/test/dependencies/none_traversal.gyp
@@ -0,0 +1,46 @@
+# Copyright (c) 2009 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': 'needs_chain',
+ 'type': 'executable',
+ 'sources': [
+ 'a.c',
+ 'main.c',
+ ],
+ 'dependencies': ['chain'],
+ },
+ {
+ 'target_name': 'chain',
+ 'type': 'none',
+ 'dependencies': ['b/b.gyp:b'],
+ },
+ {
+ 'target_name': 'doesnt_need_chain',
+ 'type': 'executable',
+ 'sources': [
+ 'main.c',
+ ],
+ 'dependencies': ['no_chain', 'other_chain'],
+ },
+ {
+ 'target_name': 'no_chain',
+ 'type': 'none',
+ 'sources': [
+ ],
+ 'dependencies': ['b/b.gyp:b'],
+ 'dependencies_traverse': 0,
+ },
+ {
+ 'target_name': 'other_chain',
+ 'type': 'static_library',
+ 'sources': [
+ 'a.c',
+ ],
+ 'dependencies': ['b/b.gyp:b3'],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependencies/sharedlib-linksettings/program.c b/tools/gyp/test/dependencies/sharedlib-linksettings/program.c
new file mode 100644
index 00000000000000..b7c15edcd6d55c
--- /dev/null
+++ b/tools/gyp/test/dependencies/sharedlib-linksettings/program.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 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
+
+/*
+ * This will fail to compile if TEST_DEFINE was propagated from sharedlib to
+ * program.
+ */
+#ifdef TEST_DEFINE
+#error TEST_DEFINE is already defined!
+#endif
+
+#define TEST_DEFINE 2
+
+extern int staticLibFunc();
+
+int main() {
+ printf("%d\n", staticLibFunc());
+ printf("%d\n", TEST_DEFINE);
+ return 0;
+}
diff --git a/tools/gyp/test/dependencies/sharedlib-linksettings/sharedlib.c b/tools/gyp/test/dependencies/sharedlib-linksettings/sharedlib.c
new file mode 100644
index 00000000000000..3199bccd665407
--- /dev/null
+++ b/tools/gyp/test/dependencies/sharedlib-linksettings/sharedlib.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sharedLibFunc() {
+ /*
+ * This will fail to compile if TEST_DEFINE was not obtained from sharedlib's
+ * link_settings.
+ */
+ return TEST_DEFINE;
+}
diff --git a/tools/gyp/test/dependencies/sharedlib-linksettings/staticlib.c b/tools/gyp/test/dependencies/sharedlib-linksettings/staticlib.c
new file mode 100644
index 00000000000000..e889b419fdd8f2
--- /dev/null
+++ b/tools/gyp/test/dependencies/sharedlib-linksettings/staticlib.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 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 will fail to compile if TEST_DEFINE was propagated from sharedlib to
+ * staticlib.
+ */
+#ifdef TEST_DEFINE
+#error TEST_DEFINE is defined!
+#endif
+
+#ifdef _WIN32
+__declspec(dllimport)
+#else
+extern
+#endif
+int sharedLibFunc();
+
+int staticLibFunc() {
+ return sharedLibFunc();
+}
diff --git a/tools/gyp/test/dependencies/sharedlib-linksettings/test.gyp b/tools/gyp/test/dependencies/sharedlib-linksettings/test.gyp
new file mode 100644
index 00000000000000..830ce3236d6662
--- /dev/null
+++ b/tools/gyp/test/dependencies/sharedlib-linksettings/test.gyp
@@ -0,0 +1,37 @@
+# Copyright (c) 2013 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': {
+ 'allow_sharedlib_linksettings_propagation': 0,
+ },
+ 'targets': [
+ {
+ 'target_name': 'sharedlib',
+ 'type': 'shared_library',
+ 'sources': [ 'sharedlib.c' ],
+ 'link_settings': {
+ 'defines': [ 'TEST_DEFINE=1' ],
+ },
+ 'conditions': [
+ ['OS=="linux"', {
+ # Support 64-bit shared libs (also works fine for 32-bit).
+ 'cflags': ['-fPIC'],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'staticlib',
+ 'type': 'static_library',
+ 'sources': [ 'staticlib.c' ],
+ 'dependencies': [ 'sharedlib' ],
+ },
+ {
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'sources': [ 'program.c' ],
+ 'dependencies': [ 'staticlib' ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependency-copy/gyptest-copy.py b/tools/gyp/test/dependency-copy/gyptest-copy.py
new file mode 100755
index 00000000000000..5ba7c73d41327a
--- /dev/null
+++ b/tools/gyp/test/dependency-copy/gyptest-copy.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 dependencies do the copy step.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('copies.gyp', chdir='src')
+
+test.build('copies.gyp', 'proj2', chdir='src')
+
+test.run_built_executable('proj1',
+ chdir='src',
+ stdout="Hello from file1.c\n")
+test.run_built_executable('proj2',
+ chdir='src',
+ stdout="Hello from file2.c\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/dependency-copy/src/copies.gyp b/tools/gyp/test/dependency-copy/src/copies.gyp
new file mode 100644
index 00000000000000..4176b18787bea3
--- /dev/null
+++ b/tools/gyp/test/dependency-copy/src/copies.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2009 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': 'proj1',
+ 'type': 'executable',
+ 'sources': [
+ 'file1.c',
+ ],
+ },
+ {
+ 'target_name': 'proj2',
+ 'type': 'executable',
+ 'sources': [
+ 'file2.c',
+ ],
+ 'dependencies': [
+ 'proj1',
+ ]
+ },
+ ],
+}
diff --git a/tools/gyp/test/dependency-copy/src/file1.c b/tools/gyp/test/dependency-copy/src/file1.c
new file mode 100644
index 00000000000000..d7c3159186f04a
--- /dev/null
+++ b/tools/gyp/test/dependency-copy/src/file1.c
@@ -0,0 +1,7 @@
+#include
+
+int main(void)
+{
+ printf("Hello from file1.c\n");
+ return 0;
+}
diff --git a/tools/gyp/test/dependency-copy/src/file2.c b/tools/gyp/test/dependency-copy/src/file2.c
new file mode 100644
index 00000000000000..cf40f57f949bfe
--- /dev/null
+++ b/tools/gyp/test/dependency-copy/src/file2.c
@@ -0,0 +1,7 @@
+#include
+
+int main(void)
+{
+ printf("Hello from file2.c\n");
+ return 0;
+}
diff --git a/tools/gyp/test/empty-target/empty-target.gyp b/tools/gyp/test/empty-target/empty-target.gyp
new file mode 100644
index 00000000000000..feefa2805899cb
--- /dev/null
+++ b/tools/gyp/test/empty-target/empty-target.gyp
@@ -0,0 +1,12 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'empty_target',
+ 'type': 'none',
+ },
+ ],
+}
diff --git a/tools/gyp/test/empty-target/gyptest-empty-target.py b/tools/gyp/test/empty-target/gyptest-empty-target.py
new file mode 100644
index 00000000000000..ecadd4a87f97e3
--- /dev/null
+++ b/tools/gyp/test/empty-target/gyptest-empty-target.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies building a target with nothing succeeds.
+"""
+
+import os
+import sys
+import TestGyp
+
+test = TestGyp.TestGyp()
+test.run_gyp('empty-target.gyp')
+test.build('empty-target.gyp', target='empty_target')
+test.pass_test()
diff --git a/tools/gyp/test/errors/dependency_cycle.gyp b/tools/gyp/test/errors/dependency_cycle.gyp
new file mode 100644
index 00000000000000..eef44bc9eb20e9
--- /dev/null
+++ b/tools/gyp/test/errors/dependency_cycle.gyp
@@ -0,0 +1,23 @@
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'target0',
+ 'type': 'none',
+ 'dependencies': [ 'target1' ],
+ },
+ {
+ 'target_name': 'target1',
+ 'type': 'none',
+ 'dependencies': [ 'target2' ],
+ },
+ {
+ 'target_name': 'target2',
+ 'type': 'none',
+ 'dependencies': [ 'target0' ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/errors/duplicate_node.gyp b/tools/gyp/test/errors/duplicate_node.gyp
new file mode 100644
index 00000000000000..d6096096bd804e
--- /dev/null
+++ b/tools/gyp/test/errors/duplicate_node.gyp
@@ -0,0 +1,12 @@
+# 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' : 'foo', 'type': 'executable' },
+ ],
+ 'targets': [
+ { 'target_name' : 'bar', 'type': 'executable' },
+ ]
+}
diff --git a/tools/gyp/test/errors/duplicate_rule.gyp b/tools/gyp/test/errors/duplicate_rule.gyp
new file mode 100644
index 00000000000000..dab98e96c2b442
--- /dev/null
+++ b/tools/gyp/test/errors/duplicate_rule.gyp
@@ -0,0 +1,22 @@
+# 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': 'foo',
+ 'type': 'executable',
+ 'rules': [
+ {
+ 'rule_name': 'bar',
+ 'extension': '',
+ },
+ {
+ 'rule_name': 'bar',
+ 'extension': '',
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/errors/duplicate_targets.gyp b/tools/gyp/test/errors/duplicate_targets.gyp
new file mode 100644
index 00000000000000..aec470eefa8a3b
--- /dev/null
+++ b/tools/gyp/test/errors/duplicate_targets.gyp
@@ -0,0 +1,14 @@
+# Copyright (c) 2009 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': 'foo'
+ },
+ {
+ 'target_name': 'foo'
+ },
+ ]
+}
diff --git a/tools/gyp/test/errors/file_cycle0.gyp b/tools/gyp/test/errors/file_cycle0.gyp
new file mode 100644
index 00000000000000..3bfafb6cb39378
--- /dev/null
+++ b/tools/gyp/test/errors/file_cycle0.gyp
@@ -0,0 +1,17 @@
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'top',
+ 'type': 'none',
+ 'dependencies': [ 'file_cycle1.gyp:middle' ],
+ },
+ {
+ 'target_name': 'bottom',
+ 'type': 'none',
+ },
+ ],
+}
diff --git a/tools/gyp/test/errors/file_cycle1.gyp b/tools/gyp/test/errors/file_cycle1.gyp
new file mode 100644
index 00000000000000..fbd7a0d1672bfd
--- /dev/null
+++ b/tools/gyp/test/errors/file_cycle1.gyp
@@ -0,0 +1,13 @@
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'middle',
+ 'type': 'none',
+ 'dependencies': [ 'file_cycle0.gyp:bottom' ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/errors/gyptest-errors.py b/tools/gyp/test/errors/gyptest-errors.py
new file mode 100755
index 00000000000000..d544622765832e
--- /dev/null
+++ b/tools/gyp/test/errors/gyptest-errors.py
@@ -0,0 +1,55 @@
+#!/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.
+
+"""
+Test that two targets with the same name generates an error.
+"""
+
+import os
+import sys
+
+import TestGyp
+import TestCmd
+
+# TODO(sbc): Remove the use of match_re below, done because scons
+# error messages were not consistent with other generators.
+# Also remove input.py:generator_wants_absolute_build_file_paths.
+
+test = TestGyp.TestGyp()
+
+stderr = ('gyp: Duplicate target definitions for '
+ '.*duplicate_targets.gyp:foo#target\n')
+test.run_gyp('duplicate_targets.gyp', status=1, stderr=stderr,
+ match=TestCmd.match_re)
+
+stderr = ('.*: Unable to find targets in build file .*missing_targets.gyp.*')
+test.run_gyp('missing_targets.gyp', status=1, stderr=stderr,
+ match=TestCmd.match_re_dotall)
+
+stderr = ('gyp: rule bar exists in duplicate, target '
+ '.*duplicate_rule.gyp:foo#target\n')
+test.run_gyp('duplicate_rule.gyp', status=1, stderr=stderr,
+ match=TestCmd.match_re)
+
+stderr = ("gyp: Key 'targets' repeated at level 1 with key path '' while "
+ "reading .*duplicate_node.gyp.*")
+test.run_gyp('duplicate_node.gyp', '--check', status=1, stderr=stderr,
+ match=TestCmd.match_re_dotall)
+
+stderr = (".*target0.*target1.*target2.*target0.*")
+test.run_gyp('dependency_cycle.gyp', status=1, stderr=stderr,
+ match=TestCmd.match_re_dotall)
+
+stderr = (".*file_cycle0.*file_cycle1.*file_cycle0.*")
+test.run_gyp('file_cycle0.gyp', status=1, stderr=stderr,
+ match=TestCmd.match_re_dotall)
+
+stderr = ("gyp: Dependency '.*missing_dep.gyp:missing.gyp#target' not found "
+ "while trying to load target .*missing_dep.gyp:foo#target\n")
+test.run_gyp('missing_dep.gyp', status=1, stderr=stderr,
+ match=TestCmd.match_re)
+
+test.pass_test()
diff --git a/tools/gyp/test/errors/missing_dep.gyp b/tools/gyp/test/errors/missing_dep.gyp
new file mode 100644
index 00000000000000..08746be3d7a279
--- /dev/null
+++ b/tools/gyp/test/errors/missing_dep.gyp
@@ -0,0 +1,15 @@
+# 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': 'foo',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'missing.gyp'
+ ]
+ },
+ ]
+}
diff --git a/tools/gyp/test/errors/missing_targets.gyp b/tools/gyp/test/errors/missing_targets.gyp
new file mode 100644
index 00000000000000..13d4f924c19895
--- /dev/null
+++ b/tools/gyp/test/errors/missing_targets.gyp
@@ -0,0 +1,8 @@
+# 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.
+
+{
+ 'target_defaults': {
+ },
+}
diff --git a/tools/gyp/test/escaping/colon/test.gyp b/tools/gyp/test/escaping/colon/test.gyp
new file mode 100644
index 00000000000000..715f95490e4b80
--- /dev/null
+++ b/tools/gyp/test/escaping/colon/test.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': 'colon',
+ 'type': 'executable',
+ 'sources': [
+ 'a:b.c',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/',
+ # MSVS2008 gets confused if the same file is in 'sources' and 'copies'
+ 'files': [ 'a:b.c-d', ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/escaping/gyptest-colon.py b/tools/gyp/test/escaping/gyptest-colon.py
new file mode 100644
index 00000000000000..f5275db6afc950
--- /dev/null
+++ b/tools/gyp/test/escaping/gyptest-colon.py
@@ -0,0 +1,43 @@
+#!/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 that filenames that contain colons are handled correctly.
+(This is important for absolute paths on Windows.)
+"""
+
+import os
+import sys
+import TestGyp
+
+# TODO: Make colons in filenames work with make, if required.
+test = TestGyp.TestGyp(formats=['!make', '!android'])
+CHDIR = 'colon'
+
+source_name = 'colon/a:b.c'
+copies_name = 'colon/a:b.c-d'
+if sys.platform == 'win32':
+ # Windows uses : as drive separator and doesn't allow it in regular filenames.
+ # Use abspath() to create a path that contains a colon instead.
+ abs_source = os.path.abspath('colon/file.c')
+ test.write('colon/test.gyp',
+ test.read('colon/test.gyp').replace("'a:b.c'", repr(abs_source)))
+ source_name = abs_source
+
+ abs_copies = os.path.abspath('colon/file.txt')
+ test.write('colon/test.gyp',
+ test.read('colon/test.gyp').replace("'a:b.c-d'", repr(abs_copies)))
+ copies_name = abs_copies
+
+# Create the file dynamically, Windows is unhappy if a file with a colon in
+# its name is checked in.
+test.write(source_name, 'int main() {}')
+test.write(copies_name, 'foo')
+
+test.run_gyp('test.gyp', chdir=CHDIR)
+test.build('test.gyp', test.ALL, chdir=CHDIR)
+test.built_file_must_exist(os.path.basename(copies_name), chdir=CHDIR)
+test.pass_test()
diff --git a/tools/gyp/test/exclusion/exclusion.gyp b/tools/gyp/test/exclusion/exclusion.gyp
new file mode 100644
index 00000000000000..1232dabaef9331
--- /dev/null
+++ b/tools/gyp/test/exclusion/exclusion.gyp
@@ -0,0 +1,23 @@
+# 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': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ 'bogus.c',
+ 'also/not/real.c',
+ 'also/not/real2.c',
+ ],
+ 'sources!': [
+ 'bogus.c',
+ 'also/not/real.c',
+ 'also/not/real2.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/exclusion/gyptest-exclusion.py b/tools/gyp/test/exclusion/gyptest-exclusion.py
new file mode 100755
index 00000000000000..1fc32bf871a22a
--- /dev/null
+++ b/tools/gyp/test/exclusion/gyptest-exclusion.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+Verifies that exclusions (e.g. sources!) are respected. Excluded sources
+that do not exist should not prevent the build from succeeding.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('exclusion.gyp')
+test.build('exclusion.gyp')
+
+# executables
+test.built_file_must_exist('hello' + test._exe, test.EXECUTABLE, bare=True)
+
+test.pass_test()
diff --git a/tools/gyp/test/exclusion/hello.c b/tools/gyp/test/exclusion/hello.c
new file mode 100644
index 00000000000000..6e7dc8e419a842
--- /dev/null
+++ b/tools/gyp/test/exclusion/hello.c
@@ -0,0 +1,15 @@
+/* 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. */
+
+#include
+
+int func1(void) {
+ return 42;
+}
+
+int main(void) {
+ printf("Hello, world!\n");
+ printf("%d\n", func1());
+ return 0;
+}
diff --git a/tools/gyp/test/external-cross-compile/gyptest-cross.py b/tools/gyp/test/external-cross-compile/gyptest-cross.py
new file mode 100755
index 00000000000000..a837ec57dc7447
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/gyptest-cross.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.
+
+"""
+Verifies that actions can be + a source scanner can be used to implement,
+cross-compiles (for Native Client at this point).
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('cross.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+test.build('cross.gyp', test.ALL, chdir='relocate/src')
+
+expect = """\
+From test1.cc
+From test2.c
+From test3.cc
+From test4.c
+"""
+test.run_built_executable('program', chdir='relocate/src', stdout=expect)
+
+
+test.pass_test()
diff --git a/tools/gyp/test/external-cross-compile/src/bogus1.cc b/tools/gyp/test/external-cross-compile/src/bogus1.cc
new file mode 100644
index 00000000000000..1b8d01199b5ba8
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/bogus1.cc
@@ -0,0 +1 @@
+From bogus1.cc
diff --git a/tools/gyp/test/external-cross-compile/src/bogus2.c b/tools/gyp/test/external-cross-compile/src/bogus2.c
new file mode 100644
index 00000000000000..cbf4a123c46e77
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/bogus2.c
@@ -0,0 +1 @@
+From bogus2.c
diff --git a/tools/gyp/test/external-cross-compile/src/cross.gyp b/tools/gyp/test/external-cross-compile/src/cross.gyp
new file mode 100644
index 00000000000000..aeda76b5bdf88d
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/cross.gyp
@@ -0,0 +1,83 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': ['cross_compile.gypi'],
+ 'target_defaults': {
+ 'variables': {
+ 'nix_lame%': 0,
+ },
+ 'target_conditions': [
+ ['nix_lame==1', {
+ 'sources/': [
+ ['exclude', 'lame'],
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'dependencies': [
+ 'program_inc',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'sources': [
+ 'program.cc',
+ ],
+ },
+ {
+ 'target_name': 'program_inc',
+ 'type': 'none',
+ 'dependencies': ['cross_program'],
+ 'actions': [
+ {
+ 'action_name': 'program_inc',
+ 'inputs': ['<(SHARED_INTERMEDIATE_DIR)/cross_program.fake'],
+ 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/cross_program.h'],
+ 'action': ['python', 'tochar.py', '<@(_inputs)', '<@(_outputs)'],
+ },
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'target_name': 'cross_program',
+ 'type': 'none',
+ 'variables': {
+ 'cross': 1,
+ 'nix_lame': 1,
+ },
+ 'dependencies': ['cross_lib'],
+ 'sources': [
+ 'test1.cc',
+ 'test2.c',
+ 'very_lame.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/cross_lib.fake',
+ ],
+ },
+ {
+ 'target_name': 'cross_lib',
+ 'type': 'none',
+ 'variables': {
+ 'cross': 1,
+ 'nix_lame': 1,
+ },
+ 'sources': [
+ 'test3.cc',
+ 'test4.c',
+ 'bogus1.cc',
+ 'bogus2.c',
+ 'sort_of_lame.cc',
+ ],
+ 'sources!': [
+ 'bogus1.cc',
+ 'bogus2.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/external-cross-compile/src/cross_compile.gypi b/tools/gyp/test/external-cross-compile/src/cross_compile.gypi
new file mode 100644
index 00000000000000..36e651903f5ade
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/cross_compile.gypi
@@ -0,0 +1,23 @@
+{
+ 'target_defaults': {
+ 'variables': {
+ 'cross%': 0,
+ },
+ 'target_conditions': [
+ ['cross==1', {
+ 'actions': [
+ {
+ 'action_name': 'cross compile >(_target_name)',
+ 'inputs': ['^@(_sources)'],
+ 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/>(_target_name).fake'],
+ 'action': [
+ 'python', 'fake_cross.py', '>@(_outputs)', '^@(_sources)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ }],
+ ],
+ },
+}
diff --git a/tools/gyp/test/external-cross-compile/src/fake_cross.py b/tools/gyp/test/external-cross-compile/src/fake_cross.py
new file mode 100644
index 00000000000000..05eacc6a6323b6
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/fake_cross.py
@@ -0,0 +1,18 @@
+#!/usr/bin/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 sys
+
+fh = open(sys.argv[1], 'w')
+
+filenames = sys.argv[2:]
+
+for filename in filenames:
+ subfile = open(filename)
+ data = subfile.read()
+ subfile.close()
+ fh.write(data)
+
+fh.close()
diff --git a/tools/gyp/test/external-cross-compile/src/program.cc b/tools/gyp/test/external-cross-compile/src/program.cc
new file mode 100644
index 00000000000000..5172ae90fefbed
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/program.cc
@@ -0,0 +1,16 @@
+/*
+ * 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
+
+static char data[] = {
+#include "cross_program.h"
+};
+
+int main(void) {
+ fwrite(data, 1, sizeof(data), stdout);
+ return 0;
+}
diff --git a/tools/gyp/test/external-cross-compile/src/test1.cc b/tools/gyp/test/external-cross-compile/src/test1.cc
new file mode 100644
index 00000000000000..b584c31d153015
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/test1.cc
@@ -0,0 +1 @@
+From test1.cc
diff --git a/tools/gyp/test/external-cross-compile/src/test2.c b/tools/gyp/test/external-cross-compile/src/test2.c
new file mode 100644
index 00000000000000..367ae19ea0c836
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/test2.c
@@ -0,0 +1 @@
+From test2.c
diff --git a/tools/gyp/test/external-cross-compile/src/test3.cc b/tools/gyp/test/external-cross-compile/src/test3.cc
new file mode 100644
index 00000000000000..9eb64735b8a0fe
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/test3.cc
@@ -0,0 +1 @@
+From test3.cc
diff --git a/tools/gyp/test/external-cross-compile/src/test4.c b/tools/gyp/test/external-cross-compile/src/test4.c
new file mode 100644
index 00000000000000..8ecc33ec16d57a
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/test4.c
@@ -0,0 +1 @@
+From test4.c
diff --git a/tools/gyp/test/external-cross-compile/src/tochar.py b/tools/gyp/test/external-cross-compile/src/tochar.py
new file mode 100644
index 00000000000000..c0780d984f0e5e
--- /dev/null
+++ b/tools/gyp/test/external-cross-compile/src/tochar.py
@@ -0,0 +1,13 @@
+#!/usr/bin/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 sys
+
+src = open(sys.argv[1])
+dst = open(sys.argv[2], 'w')
+for ch in src.read():
+ dst.write('%d,\n' % ord(ch))
+src.close()
+dst.close()
diff --git a/tools/gyp/test/generator-output/actions/actions.gyp b/tools/gyp/test/generator-output/actions/actions.gyp
new file mode 100644
index 00000000000000..dded59aff3e34f
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/actions.gyp
@@ -0,0 +1,16 @@
+# Copyright (c) 2009 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': 'pull_in_all_actions',
+ 'type': 'none',
+ 'dependencies': [
+ 'subdir1/executable.gyp:*',
+ 'subdir2/none.gyp:*',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/actions/build/README.txt b/tools/gyp/test/generator-output/actions/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt b/tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir1/actions-out/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir1/build/README.txt b/tools/gyp/test/generator-output/actions/subdir1/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir1/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir1/executable.gyp b/tools/gyp/test/generator-output/actions/subdir1/executable.gyp
new file mode 100644
index 00000000000000..6bdd60a1fb79f3
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir1/executable.gyp
@@ -0,0 +1,44 @@
+# Copyright (c) 2009 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': 'program',
+ 'type': 'executable',
+ 'msvs_cygwin_shell': 0,
+ 'sources': [
+ 'program.c',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'make-prog1',
+ 'inputs': [
+ 'make-prog1.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/prog1.c',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ },
+ {
+ 'action_name': 'make-prog2',
+ 'inputs': [
+ 'make-prog2.py',
+ ],
+ 'outputs': [
+ 'actions-out/prog2.c',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/actions/subdir1/make-prog1.py b/tools/gyp/test/generator-output/actions/subdir1/make-prog1.py
new file mode 100755
index 00000000000000..7ea1d8a2d48708
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir1/make-prog1.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = r"""
+#include
+
+void prog1(void)
+{
+ printf("Hello from make-prog1.py\n");
+}
+"""
+
+open(sys.argv[1], 'w').write(contents)
+
+sys.exit(0)
diff --git a/tools/gyp/test/generator-output/actions/subdir1/make-prog2.py b/tools/gyp/test/generator-output/actions/subdir1/make-prog2.py
new file mode 100755
index 00000000000000..0bfe4973c24c15
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir1/make-prog2.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = r"""
+#include
+
+void prog2(void)
+{
+ printf("Hello from make-prog2.py\n");
+}
+"""
+
+open(sys.argv[1], 'w').write(contents)
+
+sys.exit(0)
diff --git a/tools/gyp/test/generator-output/actions/subdir1/program.c b/tools/gyp/test/generator-output/actions/subdir1/program.c
new file mode 100644
index 00000000000000..15f0f94ee57f47
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir1/program.c
@@ -0,0 +1,12 @@
+#include
+
+extern void prog1(void);
+extern void prog2(void);
+
+int main(void)
+{
+ printf("Hello from program.c\n");
+ prog1();
+ prog2();
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt b/tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir2/actions-out/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir2/build/README.txt b/tools/gyp/test/generator-output/actions/subdir2/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir2/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/actions/subdir2/make-file.py b/tools/gyp/test/generator-output/actions/subdir2/make-file.py
new file mode 100755
index 00000000000000..fff0653144745a
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir2/make-file.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = "Hello from make-file.py\n"
+
+open(sys.argv[1], 'wb').write(contents)
diff --git a/tools/gyp/test/generator-output/actions/subdir2/none.gyp b/tools/gyp/test/generator-output/actions/subdir2/none.gyp
new file mode 100644
index 00000000000000..f98f52753d0ffe
--- /dev/null
+++ b/tools/gyp/test/generator-output/actions/subdir2/none.gyp
@@ -0,0 +1,31 @@
+# Copyright (c) 2009 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': 'file',
+ 'type': 'none',
+ 'msvs_cygwin_shell': 0,
+ 'actions': [
+ {
+ 'action_name': 'make-file',
+ 'inputs': [
+ 'make-file.py',
+ ],
+ 'outputs': [
+ 'actions-out/file.out',
+ # TODO: enhance testing infrastructure to test this
+ # without having to hard-code the intermediate dir paths.
+ #'<(INTERMEDIATE_DIR)/file.out',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/copies/build/README.txt b/tools/gyp/test/generator-output/copies/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/copies/copies-out/README.txt b/tools/gyp/test/generator-output/copies/copies-out/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/copies-out/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/copies/copies.gyp b/tools/gyp/test/generator-output/copies/copies.gyp
new file mode 100644
index 00000000000000..479a3d9b6e8b0d
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/copies.gyp
@@ -0,0 +1,50 @@
+# Copyright (c) 2009 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': 'pull_in_subdir',
+ 'type': 'none',
+ 'dependencies': [
+ 'subdir/subdir.gyp:*',
+ ],
+ },
+ {
+ 'target_name': 'copies1',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': 'copies-out',
+ 'files': [
+ 'file1',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'copies2',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out',
+ 'files': [
+ 'file2',
+ ],
+ },
+ ],
+ },
+ # Verify that a null 'files' list doesn't gag the generators.
+ {
+ 'target_name': 'copies_null',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-null',
+ 'files': [],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/copies/file1 b/tools/gyp/test/generator-output/copies/file1
new file mode 100644
index 00000000000000..84d55c5759cf6b
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/file1
@@ -0,0 +1 @@
+file1 contents
diff --git a/tools/gyp/test/generator-output/copies/file2 b/tools/gyp/test/generator-output/copies/file2
new file mode 100644
index 00000000000000..af1b8ae35db461
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/file2
@@ -0,0 +1 @@
+file2 contents
diff --git a/tools/gyp/test/generator-output/copies/subdir/build/README.txt b/tools/gyp/test/generator-output/copies/subdir/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/subdir/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt b/tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/subdir/copies-out/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/copies/subdir/file3 b/tools/gyp/test/generator-output/copies/subdir/file3
new file mode 100644
index 00000000000000..43f16f35220657
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/subdir/file3
@@ -0,0 +1 @@
+file3 contents
diff --git a/tools/gyp/test/generator-output/copies/subdir/file4 b/tools/gyp/test/generator-output/copies/subdir/file4
new file mode 100644
index 00000000000000..5f7270a0847cc6
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/subdir/file4
@@ -0,0 +1 @@
+file4 contents
diff --git a/tools/gyp/test/generator-output/copies/subdir/subdir.gyp b/tools/gyp/test/generator-output/copies/subdir/subdir.gyp
new file mode 100644
index 00000000000000..af031d283ae452
--- /dev/null
+++ b/tools/gyp/test/generator-output/copies/subdir/subdir.gyp
@@ -0,0 +1,32 @@
+# Copyright (c) 2009 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': 'copies3',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': 'copies-out',
+ 'files': [
+ 'file3',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'copies4',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/copies-out',
+ 'files': [
+ 'file4',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/gyptest-actions.py b/tools/gyp/test/generator-output/gyptest-actions.py
new file mode 100755
index 00000000000000..2bd09c887b5b93
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-actions.py
@@ -0,0 +1,58 @@
+#!/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 --generator-output= behavior when using actions.
+"""
+
+import TestGyp
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+
+# All the generated files should go under 'gypfiles'. The source directory
+# ('actions') should be untouched.
+test.writable(test.workpath('actions'), False)
+test.run_gyp('actions.gyp',
+ '--generator-output=' + test.workpath('gypfiles'),
+ chdir='actions')
+
+test.writable(test.workpath('actions'), True)
+
+test.relocate('actions', 'relocate/actions')
+test.relocate('gypfiles', 'relocate/gypfiles')
+
+test.writable(test.workpath('relocate/actions'), False)
+
+# Some of the action outputs use "pure" relative paths (i.e. without prefixes
+# like <(INTERMEDIATE_DIR) or <(PROGRAM_DIR)). Even though we are building under
+# 'gypfiles', such outputs will still be created relative to the original .gyp
+# sources. Projects probably wouldn't normally do this, since it kind of defeats
+# the purpose of '--generator-output', but it is supported behaviour.
+test.writable(test.workpath('relocate/actions/build'), True)
+test.writable(test.workpath('relocate/actions/subdir1/build'), True)
+test.writable(test.workpath('relocate/actions/subdir1/actions-out'), True)
+test.writable(test.workpath('relocate/actions/subdir2/build'), True)
+test.writable(test.workpath('relocate/actions/subdir2/actions-out'), True)
+
+test.build('actions.gyp', test.ALL, chdir='relocate/gypfiles')
+
+expect = """\
+Hello from program.c
+Hello from make-prog1.py
+Hello from make-prog2.py
+"""
+
+if test.format == 'xcode':
+ chdir = 'relocate/actions/subdir1'
+else:
+ chdir = 'relocate/gypfiles'
+test.run_built_executable('program', chdir=chdir, stdout=expect)
+
+test.must_match('relocate/actions/subdir2/actions-out/file.out',
+ "Hello from make-file.py\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-copies.py b/tools/gyp/test/generator-output/gyptest-copies.py
new file mode 100755
index 00000000000000..909aebf85f8a69
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-copies.py
@@ -0,0 +1,60 @@
+#!/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 file copies with --generator-output using an explicit build
+target of 'all'.
+"""
+
+import TestGyp
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+
+test.writable(test.workpath('copies'), False)
+
+test.run_gyp('copies.gyp',
+ '--generator-output=' + test.workpath('gypfiles'),
+ '-G', 'xcode_ninja_target_pattern=^(?!copies_null)',
+ chdir='copies')
+
+test.writable(test.workpath('copies'), True)
+
+test.relocate('copies', 'relocate/copies')
+test.relocate('gypfiles', 'relocate/gypfiles')
+
+test.writable(test.workpath('relocate/copies'), False)
+
+test.writable(test.workpath('relocate/copies/build'), True)
+test.writable(test.workpath('relocate/copies/copies-out'), True)
+test.writable(test.workpath('relocate/copies/subdir/build'), True)
+test.writable(test.workpath('relocate/copies/subdir/copies-out'), True)
+
+test.build('copies.gyp', test.ALL, chdir='relocate/gypfiles')
+
+test.must_match(['relocate', 'copies', 'copies-out', 'file1'],
+ "file1 contents\n")
+
+if test.format == 'xcode':
+ chdir = 'relocate/copies/build'
+elif test.format in ['make', 'ninja', 'xcode-ninja', 'cmake']:
+ chdir = 'relocate/gypfiles/out'
+else:
+ chdir = 'relocate/gypfiles'
+test.must_match([chdir, 'Default', 'copies-out', 'file2'], "file2 contents\n")
+
+test.must_match(['relocate', 'copies', 'subdir', 'copies-out', 'file3'],
+ "file3 contents\n")
+
+if test.format == 'xcode':
+ chdir = 'relocate/copies/subdir/build'
+elif test.format in ['make', 'ninja', 'xcode-ninja', 'cmake']:
+ chdir = 'relocate/gypfiles/out'
+else:
+ chdir = 'relocate/gypfiles'
+test.must_match([chdir, 'Default', 'copies-out', 'file4'], "file4 contents\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-depth.py b/tools/gyp/test/generator-output/gyptest-depth.py
new file mode 100755
index 00000000000000..ee59a11f045df7
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-depth.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies building a project hierarchy created when the --generator-output=
+and --depth= options is used to put the build configuration files in a separate
+directory tree.
+"""
+
+import TestGyp
+import os
+
+# This is a regression test for the make generator only.
+test = TestGyp.TestGyp(formats=['make'])
+
+test.writable(test.workpath('src'), False)
+
+toplevel_dir = os.path.basename(test.workpath())
+
+test.run_gyp(os.path.join(toplevel_dir, 'src', 'prog1.gyp'),
+ '-Dset_symroot=1',
+ '--generator-output=gypfiles',
+ depth=toplevel_dir,
+ chdir='..')
+
+test.writable(test.workpath('src/build'), True)
+test.writable(test.workpath('src/subdir2/build'), True)
+test.writable(test.workpath('src/subdir3/build'), True)
+
+test.build('prog1.gyp', test.ALL, chdir='gypfiles')
+
+chdir = 'gypfiles'
+
+expect = """\
+Hello from %s
+Hello from inc.h
+Hello from inc1/include1.h
+Hello from inc2/include2.h
+Hello from inc3/include3.h
+Hello from subdir2/deeper/deeper.h
+"""
+
+if test.format == 'xcode':
+ chdir = 'src'
+test.run_built_executable('prog1', chdir=chdir, stdout=expect % 'prog1.c')
+
+if test.format == 'xcode':
+ chdir = 'src/subdir2'
+test.run_built_executable('prog2', chdir=chdir, stdout=expect % 'prog2.c')
+
+if test.format == 'xcode':
+ chdir = 'src/subdir3'
+test.run_built_executable('prog3', chdir=chdir, stdout=expect % 'prog3.c')
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-mac-bundle.py b/tools/gyp/test/generator-output/gyptest-mac-bundle.py
new file mode 100644
index 00000000000000..840e1ec81fe998
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-mac-bundle.py
@@ -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 mac bundles work with --generator-output.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=[])
+
+ MAC_BUNDLE_DIR = 'mac-bundle'
+ GYPFILES_DIR = 'gypfiles'
+ test.writable(test.workpath(MAC_BUNDLE_DIR), False)
+ test.run_gyp('test.gyp',
+ '--generator-output=' + test.workpath(GYPFILES_DIR),
+ chdir=MAC_BUNDLE_DIR)
+ test.writable(test.workpath(MAC_BUNDLE_DIR), True)
+
+ test.build('test.gyp', test.ALL, chdir=GYPFILES_DIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-relocate.py b/tools/gyp/test/generator-output/gyptest-relocate.py
new file mode 100755
index 00000000000000..4d60a1ea52fb85
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-relocate.py
@@ -0,0 +1,60 @@
+#!/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 project hierarchy created with the --generator-output=
+option can be built even when it's relocated to a different path.
+"""
+
+import TestGyp
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+
+test.writable(test.workpath('src'), False)
+
+test.run_gyp('prog1.gyp',
+ '-Dset_symroot=1',
+ '--generator-output=' + test.workpath('gypfiles'),
+ chdir='src')
+
+test.writable(test.workpath('src'), True)
+
+test.relocate('src', 'relocate/src')
+test.relocate('gypfiles', 'relocate/gypfiles')
+
+test.writable(test.workpath('relocate/src'), False)
+
+test.writable(test.workpath('relocate/src/build'), True)
+test.writable(test.workpath('relocate/src/subdir2/build'), True)
+test.writable(test.workpath('relocate/src/subdir3/build'), True)
+
+test.build('prog1.gyp', test.ALL, chdir='relocate/gypfiles')
+
+chdir = 'relocate/gypfiles'
+
+expect = """\
+Hello from %s
+Hello from inc.h
+Hello from inc1/include1.h
+Hello from inc2/include2.h
+Hello from inc3/include3.h
+Hello from subdir2/deeper/deeper.h
+"""
+
+if test.format == 'xcode':
+ chdir = 'relocate/src'
+test.run_built_executable('prog1', chdir=chdir, stdout=expect % 'prog1.c')
+
+if test.format == 'xcode':
+ chdir = 'relocate/src/subdir2'
+test.run_built_executable('prog2', chdir=chdir, stdout=expect % 'prog2.c')
+
+if test.format == 'xcode':
+ chdir = 'relocate/src/subdir3'
+test.run_built_executable('prog3', chdir=chdir, stdout=expect % 'prog3.c')
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-rules.py b/tools/gyp/test/generator-output/gyptest-rules.py
new file mode 100755
index 00000000000000..324d5c2bb54d01
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-rules.py
@@ -0,0 +1,59 @@
+#!/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 --generator-output= behavior when using rules.
+"""
+
+import TestGyp
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+
+test.writable(test.workpath('rules'), False)
+
+test.run_gyp('rules.gyp',
+ '--generator-output=' + test.workpath('gypfiles'),
+ chdir='rules')
+
+test.writable(test.workpath('rules'), True)
+
+test.relocate('rules', 'relocate/rules')
+test.relocate('gypfiles', 'relocate/gypfiles')
+
+test.writable(test.workpath('relocate/rules'), False)
+
+test.writable(test.workpath('relocate/rules/build'), True)
+test.writable(test.workpath('relocate/rules/subdir1/build'), True)
+test.writable(test.workpath('relocate/rules/subdir2/build'), True)
+test.writable(test.workpath('relocate/rules/subdir2/rules-out'), True)
+
+test.build('rules.gyp', test.ALL, chdir='relocate/gypfiles')
+
+expect = """\
+Hello from program.c
+Hello from function1.in1
+Hello from function2.in1
+Hello from define3.in0
+Hello from define4.in0
+"""
+
+if test.format == 'xcode':
+ chdir = 'relocate/rules/subdir1'
+else:
+ chdir = 'relocate/gypfiles'
+test.run_built_executable('program', chdir=chdir, stdout=expect)
+
+test.must_match('relocate/rules/subdir2/rules-out/file1.out',
+ "Hello from file1.in0\n")
+test.must_match('relocate/rules/subdir2/rules-out/file2.out',
+ "Hello from file2.in0\n")
+test.must_match('relocate/rules/subdir2/rules-out/file3.out',
+ "Hello from file3.in1\n")
+test.must_match('relocate/rules/subdir2/rules-out/file4.out',
+ "Hello from file4.in1\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-subdir2-deep.py b/tools/gyp/test/generator-output/gyptest-subdir2-deep.py
new file mode 100755
index 00000000000000..305e178d412efe
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-subdir2-deep.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.
+
+"""
+Verifies building a target from a .gyp file a few subdirectories
+deep when the --generator-output= option is used to put the build
+configuration files in a separate directory tree.
+"""
+
+import TestGyp
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+
+test.writable(test.workpath('src'), False)
+
+test.writable(test.workpath('src/subdir2/deeper/build'), True)
+
+test.run_gyp('deeper.gyp',
+ '-Dset_symroot=1',
+ '--generator-output=' + test.workpath('gypfiles'),
+ chdir='src/subdir2/deeper')
+
+test.build('deeper.gyp', test.ALL, chdir='gypfiles')
+
+chdir = 'gypfiles'
+
+if test.format == 'xcode':
+ chdir = 'src/subdir2/deeper'
+test.run_built_executable('deeper',
+ chdir=chdir,
+ stdout="Hello from deeper.c\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-symlink.py b/tools/gyp/test/generator-output/gyptest-symlink.py
new file mode 100755
index 00000000000000..7390fcd37f15d0
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-symlink.py
@@ -0,0 +1,43 @@
+#!/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 building a target when the --generator-output= option is used to put
+the build configuration files in a separate directory tree referenced by a
+symlink.
+"""
+
+import TestGyp
+import os
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+if not hasattr(os, 'symlink'):
+ test.skip_test('Missing os.symlink -- skipping test.\n')
+
+test.writable(test.workpath('src'), False)
+
+test.writable(test.workpath('src/subdir2/deeper/build'), True)
+
+test.subdir(test.workpath('build'))
+test.subdir(test.workpath('build/deeper'))
+test.symlink('build/deeper', test.workpath('symlink'))
+
+test.writable(test.workpath('build/deeper'), True)
+test.run_gyp('deeper.gyp',
+ '-Dset_symroot=2',
+ '--generator-output=' + test.workpath('symlink'),
+ chdir='src/subdir2/deeper')
+
+chdir = 'symlink'
+test.build('deeper.gyp', test.ALL, chdir=chdir)
+
+if test.format == 'xcode':
+ chdir = 'src/subdir2/deeper'
+test.run_built_executable('deeper',
+ chdir=chdir,
+ stdout="Hello from deeper.c\n")
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/gyptest-top-all.py b/tools/gyp/test/generator-output/gyptest-top-all.py
new file mode 100755
index 00000000000000..c1d9f60d66953a
--- /dev/null
+++ b/tools/gyp/test/generator-output/gyptest-top-all.py
@@ -0,0 +1,54 @@
+#!/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 building a project hierarchy created when the --generator-output=
+option is used to put the build configuration files in a separate
+directory tree.
+"""
+
+import TestGyp
+
+# Android doesn't support --generator-output.
+test = TestGyp.TestGyp(formats=['!android'])
+
+test.writable(test.workpath('src'), False)
+
+test.run_gyp('prog1.gyp',
+ '-Dset_symroot=1',
+ '--generator-output=' + test.workpath('gypfiles'),
+ chdir='src')
+
+test.writable(test.workpath('src/build'), True)
+test.writable(test.workpath('src/subdir2/build'), True)
+test.writable(test.workpath('src/subdir3/build'), True)
+
+test.build('prog1.gyp', test.ALL, chdir='gypfiles')
+
+chdir = 'gypfiles'
+
+expect = """\
+Hello from %s
+Hello from inc.h
+Hello from inc1/include1.h
+Hello from inc2/include2.h
+Hello from inc3/include3.h
+Hello from subdir2/deeper/deeper.h
+"""
+
+if test.format == 'xcode':
+ chdir = 'src'
+test.run_built_executable('prog1', chdir=chdir, stdout=expect % 'prog1.c')
+
+if test.format == 'xcode':
+ chdir = 'src/subdir2'
+test.run_built_executable('prog2', chdir=chdir, stdout=expect % 'prog2.c')
+
+if test.format == 'xcode':
+ chdir = 'src/subdir3'
+test.run_built_executable('prog3', chdir=chdir, stdout=expect % 'prog3.c')
+
+test.pass_test()
diff --git a/tools/gyp/test/generator-output/mac-bundle/Info.plist b/tools/gyp/test/generator-output/mac-bundle/Info.plist
new file mode 100644
index 00000000000000..8cb142e9f5aa52
--- /dev/null
+++ b/tools/gyp/test/generator-output/mac-bundle/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.google.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ause
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ ${MACOSX_DEPLOYMENT_TARGET}
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/tools/gyp/test/generator-output/mac-bundle/app.order b/tools/gyp/test/generator-output/mac-bundle/app.order
new file mode 100644
index 00000000000000..4eb9e89d39c8b5
--- /dev/null
+++ b/tools/gyp/test/generator-output/mac-bundle/app.order
@@ -0,0 +1 @@
+_main
diff --git a/tools/gyp/test/generator-output/mac-bundle/header.h b/tools/gyp/test/generator-output/mac-bundle/header.h
new file mode 100644
index 00000000000000..7ed77751229041
--- /dev/null
+++ b/tools/gyp/test/generator-output/mac-bundle/header.h
@@ -0,0 +1 @@
+int f();
diff --git a/tools/gyp/test/generator-output/mac-bundle/main.c b/tools/gyp/test/generator-output/mac-bundle/main.c
new file mode 100644
index 00000000000000..237c8ce181774d
--- /dev/null
+++ b/tools/gyp/test/generator-output/mac-bundle/main.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/generator-output/mac-bundle/resource.sb b/tools/gyp/test/generator-output/mac-bundle/resource.sb
new file mode 100644
index 00000000000000..731befc4570d20
--- /dev/null
+++ b/tools/gyp/test/generator-output/mac-bundle/resource.sb
@@ -0,0 +1 @@
+A text file.
diff --git a/tools/gyp/test/generator-output/mac-bundle/test.gyp b/tools/gyp/test/generator-output/mac-bundle/test.gyp
new file mode 100644
index 00000000000000..35ac674f6d7d83
--- /dev/null
+++ b/tools/gyp/test/generator-output/mac-bundle/test.gyp
@@ -0,0 +1,25 @@
+# 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 App Gyp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'main.c',
+ ],
+ 'mac_bundle_resources': [
+ 'resource.sb',
+ ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ 'ORDER_FILE': 'app.order',
+ 'GCC_PREFIX_HEADER': 'header.h',
+ 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/rules/build/README.txt b/tools/gyp/test/generator-output/rules/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/rules/copy-file.py b/tools/gyp/test/generator-output/rules/copy-file.py
new file mode 100755
index 00000000000000..938c336adb01e5
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/copy-file.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 sys
+
+contents = open(sys.argv[1], 'r').read()
+open(sys.argv[2], 'wb').write(contents)
+
+sys.exit(0)
diff --git a/tools/gyp/test/generator-output/rules/rules.gyp b/tools/gyp/test/generator-output/rules/rules.gyp
new file mode 100644
index 00000000000000..dded59aff3e34f
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/rules.gyp
@@ -0,0 +1,16 @@
+# Copyright (c) 2009 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': 'pull_in_all_actions',
+ 'type': 'none',
+ 'dependencies': [
+ 'subdir1/executable.gyp:*',
+ 'subdir2/none.gyp:*',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/rules/subdir1/build/README.txt b/tools/gyp/test/generator-output/rules/subdir1/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/rules/subdir1/define3.in0 b/tools/gyp/test/generator-output/rules/subdir1/define3.in0
new file mode 100644
index 00000000000000..cc29c643f397ce
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/define3.in0
@@ -0,0 +1 @@
+#define STRING3 "Hello from define3.in0\n"
diff --git a/tools/gyp/test/generator-output/rules/subdir1/define4.in0 b/tools/gyp/test/generator-output/rules/subdir1/define4.in0
new file mode 100644
index 00000000000000..c9b0467b3287f1
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/define4.in0
@@ -0,0 +1 @@
+#define STRING4 "Hello from define4.in0\n"
diff --git a/tools/gyp/test/generator-output/rules/subdir1/executable.gyp b/tools/gyp/test/generator-output/rules/subdir1/executable.gyp
new file mode 100644
index 00000000000000..42bee4d7467f3e
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/executable.gyp
@@ -0,0 +1,59 @@
+# Copyright (c) 2009 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': 'program',
+ 'type': 'executable',
+ 'msvs_cygwin_shell': 0,
+ 'sources': [
+ 'program.c',
+ 'function1.in1',
+ 'function2.in1',
+ 'define3.in0',
+ 'define4.in0',
+ ],
+ 'include_dirs': [
+ '<(INTERMEDIATE_DIR)',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'copy_file_0',
+ 'extension': 'in0',
+ 'inputs': [
+ '../copy-file.py',
+ ],
+ 'outputs': [
+ # TODO: fix Make to support generated files not
+ # in a variable-named path like <(INTERMEDIATE_DIR)
+ #'<(RULE_INPUT_ROOT).c',
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).h',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 0,
+ },
+ {
+ 'rule_name': 'copy_file_1',
+ 'extension': 'in1',
+ 'inputs': [
+ '../copy-file.py',
+ ],
+ 'outputs': [
+ # TODO: fix Make to support generated files not
+ # in a variable-named path like <(INTERMEDIATE_DIR)
+ #'<(RULE_INPUT_ROOT).c',
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).c',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/rules/subdir1/function1.in1 b/tools/gyp/test/generator-output/rules/subdir1/function1.in1
new file mode 100644
index 00000000000000..545e7ca16bb273
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/function1.in1
@@ -0,0 +1,6 @@
+#include
+
+void function1(void)
+{
+ printf("Hello from function1.in1\n");
+}
diff --git a/tools/gyp/test/generator-output/rules/subdir1/function2.in1 b/tools/gyp/test/generator-output/rules/subdir1/function2.in1
new file mode 100644
index 00000000000000..6bad43f9cf45b2
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/function2.in1
@@ -0,0 +1,6 @@
+#include
+
+void function2(void)
+{
+ printf("Hello from function2.in1\n");
+}
diff --git a/tools/gyp/test/generator-output/rules/subdir1/program.c b/tools/gyp/test/generator-output/rules/subdir1/program.c
new file mode 100644
index 00000000000000..56b320632a75cb
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir1/program.c
@@ -0,0 +1,18 @@
+#include
+#include "define3.h"
+#include "define4.h"
+
+extern void function1(void);
+extern void function2(void);
+extern void function3(void);
+extern void function4(void);
+
+int main(void)
+{
+ printf("Hello from program.c\n");
+ function1();
+ function2();
+ printf("%s", STRING3);
+ printf("%s", STRING4);
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/rules/subdir2/build/README.txt b/tools/gyp/test/generator-output/rules/subdir2/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/rules/subdir2/file1.in0 b/tools/gyp/test/generator-output/rules/subdir2/file1.in0
new file mode 100644
index 00000000000000..7aca64f4ce0fb8
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/file1.in0
@@ -0,0 +1 @@
+Hello from file1.in0
diff --git a/tools/gyp/test/generator-output/rules/subdir2/file2.in0 b/tools/gyp/test/generator-output/rules/subdir2/file2.in0
new file mode 100644
index 00000000000000..80a281a2a9a19d
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/file2.in0
@@ -0,0 +1 @@
+Hello from file2.in0
diff --git a/tools/gyp/test/generator-output/rules/subdir2/file3.in1 b/tools/gyp/test/generator-output/rules/subdir2/file3.in1
new file mode 100644
index 00000000000000..60ae2e7931136d
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/file3.in1
@@ -0,0 +1 @@
+Hello from file3.in1
diff --git a/tools/gyp/test/generator-output/rules/subdir2/file4.in1 b/tools/gyp/test/generator-output/rules/subdir2/file4.in1
new file mode 100644
index 00000000000000..5a3c30720e7af0
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/file4.in1
@@ -0,0 +1 @@
+Hello from file4.in1
diff --git a/tools/gyp/test/generator-output/rules/subdir2/none.gyp b/tools/gyp/test/generator-output/rules/subdir2/none.gyp
new file mode 100644
index 00000000000000..664cbd9cb75f6c
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/none.gyp
@@ -0,0 +1,49 @@
+# Copyright (c) 2009 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': 'files',
+ 'type': 'none',
+ 'msvs_cygwin_shell': 0,
+ 'sources': [
+ 'file1.in0',
+ 'file2.in0',
+ 'file3.in1',
+ 'file4.in1',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'copy_file_0',
+ 'extension': 'in0',
+ 'inputs': [
+ '../copy-file.py',
+ ],
+ 'outputs': [
+ 'rules-out/<(RULE_INPUT_ROOT).out',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 0,
+ },
+ {
+ 'rule_name': 'copy_file_1',
+ 'extension': 'in1',
+ 'inputs': [
+ '../copy-file.py',
+ ],
+ 'outputs': [
+ 'rules-out/<(RULE_INPUT_ROOT).out',
+ ],
+ 'action': [
+ 'python', '<(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)',
+ ],
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt b/tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/rules/subdir2/rules-out/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/src/build/README.txt b/tools/gyp/test/generator-output/src/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/src/inc.h b/tools/gyp/test/generator-output/src/inc.h
new file mode 100644
index 00000000000000..57aa1a5a7443fb
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/inc.h
@@ -0,0 +1 @@
+#define INC_STRING "inc.h"
diff --git a/tools/gyp/test/generator-output/src/inc1/include1.h b/tools/gyp/test/generator-output/src/inc1/include1.h
new file mode 100644
index 00000000000000..1d59065fc90a72
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/inc1/include1.h
@@ -0,0 +1 @@
+#define INCLUDE1_STRING "inc1/include1.h"
diff --git a/tools/gyp/test/generator-output/src/prog1.c b/tools/gyp/test/generator-output/src/prog1.c
new file mode 100644
index 00000000000000..bf7c2a17bd3a8f
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/prog1.c
@@ -0,0 +1,18 @@
+#include
+
+#include "inc.h"
+#include "include1.h"
+#include "include2.h"
+#include "include3.h"
+#include "deeper.h"
+
+int main(void)
+{
+ printf("Hello from prog1.c\n");
+ printf("Hello from %s\n", INC_STRING);
+ printf("Hello from %s\n", INCLUDE1_STRING);
+ printf("Hello from %s\n", INCLUDE2_STRING);
+ printf("Hello from %s\n", INCLUDE3_STRING);
+ printf("Hello from %s\n", DEEPER_STRING);
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/src/prog1.gyp b/tools/gyp/test/generator-output/src/prog1.gyp
new file mode 100644
index 00000000000000..d50e6fb0a7edd7
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/prog1.gyp
@@ -0,0 +1,28 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ 'symroot.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog1',
+ 'type': 'executable',
+ 'dependencies': [
+ 'subdir2/prog2.gyp:prog2',
+ ],
+ 'include_dirs': [
+ '.',
+ 'inc1',
+ 'subdir2/inc2',
+ 'subdir3/inc3',
+ 'subdir2/deeper',
+ ],
+ 'sources': [
+ 'prog1.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/src/subdir2/build/README.txt b/tools/gyp/test/generator-output/src/subdir2/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt b/tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/deeper/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c
new file mode 100644
index 00000000000000..843505cd11cd8b
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.c
@@ -0,0 +1,7 @@
+#include
+
+int main(void)
+{
+ printf("Hello from deeper.c\n");
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp
new file mode 100644
index 00000000000000..86487708723770
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../../symroot.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'deeper',
+ 'type': 'executable',
+ 'sources': [
+ 'deeper.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h
new file mode 100644
index 00000000000000..f6484a0fe58666
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/deeper/deeper.h
@@ -0,0 +1 @@
+#define DEEPER_STRING "subdir2/deeper/deeper.h"
diff --git a/tools/gyp/test/generator-output/src/subdir2/inc2/include2.h b/tools/gyp/test/generator-output/src/subdir2/inc2/include2.h
new file mode 100644
index 00000000000000..1ccfa5dea79fea
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/inc2/include2.h
@@ -0,0 +1 @@
+#define INCLUDE2_STRING "inc2/include2.h"
diff --git a/tools/gyp/test/generator-output/src/subdir2/prog2.c b/tools/gyp/test/generator-output/src/subdir2/prog2.c
new file mode 100644
index 00000000000000..d80d8719843767
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/prog2.c
@@ -0,0 +1,18 @@
+#include
+
+#include "inc.h"
+#include "include1.h"
+#include "include2.h"
+#include "include3.h"
+#include "deeper.h"
+
+int main(void)
+{
+ printf("Hello from prog2.c\n");
+ printf("Hello from %s\n", INC_STRING);
+ printf("Hello from %s\n", INCLUDE1_STRING);
+ printf("Hello from %s\n", INCLUDE2_STRING);
+ printf("Hello from %s\n", INCLUDE3_STRING);
+ printf("Hello from %s\n", DEEPER_STRING);
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/src/subdir2/prog2.gyp b/tools/gyp/test/generator-output/src/subdir2/prog2.gyp
new file mode 100644
index 00000000000000..7176ed8be74ef1
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir2/prog2.gyp
@@ -0,0 +1,28 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../symroot.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog2',
+ 'type': 'executable',
+ 'include_dirs': [
+ '..',
+ '../inc1',
+ 'inc2',
+ '../subdir3/inc3',
+ 'deeper',
+ ],
+ 'dependencies': [
+ '../subdir3/prog3.gyp:prog3',
+ ],
+ 'sources': [
+ 'prog2.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/src/subdir3/build/README.txt b/tools/gyp/test/generator-output/src/subdir3/build/README.txt
new file mode 100644
index 00000000000000..90ef8861931485
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir3/build/README.txt
@@ -0,0 +1,4 @@
+A place-holder for this Xcode build output directory, so that the
+test script can verify that .xcodeproj files are not created in
+their normal location by making the src/ read-only, and then
+selectively making this build directory writable.
diff --git a/tools/gyp/test/generator-output/src/subdir3/inc3/include3.h b/tools/gyp/test/generator-output/src/subdir3/inc3/include3.h
new file mode 100644
index 00000000000000..bf53bf1f0070d9
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir3/inc3/include3.h
@@ -0,0 +1 @@
+#define INCLUDE3_STRING "inc3/include3.h"
diff --git a/tools/gyp/test/generator-output/src/subdir3/prog3.c b/tools/gyp/test/generator-output/src/subdir3/prog3.c
new file mode 100644
index 00000000000000..c72233da193ca2
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir3/prog3.c
@@ -0,0 +1,18 @@
+#include
+
+#include "inc.h"
+#include "include1.h"
+#include "include2.h"
+#include "include3.h"
+#include "deeper.h"
+
+int main(void)
+{
+ printf("Hello from prog3.c\n");
+ printf("Hello from %s\n", INC_STRING);
+ printf("Hello from %s\n", INCLUDE1_STRING);
+ printf("Hello from %s\n", INCLUDE2_STRING);
+ printf("Hello from %s\n", INCLUDE3_STRING);
+ printf("Hello from %s\n", DEEPER_STRING);
+ return 0;
+}
diff --git a/tools/gyp/test/generator-output/src/subdir3/prog3.gyp b/tools/gyp/test/generator-output/src/subdir3/prog3.gyp
new file mode 100644
index 00000000000000..46c5e000a27247
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/subdir3/prog3.gyp
@@ -0,0 +1,25 @@
+# Copyright (c) 2009 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.
+
+{
+ 'includes': [
+ '../symroot.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'prog3',
+ 'type': 'executable',
+ 'include_dirs': [
+ '..',
+ '../inc1',
+ '../subdir2/inc2',
+ 'inc3',
+ '../subdir2/deeper',
+ ],
+ 'sources': [
+ 'prog3.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/generator-output/src/symroot.gypi b/tools/gyp/test/generator-output/src/symroot.gypi
new file mode 100644
index 00000000000000..519916427c9f13
--- /dev/null
+++ b/tools/gyp/test/generator-output/src/symroot.gypi
@@ -0,0 +1,16 @@
+# Copyright (c) 2009 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.
+
+{
+ 'variables': {
+ 'set_symroot%': 0,
+ },
+ 'conditions': [
+ ['set_symroot == 1', {
+ 'xcode_settings': {
+ 'SYMROOT': '<(DEPTH)/build',
+ },
+ }],
+ ],
+}
diff --git a/tools/gyp/test/gyp-defines/defines.gyp b/tools/gyp/test/gyp-defines/defines.gyp
new file mode 100644
index 00000000000000..f59bbd20d299a5
--- /dev/null
+++ b/tools/gyp/test/gyp-defines/defines.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_target',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'test_action',
+ 'inputs': [],
+ 'outputs': [ 'action.txt' ],
+ 'action': [
+ 'python',
+ 'echo.py',
+ '<(key)',
+ '<(_outputs)',
+ ],
+ 'msvs_cygwin_shell': 0,
+ }
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/gyp-defines/echo.py b/tools/gyp/test/gyp-defines/echo.py
new file mode 100644
index 00000000000000..b85add12f68b3a
--- /dev/null
+++ b/tools/gyp/test/gyp-defines/echo.py
@@ -0,0 +1,11 @@
+#!/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 sys
+
+f = open(sys.argv[2], 'w+')
+f.write(sys.argv[1])
+f.close()
diff --git a/tools/gyp/test/gyp-defines/gyptest-multiple-values.py b/tools/gyp/test/gyp-defines/gyptest-multiple-values.py
new file mode 100644
index 00000000000000..6764dbd545dcdc
--- /dev/null
+++ b/tools/gyp/test/gyp-defines/gyptest-multiple-values.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.
+
+"""
+Verifies that when multiple values are supplied for a gyp define, the last one
+is used.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+os.environ['GYP_DEFINES'] = 'key=value1 key=value2 key=value3'
+test.run_gyp('defines.gyp')
+
+test.build('defines.gyp')
+test.must_contain('action.txt', 'value3')
+
+# The last occurrence of a repeated set should take precedence over other
+# values.
+os.environ['GYP_DEFINES'] = 'key=repeated_value key=value1 key=repeated_value'
+test.run_gyp('defines.gyp')
+
+if test.format == 'msvs' and not test.uses_msbuild:
+ # msvs versions before 2010 don't detect build rule changes not reflected
+ # in file system timestamps. Rebuild to see differences.
+ test.build('defines.gyp', rebuild=True)
+elif test.format == 'android':
+ # The Android build system doesn't currently have a way to get files whose
+ # build rules have changed (but whose timestamps haven't) to be rebuilt.
+ # See bug http://code.google.com/p/gyp/issues/detail?id=308
+ test.unlink('action.txt')
+ test.build('defines.gyp')
+else:
+ test.build('defines.gyp')
+test.must_contain('action.txt', 'repeated_value')
+
+test.pass_test()
diff --git a/tools/gyp/test/gyp-defines/gyptest-regyp.py b/tools/gyp/test/gyp-defines/gyptest-regyp.py
new file mode 100644
index 00000000000000..0895d81d4f559e
--- /dev/null
+++ b/tools/gyp/test/gyp-defines/gyptest-regyp.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 when the same value is repeated for a gyp define, duplicates are
+stripped from the regeneration rule.
+"""
+
+import os
+import TestGyp
+
+# Regenerating build files when a gyp file changes is currently only supported
+# by the make generator.
+test = TestGyp.TestGyp(formats=['make'])
+
+os.environ['GYP_DEFINES'] = 'key=repeated_value key=value1 key=repeated_value'
+test.run_gyp('defines.gyp')
+test.build('defines.gyp')
+
+# The last occurrence of a repeated set should take precedence over other
+# values. See gyptest-multiple-values.py.
+test.must_contain('action.txt', 'repeated_value')
+
+# So the regeneration rule needs to use the correct order.
+test.must_not_contain(
+ 'Makefile', '"-Dkey=repeated_value" "-Dkey=value1" "-Dkey=repeated_value"')
+test.must_contain('Makefile', '"-Dkey=value1" "-Dkey=repeated_value"')
+
+# Sleep so that the changed gyp file will have a newer timestamp than the
+# previously generated build files.
+test.sleep()
+os.utime("defines.gyp", None)
+
+test.build('defines.gyp')
+test.must_contain('action.txt', 'repeated_value')
+
+test.pass_test()
diff --git a/tools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py b/tools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py
new file mode 100755
index 00000000000000..ba515288004199
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/gyptest-exported-hard-dependency.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 a hard_dependency that is exported is pulled in as a dependency
+for a target if the target is a static library and if the generator will
+remove dependencies between static libraries.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+if test.format == 'dump_dependency_json':
+ test.skip_test('Skipping test; dependency JSON does not adjust ' \
+ 'static libraries.\n')
+
+test.run_gyp('hard_dependency.gyp', chdir='src')
+
+chdir = 'relocate/src'
+test.relocate('src', chdir)
+
+test.build('hard_dependency.gyp', 'c', chdir=chdir)
+
+# The 'a' static library should be built, as it has actions with side-effects
+# that are necessary to compile 'c'. Even though 'c' does not directly depend
+# on 'a', because 'a' is a hard_dependency that 'b' exports, 'c' should import
+# it as a hard_dependency and ensure it is built before building 'c'.
+test.built_file_must_exist('a', type=test.STATIC_LIB, chdir=chdir)
+test.built_file_must_not_exist('b', type=test.STATIC_LIB, chdir=chdir)
+test.built_file_must_exist('c', type=test.STATIC_LIB, chdir=chdir)
+test.built_file_must_not_exist('d', type=test.STATIC_LIB, chdir=chdir)
+
+test.pass_test()
diff --git a/tools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py b/tools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py
new file mode 100755
index 00000000000000..10774ca2a0025f
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/gyptest-no-exported-hard-dependency.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 a hard_dependency that is not exported is not pulled in as a
+dependency for a target if the target does not explicitly specify a dependency
+and none of its dependencies export the hard_dependency.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+if test.format == 'dump_dependency_json':
+ test.skip_test('Skipping test; dependency JSON does not adjust ' \
+ 'static libaries.\n')
+
+test.run_gyp('hard_dependency.gyp', chdir='src')
+
+chdir = 'relocate/src'
+test.relocate('src', chdir)
+
+test.build('hard_dependency.gyp', 'd', chdir=chdir)
+
+# Because 'c' does not export a hard_dependency, only the target 'd' should
+# be built. This is because the 'd' target does not need the generated headers
+# in order to be compiled.
+test.built_file_must_not_exist('a', type=test.STATIC_LIB, chdir=chdir)
+test.built_file_must_not_exist('b', type=test.STATIC_LIB, chdir=chdir)
+test.built_file_must_not_exist('c', type=test.STATIC_LIB, chdir=chdir)
+test.built_file_must_exist('d', type=test.STATIC_LIB, chdir=chdir)
+
+test.pass_test()
diff --git a/tools/gyp/test/hard_dependency/src/a.c b/tools/gyp/test/hard_dependency/src/a.c
new file mode 100644
index 00000000000000..0fa0223c979c96
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/a.c
@@ -0,0 +1,9 @@
+/* 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. */
+
+#include "a.h"
+
+int funcA() {
+ return 42;
+}
diff --git a/tools/gyp/test/hard_dependency/src/a.h b/tools/gyp/test/hard_dependency/src/a.h
new file mode 100644
index 00000000000000..854a06504ad3d0
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/a.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2009 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. */
+
+#ifndef A_H_
+#define A_H_
+
+#include "generated.h"
+
+int funcA();
+
+#endif // A_H_
diff --git a/tools/gyp/test/hard_dependency/src/b.c b/tools/gyp/test/hard_dependency/src/b.c
new file mode 100644
index 00000000000000..0baace929e95b9
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/b.c
@@ -0,0 +1,9 @@
+/* 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. */
+
+#include "a.h"
+
+int funcB() {
+ return funcA();
+}
diff --git a/tools/gyp/test/hard_dependency/src/b.h b/tools/gyp/test/hard_dependency/src/b.h
new file mode 100644
index 00000000000000..22b48cefe206ff
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/b.h
@@ -0,0 +1,12 @@
+/* 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. */
+
+#ifndef B_H_
+#define B_H_
+
+#include "a.h"
+
+int funcB();
+
+#endif // B_H_
diff --git a/tools/gyp/test/hard_dependency/src/c.c b/tools/gyp/test/hard_dependency/src/c.c
new file mode 100644
index 00000000000000..7d0068208ec64b
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/c.c
@@ -0,0 +1,10 @@
+/* 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. */
+
+#include "b.h"
+#include "c.h"
+
+int funcC() {
+ return funcB();
+}
diff --git a/tools/gyp/test/hard_dependency/src/c.h b/tools/gyp/test/hard_dependency/src/c.h
new file mode 100644
index 00000000000000..f4ea7fefa2beb5
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/c.h
@@ -0,0 +1,10 @@
+/* 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. */
+
+#ifndef C_H_
+#define C_H_
+
+int funcC();
+
+#endif // C_H_
diff --git a/tools/gyp/test/hard_dependency/src/d.c b/tools/gyp/test/hard_dependency/src/d.c
new file mode 100644
index 00000000000000..d016c3ce71899f
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/d.c
@@ -0,0 +1,9 @@
+/* Copyright (c) 2009 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 "c.h"
+
+int funcD() {
+ return funcC();
+}
diff --git a/tools/gyp/test/hard_dependency/src/emit.py b/tools/gyp/test/hard_dependency/src/emit.py
new file mode 100755
index 00000000000000..2df74b79a12c56
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/emit.py
@@ -0,0 +1,11 @@
+#!/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.
+
+import sys
+
+f = open(sys.argv[1], 'wb')
+f.write('/* Hello World */\n')
+f.close()
diff --git a/tools/gyp/test/hard_dependency/src/hard_dependency.gyp b/tools/gyp/test/hard_dependency/src/hard_dependency.gyp
new file mode 100644
index 00000000000000..4479c5f0452e15
--- /dev/null
+++ b/tools/gyp/test/hard_dependency/src/hard_dependency.gyp
@@ -0,0 +1,78 @@
+# 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': 'a',
+ 'type': 'static_library',
+ 'sources': [
+ 'a.c',
+ 'a.h',
+ ],
+ 'hard_dependency': 1,
+ 'actions': [
+ {
+ 'action_name': 'generate_headers',
+ 'inputs': [
+ 'emit.py'
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/generated.h'
+ ],
+ 'action': [
+ 'python',
+ 'emit.py',
+ '<(SHARED_INTERMEDIATE_DIR)/generated.h',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ },
+ },
+ {
+ 'target_name': 'b',
+ 'type': 'static_library',
+ 'sources': [
+ 'b.c',
+ 'b.h',
+ ],
+ 'dependencies': [
+ 'a',
+ ],
+ 'export_dependent_settings': [
+ 'a',
+ ],
+ },
+ {
+ 'target_name': 'c',
+ 'type': 'static_library',
+ 'sources': [
+ 'c.c',
+ 'c.h',
+ ],
+ 'dependencies': [
+ 'b',
+ ],
+ },
+ {
+ 'target_name': 'd',
+ 'type': 'static_library',
+ 'sources': [
+ 'd.c',
+ ],
+ 'dependencies': [
+ 'c',
+ ],
+ }
+ ],
+}
diff --git a/tools/gyp/test/hello/gyptest-all.py b/tools/gyp/test/hello/gyptest-all.py
new file mode 100755
index 00000000000000..1739b6886e9fb0
--- /dev/null
+++ b/tools/gyp/test/hello/gyptest-all.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 simplest-possible build of a "Hello, world!" program
+using an explicit build target of 'all'.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_all')
+
+test.run_gyp('hello.gyp')
+
+test.build('hello.gyp', test.ALL)
+
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+test.up_to_date('hello.gyp', test.ALL)
+
+test.pass_test()
diff --git a/tools/gyp/test/hello/gyptest-default.py b/tools/gyp/test/hello/gyptest-default.py
new file mode 100755
index 00000000000000..22377e7ac50433
--- /dev/null
+++ b/tools/gyp/test/hello/gyptest-default.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 simplest-possible build of a "Hello, world!" program
+using the default build target.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_default')
+
+test.run_gyp('hello.gyp')
+
+test.build('hello.gyp')
+
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+test.up_to_date('hello.gyp', test.DEFAULT)
+
+test.pass_test()
diff --git a/tools/gyp/test/hello/gyptest-disable-regyp.py b/tools/gyp/test/hello/gyptest-disable-regyp.py
new file mode 100755
index 00000000000000..1e4b306674862c
--- /dev/null
+++ b/tools/gyp/test/hello/gyptest-disable-regyp.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 Makefiles don't get rebuilt when a source gyp file changes and
+the disable_regeneration generator flag is set.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('hello.gyp', '-Gauto_regeneration=0')
+
+test.build('hello.gyp', test.ALL)
+
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+# Sleep so that the changed gyp file will have a newer timestamp than the
+# previously generated build files.
+test.sleep()
+test.write('hello.gyp', test.read('hello2.gyp'))
+
+test.build('hello.gyp', test.ALL)
+
+# Should still be the old executable, as regeneration was disabled.
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/hello/gyptest-regyp-output.py b/tools/gyp/test/hello/gyptest-regyp-output.py
new file mode 100644
index 00000000000000..5e698b1e1148bd
--- /dev/null
+++ b/tools/gyp/test/hello/gyptest-regyp-output.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 Makefiles get rebuilt when a source gyp file changes and
+--generator-output is used.
+"""
+
+import TestGyp
+
+# Regenerating build files when a gyp file changes is currently only supported
+# by the make and Android generators, and --generator-output is not supported
+# by Android and ninja, so we can only test for make.
+test = TestGyp.TestGyp(formats=['make'])
+
+CHDIR='generator-output'
+
+test.run_gyp('hello.gyp', '--generator-output=%s' % CHDIR)
+
+test.build('hello.gyp', test.ALL, chdir=CHDIR)
+
+test.run_built_executable('hello', stdout="Hello, world!\n", chdir=CHDIR)
+
+# Sleep so that the changed gyp file will have a newer timestamp than the
+# previously generated build files.
+test.sleep()
+test.write('hello.gyp', test.read('hello2.gyp'))
+
+test.build('hello.gyp', test.ALL, chdir=CHDIR)
+
+test.run_built_executable('hello', stdout="Hello, two!\n", chdir=CHDIR)
+
+test.pass_test()
diff --git a/tools/gyp/test/hello/gyptest-regyp.py b/tools/gyp/test/hello/gyptest-regyp.py
new file mode 100755
index 00000000000000..b513edcd07cda6
--- /dev/null
+++ b/tools/gyp/test/hello/gyptest-regyp.py
@@ -0,0 +1,32 @@
+#!/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 Makefiles get rebuilt when a source gyp file changes.
+"""
+
+import TestGyp
+
+# Regenerating build files when a gyp file changes is currently only supported
+# by the make generator.
+test = TestGyp.TestGyp(formats=['make'])
+
+test.run_gyp('hello.gyp')
+
+test.build('hello.gyp', test.ALL)
+
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+# Sleep so that the changed gyp file will have a newer timestamp than the
+# previously generated build files.
+test.sleep()
+test.write('hello.gyp', test.read('hello2.gyp'))
+
+test.build('hello.gyp', test.ALL)
+
+test.run_built_executable('hello', stdout="Hello, two!\n")
+
+test.pass_test()
diff --git a/tools/gyp/test/hello/gyptest-target.py b/tools/gyp/test/hello/gyptest-target.py
new file mode 100755
index 00000000000000..1abaf7057bf8ff
--- /dev/null
+++ b/tools/gyp/test/hello/gyptest-target.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 simplest-possible build of a "Hello, world!" program
+using an explicit build target of 'hello'.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp(workdir='workarea_target')
+
+test.run_gyp('hello.gyp')
+
+test.build('hello.gyp', 'hello')
+
+test.run_built_executable('hello', stdout="Hello, world!\n")
+
+test.up_to_date('hello.gyp', 'hello')
+
+test.pass_test()
diff --git a/tools/gyp/test/hello/hello.c b/tools/gyp/test/hello/hello.c
new file mode 100644
index 00000000000000..0a4c806019e864
--- /dev/null
+++ b/tools/gyp/test/hello/hello.c
@@ -0,0 +1,11 @@
+/* Copyright (c) 2009 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
+
+int main(void)
+{
+ printf("Hello, world!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/hello/hello.gyp b/tools/gyp/test/hello/hello.gyp
new file mode 100644
index 00000000000000..1974d51ccd1934
--- /dev/null
+++ b/tools/gyp/test/hello/hello.gyp
@@ -0,0 +1,15 @@
+# Copyright (c) 2009 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'hello',
+ 'type': 'executable',
+ 'sources': [
+ 'hello.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/hello/hello2.c b/tools/gyp/test/hello/hello2.c
new file mode 100644
index 00000000000000..b14299cae0d458
--- /dev/null
+++ b/tools/gyp/test/hello/hello2.c
@@ -0,0 +1,11 @@
+/* Copyright (c) 2009 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
+
+int main(void)
+{
+ printf("Hello, two!\n");
+ return 0;
+}
diff --git a/tools/gyp/test/hello/hello2.gyp b/tools/gyp/test/hello/hello2.gyp
new file mode 100644
index 00000000000000..25b08caf3cad0e
--- /dev/null
+++ b/tools/gyp/test/hello/hello2.gyp
@@ -0,0 +1,15 @@
+# Copyright (c) 2009 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': [
+ 'hello2.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes-config-arg.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-config-arg.py
new file mode 100755
index 00000000000000..82e39f9d073a41
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-config-arg.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 inclusion of $HOME/.gyp/include.gypi works when --config-dir is
+specified.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+os.environ['HOME'] = os.path.abspath('home2')
+
+test.run_gyp('all.gyp', '--config-dir=~/.gyp_new', chdir='src')
+
+# After relocating, we should still be able to build (build file shouldn't
+# contain relative reference to ~/.gyp/include.gypi)
+test.relocate('src', 'relocate/src')
+
+test.build('all.gyp', test.ALL, chdir='relocate/src')
+
+test.run_built_executable('printfoo',
+ chdir='relocate/src',
+ stdout='FOO is fromhome3\n')
+
+test.pass_test()
diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes-config-env.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-config-env.py
new file mode 100755
index 00000000000000..6f4b299ede4c74
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-config-env.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 inclusion of $HOME/.gyp_new/include.gypi works when GYP_CONFIG_DIR
+is set.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+os.environ['HOME'] = os.path.abspath('home')
+os.environ['GYP_CONFIG_DIR'] = os.path.join(os.path.abspath('home2'),
+ '.gyp_new')
+
+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_new/include.gypi)
+test.relocate('src', 'relocate/src')
+
+test.build('all.gyp', test.ALL, chdir='relocate/src')
+
+test.run_built_executable('printfoo',
+ chdir='relocate/src',
+ stdout='FOO is fromhome3\n')
+
+test.pass_test()
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
new file mode 100755
index 00000000000000..fdf8b144648380
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes-regyp.py
@@ -0,0 +1,44 @@
+#!/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 inclusion of $HOME/.gyp/include.gypi works properly with relocation
+and with regeneration.
+"""
+
+import os
+import TestGyp
+
+# Regenerating build files when a gyp file changes is currently only supported
+# by the make generator.
+test = TestGyp.TestGyp(formats=['make'])
+
+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/include.gypi)
+test.relocate('src', 'relocate/src')
+
+test.build('all.gyp', test.ALL, chdir='relocate/src')
+
+test.run_built_executable('printfoo',
+ chdir='relocate/src',
+ stdout='FOO is fromhome\n')
+
+# 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'))
+
+test.build('all.gyp', test.ALL, chdir='relocate/src')
+
+test.run_built_executable('printfoo',
+ chdir='relocate/src',
+ stdout='FOO is fromhome2\n')
+
+test.pass_test()
diff --git a/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py b/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py
new file mode 100755
index 00000000000000..8ad52556becc0f
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/gyptest-home-includes.py
@@ -0,0 +1,30 @@
+#!/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.
+
+"""
+Verifies inclusion of $HOME/.gyp/include.gypi works.
+"""
+
+import os
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+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/include.gypi)
+test.relocate('src', 'relocate/src')
+
+test.build('all.gyp', test.ALL, chdir='relocate/src')
+
+test.run_built_executable('printfoo',
+ chdir='relocate/src',
+ stdout='FOO is fromhome\n')
+
+test.pass_test()
diff --git a/tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi b/tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi
new file mode 100644
index 00000000000000..fcfb39befd841c
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/home/.gyp/include.gypi
@@ -0,0 +1,5 @@
+{
+ 'variables': {
+ 'foo': '"fromhome"',
+ },
+}
diff --git a/tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi b/tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi
new file mode 100644
index 00000000000000..f0d84b31ad50a4
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/home2/.gyp/include.gypi
@@ -0,0 +1,5 @@
+{
+ 'variables': {
+ 'foo': '"fromhome2"',
+ },
+}
diff --git a/tools/gyp/test/home_dot_gyp/home2/.gyp_new/include.gypi b/tools/gyp/test/home_dot_gyp/home2/.gyp_new/include.gypi
new file mode 100644
index 00000000000000..4094dfd2f8f946
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/home2/.gyp_new/include.gypi
@@ -0,0 +1,5 @@
+{
+ 'variables': {
+ 'foo': '"fromhome3"',
+ },
+}
diff --git a/tools/gyp/test/home_dot_gyp/src/all.gyp b/tools/gyp/test/home_dot_gyp/src/all.gyp
new file mode 100644
index 00000000000000..09d4e11cdced52
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/src/all.gyp
@@ -0,0 +1,21 @@
+# Copyright (c) 2009 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.
+
+{
+ 'variables': {
+ 'foo%': '"fromdefault"',
+ },
+ 'targets': [
+ {
+ 'target_name': 'printfoo',
+ 'type': 'executable',
+ 'sources': [
+ 'printfoo.c',
+ ],
+ 'defines': [
+ 'FOO=<(foo)',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/home_dot_gyp/src/printfoo.c b/tools/gyp/test/home_dot_gyp/src/printfoo.c
new file mode 100644
index 00000000000000..9bb67181b99a08
--- /dev/null
+++ b/tools/gyp/test/home_dot_gyp/src/printfoo.c
@@ -0,0 +1,7 @@
+#include
+
+int main(void)
+{
+ printf("FOO is %s\n", FOO);
+ return 0;
+}
diff --git a/tools/gyp/test/include_dirs/gyptest-all.py b/tools/gyp/test/include_dirs/gyptest-all.py
new file mode 100755
index 00000000000000..d64bc6a9cadb9f
--- /dev/null
+++ b/tools/gyp/test/include_dirs/gyptest-all.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 use of include_dirs when using an explicit build target of 'all'.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('includes.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+test.build('includes.gyp', test.ALL, chdir='relocate/src')
+
+expect = """\
+Hello from includes.c
+Hello from inc.h
+Hello from include1.h
+Hello from subdir/inc2/include2.h
+Hello from shadow2/shadow.h
+"""
+test.run_built_executable('includes', stdout=expect, chdir='relocate/src')
+
+if test.format == 'xcode':
+ chdir='relocate/src/subdir'
+else:
+ chdir='relocate/src'
+
+expect = """\
+Hello from subdir/subdir_includes.c
+Hello from subdir/inc.h
+Hello from include1.h
+Hello from subdir/inc2/include2.h
+"""
+test.run_built_executable('subdir_includes', stdout=expect, chdir=chdir)
+
+test.pass_test()
diff --git a/tools/gyp/test/include_dirs/gyptest-default.py b/tools/gyp/test/include_dirs/gyptest-default.py
new file mode 100755
index 00000000000000..fc6141587e10be
--- /dev/null
+++ b/tools/gyp/test/include_dirs/gyptest-default.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 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 use of include_dirs when using the default build target.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('includes.gyp', chdir='src')
+
+test.relocate('src', 'relocate/src')
+
+test.build('includes.gyp', test.ALL, chdir='relocate/src')
+
+expect = """\
+Hello from includes.c
+Hello from inc.h
+Hello from include1.h
+Hello from subdir/inc2/include2.h
+Hello from shadow2/shadow.h
+"""
+test.run_built_executable('includes', stdout=expect, chdir='relocate/src')
+
+if test.format == 'xcode':
+ chdir='relocate/src/subdir'
+else:
+ chdir='relocate/src'
+
+expect = """\
+Hello from subdir/subdir_includes.c
+Hello from subdir/inc.h
+Hello from include1.h
+Hello from subdir/inc2/include2.h
+"""
+test.run_built_executable('subdir_includes', stdout=expect, chdir=chdir)
+
+test.pass_test()
diff --git a/tools/gyp/test/include_dirs/src/inc.h b/tools/gyp/test/include_dirs/src/inc.h
new file mode 100644
index 00000000000000..0398d6915f9a72
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/inc.h
@@ -0,0 +1 @@
+#define INC_STRING "inc.h"
diff --git a/tools/gyp/test/include_dirs/src/inc1/include1.h b/tools/gyp/test/include_dirs/src/inc1/include1.h
new file mode 100644
index 00000000000000..43356b5f47a57c
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/inc1/include1.h
@@ -0,0 +1 @@
+#define INCLUDE1_STRING "include1.h"
diff --git a/tools/gyp/test/include_dirs/src/includes.c b/tools/gyp/test/include_dirs/src/includes.c
new file mode 100644
index 00000000000000..6e2a23cdffcd1d
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/includes.c
@@ -0,0 +1,19 @@
+#include
+
+#include "inc.h"
+#include "include1.h"
+#include "include2.h"
+#include "shadow.h"
+
+int main(void)
+{
+ printf("Hello from includes.c\n");
+ printf("Hello from %s\n", INC_STRING);
+ printf("Hello from %s\n", INCLUDE1_STRING);
+ printf("Hello from %s\n", INCLUDE2_STRING);
+ /* Test that include_dirs happen first: The gyp file has a -Ishadow1
+ cflag and an include_dir of shadow2. Including shadow.h should get
+ the shadow.h from the include_dir. */
+ printf("Hello from %s\n", SHADOW_STRING);
+ return 0;
+}
diff --git a/tools/gyp/test/include_dirs/src/includes.gyp b/tools/gyp/test/include_dirs/src/includes.gyp
new file mode 100644
index 00000000000000..3592690208aad2
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/includes.gyp
@@ -0,0 +1,27 @@
+# Copyright (c) 2009 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': 'includes',
+ 'type': 'executable',
+ 'dependencies': [
+ 'subdir/subdir_includes.gyp:subdir_includes',
+ ],
+ 'cflags': [
+ '-Ishadow1',
+ ],
+ 'include_dirs': [
+ '.',
+ 'inc1',
+ 'shadow2',
+ 'subdir/inc2',
+ ],
+ 'sources': [
+ 'includes.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/include_dirs/src/shadow1/shadow.h b/tools/gyp/test/include_dirs/src/shadow1/shadow.h
new file mode 100644
index 00000000000000..80f6de20b86c75
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/shadow1/shadow.h
@@ -0,0 +1 @@
+#define SHADOW_STRING "shadow1/shadow.h"
diff --git a/tools/gyp/test/include_dirs/src/shadow2/shadow.h b/tools/gyp/test/include_dirs/src/shadow2/shadow.h
new file mode 100644
index 00000000000000..fad5ccd0856154
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/shadow2/shadow.h
@@ -0,0 +1 @@
+#define SHADOW_STRING "shadow2/shadow.h"
diff --git a/tools/gyp/test/include_dirs/src/subdir/inc.h b/tools/gyp/test/include_dirs/src/subdir/inc.h
new file mode 100644
index 00000000000000..0a68d7b36ad71e
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/subdir/inc.h
@@ -0,0 +1 @@
+#define INC_STRING "subdir/inc.h"
diff --git a/tools/gyp/test/include_dirs/src/subdir/inc2/include2.h b/tools/gyp/test/include_dirs/src/subdir/inc2/include2.h
new file mode 100644
index 00000000000000..721577effbdffd
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/subdir/inc2/include2.h
@@ -0,0 +1 @@
+#define INCLUDE2_STRING "subdir/inc2/include2.h"
diff --git a/tools/gyp/test/include_dirs/src/subdir/subdir_includes.c b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.c
new file mode 100644
index 00000000000000..4623543c430bc5
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.c
@@ -0,0 +1,14 @@
+#include
+
+#include "inc.h"
+#include "include1.h"
+#include "include2.h"
+
+int main(void)
+{
+ printf("Hello from subdir/subdir_includes.c\n");
+ printf("Hello from %s\n", INC_STRING);
+ printf("Hello from %s\n", INCLUDE1_STRING);
+ printf("Hello from %s\n", INCLUDE2_STRING);
+ return 0;
+}
diff --git a/tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp
new file mode 100644
index 00000000000000..257d052c3c920c
--- /dev/null
+++ b/tools/gyp/test/include_dirs/src/subdir/subdir_includes.gyp
@@ -0,0 +1,20 @@
+# Copyright (c) 2009 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': 'subdir_includes',
+ 'type': 'executable',
+ 'include_dirs': [
+ '.',
+ '../inc1',
+ 'inc2',
+ ],
+ 'sources': [
+ 'subdir_includes.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py b/tools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py
new file mode 100755
index 00000000000000..bf4b91a2fcd937
--- /dev/null
+++ b/tools/gyp/test/intermediate_dir/gyptest-intermediate-dir.py
@@ -0,0 +1,44 @@
+#!/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 targets have independent INTERMEDIATE_DIRs.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+test.run_gyp('test.gyp', chdir='src')
+
+test.build('test.gyp', 'target1', chdir='src')
+# Check stuff exists.
+intermediate_file1 = test.read('src/outfile.txt')
+test.must_contain(intermediate_file1, 'target1')
+
+shared_intermediate_file1 = test.read('src/shared_outfile.txt')
+test.must_contain(shared_intermediate_file1, 'shared_target1')
+
+test.run_gyp('test2.gyp', chdir='src')
+
+# Force the shared intermediate to be rebuilt.
+test.sleep()
+test.touch('src/shared_infile.txt')
+test.build('test2.gyp', 'target2', chdir='src')
+# Check INTERMEDIATE_DIR file didn't get overwritten but SHARED_INTERMEDIATE_DIR
+# file did.
+intermediate_file2 = test.read('src/outfile.txt')
+test.must_contain(intermediate_file1, 'target1')
+test.must_contain(intermediate_file2, 'target2')
+
+shared_intermediate_file2 = test.read('src/shared_outfile.txt')
+if shared_intermediate_file1 != shared_intermediate_file2:
+ test.fail_test(shared_intermediate_file1 + ' != ' + shared_intermediate_file2)
+
+test.must_contain(shared_intermediate_file1, 'shared_target2')
+test.must_contain(shared_intermediate_file2, 'shared_target2')
+
+test.pass_test()
diff --git a/tools/gyp/test/intermediate_dir/src/script.py b/tools/gyp/test/intermediate_dir/src/script.py
new file mode 100755
index 00000000000000..7abc7ee14583d9
--- /dev/null
+++ b/tools/gyp/test/intermediate_dir/src/script.py
@@ -0,0 +1,24 @@
+#!/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.
+
+# Takes 3 arguments. Writes the 1st argument to the file in the 2nd argument,
+# and writes the absolute path to the file in the 2nd argument to the file in
+# the 3rd argument.
+
+import os
+import shlex
+import sys
+
+if len(sys.argv) == 3 and ' ' in sys.argv[2]:
+ sys.argv[2], fourth = shlex.split(sys.argv[2].replace('\\', '\\\\'))
+ sys.argv.append(fourth)
+
+#print >>sys.stderr, sys.argv
+
+with open(sys.argv[2], 'w') as f:
+ f.write(sys.argv[1])
+
+with open(sys.argv[3], 'w') as f:
+ f.write(os.path.abspath(sys.argv[2]))
diff --git a/tools/gyp/test/intermediate_dir/src/shared_infile.txt b/tools/gyp/test/intermediate_dir/src/shared_infile.txt
new file mode 100644
index 00000000000000..e2aba15d040934
--- /dev/null
+++ b/tools/gyp/test/intermediate_dir/src/shared_infile.txt
@@ -0,0 +1 @@
+dummy input
diff --git a/tools/gyp/test/intermediate_dir/src/test.gyp b/tools/gyp/test/intermediate_dir/src/test.gyp
new file mode 100644
index 00000000000000..b61e7e8ea58342
--- /dev/null
+++ b/tools/gyp/test/intermediate_dir/src/test.gyp
@@ -0,0 +1,42 @@
+# 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': 'target1',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'intermediate',
+ 'inputs': [],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/intermediate_out.txt',
+ 'outfile.txt',
+ ],
+ 'action': [
+ 'python', 'script.py', 'target1', '<(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'shared_intermediate',
+ 'inputs': [
+ 'shared_infile.txt',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/intermediate_out.txt',
+ 'shared_outfile.txt',
+ ],
+ 'action': [
+ 'python', 'script.py', 'shared_target1', '<(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/intermediate_dir/src/test2.gyp b/tools/gyp/test/intermediate_dir/src/test2.gyp
new file mode 100644
index 00000000000000..41f5564663e5e1
--- /dev/null
+++ b/tools/gyp/test/intermediate_dir/src/test2.gyp
@@ -0,0 +1,42 @@
+# 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': 'target2',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'intermediate',
+ 'inputs': [],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/intermediate_out.txt',
+ 'outfile.txt',
+ ],
+ 'action': [
+ 'python', 'script.py', 'target2', '<(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ {
+ 'action_name': 'shared_intermediate',
+ 'inputs': [
+ 'shared_infile.txt',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/intermediate_out.txt',
+ 'shared_outfile.txt',
+ ],
+ 'action': [
+ 'python', 'script.py', 'shared_target2', '<(_outputs)',
+ ],
+ # Allows the test to run without hermetic cygwin on windows.
+ 'msvs_cygwin_shell': 0,
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist-error.strings b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist-error.strings
new file mode 100644
index 00000000000000..452e7fabf9dc84
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist-error.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "Copyright ©2011 Google Inc."
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist.strings b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist.strings
new file mode 100644
index 00000000000000..35bd33a96e6a30
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/InfoPlist.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "Copyright ©2011 Google Inc.";
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/MainMenu.xib b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/MainMenu.xib
new file mode 100644
index 00000000000000..4524596787a8bd
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/MainMenu.xib
@@ -0,0 +1,4119 @@
+
+
+
+ 1060
+ 10A324
+ 719
+ 1015
+ 418.00
+
+
+
+
+
+ YES
+
+ NSApplication
+
+
+ FirstResponder
+
+
+ NSApplication
+
+
+
+ 15
+ 2
+ {{335, 390}, {480, 360}}
+ 1954021376
+ TestApp
+ NSWindow
+
+ {1.79769e+308, 1.79769e+308}
+
+
+ 256
+ {480, 360}
+
+
+ {{0, 0}, {1920, 1178}}
+ {1.79769e+308, 1.79769e+308}
+
+
+ TestAppAppDelegate
+
+
+ NSFontManager
+
+
+
+
+ YES
+
+
+ performMiniaturize:
+
+
+
+ 37
+
+
+
+ arrangeInFront:
+
+
+
+ 39
+
+
+
+ print:
+
+
+
+ 86
+
+
+
+ runPageLayout:
+
+
+
+ 87
+
+
+
+ clearRecentDocuments:
+
+
+
+ 127
+
+
+
+ orderFrontStandardAboutPanel:
+
+
+
+ 142
+
+
+
+ performClose:
+
+
+
+ 193
+
+
+
+ toggleContinuousSpellChecking:
+
+
+
+ 222
+
+
+
+ undo:
+
+
+
+ 223
+
+
+
+ copy:
+
+
+
+ 224
+
+
+
+ checkSpelling:
+
+
+
+ 225
+
+
+
+ paste:
+
+
+
+ 226
+
+
+
+ stopSpeaking:
+
+
+
+ 227
+
+
+
+ cut:
+
+
+
+ 228
+
+
+
+ showGuessPanel:
+
+
+
+ 230
+
+
+
+ redo:
+
+
+
+ 231
+
+
+
+ selectAll:
+
+
+
+ 232
+
+
+
+ startSpeaking:
+
+
+
+ 233
+
+
+
+ delete:
+
+
+
+ 235
+
+
+
+ performZoom:
+
+
+
+ 240
+
+
+
+ performFindPanelAction:
+
+
+
+ 241
+
+
+
+ centerSelectionInVisibleArea:
+
+
+
+ 245
+
+
+
+ toggleGrammarChecking:
+
+
+
+ 347
+
+
+
+ toggleSmartInsertDelete:
+
+
+
+ 355
+
+
+
+ toggleAutomaticQuoteSubstitution:
+
+
+
+ 356
+
+
+
+ toggleAutomaticLinkDetection:
+
+
+
+ 357
+
+
+
+ saveDocument:
+
+
+
+ 362
+
+
+
+ saveDocumentAs:
+
+
+
+ 363
+
+
+
+ revertDocumentToSaved:
+
+
+
+ 364
+
+
+
+ runToolbarCustomizationPalette:
+
+
+
+ 365
+
+
+
+ toggleToolbarShown:
+
+
+
+ 366
+
+
+
+ hide:
+
+
+
+ 367
+
+
+
+ hideOtherApplications:
+
+
+
+ 368
+
+
+
+ unhideAllApplications:
+
+
+
+ 370
+
+
+
+ newDocument:
+
+
+
+ 373
+
+
+
+ openDocument:
+
+
+
+ 374
+
+
+
+ addFontTrait:
+
+
+
+ 421
+
+
+
+ addFontTrait:
+
+
+
+ 422
+
+
+
+ modifyFont:
+
+
+
+ 423
+
+
+
+ orderFrontFontPanel:
+
+
+
+ 424
+
+
+
+ modifyFont:
+
+
+
+ 425
+
+
+
+ raiseBaseline:
+
+
+
+ 426
+
+
+
+ lowerBaseline:
+
+
+
+ 427
+
+
+
+ copyFont:
+
+
+
+ 428
+
+
+
+ subscript:
+
+
+
+ 429
+
+
+
+ superscript:
+
+
+
+ 430
+
+
+
+ tightenKerning:
+
+
+
+ 431
+
+
+
+ underline:
+
+
+
+ 432
+
+
+
+ orderFrontColorPanel:
+
+
+
+ 433
+
+
+
+ useAllLigatures:
+
+
+
+ 434
+
+
+
+ loosenKerning:
+
+
+
+ 435
+
+
+
+ pasteFont:
+
+
+
+ 436
+
+
+
+ unscript:
+
+
+
+ 437
+
+
+
+ useStandardKerning:
+
+
+
+ 438
+
+
+
+ useStandardLigatures:
+
+
+
+ 439
+
+
+
+ turnOffLigatures:
+
+
+
+ 440
+
+
+
+ turnOffKerning:
+
+
+
+ 441
+
+
+
+ terminate:
+
+
+
+ 449
+
+
+
+ toggleAutomaticSpellingCorrection:
+
+
+
+ 456
+
+
+
+ orderFrontSubstitutionsPanel:
+
+
+
+ 458
+
+
+
+ toggleAutomaticDashSubstitution:
+
+
+
+ 461
+
+
+
+ toggleAutomaticTextReplacement:
+
+
+
+ 463
+
+
+
+ uppercaseWord:
+
+
+
+ 464
+
+
+
+ capitalizeWord:
+
+
+
+ 467
+
+
+
+ lowercaseWord:
+
+
+
+ 468
+
+
+
+ pasteAsPlainText:
+
+
+
+ 486
+
+
+
+ performFindPanelAction:
+
+
+
+ 487
+
+
+
+ performFindPanelAction:
+
+
+
+ 488
+
+
+
+ performFindPanelAction:
+
+
+
+ 489
+
+
+
+ showHelp:
+
+
+
+ 493
+
+
+
+ delegate
+
+
+
+ 495
+
+
+
+ alignCenter:
+
+
+
+ 518
+
+
+
+ pasteRuler:
+
+
+
+ 519
+
+
+
+ toggleRuler:
+
+
+
+ 520
+
+
+
+ alignRight:
+
+
+
+ 521
+
+
+
+ copyRuler:
+
+
+
+ 522
+
+
+
+ alignJustified:
+
+
+
+ 523
+
+
+
+ alignLeft:
+
+
+
+ 524
+
+
+
+ makeBaseWritingDirectionNatural:
+
+
+
+ 525
+
+
+
+ makeBaseWritingDirectionLeftToRight:
+
+
+
+ 526
+
+
+
+ makeBaseWritingDirectionRightToLeft:
+
+
+
+ 527
+
+
+
+ makeTextWritingDirectionNatural:
+
+
+
+ 528
+
+
+
+ makeTextWritingDirectionLeftToRight:
+
+
+
+ 529
+
+
+
+ makeTextWritingDirectionRightToLeft:
+
+
+
+ 530
+
+
+
+ window
+
+
+
+ 532
+
+
+
+
+ YES
+
+ 0
+
+
+
+
+
+ -2
+
+
+ File's Owner
+
+
+ -1
+
+
+ First Responder
+
+
+ -3
+
+
+ Application
+
+
+ 29
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+ 19
+
+
+ YES
+
+
+
+
+
+ 56
+
+
+ YES
+
+
+
+
+
+ 217
+
+
+ YES
+
+
+
+
+
+ 83
+
+
+ YES
+
+
+
+
+
+ 81
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 75
+
+
+
+
+ 80
+
+
+
+
+ 78
+
+
+
+
+ 72
+
+
+
+
+ 82
+
+
+
+
+ 124
+
+
+ YES
+
+
+
+
+
+ 77
+
+
+
+
+ 73
+
+
+
+
+ 79
+
+
+
+
+ 112
+
+
+
+
+ 74
+
+
+
+
+ 125
+
+
+ YES
+
+
+
+
+
+ 126
+
+
+
+
+ 205
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 202
+
+
+
+
+ 198
+
+
+
+
+ 207
+
+
+
+
+ 214
+
+
+
+
+ 199
+
+
+
+
+ 203
+
+
+
+
+ 197
+
+
+
+
+ 206
+
+
+
+
+ 215
+
+
+
+
+ 218
+
+
+ YES
+
+
+
+
+
+ 216
+
+
+ YES
+
+
+
+
+
+ 200
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+ 219
+
+
+
+
+ 201
+
+
+
+
+ 204
+
+
+
+
+ 220
+
+
+ YES
+
+
+
+
+
+
+
+
+
+ 213
+
+
+
+
+ 210
+
+
+
+
+ 221
+
+
+
+
+ 208
+
+
+
+
+ 209
+
+
+
+
+ 57
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 58
+
+
+
+
+ 134
+
+
+
+
+ 150
+
+
+
+
+ 136
+
+
+
+
+ 144
+
+
+
+
+ 129
+
+
+
+
+ 143
+
+
+
+
+ 236
+
+
+
+
+ 131
+
+
+ YES
+
+
+
+
+
+ 149
+
+
+
+
+ 145
+
+
+
+
+ 130
+
+
+
+
+ 24
+
+
+ YES
+
+
+
+
+
+
+
+
+ 92
+
+
+
+
+ 5
+
+
+
+
+ 239
+
+
+
+
+ 23
+
+
+
+
+ 295
+
+
+ YES
+
+
+
+
+
+ 296
+
+
+ YES
+
+
+
+
+
+
+ 297
+
+
+
+
+ 298
+
+
+
+
+ 211
+
+
+ YES
+
+
+
+
+
+ 212
+
+
+ YES
+
+
+
+
+
+
+ 195
+
+
+
+
+ 196
+
+
+
+
+ 346
+
+
+
+
+ 348
+
+
+ YES
+
+
+
+
+
+ 349
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+ 350
+
+
+
+
+ 351
+
+
+
+
+ 354
+
+
+
+
+ 371
+
+
+ YES
+
+
+
+
+
+ 372
+
+
+
+
+ 375
+
+
+ YES
+
+
+
+
+
+ 376
+
+
+ YES
+
+
+
+
+
+
+ 377
+
+
+ YES
+
+
+
+
+
+ 388
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 389
+
+
+
+
+ 390
+
+
+
+
+ 391
+
+
+
+
+ 392
+
+
+
+
+ 393
+
+
+
+
+ 394
+
+
+
+
+ 395
+
+
+
+
+ 396
+
+
+
+
+ 397
+
+
+ YES
+
+
+
+
+
+ 398
+
+
+ YES
+
+
+
+
+
+ 399
+
+
+ YES
+
+
+
+
+
+ 400
+
+
+
+
+ 401
+
+
+
+
+ 402
+
+
+
+
+ 403
+
+
+
+
+ 404
+
+
+
+
+ 405
+
+
+ YES
+
+
+
+
+
+
+
+
+
+ 406
+
+
+
+
+ 407
+
+
+
+
+ 408
+
+
+
+
+ 409
+
+
+
+
+ 410
+
+
+
+
+ 411
+
+
+ YES
+
+
+
+
+
+
+
+ 412
+
+
+
+
+ 413
+
+
+
+
+ 414
+
+
+
+
+ 415
+
+
+ YES
+
+
+
+
+
+
+
+
+ 416
+
+
+
+
+ 417
+
+
+
+
+ 418
+
+
+
+
+ 419
+
+
+
+
+ 420
+
+
+
+
+ 450
+
+
+ YES
+
+
+
+
+
+ 451
+
+
+ YES
+
+
+
+
+
+
+
+ 452
+
+
+
+
+ 453
+
+
+
+
+ 454
+
+
+
+
+ 457
+
+
+
+
+ 459
+
+
+
+
+ 460
+
+
+
+
+ 462
+
+
+
+
+ 465
+
+
+
+
+ 466
+
+
+
+
+ 485
+
+
+
+
+ 490
+
+
+ YES
+
+
+
+
+
+ 491
+
+
+ YES
+
+
+
+
+
+ 492
+
+
+
+
+ 494
+
+
+
+
+ 496
+
+
+ YES
+
+
+
+
+
+ 497
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 498
+
+
+
+
+ 499
+
+
+
+
+ 500
+
+
+
+
+ 501
+
+
+
+
+ 502
+
+
+
+
+ 503
+
+
+ YES
+
+
+
+
+
+ 504
+
+
+
+
+ 505
+
+
+
+
+ 506
+
+
+
+
+ 507
+
+
+
+
+ 508
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 509
+
+
+
+
+ 510
+
+
+
+
+ 511
+
+
+
+
+ 512
+
+
+
+
+ 513
+
+
+
+
+ 514
+
+
+
+
+ 515
+
+
+
+
+ 516
+
+
+
+
+ 517
+
+
+
+
+
+
+ YES
+
+ YES
+ -3.IBPluginDependency
+ 112.IBPluginDependency
+ 112.ImportedFromIB2
+ 124.IBPluginDependency
+ 124.ImportedFromIB2
+ 125.IBPluginDependency
+ 125.ImportedFromIB2
+ 125.editorWindowContentRectSynchronizationRect
+ 126.IBPluginDependency
+ 126.ImportedFromIB2
+ 129.IBPluginDependency
+ 129.ImportedFromIB2
+ 130.IBPluginDependency
+ 130.ImportedFromIB2
+ 130.editorWindowContentRectSynchronizationRect
+ 131.IBPluginDependency
+ 131.ImportedFromIB2
+ 134.IBPluginDependency
+ 134.ImportedFromIB2
+ 136.IBPluginDependency
+ 136.ImportedFromIB2
+ 143.IBPluginDependency
+ 143.ImportedFromIB2
+ 144.IBPluginDependency
+ 144.ImportedFromIB2
+ 145.IBPluginDependency
+ 145.ImportedFromIB2
+ 149.IBPluginDependency
+ 149.ImportedFromIB2
+ 150.IBPluginDependency
+ 150.ImportedFromIB2
+ 19.IBPluginDependency
+ 19.ImportedFromIB2
+ 195.IBPluginDependency
+ 195.ImportedFromIB2
+ 196.IBPluginDependency
+ 196.ImportedFromIB2
+ 197.IBPluginDependency
+ 197.ImportedFromIB2
+ 198.IBPluginDependency
+ 198.ImportedFromIB2
+ 199.IBPluginDependency
+ 199.ImportedFromIB2
+ 200.IBEditorWindowLastContentRect
+ 200.IBPluginDependency
+ 200.ImportedFromIB2
+ 200.editorWindowContentRectSynchronizationRect
+ 201.IBPluginDependency
+ 201.ImportedFromIB2
+ 202.IBPluginDependency
+ 202.ImportedFromIB2
+ 203.IBPluginDependency
+ 203.ImportedFromIB2
+ 204.IBPluginDependency
+ 204.ImportedFromIB2
+ 205.IBEditorWindowLastContentRect
+ 205.IBPluginDependency
+ 205.ImportedFromIB2
+ 205.editorWindowContentRectSynchronizationRect
+ 206.IBPluginDependency
+ 206.ImportedFromIB2
+ 207.IBPluginDependency
+ 207.ImportedFromIB2
+ 208.IBPluginDependency
+ 208.ImportedFromIB2
+ 209.IBPluginDependency
+ 209.ImportedFromIB2
+ 210.IBPluginDependency
+ 210.ImportedFromIB2
+ 211.IBPluginDependency
+ 211.ImportedFromIB2
+ 212.IBPluginDependency
+ 212.ImportedFromIB2
+ 212.editorWindowContentRectSynchronizationRect
+ 213.IBPluginDependency
+ 213.ImportedFromIB2
+ 214.IBPluginDependency
+ 214.ImportedFromIB2
+ 215.IBPluginDependency
+ 215.ImportedFromIB2
+ 216.IBPluginDependency
+ 216.ImportedFromIB2
+ 217.IBPluginDependency
+ 217.ImportedFromIB2
+ 218.IBPluginDependency
+ 218.ImportedFromIB2
+ 219.IBPluginDependency
+ 219.ImportedFromIB2
+ 220.IBEditorWindowLastContentRect
+ 220.IBPluginDependency
+ 220.ImportedFromIB2
+ 220.editorWindowContentRectSynchronizationRect
+ 221.IBPluginDependency
+ 221.ImportedFromIB2
+ 23.IBPluginDependency
+ 23.ImportedFromIB2
+ 236.IBPluginDependency
+ 236.ImportedFromIB2
+ 239.IBPluginDependency
+ 239.ImportedFromIB2
+ 24.IBEditorWindowLastContentRect
+ 24.IBPluginDependency
+ 24.ImportedFromIB2
+ 24.editorWindowContentRectSynchronizationRect
+ 29.IBEditorWindowLastContentRect
+ 29.IBPluginDependency
+ 29.ImportedFromIB2
+ 29.WindowOrigin
+ 29.editorWindowContentRectSynchronizationRect
+ 295.IBPluginDependency
+ 296.IBEditorWindowLastContentRect
+ 296.IBPluginDependency
+ 296.editorWindowContentRectSynchronizationRect
+ 297.IBPluginDependency
+ 298.IBPluginDependency
+ 346.IBPluginDependency
+ 346.ImportedFromIB2
+ 348.IBPluginDependency
+ 348.ImportedFromIB2
+ 349.IBEditorWindowLastContentRect
+ 349.IBPluginDependency
+ 349.ImportedFromIB2
+ 349.editorWindowContentRectSynchronizationRect
+ 350.IBPluginDependency
+ 350.ImportedFromIB2
+ 351.IBPluginDependency
+ 351.ImportedFromIB2
+ 354.IBPluginDependency
+ 354.ImportedFromIB2
+ 371.IBEditorWindowLastContentRect
+ 371.IBPluginDependency
+ 371.IBWindowTemplateEditedContentRect
+ 371.NSWindowTemplate.visibleAtLaunch
+ 371.editorWindowContentRectSynchronizationRect
+ 371.windowTemplate.maxSize
+ 372.IBPluginDependency
+ 375.IBPluginDependency
+ 376.IBEditorWindowLastContentRect
+ 376.IBPluginDependency
+ 377.IBPluginDependency
+ 388.IBEditorWindowLastContentRect
+ 388.IBPluginDependency
+ 389.IBPluginDependency
+ 390.IBPluginDependency
+ 391.IBPluginDependency
+ 392.IBPluginDependency
+ 393.IBPluginDependency
+ 394.IBPluginDependency
+ 395.IBPluginDependency
+ 396.IBPluginDependency
+ 397.IBPluginDependency
+ 398.IBPluginDependency
+ 399.IBPluginDependency
+ 400.IBPluginDependency
+ 401.IBPluginDependency
+ 402.IBPluginDependency
+ 403.IBPluginDependency
+ 404.IBPluginDependency
+ 405.IBPluginDependency
+ 406.IBPluginDependency
+ 407.IBPluginDependency
+ 408.IBPluginDependency
+ 409.IBPluginDependency
+ 410.IBPluginDependency
+ 411.IBPluginDependency
+ 412.IBPluginDependency
+ 413.IBPluginDependency
+ 414.IBPluginDependency
+ 415.IBPluginDependency
+ 416.IBPluginDependency
+ 417.IBPluginDependency
+ 418.IBPluginDependency
+ 419.IBPluginDependency
+ 450.IBPluginDependency
+ 451.IBEditorWindowLastContentRect
+ 451.IBPluginDependency
+ 452.IBPluginDependency
+ 453.IBPluginDependency
+ 454.IBPluginDependency
+ 457.IBPluginDependency
+ 459.IBPluginDependency
+ 460.IBPluginDependency
+ 462.IBPluginDependency
+ 465.IBPluginDependency
+ 466.IBPluginDependency
+ 485.IBPluginDependency
+ 490.IBPluginDependency
+ 491.IBEditorWindowLastContentRect
+ 491.IBPluginDependency
+ 492.IBPluginDependency
+ 496.IBPluginDependency
+ 497.IBEditorWindowLastContentRect
+ 497.IBPluginDependency
+ 498.IBPluginDependency
+ 499.IBPluginDependency
+ 5.IBPluginDependency
+ 5.ImportedFromIB2
+ 500.IBPluginDependency
+ 501.IBPluginDependency
+ 502.IBPluginDependency
+ 503.IBPluginDependency
+ 504.IBPluginDependency
+ 505.IBPluginDependency
+ 506.IBPluginDependency
+ 507.IBPluginDependency
+ 508.IBEditorWindowLastContentRect
+ 508.IBPluginDependency
+ 509.IBPluginDependency
+ 510.IBPluginDependency
+ 511.IBPluginDependency
+ 512.IBPluginDependency
+ 513.IBPluginDependency
+ 514.IBPluginDependency
+ 515.IBPluginDependency
+ 516.IBPluginDependency
+ 517.IBPluginDependency
+ 56.IBPluginDependency
+ 56.ImportedFromIB2
+ 57.IBEditorWindowLastContentRect
+ 57.IBPluginDependency
+ 57.ImportedFromIB2
+ 57.editorWindowContentRectSynchronizationRect
+ 58.IBPluginDependency
+ 58.ImportedFromIB2
+ 72.IBPluginDependency
+ 72.ImportedFromIB2
+ 73.IBPluginDependency
+ 73.ImportedFromIB2
+ 74.IBPluginDependency
+ 74.ImportedFromIB2
+ 75.IBPluginDependency
+ 75.ImportedFromIB2
+ 77.IBPluginDependency
+ 77.ImportedFromIB2
+ 78.IBPluginDependency
+ 78.ImportedFromIB2
+ 79.IBPluginDependency
+ 79.ImportedFromIB2
+ 80.IBPluginDependency
+ 80.ImportedFromIB2
+ 81.IBEditorWindowLastContentRect
+ 81.IBPluginDependency
+ 81.ImportedFromIB2
+ 81.editorWindowContentRectSynchronizationRect
+ 82.IBPluginDependency
+ 82.ImportedFromIB2
+ 83.IBPluginDependency
+ 83.ImportedFromIB2
+ 92.IBPluginDependency
+ 92.ImportedFromIB2
+
+
+ YES
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{522, 812}, {146, 23}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{436, 809}, {64, 6}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{753, 187}, {275, 113}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {275, 83}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{547, 180}, {254, 283}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{187, 434}, {243, 243}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {167, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{753, 217}, {238, 103}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {241, 103}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{654, 239}, {194, 73}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{525, 802}, {197, 73}}
+ {{380, 836}, {512, 20}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {74, 862}
+ {{6, 978}, {478, 20}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{604, 269}, {231, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{475, 832}, {234, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{746, 287}, {220, 133}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {215, 63}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{380, 496}, {480, 360}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{380, 496}, {480, 360}}
+
+ {{33, 99}, {480, 360}}
+ {3.40282e+38, 3.40282e+38}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{591, 420}, {83, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{523, 2}, {178, 283}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{753, 197}, {170, 63}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{725, 289}, {246, 23}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{674, 260}, {204, 183}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{878, 180}, {164, 173}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{286, 129}, {275, 183}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{23, 794}, {245, 183}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{452, 109}, {196, 203}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{145, 474}, {199, 203}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+
+
+
+ YES
+
+
+ YES
+
+
+
+
+ YES
+
+
+ YES
+
+
+
+ 532
+
+
+
+ YES
+
+ TestAppAppDelegate
+ NSObject
+
+ window
+ NSWindow
+
+
+ IBProjectSource
+ TestAppAppDelegate.h
+
+
+
+
+ YES
+
+ NSApplication
+ NSResponder
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSApplication.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSApplicationScripting.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSColorPanel.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSHelpManager.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSPageLayout.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSUserInterfaceItemSearching.h
+
+
+
+ NSBrowser
+ NSControl
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSBrowser.h
+
+
+
+ NSControl
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSControl.h
+
+
+
+ NSDocument
+ NSObject
+
+ YES
+
+ YES
+ printDocument:
+ revertDocumentToSaved:
+ runPageLayout:
+ saveDocument:
+ saveDocumentAs:
+ saveDocumentTo:
+
+
+ YES
+ id
+ id
+ id
+ id
+ id
+ id
+
+
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDocument.h
+
+
+
+ NSDocument
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDocumentScripting.h
+
+
+
+ NSDocumentController
+ NSObject
+
+ YES
+
+ YES
+ clearRecentDocuments:
+ newDocument:
+ openDocument:
+ saveAllDocuments:
+
+
+ YES
+ id
+ id
+ id
+ id
+
+
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDocumentController.h
+
+
+
+ NSFontManager
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSFontManager.h
+
+
+
+ NSFormatter
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSFormatter.h
+
+
+
+ NSMatrix
+ NSControl
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMatrix.h
+
+
+
+ NSMenu
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMenu.h
+
+
+
+ NSMenuItem
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMenuItem.h
+
+
+
+ NSMovieView
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMovieView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSAccessibility.h
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDictionaryController.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDragging.h
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSFontPanel.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSKeyValueBinding.h
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSNibLoading.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSOutlineView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSPasteboard.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSSavePanel.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSTableView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSToolbarItem.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSArchiver.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSClassDescription.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSError.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSFileManager.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSKeyValueCoding.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSKeyValueObserving.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSKeyedArchiver.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSObject.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSObjectScripting.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSPortCoder.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSRunLoop.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptClassDescription.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptKeyValueCoding.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptObjectSpecifiers.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptWhoseTests.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSThread.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSURL.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSURLConnection.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSURLDownload.h
+
+
+
+ NSResponder
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSInterfaceStyle.h
+
+
+
+ NSResponder
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSResponder.h
+
+
+
+ NSTableView
+ NSControl
+
+
+
+ NSText
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSText.h
+
+
+
+ NSTextView
+ NSText
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSTextView.h
+
+
+
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSClipView.h
+
+
+
+ NSView
+
+
+
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSRulerView.h
+
+
+
+ NSView
+ NSResponder
+
+
+
+ NSWindow
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDrawer.h
+
+
+
+ NSWindow
+ NSResponder
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSWindow.h
+
+
+
+ NSWindow
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSWindowScripting.h
+
+
+
+
+ 0
+
+ com.apple.InterfaceBuilder.CocoaPlugin.macosx
+
+
+
+ com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
+
+
+ YES
+ ../TestApp.xcodeproj
+ 3
+
+
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/Main_iPhone.storyboard b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/Main_iPhone.storyboard
new file mode 100644
index 00000000000000..723bc851227aac
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/English.lproj/Main_iPhone.storyboard
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/AppIcon.appiconset/Contents.json b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..2db2b1c7c6c316
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,58 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/Contents.json b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/Contents.json
new file mode 100644
index 00000000000000..0a87b6edc67517
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "super_sylvain.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "super_sylvain@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "super_sylvain@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain.png b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain.png
new file mode 100644
index 00000000000000..0ba769182f117a
Binary files /dev/null and b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain.png differ
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@2x.png b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@2x.png
new file mode 100644
index 00000000000000..edfa6a5682f108
Binary files /dev/null and b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@2x.png differ
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@3x.png b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@3x.png
new file mode 100644
index 00000000000000..e0652efc729dcf
Binary files /dev/null and b/tools/gyp/test/ios/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@3x.png differ
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/TestApp-Info.plist b/tools/gyp/test/ios/app-bundle/TestApp/TestApp-Info.plist
new file mode 100644
index 00000000000000..8cb142e9f5aa52
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/TestApp-Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.google.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ause
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ ${MACOSX_DEPLOYMENT_TARGET}
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/check_no_signature.py b/tools/gyp/test/ios/app-bundle/TestApp/check_no_signature.py
new file mode 100644
index 00000000000000..4f6e340072130b
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/check_no_signature.py
@@ -0,0 +1,13 @@
+#!/usr/bin/python
+
+import os
+import subprocess
+import sys
+
+p = os.path.join(os.environ['BUILT_PRODUCTS_DIR'],os.environ['EXECUTABLE_PATH'])
+proc = subprocess.Popen(['codesign', '-v', p],
+ stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+o = proc.communicate()[0].strip()
+if "code object is not signed at all" not in o:
+ sys.stderr.write('File should not already be signed.')
+ sys.exit(1)
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/main.m b/tools/gyp/test/ios/app-bundle/TestApp/main.m
new file mode 100644
index 00000000000000..ec93e0e237a18f
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/main.m
@@ -0,0 +1,13 @@
+// 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.
+
+#import
+
+int main(int argc, char *argv[])
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ int retVal = UIApplicationMain(argc, argv, nil, nil);
+ [pool release];
+ return retVal;
+}
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/only-compile-in-32-bits.m b/tools/gyp/test/ios/app-bundle/TestApp/only-compile-in-32-bits.m
new file mode 100644
index 00000000000000..28bb1177881606
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/only-compile-in-32-bits.m
@@ -0,0 +1,7 @@
+// Copyright (c) 2013 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.
+
+#if defined(__LP64__)
+# error 64-bit build
+#endif
diff --git a/tools/gyp/test/ios/app-bundle/TestApp/only-compile-in-64-bits.m b/tools/gyp/test/ios/app-bundle/TestApp/only-compile-in-64-bits.m
new file mode 100644
index 00000000000000..e6d255841875eb
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/TestApp/only-compile-in-64-bits.m
@@ -0,0 +1,7 @@
+// Copyright (c) 2013 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.
+
+#if !defined(__LP64__)
+# error 32-bit build
+#endif
diff --git a/tools/gyp/test/ios/app-bundle/test-archs.gyp b/tools/gyp/test/ios/app-bundle/test-archs.gyp
new file mode 100644
index 00000000000000..b1558c94becc80
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/test-archs.gyp
@@ -0,0 +1,110 @@
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ],
+ 'target_defaults': {
+ 'product_extension': 'bundle',
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'CODE_SIGNING_REQUIRED': 'NO',
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ 'CONFIGURATION_BUILD_DIR':'build/Default',
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'TestNoArchs',
+ 'product_name': 'TestNoArchs',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ 'TestApp/only-compile-in-32-bits.m',
+ ],
+ 'xcode_settings': {
+ 'VALID_ARCHS': [
+ 'i386',
+ 'x86_64',
+ 'arm64',
+ 'armv7',
+ ],
+ }
+ },
+ {
+ 'target_name': 'TestArch32Bits',
+ 'product_name': 'TestArch32Bits',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ 'TestApp/only-compile-in-32-bits.m',
+ ],
+ 'xcode_settings': {
+ 'ARCHS': [
+ '$(ARCHS_STANDARD)',
+ ],
+ 'VALID_ARCHS': [
+ 'i386',
+ 'armv7',
+ ],
+ },
+ },
+ {
+ 'target_name': 'TestArch64Bits',
+ 'product_name': 'TestArch64Bits',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ 'TestApp/only-compile-in-64-bits.m',
+ ],
+ 'xcode_settings': {
+ 'ARCHS': [
+ '$(ARCHS_STANDARD_INCLUDING_64_BIT)',
+ ],
+ 'VALID_ARCHS': [
+ 'x86_64',
+ 'arm64',
+ ],
+ },
+ },
+ {
+ 'target_name': 'TestMultiArchs',
+ 'product_name': 'TestMultiArchs',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ ],
+ 'xcode_settings': {
+ 'ARCHS': [
+ '$(ARCHS_STANDARD_INCLUDING_64_BIT)',
+ ],
+ 'VALID_ARCHS': [
+ 'x86_64',
+ 'i386',
+ 'arm64',
+ 'armv7',
+ ],
+ }
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/app-bundle/test-assets-catalog.gyp b/tools/gyp/test/ios/app-bundle/test-assets-catalog.gyp
new file mode 100644
index 00000000000000..9a12d07af7be9e
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/test-assets-catalog.gyp
@@ -0,0 +1,45 @@
+# 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.
+{
+ 'conditions': [
+ ['"<(GENERATOR)"=="ninja"', {
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ }],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App Assets Catalog Gyp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ 'TestApp/English.lproj/Main_iPhone.storyboard',
+ 'TestApp/Images.xcassets',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ 'CONFIGURATION_BUILD_DIR':'build/Default',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/app-bundle/test-crosscompile.gyp b/tools/gyp/test/ios/app-bundle/test-crosscompile.gyp
new file mode 100644
index 00000000000000..d9049588ba3ec9
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/test-crosscompile.gyp
@@ -0,0 +1,47 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ],
+ 'targets': [
+ # This target will not be built, but is here so that ninja Xcode emulation
+ # understand this is a multi-platform (ios + mac) build.
+ {
+ 'target_name': 'TestDummy',
+ 'product_name': 'TestDummy',
+ 'toolsets': ['target'],
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'tool_main.cc',
+ ],
+ 'xcode_settings': {
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ },
+ },
+ {
+ 'target_name': 'TestHost',
+ 'product_name': 'TestHost',
+ 'toolsets': ['host'],
+ 'type': 'executable',
+ 'mac_bundle': 0,
+ 'sources': [
+ 'tool_main.cc',
+ ],
+ 'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'ARCHS': [
+ '$(ARCHS_STANDARD)',
+ 'x86_64',
+ ],
+ 'VALID_ARCHS': [
+ 'x86_64',
+ ],
+ }
+ }
+ ],
+}
diff --git a/tools/gyp/test/ios/app-bundle/test-device.gyp b/tools/gyp/test/ios/app-bundle/test-device.gyp
new file mode 100644
index 00000000000000..28cdbb3af58af3
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/test-device.gyp
@@ -0,0 +1,79 @@
+# 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.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App Gyp',
+ 'type': 'executable',
+ 'product_extension': 'bundle',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '4.2',
+ 'CONFIGURATION_BUILD_DIR':'build/Default',
+ },
+ },
+ {
+ 'target_name': 'sig_test',
+ 'product_name': 'sig_test',
+ 'type': 'executable',
+ 'product_extension': 'bundle',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Verify no signature',
+ 'action': [
+ 'python',
+ 'TestApp/check_no_signature.py'
+ ],
+ },
+ ],
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer',
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '4.2',
+ 'CONFIGURATION_BUILD_DIR':'buildsig/Default',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/app-bundle/test.gyp b/tools/gyp/test/ios/app-bundle/test.gyp
new file mode 100644
index 00000000000000..b60474df8a38b3
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/test.gyp
@@ -0,0 +1,74 @@
+# 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.
+{
+ 'conditions': [
+ ['"<(GENERATOR)"=="ninja"', {
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ }],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App Gyp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ 'TestApp/English.lproj/Main_iPhone.storyboard',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'IPHONEOS_DEPLOYMENT_TARGET': '5.0',
+ 'CONFIGURATION_BUILD_DIR':'build/Default',
+ },
+ },
+ {
+ 'target_name': 'test_app_xml',
+ 'product_name': 'Test App Gyp XML',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ 'TestApp/English.lproj/Main_iPhone.storyboard',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'INFOPLIST_OUTPUT_FORMAT':'xml',
+ 'SDKROOT': 'iphonesimulator', # -isysroot
+ 'IPHONEOS_DEPLOYMENT_TARGET': '5.0',
+ 'CONFIGURATION_BUILD_DIR':'build/Default',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/app-bundle/tool_main.cc b/tools/gyp/test/ios/app-bundle/tool_main.cc
new file mode 100644
index 00000000000000..9dc3c94f3431be
--- /dev/null
+++ b/tools/gyp/test/ios/app-bundle/tool_main.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+int main() {
+ return 0;
+}
diff --git a/tools/gyp/test/ios/deployment-target/check-version-min.c b/tools/gyp/test/ios/deployment-target/check-version-min.c
new file mode 100644
index 00000000000000..6b05a27f0aea0d
--- /dev/null
+++ b/tools/gyp/test/ios/deployment-target/check-version-min.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013 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
+
+/* GYPTEST_MAC_VERSION_MIN: should be set to the corresponding value of
+ * xcode setting 'MACOSX_DEPLOYMENT_TARGET', otherwise both should be
+ * left undefined.
+ *
+ * GYPTEST_IOS_VERSION_MIN: should be set to the corresponding value of
+ * xcode setting 'IPHONEOS_DEPLOYMENT_TARGET', otherwise both should be
+ * left undefined.
+ */
+
+#if defined(GYPTEST_MAC_VERSION_MIN)
+# if GYPTEST_MAC_VERSION_MIN != __MAC_OS_X_VERSION_MIN_REQUIRED
+# error __MAC_OS_X_VERSION_MIN_REQUIRED has wrong value
+# endif
+#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+# error __MAC_OS_X_VERSION_MIN_REQUIRED should be undefined
+#endif
+
+#if defined(GYPTEST_IOS_VERSION_MIN)
+# if GYPTEST_IOS_VERSION_MIN != __IPHONE_OS_VERSION_MIN_REQUIRED
+# error __IPHONE_OS_VERSION_MIN_REQUIRED has wrong value
+# endif
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+# error __IPHONE_OS_VERSION_MIN_REQUIRED should be undefined
+#endif
+
+int main() { return 0; }
diff --git a/tools/gyp/test/ios/deployment-target/deployment-target.gyp b/tools/gyp/test/ios/deployment-target/deployment-target.gyp
new file mode 100644
index 00000000000000..53cfaa38d1a8ca
--- /dev/null
+++ b/tools/gyp/test/ios/deployment-target/deployment-target.gyp
@@ -0,0 +1,57 @@
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'iphoneos-version-min-4.3',
+ 'type': 'static_library',
+ 'sources': [ 'check-version-min.c', ],
+ 'defines': [ 'GYPTEST_IOS_VERSION_MIN=40300', ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'ARCHS': [ 'armv7' ],
+ 'SDKROOT': 'iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '4.3',
+ },
+ },
+ {
+ 'target_name': 'iphoneos-version-min-5.0',
+ 'type': 'static_library',
+ 'sources': [ 'check-version-min.c', ],
+ 'defines': [ 'GYPTEST_IOS_VERSION_MIN=50000', ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'ARCHS': [ 'armv7' ],
+ 'SDKROOT': 'iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '5.0',
+ },
+ },
+ {
+ 'target_name': 'iphonesimulator-version-min-4.3',
+ 'type': 'static_library',
+ 'sources': [ 'check-version-min.c', ],
+ 'defines': [ 'GYPTEST_IOS_VERSION_MIN=40300', ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'SDKROOT': 'iphonesimulator',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '4.3',
+ },
+ },
+ {
+ 'target_name': 'iphonesimulator-version-min-5.0',
+ 'type': 'static_library',
+ 'sources': [ 'check-version-min.c', ],
+ 'defines': [ 'GYPTEST_IOS_VERSION_MIN=50000', ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'SDKROOT': 'iphonesimulator',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '5.0',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/extension/ActionExtension/ActionViewController.h b/tools/gyp/test/ios/extension/ActionExtension/ActionViewController.h
new file mode 100644
index 00000000000000..1c925090296183
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ActionExtension/ActionViewController.h
@@ -0,0 +1,9 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import
+
+@interface ActionViewController : UIViewController
+
+@end
diff --git a/tools/gyp/test/ios/extension/ActionExtension/ActionViewController.m b/tools/gyp/test/ios/extension/ActionExtension/ActionViewController.m
new file mode 100644
index 00000000000000..d37bacdae1764c
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ActionExtension/ActionViewController.m
@@ -0,0 +1,31 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ActionViewController.h"
+#import
+
+@interface ActionViewController ()
+
+@end
+
+@implementation ActionViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+- (IBAction)done {
+ // Return any edited content to the host app.
+ // This template doesn't do anything, so we just echo the passed in items.
+ [self.extensionContext
+ completeRequestReturningItems:self.extensionContext.inputItems
+ completionHandler:nil];
+}
+
+@end
diff --git a/tools/gyp/test/ios/extension/ActionExtension/Info.plist b/tools/gyp/test/ios/extension/ActionExtension/Info.plist
new file mode 100644
index 00000000000000..f89cd790bc23b1
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ActionExtension/Info.plist
@@ -0,0 +1,42 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ActionExtension
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIdentifier
+ com.google.gyptest.extension.ActionExtension
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ XPC!
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ NSExtension
+
+ NSExtensionAttributes
+
+ NSExtensionActivationRule
+ TRUEPREDICATE
+ NSExtensionPointName
+ com.apple.ui-services
+ NSExtensionPointVersion
+ 1.0
+
+ NSExtensionMainStoryboard
+ MainInterface
+ NSExtensionPointIdentifier
+ com.apple.ui-services
+
+
+
diff --git a/tools/gyp/test/ios/extension/ActionExtension/MainInterface.storyboard b/tools/gyp/test/ios/extension/ActionExtension/MainInterface.storyboard
new file mode 100644
index 00000000000000..5aa58184e80bba
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ActionExtension/MainInterface.storyboard
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/AppDelegate.h b/tools/gyp/test/ios/extension/ExtensionContainer/AppDelegate.h
new file mode 100644
index 00000000000000..526cb31614abb6
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/AppDelegate.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import
+
+@interface AppDelegate : UIResponder
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/AppDelegate.m b/tools/gyp/test/ios/extension/ExtensionContainer/AppDelegate.m
new file mode 100644
index 00000000000000..1197bc1bbc0ca7
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/AppDelegate.m
@@ -0,0 +1,19 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication*)application
+ didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+ // Override point for customization after application launch.
+ return YES;
+}
+
+@end
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/Base.lproj/Main.storyboard b/tools/gyp/test/ios/extension/ExtensionContainer/Base.lproj/Main.storyboard
new file mode 100644
index 00000000000000..e8f3cfb40c36f8
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/Images.xcassets/AppIcon.appiconset/Contents.json b/tools/gyp/test/ios/extension/ExtensionContainer/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..f697f61f4aaf5c
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,53 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/Images.xcassets/LaunchImage.launchimage/Contents.json b/tools/gyp/test/ios/extension/ExtensionContainer/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 00000000000000..4458b40c055859
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,51 @@
+{
+ "images" : [
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/Info.plist b/tools/gyp/test/ios/extension/ExtensionContainer/Info.plist
new file mode 100644
index 00000000000000..31ccf4cc82e149
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ ExtensionContainer
+ CFBundleIdentifier
+ com.google.gyptest.extension
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+
+
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/ViewController.h b/tools/gyp/test/ios/extension/ExtensionContainer/ViewController.h
new file mode 100644
index 00000000000000..fa12f5ba67c67a
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/ViewController.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import
+
+@interface ViewController : UIViewController
+
+
+@end
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/ViewController.m b/tools/gyp/test/ios/extension/ExtensionContainer/ViewController.m
new file mode 100644
index 00000000000000..3810fa9cba82c2
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/ViewController.m
@@ -0,0 +1,24 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ViewController.h"
+
+@interface ViewController ()
+
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/tools/gyp/test/ios/extension/ExtensionContainer/main.m b/tools/gyp/test/ios/extension/ExtensionContainer/main.m
new file mode 100644
index 00000000000000..47aecb514839c4
--- /dev/null
+++ b/tools/gyp/test/ios/extension/ExtensionContainer/main.m
@@ -0,0 +1,13 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char* argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil,
+ NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/tools/gyp/test/ios/extension/extension.gyp b/tools/gyp/test/ios/extension/extension.gyp
new file mode 100644
index 00000000000000..fe27cb24afc4a2
--- /dev/null
+++ b/tools/gyp/test/ios/extension/extension.gyp
@@ -0,0 +1,84 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'ExtensionContainer',
+ 'product_name': 'ExtensionContainer',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'mac_bundle_resources': [
+ 'ExtensionContainer/Base.lproj/Main.storyboard',
+ ],
+ 'sources': [
+ 'ExtensionContainer/AppDelegate.h',
+ 'ExtensionContainer/AppDelegate.m',
+ 'ExtensionContainer/ViewController.h',
+ 'ExtensionContainer/ViewController.m',
+ 'ExtensionContainer/main.m',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/ExtensionContainer.app/PlugIns',
+ 'files': [
+ '<(PRODUCT_DIR)/ActionExtension.appex',
+ ]}],
+ 'dependencies': [
+ 'ActionExtension'
+ ],
+
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'INFOPLIST_FILE': 'ExtensionContainer/Info.plist',
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'ARCHS': [ 'armv7' ],
+ 'SDKROOT': 'iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ 'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer',
+ },
+ },
+ {
+ 'target_name': 'ActionExtension',
+ 'product_name': 'ActionExtension',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'ios_app_extension': 1,
+ 'sources': [
+ 'ActionExtension/ActionViewController.h',
+ 'ActionExtension/ActionViewController.m',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'INFOPLIST_FILE': 'ActionExtension/Info.plist',
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'ARCHS': [ 'armv7' ],
+ 'SDKROOT': 'iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ 'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/ios/gyptest-app-ios-assets-catalog.py b/tools/gyp/test/ios/gyptest-app-ios-assets-catalog.py
new file mode 100755
index 00000000000000..efd96ac752f028
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-app-ios-assets-catalog.py
@@ -0,0 +1,57 @@
+#!/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 ios app bundles are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import os.path
+import sys
+
+# Xcode supports for assets catalog was introduced in Xcode 6.0
+if sys.platform == 'darwin' and TestMac.Xcode.Version() >= '0600':
+ test_gyp_path = 'test-assets-catalog.gyp'
+ test_app_path = 'Test App Assets Catalog Gyp.app'
+
+ test = TestGyp.TestGyp(formats=['xcode', 'ninja'])
+ test.run_gyp(test_gyp_path, chdir='app-bundle')
+ test.build(test_gyp_path, test.ALL, chdir='app-bundle')
+
+ # Test that the extension is .bundle
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'Test App Assets Catalog Gyp'),
+ chdir='app-bundle')
+
+ # Info.plist
+ info_plist = test.built_file_path(
+ os.path.join(test_app_path, 'Info.plist'),
+ chdir='app-bundle')
+ # Resources
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'English.lproj/InfoPlist.strings'),
+ chdir='app-bundle')
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'English.lproj/MainMenu.nib'),
+ chdir='app-bundle')
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'English.lproj/Main_iPhone.storyboardc'),
+ chdir='app-bundle')
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'Assets.car'),
+ chdir='app-bundle')
+
+ # Packaging
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'PkgInfo'),
+ chdir='app-bundle')
+ test.built_file_must_match(
+ os.path.join(test_app_path, 'PkgInfo'), 'APPLause',
+ chdir='app-bundle')
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-app-ios.py b/tools/gyp/test/ios/gyptest-app-ios.py
new file mode 100755
index 00000000000000..f5fd5f93f7424f
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-app-ios.py
@@ -0,0 +1,71 @@
+#!/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 ios app bundles are built correctly.
+"""
+
+import TestGyp
+
+import subprocess
+import sys
+
+def CheckFileXMLPropertyList(file):
+ output = subprocess.check_output(['file', file])
+ # The double space after XML is intentional.
+ if not 'XML document text' in output:
+ print 'File: Expected XML document text, got %s' % output
+ test.fail_test()
+
+def CheckFileBinaryPropertyList(file):
+ output = subprocess.check_output(['file', file])
+ if not 'Apple binary property list' in output:
+ print 'File: Expected Apple binary property list, got %s' % output
+ test.fail_test()
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode', 'ninja'])
+
+ test.run_gyp('test.gyp', chdir='app-bundle')
+
+ test.build('test.gyp', test.ALL, chdir='app-bundle')
+
+ # Test that the extension is .bundle
+ test.built_file_must_exist('Test App Gyp.app/Test App Gyp',
+ chdir='app-bundle')
+
+ # Info.plist
+ info_plist = test.built_file_path('Test App Gyp.app/Info.plist',
+ chdir='app-bundle')
+ test.built_file_must_exist(info_plist)
+ CheckFileBinaryPropertyList(info_plist)
+
+ # XML Info.plist
+ info_plist = test.built_file_path('Test App Gyp XML.app/Info.plist',
+ chdir='app-bundle')
+ CheckFileXMLPropertyList(info_plist)
+
+ # Resources
+ strings_file = test.built_file_path(
+ 'Test App Gyp.app/English.lproj/InfoPlist.strings',
+ chdir='app-bundle')
+ test.built_file_must_exist(strings_file)
+ CheckFileBinaryPropertyList(strings_file)
+
+ test.built_file_must_exist(
+ 'Test App Gyp.app/English.lproj/MainMenu.nib',
+ chdir='app-bundle')
+ test.built_file_must_exist(
+ 'Test App Gyp.app/English.lproj/Main_iPhone.storyboardc',
+ chdir='app-bundle')
+
+ # Packaging
+ test.built_file_must_exist('Test App Gyp.app/PkgInfo',
+ chdir='app-bundle')
+ test.built_file_must_match('Test App Gyp.app/PkgInfo', 'APPLause',
+ chdir='app-bundle')
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-archs.py b/tools/gyp/test/ios/gyptest-archs.py
new file mode 100644
index 00000000000000..38c5c61fd8f388
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-archs.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 device and simulator bundles are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import collections
+import sys
+
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ test_cases = [
+ ('Default', 'TestArch32Bits', ['i386']),
+ ('Default-iphoneos', 'TestArch32Bits', ['armv7']),
+ ]
+
+ if TestMac.Xcode.Version() < '0510':
+ test_cases.extend([
+ ('Default', 'TestNoArchs', ['i386']),
+ ('Default-iphoneos', 'TestNoArchs', ['armv7'])])
+
+ if TestMac.Xcode.Version() >= '0500':
+ test_cases.extend([
+ ('Default', 'TestArch64Bits', ['x86_64']),
+ ('Default', 'TestMultiArchs', ['i386', 'x86_64']),
+ ('Default-iphoneos', 'TestArch64Bits', ['arm64']),
+ ('Default-iphoneos', 'TestMultiArchs', ['armv7', 'arm64'])])
+
+ test.run_gyp('test-archs.gyp', chdir='app-bundle')
+ for configuration, target, archs in test_cases:
+ is_device_build = configuration.endswith('-iphoneos')
+
+ kwds = collections.defaultdict(list)
+ if test.format == 'xcode':
+ if is_device_build:
+ configuration, sdk = configuration.split('-')
+ kwds['arguments'].extend(['-sdk', sdk])
+ if TestMac.Xcode.Version() < '0500':
+ kwds['arguments'].extend(['-arch', archs[0]])
+
+ test.set_configuration(configuration)
+ filename = '%s.bundle/%s' % (target, target)
+ test.build('test-archs.gyp', target, chdir='app-bundle', **kwds)
+ result_file = test.built_file_path(filename, chdir='app-bundle')
+
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, archs)
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-crosscompile.py b/tools/gyp/test/ios/gyptest-crosscompile.py
new file mode 100644
index 00000000000000..a0816836e577f4
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-crosscompile.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that tools are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import sys
+import os
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ oldenv = os.environ.copy()
+ try:
+ os.environ['GYP_CROSSCOMPILE'] = '1'
+ test.run_gyp('test-crosscompile.gyp', chdir='app-bundle')
+ finally:
+ os.environ.clear()
+ os.environ.update(oldenv)
+
+ test.set_configuration('Default')
+ test.build('test-crosscompile.gyp', 'TestHost', chdir='app-bundle')
+ result_file = test.built_file_path('TestHost', chdir='app-bundle')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['x86_64'])
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-deployment-target.py b/tools/gyp/test/ios/gyptest-deployment-target.py
new file mode 100644
index 00000000000000..526c136e4e4274
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-deployment-target.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 IPHONEOS_DEPLOYMENT_TARGET works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['make', 'ninja', 'xcode'])
+
+ test.run_gyp('deployment-target.gyp', chdir='deployment-target')
+
+ test.build('deployment-target.gyp', test.ALL, chdir='deployment-target')
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-extension.py b/tools/gyp/test/ios/gyptest-extension.py
new file mode 100755
index 00000000000000..8f421310650d55
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-extension.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that ios app extensions are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import sys
+if sys.platform == 'darwin' and TestMac.Xcode.Version()>="0600":
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ test.run_gyp('extension.gyp', chdir='extension')
+
+ test.build('extension.gyp', 'ExtensionContainer', chdir='extension')
+
+ # Test that the extension is .appex
+ test.built_file_must_exist(
+ 'ExtensionContainer.app/PlugIns/ActionExtension.appex',
+ chdir='extension')
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-per-config-settings.py b/tools/gyp/test/ios/gyptest-per-config-settings.py
new file mode 100644
index 00000000000000..d15907e721e074
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-per-config-settings.py
@@ -0,0 +1,147 @@
+#!/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 device and simulator bundles are built correctly.
+"""
+
+import plistlib
+import TestGyp
+import os
+import struct
+import subprocess
+import sys
+import tempfile
+
+
+def CheckFileType(file, expected):
+ proc = subprocess.Popen(['lipo', '-info', file], stdout=subprocess.PIPE)
+ o = proc.communicate()[0].strip()
+ assert not proc.returncode
+ if not expected in o:
+ print 'File: Expected %s, got %s' % (expected, o)
+ test.fail_test()
+
+def HasCerts():
+ # Because the bots do not have certs, don't check them if there are no
+ # certs available.
+ proc = subprocess.Popen(['security','find-identity','-p', 'codesigning',
+ '-v'], stdout=subprocess.PIPE)
+ return "0 valid identities found" not in proc.communicate()[0].strip()
+
+def CheckSignature(file):
+ proc = subprocess.Popen(['codesign', '-v', file], stdout=subprocess.PIPE)
+ o = proc.communicate()[0].strip()
+ assert not proc.returncode
+ if "code object is not signed at all" in o:
+ print 'File %s not properly signed.' % (file)
+ test.fail_test()
+
+def CheckEntitlements(file, expected_entitlements):
+ with tempfile.NamedTemporaryFile() as temp:
+ proc = subprocess.Popen(['codesign', '--display', '--entitlements',
+ temp.name, file], stdout=subprocess.PIPE)
+ o = proc.communicate()[0].strip()
+ assert not proc.returncode
+ data = temp.read()
+ entitlements = ParseEntitlements(data)
+ if not entitlements:
+ print 'No valid entitlements found in %s.' % (file)
+ test.fail_test()
+ if entitlements != expected_entitlements:
+ print 'Unexpected entitlements found in %s.' % (file)
+ test.fail_test()
+
+def ParseEntitlements(data):
+ if len(data) < 8:
+ return None
+ magic, length = struct.unpack('>II', data[:8])
+ if magic != 0xfade7171 or length != len(data):
+ return None
+ return data[8:]
+
+def GetProductVersion():
+ args = ['xcodebuild','-version','-sdk','iphoneos','ProductVersion']
+ job = subprocess.Popen(args, stdout=subprocess.PIPE)
+ return job.communicate()[0].strip()
+
+def CheckPlistvalue(plist, key, expected):
+ if key not in plist:
+ print '%s not set in plist' % key
+ test.fail_test()
+ return
+ actual = plist[key]
+ if actual != expected:
+ print 'File: Expected %s, got %s for %s' % (expected, actual, key)
+ test.fail_test()
+
+def CheckPlistNotSet(plist, key):
+ if key in plist:
+ print '%s should not be set in plist' % key
+ test.fail_test()
+ return
+
+def ConvertBinaryPlistToXML(path):
+ proc = subprocess.call(['plutil', '-convert', 'xml1', path],
+ stdout=subprocess.PIPE)
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ test.run_gyp('test-device.gyp', chdir='app-bundle')
+
+ test_configs = ['Default-iphoneos', 'Default']
+ # TODO(justincohen): Disabling 'Default-iphoneos' for xcode until bots are
+ # configured with signing certs.
+ if test.format == 'xcode':
+ test_configs.remove('Default-iphoneos')
+
+ for configuration in test_configs:
+ test.set_configuration(configuration)
+ test.build('test-device.gyp', 'test_app', chdir='app-bundle')
+ result_file = test.built_file_path('Test App Gyp.bundle/Test App Gyp',
+ chdir='app-bundle')
+ test.must_exist(result_file)
+
+ info_plist = test.built_file_path('Test App Gyp.bundle/Info.plist',
+ chdir='app-bundle')
+
+ # plistlib doesn't support binary plists, but that's what Xcode creates.
+ if test.format == 'xcode':
+ ConvertBinaryPlistToXML(info_plist)
+ plist = plistlib.readPlist(info_plist)
+
+ CheckPlistvalue(plist, 'UIDeviceFamily', [1, 2])
+
+ if configuration == 'Default-iphoneos':
+ CheckFileType(result_file, 'armv7')
+ CheckPlistvalue(plist, 'DTPlatformVersion', GetProductVersion())
+ CheckPlistvalue(plist, 'CFBundleSupportedPlatforms', ['iPhoneOS'])
+ CheckPlistvalue(plist, 'DTPlatformName', 'iphoneos')
+ else:
+ CheckFileType(result_file, 'i386')
+ CheckPlistNotSet(plist, 'DTPlatformVersion')
+ CheckPlistvalue(plist, 'CFBundleSupportedPlatforms', ['iPhoneSimulator'])
+ CheckPlistvalue(plist, 'DTPlatformName', 'iphonesimulator')
+
+ if HasCerts() and configuration == 'Default-iphoneos':
+ test.build('test-device.gyp', 'sig_test', chdir='app-bundle')
+ result_file = test.built_file_path('sig_test.bundle/sig_test',
+ chdir='app-bundle')
+ CheckSignature(result_file)
+ info_plist = test.built_file_path('sig_test.bundle/Info.plist',
+ chdir='app-bundle')
+
+ plist = plistlib.readPlist(info_plist)
+ CheckPlistvalue(plist, 'UIDeviceFamily', [1])
+
+ entitlements_file = test.built_file_path('sig_test.xcent',
+ chdir='app-bundle')
+ if os.path.isfile(entitlements_file):
+ expected_entitlements = open(entitlements_file).read()
+ CheckEntitlements(result_file, expected_entitlements)
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-watch.py b/tools/gyp/test/ios/gyptest-watch.py
new file mode 100755
index 00000000000000..3a0a8b5e93aff1
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-watch.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that ios watch extensions and apps are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import sys
+if sys.platform == 'darwin' and TestMac.Xcode.Version() >= "0620":
+ test = TestGyp.TestGyp(formats=['ninja', 'xcode'])
+
+ test.run_gyp('watch.gyp', chdir='watch')
+
+ test.build(
+ 'watch.gyp',
+ 'WatchContainer',
+ chdir='watch')
+
+ # Test that the extension exists
+ test.built_file_must_exist(
+ 'WatchContainer.app/PlugIns/WatchKitExtension.appex',
+ chdir='watch')
+
+ # Test that the watch app exists
+ test.built_file_must_exist(
+ 'WatchContainer.app/PlugIns/WatchKitExtension.appex/WatchApp.app',
+ chdir='watch')
+
+ test.pass_test()
diff --git a/tools/gyp/test/ios/gyptest-xcode-ninja.py b/tools/gyp/test/ios/gyptest-xcode-ninja.py
new file mode 100644
index 00000000000000..609db8c98f56f5
--- /dev/null
+++ b/tools/gyp/test/ios/gyptest-xcode-ninja.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.
+
+"""
+Verify that the xcode-ninja GYP_GENERATOR runs and builds correctly.
+"""
+
+import TestGyp
+
+import os
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+ # Run ninja and xcode-ninja
+ test.formats = ['ninja', 'xcode-ninja']
+ test.run_gyp('test.gyp', chdir='app-bundle')
+
+ # If it builds the target, it works.
+ test.build('test.ninja.gyp', chdir='app-bundle')
+ test.pass_test()
diff --git a/tools/gyp/test/ios/watch/WatchApp/Images.xcassets/AppIcon.appiconset/Contents.json b/tools/gyp/test/ios/watch/WatchApp/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..562c5eff9792f1
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchApp/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,62 @@
+{
+ "images" : [
+ {
+ "size" : "14.5x14.5",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "notificationCenter",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "18x18",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "notificationCenter",
+ "subtype" : "42mm"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29.3x29.3",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "appLauncher",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "44x44",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "appLauncher",
+ "subtype" : "42mm"
+ },
+ {
+ "size" : "86x86",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "quickLook",
+ "subtype" : "38mm"
+ },
+ {
+ "size" : "98x98",
+ "idiom" : "watch",
+ "scale" : "2x",
+ "role" : "quickLook",
+ "subtype" : "42mm"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/watch/WatchApp/Images.xcassets/LaunchImage.launchimage/Contents.json b/tools/gyp/test/ios/watch/WatchApp/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 00000000000000..ed123feff6c4e1
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchApp/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,24 @@
+{
+ "images" : [
+ {
+ "orientation" : "portrait",
+ "idiom" : "watch",
+ "extent" : "full-screen",
+ "minimum-system-version" : "8.0",
+ "subtype" : "38mm",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "watch",
+ "extent" : "full-screen",
+ "minimum-system-version" : "8.0",
+ "subtype" : "42mm",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/watch/WatchApp/Info.plist b/tools/gyp/test/ios/watch/WatchApp/Info.plist
new file mode 100644
index 00000000000000..3cf65b82850160
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchApp/Info.plist
@@ -0,0 +1,35 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ WatchApp
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ com.google.gyptest.watch.watchapp
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+
+ WKCompanionAppBundleIdentifier
+ com.google.gyptest.watch
+ WKWatchKitApp
+
+
+
diff --git a/tools/gyp/test/ios/watch/WatchApp/Interface.storyboard b/tools/gyp/test/ios/watch/WatchApp/Interface.storyboard
new file mode 100644
index 00000000000000..5f52cb6c9078db
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchApp/Interface.storyboard
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/test/ios/watch/WatchContainer/AppDelegate.h b/tools/gyp/test/ios/watch/WatchContainer/AppDelegate.h
new file mode 100644
index 00000000000000..526cb31614abb6
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/AppDelegate.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import
+
+@interface AppDelegate : UIResponder
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/tools/gyp/test/ios/watch/WatchContainer/AppDelegate.m b/tools/gyp/test/ios/watch/WatchContainer/AppDelegate.m
new file mode 100644
index 00000000000000..1197bc1bbc0ca7
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/AppDelegate.m
@@ -0,0 +1,19 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication*)application
+ didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+ // Override point for customization after application launch.
+ return YES;
+}
+
+@end
diff --git a/tools/gyp/test/ios/watch/WatchContainer/Base.lproj/Main.storyboard b/tools/gyp/test/ios/watch/WatchContainer/Base.lproj/Main.storyboard
new file mode 100644
index 00000000000000..e8f3cfb40c36f8
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/test/ios/watch/WatchContainer/Images.xcassets/AppIcon.appiconset/Contents.json b/tools/gyp/test/ios/watch/WatchContainer/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..f697f61f4aaf5c
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,53 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/watch/WatchContainer/Images.xcassets/LaunchImage.launchimage/Contents.json b/tools/gyp/test/ios/watch/WatchContainer/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 00000000000000..4458b40c055859
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,51 @@
+{
+ "images" : [
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/watch/WatchContainer/Info.plist b/tools/gyp/test/ios/watch/WatchContainer/Info.plist
new file mode 100644
index 00000000000000..a40319c78eac09
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ WatchContainer
+ CFBundleIdentifier
+ com.google.gyptest.watch
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+
+
diff --git a/tools/gyp/test/ios/watch/WatchContainer/ViewController.h b/tools/gyp/test/ios/watch/WatchContainer/ViewController.h
new file mode 100644
index 00000000000000..fa12f5ba67c67a
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/ViewController.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import
+
+@interface ViewController : UIViewController
+
+
+@end
diff --git a/tools/gyp/test/ios/watch/WatchContainer/ViewController.m b/tools/gyp/test/ios/watch/WatchContainer/ViewController.m
new file mode 100644
index 00000000000000..3810fa9cba82c2
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/ViewController.m
@@ -0,0 +1,24 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ViewController.h"
+
+@interface ViewController ()
+
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/tools/gyp/test/ios/watch/WatchContainer/main.m b/tools/gyp/test/ios/watch/WatchContainer/main.m
new file mode 100644
index 00000000000000..47aecb514839c4
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchContainer/main.m
@@ -0,0 +1,13 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char* argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil,
+ NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/tools/gyp/test/ios/watch/WatchKitExtension/Images.xcassets/MyImage.imageset/Contents.json b/tools/gyp/test/ios/watch/WatchKitExtension/Images.xcassets/MyImage.imageset/Contents.json
new file mode 100644
index 00000000000000..f80d9508688cad
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchKitExtension/Images.xcassets/MyImage.imageset/Contents.json
@@ -0,0 +1,20 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/tools/gyp/test/ios/watch/WatchKitExtension/Info.plist b/tools/gyp/test/ios/watch/WatchKitExtension/Info.plist
new file mode 100644
index 00000000000000..7a354643ef4ef2
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchKitExtension/Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ WatchContainer WatchKit Extension
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ com.google.gyptest.watch.watchkitextension
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ XPC!
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ NSExtension
+
+ NSExtensionAttributes
+
+ WKAppBundleIdentifier
+ com.google.gyptest.watch.watchapp
+
+ NSExtensionPointIdentifier
+ com.apple.watchkit
+
+ RemoteInterfacePrincipalClass
+ InterfaceController
+
+
diff --git a/tools/gyp/test/ios/watch/WatchKitExtension/InterfaceController.h b/tools/gyp/test/ios/watch/WatchKitExtension/InterfaceController.h
new file mode 100644
index 00000000000000..18fcb10fb098f6
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchKitExtension/InterfaceController.h
@@ -0,0 +1,9 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import
+#import
+
+@interface InterfaceController : WKInterfaceController
+@end
diff --git a/tools/gyp/test/ios/watch/WatchKitExtension/InterfaceController.m b/tools/gyp/test/ios/watch/WatchKitExtension/InterfaceController.m
new file mode 100644
index 00000000000000..b667c834d6053b
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchKitExtension/InterfaceController.m
@@ -0,0 +1,24 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "InterfaceController.h"
+
+@implementation InterfaceController
+
+- (instancetype)initWithContext:(id)context {
+ if ((self = [super initWithContext:context])) {
+ // -initWithContext:
+ }
+ return self;
+}
+
+- (void)willActivate {
+ // -willActivate
+}
+
+- (void)didDeactivate {
+ // -didDeactivate
+}
+
+@end
diff --git a/tools/gyp/test/ios/watch/WatchKitExtension/MainInterface.storyboard b/tools/gyp/test/ios/watch/WatchKitExtension/MainInterface.storyboard
new file mode 100644
index 00000000000000..5aa58184e80bba
--- /dev/null
+++ b/tools/gyp/test/ios/watch/WatchKitExtension/MainInterface.storyboard
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/gyp/test/ios/watch/watch.gyp b/tools/gyp/test/ios/watch/watch.gyp
new file mode 100644
index 00000000000000..ac26f63eee0daa
--- /dev/null
+++ b/tools/gyp/test/ios/watch/watch.gyp
@@ -0,0 +1,104 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ 'target_defaults': {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-fobjc-abi-version=2',
+ ],
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'SDKROOT': 'iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '8.2',
+ 'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer',
+ }
+ },
+ 'targets': [
+ {
+ 'target_name': 'WatchContainer',
+ 'product_name': 'WatchContainer',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'mac_bundle_resources': [
+ 'WatchContainer/Base.lproj/Main.storyboard',
+ ],
+ 'sources': [
+ 'WatchContainer/AppDelegate.h',
+ 'WatchContainer/AppDelegate.m',
+ 'WatchContainer/ViewController.h',
+ 'WatchContainer/ViewController.m',
+ 'WatchContainer/main.m',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/WatchContainer.app/PlugIns',
+ 'files': [
+ '<(PRODUCT_DIR)/WatchKitExtension.appex',
+ ]}],
+ 'dependencies': [
+ 'WatchKitExtension'
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'WatchContainer/Info.plist',
+ },
+ },
+ {
+ 'target_name': 'WatchKitExtension',
+ 'product_name': 'WatchKitExtension',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'ios_watchkit_extension': 1,
+ 'sources': [
+ 'WatchKitExtension/InterfaceController.h',
+ 'WatchKitExtension/InterfaceController.m',
+ ],
+ 'mac_bundle_resources': [
+ 'WatchKitExtension/Images.xcassets',
+ '<(PRODUCT_DIR)/WatchApp.app',
+ ],
+ 'dependencies': [
+ 'WatchApp'
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/WatchKit.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'WatchKitExtension/Info.plist',
+ 'SKIP_INSTALL': 'YES',
+ 'COPY_PHASE_STRIP': 'NO',
+ },
+ },
+ {
+ 'target_name': 'WatchApp',
+ 'product_name': 'WatchApp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'ios_watch_app': 1,
+ 'mac_bundle_resources': [
+ 'WatchApp/Images.xcassets',
+ 'WatchApp/Interface.storyboard',
+ ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'WatchApp/Info.plist',
+ 'SKIP_INSTALL': 'YES',
+ 'COPY_PHASE_STRIP': 'NO',
+ 'TARGETED_DEVICE_FAMILY': '4',
+ 'TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]': '1,4',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/lib/README.txt b/tools/gyp/test/lib/README.txt
new file mode 100644
index 00000000000000..b3d724574e6377
--- /dev/null
+++ b/tools/gyp/test/lib/README.txt
@@ -0,0 +1,17 @@
+Supporting modules for GYP testing.
+
+ TestCmd.py
+ TestCommon.py
+
+ Modules for generic testing of command-line utilities,
+ specifically including the ability to copy a test configuration
+ to temporary directories (with default cleanup on exit) as part
+ of running test scripts that invoke commands, compare actual
+ against expected output, etc.
+
+ Our copies of these come from the SCons project,
+ http://www.scons.org/.
+
+ TestGyp.py
+
+ Modules for GYP-specific tests, of course.
diff --git a/tools/gyp/test/lib/TestCmd.py b/tools/gyp/test/lib/TestCmd.py
new file mode 100644
index 00000000000000..10f45f6cfa11ce
--- /dev/null
+++ b/tools/gyp/test/lib/TestCmd.py
@@ -0,0 +1,1597 @@
+"""
+TestCmd.py: a testing framework for commands and scripts.
+
+The TestCmd module provides a framework for portable automated testing
+of executable commands and scripts (in any language, not just Python),
+especially commands and scripts that require file system interaction.
+
+In addition to running tests and evaluating conditions, the TestCmd
+module manages and cleans up one or more temporary workspace
+directories, and provides methods for creating files and directories in
+those workspace directories from in-line data, here-documents), allowing
+tests to be completely self-contained.
+
+A TestCmd environment object is created via the usual invocation:
+
+ import TestCmd
+ test = TestCmd.TestCmd()
+
+There are a bunch of keyword arguments available at instantiation:
+
+ test = TestCmd.TestCmd(description = 'string',
+ program = 'program_or_script_to_test',
+ interpreter = 'script_interpreter',
+ workdir = 'prefix',
+ subdir = 'subdir',
+ verbose = Boolean,
+ match = default_match_function,
+ diff = default_diff_function,
+ combine = Boolean)
+
+There are a bunch of methods that let you do different things:
+
+ test.verbose_set(1)
+
+ test.description_set('string')
+
+ test.program_set('program_or_script_to_test')
+
+ test.interpreter_set('script_interpreter')
+ test.interpreter_set(['script_interpreter', 'arg'])
+
+ test.workdir_set('prefix')
+ test.workdir_set('')
+
+ test.workpath('file')
+ test.workpath('subdir', 'file')
+
+ test.subdir('subdir', ...)
+
+ test.rmdir('subdir', ...)
+
+ test.write('file', "contents\n")
+ test.write(['subdir', 'file'], "contents\n")
+
+ test.read('file')
+ test.read(['subdir', 'file'])
+ test.read('file', mode)
+ test.read(['subdir', 'file'], mode)
+
+ test.writable('dir', 1)
+ test.writable('dir', None)
+
+ test.preserve(condition, ...)
+
+ test.cleanup(condition)
+
+ test.command_args(program = 'program_or_script_to_run',
+ interpreter = 'script_interpreter',
+ arguments = 'arguments to pass to program')
+
+ test.run(program = 'program_or_script_to_run',
+ interpreter = 'script_interpreter',
+ arguments = 'arguments to pass to program',
+ chdir = 'directory_to_chdir_to',
+ stdin = 'input to feed to the program\n')
+ universal_newlines = True)
+
+ p = test.start(program = 'program_or_script_to_run',
+ interpreter = 'script_interpreter',
+ arguments = 'arguments to pass to program',
+ universal_newlines = None)
+
+ test.finish(self, p)
+
+ test.pass_test()
+ test.pass_test(condition)
+ test.pass_test(condition, function)
+
+ test.fail_test()
+ test.fail_test(condition)
+ test.fail_test(condition, function)
+ test.fail_test(condition, function, skip)
+
+ test.no_result()
+ test.no_result(condition)
+ test.no_result(condition, function)
+ test.no_result(condition, function, skip)
+
+ test.stdout()
+ test.stdout(run)
+
+ test.stderr()
+ test.stderr(run)
+
+ test.symlink(target, link)
+
+ test.banner(string)
+ test.banner(string, width)
+
+ test.diff(actual, expected)
+
+ test.match(actual, expected)
+
+ test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n")
+ test.match_exact(["actual 1\n", "actual 2\n"],
+ ["expected 1\n", "expected 2\n"])
+
+ test.match_re("actual 1\nactual 2\n", regex_string)
+ test.match_re(["actual 1\n", "actual 2\n"], list_of_regexes)
+
+ test.match_re_dotall("actual 1\nactual 2\n", regex_string)
+ test.match_re_dotall(["actual 1\n", "actual 2\n"], list_of_regexes)
+
+ test.tempdir()
+ test.tempdir('temporary-directory')
+
+ test.sleep()
+ test.sleep(seconds)
+
+ test.where_is('foo')
+ test.where_is('foo', 'PATH1:PATH2')
+ test.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4')
+
+ test.unlink('file')
+ test.unlink('subdir', 'file')
+
+The TestCmd module provides pass_test(), fail_test(), and no_result()
+unbound functions that report test results for use with the Aegis change
+management system. These methods terminate the test immediately,
+reporting PASSED, FAILED, or NO RESULT respectively, and exiting with
+status 0 (success), 1 or 2 respectively. This allows for a distinction
+between an actual failed test and a test that could not be properly
+evaluated because of an external condition (such as a full file system
+or incorrect permissions).
+
+ import TestCmd
+
+ TestCmd.pass_test()
+ TestCmd.pass_test(condition)
+ TestCmd.pass_test(condition, function)
+
+ TestCmd.fail_test()
+ TestCmd.fail_test(condition)
+ TestCmd.fail_test(condition, function)
+ TestCmd.fail_test(condition, function, skip)
+
+ TestCmd.no_result()
+ TestCmd.no_result(condition)
+ TestCmd.no_result(condition, function)
+ TestCmd.no_result(condition, function, skip)
+
+The TestCmd module also provides unbound functions that handle matching
+in the same way as the match_*() methods described above.
+
+ import TestCmd
+
+ test = TestCmd.TestCmd(match = TestCmd.match_exact)
+
+ test = TestCmd.TestCmd(match = TestCmd.match_re)
+
+ test = TestCmd.TestCmd(match = TestCmd.match_re_dotall)
+
+The TestCmd module provides unbound functions that can be used for the
+"diff" argument to TestCmd.TestCmd instantiation:
+
+ import TestCmd
+
+ test = TestCmd.TestCmd(match = TestCmd.match_re,
+ diff = TestCmd.diff_re)
+
+ test = TestCmd.TestCmd(diff = TestCmd.simple_diff)
+
+The "diff" argument can also be used with standard difflib functions:
+
+ import difflib
+
+ test = TestCmd.TestCmd(diff = difflib.context_diff)
+
+ test = TestCmd.TestCmd(diff = difflib.unified_diff)
+
+Lastly, the where_is() method also exists in an unbound function
+version.
+
+ import TestCmd
+
+ TestCmd.where_is('foo')
+ TestCmd.where_is('foo', 'PATH1:PATH2')
+ TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4')
+"""
+
+# Copyright 2000-2010 Steven Knight
+# This module is free software, and you may redistribute it and/or modify
+# it under the same terms as Python itself, so long as this copyright message
+# and disclaimer are retained in their original form.
+#
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+#
+# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
+# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+__author__ = "Steven Knight "
+__revision__ = "TestCmd.py 0.37.D001 2010/01/11 16:55:50 knight"
+__version__ = "0.37"
+
+import errno
+import os
+import os.path
+import re
+import shutil
+import stat
+import string
+import sys
+import tempfile
+import time
+import traceback
+import types
+import UserList
+
+__all__ = [
+ 'diff_re',
+ 'fail_test',
+ 'no_result',
+ 'pass_test',
+ 'match_exact',
+ 'match_re',
+ 'match_re_dotall',
+ 'python_executable',
+ 'TestCmd'
+]
+
+try:
+ import difflib
+except ImportError:
+ __all__.append('simple_diff')
+
+def is_List(e):
+ return type(e) is types.ListType \
+ or isinstance(e, UserList.UserList)
+
+try:
+ from UserString import UserString
+except ImportError:
+ class UserString:
+ pass
+
+if hasattr(types, 'UnicodeType'):
+ def is_String(e):
+ return type(e) is types.StringType \
+ or type(e) is types.UnicodeType \
+ or isinstance(e, UserString)
+else:
+ def is_String(e):
+ return type(e) is types.StringType or isinstance(e, UserString)
+
+tempfile.template = 'testcmd.'
+if os.name in ('posix', 'nt'):
+ tempfile.template = 'testcmd.' + str(os.getpid()) + '.'
+else:
+ tempfile.template = 'testcmd.'
+
+re_space = re.compile('\s')
+
+_Cleanup = []
+
+_chain_to_exitfunc = None
+
+def _clean():
+ global _Cleanup
+ cleanlist = filter(None, _Cleanup)
+ del _Cleanup[:]
+ cleanlist.reverse()
+ for test in cleanlist:
+ test.cleanup()
+ if _chain_to_exitfunc:
+ _chain_to_exitfunc()
+
+try:
+ import atexit
+except ImportError:
+ # TODO(1.5): atexit requires python 2.0, so chain sys.exitfunc
+ try:
+ _chain_to_exitfunc = sys.exitfunc
+ except AttributeError:
+ pass
+ sys.exitfunc = _clean
+else:
+ atexit.register(_clean)
+
+try:
+ zip
+except NameError:
+ def zip(*lists):
+ result = []
+ for i in xrange(min(map(len, lists))):
+ result.append(tuple(map(lambda l, i=i: l[i], lists)))
+ return result
+
+class Collector:
+ def __init__(self, top):
+ self.entries = [top]
+ def __call__(self, arg, dirname, names):
+ pathjoin = lambda n, d=dirname: os.path.join(d, n)
+ self.entries.extend(map(pathjoin, names))
+
+def _caller(tblist, skip):
+ string = ""
+ arr = []
+ for file, line, name, text in tblist:
+ if file[-10:] == "TestCmd.py":
+ break
+ arr = [(file, line, name, text)] + arr
+ atfrom = "at"
+ for file, line, name, text in arr[skip:]:
+ if name in ("?", ""):
+ name = ""
+ else:
+ name = " (" + name + ")"
+ string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name))
+ atfrom = "\tfrom"
+ return string
+
+def fail_test(self = None, condition = 1, function = None, skip = 0):
+ """Cause the test to fail.
+
+ By default, the fail_test() method reports that the test FAILED
+ and exits with a status of 1. If a condition argument is supplied,
+ the test fails only if the condition is true.
+ """
+ if not condition:
+ return
+ if not function is None:
+ function()
+ of = ""
+ desc = ""
+ sep = " "
+ if not self is None:
+ if self.program:
+ of = " of " + self.program
+ sep = "\n\t"
+ if self.description:
+ desc = " [" + self.description + "]"
+ sep = "\n\t"
+
+ at = _caller(traceback.extract_stack(), skip)
+ sys.stderr.write("FAILED test" + of + desc + sep + at)
+
+ sys.exit(1)
+
+def no_result(self = None, condition = 1, function = None, skip = 0):
+ """Causes a test to exit with no valid result.
+
+ By default, the no_result() method reports NO RESULT for the test
+ and exits with a status of 2. If a condition argument is supplied,
+ the test fails only if the condition is true.
+ """
+ if not condition:
+ return
+ if not function is None:
+ function()
+ of = ""
+ desc = ""
+ sep = " "
+ if not self is None:
+ if self.program:
+ of = " of " + self.program
+ sep = "\n\t"
+ if self.description:
+ desc = " [" + self.description + "]"
+ sep = "\n\t"
+
+ if os.environ.get('TESTCMD_DEBUG_SKIPS'):
+ at = _caller(traceback.extract_stack(), skip)
+ sys.stderr.write("NO RESULT for test" + of + desc + sep + at)
+ else:
+ sys.stderr.write("NO RESULT\n")
+
+ sys.exit(2)
+
+def pass_test(self = None, condition = 1, function = None):
+ """Causes a test to pass.
+
+ By default, the pass_test() method reports PASSED for the test
+ and exits with a status of 0. If a condition argument is supplied,
+ the test passes only if the condition is true.
+ """
+ if not condition:
+ return
+ if not function is None:
+ function()
+ sys.stderr.write("PASSED\n")
+ sys.exit(0)
+
+def match_exact(lines = None, matches = None):
+ """
+ """
+ if not is_List(lines):
+ lines = string.split(lines, "\n")
+ if not is_List(matches):
+ matches = string.split(matches, "\n")
+ if len(lines) != len(matches):
+ return
+ for i in range(len(lines)):
+ if lines[i] != matches[i]:
+ return
+ return 1
+
+def match_re(lines = None, res = None):
+ """
+ """
+ if not is_List(lines):
+ lines = string.split(lines, "\n")
+ if not is_List(res):
+ res = string.split(res, "\n")
+ if len(lines) != len(res):
+ return
+ for i in range(len(lines)):
+ s = "^" + res[i] + "$"
+ try:
+ expr = re.compile(s)
+ except re.error, e:
+ msg = "Regular expression error in %s: %s"
+ raise re.error, msg % (repr(s), e[0])
+ if not expr.search(lines[i]):
+ return
+ return 1
+
+def match_re_dotall(lines = None, res = None):
+ """
+ """
+ if not type(lines) is type(""):
+ lines = string.join(lines, "\n")
+ if not type(res) is type(""):
+ res = string.join(res, "\n")
+ s = "^" + res + "$"
+ try:
+ expr = re.compile(s, re.DOTALL)
+ except re.error, e:
+ msg = "Regular expression error in %s: %s"
+ raise re.error, msg % (repr(s), e[0])
+ if expr.match(lines):
+ return 1
+
+try:
+ import difflib
+except ImportError:
+ pass
+else:
+ def simple_diff(a, b, fromfile='', tofile='',
+ fromfiledate='', tofiledate='', n=3, lineterm='\n'):
+ """
+ A function with the same calling signature as difflib.context_diff
+ (diff -c) and difflib.unified_diff (diff -u) but which prints
+ output like the simple, unadorned 'diff" command.
+ """
+ sm = difflib.SequenceMatcher(None, a, b)
+ def comma(x1, x2):
+ return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2)
+ result = []
+ for op, a1, a2, b1, b2 in sm.get_opcodes():
+ if op == 'delete':
+ result.append("%sd%d" % (comma(a1, a2), b1))
+ result.extend(map(lambda l: '< ' + l, a[a1:a2]))
+ elif op == 'insert':
+ result.append("%da%s" % (a1, comma(b1, b2)))
+ result.extend(map(lambda l: '> ' + l, b[b1:b2]))
+ elif op == 'replace':
+ result.append("%sc%s" % (comma(a1, a2), comma(b1, b2)))
+ result.extend(map(lambda l: '< ' + l, a[a1:a2]))
+ result.append('---')
+ result.extend(map(lambda l: '> ' + l, b[b1:b2]))
+ return result
+
+def diff_re(a, b, fromfile='', tofile='',
+ fromfiledate='', tofiledate='', n=3, lineterm='\n'):
+ """
+ A simple "diff" of two sets of lines when the expected lines
+ are regular expressions. This is a really dumb thing that
+ just compares each line in turn, so it doesn't look for
+ chunks of matching lines and the like--but at least it lets
+ you know exactly which line first didn't compare correctl...
+ """
+ result = []
+ diff = len(a) - len(b)
+ if diff < 0:
+ a = a + ['']*(-diff)
+ elif diff > 0:
+ b = b + ['']*diff
+ i = 0
+ for aline, bline in zip(a, b):
+ s = "^" + aline + "$"
+ try:
+ expr = re.compile(s)
+ except re.error, e:
+ msg = "Regular expression error in %s: %s"
+ raise re.error, msg % (repr(s), e[0])
+ if not expr.search(bline):
+ result.append("%sc%s" % (i+1, i+1))
+ result.append('< ' + repr(a[i]))
+ result.append('---')
+ result.append('> ' + repr(b[i]))
+ i = i+1
+ return result
+
+if os.name == 'java':
+
+ python_executable = os.path.join(sys.prefix, 'jython')
+
+else:
+
+ python_executable = sys.executable
+
+if sys.platform == 'win32':
+
+ default_sleep_seconds = 2
+
+ def where_is(file, path=None, pathext=None):
+ if path is None:
+ path = os.environ['PATH']
+ if is_String(path):
+ path = string.split(path, os.pathsep)
+ if pathext is None:
+ pathext = os.environ['PATHEXT']
+ if is_String(pathext):
+ pathext = string.split(pathext, os.pathsep)
+ for ext in pathext:
+ if string.lower(ext) == string.lower(file[-len(ext):]):
+ pathext = ['']
+ break
+ for dir in path:
+ f = os.path.join(dir, file)
+ for ext in pathext:
+ fext = f + ext
+ if os.path.isfile(fext):
+ return fext
+ return None
+
+else:
+
+ def where_is(file, path=None, pathext=None):
+ if path is None:
+ path = os.environ['PATH']
+ if is_String(path):
+ path = string.split(path, os.pathsep)
+ for dir in path:
+ f = os.path.join(dir, file)
+ if os.path.isfile(f):
+ try:
+ st = os.stat(f)
+ except OSError:
+ continue
+ if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
+ return f
+ return None
+
+ default_sleep_seconds = 1
+
+
+
+try:
+ import subprocess
+except ImportError:
+ # The subprocess module doesn't exist in this version of Python,
+ # so we're going to cobble up something that looks just enough
+ # like its API for our purposes below.
+ import new
+
+ subprocess = new.module('subprocess')
+
+ subprocess.PIPE = 'PIPE'
+ subprocess.STDOUT = 'STDOUT'
+ subprocess.mswindows = (sys.platform == 'win32')
+
+ try:
+ import popen2
+ popen2.Popen3
+ except AttributeError:
+ class Popen3:
+ universal_newlines = 1
+ def __init__(self, command, **kw):
+ if sys.platform == 'win32' and command[0] == '"':
+ command = '"' + command + '"'
+ (stdin, stdout, stderr) = os.popen3(' ' + command)
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ def close_output(self):
+ self.stdout.close()
+ self.resultcode = self.stderr.close()
+ def wait(self):
+ resultcode = self.resultcode
+ if os.WIFEXITED(resultcode):
+ return os.WEXITSTATUS(resultcode)
+ elif os.WIFSIGNALED(resultcode):
+ return os.WTERMSIG(resultcode)
+ else:
+ return None
+
+ else:
+ try:
+ popen2.Popen4
+ except AttributeError:
+ # A cribbed Popen4 class, with some retrofitted code from
+ # the Python 1.5 Popen3 class methods to do certain things
+ # by hand.
+ class Popen4(popen2.Popen3):
+ childerr = None
+
+ def __init__(self, cmd, bufsize=-1):
+ p2cread, p2cwrite = os.pipe()
+ c2pread, c2pwrite = os.pipe()
+ self.pid = os.fork()
+ if self.pid == 0:
+ # Child
+ os.dup2(p2cread, 0)
+ os.dup2(c2pwrite, 1)
+ os.dup2(c2pwrite, 2)
+ for i in range(3, popen2.MAXFD):
+ try:
+ os.close(i)
+ except: pass
+ try:
+ os.execvp(cmd[0], cmd)
+ finally:
+ os._exit(1)
+ # Shouldn't come here, I guess
+ os._exit(1)
+ os.close(p2cread)
+ self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
+ os.close(c2pwrite)
+ self.fromchild = os.fdopen(c2pread, 'r', bufsize)
+ popen2._active.append(self)
+
+ popen2.Popen4 = Popen4
+
+ class Popen3(popen2.Popen3, popen2.Popen4):
+ universal_newlines = 1
+ def __init__(self, command, **kw):
+ if kw.get('stderr') == 'STDOUT':
+ apply(popen2.Popen4.__init__, (self, command, 1))
+ else:
+ apply(popen2.Popen3.__init__, (self, command, 1))
+ self.stdin = self.tochild
+ self.stdout = self.fromchild
+ self.stderr = self.childerr
+ def wait(self, *args, **kw):
+ resultcode = apply(popen2.Popen3.wait, (self,)+args, kw)
+ if os.WIFEXITED(resultcode):
+ return os.WEXITSTATUS(resultcode)
+ elif os.WIFSIGNALED(resultcode):
+ return os.WTERMSIG(resultcode)
+ else:
+ return None
+
+ subprocess.Popen = Popen3
+
+
+
+# From Josiah Carlson,
+# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554
+
+PIPE = subprocess.PIPE
+
+if subprocess.mswindows:
+ from win32file import ReadFile, WriteFile
+ from win32pipe import PeekNamedPipe
+ import msvcrt
+else:
+ import select
+ import fcntl
+
+ try: fcntl.F_GETFL
+ except AttributeError: fcntl.F_GETFL = 3
+
+ try: fcntl.F_SETFL
+ except AttributeError: fcntl.F_SETFL = 4
+
+class Popen(subprocess.Popen):
+ def recv(self, maxsize=None):
+ return self._recv('stdout', maxsize)
+
+ def recv_err(self, maxsize=None):
+ return self._recv('stderr', maxsize)
+
+ def send_recv(self, input='', maxsize=None):
+ return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
+
+ def get_conn_maxsize(self, which, maxsize):
+ if maxsize is None:
+ maxsize = 1024
+ elif maxsize < 1:
+ maxsize = 1
+ return getattr(self, which), maxsize
+
+ def _close(self, which):
+ getattr(self, which).close()
+ setattr(self, which, None)
+
+ if subprocess.mswindows:
+ def send(self, input):
+ if not self.stdin:
+ return None
+
+ try:
+ x = msvcrt.get_osfhandle(self.stdin.fileno())
+ (errCode, written) = WriteFile(x, input)
+ except ValueError:
+ return self._close('stdin')
+ except (subprocess.pywintypes.error, Exception), why:
+ if why[0] in (109, errno.ESHUTDOWN):
+ return self._close('stdin')
+ raise
+
+ return written
+
+ def _recv(self, which, maxsize):
+ conn, maxsize = self.get_conn_maxsize(which, maxsize)
+ if conn is None:
+ return None
+
+ try:
+ x = msvcrt.get_osfhandle(conn.fileno())
+ (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
+ if maxsize < nAvail:
+ nAvail = maxsize
+ if nAvail > 0:
+ (errCode, read) = ReadFile(x, nAvail, None)
+ except ValueError:
+ return self._close(which)
+ except (subprocess.pywintypes.error, Exception), why:
+ if why[0] in (109, errno.ESHUTDOWN):
+ return self._close(which)
+ raise
+
+ #if self.universal_newlines:
+ # read = self._translate_newlines(read)
+ return read
+
+ else:
+ def send(self, input):
+ if not self.stdin:
+ return None
+
+ if not select.select([], [self.stdin], [], 0)[1]:
+ return 0
+
+ try:
+ written = os.write(self.stdin.fileno(), input)
+ except OSError, why:
+ if why[0] == errno.EPIPE: #broken pipe
+ return self._close('stdin')
+ raise
+
+ return written
+
+ def _recv(self, which, maxsize):
+ conn, maxsize = self.get_conn_maxsize(which, maxsize)
+ if conn is None:
+ return None
+
+ try:
+ flags = fcntl.fcntl(conn, fcntl.F_GETFL)
+ except TypeError:
+ flags = None
+ else:
+ if not conn.closed:
+ fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+
+ try:
+ if not select.select([conn], [], [], 0)[0]:
+ return ''
+
+ r = conn.read(maxsize)
+ if not r:
+ return self._close(which)
+
+ #if self.universal_newlines:
+ # r = self._translate_newlines(r)
+ return r
+ finally:
+ if not conn.closed and not flags is None:
+ fcntl.fcntl(conn, fcntl.F_SETFL, flags)
+
+disconnect_message = "Other end disconnected!"
+
+def recv_some(p, t=.1, e=1, tr=5, stderr=0):
+ if tr < 1:
+ tr = 1
+ x = time.time()+t
+ y = []
+ r = ''
+ pr = p.recv
+ if stderr:
+ pr = p.recv_err
+ while time.time() < x or r:
+ r = pr()
+ if r is None:
+ if e:
+ raise Exception(disconnect_message)
+ else:
+ break
+ elif r:
+ y.append(r)
+ else:
+ time.sleep(max((x-time.time())/tr, 0))
+ return ''.join(y)
+
+# TODO(3.0: rewrite to use memoryview()
+def send_all(p, data):
+ while len(data):
+ sent = p.send(data)
+ if sent is None:
+ raise Exception(disconnect_message)
+ data = buffer(data, sent)
+
+
+
+try:
+ object
+except NameError:
+ class object:
+ pass
+
+
+
+class TestCmd(object):
+ """Class TestCmd
+ """
+
+ def __init__(self, description = None,
+ program = None,
+ interpreter = None,
+ workdir = None,
+ subdir = None,
+ verbose = None,
+ match = None,
+ diff = None,
+ combine = 0,
+ universal_newlines = 1):
+ self._cwd = os.getcwd()
+ self.description_set(description)
+ self.program_set(program)
+ self.interpreter_set(interpreter)
+ if verbose is None:
+ try:
+ verbose = max( 0, int(os.environ.get('TESTCMD_VERBOSE', 0)) )
+ except ValueError:
+ verbose = 0
+ self.verbose_set(verbose)
+ self.combine = combine
+ self.universal_newlines = universal_newlines
+ if match is not None:
+ self.match_function = match
+ else:
+ self.match_function = match_re
+ if diff is not None:
+ self.diff_function = diff
+ else:
+ try:
+ difflib
+ except NameError:
+ pass
+ else:
+ self.diff_function = simple_diff
+ #self.diff_function = difflib.context_diff
+ #self.diff_function = difflib.unified_diff
+ self._dirlist = []
+ self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0}
+ if os.environ.has_key('PRESERVE') and not os.environ['PRESERVE'] is '':
+ self._preserve['pass_test'] = os.environ['PRESERVE']
+ self._preserve['fail_test'] = os.environ['PRESERVE']
+ self._preserve['no_result'] = os.environ['PRESERVE']
+ else:
+ try:
+ self._preserve['pass_test'] = os.environ['PRESERVE_PASS']
+ except KeyError:
+ pass
+ try:
+ self._preserve['fail_test'] = os.environ['PRESERVE_FAIL']
+ except KeyError:
+ pass
+ try:
+ self._preserve['no_result'] = os.environ['PRESERVE_NO_RESULT']
+ except KeyError:
+ pass
+ self._stdout = []
+ self._stderr = []
+ self.status = None
+ self.condition = 'no_result'
+ self.workdir_set(workdir)
+ self.subdir(subdir)
+
+ def __del__(self):
+ self.cleanup()
+
+ def __repr__(self):
+ return "%x" % id(self)
+
+ banner_char = '='
+ banner_width = 80
+
+ def banner(self, s, width=None):
+ if width is None:
+ width = self.banner_width
+ return s + self.banner_char * (width - len(s))
+
+ if os.name == 'posix':
+
+ def escape(self, arg):
+ "escape shell special characters"
+ slash = '\\'
+ special = '"$'
+
+ arg = string.replace(arg, slash, slash+slash)
+ for c in special:
+ arg = string.replace(arg, c, slash+c)
+
+ if re_space.search(arg):
+ arg = '"' + arg + '"'
+ return arg
+
+ else:
+
+ # Windows does not allow special characters in file names
+ # anyway, so no need for an escape function, we will just quote
+ # the arg.
+ def escape(self, arg):
+ if re_space.search(arg):
+ arg = '"' + arg + '"'
+ return arg
+
+ def canonicalize(self, path):
+ if is_List(path):
+ path = apply(os.path.join, tuple(path))
+ if not os.path.isabs(path):
+ path = os.path.join(self.workdir, path)
+ return path
+
+ def chmod(self, path, mode):
+ """Changes permissions on the specified file or directory
+ path name."""
+ path = self.canonicalize(path)
+ os.chmod(path, mode)
+
+ def cleanup(self, condition = None):
+ """Removes any temporary working directories for the specified
+ TestCmd environment. If the environment variable PRESERVE was
+ set when the TestCmd environment was created, temporary working
+ directories are not removed. If any of the environment variables
+ PRESERVE_PASS, PRESERVE_FAIL, or PRESERVE_NO_RESULT were set
+ when the TestCmd environment was created, then temporary working
+ directories are not removed if the test passed, failed, or had
+ no result, respectively. Temporary working directories are also
+ preserved for conditions specified via the preserve method.
+
+ Typically, this method is not called directly, but is used when
+ the script exits to clean up temporary working directories as
+ appropriate for the exit status.
+ """
+ if not self._dirlist:
+ return
+ os.chdir(self._cwd)
+ self.workdir = None
+ if condition is None:
+ condition = self.condition
+ if self._preserve[condition]:
+ for dir in self._dirlist:
+ print "Preserved directory", dir
+ else:
+ list = self._dirlist[:]
+ list.reverse()
+ for dir in list:
+ self.writable(dir, 1)
+ shutil.rmtree(dir, ignore_errors = 1)
+ self._dirlist = []
+
+ try:
+ global _Cleanup
+ _Cleanup.remove(self)
+ except (AttributeError, ValueError):
+ pass
+
+ def command_args(self, program = None,
+ interpreter = None,
+ arguments = None):
+ if program:
+ if type(program) == type('') and not os.path.isabs(program):
+ program = os.path.join(self._cwd, program)
+ else:
+ program = self.program
+ if not interpreter:
+ interpreter = self.interpreter
+ if not type(program) in [type([]), type(())]:
+ program = [program]
+ cmd = list(program)
+ if interpreter:
+ if not type(interpreter) in [type([]), type(())]:
+ interpreter = [interpreter]
+ cmd = list(interpreter) + cmd
+ if arguments:
+ if type(arguments) == type(''):
+ arguments = string.split(arguments)
+ cmd.extend(arguments)
+ return cmd
+
+ def description_set(self, description):
+ """Set the description of the functionality being tested.
+ """
+ self.description = description
+
+ try:
+ difflib
+ except NameError:
+ def diff(self, a, b, name, *args, **kw):
+ print self.banner('Expected %s' % name)
+ print a
+ print self.banner('Actual %s' % name)
+ print b
+ else:
+ def diff(self, a, b, name, *args, **kw):
+ print self.banner(name)
+ args = (a.splitlines(), b.splitlines()) + args
+ lines = apply(self.diff_function, args, kw)
+ for l in lines:
+ print l
+
+ def fail_test(self, condition = 1, function = None, skip = 0):
+ """Cause the test to fail.
+ """
+ if not condition:
+ return
+ self.condition = 'fail_test'
+ fail_test(self = self,
+ condition = condition,
+ function = function,
+ skip = skip)
+
+ def interpreter_set(self, interpreter):
+ """Set the program to be used to interpret the program
+ under test as a script.
+ """
+ self.interpreter = interpreter
+
+ def match(self, lines, matches):
+ """Compare actual and expected file contents.
+ """
+ return self.match_function(lines, matches)
+
+ def match_exact(self, lines, matches):
+ """Compare actual and expected file contents.
+ """
+ return match_exact(lines, matches)
+
+ def match_re(self, lines, res):
+ """Compare actual and expected file contents.
+ """
+ return match_re(lines, res)
+
+ def match_re_dotall(self, lines, res):
+ """Compare actual and expected file contents.
+ """
+ return match_re_dotall(lines, res)
+
+ def no_result(self, condition = 1, function = None, skip = 0):
+ """Report that the test could not be run.
+ """
+ if not condition:
+ return
+ self.condition = 'no_result'
+ no_result(self = self,
+ condition = condition,
+ function = function,
+ skip = skip)
+
+ def pass_test(self, condition = 1, function = None):
+ """Cause the test to pass.
+ """
+ if not condition:
+ return
+ self.condition = 'pass_test'
+ pass_test(self = self, condition = condition, function = function)
+
+ def preserve(self, *conditions):
+ """Arrange for the temporary working directories for the
+ specified TestCmd environment to be preserved for one or more
+ conditions. If no conditions are specified, arranges for
+ the temporary working directories to be preserved for all
+ conditions.
+ """
+ if conditions is ():
+ conditions = ('pass_test', 'fail_test', 'no_result')
+ for cond in conditions:
+ self._preserve[cond] = 1
+
+ def program_set(self, program):
+ """Set the executable program or script to be tested.
+ """
+ if program and not os.path.isabs(program):
+ program = os.path.join(self._cwd, program)
+ self.program = program
+
+ def read(self, file, mode = 'rb'):
+ """Reads and returns the contents of the specified file name.
+ The file name may be a list, in which case the elements are
+ concatenated with the os.path.join() method. The file is
+ assumed to be under the temporary working directory unless it
+ is an absolute path name. The I/O mode for the file may
+ be specified; it must begin with an 'r'. The default is
+ 'rb' (binary read).
+ """
+ file = self.canonicalize(file)
+ if mode[0] != 'r':
+ raise ValueError, "mode must begin with 'r'"
+ with open(file, mode) as f:
+ result = f.read()
+ return result
+
+ def rmdir(self, dir):
+ """Removes the specified dir name.
+ The dir name may be a list, in which case the elements are
+ concatenated with the os.path.join() method. The dir is
+ assumed to be under the temporary working directory unless it
+ is an absolute path name.
+ The dir must be empty.
+ """
+ dir = self.canonicalize(dir)
+ os.rmdir(dir)
+
+ def start(self, program = None,
+ interpreter = None,
+ arguments = None,
+ universal_newlines = None,
+ **kw):
+ """
+ Starts a program or script for the test environment.
+
+ The specified program will have the original directory
+ prepended unless it is enclosed in a [list].
+ """
+ cmd = self.command_args(program, interpreter, arguments)
+ cmd_string = string.join(map(self.escape, cmd), ' ')
+ if self.verbose:
+ sys.stderr.write(cmd_string + "\n")
+ if universal_newlines is None:
+ universal_newlines = self.universal_newlines
+
+ # On Windows, if we make stdin a pipe when we plan to send
+ # no input, and the test program exits before
+ # Popen calls msvcrt.open_osfhandle, that call will fail.
+ # So don't use a pipe for stdin if we don't need one.
+ stdin = kw.get('stdin', None)
+ if stdin is not None:
+ stdin = subprocess.PIPE
+
+ combine = kw.get('combine', self.combine)
+ if combine:
+ stderr_value = subprocess.STDOUT
+ else:
+ stderr_value = subprocess.PIPE
+
+ return Popen(cmd,
+ stdin=stdin,
+ stdout=subprocess.PIPE,
+ stderr=stderr_value,
+ universal_newlines=universal_newlines)
+
+ def finish(self, popen, **kw):
+ """
+ Finishes and waits for the process being run under control of
+ the specified popen argument, recording the exit status,
+ standard output and error output.
+ """
+ popen.stdin.close()
+ self.status = popen.wait()
+ if not self.status:
+ self.status = 0
+ self._stdout.append(popen.stdout.read())
+ if popen.stderr:
+ stderr = popen.stderr.read()
+ else:
+ stderr = ''
+ self._stderr.append(stderr)
+
+ def run(self, program = None,
+ interpreter = None,
+ arguments = None,
+ chdir = None,
+ stdin = None,
+ universal_newlines = None):
+ """Runs a test of the program or script for the test
+ environment. Standard output and error output are saved for
+ future retrieval via the stdout() and stderr() methods.
+
+ The specified program will have the original directory
+ prepended unless it is enclosed in a [list].
+ """
+ if chdir:
+ oldcwd = os.getcwd()
+ if not os.path.isabs(chdir):
+ chdir = os.path.join(self.workpath(chdir))
+ if self.verbose:
+ sys.stderr.write("chdir(" + chdir + ")\n")
+ os.chdir(chdir)
+ p = self.start(program,
+ interpreter,
+ arguments,
+ universal_newlines,
+ stdin=stdin)
+ if stdin:
+ if is_List(stdin):
+ for line in stdin:
+ p.stdin.write(line)
+ else:
+ p.stdin.write(stdin)
+ p.stdin.close()
+
+ out = p.stdout.read()
+ if p.stderr is None:
+ err = ''
+ else:
+ err = p.stderr.read()
+ try:
+ close_output = p.close_output
+ except AttributeError:
+ p.stdout.close()
+ if not p.stderr is None:
+ p.stderr.close()
+ else:
+ close_output()
+
+ self._stdout.append(out)
+ self._stderr.append(err)
+
+ self.status = p.wait()
+ if not self.status:
+ self.status = 0
+
+ if chdir:
+ os.chdir(oldcwd)
+ if self.verbose >= 2:
+ write = sys.stdout.write
+ write('============ STATUS: %d\n' % self.status)
+ out = self.stdout()
+ if out or self.verbose >= 3:
+ write('============ BEGIN STDOUT (len=%d):\n' % len(out))
+ write(out)
+ write('============ END STDOUT\n')
+ err = self.stderr()
+ if err or self.verbose >= 3:
+ write('============ BEGIN STDERR (len=%d)\n' % len(err))
+ write(err)
+ write('============ END STDERR\n')
+
+ def sleep(self, seconds = default_sleep_seconds):
+ """Sleeps at least the specified number of seconds. If no
+ number is specified, sleeps at least the minimum number of
+ seconds necessary to advance file time stamps on the current
+ system. Sleeping more seconds is all right.
+ """
+ time.sleep(seconds)
+
+ def stderr(self, run = None):
+ """Returns the error output from the specified run number.
+ If there is no specified run number, then returns the error
+ output of the last run. If the run number is less than zero,
+ then returns the error output from that many runs back from the
+ current run.
+ """
+ if not run:
+ run = len(self._stderr)
+ elif run < 0:
+ run = len(self._stderr) + run
+ run = run - 1
+ return self._stderr[run]
+
+ def stdout(self, run = None):
+ """Returns the standard output from the specified run number.
+ If there is no specified run number, then returns the standard
+ output of the last run. If the run number is less than zero,
+ then returns the standard output from that many runs back from
+ the current run.
+ """
+ if not run:
+ run = len(self._stdout)
+ elif run < 0:
+ run = len(self._stdout) + run
+ run = run - 1
+ return self._stdout[run]
+
+ def subdir(self, *subdirs):
+ """Create new subdirectories under the temporary working
+ directory, one for each argument. An argument may be a list,
+ in which case the list elements are concatenated using the
+ os.path.join() method. Subdirectories multiple levels deep
+ must be created using a separate argument for each level:
+
+ test.subdir('sub', ['sub', 'dir'], ['sub', 'dir', 'ectory'])
+
+ Returns the number of subdirectories actually created.
+ """
+ count = 0
+ for sub in subdirs:
+ if sub is None:
+ continue
+ if is_List(sub):
+ sub = apply(os.path.join, tuple(sub))
+ new = os.path.join(self.workdir, sub)
+ try:
+ os.mkdir(new)
+ except OSError:
+ pass
+ else:
+ count = count + 1
+ return count
+
+ def symlink(self, target, link):
+ """Creates a symlink to the specified target.
+ The link name may be a list, in which case the elements are
+ concatenated with the os.path.join() method. The link is
+ assumed to be under the temporary working directory unless it
+ is an absolute path name. The target is *not* assumed to be
+ under the temporary working directory.
+ """
+ link = self.canonicalize(link)
+ os.symlink(target, link)
+
+ def tempdir(self, path=None):
+ """Creates a temporary directory.
+ A unique directory name is generated if no path name is specified.
+ The directory is created, and will be removed when the TestCmd
+ object is destroyed.
+ """
+ if path is None:
+ try:
+ path = tempfile.mktemp(prefix=tempfile.template)
+ except TypeError:
+ path = tempfile.mktemp()
+ os.mkdir(path)
+
+ # Symlinks in the path will report things
+ # differently from os.getcwd(), so chdir there
+ # and back to fetch the canonical path.
+ cwd = os.getcwd()
+ try:
+ os.chdir(path)
+ path = os.getcwd()
+ finally:
+ os.chdir(cwd)
+
+ # Uppercase the drive letter since the case of drive
+ # letters is pretty much random on win32:
+ drive,rest = os.path.splitdrive(path)
+ if drive:
+ path = string.upper(drive) + rest
+
+ #
+ self._dirlist.append(path)
+ global _Cleanup
+ try:
+ _Cleanup.index(self)
+ except ValueError:
+ _Cleanup.append(self)
+
+ return path
+
+ def touch(self, path, mtime=None):
+ """Updates the modification time on the specified file or
+ directory path name. The default is to update to the
+ current time if no explicit modification time is specified.
+ """
+ path = self.canonicalize(path)
+ atime = os.path.getatime(path)
+ if mtime is None:
+ mtime = time.time()
+ os.utime(path, (atime, mtime))
+
+ def unlink(self, file):
+ """Unlinks the specified file name.
+ The file name may be a list, in which case the elements are
+ concatenated with the os.path.join() method. The file is
+ assumed to be under the temporary working directory unless it
+ is an absolute path name.
+ """
+ file = self.canonicalize(file)
+ os.unlink(file)
+
+ def verbose_set(self, verbose):
+ """Set the verbose level.
+ """
+ self.verbose = verbose
+
+ def where_is(self, file, path=None, pathext=None):
+ """Find an executable file.
+ """
+ if is_List(file):
+ file = apply(os.path.join, tuple(file))
+ if not os.path.isabs(file):
+ file = where_is(file, path, pathext)
+ return file
+
+ def workdir_set(self, path):
+ """Creates a temporary working directory with the specified
+ path name. If the path is a null string (''), a unique
+ directory name is created.
+ """
+ if (path != None):
+ if path == '':
+ path = None
+ path = self.tempdir(path)
+ self.workdir = path
+
+ def workpath(self, *args):
+ """Returns the absolute path name to a subdirectory or file
+ within the current temporary working directory. Concatenates
+ the temporary working directory name with the specified
+ arguments using the os.path.join() method.
+ """
+ return apply(os.path.join, (self.workdir,) + tuple(args))
+
+ def readable(self, top, read=1):
+ """Make the specified directory tree readable (read == 1)
+ or not (read == None).
+
+ This method has no effect on Windows systems, which use a
+ completely different mechanism to control file readability.
+ """
+
+ if sys.platform == 'win32':
+ return
+
+ if read:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IREAD))
+ else:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IREAD))
+
+ if os.path.isfile(top):
+ # If it's a file, that's easy, just chmod it.
+ do_chmod(top)
+ elif read:
+ # It's a directory and we're trying to turn on read
+ # permission, so it's also pretty easy, just chmod the
+ # directory and then chmod every entry on our walk down the
+ # tree. Because os.path.walk() is top-down, we'll enable
+ # read permission on any directories that have it disabled
+ # before os.path.walk() tries to list their contents.
+ do_chmod(top)
+
+ def chmod_entries(arg, dirname, names, do_chmod=do_chmod):
+ for n in names:
+ do_chmod(os.path.join(dirname, n))
+
+ os.path.walk(top, chmod_entries, None)
+ else:
+ # It's a directory and we're trying to turn off read
+ # permission, which means we have to chmod the directoreis
+ # in the tree bottom-up, lest disabling read permission from
+ # the top down get in the way of being able to get at lower
+ # parts of the tree. But os.path.walk() visits things top
+ # down, so we just use an object to collect a list of all
+ # of the entries in the tree, reverse the list, and then
+ # chmod the reversed (bottom-up) list.
+ col = Collector(top)
+ os.path.walk(top, col, None)
+ col.entries.reverse()
+ for d in col.entries: do_chmod(d)
+
+ def writable(self, top, write=1):
+ """Make the specified directory tree writable (write == 1)
+ or not (write == None).
+ """
+
+ if sys.platform == 'win32':
+
+ if write:
+ def do_chmod(fname):
+ try: os.chmod(fname, stat.S_IWRITE)
+ except OSError: pass
+ else:
+ def do_chmod(fname):
+ try: os.chmod(fname, stat.S_IREAD)
+ except OSError: pass
+
+ else:
+
+ if write:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200))
+ else:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200))
+
+ if os.path.isfile(top):
+ do_chmod(top)
+ else:
+ col = Collector(top)
+ os.path.walk(top, col, None)
+ for d in col.entries: do_chmod(d)
+
+ def executable(self, top, execute=1):
+ """Make the specified directory tree executable (execute == 1)
+ or not (execute == None).
+
+ This method has no effect on Windows systems, which use a
+ completely different mechanism to control file executability.
+ """
+
+ if sys.platform == 'win32':
+ return
+
+ if execute:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IEXEC))
+ else:
+ def do_chmod(fname):
+ try: st = os.stat(fname)
+ except OSError: pass
+ else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IEXEC))
+
+ if os.path.isfile(top):
+ # If it's a file, that's easy, just chmod it.
+ do_chmod(top)
+ elif execute:
+ # It's a directory and we're trying to turn on execute
+ # permission, so it's also pretty easy, just chmod the
+ # directory and then chmod every entry on our walk down the
+ # tree. Because os.path.walk() is top-down, we'll enable
+ # execute permission on any directories that have it disabled
+ # before os.path.walk() tries to list their contents.
+ do_chmod(top)
+
+ def chmod_entries(arg, dirname, names, do_chmod=do_chmod):
+ for n in names:
+ do_chmod(os.path.join(dirname, n))
+
+ os.path.walk(top, chmod_entries, None)
+ else:
+ # It's a directory and we're trying to turn off execute
+ # permission, which means we have to chmod the directories
+ # in the tree bottom-up, lest disabling execute permission from
+ # the top down get in the way of being able to get at lower
+ # parts of the tree. But os.path.walk() visits things top
+ # down, so we just use an object to collect a list of all
+ # of the entries in the tree, reverse the list, and then
+ # chmod the reversed (bottom-up) list.
+ col = Collector(top)
+ os.path.walk(top, col, None)
+ col.entries.reverse()
+ for d in col.entries: do_chmod(d)
+
+ def write(self, file, content, mode = 'wb'):
+ """Writes the specified content text (second argument) to the
+ specified file name (first argument). The file name may be
+ a list, in which case the elements are concatenated with the
+ os.path.join() method. The file is created under the temporary
+ working directory. Any subdirectories in the path must already
+ exist. The I/O mode for the file may be specified; it must
+ begin with a 'w'. The default is 'wb' (binary write).
+ """
+ file = self.canonicalize(file)
+ if mode[0] != 'w':
+ raise ValueError, "mode must begin with 'w'"
+ with open(file, mode) as f:
+ f.write(content)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/tools/gyp/test/lib/TestCommon.py b/tools/gyp/test/lib/TestCommon.py
new file mode 100644
index 00000000000000..c54530c9802195
--- /dev/null
+++ b/tools/gyp/test/lib/TestCommon.py
@@ -0,0 +1,570 @@
+"""
+TestCommon.py: a testing framework for commands and scripts
+ with commonly useful error handling
+
+The TestCommon module provides a simple, high-level interface for writing
+tests of executable commands and scripts, especially commands and scripts
+that interact with the file system. All methods throw exceptions and
+exit on failure, with useful error messages. This makes a number of
+explicit checks unnecessary, making the test scripts themselves simpler
+to write and easier to read.
+
+The TestCommon class is a subclass of the TestCmd class. In essence,
+TestCommon is a wrapper that handles common TestCmd error conditions in
+useful ways. You can use TestCommon directly, or subclass it for your
+program and add additional (or override) methods to tailor it to your
+program's specific needs. Alternatively, the TestCommon class serves
+as a useful example of how to define your own TestCmd subclass.
+
+As a subclass of TestCmd, TestCommon provides access to all of the
+variables and methods from the TestCmd module. Consequently, you can
+use any variable or method documented in the TestCmd module without
+having to explicitly import TestCmd.
+
+A TestCommon environment object is created via the usual invocation:
+
+ import TestCommon
+ test = TestCommon.TestCommon()
+
+You can use all of the TestCmd keyword arguments when instantiating a
+TestCommon object; see the TestCmd documentation for details.
+
+Here is an overview of the methods and keyword arguments that are
+provided by the TestCommon class:
+
+ test.must_be_writable('file1', ['file2', ...])
+
+ test.must_contain('file', 'required text\n')
+
+ test.must_contain_all_lines(output, lines, ['title', find])
+
+ test.must_contain_any_line(output, lines, ['title', find])
+
+ test.must_exist('file1', ['file2', ...])
+
+ test.must_match('file', "expected contents\n")
+
+ test.must_not_be_writable('file1', ['file2', ...])
+
+ test.must_not_contain('file', 'banned text\n')
+
+ test.must_not_contain_any_line(output, lines, ['title', find])
+
+ test.must_not_exist('file1', ['file2', ...])
+
+ test.run(options = "options to be prepended to arguments",
+ stdout = "expected standard output from the program",
+ stderr = "expected error output from the program",
+ status = expected_status,
+ match = match_function)
+
+The TestCommon module also provides the following variables
+
+ TestCommon.python_executable
+ TestCommon.exe_suffix
+ TestCommon.obj_suffix
+ TestCommon.shobj_prefix
+ TestCommon.shobj_suffix
+ TestCommon.lib_prefix
+ TestCommon.lib_suffix
+ TestCommon.dll_prefix
+ TestCommon.dll_suffix
+
+"""
+
+# Copyright 2000-2010 Steven Knight
+# This module is free software, and you may redistribute it and/or modify
+# it under the same terms as Python itself, so long as this copyright message
+# and disclaimer are retained in their original form.
+#
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+#
+# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
+# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+__author__ = "Steven Knight "
+__revision__ = "TestCommon.py 0.37.D001 2010/01/11 16:55:50 knight"
+__version__ = "0.37"
+
+import copy
+import os
+import os.path
+import stat
+import string
+import sys
+import types
+import UserList
+
+from TestCmd import *
+from TestCmd import __all__
+
+__all__.extend([ 'TestCommon',
+ 'exe_suffix',
+ 'obj_suffix',
+ 'shobj_prefix',
+ 'shobj_suffix',
+ 'lib_prefix',
+ 'lib_suffix',
+ 'dll_prefix',
+ 'dll_suffix',
+ ])
+
+# Variables that describe the prefixes and suffixes on this system.
+if sys.platform == 'win32':
+ exe_suffix = '.exe'
+ obj_suffix = '.obj'
+ shobj_suffix = '.obj'
+ shobj_prefix = ''
+ lib_prefix = ''
+ lib_suffix = '.lib'
+ dll_prefix = ''
+ dll_suffix = '.dll'
+elif sys.platform == 'cygwin':
+ exe_suffix = '.exe'
+ obj_suffix = '.o'
+ shobj_suffix = '.os'
+ shobj_prefix = ''
+ lib_prefix = 'lib'
+ lib_suffix = '.a'
+ dll_prefix = ''
+ dll_suffix = '.dll'
+elif string.find(sys.platform, 'irix') != -1:
+ exe_suffix = ''
+ obj_suffix = '.o'
+ shobj_suffix = '.o'
+ shobj_prefix = ''
+ lib_prefix = 'lib'
+ lib_suffix = '.a'
+ dll_prefix = 'lib'
+ dll_suffix = '.so'
+elif string.find(sys.platform, 'darwin') != -1:
+ exe_suffix = ''
+ obj_suffix = '.o'
+ shobj_suffix = '.os'
+ shobj_prefix = ''
+ lib_prefix = 'lib'
+ lib_suffix = '.a'
+ dll_prefix = 'lib'
+ dll_suffix = '.dylib'
+elif string.find(sys.platform, 'sunos') != -1:
+ exe_suffix = ''
+ obj_suffix = '.o'
+ shobj_suffix = '.os'
+ shobj_prefix = 'so_'
+ lib_prefix = 'lib'
+ lib_suffix = '.a'
+ dll_prefix = 'lib'
+ dll_suffix = '.dylib'
+else:
+ exe_suffix = ''
+ obj_suffix = '.o'
+ shobj_suffix = '.os'
+ shobj_prefix = ''
+ lib_prefix = 'lib'
+ lib_suffix = '.a'
+ dll_prefix = 'lib'
+ dll_suffix = '.so'
+
+def is_List(e):
+ return type(e) is types.ListType \
+ or isinstance(e, UserList.UserList)
+
+def is_writable(f):
+ mode = os.stat(f)[stat.ST_MODE]
+ return mode & stat.S_IWUSR
+
+def separate_files(flist):
+ existing = []
+ missing = []
+ for f in flist:
+ if os.path.exists(f):
+ existing.append(f)
+ else:
+ missing.append(f)
+ return existing, missing
+
+def _failed(self, status = 0):
+ if self.status is None or status is None:
+ return None
+ try:
+ return _status(self) not in status
+ except TypeError:
+ # status wasn't an iterable
+ return _status(self) != status
+
+def _status(self):
+ return self.status
+
+class TestCommon(TestCmd):
+
+ # Additional methods from the Perl Test::Cmd::Common module
+ # that we may wish to add in the future:
+ #
+ # $test->subdir('subdir', ...);
+ #
+ # $test->copy('src_file', 'dst_file');
+
+ def __init__(self, **kw):
+ """Initialize a new TestCommon instance. This involves just
+ calling the base class initialization, and then changing directory
+ to the workdir.
+ """
+ apply(TestCmd.__init__, [self], kw)
+ os.chdir(self.workdir)
+
+ def must_be_writable(self, *files):
+ """Ensures that the specified file(s) exist and are writable.
+ An individual file can be specified as a list of directory names,
+ in which case the pathname will be constructed by concatenating
+ them. Exits FAILED if any of the files does not exist or is
+ not writable.
+ """
+ files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files)
+ existing, missing = separate_files(files)
+ unwritable = filter(lambda x, iw=is_writable: not iw(x), existing)
+ if missing:
+ print "Missing files: `%s'" % string.join(missing, "', `")
+ if unwritable:
+ print "Unwritable files: `%s'" % string.join(unwritable, "', `")
+ self.fail_test(missing + unwritable)
+
+ def must_contain(self, file, required, mode = 'rb'):
+ """Ensures that the specified file contains the required text.
+ """
+ file_contents = self.read(file, mode)
+ contains = (string.find(file_contents, required) != -1)
+ if not contains:
+ print "File `%s' does not contain required string." % file
+ print self.banner('Required string ')
+ print required
+ print self.banner('%s contents ' % file)
+ print file_contents
+ self.fail_test(not contains)
+
+ def must_contain_all_lines(self, output, lines, title=None, find=None):
+ """Ensures that the specified output string (first argument)
+ contains all of the specified lines (second argument).
+
+ An optional third argument can be used to describe the type
+ of output being searched, and only shows up in failure output.
+
+ An optional fourth argument can be used to supply a different
+ function, of the form "find(line, output), to use when searching
+ for lines in the output.
+ """
+ if find is None:
+ find = lambda o, l: string.find(o, l) != -1
+ missing = []
+ for line in lines:
+ if not find(output, line):
+ missing.append(line)
+
+ if missing:
+ if title is None:
+ title = 'output'
+ sys.stdout.write("Missing expected lines from %s:\n" % title)
+ for line in missing:
+ sys.stdout.write(' ' + repr(line) + '\n')
+ sys.stdout.write(self.banner(title + ' '))
+ sys.stdout.write(output)
+ self.fail_test()
+
+ def must_contain_any_line(self, output, lines, title=None, find=None):
+ """Ensures that the specified output string (first argument)
+ contains at least one of the specified lines (second argument).
+
+ An optional third argument can be used to describe the type
+ of output being searched, and only shows up in failure output.
+
+ An optional fourth argument can be used to supply a different
+ function, of the form "find(line, output), to use when searching
+ for lines in the output.
+ """
+ if find is None:
+ find = lambda o, l: string.find(o, l) != -1
+ for line in lines:
+ if find(output, line):
+ return
+
+ if title is None:
+ title = 'output'
+ sys.stdout.write("Missing any expected line from %s:\n" % title)
+ for line in lines:
+ sys.stdout.write(' ' + repr(line) + '\n')
+ sys.stdout.write(self.banner(title + ' '))
+ sys.stdout.write(output)
+ self.fail_test()
+
+ def must_contain_lines(self, lines, output, title=None):
+ # Deprecated; retain for backwards compatibility.
+ return self.must_contain_all_lines(output, lines, title)
+
+ def must_exist(self, *files):
+ """Ensures that the specified file(s) must exist. An individual
+ file be specified as a list of directory names, in which case the
+ pathname will be constructed by concatenating them. Exits FAILED
+ if any of the files does not exist.
+ """
+ files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files)
+ missing = filter(lambda x: not os.path.exists(x), files)
+ if missing:
+ print "Missing files: `%s'" % string.join(missing, "', `")
+ self.fail_test(missing)
+
+ def must_match(self, file, expect, mode = 'rb'):
+ """Matches the contents of the specified file (first argument)
+ against the expected contents (second argument). The expected
+ contents are a list of lines or a string which will be split
+ on newlines.
+ """
+ file_contents = self.read(file, mode)
+ try:
+ self.fail_test(not self.match(file_contents, expect))
+ except KeyboardInterrupt:
+ raise
+ except:
+ print "Unexpected contents of `%s'" % file
+ self.diff(expect, file_contents, 'contents ')
+ raise
+
+ def must_not_contain(self, file, banned, mode = 'rb'):
+ """Ensures that the specified file doesn't contain the banned text.
+ """
+ file_contents = self.read(file, mode)
+ contains = (string.find(file_contents, banned) != -1)
+ if contains:
+ print "File `%s' contains banned string." % file
+ print self.banner('Banned string ')
+ print banned
+ print self.banner('%s contents ' % file)
+ print file_contents
+ self.fail_test(contains)
+
+ def must_not_contain_any_line(self, output, lines, title=None, find=None):
+ """Ensures that the specified output string (first argument)
+ does not contain any of the specified lines (second argument).
+
+ An optional third argument can be used to describe the type
+ of output being searched, and only shows up in failure output.
+
+ An optional fourth argument can be used to supply a different
+ function, of the form "find(line, output), to use when searching
+ for lines in the output.
+ """
+ if find is None:
+ find = lambda o, l: string.find(o, l) != -1
+ unexpected = []
+ for line in lines:
+ if find(output, line):
+ unexpected.append(line)
+
+ if unexpected:
+ if title is None:
+ title = 'output'
+ sys.stdout.write("Unexpected lines in %s:\n" % title)
+ for line in unexpected:
+ sys.stdout.write(' ' + repr(line) + '\n')
+ sys.stdout.write(self.banner(title + ' '))
+ sys.stdout.write(output)
+ self.fail_test()
+
+ def must_not_contain_lines(self, lines, output, title=None):
+ return self.must_not_contain_any_line(output, lines, title)
+
+ def must_not_exist(self, *files):
+ """Ensures that the specified file(s) must not exist.
+ An individual file be specified as a list of directory names, in
+ which case the pathname will be constructed by concatenating them.
+ Exits FAILED if any of the files exists.
+ """
+ files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files)
+ existing = filter(os.path.exists, files)
+ if existing:
+ print "Unexpected files exist: `%s'" % string.join(existing, "', `")
+ self.fail_test(existing)
+
+ def must_not_be_writable(self, *files):
+ """Ensures that the specified file(s) exist and are not writable.
+ An individual file can be specified as a list of directory names,
+ in which case the pathname will be constructed by concatenating
+ them. Exits FAILED if any of the files does not exist or is
+ writable.
+ """
+ files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files)
+ existing, missing = separate_files(files)
+ writable = filter(is_writable, existing)
+ if missing:
+ print "Missing files: `%s'" % string.join(missing, "', `")
+ if writable:
+ print "Writable files: `%s'" % string.join(writable, "', `")
+ self.fail_test(missing + writable)
+
+ def _complete(self, actual_stdout, expected_stdout,
+ actual_stderr, expected_stderr, status, match):
+ """
+ Post-processes running a subcommand, checking for failure
+ status and displaying output appropriately.
+ """
+ if _failed(self, status):
+ expect = ''
+ if status != 0:
+ expect = " (expected %s)" % str(status)
+ print "%s returned %s%s" % (self.program, str(_status(self)), expect)
+ print self.banner('STDOUT ')
+ print actual_stdout
+ print self.banner('STDERR ')
+ print actual_stderr
+ self.fail_test()
+ if not expected_stdout is None and not match(actual_stdout, expected_stdout):
+ self.diff(expected_stdout, actual_stdout, 'STDOUT ')
+ if actual_stderr:
+ print self.banner('STDERR ')
+ print actual_stderr
+ self.fail_test()
+ if not expected_stderr is None and not match(actual_stderr, expected_stderr):
+ print self.banner('STDOUT ')
+ print actual_stdout
+ self.diff(expected_stderr, actual_stderr, 'STDERR ')
+ self.fail_test()
+
+ def start(self, program = None,
+ interpreter = None,
+ arguments = None,
+ universal_newlines = None,
+ **kw):
+ """
+ Starts a program or script for the test environment.
+
+ This handles the "options" keyword argument and exceptions.
+ """
+ options = kw.pop('options', None)
+ if options:
+ if arguments is None:
+ arguments = options
+ else:
+ arguments = options + " " + arguments
+
+ try:
+ return apply(TestCmd.start,
+ (self, program, interpreter, arguments, universal_newlines),
+ kw)
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ print self.banner('STDOUT ')
+ try:
+ print self.stdout()
+ except IndexError:
+ pass
+ print self.banner('STDERR ')
+ try:
+ print self.stderr()
+ except IndexError:
+ pass
+ cmd_args = self.command_args(program, interpreter, arguments)
+ sys.stderr.write('Exception trying to execute: %s\n' % cmd_args)
+ raise e
+
+ def finish(self, popen, stdout = None, stderr = '', status = 0, **kw):
+ """
+ Finishes and waits for the process being run under control of
+ the specified popen argument. Additional arguments are similar
+ to those of the run() method:
+
+ stdout The expected standard output from
+ the command. A value of None means
+ don't test standard output.
+
+ stderr The expected error output from
+ the command. A value of None means
+ don't test error output.
+
+ status The expected exit status from the
+ command. A value of None means don't
+ test exit status.
+ """
+ apply(TestCmd.finish, (self, popen,), kw)
+ match = kw.get('match', self.match)
+ self._complete(self.stdout(), stdout,
+ self.stderr(), stderr, status, match)
+
+ def run(self, options = None, arguments = None,
+ stdout = None, stderr = '', status = 0, **kw):
+ """Runs the program under test, checking that the test succeeded.
+
+ The arguments are the same as the base TestCmd.run() method,
+ with the addition of:
+
+ options Extra options that get appended to the beginning
+ of the arguments.
+
+ stdout The expected standard output from
+ the command. A value of None means
+ don't test standard output.
+
+ stderr The expected error output from
+ the command. A value of None means
+ don't test error output.
+
+ status The expected exit status from the
+ command. A value of None means don't
+ test exit status.
+
+ By default, this expects a successful exit (status = 0), does
+ not test standard output (stdout = None), and expects that error
+ output is empty (stderr = "").
+ """
+ if options:
+ if arguments is None:
+ arguments = options
+ else:
+ arguments = options + " " + arguments
+ kw['arguments'] = arguments
+ match = kw.pop('match', self.match)
+ apply(TestCmd.run, [self], kw)
+ self._complete(self.stdout(), stdout,
+ self.stderr(), stderr, status, match)
+
+ def skip_test(self, message="Skipping test.\n"):
+ """Skips a test.
+
+ Proper test-skipping behavior is dependent on the external
+ TESTCOMMON_PASS_SKIPS environment variable. If set, we treat
+ the skip as a PASS (exit 0), and otherwise treat it as NO RESULT.
+ In either case, we print the specified message as an indication
+ that the substance of the test was skipped.
+
+ (This was originally added to support development under Aegis.
+ Technically, skipping a test is a NO RESULT, but Aegis would
+ treat that as a test failure and prevent the change from going to
+ the next step. Since we ddn't want to force anyone using Aegis
+ to have to install absolutely every tool used by the tests, we
+ would actually report to Aegis that a skipped test has PASSED
+ so that the workflow isn't held up.)
+ """
+ if message:
+ sys.stdout.write(message)
+ sys.stdout.flush()
+ pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS')
+ if pass_skips in [None, 0, '0']:
+ # skip=1 means skip this function when showing where this
+ # result came from. They only care about the line where the
+ # script called test.skip_test(), not the line number where
+ # we call test.no_result().
+ self.no_result(skip=1)
+ else:
+ # We're under the development directory for this change,
+ # so this is an Aegis invocation; pass the test (exit 0).
+ self.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/tools/gyp/test/lib/TestGyp.py b/tools/gyp/test/lib/TestGyp.py
new file mode 100644
index 00000000000000..e085b7b9a8f074
--- /dev/null
+++ b/tools/gyp/test/lib/TestGyp.py
@@ -0,0 +1,1420 @@
+# 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.
+
+"""
+TestGyp.py: a testing framework for GYP integration tests.
+"""
+
+import collections
+from contextlib import contextmanager
+import itertools
+import os
+import random
+import re
+import shutil
+import stat
+import subprocess
+import sys
+import tempfile
+import time
+
+import TestCmd
+import TestCommon
+from TestCommon import __all__
+
+__all__.extend([
+ 'TestGyp',
+])
+
+
+def remove_debug_line_numbers(contents):
+ """Function to remove the line numbers from the debug output
+ of gyp and thus reduce the extreme fragility of the stdout
+ comparison tests.
+ """
+ lines = contents.splitlines()
+ # split each line on ":"
+ lines = [l.split(":", 3) for l in lines]
+ # join each line back together while ignoring the
+ # 3rd column which is the line number
+ lines = [len(l) > 3 and ":".join(l[3:]) or l for l in lines]
+ return "\n".join(lines)
+
+
+def match_modulo_line_numbers(contents_a, contents_b):
+ """File contents matcher that ignores line numbers."""
+ contents_a = remove_debug_line_numbers(contents_a)
+ contents_b = remove_debug_line_numbers(contents_b)
+ return TestCommon.match_exact(contents_a, contents_b)
+
+
+@contextmanager
+def LocalEnv(local_env):
+ """Context manager to provide a local OS environment."""
+ old_env = os.environ.copy()
+ os.environ.update(local_env)
+ try:
+ yield
+ finally:
+ os.environ.clear()
+ os.environ.update(old_env)
+
+
+class TestGypBase(TestCommon.TestCommon):
+ """
+ Class for controlling end-to-end tests of gyp generators.
+
+ Instantiating this class will create a temporary directory and
+ arrange for its destruction (via the TestCmd superclass) and
+ copy all of the non-gyptest files in the directory hierarchy of the
+ executing script.
+
+ The default behavior is to test the 'gyp' or 'gyp.bat' file in the
+ current directory. An alternative may be specified explicitly on
+ instantiation, or by setting the TESTGYP_GYP environment variable.
+
+ This class should be subclassed for each supported gyp generator
+ (format). Various abstract methods below define calling signatures
+ used by the test scripts to invoke builds on the generated build
+ configuration and to run executables generated by those builds.
+ """
+
+ formats = []
+ build_tool = None
+ build_tool_list = []
+
+ _exe = TestCommon.exe_suffix
+ _obj = TestCommon.obj_suffix
+ shobj_ = TestCommon.shobj_prefix
+ _shobj = TestCommon.shobj_suffix
+ lib_ = TestCommon.lib_prefix
+ _lib = TestCommon.lib_suffix
+ dll_ = TestCommon.dll_prefix
+ _dll = TestCommon.dll_suffix
+
+ # Constants to represent different targets.
+ ALL = '__all__'
+ DEFAULT = '__default__'
+
+ # Constants for different target types.
+ EXECUTABLE = '__executable__'
+ STATIC_LIB = '__static_lib__'
+ SHARED_LIB = '__shared_lib__'
+
+ def __init__(self, gyp=None, *args, **kw):
+ self.origin_cwd = os.path.abspath(os.path.dirname(sys.argv[0]))
+ self.extra_args = sys.argv[1:]
+
+ if not gyp:
+ gyp = os.environ.get('TESTGYP_GYP')
+ if not gyp:
+ if sys.platform == 'win32':
+ gyp = 'gyp.bat'
+ else:
+ gyp = 'gyp'
+ self.gyp = os.path.abspath(gyp)
+ self.no_parallel = False
+
+ self.formats = [self.format]
+
+ self.initialize_build_tool()
+
+ kw.setdefault('match', TestCommon.match_exact)
+
+ # Put test output in out/testworkarea by default.
+ # Use temporary names so there are no collisions.
+ workdir = os.path.join('out', kw.get('workdir', 'testworkarea'))
+ # Create work area if it doesn't already exist.
+ if not os.path.isdir(workdir):
+ os.makedirs(workdir)
+
+ kw['workdir'] = tempfile.mktemp(prefix='testgyp.', dir=workdir)
+
+ formats = kw.pop('formats', [])
+
+ super(TestGypBase, self).__init__(*args, **kw)
+
+ real_format = self.format.split('-')[-1]
+ excluded_formats = set([f for f in formats if f[0] == '!'])
+ included_formats = set(formats) - excluded_formats
+ if ('!'+real_format in excluded_formats or
+ included_formats and real_format not in included_formats):
+ msg = 'Invalid test for %r format; skipping test.\n'
+ self.skip_test(msg % self.format)
+
+ self.copy_test_configuration(self.origin_cwd, self.workdir)
+ self.set_configuration(None)
+
+ # Set $HOME so that gyp doesn't read the user's actual
+ # ~/.gyp/include.gypi file, which may contain variables
+ # and other settings that would change the output.
+ os.environ['HOME'] = self.workpath()
+ # Clear $GYP_DEFINES for the same reason.
+ if 'GYP_DEFINES' in os.environ:
+ del os.environ['GYP_DEFINES']
+ # Override the user's language settings, which could
+ # otherwise make the output vary from what is expected.
+ os.environ['LC_ALL'] = 'C'
+
+ def built_file_must_exist(self, name, type=None, **kw):
+ """
+ Fails the test if the specified built file name does not exist.
+ """
+ return self.must_exist(self.built_file_path(name, type, **kw))
+
+ def built_file_must_not_exist(self, name, type=None, **kw):
+ """
+ Fails the test if the specified built file name exists.
+ """
+ return self.must_not_exist(self.built_file_path(name, type, **kw))
+
+ def built_file_must_match(self, name, contents, **kw):
+ """
+ Fails the test if the contents of the specified built file name
+ do not match the specified contents.
+ """
+ return self.must_match(self.built_file_path(name, **kw), contents)
+
+ def built_file_must_not_match(self, name, contents, **kw):
+ """
+ Fails the test if the contents of the specified built file name
+ match the specified contents.
+ """
+ return self.must_not_match(self.built_file_path(name, **kw), contents)
+
+ def built_file_must_not_contain(self, name, contents, **kw):
+ """
+ Fails the test if the specified built file name contains the specified
+ contents.
+ """
+ return self.must_not_contain(self.built_file_path(name, **kw), contents)
+
+ def copy_test_configuration(self, source_dir, dest_dir):
+ """
+ Copies the test configuration from the specified source_dir
+ (the directory in which the test script lives) to the
+ specified dest_dir (a temporary working directory).
+
+ This ignores all files and directories that begin with
+ the string 'gyptest', and all '.svn' subdirectories.
+ """
+ for root, dirs, files in os.walk(source_dir):
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ dirs = [ d for d in dirs if not d.startswith('gyptest') ]
+ files = [ f for f in files if not f.startswith('gyptest') ]
+ for dirname in dirs:
+ source = os.path.join(root, dirname)
+ destination = source.replace(source_dir, dest_dir)
+ os.mkdir(destination)
+ if sys.platform != 'win32':
+ shutil.copystat(source, destination)
+ for filename in files:
+ source = os.path.join(root, filename)
+ destination = source.replace(source_dir, dest_dir)
+ shutil.copy2(source, destination)
+
+ def initialize_build_tool(self):
+ """
+ Initializes the .build_tool attribute.
+
+ Searches the .build_tool_list for an executable name on the user's
+ $PATH. The first tool on the list is used as-is if nothing is found
+ on the current $PATH.
+ """
+ for build_tool in self.build_tool_list:
+ if not build_tool:
+ continue
+ if os.path.isabs(build_tool):
+ self.build_tool = build_tool
+ return
+ build_tool = self.where_is(build_tool)
+ if build_tool:
+ self.build_tool = build_tool
+ return
+
+ if self.build_tool_list:
+ self.build_tool = self.build_tool_list[0]
+
+ def relocate(self, source, destination):
+ """
+ Renames (relocates) the specified source (usually a directory)
+ to the specified destination, creating the destination directory
+ first if necessary.
+
+ Note: Don't use this as a generic "rename" operation. In the
+ future, "relocating" parts of a GYP tree may affect the state of
+ the test to modify the behavior of later method calls.
+ """
+ destination_dir = os.path.dirname(destination)
+ if not os.path.exists(destination_dir):
+ self.subdir(destination_dir)
+ os.rename(source, destination)
+
+ def report_not_up_to_date(self):
+ """
+ Reports that a build is not up-to-date.
+
+ This provides common reporting for formats that have complicated
+ conditions for checking whether a build is up-to-date. Formats
+ that expect exact output from the command (make) can
+ just set stdout= when they call the run_build() method.
+ """
+ print "Build is not up-to-date:"
+ print self.banner('STDOUT ')
+ print self.stdout()
+ stderr = self.stderr()
+ if stderr:
+ print self.banner('STDERR ')
+ print stderr
+
+ def run_gyp(self, gyp_file, *args, **kw):
+ """
+ Runs gyp against the specified gyp_file with the specified args.
+ """
+
+ # When running gyp, and comparing its output we use a comparitor
+ # that ignores the line numbers that gyp logs in its debug output.
+ if kw.pop('ignore_line_numbers', False):
+ kw.setdefault('match', match_modulo_line_numbers)
+
+ # TODO: --depth=. works around Chromium-specific tree climbing.
+ depth = kw.pop('depth', '.')
+ run_args = ['--depth='+depth]
+ run_args.extend(['--format='+f for f in self.formats]);
+ run_args.append(gyp_file)
+ if self.no_parallel:
+ run_args += ['--no-parallel']
+ # TODO: if extra_args contains a '--build' flag
+ # we really want that to only apply to the last format (self.format).
+ run_args.extend(self.extra_args)
+ # Default xcode_ninja_target_pattern to ^.*$ to fix xcode-ninja tests
+ xcode_ninja_target_pattern = kw.pop('xcode_ninja_target_pattern', '.*')
+ run_args.extend(
+ ['-G', 'xcode_ninja_target_pattern=%s' % xcode_ninja_target_pattern])
+ run_args.extend(args)
+ return self.run(program=self.gyp, arguments=run_args, **kw)
+
+ def run(self, *args, **kw):
+ """
+ Executes a program by calling the superclass .run() method.
+
+ This exists to provide a common place to filter out keyword
+ arguments implemented in this layer, without having to update
+ the tool-specific subclasses or clutter the tests themselves
+ with platform-specific code.
+ """
+ if kw.has_key('SYMROOT'):
+ del kw['SYMROOT']
+ super(TestGypBase, self).run(*args, **kw)
+
+ def set_configuration(self, configuration):
+ """
+ Sets the configuration, to be used for invoking the build
+ tool and testing potential built output.
+ """
+ self.configuration = configuration
+
+ def configuration_dirname(self):
+ if self.configuration:
+ return self.configuration.split('|')[0]
+ else:
+ return 'Default'
+
+ def configuration_buildname(self):
+ if self.configuration:
+ return self.configuration
+ else:
+ return 'Default'
+
+ #
+ # Abstract methods to be defined by format-specific subclasses.
+ #
+
+ def build(self, gyp_file, target=None, **kw):
+ """
+ Runs a build of the specified target against the configuration
+ generated from the specified gyp_file.
+
+ A 'target' argument of None or the special value TestGyp.DEFAULT
+ specifies the default argument for the underlying build tool.
+ A 'target' argument of TestGyp.ALL specifies the 'all' target
+ (if any) of the underlying build tool.
+ """
+ raise NotImplementedError
+
+ def built_file_path(self, name, type=None, **kw):
+ """
+ Returns a path to the specified file name, of the specified type.
+ """
+ raise NotImplementedError
+
+ def built_file_basename(self, name, type=None, **kw):
+ """
+ Returns the base name of the specified file name, of the specified type.
+
+ A bare=True keyword argument specifies that prefixes and suffixes shouldn't
+ be applied.
+ """
+ if not kw.get('bare'):
+ if type == self.EXECUTABLE:
+ name = name + self._exe
+ elif type == self.STATIC_LIB:
+ name = self.lib_ + name + self._lib
+ elif type == self.SHARED_LIB:
+ name = self.dll_ + name + self._dll
+ return name
+
+ def run_built_executable(self, name, *args, **kw):
+ """
+ Runs an executable program built from a gyp-generated configuration.
+
+ The specified name should be independent of any particular generator.
+ Subclasses should find the output executable in the appropriate
+ output build directory, tack on any necessary executable suffix, etc.
+ """
+ raise NotImplementedError
+
+ def up_to_date(self, gyp_file, target=None, **kw):
+ """
+ Verifies that a build of the specified target is up to date.
+
+ The subclass should implement this by calling build()
+ (or a reasonable equivalent), checking whatever conditions
+ will tell it the build was an "up to date" null build, and
+ failing if it isn't.
+ """
+ raise NotImplementedError
+
+
+class TestGypGypd(TestGypBase):
+ """
+ Subclass for testing the GYP 'gypd' generator (spit out the
+ internal data structure as pretty-printed Python).
+ """
+ format = 'gypd'
+ def __init__(self, gyp=None, *args, **kw):
+ super(TestGypGypd, self).__init__(*args, **kw)
+ # gypd implies the use of 'golden' files, so parallelizing conflicts as it
+ # causes ordering changes.
+ self.no_parallel = True
+
+
+class TestGypCustom(TestGypBase):
+ """
+ Subclass for testing the GYP with custom generator
+ """
+
+ def __init__(self, gyp=None, *args, **kw):
+ self.format = kw.pop("format")
+ super(TestGypCustom, self).__init__(*args, **kw)
+
+
+class TestGypAndroid(TestGypBase):
+ """
+ Subclass for testing the GYP Android makefile generator. Note that
+ build/envsetup.sh and lunch must have been run before running tests.
+ """
+ format = 'android'
+
+ # Note that we can't use mmm as the build tool because ...
+ # - it builds all targets, whereas we need to pass a target
+ # - it is a function, whereas the test runner assumes the build tool is a file
+ # Instead we use make and duplicate the logic from mmm.
+ build_tool_list = ['make']
+
+ # We use our custom target 'gyp_all_modules', as opposed to the 'all_modules'
+ # target used by mmm, to build only those targets which are part of the gyp
+ # target 'all'.
+ ALL = 'gyp_all_modules'
+
+ def __init__(self, gyp=None, *args, **kw):
+ # Android requires build and test output to be inside its source tree.
+ # We use the following working directory for the test's source, but the
+ # test's build output still goes to $ANDROID_PRODUCT_OUT.
+ # Note that some tests explicitly set format='gypd' to invoke the gypd
+ # backend. This writes to the source tree, but there's no way around this.
+ kw['workdir'] = os.path.join('/tmp', 'gyptest',
+ kw.get('workdir', 'testworkarea'))
+ # We need to remove all gyp outputs from out/. Ths is because some tests
+ # don't have rules to regenerate output, so they will simply re-use stale
+ # output if present. Since the test working directory gets regenerated for
+ # each test run, this can confuse things.
+ # We don't have a list of build outputs because we don't know which
+ # dependent targets were built. Instead we delete all gyp-generated output.
+ # This may be excessive, but should be safe.
+ out_dir = os.environ['ANDROID_PRODUCT_OUT']
+ obj_dir = os.path.join(out_dir, 'obj')
+ shutil.rmtree(os.path.join(obj_dir, 'GYP'), ignore_errors = True)
+ for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']:
+ for d in os.listdir(os.path.join(obj_dir, x)):
+ if d.endswith('_gyp_intermediates'):
+ shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True)
+ for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]:
+ for d in os.listdir(os.path.join(out_dir, x)):
+ if d.endswith('_gyp.so'):
+ os.remove(os.path.join(out_dir, x, d))
+
+ super(TestGypAndroid, self).__init__(*args, **kw)
+ self._adb_path = os.path.join(os.environ['ANDROID_HOST_OUT'], 'bin', 'adb')
+ self._device_serial = None
+ adb_devices_out = self._call_adb(['devices'])
+ devices = [l.split()[0] for l in adb_devices_out.splitlines()[1:-1]
+ if l.split()[1] == 'device']
+ if len(devices) == 0:
+ self._device_serial = None
+ else:
+ if len(devices) > 1:
+ self._device_serial = random.choice(devices)
+ else:
+ self._device_serial = devices[0]
+ self._call_adb(['root'])
+ self._to_install = set()
+
+ def target_name(self, target):
+ if target == self.ALL:
+ return self.ALL
+ # The default target is 'droid'. However, we want to use our special target
+ # to build only the gyp target 'all'.
+ if target in (None, self.DEFAULT):
+ return self.ALL
+ return target
+
+ _INSTALLABLE_PREFIX = 'Install: '
+
+ def build(self, gyp_file, target=None, **kw):
+ """
+ Runs a build using the Android makefiles generated from the specified
+ gyp_file. This logic is taken from Android's mmm.
+ """
+ arguments = kw.get('arguments', [])[:]
+ arguments.append(self.target_name(target))
+ arguments.append('-C')
+ arguments.append(os.environ['ANDROID_BUILD_TOP'])
+ kw['arguments'] = arguments
+ chdir = kw.get('chdir', '')
+ makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk')
+ os.environ['ONE_SHOT_MAKEFILE'] = makefile
+ result = self.run(program=self.build_tool, **kw)
+ for l in self.stdout().splitlines():
+ if l.startswith(TestGypAndroid._INSTALLABLE_PREFIX):
+ self._to_install.add(os.path.abspath(os.path.join(
+ os.environ['ANDROID_BUILD_TOP'],
+ l[len(TestGypAndroid._INSTALLABLE_PREFIX):])))
+ del os.environ['ONE_SHOT_MAKEFILE']
+ return result
+
+ def android_module(self, group, name, subdir):
+ if subdir:
+ name = '%s_%s' % (subdir, name)
+ if group == 'SHARED_LIBRARIES':
+ name = 'lib_%s' % name
+ return '%s_gyp' % name
+
+ def intermediates_dir(self, group, module_name):
+ return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group,
+ '%s_intermediates' % module_name)
+
+ def built_file_path(self, name, type=None, **kw):
+ """
+ Returns a path to the specified file name, of the specified type,
+ as built by Android. Note that we don't support the configuration
+ parameter.
+ """
+ # Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from
+ # the Android build system.
+ if type == None or type == self.EXECUTABLE:
+ return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP',
+ 'shared_intermediates', name)
+ subdir = kw.get('subdir')
+ if type == self.STATIC_LIB:
+ group = 'STATIC_LIBRARIES'
+ module_name = self.android_module(group, name, subdir)
+ return os.path.join(self.intermediates_dir(group, module_name),
+ '%s.a' % module_name)
+ if type == self.SHARED_LIB:
+ group = 'SHARED_LIBRARIES'
+ module_name = self.android_module(group, name, subdir)
+ return os.path.join(self.intermediates_dir(group, module_name), 'LINKED',
+ '%s.so' % module_name)
+ assert False, 'Unhandled type'
+
+ def _adb_failure(self, command, msg, stdout, stderr):
+ """ Reports a failed adb command and fails the containing test.
+
+ Args:
+ command: The adb command that failed.
+ msg: The error description.
+ stdout: The standard output.
+ stderr: The standard error.
+ """
+ print '%s failed%s' % (' '.join(command), ': %s' % msg if msg else '')
+ print self.banner('STDOUT ')
+ stdout.seek(0)
+ print stdout.read()
+ print self.banner('STDERR ')
+ stderr.seek(0)
+ print stderr.read()
+ self.fail_test()
+
+ def _call_adb(self, command, timeout=15, retry=3):
+ """ Calls the provided adb command.
+
+ If the command fails, the test fails.
+
+ Args:
+ command: The adb command to call.
+ Returns:
+ The command's output.
+ """
+ with tempfile.TemporaryFile(bufsize=0) as adb_out:
+ with tempfile.TemporaryFile(bufsize=0) as adb_err:
+ adb_command = [self._adb_path]
+ if self._device_serial:
+ adb_command += ['-s', self._device_serial]
+ is_shell = (command[0] == 'shell')
+ if is_shell:
+ command = [command[0], '%s; echo "\n$?";' % ' '.join(command[1:])]
+ adb_command += command
+
+ for attempt in xrange(1, retry + 1):
+ adb_out.seek(0)
+ adb_out.truncate(0)
+ adb_err.seek(0)
+ adb_err.truncate(0)
+ proc = subprocess.Popen(adb_command, stdout=adb_out, stderr=adb_err)
+ deadline = time.time() + timeout
+ timed_out = False
+ while proc.poll() is None and not timed_out:
+ time.sleep(1)
+ timed_out = time.time() > deadline
+ if timed_out:
+ print 'Timeout for command %s (attempt %d of %s)' % (
+ adb_command, attempt, retry)
+ try:
+ proc.kill()
+ except:
+ pass
+ else:
+ break
+
+ if proc.returncode != 0: # returncode is None in the case of a timeout.
+ self._adb_failure(
+ adb_command, 'retcode=%s' % proc.returncode, adb_out, adb_err)
+ return
+
+ adb_out.seek(0)
+ output = adb_out.read()
+ if is_shell:
+ output = output.splitlines(True)
+ try:
+ output[-2] = output[-2].rstrip('\r\n')
+ output, rc = (''.join(output[:-1]), int(output[-1]))
+ except ValueError:
+ self._adb_failure(adb_command, 'unexpected output format',
+ adb_out, adb_err)
+ if rc != 0:
+ self._adb_failure(adb_command, 'exited with %d' % rc, adb_out,
+ adb_err)
+ return output
+
+ def run_built_executable(self, name, *args, **kw):
+ """
+ Runs an executable program built from a gyp-generated configuration.
+ """
+ match = kw.pop('match', self.match)
+
+ executable_file = self.built_file_path(name, type=self.EXECUTABLE, **kw)
+ if executable_file not in self._to_install:
+ self.fail_test()
+
+ if not self._device_serial:
+ self.skip_test(message='No devices attached.\n')
+
+ storage = self._call_adb(['shell', 'echo', '$ANDROID_DATA']).strip()
+ if not len(storage):
+ self.fail_test()
+
+ installed = set()
+ try:
+ for i in self._to_install:
+ a = os.path.abspath(
+ os.path.join(os.environ['ANDROID_BUILD_TOP'], i))
+ dest = '%s/%s' % (storage, os.path.basename(a))
+ self._call_adb(['push', os.path.abspath(a), dest])
+ installed.add(dest)
+ if i == executable_file:
+ device_executable = dest
+ self._call_adb(['shell', 'chmod', '755', device_executable])
+
+ out = self._call_adb(
+ ['shell', 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%s' % storage,
+ device_executable],
+ timeout=60,
+ retry=1)
+ out = out.replace('\r\n', '\n')
+ self._complete(out, kw.pop('stdout', None), None, None, None, match)
+ finally:
+ if len(installed):
+ self._call_adb(['shell', 'rm'] + list(installed))
+
+ def match_single_line(self, lines = None, expected_line = None):
+ """
+ Checks that specified line appears in the text.
+ """
+ for line in lines.split('\n'):
+ if line == expected_line:
+ return 1
+ return
+
+ def up_to_date(self, gyp_file, target=None, **kw):
+ """
+ Verifies that a build of the specified target is up to date.
+ """
+ kw['stdout'] = ("make: Nothing to be done for `%s'." %
+ self.target_name(target))
+
+ # We need to supply a custom matcher, since we don't want to depend on the
+ # exact stdout string.
+ kw['match'] = self.match_single_line
+ return self.build(gyp_file, target, **kw)
+
+
+class TestGypCMake(TestGypBase):
+ """
+ Subclass for testing the GYP CMake generator, using cmake's ninja backend.
+ """
+ format = 'cmake'
+ build_tool_list = ['cmake']
+ ALL = 'all'
+
+ def cmake_build(self, gyp_file, target=None, **kw):
+ arguments = kw.get('arguments', [])[:]
+
+ self.build_tool_list = ['cmake']
+ self.initialize_build_tool()
+
+ chdir = os.path.join(kw.get('chdir', '.'),
+ 'out',
+ self.configuration_dirname())
+ kw['chdir'] = chdir
+
+ arguments.append('-G')
+ arguments.append('Ninja')
+
+ kw['arguments'] = arguments
+
+ stderr = kw.get('stderr', None)
+ if stderr:
+ kw['stderr'] = stderr.split('$$$')[0]
+
+ self.run(program=self.build_tool, **kw)
+
+ def ninja_build(self, gyp_file, target=None, **kw):
+ arguments = kw.get('arguments', [])[:]
+
+ self.build_tool_list = ['ninja']
+ self.initialize_build_tool()
+
+ # Add a -C output/path to the command line.
+ arguments.append('-C')
+ arguments.append(os.path.join('out', self.configuration_dirname()))
+
+ if target not in (None, self.DEFAULT):
+ arguments.append(target)
+
+ kw['arguments'] = arguments
+
+ stderr = kw.get('stderr', None)
+ if stderr:
+ stderrs = stderr.split('$$$')
+ kw['stderr'] = stderrs[1] if len(stderrs) > 1 else ''
+
+ return self.run(program=self.build_tool, **kw)
+
+ def build(self, gyp_file, target=None, status=0, **kw):
+ # Two tools must be run to build, cmake and the ninja.
+ # Allow cmake to succeed when the overall expectation is to fail.
+ if status is None:
+ kw['status'] = None
+ else:
+ if not isinstance(status, collections.Iterable): status = (status,)
+ kw['status'] = list(itertools.chain((0,), status))
+ self.cmake_build(gyp_file, target, **kw)
+ kw['status'] = status
+ self.ninja_build(gyp_file, target, **kw)
+
+ def run_built_executable(self, name, *args, **kw):
+ # 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':
+ configuration = self.configuration_dirname()
+ os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration)
+ return self.run(program=program, *args, **kw)
+
+ def built_file_path(self, name, type=None, **kw):
+ result = []
+ chdir = kw.get('chdir')
+ if chdir:
+ result.append(chdir)
+ result.append('out')
+ result.append(self.configuration_dirname())
+ if type == self.STATIC_LIB:
+ if sys.platform != 'darwin':
+ result.append('obj.target')
+ elif type == self.SHARED_LIB:
+ if sys.platform != 'darwin' and sys.platform != 'win32':
+ result.append('lib.target')
+ subdir = kw.get('subdir')
+ if subdir and type != self.SHARED_LIB:
+ result.append(subdir)
+ result.append(self.built_file_basename(name, type, **kw))
+ return self.workpath(*result)
+
+ def up_to_date(self, gyp_file, target=None, **kw):
+ result = self.ninja_build(gyp_file, target, **kw)
+ if not result:
+ stdout = self.stdout()
+ if 'ninja: no work to do' not in stdout:
+ self.report_not_up_to_date()
+ self.fail_test()
+ return result
+
+
+class TestGypMake(TestGypBase):
+ """
+ Subclass for testing the GYP Make generator.
+ """
+ format = 'make'
+ build_tool_list = ['make']
+ ALL = 'all'
+ def build(self, gyp_file, target=None, **kw):
+ """
+ Runs a Make build using the Makefiles generated from the specified
+ gyp_file.
+ """
+ arguments = kw.get('arguments', [])[:]
+ if self.configuration:
+ arguments.append('BUILDTYPE=' + self.configuration)
+ if target not in (None, self.DEFAULT):
+ arguments.append(target)
+ # Sub-directory builds provide per-gyp Makefiles (i.e.
+ # Makefile.gyp_filename), so use that if there is no Makefile.
+ chdir = kw.get('chdir', '')
+ if not os.path.exists(os.path.join(chdir, 'Makefile')):
+ print "NO Makefile in " + os.path.join(chdir, 'Makefile')
+ arguments.insert(0, '-f')
+ arguments.insert(1, os.path.splitext(gyp_file)[0] + '.Makefile')
+ kw['arguments'] = arguments
+ return self.run(program=self.build_tool, **kw)
+ def up_to_date(self, gyp_file, target=None, **kw):
+ """
+ Verifies that a build of the specified Make target is up to date.
+ """
+ if target in (None, self.DEFAULT):
+ message_target = 'all'
+ else:
+ message_target = target
+ kw['stdout'] = "make: Nothing to be done for `%s'.\n" % message_target
+ return self.build(gyp_file, target, **kw)
+ def run_built_executable(self, name, *args, **kw):
+ """
+ Runs an executable built by Make.
+ """
+ configuration = self.configuration_dirname()
+ libdir = os.path.join('out', configuration, 'lib')
+ # TODO(piman): when everything is cross-compile safe, remove lib.target
+ if sys.platform == 'darwin':
+ # Mac puts target shared libraries right in the product directory.
+ configuration = self.configuration_dirname()
+ os.environ['DYLD_LIBRARY_PATH'] = (
+ libdir + '.host:' + os.path.join('out', configuration))
+ else:
+ os.environ['LD_LIBRARY_PATH'] = libdir + '.host:' + libdir + '.target'
+ # Enclosing the name in a list avoids prepending the original dir.
+ program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
+ return self.run(program=program, *args, **kw)
+ def built_file_path(self, name, type=None, **kw):
+ """
+ Returns a path to the specified file name, of the specified type,
+ as built by Make.
+
+ Built files are in the subdirectory 'out/{configuration}'.
+ The default is 'out/Default'.
+
+ A chdir= keyword argument specifies the source directory
+ relative to which the output subdirectory can be found.
+
+ "type" values of STATIC_LIB or SHARED_LIB append the necessary
+ prefixes and suffixes to a platform-independent library base name.
+
+ A subdir= keyword argument specifies a library subdirectory within
+ the default 'obj.target'.
+ """
+ result = []
+ chdir = kw.get('chdir')
+ if chdir:
+ result.append(chdir)
+ configuration = self.configuration_dirname()
+ result.extend(['out', configuration])
+ if type == self.STATIC_LIB and sys.platform != 'darwin':
+ result.append('obj.target')
+ elif type == self.SHARED_LIB and sys.platform != 'darwin':
+ result.append('lib.target')
+ subdir = kw.get('subdir')
+ if subdir and type != self.SHARED_LIB:
+ result.append(subdir)
+ result.append(self.built_file_basename(name, type, **kw))
+ return self.workpath(*result)
+
+
+def ConvertToCygpath(path):
+ """Convert to cygwin path if we are using cygwin."""
+ if sys.platform == 'cygwin':
+ p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
+ path = p.communicate()[0].strip()
+ return path
+
+
+def FindMSBuildInstallation(msvs_version = 'auto'):
+ """Returns path to MSBuild for msvs_version or latest available.
+
+ Looks in the registry to find install location of MSBuild.
+ MSBuild before v4.0 will not build c++ projects, so only use newer versions.
+ """
+ import TestWin
+ registry = TestWin.Registry()
+
+ msvs_to_msbuild = {
+ '2013': r'12.0',
+ '2012': r'4.0', # Really v4.0.30319 which comes with .NET 4.5.
+ '2010': r'4.0'}
+
+ msbuild_basekey = r'HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions'
+ if not registry.KeyExists(msbuild_basekey):
+ print 'Error: could not find MSBuild base registry entry'
+ return None
+
+ msbuild_version = None
+ if msvs_version in msvs_to_msbuild:
+ msbuild_test_version = msvs_to_msbuild[msvs_version]
+ if registry.KeyExists(msbuild_basekey + '\\' + msbuild_test_version):
+ msbuild_version = msbuild_test_version
+ else:
+ print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" '
+ 'but corresponding MSBuild "%s" was not found.' %
+ (msvs_version, msbuild_version))
+ if not msbuild_version:
+ for msvs_version in sorted(msvs_to_msbuild, reverse=True):
+ msbuild_test_version = msvs_to_msbuild[msvs_version]
+ if registry.KeyExists(msbuild_basekey + '\\' + msbuild_test_version):
+ msbuild_version = msbuild_test_version
+ break
+ if not msbuild_version:
+ print 'Error: could not find MSBuild registry entry'
+ return None
+
+ msbuild_path = registry.GetValue(msbuild_basekey + '\\' + msbuild_version,
+ 'MSBuildToolsPath')
+ if not msbuild_path:
+ print 'Error: could not get MSBuild registry entry value'
+ return None
+
+ return os.path.join(msbuild_path, 'MSBuild.exe')
+
+
+def FindVisualStudioInstallation():
+ """Returns appropriate values for .build_tool and .uses_msbuild fields
+ of TestGypBase for Visual Studio.
+
+ We use the value specified by GYP_MSVS_VERSION. If not specified, we
+ search %PATH% and %PATHEXT% for a devenv.{exe,bat,...} executable.
+ Failing that, we search for likely deployment paths.
+ """
+ possible_roots = ['%s:\\Program Files%s' % (chr(drive), suffix)
+ for drive in range(ord('C'), ord('Z') + 1)
+ for suffix in ['', ' (x86)']]
+ possible_paths = {
+ '2013': r'Microsoft Visual Studio 12.0\Common7\IDE\devenv.com',
+ '2012': r'Microsoft Visual Studio 11.0\Common7\IDE\devenv.com',
+ '2010': r'Microsoft Visual Studio 10.0\Common7\IDE\devenv.com',
+ '2008': r'Microsoft Visual Studio 9.0\Common7\IDE\devenv.com',
+ '2005': r'Microsoft Visual Studio 8\Common7\IDE\devenv.com'}
+
+ possible_roots = [ConvertToCygpath(r) for r in possible_roots]
+
+ msvs_version = 'auto'
+ for flag in (f for f in sys.argv if f.startswith('msvs_version=')):
+ msvs_version = flag.split('=')[-1]
+ msvs_version = os.environ.get('GYP_MSVS_VERSION', msvs_version)
+
+ if msvs_version in possible_paths:
+ # Check that the path to the specified GYP_MSVS_VERSION exists.
+ path = possible_paths[msvs_version]
+ for r in possible_roots:
+ build_tool = os.path.join(r, path)
+ if os.path.exists(build_tool):
+ uses_msbuild = msvs_version >= '2010'
+ msbuild_path = FindMSBuildInstallation(msvs_version)
+ return build_tool, uses_msbuild, msbuild_path
+ else:
+ print ('Warning: Environment variable GYP_MSVS_VERSION specifies "%s" '
+ 'but corresponding "%s" was not found.' % (msvs_version, path))
+ # Neither GYP_MSVS_VERSION nor the path help us out. Iterate through
+ # the choices looking for a match.
+ for version in sorted(possible_paths, reverse=True):
+ path = possible_paths[version]
+ for r in possible_roots:
+ build_tool = os.path.join(r, path)
+ if os.path.exists(build_tool):
+ uses_msbuild = msvs_version >= '2010'
+ msbuild_path = FindMSBuildInstallation(msvs_version)
+ return build_tool, uses_msbuild, msbuild_path
+ print 'Error: could not find devenv'
+ sys.exit(1)
+
+class TestGypOnMSToolchain(TestGypBase):
+ """
+ Common subclass for testing generators that target the Microsoft Visual
+ Studio toolchain (cl, link, dumpbin, etc.)
+ """
+ @staticmethod
+ def _ComputeVsvarsPath(devenv_path):
+ devenv_dir = os.path.split(devenv_path)[0]
+ vsvars_path = os.path.join(devenv_path, '../../Tools/vsvars32.bat')
+ return vsvars_path
+
+ def initialize_build_tool(self):
+ super(TestGypOnMSToolchain, self).initialize_build_tool()
+ if sys.platform in ('win32', 'cygwin'):
+ build_tools = FindVisualStudioInstallation()
+ self.devenv_path, self.uses_msbuild, self.msbuild_path = build_tools
+ self.vsvars_path = TestGypOnMSToolchain._ComputeVsvarsPath(
+ self.devenv_path)
+
+ def run_dumpbin(self, *dumpbin_args):
+ """Run the dumpbin tool with the specified arguments, and capturing and
+ returning stdout."""
+ assert sys.platform in ('win32', 'cygwin')
+ cmd = os.environ.get('COMSPEC', 'cmd.exe')
+ arguments = [cmd, '/c', self.vsvars_path, '&&', 'dumpbin']
+ arguments.extend(dumpbin_args)
+ proc = subprocess.Popen(arguments, stdout=subprocess.PIPE)
+ output = proc.communicate()[0]
+ assert not proc.returncode
+ return output
+
+class TestGypNinja(TestGypOnMSToolchain):
+ """
+ Subclass for testing the GYP Ninja generator.
+ """
+ format = 'ninja'
+ build_tool_list = ['ninja']
+ ALL = 'all'
+ DEFAULT = 'all'
+
+ def run_gyp(self, gyp_file, *args, **kw):
+ TestGypBase.run_gyp(self, gyp_file, *args, **kw)
+
+ def build(self, gyp_file, target=None, **kw):
+ arguments = kw.get('arguments', [])[:]
+
+ # Add a -C output/path to the command line.
+ arguments.append('-C')
+ arguments.append(os.path.join('out', self.configuration_dirname()))
+
+ if target is None:
+ target = 'all'
+ arguments.append(target)
+
+ kw['arguments'] = arguments
+ return self.run(program=self.build_tool, **kw)
+
+ def run_built_executable(self, name, *args, **kw):
+ # 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':
+ configuration = self.configuration_dirname()
+ os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration)
+ return self.run(program=program, *args, **kw)
+
+ def built_file_path(self, name, type=None, **kw):
+ result = []
+ chdir = kw.get('chdir')
+ if chdir:
+ result.append(chdir)
+ result.append('out')
+ result.append(self.configuration_dirname())
+ if type == self.STATIC_LIB:
+ if sys.platform != 'darwin':
+ result.append('obj')
+ elif type == self.SHARED_LIB:
+ if sys.platform != 'darwin' and sys.platform != 'win32':
+ result.append('lib')
+ subdir = kw.get('subdir')
+ if subdir and type != self.SHARED_LIB:
+ result.append(subdir)
+ result.append(self.built_file_basename(name, type, **kw))
+ return self.workpath(*result)
+
+ def up_to_date(self, gyp_file, target=None, **kw):
+ result = self.build(gyp_file, target, **kw)
+ if not result:
+ stdout = self.stdout()
+ if 'ninja: no work to do' not in stdout:
+ self.report_not_up_to_date()
+ self.fail_test()
+ return result
+
+
+class TestGypMSVS(TestGypOnMSToolchain):
+ """
+ Subclass for testing the GYP Visual Studio generator.
+ """
+ format = 'msvs'
+
+ u = r'=== Build: 0 succeeded, 0 failed, (\d+) up-to-date, 0 skipped ==='
+ up_to_date_re = re.compile(u, re.M)
+
+ # Initial None element will indicate to our .initialize_build_tool()
+ # method below that 'devenv' was not found on %PATH%.
+ #
+ # Note: we must use devenv.com to be able to capture build output.
+ # Directly executing devenv.exe only sends output to BuildLog.htm.
+ build_tool_list = [None, 'devenv.com']
+
+ def initialize_build_tool(self):
+ super(TestGypMSVS, self).initialize_build_tool()
+ self.build_tool = self.devenv_path
+
+ def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw):
+ """
+ Runs a Visual Studio build using the configuration generated
+ from the specified gyp_file.
+ """
+ configuration = self.configuration_buildname()
+ if clean:
+ build = '/Clean'
+ elif rebuild:
+ build = '/Rebuild'
+ else:
+ build = '/Build'
+ arguments = kw.get('arguments', [])[:]
+ arguments.extend([gyp_file.replace('.gyp', '.sln'),
+ build, configuration])
+ # Note: the Visual Studio generator doesn't add an explicit 'all'
+ # target, so we just treat it the same as the default.
+ if target not in (None, self.ALL, self.DEFAULT):
+ arguments.extend(['/Project', target])
+ if self.configuration:
+ arguments.extend(['/ProjectConfig', self.configuration])
+ kw['arguments'] = arguments
+ return self.run(program=self.build_tool, **kw)
+ def up_to_date(self, gyp_file, target=None, **kw):
+ """
+ Verifies that a build of the specified Visual Studio target is up to date.
+
+ Beware that VS2010 will behave strangely if you build under
+ C:\USERS\yourname\AppData\Local. It will cause needless work. The ouptut
+ will be "1 succeeded and 0 up to date". MSBuild tracing reveals that:
+ "Project 'C:\Users\...\AppData\Local\...vcxproj' not up to date because
+ 'C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\VC\BIN\1033\CLUI.DLL'
+ was modified at 02/21/2011 17:03:30, which is newer than '' which was
+ modified at 01/01/0001 00:00:00.
+
+ The workaround is to specify a workdir when instantiating the test, e.g.
+ test = TestGyp.TestGyp(workdir='workarea')
+ """
+ result = self.build(gyp_file, target, **kw)
+ if not result:
+ stdout = self.stdout()
+
+ m = self.up_to_date_re.search(stdout)
+ up_to_date = m and int(m.group(1)) > 0
+ if not up_to_date:
+ self.report_not_up_to_date()
+ self.fail_test()
+ return result
+ def run_built_executable(self, name, *args, **kw):
+ """
+ Runs an executable built by Visual Studio.
+ """
+ configuration = self.configuration_dirname()
+ # Enclosing the name in a list avoids prepending the original dir.
+ program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
+ return self.run(program=program, *args, **kw)
+ def built_file_path(self, name, type=None, **kw):
+ """
+ Returns a path to the specified file name, of the specified type,
+ as built by Visual Studio.
+
+ Built files are in a subdirectory that matches the configuration
+ name. The default is 'Default'.
+
+ A chdir= keyword argument specifies the source directory
+ relative to which the output subdirectory can be found.
+
+ "type" values of STATIC_LIB or SHARED_LIB append the necessary
+ prefixes and suffixes to a platform-independent library base name.
+ """
+ result = []
+ chdir = kw.get('chdir')
+ if chdir:
+ result.append(chdir)
+ result.append(self.configuration_dirname())
+ if type == self.STATIC_LIB:
+ result.append('lib')
+ result.append(self.built_file_basename(name, type, **kw))
+ return self.workpath(*result)
+
+
+class TestGypMSVSNinja(TestGypNinja):
+ """
+ Subclass for testing the GYP Visual Studio Ninja generator.
+ """
+ format = 'msvs-ninja'
+
+ def initialize_build_tool(self):
+ super(TestGypMSVSNinja, self).initialize_build_tool()
+ # When using '--build', make sure ninja is first in the format list.
+ self.formats.insert(0, 'ninja')
+
+ def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw):
+ """
+ Runs a Visual Studio build using the configuration generated
+ from the specified gyp_file.
+ """
+ arguments = kw.get('arguments', [])[:]
+ if target in (None, self.ALL, self.DEFAULT):
+ # Note: the Visual Studio generator doesn't add an explicit 'all' target.
+ # This will build each project. This will work if projects are hermetic,
+ # but may fail if they are not (a project may run more than once).
+ # It would be nice to supply an all.metaproj for MSBuild.
+ arguments.extend([gyp_file.replace('.gyp', '.sln')])
+ else:
+ # MSBuild documentation claims that one can specify a sln but then build a
+ # project target like 'msbuild a.sln /t:proj:target' but this format only
+ # supports 'Clean', 'Rebuild', and 'Publish' (with none meaning Default).
+ # This limitation is due to the .sln -> .sln.metaproj conversion.
+ # The ':' is not special, 'proj:target' is a target in the metaproj.
+ arguments.extend([target+'.vcxproj'])
+
+ if clean:
+ build = 'Clean'
+ elif rebuild:
+ build = 'Rebuild'
+ else:
+ build = 'Build'
+ arguments.extend(['/target:'+build])
+ configuration = self.configuration_buildname()
+ config = configuration.split('|')
+ arguments.extend(['/property:Configuration='+config[0]])
+ if len(config) > 1:
+ arguments.extend(['/property:Platform='+config[1]])
+ arguments.extend(['/property:BuildInParallel=false'])
+ arguments.extend(['/verbosity:minimal'])
+
+ kw['arguments'] = arguments
+ return self.run(program=self.msbuild_path, **kw)
+
+
+class TestGypXcode(TestGypBase):
+ """
+ Subclass for testing the GYP Xcode generator.
+ """
+ format = 'xcode'
+ build_tool_list = ['xcodebuild']
+
+ phase_script_execution = ("\n"
+ "PhaseScriptExecution /\\S+/Script-[0-9A-F]+\\.sh\n"
+ " cd /\\S+\n"
+ " /bin/sh -c /\\S+/Script-[0-9A-F]+\\.sh\n"
+ "(make: Nothing to be done for `all'\\.\n)?")
+
+ strip_up_to_date_expressions = [
+ # Various actions or rules can run even when the overall build target
+ # is up to date. Strip those phases' GYP-generated output.
+ re.compile(phase_script_execution, re.S),
+
+ # The message from distcc_pump can trail the "BUILD SUCCEEDED"
+ # message, so strip that, too.
+ re.compile('__________Shutting down distcc-pump include server\n', re.S),
+ ]
+
+ up_to_date_endings = (
+ 'Checking Dependencies...\n** BUILD SUCCEEDED **\n', # Xcode 3.0/3.1
+ 'Check dependencies\n** BUILD SUCCEEDED **\n\n', # Xcode 3.2
+ 'Check dependencies\n\n\n** BUILD SUCCEEDED **\n\n', # Xcode 4.2
+ 'Check dependencies\n\n** BUILD SUCCEEDED **\n\n', # Xcode 5.0
+ )
+
+ def build(self, gyp_file, target=None, **kw):
+ """
+ Runs an xcodebuild using the .xcodeproj generated from the specified
+ gyp_file.
+ """
+ # Be sure we're working with a copy of 'arguments' since we modify it.
+ # The caller may not be expecting it to be modified.
+ arguments = kw.get('arguments', [])[:]
+ arguments.extend(['-project', gyp_file.replace('.gyp', '.xcodeproj')])
+ if target == self.ALL:
+ arguments.append('-alltargets',)
+ elif target not in (None, self.DEFAULT):
+ arguments.extend(['-target', target])
+ if self.configuration:
+ arguments.extend(['-configuration', self.configuration])
+ symroot = kw.get('SYMROOT', '$SRCROOT/build')
+ if symroot:
+ arguments.append('SYMROOT='+symroot)
+ kw['arguments'] = arguments
+
+ # Work around spurious stderr output from Xcode 4, http://crbug.com/181012
+ match = kw.pop('match', self.match)
+ def match_filter_xcode(actual, expected):
+ if actual:
+ if not TestCmd.is_List(actual):
+ actual = actual.split('\n')
+ if not TestCmd.is_List(expected):
+ expected = expected.split('\n')
+ actual = [a for a in actual
+ if 'No recorder, buildTask:
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void lib1_function(void)
+{
+ fprintf(stdout, "Hello from lib1.c\n");
+ fflush(stdout);
+}
diff --git a/tools/gyp/test/library/src/lib1_moveable.c b/tools/gyp/test/library/src/lib1_moveable.c
new file mode 100644
index 00000000000000..5d3cc1d9aab267
--- /dev/null
+++ b/tools/gyp/test/library/src/lib1_moveable.c
@@ -0,0 +1,10 @@
+#include
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void moveable_function(void)
+{
+ fprintf(stdout, "Hello from lib1_moveable.c\n");
+ fflush(stdout);
+}
diff --git a/tools/gyp/test/library/src/lib2.c b/tools/gyp/test/library/src/lib2.c
new file mode 100644
index 00000000000000..21dda726530fc2
--- /dev/null
+++ b/tools/gyp/test/library/src/lib2.c
@@ -0,0 +1,10 @@
+#include
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void lib2_function(void)
+{
+ fprintf(stdout, "Hello from lib2.c\n");
+ fflush(stdout);
+}
diff --git a/tools/gyp/test/library/src/lib2_moveable.c b/tools/gyp/test/library/src/lib2_moveable.c
new file mode 100644
index 00000000000000..f645071d1eb863
--- /dev/null
+++ b/tools/gyp/test/library/src/lib2_moveable.c
@@ -0,0 +1,10 @@
+#include
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void moveable_function(void)
+{
+ fprintf(stdout, "Hello from lib2_moveable.c\n");
+ fflush(stdout);
+}
diff --git a/tools/gyp/test/library/src/library.gyp b/tools/gyp/test/library/src/library.gyp
new file mode 100644
index 00000000000000..bc355164260373
--- /dev/null
+++ b/tools/gyp/test/library/src/library.gyp
@@ -0,0 +1,58 @@
+# Copyright (c) 2009 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.
+
+{
+ 'variables': {
+ 'moveable_function%': 0,
+ },
+ 'targets': [
+ {
+ 'target_name': 'program',
+ 'type': 'executable',
+ 'dependencies': [
+ 'lib1',
+ 'lib2',
+ ],
+ 'sources': [
+ 'program.c',
+ ],
+ },
+ {
+ 'target_name': 'lib1',
+ 'type': '<(library)',
+ 'sources': [
+ 'lib1.c',
+ ],
+ 'conditions': [
+ ['moveable_function=="lib1"', {
+ 'sources': [
+ 'lib1_moveable.c',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'lib2',
+ 'type': '<(library)',
+ 'sources': [
+ 'lib2.c',
+ ],
+ 'conditions': [
+ ['moveable_function=="lib2"', {
+ 'sources': [
+ 'lib2_moveable.c',
+ ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'target_defaults': {
+ # Support 64-bit shared libs (also works fine for 32-bit).
+ 'cflags': ['-fPIC'],
+ },
+ }],
+ ],
+}
diff --git a/tools/gyp/test/library/src/program.c b/tools/gyp/test/library/src/program.c
new file mode 100644
index 00000000000000..d460f60e40511f
--- /dev/null
+++ b/tools/gyp/test/library/src/program.c
@@ -0,0 +1,15 @@
+#include
+
+extern void lib1_function(void);
+extern void lib2_function(void);
+extern void moveable_function(void);
+
+int main(void)
+{
+ fprintf(stdout, "Hello from program.c\n");
+ fflush(stdout);
+ lib1_function();
+ lib2_function();
+ moveable_function();
+ return 0;
+}
diff --git a/tools/gyp/test/library/src/shared_dependency.gyp b/tools/gyp/test/library/src/shared_dependency.gyp
new file mode 100644
index 00000000000000..7d29f5de598551
--- /dev/null
+++ b/tools/gyp/test/library/src/shared_dependency.gyp
@@ -0,0 +1,33 @@
+# Copyright (c) 2009 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': 'lib1',
+ 'type': 'shared_library',
+ 'sources': [
+ 'lib1.c',
+ ],
+ },
+ {
+ 'target_name': 'lib2',
+ 'type': 'shared_library',
+ 'sources': [
+ 'lib2.c',
+ ],
+ 'dependencies': [
+ 'lib1',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'target_defaults': {
+ # Support 64-bit shared libs (also works fine for 32-bit).
+ 'cflags': ['-fPIC'],
+ },
+ }],
+ ],
+}
diff --git a/tools/gyp/test/library_dirs/gyptest-library-dirs.py b/tools/gyp/test/library_dirs/gyptest-library-dirs.py
new file mode 100644
index 00000000000000..5edd6e757918e6
--- /dev/null
+++ b/tools/gyp/test/library_dirs/gyptest-library-dirs.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 library_dirs (in link_settings) are properly found.
+"""
+
+import sys
+
+import TestGyp
+
+test = TestGyp.TestGyp(formats=['!android'])
+
+lib_dir = test.tempdir('secret_location')
+
+test.run_gyp('test.gyp',
+ '-D', 'abs_path_to_secret_library_location={0}'.format(lib_dir),
+ chdir='subdir')
+
+# Must build each target independently, since they are not in each others'
+# 'dependencies' (test.ALL does NOT work here for some builders, and in any case
+# would not ensure the correct ordering).
+test.build('test.gyp', 'mylib', chdir='subdir')
+test.build('test.gyp', 'libraries-search-path-test', chdir='subdir')
+
+expect = """Hello world
+"""
+test.run_built_executable(
+ 'libraries-search-path-test', chdir='subdir', stdout=expect)
+
+if sys.platform in ('win32', 'cygwin'):
+ test.run_gyp('test-win.gyp',
+ '-D',
+ 'abs_path_to_secret_library_location={0}'.format(lib_dir),
+ chdir='subdir')
+
+ test.build('test.gyp', 'mylib', chdir='subdir')
+ test.build('test-win.gyp',
+ 'libraries-search-path-test-lib-suffix',
+ chdir='subdir')
+
+ test.run_built_executable(
+ 'libraries-search-path-test-lib-suffix', chdir='subdir', stdout=expect)
+
+
+test.pass_test()
+test.cleanup()
diff --git a/tools/gyp/test/library_dirs/subdir/README.txt b/tools/gyp/test/library_dirs/subdir/README.txt
new file mode 100644
index 00000000000000..4031ded85f6301
--- /dev/null
+++ b/tools/gyp/test/library_dirs/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/library_dirs/subdir/hello.cc b/tools/gyp/test/library_dirs/subdir/hello.cc
new file mode 100644
index 00000000000000..5dbbd48d349c4e
--- /dev/null
+++ b/tools/gyp/test/library_dirs/subdir/hello.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2013 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
+#include "mylib.h"
+
+int main() {
+ std::cout << "Hello " << my_foo(99) << std::endl;
+ return 0;
+}
diff --git a/tools/gyp/test/library_dirs/subdir/mylib.cc b/tools/gyp/test/library_dirs/subdir/mylib.cc
new file mode 100644
index 00000000000000..654f3d0e6cc8d2
--- /dev/null
+++ b/tools/gyp/test/library_dirs/subdir/mylib.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2013 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 "mylib.h"
+
+std::string my_foo(int x) {
+ return std::string("world");
+}
diff --git a/tools/gyp/test/library_dirs/subdir/mylib.h b/tools/gyp/test/library_dirs/subdir/mylib.h
new file mode 100644
index 00000000000000..84b4022e7b180b
--- /dev/null
+++ b/tools/gyp/test/library_dirs/subdir/mylib.h
@@ -0,0 +1,12 @@
+// Copyright (c) 2013 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.
+
+#ifndef TEST_LIBRARY_DIRS_SUBDIR_MYLIB_H
+#define TEST_LIBRARY_DIRS_SUBDIR_MYLIB_H
+
+#include
+
+std::string my_foo(int);
+
+#endif // TEST_LIBRARY_DIRS_SUBDIR_MYLIB_H
diff --git a/tools/gyp/test/library_dirs/subdir/test-win.gyp b/tools/gyp/test/library_dirs/subdir/test-win.gyp
new file mode 100644
index 00000000000000..033b6f7fdd03c6
--- /dev/null
+++ b/tools/gyp/test/library_dirs/subdir/test-win.gyp
@@ -0,0 +1,60 @@
+# Copyright (c) 2013 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 creates a static library and puts it in a nonstandard location for
+ # libraries-search-path-test.
+ 'target_name': 'mylib',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ # This directory is NOT in the default library search locations. It also
+ # MUST be passed in on the gyp command line:
+ #
+ # -D abs_path_to_secret_library_location=/some_absolute_path
+ #
+ # The gyptest itself (../gyptest-library-dirs.py) provides this.
+ 'product_dir': '<(abs_path_to_secret_library_location)',
+ 'sources': [
+ 'mylib.cc',
+ ],
+ },
+ {
+ 'target_name': 'libraries-search-path-test-lib-suffix',
+ 'type': 'executable',
+ 'dependencies': [
+ # It is important to NOT list the mylib as a dependency here, because
+ # some build systems will track it down based on its product_dir,
+ # such that the link succeeds even without the library_dirs below.
+ #
+ # The point of this weird structuring is to ensure that 'library_dirs'
+ # works as advertised, such that just '-lmylib' (or its equivalent)
+ # works based on the directories that library_dirs puts in the library
+ # link path.
+ #
+ # If 'mylib' was listed as a proper dependency here, the build system
+ # would find it and link with its path on disk.
+ #
+ # Note that this implies 'mylib' must already be built when building
+ # 'libraries-search-path-test' (see ../gyptest-library-dirs.py).
+ #
+ #'mylib',
+ ],
+ 'sources': [
+ 'hello.cc',
+ ],
+ # Note that without this, the mylib library would not be found and
+ # successfully linked.
+ 'library_dirs': [
+ '<(abs_path_to_secret_library_location)',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lmylib.lib',
+ ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/library_dirs/subdir/test.gyp b/tools/gyp/test/library_dirs/subdir/test.gyp
new file mode 100644
index 00000000000000..f83d7f2bf1f8ff
--- /dev/null
+++ b/tools/gyp/test/library_dirs/subdir/test.gyp
@@ -0,0 +1,68 @@
+# Copyright (c) 2013 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 creates a static library and puts it in a nonstandard location for
+ # libraries-search-path-test.
+ 'target_name': 'mylib',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ # This directory is NOT in the default library search locations. It also
+ # MUST be passed in on the gyp command line:
+ #
+ # -D abs_path_to_secret_library_location=/some_absolute_path
+ #
+ # The gyptest itself (../gyptest-library-dirs.py) provides this.
+ 'product_dir': '<(abs_path_to_secret_library_location)',
+ 'sources': [
+ 'mylib.cc',
+ ],
+ },
+ {
+ 'target_name': 'libraries-search-path-test',
+ 'type': 'executable',
+ 'dependencies': [
+ # It is important to NOT list the mylib as a dependency here, because
+ # some build systems will track it down based on its product_dir,
+ # such that the link succeeds even without the library_dirs below.
+ #
+ # The point of this weird structuring is to ensure that 'library_dirs'
+ # works as advertised, such that just '-lmylib' (or its equivalent)
+ # works based on the directories that library_dirs puts in the library
+ # link path.
+ #
+ # If 'mylib' was listed as a proper dependency here, the build system
+ # would find it and link with its path on disk.
+ #
+ # Note that this implies 'mylib' must already be built when building
+ # 'libraries-search-path-test' (see ../gyptest-library-dirs.py).
+ #
+ #'mylib',
+ ],
+ 'sources': [
+ 'hello.cc',
+ ],
+ # Note that without this, the mylib library would not be found and
+ # successfully linked.
+ 'library_dirs': [
+ '<(abs_path_to_secret_library_location)',
+ ],
+ 'link_settings': {
+ 'conditions': [
+ ['OS=="linux"', {
+ 'libraries': [
+ '-lmylib',
+ ],
+ }, { # else
+ 'libraries': [
+ '<(STATIC_LIB_PREFIX)mylib<(STATIC_LIB_SUFFIX)',
+ ],
+ }],
+ ], # conditions
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/link-dependency/gyptest-link-dependency.py b/tools/gyp/test/link-dependency/gyptest-link-dependency.py
new file mode 100755
index 00000000000000..3a8300d44e0031
--- /dev/null
+++ b/tools/gyp/test/link-dependency/gyptest-link-dependency.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 a target marked as 'link_dependency==1' isn't being pulled into
+the 'none' target's dependency (which would otherwise lead to a dependency
+cycle in ninja).
+"""
+
+import TestGyp
+
+# See https://codereview.chromium.org/177043010/#msg15 for why this doesn't
+# work with cmake.
+test = TestGyp.TestGyp(formats=['!cmake'])
+
+test.run_gyp('test.gyp')
+test.build('test.gyp', 'main')
+
+# If running gyp worked, all is well.
+test.pass_test()
diff --git a/tools/gyp/test/link-dependency/main.c b/tools/gyp/test/link-dependency/main.c
new file mode 100644
index 00000000000000..543d8b6951eecb
--- /dev/null
+++ b/tools/gyp/test/link-dependency/main.c
@@ -0,0 +1,7 @@
+#include
+#include
+int main() {
+ void *p = malloc(1);
+ printf("p: %p\n", p);
+ return 0;
+}
diff --git a/tools/gyp/test/link-dependency/mymalloc.c b/tools/gyp/test/link-dependency/mymalloc.c
new file mode 100644
index 00000000000000..f80bc02a62682e
--- /dev/null
+++ b/tools/gyp/test/link-dependency/mymalloc.c
@@ -0,0 +1,12 @@
+#include
+
+// The windows ninja generator is expecting an import library to get generated,
+// but it doesn't if there are no exports.
+#ifdef _MSC_VER
+__declspec(dllexport) void foo() {}
+#endif
+
+void *malloc(size_t size) {
+ (void)size;
+ return (void*)0xdeadbeef;
+}
diff --git a/tools/gyp/test/link-dependency/test.gyp b/tools/gyp/test/link-dependency/test.gyp
new file mode 100644
index 00000000000000..e0ac7f2670c0a3
--- /dev/null
+++ b/tools/gyp/test/link-dependency/test.gyp
@@ -0,0 +1,37 @@
+{
+ 'variables': {
+ 'custom_malloc%' : 1,
+ },
+ 'target_defaults': {
+ 'conditions': [
+ ['custom_malloc==1', {
+ 'dependencies': [
+ 'malloc',
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'main',
+ 'type': 'none',
+ 'dependencies': [ 'main_initial',],
+ },
+ {
+ 'target_name': 'main_initial',
+ 'type': 'executable',
+ 'product_name': 'main',
+ 'sources': [ 'main.c' ],
+ },
+ {
+ 'target_name': 'malloc',
+ 'type': 'shared_library',
+ 'variables': {
+ 'prune_self_dependency': 1,
+ # Targets with type 'none' won't depend on this target.
+ 'link_dependency': 1,
+ },
+ 'sources': [ 'mymalloc.c' ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/link-objects/base.c b/tools/gyp/test/link-objects/base.c
new file mode 100644
index 00000000000000..332745920522cd
--- /dev/null
+++ b/tools/gyp/test/link-objects/base.c
@@ -0,0 +1,6 @@
+void extra();
+
+int main(void) {
+ extra();
+ return 0;
+}
diff --git a/tools/gyp/test/link-objects/extra.c b/tools/gyp/test/link-objects/extra.c
new file mode 100644
index 00000000000000..1d7ee09b1079a6
--- /dev/null
+++ b/tools/gyp/test/link-objects/extra.c
@@ -0,0 +1,5 @@
+#include
+
+void extra() {
+ printf("PASS\n");
+}
diff --git a/tools/gyp/test/link-objects/gyptest-all.py b/tools/gyp/test/link-objects/gyptest-all.py
new file mode 100755
index 00000000000000..45bd6e1891357d
--- /dev/null
+++ b/tools/gyp/test/link-objects/gyptest-all.py
@@ -0,0 +1,28 @@
+#!/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.
+
+"""
+Put an object file on the sources list.
+Expect the result to link ok.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform != 'darwin':
+ # Currently only works under the linux make build.
+ test = TestGyp.TestGyp(formats=['make'])
+
+ test.run_gyp('link-objects.gyp')
+
+ test.build('link-objects.gyp', test.ALL)
+
+ test.run_built_executable('link-objects', stdout="PASS\n")
+
+ test.up_to_date('link-objects.gyp', test.ALL)
+
+ test.pass_test()
diff --git a/tools/gyp/test/link-objects/link-objects.gyp b/tools/gyp/test/link-objects/link-objects.gyp
new file mode 100644
index 00000000000000..ab72855531ae61
--- /dev/null
+++ b/tools/gyp/test/link-objects/link-objects.gyp
@@ -0,0 +1,24 @@
+# Copyright (c) 2009 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': 'link-objects',
+ 'type': 'executable',
+ 'actions': [
+ {
+ 'action_name': 'build extra object',
+ 'inputs': ['extra.c'],
+ 'outputs': ['extra.o'],
+ 'action': ['gcc', '-o', 'extra.o', '-c', 'extra.c'],
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ 'sources': [
+ 'base.c',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/linux/gyptest-implicit-rpath.py b/tools/gyp/test/linux/gyptest-implicit-rpath.py
new file mode 100644
index 00000000000000..dd7718c76dbf92
--- /dev/null
+++ b/tools/gyp/test/linux/gyptest-implicit-rpath.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 implicit rpath is added only when needed.
+"""
+
+import TestGyp
+
+import re
+import subprocess
+import sys
+
+if sys.platform.startswith('linux'):
+ test = TestGyp.TestGyp(formats=['ninja', 'make'])
+
+ CHDIR = 'implicit-rpath'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ def GetRpaths(p):
+ p = test.built_file_path(p, chdir=CHDIR)
+ r = re.compile(r'Library rpath: \[([^\]]+)\]')
+ proc = subprocess.Popen(['readelf', '-d', p], stdout=subprocess.PIPE)
+ o = proc.communicate()[0]
+ assert not proc.returncode
+ return r.findall(o)
+
+ if test.format == 'ninja':
+ expect = '$ORIGIN/lib/'
+ elif test.format == 'make':
+ expect = '$ORIGIN/lib.target/'
+ else:
+ test.fail_test()
+
+ if GetRpaths('shared_executable') != [expect]:
+ test.fail_test()
+
+ if GetRpaths('shared_executable_no_so_suffix') != [expect]:
+ test.fail_test()
+
+ if GetRpaths('static_executable'):
+ test.fail_test()
+
+ test.pass_test()
diff --git a/tools/gyp/test/linux/implicit-rpath/file.c b/tools/gyp/test/linux/implicit-rpath/file.c
new file mode 100644
index 00000000000000..56757a701bf5cf
--- /dev/null
+++ b/tools/gyp/test/linux/implicit-rpath/file.c
@@ -0,0 +1 @@
+void f() {}
diff --git a/tools/gyp/test/linux/implicit-rpath/main.c b/tools/gyp/test/linux/implicit-rpath/main.c
new file mode 100644
index 00000000000000..237c8ce181774d
--- /dev/null
+++ b/tools/gyp/test/linux/implicit-rpath/main.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/linux/implicit-rpath/test.gyp b/tools/gyp/test/linux/implicit-rpath/test.gyp
new file mode 100644
index 00000000000000..b546106986e19d
--- /dev/null
+++ b/tools/gyp/test/linux/implicit-rpath/test.gyp
@@ -0,0 +1,47 @@
+# Copyright (c) 2013 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': 'shared',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ },
+ {
+ 'target_name': 'shared_no_so_suffix',
+ 'product_extension': 'so.0.1',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ },
+ {
+ 'target_name': 'static',
+ 'type': 'static_library',
+ 'sources': [ 'file.c' ],
+ },
+ {
+ 'target_name': 'shared_executable',
+ 'type': 'executable',
+ 'sources': [ 'main.c' ],
+ 'dependencies': [
+ 'shared',
+ ]
+ },
+ {
+ 'target_name': 'shared_executable_no_so_suffix',
+ 'type': 'executable',
+ 'sources': [ 'main.c' ],
+ 'dependencies': [
+ 'shared_no_so_suffix',
+ ]
+ },
+ {
+ 'target_name': 'static_executable',
+ 'type': 'executable',
+ 'sources': [ 'main.c' ],
+ 'dependencies': [
+ 'static',
+ ]
+ },
+ ],
+}
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 00000000000000..443ea3b33c24fd
--- /dev/null
+++ b/tools/gyp/test/mac/action-envvars/action/action.gyp
@@ -0,0 +1,33 @@
+# 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 00000000000000..48d5f6bf86454f
--- /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/app-bundle/TestApp/English.lproj/InfoPlist-error.strings b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist-error.strings
new file mode 100644
index 00000000000000..452e7fabf9dc84
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist-error.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "Copyright ©2011 Google Inc."
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings
new file mode 100644
index 00000000000000..35bd33a96e6a30
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/InfoPlist.strings
@@ -0,0 +1,3 @@
+/* Localized versions of Info.plist keys */
+
+NSHumanReadableCopyright = "Copyright ©2011 Google Inc.";
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib
new file mode 100644
index 00000000000000..4524596787a8bd
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/MainMenu.xib
@@ -0,0 +1,4119 @@
+
+
+
+ 1060
+ 10A324
+ 719
+ 1015
+ 418.00
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+ 719
+
+
+ YES
+
+
+
+
+ YES
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+
+ YES
+
+ YES
+
+
+ YES
+
+
+
+ YES
+
+ NSApplication
+
+
+ FirstResponder
+
+
+ NSApplication
+
+
+
+ 15
+ 2
+ {{335, 390}, {480, 360}}
+ 1954021376
+ TestApp
+ NSWindow
+
+ {1.79769e+308, 1.79769e+308}
+
+
+ 256
+ {480, 360}
+
+
+ {{0, 0}, {1920, 1178}}
+ {1.79769e+308, 1.79769e+308}
+
+
+ TestAppAppDelegate
+
+
+ NSFontManager
+
+
+
+
+ YES
+
+
+ performMiniaturize:
+
+
+
+ 37
+
+
+
+ arrangeInFront:
+
+
+
+ 39
+
+
+
+ print:
+
+
+
+ 86
+
+
+
+ runPageLayout:
+
+
+
+ 87
+
+
+
+ clearRecentDocuments:
+
+
+
+ 127
+
+
+
+ orderFrontStandardAboutPanel:
+
+
+
+ 142
+
+
+
+ performClose:
+
+
+
+ 193
+
+
+
+ toggleContinuousSpellChecking:
+
+
+
+ 222
+
+
+
+ undo:
+
+
+
+ 223
+
+
+
+ copy:
+
+
+
+ 224
+
+
+
+ checkSpelling:
+
+
+
+ 225
+
+
+
+ paste:
+
+
+
+ 226
+
+
+
+ stopSpeaking:
+
+
+
+ 227
+
+
+
+ cut:
+
+
+
+ 228
+
+
+
+ showGuessPanel:
+
+
+
+ 230
+
+
+
+ redo:
+
+
+
+ 231
+
+
+
+ selectAll:
+
+
+
+ 232
+
+
+
+ startSpeaking:
+
+
+
+ 233
+
+
+
+ delete:
+
+
+
+ 235
+
+
+
+ performZoom:
+
+
+
+ 240
+
+
+
+ performFindPanelAction:
+
+
+
+ 241
+
+
+
+ centerSelectionInVisibleArea:
+
+
+
+ 245
+
+
+
+ toggleGrammarChecking:
+
+
+
+ 347
+
+
+
+ toggleSmartInsertDelete:
+
+
+
+ 355
+
+
+
+ toggleAutomaticQuoteSubstitution:
+
+
+
+ 356
+
+
+
+ toggleAutomaticLinkDetection:
+
+
+
+ 357
+
+
+
+ saveDocument:
+
+
+
+ 362
+
+
+
+ saveDocumentAs:
+
+
+
+ 363
+
+
+
+ revertDocumentToSaved:
+
+
+
+ 364
+
+
+
+ runToolbarCustomizationPalette:
+
+
+
+ 365
+
+
+
+ toggleToolbarShown:
+
+
+
+ 366
+
+
+
+ hide:
+
+
+
+ 367
+
+
+
+ hideOtherApplications:
+
+
+
+ 368
+
+
+
+ unhideAllApplications:
+
+
+
+ 370
+
+
+
+ newDocument:
+
+
+
+ 373
+
+
+
+ openDocument:
+
+
+
+ 374
+
+
+
+ addFontTrait:
+
+
+
+ 421
+
+
+
+ addFontTrait:
+
+
+
+ 422
+
+
+
+ modifyFont:
+
+
+
+ 423
+
+
+
+ orderFrontFontPanel:
+
+
+
+ 424
+
+
+
+ modifyFont:
+
+
+
+ 425
+
+
+
+ raiseBaseline:
+
+
+
+ 426
+
+
+
+ lowerBaseline:
+
+
+
+ 427
+
+
+
+ copyFont:
+
+
+
+ 428
+
+
+
+ subscript:
+
+
+
+ 429
+
+
+
+ superscript:
+
+
+
+ 430
+
+
+
+ tightenKerning:
+
+
+
+ 431
+
+
+
+ underline:
+
+
+
+ 432
+
+
+
+ orderFrontColorPanel:
+
+
+
+ 433
+
+
+
+ useAllLigatures:
+
+
+
+ 434
+
+
+
+ loosenKerning:
+
+
+
+ 435
+
+
+
+ pasteFont:
+
+
+
+ 436
+
+
+
+ unscript:
+
+
+
+ 437
+
+
+
+ useStandardKerning:
+
+
+
+ 438
+
+
+
+ useStandardLigatures:
+
+
+
+ 439
+
+
+
+ turnOffLigatures:
+
+
+
+ 440
+
+
+
+ turnOffKerning:
+
+
+
+ 441
+
+
+
+ terminate:
+
+
+
+ 449
+
+
+
+ toggleAutomaticSpellingCorrection:
+
+
+
+ 456
+
+
+
+ orderFrontSubstitutionsPanel:
+
+
+
+ 458
+
+
+
+ toggleAutomaticDashSubstitution:
+
+
+
+ 461
+
+
+
+ toggleAutomaticTextReplacement:
+
+
+
+ 463
+
+
+
+ uppercaseWord:
+
+
+
+ 464
+
+
+
+ capitalizeWord:
+
+
+
+ 467
+
+
+
+ lowercaseWord:
+
+
+
+ 468
+
+
+
+ pasteAsPlainText:
+
+
+
+ 486
+
+
+
+ performFindPanelAction:
+
+
+
+ 487
+
+
+
+ performFindPanelAction:
+
+
+
+ 488
+
+
+
+ performFindPanelAction:
+
+
+
+ 489
+
+
+
+ showHelp:
+
+
+
+ 493
+
+
+
+ delegate
+
+
+
+ 495
+
+
+
+ alignCenter:
+
+
+
+ 518
+
+
+
+ pasteRuler:
+
+
+
+ 519
+
+
+
+ toggleRuler:
+
+
+
+ 520
+
+
+
+ alignRight:
+
+
+
+ 521
+
+
+
+ copyRuler:
+
+
+
+ 522
+
+
+
+ alignJustified:
+
+
+
+ 523
+
+
+
+ alignLeft:
+
+
+
+ 524
+
+
+
+ makeBaseWritingDirectionNatural:
+
+
+
+ 525
+
+
+
+ makeBaseWritingDirectionLeftToRight:
+
+
+
+ 526
+
+
+
+ makeBaseWritingDirectionRightToLeft:
+
+
+
+ 527
+
+
+
+ makeTextWritingDirectionNatural:
+
+
+
+ 528
+
+
+
+ makeTextWritingDirectionLeftToRight:
+
+
+
+ 529
+
+
+
+ makeTextWritingDirectionRightToLeft:
+
+
+
+ 530
+
+
+
+ window
+
+
+
+ 532
+
+
+
+
+ YES
+
+ 0
+
+
+
+
+
+ -2
+
+
+ File's Owner
+
+
+ -1
+
+
+ First Responder
+
+
+ -3
+
+
+ Application
+
+
+ 29
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+ 19
+
+
+ YES
+
+
+
+
+
+ 56
+
+
+ YES
+
+
+
+
+
+ 217
+
+
+ YES
+
+
+
+
+
+ 83
+
+
+ YES
+
+
+
+
+
+ 81
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 75
+
+
+
+
+ 80
+
+
+
+
+ 78
+
+
+
+
+ 72
+
+
+
+
+ 82
+
+
+
+
+ 124
+
+
+ YES
+
+
+
+
+
+ 77
+
+
+
+
+ 73
+
+
+
+
+ 79
+
+
+
+
+ 112
+
+
+
+
+ 74
+
+
+
+
+ 125
+
+
+ YES
+
+
+
+
+
+ 126
+
+
+
+
+ 205
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 202
+
+
+
+
+ 198
+
+
+
+
+ 207
+
+
+
+
+ 214
+
+
+
+
+ 199
+
+
+
+
+ 203
+
+
+
+
+ 197
+
+
+
+
+ 206
+
+
+
+
+ 215
+
+
+
+
+ 218
+
+
+ YES
+
+
+
+
+
+ 216
+
+
+ YES
+
+
+
+
+
+ 200
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+ 219
+
+
+
+
+ 201
+
+
+
+
+ 204
+
+
+
+
+ 220
+
+
+ YES
+
+
+
+
+
+
+
+
+
+ 213
+
+
+
+
+ 210
+
+
+
+
+ 221
+
+
+
+
+ 208
+
+
+
+
+ 209
+
+
+
+
+ 57
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 58
+
+
+
+
+ 134
+
+
+
+
+ 150
+
+
+
+
+ 136
+
+
+
+
+ 144
+
+
+
+
+ 129
+
+
+
+
+ 143
+
+
+
+
+ 236
+
+
+
+
+ 131
+
+
+ YES
+
+
+
+
+
+ 149
+
+
+
+
+ 145
+
+
+
+
+ 130
+
+
+
+
+ 24
+
+
+ YES
+
+
+
+
+
+
+
+
+ 92
+
+
+
+
+ 5
+
+
+
+
+ 239
+
+
+
+
+ 23
+
+
+
+
+ 295
+
+
+ YES
+
+
+
+
+
+ 296
+
+
+ YES
+
+
+
+
+
+
+ 297
+
+
+
+
+ 298
+
+
+
+
+ 211
+
+
+ YES
+
+
+
+
+
+ 212
+
+
+ YES
+
+
+
+
+
+
+ 195
+
+
+
+
+ 196
+
+
+
+
+ 346
+
+
+
+
+ 348
+
+
+ YES
+
+
+
+
+
+ 349
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+ 350
+
+
+
+
+ 351
+
+
+
+
+ 354
+
+
+
+
+ 371
+
+
+ YES
+
+
+
+
+
+ 372
+
+
+
+
+ 375
+
+
+ YES
+
+
+
+
+
+ 376
+
+
+ YES
+
+
+
+
+
+
+ 377
+
+
+ YES
+
+
+
+
+
+ 388
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 389
+
+
+
+
+ 390
+
+
+
+
+ 391
+
+
+
+
+ 392
+
+
+
+
+ 393
+
+
+
+
+ 394
+
+
+
+
+ 395
+
+
+
+
+ 396
+
+
+
+
+ 397
+
+
+ YES
+
+
+
+
+
+ 398
+
+
+ YES
+
+
+
+
+
+ 399
+
+
+ YES
+
+
+
+
+
+ 400
+
+
+
+
+ 401
+
+
+
+
+ 402
+
+
+
+
+ 403
+
+
+
+
+ 404
+
+
+
+
+ 405
+
+
+ YES
+
+
+
+
+
+
+
+
+
+ 406
+
+
+
+
+ 407
+
+
+
+
+ 408
+
+
+
+
+ 409
+
+
+
+
+ 410
+
+
+
+
+ 411
+
+
+ YES
+
+
+
+
+
+
+
+ 412
+
+
+
+
+ 413
+
+
+
+
+ 414
+
+
+
+
+ 415
+
+
+ YES
+
+
+
+
+
+
+
+
+ 416
+
+
+
+
+ 417
+
+
+
+
+ 418
+
+
+
+
+ 419
+
+
+
+
+ 420
+
+
+
+
+ 450
+
+
+ YES
+
+
+
+
+
+ 451
+
+
+ YES
+
+
+
+
+
+
+
+ 452
+
+
+
+
+ 453
+
+
+
+
+ 454
+
+
+
+
+ 457
+
+
+
+
+ 459
+
+
+
+
+ 460
+
+
+
+
+ 462
+
+
+
+
+ 465
+
+
+
+
+ 466
+
+
+
+
+ 485
+
+
+
+
+ 490
+
+
+ YES
+
+
+
+
+
+ 491
+
+
+ YES
+
+
+
+
+
+ 492
+
+
+
+
+ 494
+
+
+
+
+ 496
+
+
+ YES
+
+
+
+
+
+ 497
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 498
+
+
+
+
+ 499
+
+
+
+
+ 500
+
+
+
+
+ 501
+
+
+
+
+ 502
+
+
+
+
+ 503
+
+
+ YES
+
+
+
+
+
+ 504
+
+
+
+
+ 505
+
+
+
+
+ 506
+
+
+
+
+ 507
+
+
+
+
+ 508
+
+
+ YES
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 509
+
+
+
+
+ 510
+
+
+
+
+ 511
+
+
+
+
+ 512
+
+
+
+
+ 513
+
+
+
+
+ 514
+
+
+
+
+ 515
+
+
+
+
+ 516
+
+
+
+
+ 517
+
+
+
+
+
+
+ YES
+
+ YES
+ -3.IBPluginDependency
+ 112.IBPluginDependency
+ 112.ImportedFromIB2
+ 124.IBPluginDependency
+ 124.ImportedFromIB2
+ 125.IBPluginDependency
+ 125.ImportedFromIB2
+ 125.editorWindowContentRectSynchronizationRect
+ 126.IBPluginDependency
+ 126.ImportedFromIB2
+ 129.IBPluginDependency
+ 129.ImportedFromIB2
+ 130.IBPluginDependency
+ 130.ImportedFromIB2
+ 130.editorWindowContentRectSynchronizationRect
+ 131.IBPluginDependency
+ 131.ImportedFromIB2
+ 134.IBPluginDependency
+ 134.ImportedFromIB2
+ 136.IBPluginDependency
+ 136.ImportedFromIB2
+ 143.IBPluginDependency
+ 143.ImportedFromIB2
+ 144.IBPluginDependency
+ 144.ImportedFromIB2
+ 145.IBPluginDependency
+ 145.ImportedFromIB2
+ 149.IBPluginDependency
+ 149.ImportedFromIB2
+ 150.IBPluginDependency
+ 150.ImportedFromIB2
+ 19.IBPluginDependency
+ 19.ImportedFromIB2
+ 195.IBPluginDependency
+ 195.ImportedFromIB2
+ 196.IBPluginDependency
+ 196.ImportedFromIB2
+ 197.IBPluginDependency
+ 197.ImportedFromIB2
+ 198.IBPluginDependency
+ 198.ImportedFromIB2
+ 199.IBPluginDependency
+ 199.ImportedFromIB2
+ 200.IBEditorWindowLastContentRect
+ 200.IBPluginDependency
+ 200.ImportedFromIB2
+ 200.editorWindowContentRectSynchronizationRect
+ 201.IBPluginDependency
+ 201.ImportedFromIB2
+ 202.IBPluginDependency
+ 202.ImportedFromIB2
+ 203.IBPluginDependency
+ 203.ImportedFromIB2
+ 204.IBPluginDependency
+ 204.ImportedFromIB2
+ 205.IBEditorWindowLastContentRect
+ 205.IBPluginDependency
+ 205.ImportedFromIB2
+ 205.editorWindowContentRectSynchronizationRect
+ 206.IBPluginDependency
+ 206.ImportedFromIB2
+ 207.IBPluginDependency
+ 207.ImportedFromIB2
+ 208.IBPluginDependency
+ 208.ImportedFromIB2
+ 209.IBPluginDependency
+ 209.ImportedFromIB2
+ 210.IBPluginDependency
+ 210.ImportedFromIB2
+ 211.IBPluginDependency
+ 211.ImportedFromIB2
+ 212.IBPluginDependency
+ 212.ImportedFromIB2
+ 212.editorWindowContentRectSynchronizationRect
+ 213.IBPluginDependency
+ 213.ImportedFromIB2
+ 214.IBPluginDependency
+ 214.ImportedFromIB2
+ 215.IBPluginDependency
+ 215.ImportedFromIB2
+ 216.IBPluginDependency
+ 216.ImportedFromIB2
+ 217.IBPluginDependency
+ 217.ImportedFromIB2
+ 218.IBPluginDependency
+ 218.ImportedFromIB2
+ 219.IBPluginDependency
+ 219.ImportedFromIB2
+ 220.IBEditorWindowLastContentRect
+ 220.IBPluginDependency
+ 220.ImportedFromIB2
+ 220.editorWindowContentRectSynchronizationRect
+ 221.IBPluginDependency
+ 221.ImportedFromIB2
+ 23.IBPluginDependency
+ 23.ImportedFromIB2
+ 236.IBPluginDependency
+ 236.ImportedFromIB2
+ 239.IBPluginDependency
+ 239.ImportedFromIB2
+ 24.IBEditorWindowLastContentRect
+ 24.IBPluginDependency
+ 24.ImportedFromIB2
+ 24.editorWindowContentRectSynchronizationRect
+ 29.IBEditorWindowLastContentRect
+ 29.IBPluginDependency
+ 29.ImportedFromIB2
+ 29.WindowOrigin
+ 29.editorWindowContentRectSynchronizationRect
+ 295.IBPluginDependency
+ 296.IBEditorWindowLastContentRect
+ 296.IBPluginDependency
+ 296.editorWindowContentRectSynchronizationRect
+ 297.IBPluginDependency
+ 298.IBPluginDependency
+ 346.IBPluginDependency
+ 346.ImportedFromIB2
+ 348.IBPluginDependency
+ 348.ImportedFromIB2
+ 349.IBEditorWindowLastContentRect
+ 349.IBPluginDependency
+ 349.ImportedFromIB2
+ 349.editorWindowContentRectSynchronizationRect
+ 350.IBPluginDependency
+ 350.ImportedFromIB2
+ 351.IBPluginDependency
+ 351.ImportedFromIB2
+ 354.IBPluginDependency
+ 354.ImportedFromIB2
+ 371.IBEditorWindowLastContentRect
+ 371.IBPluginDependency
+ 371.IBWindowTemplateEditedContentRect
+ 371.NSWindowTemplate.visibleAtLaunch
+ 371.editorWindowContentRectSynchronizationRect
+ 371.windowTemplate.maxSize
+ 372.IBPluginDependency
+ 375.IBPluginDependency
+ 376.IBEditorWindowLastContentRect
+ 376.IBPluginDependency
+ 377.IBPluginDependency
+ 388.IBEditorWindowLastContentRect
+ 388.IBPluginDependency
+ 389.IBPluginDependency
+ 390.IBPluginDependency
+ 391.IBPluginDependency
+ 392.IBPluginDependency
+ 393.IBPluginDependency
+ 394.IBPluginDependency
+ 395.IBPluginDependency
+ 396.IBPluginDependency
+ 397.IBPluginDependency
+ 398.IBPluginDependency
+ 399.IBPluginDependency
+ 400.IBPluginDependency
+ 401.IBPluginDependency
+ 402.IBPluginDependency
+ 403.IBPluginDependency
+ 404.IBPluginDependency
+ 405.IBPluginDependency
+ 406.IBPluginDependency
+ 407.IBPluginDependency
+ 408.IBPluginDependency
+ 409.IBPluginDependency
+ 410.IBPluginDependency
+ 411.IBPluginDependency
+ 412.IBPluginDependency
+ 413.IBPluginDependency
+ 414.IBPluginDependency
+ 415.IBPluginDependency
+ 416.IBPluginDependency
+ 417.IBPluginDependency
+ 418.IBPluginDependency
+ 419.IBPluginDependency
+ 450.IBPluginDependency
+ 451.IBEditorWindowLastContentRect
+ 451.IBPluginDependency
+ 452.IBPluginDependency
+ 453.IBPluginDependency
+ 454.IBPluginDependency
+ 457.IBPluginDependency
+ 459.IBPluginDependency
+ 460.IBPluginDependency
+ 462.IBPluginDependency
+ 465.IBPluginDependency
+ 466.IBPluginDependency
+ 485.IBPluginDependency
+ 490.IBPluginDependency
+ 491.IBEditorWindowLastContentRect
+ 491.IBPluginDependency
+ 492.IBPluginDependency
+ 496.IBPluginDependency
+ 497.IBEditorWindowLastContentRect
+ 497.IBPluginDependency
+ 498.IBPluginDependency
+ 499.IBPluginDependency
+ 5.IBPluginDependency
+ 5.ImportedFromIB2
+ 500.IBPluginDependency
+ 501.IBPluginDependency
+ 502.IBPluginDependency
+ 503.IBPluginDependency
+ 504.IBPluginDependency
+ 505.IBPluginDependency
+ 506.IBPluginDependency
+ 507.IBPluginDependency
+ 508.IBEditorWindowLastContentRect
+ 508.IBPluginDependency
+ 509.IBPluginDependency
+ 510.IBPluginDependency
+ 511.IBPluginDependency
+ 512.IBPluginDependency
+ 513.IBPluginDependency
+ 514.IBPluginDependency
+ 515.IBPluginDependency
+ 516.IBPluginDependency
+ 517.IBPluginDependency
+ 56.IBPluginDependency
+ 56.ImportedFromIB2
+ 57.IBEditorWindowLastContentRect
+ 57.IBPluginDependency
+ 57.ImportedFromIB2
+ 57.editorWindowContentRectSynchronizationRect
+ 58.IBPluginDependency
+ 58.ImportedFromIB2
+ 72.IBPluginDependency
+ 72.ImportedFromIB2
+ 73.IBPluginDependency
+ 73.ImportedFromIB2
+ 74.IBPluginDependency
+ 74.ImportedFromIB2
+ 75.IBPluginDependency
+ 75.ImportedFromIB2
+ 77.IBPluginDependency
+ 77.ImportedFromIB2
+ 78.IBPluginDependency
+ 78.ImportedFromIB2
+ 79.IBPluginDependency
+ 79.ImportedFromIB2
+ 80.IBPluginDependency
+ 80.ImportedFromIB2
+ 81.IBEditorWindowLastContentRect
+ 81.IBPluginDependency
+ 81.ImportedFromIB2
+ 81.editorWindowContentRectSynchronizationRect
+ 82.IBPluginDependency
+ 82.ImportedFromIB2
+ 83.IBPluginDependency
+ 83.ImportedFromIB2
+ 92.IBPluginDependency
+ 92.ImportedFromIB2
+
+
+ YES
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{522, 812}, {146, 23}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{436, 809}, {64, 6}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{753, 187}, {275, 113}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {275, 83}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{547, 180}, {254, 283}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{187, 434}, {243, 243}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {167, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{753, 217}, {238, 103}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {241, 103}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{654, 239}, {194, 73}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{525, 802}, {197, 73}}
+ {{380, 836}, {512, 20}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {74, 862}
+ {{6, 978}, {478, 20}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{604, 269}, {231, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{475, 832}, {234, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{746, 287}, {220, 133}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{608, 612}, {215, 63}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{380, 496}, {480, 360}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{380, 496}, {480, 360}}
+
+ {{33, 99}, {480, 360}}
+ {3.40282e+38, 3.40282e+38}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{591, 420}, {83, 43}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{523, 2}, {178, 283}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{753, 197}, {170, 63}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{725, 289}, {246, 23}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{674, 260}, {204, 183}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ {{878, 180}, {164, 173}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{286, 129}, {275, 183}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{23, 794}, {245, 183}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{452, 109}, {196, 203}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ {{145, 474}, {199, 203}}
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+ com.apple.InterfaceBuilder.CocoaPlugin
+
+
+
+
+ YES
+
+
+ YES
+
+
+
+
+ YES
+
+
+ YES
+
+
+
+ 532
+
+
+
+ YES
+
+ TestAppAppDelegate
+ NSObject
+
+ window
+ NSWindow
+
+
+ IBProjectSource
+ TestAppAppDelegate.h
+
+
+
+
+ YES
+
+ NSApplication
+ NSResponder
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSApplication.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSApplicationScripting.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSColorPanel.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSHelpManager.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSPageLayout.h
+
+
+
+ NSApplication
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSUserInterfaceItemSearching.h
+
+
+
+ NSBrowser
+ NSControl
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSBrowser.h
+
+
+
+ NSControl
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSControl.h
+
+
+
+ NSDocument
+ NSObject
+
+ YES
+
+ YES
+ printDocument:
+ revertDocumentToSaved:
+ runPageLayout:
+ saveDocument:
+ saveDocumentAs:
+ saveDocumentTo:
+
+
+ YES
+ id
+ id
+ id
+ id
+ id
+ id
+
+
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDocument.h
+
+
+
+ NSDocument
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDocumentScripting.h
+
+
+
+ NSDocumentController
+ NSObject
+
+ YES
+
+ YES
+ clearRecentDocuments:
+ newDocument:
+ openDocument:
+ saveAllDocuments:
+
+
+ YES
+ id
+ id
+ id
+ id
+
+
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDocumentController.h
+
+
+
+ NSFontManager
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSFontManager.h
+
+
+
+ NSFormatter
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSFormatter.h
+
+
+
+ NSMatrix
+ NSControl
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMatrix.h
+
+
+
+ NSMenu
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMenu.h
+
+
+
+ NSMenuItem
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMenuItem.h
+
+
+
+ NSMovieView
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSMovieView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSAccessibility.h
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDictionaryController.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDragging.h
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSFontPanel.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSKeyValueBinding.h
+
+
+
+ NSObject
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSNibLoading.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSOutlineView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSPasteboard.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSSavePanel.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSTableView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSToolbarItem.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSView.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSArchiver.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSClassDescription.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSError.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSFileManager.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSKeyValueCoding.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSKeyValueObserving.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSKeyedArchiver.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSObject.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSObjectScripting.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSPortCoder.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSRunLoop.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptClassDescription.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptKeyValueCoding.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptObjectSpecifiers.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSScriptWhoseTests.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSThread.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSURL.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSURLConnection.h
+
+
+
+ NSObject
+
+ IBFrameworkSource
+ Foundation.framework/Headers/NSURLDownload.h
+
+
+
+ NSResponder
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSInterfaceStyle.h
+
+
+
+ NSResponder
+ NSObject
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSResponder.h
+
+
+
+ NSTableView
+ NSControl
+
+
+
+ NSText
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSText.h
+
+
+
+ NSTextView
+ NSText
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSTextView.h
+
+
+
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSClipView.h
+
+
+
+ NSView
+
+
+
+ NSView
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSRulerView.h
+
+
+
+ NSView
+ NSResponder
+
+
+
+ NSWindow
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSDrawer.h
+
+
+
+ NSWindow
+ NSResponder
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSWindow.h
+
+
+
+ NSWindow
+
+ IBFrameworkSource
+ AppKit.framework/Headers/NSWindowScripting.h
+
+
+
+
+ 0
+
+ com.apple.InterfaceBuilder.CocoaPlugin.macosx
+
+
+
+ com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
+
+
+ YES
+ ../TestApp.xcodeproj
+ 3
+
+
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/utf-16be.strings b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/utf-16be.strings
new file mode 100644
index 00000000000000..580783735f5960
Binary files /dev/null and b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/utf-16be.strings differ
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/utf-16le.strings b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/utf-16le.strings
new file mode 100644
index 00000000000000..eeb383784c8b7d
Binary files /dev/null and b/tools/gyp/test/mac/app-bundle/TestApp/English.lproj/utf-16le.strings differ
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/AppIcon.appiconset/Contents.json b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..2db2b1c7c6c316
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,58 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/Contents.json b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/Contents.json
new file mode 100644
index 00000000000000..0a87b6edc67517
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "super_sylvain.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "super_sylvain@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "super_sylvain@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain.png b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain.png
new file mode 100644
index 00000000000000..0ba769182f117a
Binary files /dev/null and b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain.png differ
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@2x.png b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@2x.png
new file mode 100644
index 00000000000000..edfa6a5682f108
Binary files /dev/null and b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@2x.png differ
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@3x.png b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@3x.png
new file mode 100644
index 00000000000000..e0652efc729dcf
Binary files /dev/null and b/tools/gyp/test/mac/app-bundle/TestApp/Images.xcassets/image.imageset/super_sylvain@3x.png differ
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist b/tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist
new file mode 100644
index 00000000000000..e005852f9f9bfe
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/TestApp-Info.plist
@@ -0,0 +1,34 @@
+
+
+
+
+ BuildMachineOSBuild
+ Doesn't matter, will be overwritten
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.google.${PRODUCT_NAME:rfc1034identifier}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ause
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ ${MACOSX_DEPLOYMENT_TARGET}
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h
new file mode 100644
index 00000000000000..518645eae9d726
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.h
@@ -0,0 +1,13 @@
+// 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.
+
+#import
+
+@interface TestAppAppDelegate : NSObject {
+ NSWindow *window;
+}
+
+@property (assign) IBOutlet NSWindow *window;
+
+@end
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m
new file mode 100644
index 00000000000000..9aafa420008ce7
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/TestAppAppDelegate.m
@@ -0,0 +1,15 @@
+// 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.
+
+#import "TestAppAppDelegate.h"
+
+@implementation TestAppAppDelegate
+
+@synthesize window;
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ // Insert code here to initialize your application
+}
+
+@end
diff --git a/tools/gyp/test/mac/app-bundle/TestApp/main.m b/tools/gyp/test/mac/app-bundle/TestApp/main.m
new file mode 100644
index 00000000000000..df6a12d065b226
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/TestApp/main.m
@@ -0,0 +1,10 @@
+// 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.
+
+#import
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}
diff --git a/tools/gyp/test/mac/app-bundle/empty.c b/tools/gyp/test/mac/app-bundle/empty.c
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/tools/gyp/test/mac/app-bundle/test-assets-catalog.gyp b/tools/gyp/test/mac/app-bundle/test-assets-catalog.gyp
new file mode 100644
index 00000000000000..25f94a12d0cf0d
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/test-assets-catalog.gyp
@@ -0,0 +1,43 @@
+# 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': 'dep_framework',
+ 'product_name': 'Dependency Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'empty.c', ],
+ },
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App Assets Catalog Gyp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'dependencies': [ 'dep_framework', ],
+ 'sources': [
+ 'TestApp/main.m',
+ 'TestApp/TestApp_Prefix.pch',
+ 'TestApp/TestAppAppDelegate.h',
+ 'TestApp/TestAppAppDelegate.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings', # UTF-8
+ 'TestApp/English.lproj/utf-16be.strings',
+ 'TestApp/English.lproj/utf-16le.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ 'TestApp/Images.xcassets',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.9',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/app-bundle/test-error.gyp b/tools/gyp/test/mac/app-bundle/test-error.gyp
new file mode 100644
index 00000000000000..370772cc31efbb
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/test-error.gyp
@@ -0,0 +1,31 @@
+# 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 Gyp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'TestApp/main.m',
+ 'TestApp/TestApp_Prefix.pch',
+ 'TestApp/TestAppAppDelegate.h',
+ 'TestApp/TestAppAppDelegate.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist-error.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/app-bundle/test.gyp b/tools/gyp/test/mac/app-bundle/test.gyp
new file mode 100644
index 00000000000000..21973c3623a482
--- /dev/null
+++ b/tools/gyp/test/mac/app-bundle/test.gyp
@@ -0,0 +1,41 @@
+# 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': 'dep_framework',
+ 'product_name': 'Dependency Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'empty.c', ],
+ },
+ {
+ 'target_name': 'test_app',
+ 'product_name': 'Test App Gyp',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'dependencies': [ 'dep_framework', ],
+ 'sources': [
+ 'TestApp/main.m',
+ 'TestApp/TestApp_Prefix.pch',
+ 'TestApp/TestAppAppDelegate.h',
+ 'TestApp/TestAppAppDelegate.m',
+ ],
+ 'mac_bundle_resources': [
+ 'TestApp/English.lproj/InfoPlist.strings', # UTF-8
+ 'TestApp/English.lproj/utf-16be.strings',
+ 'TestApp/English.lproj/utf-16le.strings',
+ 'TestApp/English.lproj/MainMenu.xib',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestApp/TestApp-Info.plist',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/archs/empty_main.cc b/tools/gyp/test/mac/archs/empty_main.cc
new file mode 100644
index 00000000000000..237c8ce181774d
--- /dev/null
+++ b/tools/gyp/test/mac/archs/empty_main.cc
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/archs/file.mm b/tools/gyp/test/mac/archs/file.mm
new file mode 100644
index 00000000000000..d0b39d1f6d8bb4
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file.mm
@@ -0,0 +1 @@
+MyInt f() { return 0; }
diff --git a/tools/gyp/test/mac/archs/file_a.cc b/tools/gyp/test/mac/archs/file_a.cc
new file mode 100644
index 00000000000000..7307873c8374fb
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file_a.cc
@@ -0,0 +1,8 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "file_a.h"
+
+void DependentFunctionA() {
+}
diff --git a/tools/gyp/test/mac/archs/file_a.h b/tools/gyp/test/mac/archs/file_a.h
new file mode 100644
index 00000000000000..7439d131820b7e
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file_a.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _INCLUDED_TEST_MAC_DEPENDENCIES_FILE_A_H_
+#define _INCLUDED_TEST_MAC_DEPENDENCIES_FILE_A_H_
+
+void DependentFunctionA();
+
+#endif // _INCLUDED_TEST_MAC_DEPENDENCIES_FILE_A_H_
diff --git a/tools/gyp/test/mac/archs/file_b.cc b/tools/gyp/test/mac/archs/file_b.cc
new file mode 100644
index 00000000000000..72d59cbfb4661a
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file_b.cc
@@ -0,0 +1,8 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "file_b.h"
+
+void DependentFunctionB() {
+}
diff --git a/tools/gyp/test/mac/archs/file_b.h b/tools/gyp/test/mac/archs/file_b.h
new file mode 100644
index 00000000000000..eb272ece5504e4
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file_b.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _INCLUDED_TEST_MAC_DEPENDENCIES_FILE_B_H_
+#define _INCLUDED_TEST_MAC_DEPENDENCIES_FILE_B_H_
+
+void DependentFunctionB();
+
+#endif // _INCLUDED_TEST_MAC_DEPENDENCIES_FILE_B_H_
diff --git a/tools/gyp/test/mac/archs/file_c.cc b/tools/gyp/test/mac/archs/file_c.cc
new file mode 100644
index 00000000000000..ca39f7a6719f07
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file_c.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "file_a.h"
+#include "file_b.h"
+
+void PublicFunctionC() {
+ DependentFunctionA();
+ DependentFunctionB();
+}
diff --git a/tools/gyp/test/mac/archs/file_d.cc b/tools/gyp/test/mac/archs/file_d.cc
new file mode 100644
index 00000000000000..c40911cdca88fe
--- /dev/null
+++ b/tools/gyp/test/mac/archs/file_d.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "file_a.h"
+#include "file_b.h"
+
+void PublicFunctionD() {
+ DependentFunctionA();
+ DependentFunctionB();
+}
diff --git a/tools/gyp/test/mac/archs/header.h b/tools/gyp/test/mac/archs/header.h
new file mode 100644
index 00000000000000..0716e500c5a69d
--- /dev/null
+++ b/tools/gyp/test/mac/archs/header.h
@@ -0,0 +1 @@
+typedef int MyInt;
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 00000000000000..94216a74df6c36
--- /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 00000000000000..5a4a17b5eb14bd
--- /dev/null
+++ b/tools/gyp/test/mac/archs/my_main_file.cc
@@ -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. */
+#include
+extern int x;
+int main() {
+ printf("hello, world %d\n", x);
+}
diff --git a/tools/gyp/test/mac/archs/test-archs-multiarch.gyp b/tools/gyp/test/mac/archs/test-archs-multiarch.gyp
new file mode 100644
index 00000000000000..567e8a6653ce86
--- /dev/null
+++ b/tools/gyp/test/mac/archs/test-archs-multiarch.gyp
@@ -0,0 +1,92 @@
+# 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': 'static_32_64',
+ 'type': 'static_library',
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'shared_32_64',
+ 'type': 'shared_library',
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'shared_32_64_bundle',
+ 'product_name': 'My Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'module_32_64',
+ 'type': 'loadable_module',
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'module_32_64_bundle',
+ 'product_name': 'My Bundle',
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'exe_32_64',
+ 'type': 'executable',
+ 'sources': [ 'empty_main.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ {
+ 'target_name': 'exe_32_64_bundle',
+ 'product_name': 'Test App',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'empty_main.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ # This only needs to compile.
+ {
+ 'target_name': 'precompiled_prefix_header_mm_32_64',
+ 'type': 'shared_library',
+ 'sources': [ 'file.mm', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES',
+ },
+ },
+ # This does not compile but should not cause generation errors.
+ {
+ 'target_name': 'exe_32_64_no_sources',
+ 'type': 'executable',
+ 'dependencies': [
+ 'static_32_64',
+ ],
+ 'sources': [],
+ 'xcode_settings': {
+ 'ARCHS': ['i386', 'x86_64'],
+ },
+ },
+ ]
+}
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 00000000000000..d11a896273cab7
--- /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-dependencies.gyp b/tools/gyp/test/mac/archs/test-dependencies.gyp
new file mode 100644
index 00000000000000..0431f5f2f4fab2
--- /dev/null
+++ b/tools/gyp/test/mac/archs/test-dependencies.gyp
@@ -0,0 +1,92 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'target_defaults': {
+ 'xcode_settings': {
+ 'ARCHS': ['i386', 'x86_64'],
+ },
+ },
+ 'targets': [
+ {
+ 'target_name': 'target_a',
+ 'type': 'static_library',
+ 'sources': [
+ 'file_a.cc',
+ 'file_a.h',
+ ],
+ },
+ {
+ 'target_name': 'target_b',
+ 'type': 'static_library',
+ 'sources': [
+ 'file_b.cc',
+ 'file_b.h',
+ ],
+ },
+ {
+ 'target_name': 'target_c_standalone_helper',
+ 'type': 'loadable_module',
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'target_a',
+ 'target_b',
+ ],
+ 'sources': [
+ 'file_c.cc',
+ ],
+ },
+ {
+ 'target_name': 'target_c_standalone',
+ 'type': 'none',
+ 'dependencies': [
+ 'target_c_standalone_helper',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'Package C',
+ 'inputs': [],
+ 'outputs': [
+ '<(PRODUCT_DIR)/libc_standalone.a',
+ ],
+ 'action': [
+ 'touch',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'target_d_standalone_helper',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'target_a',
+ 'target_b',
+ ],
+ 'sources': [
+ 'file_d.cc',
+ ],
+ },
+ {
+ 'target_name': 'target_d_standalone',
+ 'type': 'none',
+ 'dependencies': [
+ 'target_d_standalone_helper',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'Package D',
+ 'inputs': [],
+ 'outputs': [
+ '<(PRODUCT_DIR)/libd_standalone.a',
+ ],
+ 'action': [
+ 'touch',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ }
+ ],
+}
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 00000000000000..8f3b6b47cc163f
--- /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/archs/test-valid-archs.gyp b/tools/gyp/test/mac/archs/test-valid-archs.gyp
new file mode 100644
index 00000000000000..c90ec1fe9b3b0b
--- /dev/null
+++ b/tools/gyp/test/mac/archs/test-valid-archs.gyp
@@ -0,0 +1,28 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lib',
+ 'product_name': 'Test',
+ 'type': 'static_library',
+ 'sources': [ 'my_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': ['i386', 'x86_64', 'unknown-arch'],
+ 'VALID_ARCHS': ['x86_64'],
+ },
+ },
+ {
+ 'target_name': 'exe',
+ 'product_name': 'Test',
+ 'type': 'executable',
+ 'dependencies': [ 'lib' ],
+ 'sources': [ 'my_main_file.cc' ],
+ 'xcode_settings': {
+ 'ARCHS': ['i386', 'x86_64', 'unknown-arch'],
+ 'VALID_ARCHS': ['x86_64'],
+ },
+ }]
+}
diff --git a/tools/gyp/test/mac/bundle-resources/change.sh b/tools/gyp/test/mac/bundle-resources/change.sh
new file mode 100755
index 00000000000000..6d0fe6c7c279ac
--- /dev/null
+++ b/tools/gyp/test/mac/bundle-resources/change.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+tr a-z A-Z < "${1}" > "${2}"
diff --git a/tools/gyp/test/mac/bundle-resources/executable-file.sh b/tools/gyp/test/mac/bundle-resources/executable-file.sh
new file mode 100755
index 00000000000000..796953a1a2cf23
--- /dev/null
+++ b/tools/gyp/test/mac/bundle-resources/executable-file.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+echo echo echo echo cho ho o o
diff --git a/tools/gyp/test/mac/bundle-resources/secret.txt b/tools/gyp/test/mac/bundle-resources/secret.txt
new file mode 100644
index 00000000000000..8baef1b4abc478
--- /dev/null
+++ b/tools/gyp/test/mac/bundle-resources/secret.txt
@@ -0,0 +1 @@
+abc
diff --git a/tools/gyp/test/mac/bundle-resources/test.gyp b/tools/gyp/test/mac/bundle-resources/test.gyp
new file mode 100644
index 00000000000000..98f57a0999e321
--- /dev/null
+++ b/tools/gyp/test/mac/bundle-resources/test.gyp
@@ -0,0 +1,58 @@
+# Copyright (c) 2013 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': 'resource',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'mac_bundle_resources': [
+ 'secret.txt',
+ 'executable-file.sh',
+ ],
+ },
+ # A rule with process_outputs_as_mac_bundle_resources should copy files
+ # into the Resources folder.
+ {
+ 'target_name': 'source_rule',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'secret.txt',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'bundlerule',
+ 'extension': 'txt',
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).txt',
+ ],
+ 'action': ['./change.sh', '<(RULE_INPUT_PATH)', '<@(_outputs)'],
+ 'message': 'Running rule on <(RULE_INPUT_PATH)',
+ 'process_outputs_as_mac_bundle_resources': 1,
+ },
+ ],
+ },
+ # So should an ordinary rule acting on mac_bundle_resources.
+ {
+ 'target_name': 'resource_rule',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'mac_bundle_resources': [
+ 'secret.txt',
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'bundlerule',
+ 'extension': 'txt',
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).txt',
+ ],
+ 'action': ['./change.sh', '<(RULE_INPUT_PATH)', '<@(_outputs)'],
+ 'message': 'Running rule on <(RULE_INPUT_PATH)',
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/cflags/ccfile.cc b/tools/gyp/test/mac/cflags/ccfile.cc
new file mode 100644
index 00000000000000..1a54d18eec7122
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/ccfile.cc
@@ -0,0 +1,7 @@
+#ifdef CFLAG
+#error CFLAG should not be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/ccfile_withcflags.cc b/tools/gyp/test/mac/cflags/ccfile_withcflags.cc
new file mode 100644
index 00000000000000..de078a06417628
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/ccfile_withcflags.cc
@@ -0,0 +1,7 @@
+#ifndef CFLAG
+#error CFLAG should be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/cfile.c b/tools/gyp/test/mac/cflags/cfile.c
new file mode 100644
index 00000000000000..0af9d0af5da474
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/cfile.c
@@ -0,0 +1,7 @@
+#ifndef CFLAG
+#error CFLAG should be set
+#endif
+
+#ifdef CCFLAG
+#error CCFLAG should not be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/cppfile.cpp b/tools/gyp/test/mac/cflags/cppfile.cpp
new file mode 100644
index 00000000000000..1a54d18eec7122
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/cppfile.cpp
@@ -0,0 +1,7 @@
+#ifdef CFLAG
+#error CFLAG should not be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/cppfile_withcflags.cpp b/tools/gyp/test/mac/cflags/cppfile_withcflags.cpp
new file mode 100644
index 00000000000000..de078a06417628
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/cppfile_withcflags.cpp
@@ -0,0 +1,7 @@
+#ifndef CFLAG
+#error CFLAG should be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/cxxfile.cxx b/tools/gyp/test/mac/cflags/cxxfile.cxx
new file mode 100644
index 00000000000000..1a54d18eec7122
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/cxxfile.cxx
@@ -0,0 +1,7 @@
+#ifdef CFLAG
+#error CFLAG should not be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/cxxfile_withcflags.cxx b/tools/gyp/test/mac/cflags/cxxfile_withcflags.cxx
new file mode 100644
index 00000000000000..de078a06417628
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/cxxfile_withcflags.cxx
@@ -0,0 +1,7 @@
+#ifndef CFLAG
+#error CFLAG should be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/mfile.m b/tools/gyp/test/mac/cflags/mfile.m
new file mode 100644
index 00000000000000..0af9d0af5da474
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/mfile.m
@@ -0,0 +1,7 @@
+#ifndef CFLAG
+#error CFLAG should be set
+#endif
+
+#ifdef CCFLAG
+#error CCFLAG should not be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/mmfile.mm b/tools/gyp/test/mac/cflags/mmfile.mm
new file mode 100644
index 00000000000000..1a54d18eec7122
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/mmfile.mm
@@ -0,0 +1,7 @@
+#ifdef CFLAG
+#error CFLAG should not be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/mmfile_withcflags.mm b/tools/gyp/test/mac/cflags/mmfile_withcflags.mm
new file mode 100644
index 00000000000000..de078a06417628
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/mmfile_withcflags.mm
@@ -0,0 +1,7 @@
+#ifndef CFLAG
+#error CFLAG should be set
+#endif
+
+#ifndef CCFLAG
+#error CCFLAG should be set
+#endif
diff --git a/tools/gyp/test/mac/cflags/test.gyp b/tools/gyp/test/mac/cflags/test.gyp
new file mode 100644
index 00000000000000..d330a548f647ea
--- /dev/null
+++ b/tools/gyp/test/mac/cflags/test.gyp
@@ -0,0 +1,132 @@
+# 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': 'mytarget',
+ 'type': 'shared_library',
+ 'sources': [
+ 'cfile.c',
+ 'mfile.m',
+ 'ccfile.cc',
+ 'cppfile.cpp',
+ 'cxxfile.cxx',
+ 'mmfile.mm',
+ ],
+ 'xcode_settings': {
+ # Normally, defines would go in 'defines' instead. This is just for
+ # testing.
+ 'OTHER_CFLAGS': [
+ '-DCFLAG',
+ ],
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '-DCCFLAG',
+ ],
+ 'GCC_C_LANGUAGE_STANDARD': 'c99',
+ },
+ },
+ {
+ 'target_name': 'mytarget_reuse_cflags',
+ 'type': 'shared_library',
+ 'sources': [
+ 'cfile.c',
+ 'mfile.m',
+ 'ccfile_withcflags.cc',
+ 'cppfile_withcflags.cpp',
+ 'cxxfile_withcflags.cxx',
+ 'mmfile_withcflags.mm',
+ ],
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-DCFLAG',
+ ],
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '$OTHER_CFLAGS',
+ '-DCCFLAG',
+ ],
+ # This is a C-only flag, to check these don't get added to C++ files.
+ 'GCC_C_LANGUAGE_STANDARD': 'c99',
+ },
+ },
+ {
+ 'target_name': 'mytarget_inherit_cflags',
+ 'type': 'shared_library',
+ 'sources': [
+ 'cfile.c',
+ 'mfile.m',
+ 'ccfile_withcflags.cc',
+ 'cppfile_withcflags.cpp',
+ 'cxxfile_withcflags.cxx',
+ 'mmfile_withcflags.mm',
+ ],
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-DCFLAG',
+ ],
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '$inherited',
+ '-DCCFLAG',
+ ],
+ 'GCC_C_LANGUAGE_STANDARD': 'c99',
+ },
+ },
+ {
+ 'target_name': 'mytarget_inherit_cflags_parens',
+ 'type': 'shared_library',
+ 'sources': [
+ 'cfile.c',
+ 'mfile.m',
+ 'ccfile_withcflags.cc',
+ 'cppfile_withcflags.cpp',
+ 'cxxfile_withcflags.cxx',
+ 'mmfile_withcflags.mm',
+ ],
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-DCFLAG',
+ ],
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '$(inherited)',
+ '-DCCFLAG',
+ ],
+ 'GCC_C_LANGUAGE_STANDARD': 'c99',
+ },
+ },
+ {
+ 'target_name': 'mytarget_inherit_cflags_braces',
+ 'type': 'shared_library',
+ 'sources': [
+ 'cfile.c',
+ 'mfile.m',
+ 'ccfile_withcflags.cc',
+ 'cppfile_withcflags.cpp',
+ 'cxxfile_withcflags.cxx',
+ 'mmfile_withcflags.mm',
+ ],
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-DCFLAG',
+ ],
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '${inherited}',
+ '-DCCFLAG',
+ ],
+ 'GCC_C_LANGUAGE_STANDARD': 'c99',
+ },
+ },
+ {
+ 'target_name': 'ansi_standard',
+ 'type': 'shared_library',
+ 'sources': [
+ 'cfile.c',
+ ],
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [
+ '-DCFLAG',
+ ],
+ 'GCC_C_LANGUAGE_STANDARD': 'ansi',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/clang-cxx-language-standard/c++11.cc b/tools/gyp/test/mac/clang-cxx-language-standard/c++11.cc
new file mode 100644
index 00000000000000..4367ff9ff6a3ec
--- /dev/null
+++ b/tools/gyp/test/mac/clang-cxx-language-standard/c++11.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 2013 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.
+
+static_assert(__cplusplus == 201103L, "wrong c++ standard version");
+
+int main() { return 0; }
diff --git a/tools/gyp/test/mac/clang-cxx-language-standard/c++98.cc b/tools/gyp/test/mac/clang-cxx-language-standard/c++98.cc
new file mode 100644
index 00000000000000..8ed4e92a71a0a8
--- /dev/null
+++ b/tools/gyp/test/mac/clang-cxx-language-standard/c++98.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2013 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.
+
+#if __cplusplus != 199711L
+#error wrong c++ standard version
+#endif
+
+enum cxx11_keywords {
+ alignas,
+ alignof,
+ char16_t,
+ char32_t,
+ constexpr,
+ decltype,
+ noexcept,
+ nullptr,
+ override,
+ static_assert,
+ thread_local,
+};
+
+int main() { return 0; }
diff --git a/tools/gyp/test/mac/clang-cxx-language-standard/clang-cxx-language-standard.gyp b/tools/gyp/test/mac/clang-cxx-language-standard/clang-cxx-language-standard.gyp
new file mode 100644
index 00000000000000..20d06d48748535
--- /dev/null
+++ b/tools/gyp/test/mac/clang-cxx-language-standard/clang-cxx-language-standard.gyp
@@ -0,0 +1,29 @@
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'c++98',
+ 'type': 'executable',
+ 'sources': [ 'c++98.cc', ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'c++98',
+ },
+ },
+ {
+ 'target_name': 'c++11',
+ 'type': 'executable',
+ 'sources': [ 'c++11.cc', ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'c++0x',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/clang-cxx-library/clang-cxx-library.gyp b/tools/gyp/test/mac/clang-cxx-library/clang-cxx-library.gyp
new file mode 100644
index 00000000000000..651c1c1891742f
--- /dev/null
+++ b/tools/gyp/test/mac/clang-cxx-library/clang-cxx-library.gyp
@@ -0,0 +1,31 @@
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+ 'targets': [
+ {
+ 'target_name': 'libc++',
+ 'type': 'executable',
+ 'sources': [ 'libc++.cc', ],
+ 'xcode_settings': {
+ 'CC': 'clang',
+ # libc++ requires OS X 10.7+.
+ 'MACOSX_DEPLOYMENT_TARGET': '10.7',
+ 'CLANG_CXX_LIBRARY': 'libc++',
+ },
+ },
+ {
+ 'target_name': 'libstdc++',
+ 'type': 'executable',
+ 'sources': [ 'libstdc++.cc', ],
+ 'xcode_settings': {
+ 'CC': 'clang',
+ 'CLANG_CXX_LIBRARY': 'libstdc++',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/clang-cxx-library/libc++.cc b/tools/gyp/test/mac/clang-cxx-library/libc++.cc
new file mode 100644
index 00000000000000..95e4b9cc5899b3
--- /dev/null
+++ b/tools/gyp/test/mac/clang-cxx-library/libc++.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2013 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
+#ifndef _LIBCPP_VERSION
+#error expected std library: libc++
+#endif
+
+int main() { std::string x; return x.size(); }
diff --git a/tools/gyp/test/mac/clang-cxx-library/libstdc++.cc b/tools/gyp/test/mac/clang-cxx-library/libstdc++.cc
new file mode 100644
index 00000000000000..b1a7c88ed47d5c
--- /dev/null
+++ b/tools/gyp/test/mac/clang-cxx-library/libstdc++.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2013 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
+#ifndef __GLIBCXX__
+#error expected std library: libstdc++
+#endif
+
+int main() { std::string x; return x.size(); }
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 00000000000000..237c8ce181774d
--- /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 00000000000000..54fc92293d1008
--- /dev/null
+++ b/tools/gyp/test/mac/copy-dylib/test.gyp
@@ -0,0 +1,30 @@
+# 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/debuginfo/file.c b/tools/gyp/test/mac/debuginfo/file.c
new file mode 100644
index 00000000000000..9cddaf1b0b401b
--- /dev/null
+++ b/tools/gyp/test/mac/debuginfo/file.c
@@ -0,0 +1,6 @@
+// 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.
+
+void f() {}
+int main() {}
diff --git a/tools/gyp/test/mac/debuginfo/test.gyp b/tools/gyp/test/mac/debuginfo/test.gyp
new file mode 100644
index 00000000000000..3faf6b5c76edf3
--- /dev/null
+++ b/tools/gyp/test/mac/debuginfo/test.gyp
@@ -0,0 +1,82 @@
+# 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': 'nonbundle_static_library',
+ 'type': 'static_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+ {
+ 'target_name': 'nonbundle_shared_library',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+ {
+ 'target_name': 'nonbundle_loadable_module',
+ 'type': 'loadable_module',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+ {
+ 'target_name': 'nonbundle_executable',
+ 'type': 'executable',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+
+ {
+ 'target_name': 'bundle_shared_library',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+ {
+ 'target_name': 'bundle_loadable_module',
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+ {
+ 'target_name': 'my_app',
+ 'product_name': 'My App',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
+ 'DEPLOYMENT_POSTPROCESSING': 'YES',
+ 'STRIP_INSTALLED_PRODUCT': 'YES',
+ },
+ },
+ ],
+}
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 00000000000000..b92732c79e00cf
--- /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 00000000000000..5e05a5190c8931
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.yourcompany.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ NSPrincipalClass
+
+
+
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 00000000000000..d64ff8ca23dbf9
--- /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 00000000000000..931bce637edaa2
--- /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 00000000000000..9f77b1f1f51c07
--- /dev/null
+++ b/tools/gyp/test/mac/depend-on-bundle/test.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': '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/deployment-target/check-version-min.c b/tools/gyp/test/mac/deployment-target/check-version-min.c
new file mode 100644
index 00000000000000..6b05a27f0aea0d
--- /dev/null
+++ b/tools/gyp/test/mac/deployment-target/check-version-min.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013 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
+
+/* GYPTEST_MAC_VERSION_MIN: should be set to the corresponding value of
+ * xcode setting 'MACOSX_DEPLOYMENT_TARGET', otherwise both should be
+ * left undefined.
+ *
+ * GYPTEST_IOS_VERSION_MIN: should be set to the corresponding value of
+ * xcode setting 'IPHONEOS_DEPLOYMENT_TARGET', otherwise both should be
+ * left undefined.
+ */
+
+#if defined(GYPTEST_MAC_VERSION_MIN)
+# if GYPTEST_MAC_VERSION_MIN != __MAC_OS_X_VERSION_MIN_REQUIRED
+# error __MAC_OS_X_VERSION_MIN_REQUIRED has wrong value
+# endif
+#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+# error __MAC_OS_X_VERSION_MIN_REQUIRED should be undefined
+#endif
+
+#if defined(GYPTEST_IOS_VERSION_MIN)
+# if GYPTEST_IOS_VERSION_MIN != __IPHONE_OS_VERSION_MIN_REQUIRED
+# error __IPHONE_OS_VERSION_MIN_REQUIRED has wrong value
+# endif
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+# error __IPHONE_OS_VERSION_MIN_REQUIRED should be undefined
+#endif
+
+int main() { return 0; }
diff --git a/tools/gyp/test/mac/deployment-target/deployment-target.gyp b/tools/gyp/test/mac/deployment-target/deployment-target.gyp
new file mode 100644
index 00000000000000..24b87b519a000d
--- /dev/null
+++ b/tools/gyp/test/mac/deployment-target/deployment-target.gyp
@@ -0,0 +1,27 @@
+# Copyright (c) 2013 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': 'macosx-version-min-10.5',
+ 'type': 'executable',
+ 'sources': [ 'check-version-min.c', ],
+ 'defines': [ 'GYPTEST_MAC_VERSION_MIN=1050', ],
+ 'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.5',
+ },
+ },
+ {
+ 'target_name': 'macosx-version-min-10.6',
+ 'type': 'executable',
+ 'sources': [ 'check-version-min.c', ],
+ 'defines': [ 'GYPTEST_MAC_VERSION_MIN=1060', ],
+ 'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.6',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/framework-dirs/calculate.c b/tools/gyp/test/mac/framework-dirs/calculate.c
new file mode 100644
index 00000000000000..4e93bf54304ca7
--- /dev/null
+++ b/tools/gyp/test/mac/framework-dirs/calculate.c
@@ -0,0 +1,14 @@
+/* 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 CalculatePerformExpression(char* expr,
+ int significantDigits,
+ int flags,
+ char* answer);
+
+int main() {
+ char buffer[1024];
+ return CalculatePerformExpression("42", 1, 0, buffer);
+}
diff --git a/tools/gyp/test/mac/framework-dirs/framework-dirs.gyp b/tools/gyp/test/mac/framework-dirs/framework-dirs.gyp
new file mode 100644
index 00000000000000..bf1cbde4defc5a
--- /dev/null
+++ b/tools/gyp/test/mac/framework-dirs/framework-dirs.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': 'calculate',
+ 'type': 'executable',
+ 'sources': [
+ 'calculate.c',
+ ],
+ 'libraries': [
+ '/System/Library/PrivateFrameworks/Calculate.framework',
+ ],
+ 'mac_framework_dirs': [
+ '/System/Library/PrivateFrameworks',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/framework-headers/myframework.h b/tools/gyp/test/mac/framework-headers/myframework.h
new file mode 100644
index 00000000000000..961fc701bc0b51
--- /dev/null
+++ b/tools/gyp/test/mac/framework-headers/myframework.h
@@ -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.
+
+#import
+
+@interface TestObject : NSObject
+@end
diff --git a/tools/gyp/test/mac/framework-headers/myframework.m b/tools/gyp/test/mac/framework-headers/myframework.m
new file mode 100644
index 00000000000000..13d53a37ab716d
--- /dev/null
+++ b/tools/gyp/test/mac/framework-headers/myframework.m
@@ -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.
+
+#import "myframework.h"
+
+@implementation TestObject
+@end
diff --git a/tools/gyp/test/mac/framework-headers/test.gyp b/tools/gyp/test/mac/framework-headers/test.gyp
new file mode 100644
index 00000000000000..0c2e02b64434ab
--- /dev/null
+++ b/tools/gyp/test/mac/framework-headers/test.gyp
@@ -0,0 +1,44 @@
+# 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_framework_headers_framework',
+ 'product_name': 'TestFramework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'myframework.h',
+ 'myframework.m',
+ ],
+ 'mac_framework_headers': [
+ 'myframework.h',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ },{
+ 'target_name': 'test_framework_headers_static',
+ 'product_name': 'TestLibrary',
+ 'type': 'static_library',
+ 'xcode_settings': {
+ 'PUBLIC_HEADERS_FOLDER_PATH': 'include',
+ },
+ 'sources': [
+ 'myframework.h',
+ 'myframework.m',
+ ],
+ 'mac_framework_headers': [
+ 'myframework.h',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings b/tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings
new file mode 100644
index 00000000000000..2d92facd653be6
--- /dev/null
+++ b/tools/gyp/test/mac/framework/TestFramework/English.lproj/InfoPlist.strings
@@ -0,0 +1 @@
+/* Localized versions of Info.plist keys */
diff --git a/tools/gyp/test/mac/framework/TestFramework/Info.plist b/tools/gyp/test/mac/framework/TestFramework/Info.plist
new file mode 100644
index 00000000000000..a791b3ee48d1e6
--- /dev/null
+++ b/tools/gyp/test/mac/framework/TestFramework/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.yourcompany.${PRODUCT_NAME:identifier}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ NSPrincipalClass
+
+
+
diff --git a/tools/gyp/test/mac/framework/TestFramework/ObjCVector.h b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.h
new file mode 100644
index 00000000000000..c2450960cd24c8
--- /dev/null
+++ b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.h
@@ -0,0 +1,28 @@
+// 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.
+
+#import
+
+#ifdef __cplusplus
+struct ObjCVectorImp;
+#else
+typedef struct _ObjCVectorImpT ObjCVectorImp;
+#endif
+
+@interface ObjCVector : NSObject {
+ @private
+ ObjCVectorImp* imp_;
+}
+
+- (id)init;
+
+- (void)addObject:(id)obj;
+- (void)addObject:(id)obj atIndex:(NSUInteger)index;
+
+- (void)removeObject:(id)obj;
+- (void)removeObjectAtIndex:(NSUInteger)index;
+
+- (id)objectAtIndex:(NSUInteger)index;
+
+@end
diff --git a/tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm
new file mode 100644
index 00000000000000..cbf431f28d4d9d
--- /dev/null
+++ b/tools/gyp/test/mac/framework/TestFramework/ObjCVector.mm
@@ -0,0 +1,63 @@
+// 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.
+
+#import "ObjCVectorInternal.h"
+#import "ObjCVector.h"
+
+#include
+
+@interface ObjCVector (Private)
+- (std::vector::iterator)makeIterator:(NSUInteger)index;
+@end
+
+@implementation ObjCVector
+
+- (id)init {
+ if ((self = [super init])) {
+ imp_ = new ObjCVectorImp();
+ }
+ return self;
+}
+
+- (void)dealloc {
+ delete imp_;
+ [super dealloc];
+}
+
+- (void)addObject:(id)obj {
+ imp_->v.push_back([obj retain]);
+}
+
+- (void)addObject:(id)obj atIndex:(NSUInteger)index {
+ imp_->v.insert([self makeIterator:index], [obj retain]);
+}
+
+- (void)removeObject:(id)obj {
+ for (std::vector::iterator it = imp_->v.begin();
+ it != imp_->v.end();
+ ++it) {
+ if ([*it isEqual:obj]) {
+ [*it autorelease];
+ imp_->v.erase(it);
+ return;
+ }
+ }
+}
+
+- (void)removeObjectAtIndex:(NSUInteger)index {
+ [imp_->v[index] autorelease];
+ imp_->v.erase([self makeIterator:index]);
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+ return imp_->v[index];
+}
+
+- (std::vector::iterator)makeIterator:(NSUInteger)index {
+ std::vector::iterator it = imp_->v.begin();
+ it += index;
+ return it;
+}
+
+@end
diff --git a/tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h b/tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h
new file mode 100644
index 00000000000000..fb6c98258b5e2b
--- /dev/null
+++ b/tools/gyp/test/mac/framework/TestFramework/ObjCVectorInternal.h
@@ -0,0 +1,9 @@
+// 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.
+
+#include
+
+struct ObjCVectorImp {
+ std::vector v;
+};
diff --git a/tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch b/tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch
new file mode 100644
index 00000000000000..394f41d9576a62
--- /dev/null
+++ b/tools/gyp/test/mac/framework/TestFramework/TestFramework_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'TestFramework' target in the 'TestFramework' project.
+//
+
+#ifdef __OBJC__
+ #import
+#endif
diff --git a/tools/gyp/test/mac/framework/empty.c b/tools/gyp/test/mac/framework/empty.c
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/tools/gyp/test/mac/framework/framework.gyp b/tools/gyp/test/mac/framework/framework.gyp
new file mode 100644
index 00000000000000..eaaef3fc57b216
--- /dev/null
+++ b/tools/gyp/test/mac/framework/framework.gyp
@@ -0,0 +1,94 @@
+# 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': 'dep_framework',
+ 'product_name': 'Dependency Bundle',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'empty.c', ],
+ },
+ {
+ 'target_name': 'test_framework',
+ 'product_name': 'Test Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'dependencies': [ 'dep_framework', ],
+ 'sources': [
+ 'TestFramework/ObjCVector.h',
+ 'TestFramework/ObjCVectorInternal.h',
+ 'TestFramework/ObjCVector.mm',
+ ],
+ 'mac_bundle_resources': [
+ 'TestFramework/English.lproj/InfoPlist.strings',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+ ],
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'TestFramework/Info.plist',
+ 'GCC_DYNAMIC_NO_PIC': 'NO',
+ },
+ 'copies': [
+ # Test copying to a file that has envvars in its dest path.
+ # Needs to be in a mac_bundle target, else CONTENTS_FOLDER_PATH isn't
+ # set.
+ {
+ 'destination': '<(PRODUCT_DIR)/$(CONTENTS_FOLDER_PATH)/Libraries',
+ 'files': [
+ 'empty.c',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'copy_target',
+ 'type': 'none',
+ 'dependencies': [ 'test_framework', 'dep_framework', ],
+ 'copies': [
+ # Test copying directories with spaces in src and dest paths.
+ {
+ 'destination': '<(PRODUCT_DIR)/Test Framework.framework/foo',
+ 'files': [
+ '<(PRODUCT_DIR)/Dependency Bundle.framework',
+ ],
+ },
+ ],
+ 'actions': [
+ {
+ 'action_name': 'aektschn',
+ 'inputs': [],
+ 'outputs': ['<(PRODUCT_DIR)/touched_file'],
+ 'action': ['touch', '${BUILT_PRODUCTS_DIR}/action_file'],
+ },
+ ],
+ },
+ {
+ 'target_name': 'copy_target_code_sign',
+ 'type': 'none',
+ 'dependencies': [ 'test_framework', 'dep_framework', ],
+ 'copies': [
+ # Test copying directories with spaces in src and dest paths.
+ {
+ 'destination': '<(PRODUCT_DIR)/Test Framework.framework/foo',
+ 'files': [
+ '<(PRODUCT_DIR)/Dependency Bundle.framework',
+ ],
+ 'xcode_code_sign': 1,
+ },
+ ],
+ 'actions': [
+ {
+ 'action_name': 'aektschn',
+ 'inputs': [],
+ 'outputs': ['<(PRODUCT_DIR)/touched_file'],
+ 'action': ['touch', '${BUILT_PRODUCTS_DIR}/action_file'],
+ },
+ ],
+ },
+ ],
+}
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 00000000000000..153e34ddd681ca
--- /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 00000000000000..cda46c839badec
--- /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 00000000000000..58da2d8e9acf40
--- /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 00000000000000..c84eeaa465ab0e
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-action-envvars.py
@@ -0,0 +1,36 @@
+#!/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'])
+
+ # The xcode-ninja generator handles gypfiles which are not at the
+ # project root incorrectly.
+ # cf. https://code.google.com/p/gyp/issues/detail?id=460
+ if test.format == 'xcode-ninja':
+ test.skip_test()
+
+ 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-assets-catalog.py b/tools/gyp/test/mac/gyptest-app-assets-catalog.py
new file mode 100755
index 00000000000000..ecb5a78c8b41d7
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-app-assets-catalog.py
@@ -0,0 +1,120 @@
+#!/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 app bundles are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import os
+import plistlib
+import subprocess
+import sys
+
+
+def ExpectEq(expected, actual):
+ if expected != actual:
+ print >>sys.stderr, 'Expected "%s", got "%s"' % (expected, actual)
+ test.fail_test()
+
+def ls(path):
+ '''Returns a list of all files in a directory, relative to the directory.'''
+ result = []
+ for dirpath, _, files in os.walk(path):
+ for f in files:
+ result.append(os.path.join(dirpath, f)[len(path) + 1:])
+ return result
+
+# Xcode supports for assets catalog was introduced in Xcode 6.0
+if sys.platform == 'darwin' and TestMac.Xcode.Version() >= '0600':
+ test_gyp_path = 'test-assets-catalog.gyp'
+ test_app_path = 'Test App Assets Catalog Gyp.app'
+
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+ test.run_gyp(test_gyp_path, chdir='app-bundle')
+ test.build(test_gyp_path, test.ALL, chdir='app-bundle')
+
+ # Binary
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'Contents/MacOS/Test App Assets Catalog Gyp'),
+ chdir='app-bundle')
+
+ # Info.plist
+ info_plist = test.built_file_path(
+ os.path.join(test_app_path, 'Contents/Info.plist'),
+ chdir='app-bundle')
+ test.must_exist(info_plist)
+ test.must_contain(
+ info_plist,
+ 'com.google.Test-App-Assets-Catalog-Gyp') # Variable expansion
+ test.must_not_contain(info_plist, '${MACOSX_DEPLOYMENT_TARGET}');
+
+ if test.format != 'make':
+ # TODO: Synthesized plist entries aren't hooked up in the make generator.
+ machine = subprocess.check_output(['sw_vers', '-buildVersion']).rstrip('\n')
+ plist = plistlib.readPlist(info_plist)
+ ExpectEq(machine, plist['BuildMachineOSBuild'])
+
+ expected = ''
+ version = TestMac.Xcode.SDKVersion()
+ expected = 'macosx' + version
+ ExpectEq(expected, plist['DTSDKName'])
+ sdkbuild = TestMac.Xcode.SDKBuild()
+ if not sdkbuild:
+ # Above command doesn't work in Xcode 4.2.
+ sdkbuild = plist['BuildMachineOSBuild']
+ ExpectEq(sdkbuild, plist['DTSDKBuild'])
+ ExpectEq(TestMac.Xcode.Version(), plist['DTXcode'])
+ ExpectEq(TestMac.Xcode.Build(), plist['DTXcodeBuild'])
+
+ # Resources
+ strings_files = ['InfoPlist.strings', 'utf-16be.strings', 'utf-16le.strings']
+ for f in strings_files:
+ strings = test.built_file_path(
+ os.path.join(test_app_path, 'Contents/Resources/English.lproj', f),
+ chdir='app-bundle')
+ test.must_exist(strings)
+ # Xcodes writes UTF-16LE with BOM.
+ contents = open(strings, 'rb').read()
+ if not contents.startswith('\xff\xfe' + '/* Localized'.encode('utf-16le')):
+ test.fail_test()
+
+ test.built_file_must_exist(
+ os.path.join(
+ test_app_path, 'Contents/Resources/English.lproj/MainMenu.nib'),
+ chdir='app-bundle')
+
+ # make does not supports .xcassets files
+ extra_content_files = []
+ if test.format != 'make':
+ extra_content_files = ['Contents/Resources/Assets.car']
+ for f in extra_content_files:
+ test.built_file_must_exist(
+ os.path.join(test_app_path, f),
+ chdir='app-bundle')
+
+ # Packaging
+ test.built_file_must_exist(
+ os.path.join(test_app_path, 'Contents/PkgInfo'),
+ chdir='app-bundle')
+ test.built_file_must_match(
+ os.path.join(test_app_path, 'Contents/PkgInfo'), 'APPLause',
+ chdir='app-bundle')
+
+ # Check that no other files get added to the bundle.
+ if set(ls(test.built_file_path(test_app_path, chdir='app-bundle'))) != \
+ set(['Contents/MacOS/Test App Assets Catalog Gyp',
+ 'Contents/Info.plist',
+ 'Contents/Resources/English.lproj/MainMenu.nib',
+ 'Contents/PkgInfo',
+ ] + extra_content_files +
+ [os.path.join('Contents/Resources/English.lproj', f)
+ for f in strings_files]):
+ test.fail_test()
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-app-error.py b/tools/gyp/test/mac/gyptest-app-error.py
new file mode 100755
index 00000000000000..8f3cef28c11d15
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-app-error.py
@@ -0,0 +1,44 @@
+#!/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 invalid strings files cause the build to fail.
+"""
+
+import TestCmd
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ expected_error = 'Old-style plist parser: missing semicolon in dictionary'
+ saw_expected_error = [False] # Python2 has no "nonlocal" keyword.
+ def match(a, b):
+ if a == b:
+ return True
+ if not TestCmd.is_List(a):
+ a = a.split('\n')
+ if not TestCmd.is_List(b):
+ b = b.split('\n')
+ if expected_error in '\n'.join(a) + '\n'.join(b):
+ saw_expected_error[0] = True
+ return True
+ return False
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'], match=match)
+
+ test.run_gyp('test-error.gyp', chdir='app-bundle')
+
+ test.build('test-error.gyp', test.ALL, chdir='app-bundle')
+
+ # Ninja pipes stderr of subprocesses to stdout.
+ if test.format in ['ninja', 'xcode-ninja'] \
+ and expected_error in test.stdout():
+ saw_expected_error[0] = True
+
+ if saw_expected_error[0]:
+ test.pass_test()
+ else:
+ test.fail_test()
diff --git a/tools/gyp/test/mac/gyptest-app.py b/tools/gyp/test/mac/gyptest-app.py
new file mode 100755
index 00000000000000..5ec4dc29ab08cf
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-app.py
@@ -0,0 +1,115 @@
+#!/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 app bundles are built correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import os
+import plistlib
+import subprocess
+import sys
+
+
+def CheckFileXMLPropertyList(file):
+ output = subprocess.check_output(['file', file])
+ # The double space after XML is intentional.
+ if not 'XML document text' in output:
+ print 'File: Expected XML document text, got %s' % output
+ test.fail_test()
+
+def ExpectEq(expected, actual):
+ if expected != actual:
+ print >>sys.stderr, 'Expected "%s", got "%s"' % (expected, actual)
+ test.fail_test()
+
+def ls(path):
+ '''Returns a list of all files in a directory, relative to the directory.'''
+ result = []
+ for dirpath, _, files in os.walk(path):
+ for f in files:
+ result.append(os.path.join(dirpath, f)[len(path) + 1:])
+ return result
+
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='app-bundle')
+
+ test.build('test.gyp', test.ALL, chdir='app-bundle')
+
+ # Binary
+ test.built_file_must_exist('Test App Gyp.app/Contents/MacOS/Test App Gyp',
+ chdir='app-bundle')
+
+ # Info.plist
+ info_plist = test.built_file_path('Test App Gyp.app/Contents/Info.plist',
+ chdir='app-bundle')
+ test.must_exist(info_plist)
+ test.must_contain(info_plist, 'com.google.Test-App-Gyp') # Variable expansion
+ test.must_not_contain(info_plist, '${MACOSX_DEPLOYMENT_TARGET}');
+ CheckFileXMLPropertyList(info_plist)
+
+ if test.format != 'make':
+ # TODO: Synthesized plist entries aren't hooked up in the make generator.
+ machine = subprocess.check_output(['sw_vers', '-buildVersion']).rstrip('\n')
+ plist = plistlib.readPlist(info_plist)
+ ExpectEq(machine, plist['BuildMachineOSBuild'])
+
+ # Prior to Xcode 5.0.0, SDKROOT (and thus DTSDKName) was only defined if
+ # set in the Xcode project file. Starting with that version, it is always
+ # defined.
+ expected = ''
+ if TestMac.Xcode.Version() >= '0500':
+ version = TestMac.Xcode.SDKVersion()
+ expected = 'macosx' + version
+ ExpectEq(expected, plist['DTSDKName'])
+ sdkbuild = TestMac.Xcode.SDKBuild()
+ if not sdkbuild:
+ # Above command doesn't work in Xcode 4.2.
+ sdkbuild = plist['BuildMachineOSBuild']
+ ExpectEq(sdkbuild, plist['DTSDKBuild'])
+ ExpectEq(TestMac.Xcode.Version(), plist['DTXcode'])
+ ExpectEq(TestMac.Xcode.Build(), plist['DTXcodeBuild'])
+
+ # Resources
+ strings_files = ['InfoPlist.strings', 'utf-16be.strings', 'utf-16le.strings']
+ for f in strings_files:
+ strings = test.built_file_path(
+ os.path.join('Test App Gyp.app/Contents/Resources/English.lproj', f),
+ chdir='app-bundle')
+ test.must_exist(strings)
+ # Xcodes writes UTF-16LE with BOM.
+ contents = open(strings, 'rb').read()
+ if not contents.startswith('\xff\xfe' + '/* Localized'.encode('utf-16le')):
+ test.fail_test()
+
+ test.built_file_must_exist(
+ 'Test App Gyp.app/Contents/Resources/English.lproj/MainMenu.nib',
+ chdir='app-bundle')
+
+ # Packaging
+ test.built_file_must_exist('Test App Gyp.app/Contents/PkgInfo',
+ chdir='app-bundle')
+ test.built_file_must_match('Test App Gyp.app/Contents/PkgInfo', 'APPLause',
+ chdir='app-bundle')
+
+ # Check that no other files get added to the bundle.
+ if set(ls(test.built_file_path('Test App Gyp.app', chdir='app-bundle'))) != \
+ set(['Contents/MacOS/Test App Gyp',
+ 'Contents/Info.plist',
+ 'Contents/Resources/English.lproj/MainMenu.nib',
+ 'Contents/PkgInfo',
+ ] +
+ [os.path.join('Contents/Resources/English.lproj', f)
+ for f in strings_files]):
+ test.fail_test()
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-archs.py b/tools/gyp/test/mac/gyptest-archs.py
new file mode 100644
index 00000000000000..106a928e9290ef
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-archs.py
@@ -0,0 +1,95 @@
+#!/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 TestMac
+
+import re
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ 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)
+
+ if TestMac.Xcode.Version() >= '0500':
+ expected_type = ['x86_64']
+ else:
+ expected_type = ['i386']
+ TestMac.CheckFileType(test, result_file, expected_type)
+
+ test.run_gyp('test-valid-archs.gyp', chdir='archs')
+ test.build('test-valid-archs.gyp', test.ALL, chdir='archs')
+ result_file = test.built_file_path('Test', chdir='archs')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['x86_64'])
+
+ test.run_gyp('test-archs-x86_64.gyp', chdir='archs')
+ test.build('test-archs-x86_64.gyp', test.ALL, chdir='archs')
+ result_file = test.built_file_path('Test64', chdir='archs')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['x86_64'])
+
+ test.run_gyp('test-dependencies.gyp', chdir='archs')
+ test.build('test-dependencies.gyp', target=test.ALL, chdir='archs')
+ products = ['c_standalone', 'd_standalone']
+ for product in products:
+ result_file = test.built_file_path(
+ product, chdir='archs', type=test.STATIC_LIB)
+ test.must_exist(result_file)
+
+ if test.format != 'make':
+ # Build all targets except 'exe_32_64_no_sources' that does build
+ # but should not cause error when generating ninja files
+ targets = [
+ 'static_32_64', 'shared_32_64', 'shared_32_64_bundle',
+ 'module_32_64', 'module_32_64_bundle',
+ 'exe_32_64', 'exe_32_64_bundle', 'precompiled_prefix_header_mm_32_64',
+ ]
+
+ test.run_gyp('test-archs-multiarch.gyp', chdir='archs')
+
+ for target in targets:
+ test.build('test-archs-multiarch.gyp', target=target, chdir='archs')
+
+ result_file = test.built_file_path(
+ 'static_32_64', chdir='archs', type=test.STATIC_LIB)
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['i386', 'x86_64'])
+
+ result_file = test.built_file_path(
+ 'shared_32_64', chdir='archs', type=test.SHARED_LIB)
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['i386', 'x86_64'])
+
+ result_file = test.built_file_path('My Framework.framework/My Framework',
+ chdir='archs')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['i386', 'x86_64'])
+ # Check that symbol "_x" made it into both versions of the binary:
+ if not all(['D _x' in subprocess.check_output(
+ ['nm', '-arch', arch, result_file]) for arch in ['i386', 'x86_64']]):
+ # This can only flakily fail, due to process ordering issues. If this
+ # does fail flakily, then something's broken, it's not the test at fault.
+ test.fail_test()
+
+ result_file = test.built_file_path(
+ 'exe_32_64', chdir='archs', type=test.EXECUTABLE)
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['i386', 'x86_64'])
+
+ result_file = test.built_file_path('Test App.app/Contents/MacOS/Test App',
+ chdir='archs')
+ test.must_exist(result_file)
+ TestMac.CheckFileType(test, result_file, ['i386', 'x86_64'])
diff --git a/tools/gyp/test/mac/gyptest-bundle-resources.py b/tools/gyp/test/mac/gyptest-bundle-resources.py
new file mode 100644
index 00000000000000..824b17f6b9bb5b
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-bundle-resources.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 things related to bundle resources.
+"""
+
+import TestGyp
+
+import os
+import stat
+import sys
+
+
+def check_attribs(path, expected_exec_bit):
+ out_path = test.built_file_path(
+ os.path.join('resource.app/Contents/Resources', path), chdir=CHDIR)
+
+ in_stat = os.stat(os.path.join(CHDIR, path))
+ out_stat = os.stat(out_path)
+ if in_stat.st_mtime == out_stat.st_mtime:
+ test.fail_test()
+ if out_stat.st_mode & stat.S_IXUSR != expected_exec_bit:
+ test.fail_test()
+
+
+if sys.platform == 'darwin':
+ # set |match| to ignore build stderr output.
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'bundle-resources'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ test.built_file_must_match('resource.app/Contents/Resources/secret.txt',
+ 'abc\n', chdir=CHDIR)
+ test.built_file_must_match('source_rule.app/Contents/Resources/secret.txt',
+ 'ABC\n', chdir=CHDIR)
+
+ test.built_file_must_match(
+ 'resource.app/Contents/Resources/executable-file.sh',
+ '#!/bin/bash\n'
+ '\n'
+ 'echo echo echo echo cho ho o o\n', chdir=CHDIR)
+
+ check_attribs('executable-file.sh', expected_exec_bit=stat.S_IXUSR)
+ check_attribs('secret.txt', expected_exec_bit=0)
+
+ # TODO(thakis): This currently fails with make.
+ if test.format != 'make':
+ test.built_file_must_match(
+ 'resource_rule.app/Contents/Resources/secret.txt', 'ABC\n', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-cflags.py b/tools/gyp/test/mac/gyptest-cflags.py
new file mode 100644
index 00000000000000..17afd1566542c0
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-cflags.py
@@ -0,0 +1,21 @@
+#!/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 compile-time flags work.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+ CHDIR = 'cflags'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-clang-cxx-language-standard.py b/tools/gyp/test/mac/gyptest-clang-cxx-language-standard.py
new file mode 100644
index 00000000000000..48708d84048d1a
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-clang-cxx-language-standard.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 CLANG_CXX_LANGUAGE_STANDARD works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['make', 'ninja', 'xcode'])
+
+ test.run_gyp('clang-cxx-language-standard.gyp',
+ chdir='clang-cxx-language-standard')
+
+ test.build('clang-cxx-language-standard.gyp', test.ALL,
+ chdir='clang-cxx-language-standard')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-clang-cxx-library.py b/tools/gyp/test/mac/gyptest-clang-cxx-library.py
new file mode 100644
index 00000000000000..effb434cba6323
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-clang-cxx-library.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 CLANG_CXX_LIBRARY works.
+"""
+
+import TestGyp
+import TestMac
+
+import os
+import sys
+
+if sys.platform == 'darwin':
+ # Xcode 4.2 on OS X 10.6 doesn't install the libc++ headers, don't run this
+ # test there.
+ if TestMac.Xcode.Version() <= '0420':
+ sys.exit(0)
+
+ test = TestGyp.TestGyp(formats=['make', 'ninja', 'xcode'])
+ test.run_gyp('clang-cxx-library.gyp', chdir='clang-cxx-library')
+ test.build('clang-cxx-library.gyp', test.ALL, chdir='clang-cxx-library')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-copies.py b/tools/gyp/test/mac/gyptest-copies.py
new file mode 100755
index 00000000000000..49685095016cf3
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-copies.py
@@ -0,0 +1,52 @@
+#!/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 'copies' with app bundles are handled correctly.
+"""
+
+import TestGyp
+
+import os
+import sys
+import time
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('framework.gyp', chdir='framework')
+
+ test.build('framework.gyp', 'copy_target', chdir='framework')
+
+ # Check that the copy succeeded.
+ test.built_file_must_exist(
+ 'Test Framework.framework/foo/Dependency Bundle.framework',
+ chdir='framework')
+ test.built_file_must_exist(
+ 'Test Framework.framework/foo/Dependency Bundle.framework/Versions/A',
+ chdir='framework')
+ test.built_file_must_exist(
+ 'Test Framework.framework/Versions/A/Libraries/empty.c',
+ chdir='framework')
+
+
+ # Check that rebuilding the target a few times works.
+ dep_bundle = test.built_file_path('Dependency Bundle.framework',
+ chdir='framework')
+ mtime = os.path.getmtime(dep_bundle)
+ atime = os.path.getatime(dep_bundle)
+ for i in range(3):
+ os.utime(dep_bundle, (atime + i * 1000, mtime + i * 1000))
+ test.build('framework.gyp', 'copy_target', chdir='framework')
+
+
+ # Check that actions ran.
+ test.built_file_must_exist('action_file', chdir='framework')
+
+ # Test that a copy with the "Code Sign on Copy" flag on succeeds.
+ test.build('framework.gyp', 'copy_target_code_sign', chdir='framework')
+
+ test.pass_test()
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 00000000000000..253623d1c69dfa
--- /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
new file mode 100755
index 00000000000000..a0e9438e2a6bfd
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-debuginfo.py
@@ -0,0 +1,36 @@
+#!/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.
+
+"""
+Tests things related to debug information generation.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='debuginfo')
+
+ test.build('test.gyp', test.ALL, chdir='debuginfo')
+
+ test.built_file_must_exist('libnonbundle_shared_library.dylib.dSYM',
+ chdir='debuginfo')
+ test.built_file_must_exist('nonbundle_loadable_module.so.dSYM',
+ chdir='debuginfo')
+ test.built_file_must_exist('nonbundle_executable.dSYM',
+ chdir='debuginfo')
+
+ test.built_file_must_exist('bundle_shared_library.framework.dSYM',
+ chdir='debuginfo')
+ test.built_file_must_exist('bundle_loadable_module.bundle.dSYM',
+ chdir='debuginfo')
+ test.built_file_must_exist('My App.app.dSYM',
+ chdir='debuginfo')
+
+ test.pass_test()
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 00000000000000..5cccb03227cbeb
--- /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-deployment-target.py b/tools/gyp/test/mac/gyptest-deployment-target.py
new file mode 100644
index 00000000000000..f5812e62db6050
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-deployment-target.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 MACOSX_DEPLOYMENT_TARGET works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['make', 'ninja', 'xcode'])
+
+ test.run_gyp('deployment-target.gyp', chdir='deployment-target')
+
+ test.build('deployment-target.gyp', test.ALL, chdir='deployment-target')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-framework-dirs.py b/tools/gyp/test/mac/gyptest-framework-dirs.py
new file mode 100644
index 00000000000000..a1ae54c57fbeb0
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-framework-dirs.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.
+
+"""
+Verify that it is possible to build an object that depends on a
+PrivateFramework.
+"""
+
+import os
+import sys
+import TestGyp
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'framework-dirs'
+ test.run_gyp('framework-dirs.gyp', chdir=CHDIR)
+ test.build('framework-dirs.gyp', 'calculate', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-framework-headers.py b/tools/gyp/test/mac/gyptest-framework-headers.py
new file mode 100644
index 00000000000000..aa13a742cdd3a6
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-framework-headers.py
@@ -0,0 +1,38 @@
+#!/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 mac_framework_headers works properly.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ # TODO(thakis): Make this work with ninja, make. http://crbug.com/129013
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+ CHDIR = 'framework-headers'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ # Test that headers are installed for frameworks
+ test.build('test.gyp', 'test_framework_headers_framework', chdir=CHDIR)
+
+ test.built_file_must_exist(
+ 'TestFramework.framework/Versions/A/TestFramework', chdir=CHDIR)
+
+ test.built_file_must_exist(
+ 'TestFramework.framework/Versions/A/Headers/myframework.h', chdir=CHDIR)
+
+ # Test that headers are installed for static libraries.
+ test.build('test.gyp', 'test_framework_headers_static', chdir=CHDIR)
+
+ test.built_file_must_exist('libTestLibrary.a', chdir=CHDIR)
+
+ test.built_file_must_exist('include/myframework.h', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-framework.py b/tools/gyp/test/mac/gyptest-framework.py
new file mode 100755
index 00000000000000..401bd98d2fba45
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-framework.py
@@ -0,0 +1,74 @@
+#!/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 app bundles are built correctly.
+"""
+
+import TestGyp
+
+import os
+import sys
+
+
+def ls(path):
+ '''Returns a list of all files in a directory, relative to the directory.'''
+ result = []
+ for dirpath, _, files in os.walk(path):
+ for f in files:
+ result.append(os.path.join(dirpath, f)[len(path) + 1:])
+ return result
+
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('framework.gyp', chdir='framework')
+
+ test.build('framework.gyp', 'test_framework', chdir='framework')
+
+ # Binary
+ test.built_file_must_exist(
+ 'Test Framework.framework/Versions/A/Test Framework',
+ chdir='framework')
+
+ # Info.plist
+ info_plist = test.built_file_path(
+ 'Test Framework.framework/Versions/A/Resources/Info.plist',
+ chdir='framework')
+ test.must_exist(info_plist)
+ test.must_contain(info_plist, 'com.yourcompany.Test_Framework')
+
+ # Resources
+ test.built_file_must_exist(
+ 'Test Framework.framework/Versions/A/Resources/English.lproj/'
+ 'InfoPlist.strings',
+ chdir='framework')
+
+ # Symlinks created by packaging process
+ test.built_file_must_exist('Test Framework.framework/Versions/Current',
+ chdir='framework')
+ test.built_file_must_exist('Test Framework.framework/Resources',
+ chdir='framework')
+ test.built_file_must_exist('Test Framework.framework/Test Framework',
+ chdir='framework')
+ # PkgInfo.
+ test.built_file_must_not_exist(
+ 'Test Framework.framework/Versions/A/Resources/PkgInfo',
+ chdir='framework')
+
+ # Check that no other files get added to the bundle.
+ if set(ls(test.built_file_path('Test Framework.framework',
+ chdir='framework'))) != \
+ set(['Versions/A/Test Framework',
+ 'Versions/A/Resources/Info.plist',
+ 'Versions/A/Resources/English.lproj/InfoPlist.strings',
+ 'Test Framework',
+ 'Versions/A/Libraries/empty.c', # Written by a gyp action.
+ ]):
+ test.fail_test()
+
+ test.pass_test()
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 00000000000000..f4ed16630e2f73
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-global-settings.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 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'])
+
+ # The xcode-ninja generator handles gypfiles which are not at the
+ # project root incorrectly.
+ # cf. https://code.google.com/p/gyp/issues/detail?id=460
+ if test.format == 'xcode-ninja':
+ test.skip_test()
+
+ 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-identical-name.py b/tools/gyp/test/mac/gyptest-identical-name.py
new file mode 100644
index 00000000000000..62048256bcdb20
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-identical-name.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies libraries (in identical-names) are properly handeled by xcode.
+
+The names for all libraries participating in this build are:
+libtestlib.a - identical-name/testlib
+libtestlib.a - identical-name/proxy/testlib
+libproxy.a - identical-name/proxy
+The first two libs produce a hash collision in Xcode when Gyp is executed,
+because they have the same name and would be copied to the same directory with
+Xcode default settings.
+For this scenario to work one needs to change the Xcode variables SYMROOT and
+CONFIGURATION_BUILD_DIR. Setting these to per-lib-unique directories, avoids
+copying the libs into the same directory.
+
+The test consists of two steps. The first one verifies that by setting both
+vars, there is no hash collision anymore during Gyp execution and that the libs
+can actually be be built. The second one verifies that there is still a hash
+collision if the vars are not set and thus the current behavior is preserved.
+"""
+
+import TestGyp
+
+import sys
+
+def IgnoreOutput(string, expected_string):
+ return True
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+
+ test.run_gyp('test.gyp', chdir='identical-name')
+ test.build('test.gyp', test.ALL, chdir='identical-name')
+
+ test.run_gyp('test-should-fail.gyp', chdir='identical-name')
+ test.built_file_must_not_exist('test-should-fail.xcodeproj')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-infoplist-process.py b/tools/gyp/test/mac/gyptest-infoplist-process.py
new file mode 100755
index 00000000000000..20874a398af0b8
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-infoplist-process.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 the Info.plist preprocessor functionality.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'infoplist-process'
+ INFO_PLIST_PATH = 'Test.app/Contents/Info.plist'
+
+ # First process both keys.
+ test.set_configuration('One')
+ test.run_gyp('test1.gyp', chdir=CHDIR)
+ test.build('test1.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, 'Foo')
+ test.must_contain(info_plist, 'Bar')
+
+ # Then process a single key.
+ test.set_configuration('Two')
+ test.run_gyp('test2.gyp', chdir=CHDIR)
+ test.build('test2.gyp', chdir=CHDIR)
+ info_plist = test.built_file_path(INFO_PLIST_PATH, chdir=CHDIR)
+ test.must_exist(info_plist)
+ test.must_contain(info_plist, 'com.google.Test') # Normal expansion works.
+ test.must_contain(info_plist, 'Foo (Bar)')
+ test.must_contain(info_plist, 'PROCESSED_KEY2')
+
+ # Then turn off the processor.
+ test.set_configuration('Three')
+ test.run_gyp('test3.gyp', chdir=CHDIR)
+ test.build('test3.gyp', chdir=CHDIR)
+ info_plist = test.built_file_path('Test App.app/Contents/Info.plist',
+ chdir=CHDIR)
+ test.must_exist(info_plist)
+ test.must_contain(info_plist, 'com.google.Test') # Normal expansion works.
+ test.must_contain(info_plist, 'PROCESSED_KEY1')
+ test.must_contain(info_plist, 'PROCESSED_KEY2')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-installname.py b/tools/gyp/test/mac/gyptest-installname.py
new file mode 100644
index 00000000000000..8f1f0246a9c714
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-installname.py
@@ -0,0 +1,80 @@
+#!/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 LD_DYLIB_INSTALL_NAME and DYLIB_INSTALL_NAME_BASE are handled
+correctly.
+"""
+
+import TestGyp
+
+import re
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'installname'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ def GetInstallname(p):
+ p = test.built_file_path(p, chdir=CHDIR)
+ r = re.compile(r'cmd LC_ID_DYLIB.*?name (.*?) \(offset \d+\)', re.DOTALL)
+ proc = subprocess.Popen(['otool', '-l', p], stdout=subprocess.PIPE)
+ o = proc.communicate()[0]
+ assert not proc.returncode
+ m = r.search(o)
+ assert m
+ return m.group(1)
+
+ if (GetInstallname('libdefault_installname.dylib') !=
+ '/usr/local/lib/libdefault_installname.dylib'):
+ test.fail_test()
+
+ if (GetInstallname('My Framework.framework/My Framework') !=
+ '/Library/Frameworks/My Framework.framework/'
+ 'Versions/A/My Framework'):
+ test.fail_test()
+
+ if (GetInstallname('libexplicit_installname.dylib') !=
+ 'Trapped in a dynamiclib factory'):
+ test.fail_test()
+
+ if (GetInstallname('libexplicit_installname_base.dylib') !=
+ '@executable_path/../../../libexplicit_installname_base.dylib'):
+ test.fail_test()
+
+ if (GetInstallname('My Other Framework.framework/My Other Framework') !=
+ '@executable_path/../../../My Other Framework.framework/'
+ 'Versions/A/My Other Framework'):
+ test.fail_test()
+
+ if (GetInstallname('libexplicit_installname_with_base.dylib') !=
+ '/usr/local/lib/libexplicit_installname_with_base.dylib'):
+ test.fail_test()
+
+ if (GetInstallname('libexplicit_installname_with_explicit_base.dylib') !=
+ '@executable_path/../libexplicit_installname_with_explicit_base.dylib'):
+ test.fail_test()
+
+ if (GetInstallname('libboth_base_and_installname.dylib') !=
+ 'Still trapped in a dynamiclib factory'):
+ test.fail_test()
+
+ if (GetInstallname('install_name_with_info_plist.framework/'
+ 'install_name_with_info_plist') !=
+ '/Library/Frameworks/install_name_with_info_plist.framework/'
+ 'Versions/A/install_name_with_info_plist'):
+ test.fail_test()
+
+ if ('DYLIB_INSTALL_NAME_BASE:standardizepath: command not found' in
+ test.stdout()):
+ test.fail_test()
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-ldflags-passed-to-libtool.py b/tools/gyp/test/mac/gyptest-ldflags-passed-to-libtool.py
new file mode 100644
index 00000000000000..addd331a73cd57
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-ldflags-passed-to-libtool.py
@@ -0,0 +1,32 @@
+#!/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 OTHER_LDFLAGS is passed to libtool.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'],
+ match = lambda a, b: True)
+
+ build_error_code = {
+ 'xcode': [1, 65], # 1 for xcode 3, 65 for xcode 4 (see `man sysexits`)
+ 'make': 2,
+ 'ninja': 1,
+ 'xcode-ninja': [1, 65],
+ }[test.format]
+
+ CHDIR = 'ldflags-libtool'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', 'ldflags_passed_to_libtool', chdir=CHDIR,
+ status=build_error_code)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-ldflags.py b/tools/gyp/test/mac/gyptest-ldflags.py
new file mode 100644
index 00000000000000..af44b8c5a056a2
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-ldflags.py
@@ -0,0 +1,74 @@
+#!/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 filenames passed to various linker flags are converted into
+build-directory relative paths correctly.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ # The xcode-ninja generator handles gypfiles which are not at the
+ # project root incorrectly.
+ # cf. https://code.google.com/p/gyp/issues/detail?id=460
+ if test.format == 'xcode-ninja':
+ test.skip_test()
+
+ CHDIR = 'ldflags'
+ test.run_gyp('subdirectory/test.gyp', chdir=CHDIR)
+
+ test.build('subdirectory/test.gyp', test.ALL, chdir=CHDIR)
+
+ test.pass_test()
+
+
+# These flags from `man ld` couldl show up in OTHER_LDFLAGS and need path
+# translation.
+#
+# Done:
+# -exported_symbols_list filename
+# -unexported_symbols_list file
+# -reexported_symbols_list file
+# -sectcreate segname sectname file
+#
+# Will be done on demand:
+# -weak_library path_to_library
+# -reexport_library path_to_library
+# -lazy_library path_to_library
+# -upward_library path_to_library
+# -syslibroot rootdir
+# -framework name[,suffix]
+# -weak_framework name[,suffix]
+# -reexport_framework name[,suffix]
+# -lazy_framework name[,suffix]
+# -upward_framework name[,suffix]
+# -force_load path_to_archive
+# -filelist file[,dirname]
+# -dtrace file
+# -order_file file # should use ORDER_FILE
+# -exported_symbols_order file
+# -bundle_loader executable # should use BUNDLE_LOADER
+# -alias_list filename
+# -seg_addr_table filename
+# -dylib_file install_name:file_name
+# -interposable_list filename
+# -object_path_lto filename
+#
+#
+# obsolete:
+# -sectorder segname sectname orderfile
+# -seg_addr_table_filename path
+#
+#
+# ??:
+# -map map_file_path
+# -sub_library library_name
+# -sub_umbrella framework_name
diff --git a/tools/gyp/test/mac/gyptest-libraries.py b/tools/gyp/test/mac/gyptest-libraries.py
new file mode 100755
index 00000000000000..d5b64bda7f4b5c
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-libraries.py
@@ -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 libraries (in link_settings) are properly found.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ # The xcode-ninja generator handles gypfiles which are not at the
+ # project root incorrectly.
+ # cf. https://code.google.com/p/gyp/issues/detail?id=460
+ if test.format == 'xcode-ninja':
+ test.skip_test()
+
+ 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-libtool-zero.py b/tools/gyp/test/mac/gyptest-libtool-zero.py
new file mode 100644
index 00000000000000..ae5b7e635b5c1d
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-libtool-zero.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies libraries have proper mtime.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'libtool-zero'
+
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', 'mylib', chdir=CHDIR)
+
+ test.up_to_date('test.gyp', 'mylib', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-loadable-module-bundle-product-extension.py b/tools/gyp/test/mac/gyptest-loadable-module-bundle-product-extension.py
new file mode 100644
index 00000000000000..7a60ca2d177a01
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-loadable-module-bundle-product-extension.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Tests that loadable_modules don't collide when using the same name with
+different file extensions.
+"""
+
+import TestGyp
+
+import os
+import struct
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'loadable-module-bundle-product-extension'
+ test.run_gyp('test.gyp',
+ '-G', 'xcode_ninja_target_pattern=^.*$',
+ chdir=CHDIR)
+
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ test.must_exist(test.built_file_path('Collide.foo', chdir=CHDIR))
+ test.must_exist(test.built_file_path('Collide.bar', chdir=CHDIR))
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-loadable-module.py b/tools/gyp/test/mac/gyptest-loadable-module.py
new file mode 100755
index 00000000000000..7ba3262d71084c
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-loadable-module.py
@@ -0,0 +1,49 @@
+#!/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 that a loadable_module target is built correctly.
+"""
+
+import TestGyp
+
+import os
+import struct
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'loadable-module'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ # Binary.
+ binary = test.built_file_path(
+ 'test_loadable_module.plugin/Contents/MacOS/test_loadable_module',
+ chdir=CHDIR)
+ test.must_exist(binary)
+ MH_BUNDLE = 8
+ if struct.unpack('4I', open(binary, 'rb').read(16))[3] != MH_BUNDLE:
+ test.fail_test()
+
+ # Info.plist.
+ info_plist = test.built_file_path(
+ 'test_loadable_module.plugin/Contents/Info.plist', chdir=CHDIR)
+ test.must_exist(info_plist)
+ test.must_contain(info_plist, """
+ CFBundleExecutable
+ test_loadable_module
+""")
+
+ # PkgInfo.
+ test.built_file_must_not_exist(
+ 'test_loadable_module.plugin/Contents/PkgInfo', chdir=CHDIR)
+ test.built_file_must_not_exist(
+ 'test_loadable_module.plugin/Contents/Resources', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-missing-cfbundlesignature.py b/tools/gyp/test/mac/gyptest-missing-cfbundlesignature.py
new file mode 100644
index 00000000000000..ef7a8d1bdcb96e
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-missing-cfbundlesignature.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 an Info.plist with CFBundleSignature works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='missing-cfbundlesignature')
+ test.build('test.gyp', test.ALL, chdir='missing-cfbundlesignature')
+
+ test.built_file_must_match('mytarget.app/Contents/PkgInfo', 'APPL????',
+ chdir='missing-cfbundlesignature')
+
+ test.built_file_must_match('myothertarget.app/Contents/PkgInfo', 'APPL????',
+ chdir='missing-cfbundlesignature')
+
+ test.built_file_must_match('thirdtarget.app/Contents/PkgInfo', 'APPL????',
+ chdir='missing-cfbundlesignature')
+ test.pass_test()
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 00000000000000..504dcd589b3f82
--- /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, '''\
+\tMy Variable
+\tsome expansion''')
+ test.must_contain(info_plist, '''\
+\tCFlags
+\t-fstack-protector-all -fno-strict-aliasing -DS="A Space"''')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-objc-arc.py b/tools/gyp/test/mac/gyptest-objc-arc.py
new file mode 100755
index 00000000000000..b3192a19dd37e2
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-objc-arc.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 ARC objc settings are handled correctly.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ # set |match| to ignore build stderr output.
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'],
+ match = lambda a, b: True)
+
+ CHDIR = 'objc-arc'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', 'arc_enabled', chdir=CHDIR)
+ test.build('test.gyp', 'arc_disabled', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-objc-gc.py b/tools/gyp/test/mac/gyptest-objc-gc.py
new file mode 100644
index 00000000000000..0cec458983908d
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-objc-gc.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 GC objc settings are handled correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import sys
+
+if sys.platform == 'darwin':
+ # set |match| to ignore build stderr output.
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'],
+ match = lambda a, b: True)
+
+ # Xcode 5.1 removed support for garbage-collection:
+ # error: garbage collection is no longer supported
+ if TestMac.Xcode.Version() < '0510':
+
+ CHDIR = 'objc-gc'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ build_error_code = {
+ 'xcode': [1, 65], # 1 for xcode 3, 65 for xcode 4 (see `man sysexits`)
+ 'make': 2,
+ 'ninja': 1,
+ }[test.format]
+
+ test.build('test.gyp', 'gc_exe_fails', chdir=CHDIR, status=build_error_code)
+ test.build(
+ 'test.gyp', 'gc_off_exe_req_lib', chdir=CHDIR, status=build_error_code)
+
+ test.build('test.gyp', 'gc_req_exe', chdir=CHDIR)
+ test.run_built_executable('gc_req_exe', chdir=CHDIR, stdout="gc on: 1\n")
+
+ test.build('test.gyp', 'gc_exe_req_lib', chdir=CHDIR)
+ test.run_built_executable(
+ 'gc_exe_req_lib', chdir=CHDIR, stdout="gc on: 1\n")
+
+ test.build('test.gyp', 'gc_exe', chdir=CHDIR)
+ test.run_built_executable('gc_exe', chdir=CHDIR, stdout="gc on: 1\n")
+
+ test.build('test.gyp', 'gc_off_exe', chdir=CHDIR)
+ test.run_built_executable('gc_off_exe', chdir=CHDIR, stdout="gc on: 0\n")
+
+ 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 00000000000000..1f04d1cb36a073
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-copy-bundle.py
@@ -0,0 +1,75 @@
+#!/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')
+ final_copies_path = os.path.join(
+ app_bundle_dir, 'Contents', 'My Framework.framework', 'Versions', 'A',
+ 'Libraries', 'copied.txt')
+
+ # 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, '''\
+\tRandomKey
+\tRandomValue''')
+
+ # 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, '''\
+\tRandomKey
+\tNewRandomValue''')
+
+ # Check the same for the copies section, test for http://crbug.com/157077
+ test.sleep()
+ contents = test.read('postbuild-copy-bundle/copied.txt')
+ contents = contents.replace('old', 'new')
+ test.write('postbuild-copy-bundle/copied.txt', contents)
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
+
+ test.must_exist(final_copies_path)
+ test.must_contain(final_copies_path, 'new copied file')
+
+ 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 00000000000000..0560904c29414e
--- /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
new file mode 100755
index 00000000000000..9cd5d47d28bf3e
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-fail.py
@@ -0,0 +1,68 @@
+#!/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.
+
+"""
+Verifies that a failing postbuild step lets the build fail.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ # set |match| to ignore build stderr output.
+ 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, 65], # 1 for xcode 3, 65 for xcode 4 (see `man sysexits`)
+ 'make': 2,
+ 'ninja': 1,
+ 'xcode-ninja': [1, 65],
+ }[test.format]
+
+
+ # If a postbuild fails, all postbuilds should be re-run on the next build.
+ # In Xcode 3, even if the first postbuild fails the other postbuilds were
+ # still executed. In Xcode 4, postbuilds are stopped after the first
+ # failing postbuild. This test checks for the Xcode 4 behavior.
+
+ # Ignore this test on Xcode 3.
+ import subprocess
+ job = subprocess.Popen(['xcodebuild', '-version'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, err = job.communicate()
+ if job.returncode != 0:
+ print out
+ raise Exception('Error %d running xcodebuild' % job.returncode)
+ if out.startswith('Xcode 3.'):
+ test.pass_test()
+
+ # Non-bundles
+ test.build('test.gyp', 'nonbundle', chdir='postbuild-fail',
+ status=build_error_code)
+ test.built_file_must_not_exist('static_touch',
+ chdir='postbuild-fail')
+ # Check for non-up-to-date-ness by checking if building again produces an
+ # error.
+ test.build('test.gyp', 'nonbundle', chdir='postbuild-fail',
+ status=build_error_code)
+
+
+ # Bundles
+ test.build('test.gyp', 'bundle', chdir='postbuild-fail',
+ status=build_error_code)
+ test.built_file_must_not_exist('dynamic_touch',
+ chdir='postbuild-fail')
+ # Check for non-up-to-date-ness by checking if building again produces an
+ # error.
+ test.build('test.gyp', 'bundle', chdir='postbuild-fail',
+ status=build_error_code)
+
+ test.pass_test()
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 00000000000000..84694f36ccadd3
--- /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.py b/tools/gyp/test/mac/gyptest-postbuild-static-library.py
new file mode 100644
index 00000000000000..8f9a6ebcb09de6
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild-static-library.py
@@ -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
new file mode 100755
index 00000000000000..684e7b8426d1de
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-postbuild.py
@@ -0,0 +1,53 @@
+#!/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.
+
+"""
+Verifies that postbuild steps work.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='postbuilds')
+
+ test.build('test.gyp', test.ALL, chdir='postbuilds')
+
+ # See comment in test/subdirectory/gyptest-subdir-default.py
+ if test.format == 'xcode':
+ chdir = 'postbuilds/subdirectory'
+ else:
+ chdir = 'postbuilds'
+
+ # Created by the postbuild scripts
+ test.built_file_must_exist('el.a_touch',
+ type=test.STATIC_LIB,
+ chdir='postbuilds')
+ test.built_file_must_exist('el.a_gyp_touch',
+ type=test.STATIC_LIB,
+ chdir='postbuilds')
+ test.built_file_must_exist('nest_el.a_touch',
+ type=test.STATIC_LIB,
+ chdir=chdir)
+ test.built_file_must_exist(
+ 'dyna.framework/Versions/A/dyna_touch',
+ chdir='postbuilds')
+ test.built_file_must_exist(
+ 'dyna.framework/Versions/A/dyna_gyp_touch',
+ chdir='postbuilds')
+ test.built_file_must_exist(
+ 'nest_dyna.framework/Versions/A/nest_dyna_touch',
+ chdir=chdir)
+ test.built_file_must_exist('dyna_standalone.dylib_gyp_touch',
+ type=test.SHARED_LIB,
+ chdir='postbuilds')
+ test.built_file_must_exist('copied_file.txt', chdir='postbuilds')
+ test.built_file_must_exist('copied_file_2.txt', chdir=chdir)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-prefixheader.py b/tools/gyp/test/mac/gyptest-prefixheader.py
new file mode 100755
index 00000000000000..0cf85f94225bf8
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-prefixheader.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 GCC_PREFIX_HEADER works.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ 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
new file mode 100755
index 00000000000000..0f26e961248302
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-rebuild.py
@@ -0,0 +1,41 @@
+#!/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 app bundles are rebuilt correctly.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'rebuild'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', 'test_app', chdir=CHDIR)
+
+ # Touch a source file, rebuild, and check that the app target is up-to-date.
+ 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-rpath.py b/tools/gyp/test/mac/gyptest-rpath.py
new file mode 100644
index 00000000000000..ef415cd7d3122a
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-rpath.py
@@ -0,0 +1,50 @@
+#!/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 LD_DYLIB_INSTALL_NAME and DYLIB_INSTALL_NAME_BASE are handled
+correctly.
+"""
+
+import TestGyp
+
+import re
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'rpath'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ test.build('test.gyp', test.ALL, chdir=CHDIR)
+
+ def GetRpaths(p):
+ p = test.built_file_path(p, chdir=CHDIR)
+ r = re.compile(r'cmd LC_RPATH.*?path (.*?) \(offset \d+\)', re.DOTALL)
+ proc = subprocess.Popen(['otool', '-l', p], stdout=subprocess.PIPE)
+ o = proc.communicate()[0]
+ assert not proc.returncode
+ return r.findall(o)
+
+ if GetRpaths('libdefault_rpath.dylib') != []:
+ test.fail_test()
+
+ if GetRpaths('libexplicit_rpath.dylib') != ['@executable_path/.']:
+ test.fail_test()
+
+ if (GetRpaths('libexplicit_rpaths_escaped.dylib') !=
+ ['First rpath', 'Second rpath']):
+ test.fail_test()
+
+ if GetRpaths('My Framework.framework/My Framework') != ['@loader_path/.']:
+ test.fail_test()
+
+ if GetRpaths('executable') != ['@executable_path/.']:
+ test.fail_test()
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-sdkroot.py b/tools/gyp/test/mac/gyptest-sdkroot.py
new file mode 100644
index 00000000000000..711726ed87c5b2
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-sdkroot.py
@@ -0,0 +1,50 @@
+#!/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 setting SDKROOT works.
+"""
+
+import TestGyp
+
+import os
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ def GetSDKPath(sdk):
+ """Return SDKROOT if the SDK version |sdk| is installed or empty string."""
+ DEVNULL = open(os.devnull, 'wb')
+ try:
+ proc = subprocess.Popen(
+ ['xcodebuild', '-version', '-sdk', 'macosx' + sdk, 'Path'],
+ stdout=subprocess.PIPE, stderr=DEVNULL)
+ return proc.communicate()[0].rstrip('\n')
+ finally:
+ DEVNULL.close()
+
+ def SelectSDK():
+ """Select the oldest SDK installed (greater than 10.6)."""
+ for sdk in ['10.6', '10.7', '10.8', '10.9']:
+ path = GetSDKPath(sdk)
+ if path:
+ return True, sdk, path
+ return False, '', ''
+
+ # Make sure this works on the bots, which only have the 10.6 sdk, and on
+ # dev machines which usually don't have the 10.6 sdk.
+ sdk_found, sdk, sdk_path = SelectSDK()
+ if not sdk_found:
+ test.fail_test()
+
+ test.write('sdkroot/test.gyp', test.read('sdkroot/test.gyp') % sdk)
+
+ test.run_gyp('test.gyp', '-D', 'sdk_path=%s' % sdk_path,
+ chdir='sdkroot')
+ test.build('test.gyp', test.ALL, chdir='sdkroot')
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-sourceless-module.py b/tools/gyp/test/mac/gyptest-sourceless-module.py
new file mode 100644
index 00000000000000..b56b75ed4f2831
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-sourceless-module.py
@@ -0,0 +1,72 @@
+#!/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')
+
+ # Build an app containing an actionless bundle.
+ test.build(
+ 'test.gyp',
+ 'bundle_dependent_on_resource_bundle_no_actions',
+ chdir='sourceless-module')
+
+ test.built_file_must_exist(
+ 'bundle_dependent_on_resource_bundle_no_actions.app/Contents/Resources/'
+ 'mac_resource_bundle_no_actions.bundle/Contents/Resources/empty.txt',
+ 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')
+
+ # TODO(thakis): shared_libraries that have no sources but depend on static
+ # libraries currently only work with the ninja generator. This is used by
+ # chrome/mac's components build.
+ if test.format == 'ninja':
+ # Check that an executable depending on a resource framework links fine too.
+ test.build(
+ 'test.gyp', 'dependent_on_resource_framework', chdir='sourceless-module')
+
+ test.built_file_must_exist(
+ 'resource_framework.framework/Resources/foo.manifest',
+ chdir='sourceless-module')
+ test.built_file_must_exist(
+ 'resource_framework.framework/resource_framework',
+ chdir='sourceless-module')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-strip-default.py b/tools/gyp/test/mac/gyptest-strip-default.py
new file mode 100644
index 00000000000000..f73fa1124cf1d4
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-strip-default.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 default STRIP_STYLEs match between different generators.
+"""
+
+import TestGyp
+
+import re
+import subprocess
+import sys
+import time
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR='strip'
+ test.run_gyp('test-defaults.gyp', chdir=CHDIR)
+
+ test.build('test-defaults.gyp', test.ALL, chdir=CHDIR)
+
+ # Lightweight check if stripping was done.
+ def OutPath(s):
+ return test.built_file_path(s, chdir=CHDIR)
+
+ def CheckNsyms(p, o_expected):
+ proc = subprocess.Popen(['nm', '-aU', p], stdout=subprocess.PIPE)
+ o = proc.communicate()[0]
+
+ # Filter out mysterious "00 0000 OPT radr://5614542" symbol which
+ # is apparently only printed on the bots (older toolchain?).
+ # Yes, "radr", not "rdar".
+ o = ''.join(filter(lambda s: 'radr://5614542' not in s, o.splitlines(True)))
+
+ o = o.replace('A', 'T')
+ o = re.sub(r'^[a-fA-F0-9]+', 'XXXXXXXX', o, flags=re.MULTILINE)
+ assert not proc.returncode
+ if o != o_expected:
+ print 'Stripping: Expected symbols """\n%s""", got """\n%s"""' % (
+ o_expected, o)
+ test.fail_test()
+
+ CheckNsyms(OutPath('libsingle_dylib.dylib'),
+"""\
+XXXXXXXX S _ci
+XXXXXXXX S _i
+XXXXXXXX T _the_function
+XXXXXXXX t _the_hidden_function
+XXXXXXXX T _the_used_function
+XXXXXXXX T _the_visible_function
+""")
+ CheckNsyms(OutPath('single_so.so'),
+"""\
+XXXXXXXX S _ci
+XXXXXXXX S _i
+XXXXXXXX T _the_function
+XXXXXXXX t _the_hidden_function
+XXXXXXXX T _the_used_function
+XXXXXXXX T _the_visible_function
+""")
+ CheckNsyms(OutPath('single_exe'),
+"""\
+XXXXXXXX T __mh_execute_header
+""")
+
+ CheckNsyms(test.built_file_path(
+ 'bundle_dylib.framework/Versions/A/bundle_dylib', chdir=CHDIR),
+"""\
+XXXXXXXX S _ci
+XXXXXXXX S _i
+XXXXXXXX T _the_function
+XXXXXXXX t _the_hidden_function
+XXXXXXXX T _the_used_function
+XXXXXXXX T _the_visible_function
+""")
+ CheckNsyms(test.built_file_path(
+ 'bundle_so.bundle/Contents/MacOS/bundle_so', chdir=CHDIR),
+"""\
+XXXXXXXX S _ci
+XXXXXXXX S _i
+XXXXXXXX T _the_function
+XXXXXXXX T _the_used_function
+XXXXXXXX T _the_visible_function
+""")
+ CheckNsyms(test.built_file_path(
+ 'bundle_exe.app/Contents/MacOS/bundle_exe', chdir=CHDIR),
+"""\
+XXXXXXXX T __mh_execute_header
+""")
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-strip.py b/tools/gyp/test/mac/gyptest-strip.py
new file mode 100755
index 00000000000000..a729521e18459b
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-strip.py
@@ -0,0 +1,61 @@
+#!/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 stripping works.
+"""
+
+import TestGyp
+import TestMac
+
+import re
+import subprocess
+import sys
+import time
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp', chdir='strip')
+
+ test.build('test.gyp', test.ALL, chdir='strip')
+
+ # Lightweight check if stripping was done.
+ def OutPath(s):
+ return test.built_file_path(s, type=test.SHARED_LIB, chdir='strip')
+
+ def CheckNsyms(p, n_expected):
+ r = re.compile(r'nsyms\s+(\d+)')
+ o = subprocess.check_output(['otool', '-l', p])
+ m = r.search(o)
+ n = int(m.group(1))
+ if n != n_expected:
+ print 'Stripping: Expected %d symbols, got %d' % (n_expected, n)
+ test.fail_test()
+
+ # Starting with Xcode 5.0, clang adds an additional symbols to the compiled
+ # file when using a relative path to the input file. So when using ninja
+ # with Xcode 5.0 or higher, take this additional symbol into consideration
+ # for unstripped builds (it is stripped by all strip commands).
+ expected_extra_symbol_count = 0
+ if test.format in ['ninja', 'xcode-ninja'] \
+ and TestMac.Xcode.Version() >= '0500':
+ expected_extra_symbol_count = 1
+
+ # The actual numbers here are not interesting, they just need to be the same
+ # in both the xcode and the make build.
+ CheckNsyms(OutPath('no_postprocess'), 29 + expected_extra_symbol_count)
+ CheckNsyms(OutPath('no_strip'), 29 + expected_extra_symbol_count)
+ CheckNsyms(OutPath('strip_all'), 0)
+ CheckNsyms(OutPath('strip_nonglobal'), 6)
+ CheckNsyms(OutPath('strip_debugging'), 7)
+ CheckNsyms(OutPath('strip_all_custom_flags'), 0)
+ CheckNsyms(test.built_file_path(
+ 'strip_all_bundle.framework/Versions/A/strip_all_bundle', chdir='strip'),
+ 0)
+ CheckNsyms(OutPath('strip_save'), 7)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-swift-library.py b/tools/gyp/test/mac/gyptest-swift-library.py
new file mode 100644
index 00000000000000..7db24c343f6112
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-swift-library.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that a swift framework builds correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import collections
+import sys
+import subprocess
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+ # Ensures that the given symbol is present in the given file, by running nm.
+ def CheckHasSymbolName(path, symbol):
+ output = subprocess.check_output(['nm', '-j', path])
+ idx = output.find(symbol)
+ if idx == -1:
+ print 'Swift: Could not find symobl: %s' % symbol
+ test.fail_test()
+
+ test_cases = []
+
+ # Run this for iOS on XCode 6.0 or greater
+ if TestMac.Xcode.Version() >= '0600':
+ test_cases.append(('Default', 'iphoneos'))
+ test_cases.append(('Default', 'iphonesimulator'))
+
+ # Run it for Mac on XCode 6.1 or greater
+ if TestMac.Xcode.Version() >= '0610':
+ test_cases.append(('Default', None))
+
+ # Generate the project.
+ test.run_gyp('test.gyp', chdir='swift-library')
+
+ # Build and verify for each configuration.
+ for configuration, sdk in test_cases:
+ kwds = collections.defaultdict(list)
+ if test.format == 'xcode':
+ if sdk is not None:
+ kwds['arguments'].extend(['-sdk', sdk])
+
+ test.set_configuration(configuration)
+ test.build('test.gyp', 'SwiftFramework', chdir='swift-library', **kwds)
+
+ filename = 'SwiftFramework.framework/SwiftFramework'
+ result_file = test.built_file_path(filename, chdir='swift-library')
+
+ test.must_exist(result_file)
+
+ # Check to make sure that our swift class (GypSwiftTest) is present in the
+ # built binary
+ CheckHasSymbolName(result_file, "C14SwiftFramework12GypSwiftTest")
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-type-envvars.py b/tools/gyp/test/mac/gyptest-type-envvars.py
new file mode 100755
index 00000000000000..a5203c5f1e2050
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-type-envvars.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.
+
+"""
+Test that MACH_O_TYPE etc are set correctly.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ test.run_gyp('test.gyp',
+ '-G', 'xcode_ninja_target_pattern=^(?!nonbundle_none).*$',
+ chdir='type_envvars')
+
+ test.build('test.gyp', test.ALL, chdir='type_envvars')
+
+ # The actual test is done by postbuild scripts during |test.build()|.
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-unicode-settings.py b/tools/gyp/test/mac/gyptest-unicode-settings.py
new file mode 100644
index 00000000000000..a71b3bd9a32eea
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-unicode-settings.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright 2013 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 unicode strings in 'xcode_settings' work.
+Also checks that ASCII control characters are escaped properly.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+ test.run_gyp('test.gyp', chdir='unicode-settings')
+ test.build('test.gyp', test.ALL, chdir='unicode-settings')
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-xcode-env-order.py b/tools/gyp/test/mac/gyptest-xcode-env-order.py
new file mode 100755
index 00000000000000..e70cf1367c6532
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-xcode-env-order.py
@@ -0,0 +1,90 @@
+#!/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 dependent Xcode settings are processed correctly.
+"""
+
+import TestGyp
+import TestMac
+
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ 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 'rules' filenames and inline actions
+ test.built_file_must_exist('rule-copy-brace.txt', chdir=CHDIR)
+ test.built_file_must_exist('rule-copy-paren.txt', chdir=CHDIR)
+ # TODO: see comment in test.gyp for this file.
+ #test.built_file_must_exist('rule-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, '''\
+\tBraceProcessedKey1
+\tD:/Source/Project/Test''')
+ test.must_contain(info_plist, '''\
+\tBraceProcessedKey2
+\t/Source/Project/Test''')
+ test.must_contain(info_plist, '''\
+\tBraceProcessedKey3
+\tcom.apple.product-type.application:D:/Source/Project/Test''')
+
+ test.must_contain(info_plist, '''\
+\tParenProcessedKey1
+\tD:/Source/Project/Test''')
+ test.must_contain(info_plist, '''\
+\tParenProcessedKey2
+\t/Source/Project/Test''')
+ test.must_contain(info_plist, '''\
+\tParenProcessedKey3
+\tcom.apple.product-type.application:D:/Source/Project/Test''')
+
+ test.must_contain(info_plist, '''\
+\tBareProcessedKey1
+\tD:/Source/Project/Test''')
+ test.must_contain(info_plist, '''\
+\tBareProcessedKey2
+\t/Source/Project/Test''')
+ # 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' and TestMac.Xcode.Version() < '0500':
+ test.must_contain(info_plist, '''\
+\tBareProcessedKey3
+\t$PRODUCT_TYPE:D:/Source/Project/Test''')
+ else:
+ # The bug has been fixed by Xcode version 5.0.0.
+ test.must_contain(info_plist, '''\
+\tBareProcessedKey3
+\tcom.apple.product-type.application:D:/Source/Project/Test''')
+
+ test.must_contain(info_plist, '''\
+\tMixedProcessedKey
+\t/Source/Project:Test:mh_execute''')
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-xcode-gcc-clang.py b/tools/gyp/test/mac/gyptest-xcode-gcc-clang.py
new file mode 100644
index 00000000000000..981c3fc564a9d6
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-xcode-gcc-clang.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 xcode-style GCC_... settings that require clang are handled
+properly.
+"""
+
+import TestGyp
+
+import os
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'xcode-gcc'
+ test.run_gyp('test-clang.gyp', chdir=CHDIR)
+
+ test.build('test-clang.gyp', 'aliasing_yes', chdir=CHDIR)
+ test.run_built_executable('aliasing_yes', chdir=CHDIR, stdout="1\n")
+ test.build('test-clang.gyp', 'aliasing_no', chdir=CHDIR)
+ test.run_built_executable('aliasing_no', chdir=CHDIR, stdout="0\n")
+
+ # The default behavior changed: strict aliasing used to be off, now it's on
+ # by default. The important part is that this is identical for all generators
+ # (which it is). TODO(thakis): Enable this once the bots have a newer Xcode.
+ #test.build('test-clang.gyp', 'aliasing_default', chdir=CHDIR)
+ #test.run_built_executable('aliasing_default', chdir=CHDIR, stdout="1\n")
+ # For now, just check the generated ninja file:
+ if test.format == 'ninja':
+ contents = open(test.built_file_path('obj/aliasing_default.ninja',
+ chdir=CHDIR)).read()
+ if 'strict-aliasing' in contents:
+ test.fail_test()
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-xcode-gcc.py b/tools/gyp/test/mac/gyptest-xcode-gcc.py
new file mode 100644
index 00000000000000..e45d0b5e8bc0c5
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-xcode-gcc.py
@@ -0,0 +1,56 @@
+#!/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 xcode-style GCC_... settings are handled properly.
+"""
+
+import TestGyp
+
+import os
+import subprocess
+import sys
+
+def IgnoreOutput(string, expected_string):
+ return True
+
+def CompilerVersion(compiler):
+ stdout = subprocess.check_output([compiler, '-v'], stderr=subprocess.STDOUT)
+ return stdout.rstrip('\n')
+
+def CompilerSupportsWarnAboutInvalidOffsetOfMacro(test):
+ # "clang" does not support the "-Winvalid-offsetof" flag, and silently
+ # ignore it. Starting with Xcode 5.0.0, "gcc" is just a "clang" binary with
+ # some hard-coded include path hack, so use the output of "-v" to detect if
+ # the compiler supports the flag or not.
+ return 'clang' not in CompilerVersion('/usr/bin/cc')
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['ninja', 'make', 'xcode'])
+
+ CHDIR = 'xcode-gcc'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+
+ # List of targets that'll pass. It expects targets of the same name with
+ # '-fail' appended that'll fail to build.
+ targets = [
+ 'warn_about_missing_newline',
+ ]
+
+ # clang doesn't warn on invalid offsetofs, it silently ignores
+ # -Wno-invalid-offsetof.
+ if CompilerSupportsWarnAboutInvalidOffsetOfMacro(test):
+ targets.append('warn_about_invalid_offsetof_macro')
+
+ for target in targets:
+ test.build('test.gyp', target, chdir=CHDIR)
+ test.built_file_must_exist(target, chdir=CHDIR)
+ fail_target = target + '-fail'
+ test.build('test.gyp', fail_target, chdir=CHDIR, status=None,
+ stderr=None, match=IgnoreOutput)
+ test.built_file_must_not_exist(fail_target, chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-xcode-support-actions.py b/tools/gyp/test/mac/gyptest-xcode-support-actions.py
new file mode 100755
index 00000000000000..ecc1402972cd80
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-xcode-support-actions.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Verifies that support actions are properly created.
+"""
+
+import TestGyp
+
+import os
+import subprocess
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+ CHDIR = 'xcode-support-actions'
+
+ test.run_gyp('test.gyp', '-Gsupport_target_suffix=_customsuffix', chdir=CHDIR)
+ test.build('test.gyp', target='target_customsuffix', chdir=CHDIR)
+
+ test.pass_test()
diff --git a/tools/gyp/test/mac/gyptest-xctest.py b/tools/gyp/test/mac/gyptest-xctest.py
new file mode 100644
index 00000000000000..a46a5fb3db6321
--- /dev/null
+++ b/tools/gyp/test/mac/gyptest-xctest.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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 xctest targets are correctly configured.
+"""
+
+import TestGyp
+
+import sys
+
+if sys.platform == 'darwin':
+ test = TestGyp.TestGyp(formats=['xcode'])
+
+ # Ignore this test if Xcode 5 is not installed
+ import subprocess
+ job = subprocess.Popen(['xcodebuild', '-version'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, err = job.communicate()
+ if job.returncode != 0:
+ raise Exception('Error %d running xcodebuild' % job.returncode)
+ xcode_version, build_number = out.splitlines()
+ # Convert the version string from 'Xcode 5.0' to ['5','0'].
+ xcode_version = xcode_version.split()[-1].split('.')
+ if xcode_version < ['5']:
+ test.pass_test()
+
+ CHDIR = 'xctest'
+ test.run_gyp('test.gyp', chdir=CHDIR)
+ test.build('test.gyp', chdir=CHDIR, arguments=['-scheme', 'classes', 'test'])
+
+ test.built_file_must_match('tests.xctest/Contents/Resources/resource.txt',
+ 'foo\n', chdir=CHDIR)
+ test.pass_test()
diff --git a/tools/gyp/test/mac/identical-name/proxy/proxy.cc b/tools/gyp/test/mac/identical-name/proxy/proxy.cc
new file mode 100644
index 00000000000000..fab17ac780e375
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/proxy/proxy.cc
@@ -0,0 +1 @@
+// Empty file
diff --git a/tools/gyp/test/mac/identical-name/proxy/proxy.gyp b/tools/gyp/test/mac/identical-name/proxy/proxy.gyp
new file mode 100644
index 00000000000000..38f44af1b57eec
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/proxy/proxy.gyp
@@ -0,0 +1,9 @@
+{
+ 'includes': ['../test.gypi'],
+ 'targets': [{
+ 'target_name': 'testlib',
+ 'type': 'none',
+ 'dependencies': ['testlib/testlib.gyp:testlib'],
+ 'sources': ['proxy.cc'],
+ }],
+}
diff --git a/tools/gyp/test/mac/identical-name/proxy/testlib/testlib.cc b/tools/gyp/test/mac/identical-name/proxy/testlib/testlib.cc
new file mode 100644
index 00000000000000..fab17ac780e375
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/proxy/testlib/testlib.cc
@@ -0,0 +1 @@
+// Empty file
diff --git a/tools/gyp/test/mac/identical-name/proxy/testlib/testlib.gyp b/tools/gyp/test/mac/identical-name/proxy/testlib/testlib.gyp
new file mode 100644
index 00000000000000..ed1c62e982eaf0
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/proxy/testlib/testlib.gyp
@@ -0,0 +1,8 @@
+{
+ 'includes': ['../../test.gypi'],
+ 'targets': [{
+ 'target_name': 'testlib',
+ 'type': 'static_library',
+ 'sources': ['testlib.cc'],
+ }],
+}
diff --git a/tools/gyp/test/mac/identical-name/test-should-fail.gyp b/tools/gyp/test/mac/identical-name/test-should-fail.gyp
new file mode 100644
index 00000000000000..72bfc7af0f8ef0
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/test-should-fail.gyp
@@ -0,0 +1,10 @@
+{
+ 'targets': [{
+ 'target_name': 'test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'testlib/testlib.gyp:proxy',
+ 'proxy/proxy.gyp:testlib',
+ ],
+ }],
+}
diff --git a/tools/gyp/test/mac/identical-name/test.gyp b/tools/gyp/test/mac/identical-name/test.gyp
new file mode 100644
index 00000000000000..717220e8666ad9
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/test.gyp
@@ -0,0 +1,11 @@
+{
+ 'includes': ['test.gypi'],
+ 'targets': [{
+ 'target_name': 'test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'testlib/testlib.gyp:proxy',
+ 'proxy/proxy.gyp:testlib',
+ ],
+ }],
+}
\ No newline at end of file
diff --git a/tools/gyp/test/mac/identical-name/test.gypi b/tools/gyp/test/mac/identical-name/test.gypi
new file mode 100644
index 00000000000000..61b7c2badf3d63
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/test.gypi
@@ -0,0 +1,7 @@
+{
+ 'target_defaults': {
+ 'xcode_settings': {
+ 'SYMROOT': '<(DEPTH)/$SRCROOT/',
+ },
+ },
+}
diff --git a/tools/gyp/test/mac/identical-name/testlib/main.cc b/tools/gyp/test/mac/identical-name/testlib/main.cc
new file mode 100644
index 00000000000000..5c2fa9bb6a78e2
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/testlib/main.cc
@@ -0,0 +1,3 @@
+int main(int argc, char **argv) {
+ return 0;
+}
diff --git a/tools/gyp/test/mac/identical-name/testlib/testlib.gyp b/tools/gyp/test/mac/identical-name/testlib/testlib.gyp
new file mode 100644
index 00000000000000..aa8b851004dc17
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/testlib/testlib.gyp
@@ -0,0 +1,14 @@
+{
+ 'includes': ['../test.gypi'],
+ 'targets': [{
+ 'target_name': 'proxy',
+ 'type': 'static_library',
+ 'sources': ['void.cc'],
+ 'dependencies': ['testlib'],
+ 'export_dependent_settings': ['testlib'],
+ }, {
+ 'target_name': 'testlib',
+ 'type': 'static_library',
+ 'sources': ['main.cc'],
+ }],
+}
diff --git a/tools/gyp/test/mac/identical-name/testlib/void.cc b/tools/gyp/test/mac/identical-name/testlib/void.cc
new file mode 100644
index 00000000000000..fab17ac780e375
--- /dev/null
+++ b/tools/gyp/test/mac/identical-name/testlib/void.cc
@@ -0,0 +1 @@
+// Empty file
diff --git a/tools/gyp/test/mac/infoplist-process/Info.plist b/tools/gyp/test/mac/infoplist-process/Info.plist
new file mode 100644
index 00000000000000..cb65721f43b59b
--- /dev/null
+++ b/tools/gyp/test/mac/infoplist-process/Info.plist
@@ -0,0 +1,36 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.google.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ ${MACOSX_DEPLOYMENT_TARGET}
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+ ProcessedKey1
+ PROCESSED_KEY1
+ ProcessedKey2
+ PROCESSED_KEY2
+
+
diff --git a/tools/gyp/test/mac/infoplist-process/main.c b/tools/gyp/test/mac/infoplist-process/main.c
new file mode 100644
index 00000000000000..1bf4b2a11abd99
--- /dev/null
+++ b/tools/gyp/test/mac/infoplist-process/main.c
@@ -0,0 +1,7 @@
+// 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.
+
+int main() {
+ return 0;
+}
diff --git a/tools/gyp/test/mac/infoplist-process/test1.gyp b/tools/gyp/test/mac/infoplist-process/test1.gyp
new file mode 100644
index 00000000000000..bc625a968be756
--- /dev/null
+++ b/tools/gyp/test/mac/infoplist-process/test1.gyp
@@ -0,0 +1,25 @@
+# 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',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'main.c',
+ ],
+ 'configurations': {
+ 'One': {
+ },
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ 'INFOPLIST_PREPROCESS': 'YES',
+ 'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'PROCESSED_KEY1=Foo PROCESSED_KEY2=Bar',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/infoplist-process/test2.gyp b/tools/gyp/test/mac/infoplist-process/test2.gyp
new file mode 100644
index 00000000000000..ecfbc9f64c9cff
--- /dev/null
+++ b/tools/gyp/test/mac/infoplist-process/test2.gyp
@@ -0,0 +1,25 @@
+# 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',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [
+ 'main.c',
+ ],
+ 'configurations': {
+ 'Two': {
+ },
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ 'INFOPLIST_PREPROCESS': 'YES',
+ 'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'PROCESSED_KEY1="Foo (Bar)"',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/infoplist-process/test3.gyp b/tools/gyp/test/mac/infoplist-process/test3.gyp
new file mode 100644
index 00000000000000..be8fe75a53ec7b
--- /dev/null
+++ b/tools/gyp/test/mac/infoplist-process/test3.gyp
@@ -0,0 +1,25 @@
+# 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',
+ ],
+ 'configurations': {
+ 'Three': {
+ },
+ },
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ 'INFOPLIST_PREPROCESS': 'NO',
+ 'INFOPLIST_PREPROCESSOR_DEFINITIONS': 'PROCESSED_KEY1=Foo',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/installname/Info.plist b/tools/gyp/test/mac/installname/Info.plist
new file mode 100644
index 00000000000000..5e05a5190c8931
--- /dev/null
+++ b/tools/gyp/test/mac/installname/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.yourcompany.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ NSPrincipalClass
+
+
+
diff --git a/tools/gyp/test/mac/installname/file.c b/tools/gyp/test/mac/installname/file.c
new file mode 100644
index 00000000000000..a39fce095f2f3a
--- /dev/null
+++ b/tools/gyp/test/mac/installname/file.c
@@ -0,0 +1 @@
+int f() { return 0; }
diff --git a/tools/gyp/test/mac/installname/main.c b/tools/gyp/test/mac/installname/main.c
new file mode 100644
index 00000000000000..237c8ce181774d
--- /dev/null
+++ b/tools/gyp/test/mac/installname/main.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/installname/test.gyp b/tools/gyp/test/mac/installname/test.gyp
new file mode 100644
index 00000000000000..60c867ff122a1b
--- /dev/null
+++ b/tools/gyp/test/mac/installname/test.gyp
@@ -0,0 +1,93 @@
+# 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': 'default_installname',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ },
+ {
+ 'target_name': 'default_bundle_installname',
+ 'product_name': 'My Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c' ],
+ },
+ {
+ 'target_name': 'explicit_installname',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'LD_DYLIB_INSTALL_NAME': 'Trapped in a dynamiclib factory',
+ },
+ },
+ {
+ 'target_name': 'explicit_installname_base',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'DYLIB_INSTALL_NAME_BASE': '@executable_path/../../..',
+
+ },
+ },
+ {
+ 'target_name': 'explicit_installname_base_bundle',
+ 'product_name': 'My Other Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'DYLIB_INSTALL_NAME_BASE': '@executable_path/../../..',
+
+ },
+ },
+ {
+ 'target_name': 'both_base_and_installname',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ # LD_DYLIB_INSTALL_NAME wins.
+ 'LD_DYLIB_INSTALL_NAME': 'Still trapped in a dynamiclib factory',
+ 'DYLIB_INSTALL_NAME_BASE': '@executable_path/../../..',
+ },
+ },
+ {
+ 'target_name': 'explicit_installname_with_base',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'LD_DYLIB_INSTALL_NAME': '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)',
+ },
+ },
+ {
+ 'target_name': 'explicit_installname_with_explicit_base',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'DYLIB_INSTALL_NAME_BASE': '@executable_path/..',
+ 'LD_DYLIB_INSTALL_NAME': '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)',
+ },
+ },
+ {
+ 'target_name': 'executable',
+ 'type': 'executable',
+ 'sources': [ 'main.c' ],
+ 'xcode_settings': {
+ 'LD_DYLIB_INSTALL_NAME': 'Should be ignored for not shared_lib',
+ },
+ },
+ # Regression test for http://crbug.com/113918
+ {
+ 'target_name': 'install_name_with_info_plist',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ 'LD_DYLIB_INSTALL_NAME': '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/ldflags-libtool/file.c b/tools/gyp/test/mac/ldflags-libtool/file.c
new file mode 100644
index 00000000000000..56757a701bf5cf
--- /dev/null
+++ b/tools/gyp/test/mac/ldflags-libtool/file.c
@@ -0,0 +1 @@
+void f() {}
diff --git a/tools/gyp/test/mac/ldflags-libtool/test.gyp b/tools/gyp/test/mac/ldflags-libtool/test.gyp
new file mode 100644
index 00000000000000..4e7aa07106bb53
--- /dev/null
+++ b/tools/gyp/test/mac/ldflags-libtool/test.gyp
@@ -0,0 +1,17 @@
+# 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': 'ldflags_passed_to_libtool',
+ 'type': 'static_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-fblorfen-horf-does-not-exist',
+ ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/ldflags/subdirectory/Info.plist b/tools/gyp/test/mac/ldflags/subdirectory/Info.plist
new file mode 100644
index 00000000000000..5f5e9abfbb5d52
--- /dev/null
+++ b/tools/gyp/test/mac/ldflags/subdirectory/Info.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ CFBundleSignature
+ ????
+
+
diff --git a/tools/gyp/test/mac/ldflags/subdirectory/file.c b/tools/gyp/test/mac/ldflags/subdirectory/file.c
new file mode 100644
index 00000000000000..90c45543bf62c1
--- /dev/null
+++ b/tools/gyp/test/mac/ldflags/subdirectory/file.c
@@ -0,0 +1,2 @@
+void f() {}
+void g() {}
diff --git a/tools/gyp/test/mac/ldflags/subdirectory/symbol_list.def b/tools/gyp/test/mac/ldflags/subdirectory/symbol_list.def
new file mode 100644
index 00000000000000..0ab7543b1fddc3
--- /dev/null
+++ b/tools/gyp/test/mac/ldflags/subdirectory/symbol_list.def
@@ -0,0 +1 @@
+_f
diff --git a/tools/gyp/test/mac/ldflags/subdirectory/test.gyp b/tools/gyp/test/mac/ldflags/subdirectory/test.gyp
new file mode 100644
index 00000000000000..db00c7465ca05d
--- /dev/null
+++ b/tools/gyp/test/mac/ldflags/subdirectory/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': 'raw',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-exported_symbols_list symbol_list.def',
+ '-sectcreate __TEXT __info_plist Info.plist',
+ ],
+ },
+ },
+ # TODO(thakis): This form should ideally be supported, too. (But
+ # -Wlfoo,bar,baz is cleaner so people should use that anyway.)
+ #{
+ # 'target_name': 'raw_sep',
+ # 'type': 'shared_library',
+ # 'sources': [ 'file.c', ],
+ # 'xcode_settings': {
+ # 'OTHER_LDFLAGS': [
+ # '-exported_symbols_list', 'symbol_list.def',
+ # '-sectcreate', '__TEXT', '__info_plist', 'Info.plist',
+ # ],
+ # },
+ #},
+ {
+ 'target_name': 'wl_space',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ # Works because clang passes unknown files on to the linker.
+ '-Wl,-exported_symbols_list symbol_list.def',
+ ],
+ },
+ },
+ # TODO(thakis): This form should ideally be supported, too. (But
+ # -Wlfoo,bar,baz is cleaner so people should use that anyway.)
+ #{
+ # 'target_name': 'wl_space_sep',
+ # 'type': 'shared_library',
+ # 'sources': [ 'file.c', ],
+ # 'xcode_settings': {
+ # 'OTHER_LDFLAGS': [
+ # # Works because clang passes unknown files on to the linker.
+ # '-Wl,-exported_symbols_list', 'symbol_list.def',
+ # ],
+ # },
+ #},
+ {
+ 'target_name': 'wl_comma',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-Wl,-exported_symbols_list,symbol_list.def',
+ '-Wl,-sectcreate,__TEXT,__info_plist,Info.plist',
+ ],
+ },
+ },
+ ],
+}
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 00000000000000..4031ded85f6301
--- /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 00000000000000..a43554c8caf8f0
--- /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
+
+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 00000000000000..e771991e836a67
--- /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 00000000000000..59fef51017b5bc
--- /dev/null
+++ b/tools/gyp/test/mac/libraries/subdir/test.gyp
@@ -0,0 +1,65 @@
+# 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',
+ ],
+ },
+ },
+ {
+ # 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/libtool-zero/mylib.c b/tools/gyp/test/mac/libtool-zero/mylib.c
new file mode 100644
index 00000000000000..b26d61bd6ba917
--- /dev/null
+++ b/tools/gyp/test/mac/libtool-zero/mylib.c
@@ -0,0 +1,7 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+int my_foo(int x) {
+ return x + 1;
+}
diff --git a/tools/gyp/test/mac/libtool-zero/test.gyp b/tools/gyp/test/mac/libtool-zero/test.gyp
new file mode 100644
index 00000000000000..0d6ee5535eede1
--- /dev/null
+++ b/tools/gyp/test/mac/libtool-zero/test.gyp
@@ -0,0 +1,18 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'mylib',
+ 'type': 'static_library',
+ 'sources': [
+ 'mylib.c',
+ ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386', 'x86_64' ],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/loadable-module-bundle-product-extension/src.cc b/tools/gyp/test/mac/loadable-module-bundle-product-extension/src.cc
new file mode 100644
index 00000000000000..3d878e9697d61b
--- /dev/null
+++ b/tools/gyp/test/mac/loadable-module-bundle-product-extension/src.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+int test() {
+ return 1337;
+}
diff --git a/tools/gyp/test/mac/loadable-module-bundle-product-extension/test.gyp b/tools/gyp/test/mac/loadable-module-bundle-product-extension/test.gyp
new file mode 100644
index 00000000000000..684a2c02aa33c4
--- /dev/null
+++ b/tools/gyp/test/mac/loadable-module-bundle-product-extension/test.gyp
@@ -0,0 +1,24 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'targets': [{
+ 'target_name': 'test',
+ 'type': 'none',
+ 'dependencies': ['child_one', 'child_two'],
+ }, {
+ 'target_name': 'child_one',
+ 'product_name': 'Collide',
+ 'product_extension': 'bar',
+ 'sources': ['src.cc'],
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ }, {
+ 'target_name': 'child_two',
+ 'product_name': 'Collide',
+ 'product_extension': 'foo',
+ 'sources': ['src.cc'],
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ }],
+}
diff --git a/tools/gyp/test/mac/loadable-module/Info.plist b/tools/gyp/test/mac/loadable-module/Info.plist
new file mode 100644
index 00000000000000..f6607aebd91cae
--- /dev/null
+++ b/tools/gyp/test/mac/loadable-module/Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIdentifier
+ com.google.test_loadable_module
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ BRPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ CFPlugInDynamicRegisterFunction
+
+ CFPlugInDynamicRegistration
+ NO
+
+
diff --git a/tools/gyp/test/mac/loadable-module/module.c b/tools/gyp/test/mac/loadable-module/module.c
new file mode 100644
index 00000000000000..958453834744cb
--- /dev/null
+++ b/tools/gyp/test/mac/loadable-module/module.c
@@ -0,0 +1,11 @@
+// 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.
+
+int SuperFly() {
+ return 42;
+}
+
+const char* SuperFoo() {
+ return "Hello World";
+}
diff --git a/tools/gyp/test/mac/loadable-module/test.gyp b/tools/gyp/test/mac/loadable-module/test.gyp
new file mode 100644
index 00000000000000..3c8a5309d25879
--- /dev/null
+++ b/tools/gyp/test/mac/loadable-module/test.gyp
@@ -0,0 +1,18 @@
+# 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_loadable_module',
+ 'type': 'loadable_module',
+ 'mac_bundle': 1,
+ 'sources': [ 'module.c' ],
+ 'product_extension': 'plugin',
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/missing-cfbundlesignature/Info.plist b/tools/gyp/test/mac/missing-cfbundlesignature/Info.plist
new file mode 100644
index 00000000000000..0c3167488440d6
--- /dev/null
+++ b/tools/gyp/test/mac/missing-cfbundlesignature/Info.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundlePackageType
+ APPL
+
+
diff --git a/tools/gyp/test/mac/missing-cfbundlesignature/Other-Info.plist b/tools/gyp/test/mac/missing-cfbundlesignature/Other-Info.plist
new file mode 100644
index 00000000000000..47095281c8a0c8
--- /dev/null
+++ b/tools/gyp/test/mac/missing-cfbundlesignature/Other-Info.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ F
+
+
diff --git a/tools/gyp/test/mac/missing-cfbundlesignature/Third-Info.plist b/tools/gyp/test/mac/missing-cfbundlesignature/Third-Info.plist
new file mode 100644
index 00000000000000..5b61fe2664ec35
--- /dev/null
+++ b/tools/gyp/test/mac/missing-cfbundlesignature/Third-Info.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ some really long string
+
+
diff --git a/tools/gyp/test/mac/missing-cfbundlesignature/file.c b/tools/gyp/test/mac/missing-cfbundlesignature/file.c
new file mode 100644
index 00000000000000..237c8ce181774d
--- /dev/null
+++ b/tools/gyp/test/mac/missing-cfbundlesignature/file.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/missing-cfbundlesignature/test.gyp b/tools/gyp/test/mac/missing-cfbundlesignature/test.gyp
new file mode 100644
index 00000000000000..b50cc2791a031d
--- /dev/null
+++ b/tools/gyp/test/mac/missing-cfbundlesignature/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': 'mytarget',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Info.plist',
+ },
+ },
+ {
+ 'target_name': 'myothertarget',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Other-Info.plist',
+ },
+ },
+ {
+ 'target_name': 'thirdtarget',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'INFOPLIST_FILE': 'Third-Info.plist',
+ },
+ },
+ ],
+}
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 00000000000000..11fc4b660d77cd
--- /dev/null
+++ b/tools/gyp/test/mac/non-strs-flattened-to-env/Info.plist
@@ -0,0 +1,15 @@
+
+
+
+
+
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ ????
+ My Variable
+ ${MY_VAR}
+ CFlags
+ ${OTHER_CFLAGS}
+
+
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 00000000000000..1711567ef56670
--- /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 00000000000000..aaf821c9257010
--- /dev/null
+++ b/tools/gyp/test/mac/non-strs-flattened-to-env/test.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': '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'
+ ],
+ },
+ 'include_dirs': [
+ '$(SDKROOT)/usr/include/libxml2',
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/objc-arc/c-file.c b/tools/gyp/test/mac/objc-arc/c-file.c
new file mode 100644
index 00000000000000..bd6a24eec5929a
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/c-file.c
@@ -0,0 +1,5 @@
+#if __has_feature(objc_arc)
+#error "C files shouldn't be ARC'd!"
+#endif
+
+void c_fun() {}
diff --git a/tools/gyp/test/mac/objc-arc/cc-file.cc b/tools/gyp/test/mac/objc-arc/cc-file.cc
new file mode 100644
index 00000000000000..95e14ea3e053f1
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/cc-file.cc
@@ -0,0 +1,5 @@
+#if __has_feature(objc_arc)
+#error "C++ files shouldn't be ARC'd!"
+#endif
+
+void cc_fun() {}
diff --git a/tools/gyp/test/mac/objc-arc/m-file-no-arc.m b/tools/gyp/test/mac/objc-arc/m-file-no-arc.m
new file mode 100644
index 00000000000000..8ffaabfaae5130
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/m-file-no-arc.m
@@ -0,0 +1,5 @@
+#if __has_feature(objc_arc)
+#error "ObjC files without CLANG_ENABLE_OBJC_ARC should not be ARC'd!"
+#endif
+
+void m_fun() {}
diff --git a/tools/gyp/test/mac/objc-arc/m-file.m b/tools/gyp/test/mac/objc-arc/m-file.m
new file mode 100644
index 00000000000000..9689b1f8e5da5e
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/m-file.m
@@ -0,0 +1,5 @@
+#if !__has_feature(objc_arc)
+#error "ObjC files with CLANG_ENABLE_OBJC_ARC should be ARC'd!"
+#endif
+
+void m_fun() {}
diff --git a/tools/gyp/test/mac/objc-arc/mm-file-no-arc.mm b/tools/gyp/test/mac/objc-arc/mm-file-no-arc.mm
new file mode 100644
index 00000000000000..0dac5394948138
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/mm-file-no-arc.mm
@@ -0,0 +1,5 @@
+#if __has_feature(objc_arc)
+#error "ObjC++ files without CLANG_ENABLE_OBJC_ARC should not be ARC'd!"
+#endif
+
+void mm_fun() {}
diff --git a/tools/gyp/test/mac/objc-arc/mm-file.mm b/tools/gyp/test/mac/objc-arc/mm-file.mm
new file mode 100644
index 00000000000000..9467e965633650
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/mm-file.mm
@@ -0,0 +1,5 @@
+#if !__has_feature(objc_arc)
+#error "ObjC++ files with CLANG_ENABLE_OBJC_ARC should be ARC'd!"
+#endif
+
+void mm_fun() {}
diff --git a/tools/gyp/test/mac/objc-arc/test.gyp b/tools/gyp/test/mac/objc-arc/test.gyp
new file mode 100644
index 00000000000000..d4e9ef8932b284
--- /dev/null
+++ b/tools/gyp/test/mac/objc-arc/test.gyp
@@ -0,0 +1,44 @@
+# Copyright (c) 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'make_global_settings': [
+ ['CC', '/usr/bin/clang'],
+ ['CXX', '/usr/bin/clang++'],
+ ],
+
+ 'targets': [
+ {
+ 'target_name': 'arc_enabled',
+ 'type': 'static_library',
+ 'sources': [
+ 'c-file.c',
+ 'cc-file.cc',
+ 'm-file.m',
+ 'mm-file.mm',
+ ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.6',
+ 'ARCHS': [ 'x86_64' ], # For the non-fragile objc ABI.
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ },
+ },
+
+ {
+ 'target_name': 'arc_disabled',
+ 'type': 'static_library',
+ 'sources': [
+ 'c-file.c',
+ 'cc-file.cc',
+ 'm-file-no-arc.m',
+ 'mm-file-no-arc.mm',
+ ],
+ 'xcode_settings': {
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.6',
+ 'ARCHS': [ 'x86_64' ], # For the non-fragile objc ABI.
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/objc-gc/c-file.c b/tools/gyp/test/mac/objc-gc/c-file.c
new file mode 100644
index 00000000000000..2855a00eaaa693
--- /dev/null
+++ b/tools/gyp/test/mac/objc-gc/c-file.c
@@ -0,0 +1 @@
+void c_fun() {}
diff --git a/tools/gyp/test/mac/objc-gc/cc-file.cc b/tools/gyp/test/mac/objc-gc/cc-file.cc
new file mode 100644
index 00000000000000..71e47a0126fea6
--- /dev/null
+++ b/tools/gyp/test/mac/objc-gc/cc-file.cc
@@ -0,0 +1 @@
+void cc_fun() {}
diff --git a/tools/gyp/test/mac/objc-gc/main.m b/tools/gyp/test/mac/objc-gc/main.m
new file mode 100644
index 00000000000000..1a87f8e70fe29c
--- /dev/null
+++ b/tools/gyp/test/mac/objc-gc/main.m
@@ -0,0 +1,6 @@
+#import
+
+int main() {
+ printf("gc on: %d\n", [NSGarbageCollector defaultCollector] != NULL);
+ return 0;
+}
diff --git a/tools/gyp/test/mac/objc-gc/needs-gc-mm.mm b/tools/gyp/test/mac/objc-gc/needs-gc-mm.mm
new file mode 100644
index 00000000000000..fc3fee9f34ab7b
--- /dev/null
+++ b/tools/gyp/test/mac/objc-gc/needs-gc-mm.mm
@@ -0,0 +1 @@
+void objcpp_fun() { }
diff --git a/tools/gyp/test/mac/objc-gc/needs-gc.m b/tools/gyp/test/mac/objc-gc/needs-gc.m
new file mode 100644
index 00000000000000..ca77976b1da1b6
--- /dev/null
+++ b/tools/gyp/test/mac/objc-gc/needs-gc.m
@@ -0,0 +1 @@
+void objc_fun() { }
diff --git a/tools/gyp/test/mac/objc-gc/test.gyp b/tools/gyp/test/mac/objc-gc/test.gyp
new file mode 100644
index 00000000000000..5b6a53bd38c5d3
--- /dev/null
+++ b/tools/gyp/test/mac/objc-gc/test.gyp
@@ -0,0 +1,101 @@
+# 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': [
+ # For some reason, static_library targets that are built with gc=required
+ # and then linked to executables that don't use gc, the linker doesn't
+ # complain. For shared_libraries it does, so use that.
+ {
+ 'target_name': 'no_gc_lib',
+ 'type': 'shared_library',
+ 'sources': [
+ 'c-file.c',
+ 'cc-file.cc',
+ 'needs-gc-mm.mm',
+ 'needs-gc.m',
+ ],
+ },
+ {
+ 'target_name': 'gc_lib',
+ 'type': 'shared_library',
+ 'sources': [
+ 'c-file.c',
+ 'cc-file.cc',
+ 'needs-gc-mm.mm',
+ 'needs-gc.m',
+ ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_OBJC_GC': 'supported',
+ },
+ },
+ {
+ 'target_name': 'gc_req_lib',
+ 'type': 'shared_library',
+ 'sources': [
+ 'c-file.c',
+ 'cc-file.cc',
+ 'needs-gc-mm.mm',
+ 'needs-gc.m',
+ ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_OBJC_GC': 'required',
+ },
+ },
+
+ {
+ 'target_name': 'gc_exe_fails',
+ 'type': 'executable',
+ 'sources': [ 'main.m' ],
+ 'dependencies': [ 'no_gc_lib' ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_OBJC_GC': 'required',
+ },
+ 'libraries': [ 'Foundation.framework' ],
+ },
+ {
+ 'target_name': 'gc_req_exe',
+ 'type': 'executable',
+ 'sources': [ 'main.m' ],
+ 'dependencies': [ 'gc_lib' ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_OBJC_GC': 'required',
+ },
+ 'libraries': [ 'Foundation.framework' ],
+ },
+ {
+ 'target_name': 'gc_exe_req_lib',
+ 'type': 'executable',
+ 'sources': [ 'main.m' ],
+ 'dependencies': [ 'gc_req_lib' ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_OBJC_GC': 'supported',
+ },
+ 'libraries': [ 'Foundation.framework' ],
+ },
+ {
+ 'target_name': 'gc_exe',
+ 'type': 'executable',
+ 'sources': [ 'main.m' ],
+ 'dependencies': [ 'gc_lib' ],
+ 'xcode_settings': {
+ 'GCC_ENABLE_OBJC_GC': 'supported',
+ },
+ 'libraries': [ 'Foundation.framework' ],
+ },
+ {
+ 'target_name': 'gc_off_exe_req_lib',
+ 'type': 'executable',
+ 'sources': [ 'main.m' ],
+ 'dependencies': [ 'gc_req_lib' ],
+ 'libraries': [ 'Foundation.framework' ],
+ },
+ {
+ 'target_name': 'gc_off_exe',
+ 'type': 'executable',
+ 'sources': [ 'main.m' ],
+ 'dependencies': [ 'gc_lib' ],
+ 'libraries': [ 'Foundation.framework' ],
+ },
+ ],
+}
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 00000000000000..ec36829c080546
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/Framework-Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.yourcompany.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ NSPrincipalClass
+
+ RandomKey
+ RandomValue
+
+
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 00000000000000..98fd5152000f7b
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/TestApp-Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.google.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ ${MACOSX_DEPLOYMENT_TARGET}
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/tools/gyp/test/mac/postbuild-copy-bundle/copied.txt b/tools/gyp/test/mac/postbuild-copy-bundle/copied.txt
new file mode 100644
index 00000000000000..178413886a8f4e
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/copied.txt
@@ -0,0 +1 @@
+old copied file
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 00000000000000..e69de29bb2d1d6
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 00000000000000..21c19635262233
--- /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 00000000000000..930fec66126d07
--- /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 00000000000000..42057fa2357faf
--- /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 00000000000000..a03e6432f2a469
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-copy-bundle/test.gyp
@@ -0,0 +1,49 @@
+# 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',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/$(CONTENTS_FOLDER_PATH)/Libraries',
+ 'files': [ 'copied.txt' ],
+ },
+ ],
+ },
+ {
+ '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 00000000000000..d3f54d76cdc235
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-defaults/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ ????
+ CFBundleName
+ ${PRODUCT_NAME}
+
+
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 00000000000000..1711567ef56670
--- /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 00000000000000..56af2a8329bd67
--- /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 00000000000000..be0a075efc6f0c
--- /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/file.c b/tools/gyp/test/mac/postbuild-fail/file.c
new file mode 100644
index 00000000000000..91695b10c6f183
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-fail/file.c
@@ -0,0 +1,6 @@
+// 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.
+
+// That's right, this is copyrighted.
+void f() {}
diff --git a/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh b/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh
new file mode 100755
index 00000000000000..dc1a60d987e288
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-fail/postbuild-fail.sh
@@ -0,0 +1,6 @@
+#!/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.
+
+exit 1
diff --git a/tools/gyp/test/mac/postbuild-fail/test.gyp b/tools/gyp/test/mac/postbuild-fail/test.gyp
new file mode 100644
index 00000000000000..e63283db036bdd
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-fail/test.gyp
@@ -0,0 +1,38 @@
+# 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': 'nonbundle',
+ 'type': 'static_library',
+ 'sources': [ 'file.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild Fail',
+ 'action': [ './postbuild-fail.sh', ],
+ },
+ {
+ 'postbuild_name': 'Runs after failing postbuild',
+ 'action': [ './touch-static.sh', ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'bundle',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Postbuild Fail',
+ 'action': [ './postbuild-fail.sh', ],
+ },
+ {
+ 'postbuild_name': 'Runs after failing postbuild',
+ 'action': [ './touch-dynamic.sh', ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/postbuild-fail/touch-dynamic.sh b/tools/gyp/test/mac/postbuild-fail/touch-dynamic.sh
new file mode 100755
index 00000000000000..a388a64102bf6b
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-fail/touch-dynamic.sh
@@ -0,0 +1,7 @@
+#!/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.
+
+set -e
+touch "${BUILT_PRODUCTS_DIR}/dynamic_touch"
diff --git a/tools/gyp/test/mac/postbuild-fail/touch-static.sh b/tools/gyp/test/mac/postbuild-fail/touch-static.sh
new file mode 100755
index 00000000000000..97ecaa68682f51
--- /dev/null
+++ b/tools/gyp/test/mac/postbuild-fail/touch-static.sh
@@ -0,0 +1,7 @@
+#!/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.
+
+set -e
+touch "${BUILT_PRODUCTS_DIR}/static_touch"
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 00000000000000..21c19635262233
--- /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 00000000000000..b6170cf7a7f863
--- /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 00000000000000..c350b20d6832cf
--- /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 00000000000000..9554336c0c396d
--- /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 00000000000000..37de4de4f6db98
--- /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 00000000000000..9ef55a0afa95f2
--- /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/postbuilds/copy.sh b/tools/gyp/test/mac/postbuilds/copy.sh
new file mode 100755
index 00000000000000..ecad0381db854b
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/copy.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+cp "$@"
diff --git a/tools/gyp/test/mac/postbuilds/file.c b/tools/gyp/test/mac/postbuilds/file.c
new file mode 100644
index 00000000000000..653e71ff7ecc67
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/file.c
@@ -0,0 +1,4 @@
+// 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.
+void f() {}
diff --git a/tools/gyp/test/mac/postbuilds/file_g.c b/tools/gyp/test/mac/postbuilds/file_g.c
new file mode 100644
index 00000000000000..0f7849d2080fbc
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/file_g.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 g() {}
diff --git a/tools/gyp/test/mac/postbuilds/file_h.c b/tools/gyp/test/mac/postbuilds/file_h.c
new file mode 100644
index 00000000000000..521d1f4d56d1be
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/file_h.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 h() {}
diff --git a/tools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh b/tools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh
new file mode 100755
index 00000000000000..c623c8bf21870d
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/script/shared_library_postbuild.sh
@@ -0,0 +1,23 @@
+#!/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.
+
+set -e
+
+lib="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}"
+nm ${lib} > /dev/null # Just make sure this works.
+
+pattern="${1}"
+
+if [ $pattern != "a|b" ]; then
+ echo "Parameter quoting is broken"
+ exit 1
+fi
+
+if [ "${2}" != "arg with spaces" ]; then
+ echo "Parameter space escaping is broken"
+ exit 1
+fi
+
+touch "${lib}"_touch
diff --git a/tools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh b/tools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh
new file mode 100755
index 00000000000000..2bf09b34e1c998
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/script/static_library_postbuild.sh
@@ -0,0 +1,23 @@
+#!/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.
+
+set -e
+
+lib="${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}"
+nm ${lib} > /dev/null # Just make sure this works.
+
+pattern="${1}"
+
+if [ $pattern != "a|b" ]; then
+ echo "Parameter quote escaping is broken"
+ exit 1
+fi
+
+if [ "${2}" != "arg with spaces" ]; then
+ echo "Parameter space escaping is broken"
+ exit 1
+fi
+
+touch "${lib}"_touch.a
diff --git a/tools/gyp/test/mac/postbuilds/subdirectory/copied_file.txt b/tools/gyp/test/mac/postbuilds/subdirectory/copied_file.txt
new file mode 100644
index 00000000000000..a634f85b6cc452
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/subdirectory/copied_file.txt
@@ -0,0 +1 @@
+This file should be copied to the products dir.
diff --git a/tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp b/tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp
new file mode 100644
index 00000000000000..623de9f625bc15
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/subdirectory/nested_target.gyp
@@ -0,0 +1,52 @@
+# 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': 'nest_el',
+ 'type': 'static_library',
+ 'sources': [ '../file_g.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Static library postbuild',
+ 'variables': {
+ 'some_regex': 'a|b',
+ },
+ 'action': [
+ '../script/static_library_postbuild.sh',
+ '<(some_regex)',
+ 'arg with spaces',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'nest_dyna',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ '../file_h.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Dynamic library postbuild',
+ 'variables': {
+ 'some_regex': 'a|b',
+ },
+ 'action': [
+ '../script/shared_library_postbuild.sh',
+ '<(some_regex)',
+ 'arg with spaces',
+ ],
+ },
+ {
+ 'postbuild_name': 'Test paths relative to gyp file',
+ 'action': [
+ '../copy.sh',
+ './copied_file.txt',
+ '${BUILT_PRODUCTS_DIR}/copied_file_2.txt',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/postbuilds/test.gyp b/tools/gyp/test/mac/postbuilds/test.gyp
new file mode 100644
index 00000000000000..7c0b523f86f0e2
--- /dev/null
+++ b/tools/gyp/test/mac/postbuilds/test.gyp
@@ -0,0 +1,93 @@
+# 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': 'el',
+ 'type': 'static_library',
+ 'sources': [ 'file.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Static library postbuild',
+ 'variables': {
+ 'some_regex': 'a|b',
+ },
+ 'action': [
+ 'script/static_library_postbuild.sh',
+ '<(some_regex)',
+ 'arg with spaces',
+ ],
+ },
+ {
+ 'postbuild_name': 'Test variable in gyp file',
+ 'action': [
+ 'cp',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}_gyp_touch.a',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'dyna',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'dependencies': [
+ 'subdirectory/nested_target.gyp:nest_dyna',
+ 'subdirectory/nested_target.gyp:nest_el',
+ ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Dynamic library postbuild',
+ 'variables': {
+ 'some_regex': 'a|b',
+ },
+ 'action': [
+ 'script/shared_library_postbuild.sh',
+ '<(some_regex)',
+ 'arg with spaces',
+ ],
+ },
+ {
+ 'postbuild_name': 'Test variable in gyp file',
+ 'action': [
+ 'cp',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}_gyp_touch',
+ ],
+ },
+ {
+ 'postbuild_name': 'Test paths relative to gyp file',
+ 'action': [
+ './copy.sh',
+ 'subdirectory/copied_file.txt',
+ '${BUILT_PRODUCTS_DIR}',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'dyna_standalone',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c', ],
+ 'postbuilds': [
+ {
+ 'postbuild_name': 'Test variable in gyp file',
+ 'action': [
+ 'cp',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}',
+ '${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}_gyp_touch.dylib',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'EmptyBundle',
+ 'product_extension': 'bundle',
+ 'type': 'executable',
+ 'mac_bundle': 1,
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/prefixheader/file.c b/tools/gyp/test/mac/prefixheader/file.c
new file mode 100644
index 00000000000000..d0b39d1f6d8bb4
--- /dev/null
+++ b/tools/gyp/test/mac/prefixheader/file.c
@@ -0,0 +1 @@
+MyInt f() { return 0; }
diff --git a/tools/gyp/test/mac/prefixheader/file.cc b/tools/gyp/test/mac/prefixheader/file.cc
new file mode 100644
index 00000000000000..d0b39d1f6d8bb4
--- /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 00000000000000..d0b39d1f6d8bb4
--- /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 00000000000000..d0b39d1f6d8bb4
--- /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/header.h b/tools/gyp/test/mac/prefixheader/header.h
new file mode 100644
index 00000000000000..0716e500c5a69d
--- /dev/null
+++ b/tools/gyp/test/mac/prefixheader/header.h
@@ -0,0 +1 @@
+typedef int MyInt;
diff --git a/tools/gyp/test/mac/prefixheader/test.gyp b/tools/gyp/test/mac/prefixheader/test.gyp
new file mode 100644
index 00000000000000..7e6b1af807f258
--- /dev/null
+++ b/tools/gyp/test/mac/prefixheader/test.gyp
@@ -0,0 +1,82 @@
+# 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': 'prefix_header_c',
+ 'type': 'static_library',
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ },
+ },
+ {
+ 'target_name': 'precompiled_prefix_header_c',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c', ],
+ 'xcode_settings': {
+ 'GCC_PREFIX_HEADER': 'header.h',
+ '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 00000000000000..98fd5152000f7b
--- /dev/null
+++ b/tools/gyp/test/mac/rebuild/TestApp-Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ com.google.${PRODUCT_NAME}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ ${MACOSX_DEPLOYMENT_TARGET}
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
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 00000000000000..7caf105b6e05cb
--- /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 00000000000000..e69de29bb2d1d6
diff --git a/tools/gyp/test/mac/rebuild/main.c b/tools/gyp/test/mac/rebuild/main.c
new file mode 100644
index 00000000000000..237c8ce181774d
--- /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 00000000000000..15b4e4ef2f5b95
--- /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/rpath/file.c b/tools/gyp/test/mac/rpath/file.c
new file mode 100644
index 00000000000000..56757a701bf5cf
--- /dev/null
+++ b/tools/gyp/test/mac/rpath/file.c
@@ -0,0 +1 @@
+void f() {}
diff --git a/tools/gyp/test/mac/rpath/main.c b/tools/gyp/test/mac/rpath/main.c
new file mode 100644
index 00000000000000..237c8ce181774d
--- /dev/null
+++ b/tools/gyp/test/mac/rpath/main.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/tools/gyp/test/mac/rpath/test.gyp b/tools/gyp/test/mac/rpath/test.gyp
new file mode 100644
index 00000000000000..7255cb7cd20b0a
--- /dev/null
+++ b/tools/gyp/test/mac/rpath/test.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': 'default_rpath',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ },
+ {
+ 'target_name': 'explicit_rpath',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'LD_RUNPATH_SEARCH_PATHS': ['@executable_path/.'],
+ },
+ },
+ {
+ 'target_name': 'explicit_rpaths_escaped',
+ 'type': 'shared_library',
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ # Xcode requires spaces to be escaped, else it ends up adding two
+ # independent rpaths.
+ 'LD_RUNPATH_SEARCH_PATHS': ['First\\ rpath', 'Second\\ rpath'],
+ },
+ },
+ {
+ 'target_name': 'explicit_rpaths_bundle',
+ 'product_name': 'My Framework',
+ 'type': 'shared_library',
+ 'mac_bundle': 1,
+ 'sources': [ 'file.c' ],
+ 'xcode_settings': {
+ 'LD_RUNPATH_SEARCH_PATHS': ['@loader_path/.'],
+ },
+ },
+ {
+ 'target_name': 'executable',
+ 'type': 'executable',
+ 'sources': [ 'main.c' ],
+ 'xcode_settings': {
+ 'LD_RUNPATH_SEARCH_PATHS': ['@executable_path/.'],
+ },
+ },
+ ],
+}
diff --git a/tools/gyp/test/mac/sdkroot/file.cc b/tools/gyp/test/mac/sdkroot/file.cc
new file mode 100644
index 00000000000000..13ae9710402af2
--- /dev/null
+++ b/tools/gyp/test/mac/sdkroot/file.cc
@@ -0,0 +1,5 @@
+#include