Browse Source

Upgrade GYP to r1115

v0.7.4-release
Ryan Dahl 13 years ago
parent
commit
60a9e1e40f
  1. 81
      tools/gyp/buildbot/buildbot_run.py
  2. 2
      tools/gyp/gyptest.py
  3. 12
      tools/gyp/pylib/gyp/common.py
  4. 15
      tools/gyp/pylib/gyp/generator/dump_dependency_json.py
  5. 16
      tools/gyp/pylib/gyp/generator/make.py
  6. 166
      tools/gyp/pylib/gyp/generator/ninja.py
  7. 9
      tools/gyp/pylib/gyp/input.py
  8. 67
      tools/gyp/pylib/gyp/ninja_syntax.py
  9. 7
      tools/gyp/test/dependencies/b/b.gyp
  10. 9
      tools/gyp/test/dependencies/b/b3.c
  11. 2
      tools/gyp/test/dependencies/gyptest-lib-only.py
  12. 25
      tools/gyp/test/dependencies/gyptest-none-traversal.py
  13. 14
      tools/gyp/test/dependencies/main.c
  14. 46
      tools/gyp/test/dependencies/none_traversal.gyp
  15. 21
      tools/gyp/test/lib/TestGyp.py

81
tools/gyp/buildbot/buildbot_run.py

@ -13,7 +13,45 @@ import subprocess
import sys import sys
def GypTestFormat(title, format, msvs_version=None): if sys.platform in ['win32', 'cygwin']:
EXE_SUFFIX = '.exe'
else:
EXE_SUFFIX = ''
BUILDBOT_DIR = os.path.dirname(os.path.abspath(__file__))
TRUNK_DIR = os.path.dirname(BUILDBOT_DIR)
ROOT_DIR = os.path.dirname(TRUNK_DIR)
OUT_DIR = os.path.join(TRUNK_DIR, 'out')
NINJA_PATH = os.path.join(TRUNK_DIR, 'ninja' + EXE_SUFFIX)
NINJA_WORK_DIR = os.path.join(ROOT_DIR, 'ninja_work')
def InstallNinja():
"""Install + build ninja.
Returns:
0 for success, 1 for failure.
"""
print '@@@BUILD_STEP install ninja@@@'
# Delete old version if any.
try:
shutil.rmtree(NINJA_WORK_DIR, ignore_errors=True)
except:
pass
# Sync new copy from git.
subprocess.check_call(
'git clone https://github.com/martine/ninja.git ' + NINJA_WORK_DIR,
shell=True)
# Bootstrap.
subprocess.check_call('./bootstrap.sh', cwd=NINJA_WORK_DIR, shell=True)
# Copy out ninja.
shutil.copyfile(os.path.join(NINJA_WORK_DIR, 'ninja' + EXE_SUFFIX),
NINJA_PATH)
os.chmod(NINJA_PATH, 0777)
def GypTestFormat(title, format=None, msvs_version=None):
"""Run the gyp tests for a given format, emitting annotator tags. """Run the gyp tests for a given format, emitting annotator tags.
See annotator docs at: See annotator docs at:
@ -23,12 +61,27 @@ def GypTestFormat(title, format, msvs_version=None):
Returns: Returns:
0 for sucesss, 1 for failure. 0 for sucesss, 1 for failure.
""" """
if not format:
format = title
# Install ninja if needed.
# NOTE: as ninja gets installed each time, regressions to ninja can come
# either from changes to ninja itself, or changes to gyp.
if format == 'ninja':
try:
InstallNinja()
except Exception, e:
print '@@@STEP_FAILURE@@@'
print str(e)
return 1
print '@@@BUILD_STEP ' + title + '@@@' print '@@@BUILD_STEP ' + title + '@@@'
sys.stdout.flush() sys.stdout.flush()
buildbot_dir = os.path.dirname(os.path.abspath(__file__))
trunk_dir = os.path.dirname(buildbot_dir)
root_dir = os.path.dirname(trunk_dir)
env = os.environ.copy() env = os.environ.copy()
# TODO(bradnelson): remove this when this issue is resolved:
# http://code.google.com/p/chromium/issues/detail?id=108251
if format == 'ninja':
env['NOGOLD'] = '1'
if msvs_version: if msvs_version:
env['GYP_MSVS_VERSION'] = msvs_version env['GYP_MSVS_VERSION'] = msvs_version
retcode = subprocess.call(' '.join( retcode = subprocess.call(' '.join(
@ -38,7 +91,7 @@ def GypTestFormat(title, format, msvs_version=None):
'--format', format, '--format', format,
'--chdir', 'trunk', '--chdir', 'trunk',
'--path', '../scons']), '--path', '../scons']),
cwd=root_dir, env=env, shell=True) cwd=ROOT_DIR, env=env, shell=True)
if retcode: if retcode:
# Emit failure tag, and keep going. # Emit failure tag, and keep going.
print '@@@STEP_FAILURE@@@' print '@@@STEP_FAILURE@@@'
@ -49,17 +102,23 @@ def GypTestFormat(title, format, msvs_version=None):
def GypBuild(): def GypBuild():
# Dump out/ directory. # Dump out/ directory.
print '@@@BUILD_STEP cleanup@@@' print '@@@BUILD_STEP cleanup@@@'
print 'Removing out/ ...' print 'Removing %s...' % OUT_DIR
shutil.rmtree('out', ignore_errors=True) shutil.rmtree(OUT_DIR, ignore_errors=True)
print 'Removing %s...' % NINJA_WORK_DIR
shutil.rmtree(NINJA_WORK_DIR, ignore_errors=True)
print 'Removing %s...' % NINJA_PATH
shutil.rmtree(NINJA_PATH, ignore_errors=True)
print 'Done.' print 'Done.'
retcode = 0 retcode = 0
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
retcode += GypTestFormat('scons', format='scons') retcode += GypTestFormat('ninja')
retcode += GypTestFormat('make', format='make') retcode += GypTestFormat('scons')
retcode += GypTestFormat('make')
elif sys.platform == 'darwin': elif sys.platform == 'darwin':
retcode += GypTestFormat('xcode', format='xcode') retcode += GypTestFormat('ninja')
retcode += GypTestFormat('make', format='make') retcode += GypTestFormat('xcode')
retcode += GypTestFormat('make')
elif sys.platform == 'win32': elif sys.platform == 'win32':
retcode += GypTestFormat('msvs-2008', format='msvs', msvs_version='2008') retcode += GypTestFormat('msvs-2008', format='msvs', msvs_version='2008')
if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64': if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64':

2
tools/gyp/gyptest.py

@ -212,7 +212,7 @@ def main(argv=None):
'win32': ['msvs'], 'win32': ['msvs'],
'linux2': ['make', 'ninja'], 'linux2': ['make', 'ninja'],
'linux3': ['make', 'ninja'], 'linux3': ['make', 'ninja'],
'darwin': ['make', 'xcode'], 'darwin': ['make', 'ninja', 'xcode'],
}[sys.platform] }[sys.platform]
for format in format_list: for format in format_list:

12
tools/gyp/pylib/gyp/common.py

@ -344,6 +344,18 @@ def WriteOnDiff(filename):
return Writer() return Writer()
def GetFlavor(params):
"""Returns |params.flavor| if it's set, the system's default flavor else."""
flavors = {
'darwin': 'mac',
'sunos5': 'solaris',
'freebsd7': 'freebsd',
'freebsd8': 'freebsd',
}
flavor = flavors.get(sys.platform, 'linux')
return params.get('flavor', flavor)
# From Alex Martelli, # From Alex Martelli,
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
# ASPN: Python Cookbook: Remove duplicates from a sequence # ASPN: Python Cookbook: Remove duplicates from a sequence

15
tools/gyp/pylib/gyp/generator/dump_dependency_json.py

@ -25,21 +25,10 @@ for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
generator_default_variables[unused] = '' generator_default_variables[unused] = ''
def GetFlavor(params):
"""Returns |params.flavor| if it's set, the system's default flavor else."""
flavors = {
'darwin': 'mac',
'sunos5': 'solaris',
'freebsd7': 'freebsd',
'freebsd8': 'freebsd',
}
flavor = flavors.get(sys.platform, 'linux')
return params.get('flavor', flavor)
def CalculateVariables(default_variables, params): def CalculateVariables(default_variables, params):
generator_flags = params.get('generator_flags', {}) generator_flags = params.get('generator_flags', {})
default_variables['OS'] = generator_flags.get('os', GetFlavor(params)) default_variables['OS'] = generator_flags.get(
'os', gyp.common.GetFlavor(params))
def CalculateGeneratorInputInfo(params): def CalculateGeneratorInputInfo(params):

16
tools/gyp/pylib/gyp/generator/make.py

@ -55,25 +55,13 @@ generator_supports_multiple_toolsets = True
generator_wants_sorted_dependencies = False generator_wants_sorted_dependencies = False
def GetFlavor(params):
"""Returns |params.flavor| if it's set, the system's default flavor else."""
flavors = {
'darwin': 'mac',
'sunos5': 'solaris',
'freebsd7': 'freebsd',
'freebsd8': 'freebsd',
}
flavor = flavors.get(sys.platform, 'linux')
return params.get('flavor', flavor)
def CalculateVariables(default_variables, params): def CalculateVariables(default_variables, params):
"""Calculate additional variables for use in the build (called by gyp).""" """Calculate additional variables for use in the build (called by gyp)."""
cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc')) cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc'))
default_variables['LINKER_SUPPORTS_ICF'] = \ default_variables['LINKER_SUPPORTS_ICF'] = \
gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target) gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target)
flavor = GetFlavor(params) flavor = gyp.common.GetFlavor(params)
if flavor == 'mac': if flavor == 'mac':
default_variables.setdefault('OS', 'mac') default_variables.setdefault('OS', 'mac')
default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
@ -2722,7 +2710,7 @@ def CopyTool(flavor, out_path):
def GenerateOutput(target_list, target_dicts, data, params): def GenerateOutput(target_list, target_dicts, data, params):
options = params['options'] options = params['options']
flavor = GetFlavor(params) flavor = gyp.common.GetFlavor(params)
generator_flags = params.get('generator_flags', {}) generator_flags = params.get('generator_flags', {})
builddir_name = generator_flags.get('output_dir', 'out') builddir_name = generator_flags.get('output_dir', 'out')
android_ndk_version = generator_flags.get('android_ndk_version', None) android_ndk_version = generator_flags.get('android_ndk_version', None)

166
tools/gyp/pylib/gyp/generator/ninja.py

@ -13,18 +13,15 @@ import sys
import gyp.ninja_syntax as ninja_syntax import gyp.ninja_syntax as ninja_syntax
generator_default_variables = { generator_default_variables = {
'OS': 'linux',
'EXECUTABLE_PREFIX': '', 'EXECUTABLE_PREFIX': '',
'EXECUTABLE_SUFFIX': '', 'EXECUTABLE_SUFFIX': '',
'STATIC_LIB_PREFIX': '', 'STATIC_LIB_PREFIX': '',
'STATIC_LIB_SUFFIX': '.a', 'STATIC_LIB_SUFFIX': '.a',
'SHARED_LIB_PREFIX': 'lib', 'SHARED_LIB_PREFIX': 'lib',
'SHARED_LIB_SUFFIX': '.so',
# Gyp expects the following variables to be expandable by the build # Gyp expects the following variables to be expandable by the build
# system to the appropriate locations. Ninja prefers paths to be # system to the appropriate locations. Ninja prefers paths to be
# known at compile time. To resolve this, introduce special # known at gyp time. To resolve this, introduce special
# variables starting with $! (which begin with a $ so gyp knows it # variables starting with $! (which begin with a $ so gyp knows it
# should be treated as a path, but is otherwise an invalid # should be treated as a path, but is otherwise an invalid
# ninja/shell variable) that are passed to gyp here but expanded # ninja/shell variable) that are passed to gyp here but expanded
@ -103,7 +100,7 @@ def InvertRelativePath(path):
# to the input file name as well as the output target name. # to the input file name as well as the output target name.
class NinjaWriter: class NinjaWriter:
def __init__(self, target_outputs, base_dir, build_dir, output_file): def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor):
""" """
base_dir: path from source root to directory containing this gyp file, base_dir: path from source root to directory containing this gyp file,
by gyp semantics, all input paths are relative to this by gyp semantics, all input paths are relative to this
@ -114,6 +111,7 @@ class NinjaWriter:
self.base_dir = base_dir self.base_dir = base_dir
self.build_dir = build_dir self.build_dir = build_dir
self.ninja = ninja_syntax.Writer(output_file) self.ninja = ninja_syntax.Writer(output_file)
self.flavor = flavor
# Relative path from build output dir to base dir. # Relative path from build output dir to base dir.
self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir) self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir)
@ -422,18 +420,32 @@ class NinjaWriter:
self.ninja.variable('cc', '$cc_host') self.ninja.variable('cc', '$cc_host')
self.ninja.variable('cxx', '$cxx_host') self.ninja.variable('cxx', '$cxx_host')
if self.flavor == 'mac':
# TODO(jeremya/thakis): Extract these from XcodeSettings instead.
cflags = []
cflags_c = []
cflags_cc = []
cflags_objc = []
cflags_objcc = []
else:
cflags = config.get('cflags', [])
cflags_c = config.get('cflags_c', [])
cflags_cc = config.get('cflags_cc', [])
self.WriteVariableList('defines', self.WriteVariableList('defines',
[QuoteShellArgument(ninja_syntax.escape('-D' + d)) [QuoteShellArgument(ninja_syntax.escape('-D' + d))
for d in config.get('defines', [])]) for d in config.get('defines', [])])
self.WriteVariableList('includes', self.WriteVariableList('includes',
['-I' + self.GypPathToNinja(i) ['-I' + self.GypPathToNinja(i)
for i in config.get('include_dirs', [])]) for i in config.get('include_dirs', [])])
self.WriteVariableList('cflags', map(self.ExpandSpecial, self.WriteVariableList('cflags', map(self.ExpandSpecial, cflags))
config.get('cflags', []))) self.WriteVariableList('cflags_c', map(self.ExpandSpecial, cflags_c))
self.WriteVariableList('cflags_c', map(self.ExpandSpecial, self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc))
config.get('cflags_c', []))) if self.flavor == 'mac':
self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, self.WriteVariableList('cflags_objc', map(self.ExpandSpecial,
config.get('cflags_cc', []))) cflags_objc))
self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial,
cflags_objcc))
self.ninja.newline() self.ninja.newline()
outputs = [] outputs = []
for source in sources: for source in sources:
@ -443,6 +455,10 @@ class NinjaWriter:
command = 'cxx' command = 'cxx'
elif ext in ('c', 's', 'S'): elif ext in ('c', 's', 'S'):
command = 'cc' command = 'cc'
elif self.flavor == 'mac' and ext == 'm':
command = 'objc'
elif self.flavor == 'mac' and ext == 'mm':
command = 'objcxx'
else: else:
# TODO: should we assert here on unexpected extensions? # TODO: should we assert here on unexpected extensions?
continue continue
@ -498,9 +514,14 @@ class NinjaWriter:
command = command_map[spec['type']] command = command_map[spec['type']]
if output_uses_linker: if output_uses_linker:
if self.flavor == 'mac':
# TODO(jeremya/thakis): Get this from XcodeSettings.
ldflags = []
else:
ldflags = config.get('ldflags', [])
self.WriteVariableList('ldflags', self.WriteVariableList('ldflags',
gyp.common.uniquer(map(self.ExpandSpecial, gyp.common.uniquer(map(self.ExpandSpecial,
config.get('ldflags', [])))) ldflags)))
self.WriteVariableList('libs', self.WriteVariableList('libs',
gyp.common.uniquer(map(self.ExpandSpecial, gyp.common.uniquer(map(self.ExpandSpecial,
spec.get('libraries', [])))) spec.get('libraries', []))))
@ -534,6 +555,10 @@ class NinjaWriter:
'loadable_module': 'so', 'loadable_module': 'so',
'shared_library': 'so', 'shared_library': 'so',
} }
# TODO(thakis/jeremya): Remove once the mac path name computation is done
# by XcodeSettings.
if self.flavor == 'mac':
DEFAULT_EXTENSION['shared_library'] = 'dylib'
extension = spec.get('product_extension', extension = spec.get('product_extension',
DEFAULT_EXTENSION.get(spec['type'], '')) DEFAULT_EXTENSION.get(spec['type'], ''))
if extension: if extension:
@ -576,6 +601,10 @@ class NinjaWriter:
if self.toolset != 'target': if self.toolset != 'target':
libdir = 'lib/%s' % self.toolset libdir = 'lib/%s' % self.toolset
return os.path.join(libdir, filename) return os.path.join(libdir, filename)
# TODO(thakis/jeremya): Remove once the mac path name computation is done
# by XcodeSettings.
elif spec['type'] == 'static_library' and self.flavor == 'mac':
return filename
else: else:
return self.GypPathToUniqueOutput(filename, qualified=False) return self.GypPathToUniqueOutput(filename, qualified=False)
@ -619,6 +648,32 @@ def CalculateVariables(default_variables, params):
default_variables['LINKER_SUPPORTS_ICF'] = \ default_variables['LINKER_SUPPORTS_ICF'] = \
gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target) gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target)
flavor = gyp.common.GetFlavor(params)
if flavor == 'mac':
default_variables.setdefault('OS', 'mac')
default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
# TODO(jeremya/thakis): Set SHARED_LIB_DIR / LIB_DIR.
# Copy additional generator configuration data from Xcode, which is shared
# by the Mac Ninja generator.
import gyp.generator.xcode as xcode_generator
global generator_additional_non_configuration_keys
generator_additional_non_configuration_keys = getattr(xcode_generator,
'generator_additional_non_configuration_keys', [])
global generator_additional_path_sections
generator_additional_path_sections = getattr(xcode_generator,
'generator_additional_path_sections', [])
global generator_extra_sources_for_rules
generator_extra_sources_for_rules = getattr(xcode_generator,
'generator_extra_sources_for_rules', [])
else:
operating_system = flavor
if flavor == 'android':
operating_system = 'linux' # Keep this legacy behavior for now.
default_variables.setdefault('OS', operating_system)
default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
def OpenOutput(path): def OpenOutput(path):
"""Open |path| for writing, creating directories if necessary.""" """Open |path| for writing, creating directories if necessary."""
@ -631,6 +686,7 @@ def OpenOutput(path):
def GenerateOutput(target_list, target_dicts, data, params): def GenerateOutput(target_list, target_dicts, data, params):
options = params['options'] options = params['options']
flavor = gyp.common.GetFlavor(params)
generator_flags = params.get('generator_flags', {}) generator_flags = params.get('generator_flags', {})
if options.generator_output: if options.generator_output:
@ -653,7 +709,13 @@ def GenerateOutput(target_list, target_dicts, data, params):
# TODO: compute cc/cxx/ld/etc. by command-line arguments and system tests. # TODO: compute cc/cxx/ld/etc. by command-line arguments and system tests.
master_ninja.variable('cc', os.environ.get('CC', 'gcc')) master_ninja.variable('cc', os.environ.get('CC', 'gcc'))
master_ninja.variable('cxx', os.environ.get('CXX', 'g++')) master_ninja.variable('cxx', os.environ.get('CXX', 'g++'))
master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4') # TODO(bradnelson): remove NOGOLD when this is resolved:
# http://code.google.com/p/chromium/issues/detail?id=108251
if flavor != 'mac' and not os.environ.get('NOGOLD'):
master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4')
else:
# TODO(jeremya/thakis): flock
master_ninja.variable('ld', '$cxx')
master_ninja.variable('cc_host', '$cc') master_ninja.variable('cc_host', '$cc')
master_ninja.variable('cxx_host', '$cxx') master_ninja.variable('cxx_host', '$cxx')
master_ninja.newline() master_ninja.newline()
@ -670,25 +732,60 @@ def GenerateOutput(target_list, target_dicts, data, params):
command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc ' command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
'-c $in -o $out'), '-c $in -o $out'),
depfile='$out.d') depfile='$out.d')
master_ninja.rule( if flavor != 'mac':
'alink', master_ninja.rule(
description='AR $out', 'alink',
command='rm -f $out && ar rcsT $out $in') description='AR $out',
master_ninja.rule( command='rm -f $out && ar rcsT $out $in')
'solink', master_ninja.rule(
description='SOLINK $out', 'solink',
command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' description='SOLINK $out',
'-Wl,--whole-archive $in -Wl,--no-whole-archive $libs')) command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname '
master_ninja.rule( '-Wl,--whole-archive $in -Wl,--no-whole-archive $libs'))
'solink_module', master_ninja.rule(
description='SOLINK(module) $out', 'solink_module',
command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' description='SOLINK(module) $out',
'-Wl,--start-group $in -Wl,--end-group $libs')) command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname '
master_ninja.rule( '-Wl,--start-group $in -Wl,--end-group $libs'))
'link', master_ninja.rule(
description='LINK $out', 'link',
command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib ' description='LINK $out',
'-Wl,--start-group $in -Wl,--end-group $libs')) command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib '
'-Wl,--start-group $in -Wl,--end-group $libs'))
else:
master_ninja.rule(
'objc',
description='OBJC $out',
command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
'$cflags_objc -c $in -o $out'),
depfile='$out.d')
master_ninja.rule(
'objcxx',
description='OBJCXX $out',
command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
'$cflags_objcc -c $in -o $out'),
depfile='$out.d')
master_ninja.rule(
'alink',
description='LIBTOOL-STATIC $out',
command='rm -f $out && libtool -static -o $out $in')
# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass
# -bundle -single_module here (for osmesa.so).
master_ninja.rule(
'solink',
description='SOLINK $out',
command=('$ld -shared $ldflags -o $out '
'$in $libs'))
master_ninja.rule(
'solink_module',
description='SOLINK(module) $out',
command=('$ld -shared $ldflags -o $out '
'$in $libs'))
master_ninja.rule(
'link',
description='LINK $out',
command=('$ld $ldflags -o $out '
'$in $libs'))
master_ninja.rule( master_ninja.rule(
'stamp', 'stamp',
description='STAMP $out', description='STAMP $out',
@ -696,7 +793,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
master_ninja.rule( master_ninja.rule(
'copy', 'copy',
description='COPY $in $out', description='COPY $in $out',
command='ln -f $in $out 2>/dev/null || cp -af $in $out') command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
master_ninja.newline() master_ninja.newline()
all_targets = set() all_targets = set()
@ -726,7 +823,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
writer = NinjaWriter(target_outputs, base_path, builddir, writer = NinjaWriter(target_outputs, base_path, builddir,
OpenOutput(os.path.join(options.toplevel_dir, OpenOutput(os.path.join(options.toplevel_dir,
builddir, builddir,
output_file))) output_file)),
flavor)
master_ninja.subninja(output_file) master_ninja.subninja(output_file)
output, compile_depends = writer.WriteSpec(spec, config) output, compile_depends = writer.WriteSpec(spec, config)

9
tools/gyp/pylib/gyp/input.py

@ -1,4 +1,4 @@
# Copyright (c) 2011 The Chromium Authors. All rights reserved. # Copyright (c) 2011 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
@ -1358,6 +1358,13 @@ class DependencyGraphNode(object):
# True) and this target won't be linked. # True) and this target won't be linked.
return dependencies return dependencies
# 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)
return dependencies
# Executables and loadable modules are already fully and finally linked. # Executables and loadable modules are already fully and finally linked.
# Nothing else can be a link dependency of them, there can only be # Nothing else can be a link dependency of them, there can only be
# dependencies in the sense that a dependent target might run an # dependencies in the sense that a dependent target might run an

67
tools/gyp/pylib/gyp/ninja_syntax.py

@ -10,6 +10,10 @@ use Python.
""" """
import textwrap import textwrap
import re
def escape_spaces(word):
return word.replace('$ ','$$ ').replace(' ','$ ')
class Writer(object): class Writer(object):
def __init__(self, output, width=78): def __init__(self, output, width=78):
@ -24,29 +28,40 @@ class Writer(object):
self.output.write('# ' + line + '\n') self.output.write('# ' + line + '\n')
def variable(self, key, value, indent=0): def variable(self, key, value, indent=0):
if value is None:
return
if isinstance(value, list):
value = ' '.join(value)
self._line('%s = %s' % (key, value), indent) self._line('%s = %s' % (key, value), indent)
def rule(self, name, command, description=None, depfile=None): def rule(self, name, command, description=None, depfile=None,
generator=False):
self._line('rule %s' % name) self._line('rule %s' % name)
self.variable('command', command, indent=1) self.variable('command', command, indent=1)
if description: if description:
self.variable('description', description, indent=1) self.variable('description', description, indent=1)
if depfile: if depfile:
self.variable('depfile', depfile, indent=1) self.variable('depfile', depfile, indent=1)
if generator:
self.variable('generator', '1', indent=1)
def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
variables=None): variables=None):
outputs = self._as_list(outputs) outputs = self._as_list(outputs)
all_inputs = self._as_list(inputs)[:] all_inputs = self._as_list(inputs)[:]
out_outputs = map(escape_spaces, outputs)
all_inputs = map(escape_spaces, all_inputs)
if implicit: if implicit:
implicit = map(escape_spaces, self._as_list(implicit))
all_inputs.append('|') all_inputs.append('|')
all_inputs.extend(self._as_list(implicit)) all_inputs.extend(implicit)
if order_only: if order_only:
order_only = map(escape_spaces, self._as_list(order_only))
all_inputs.append('||') all_inputs.append('||')
all_inputs.extend(self._as_list(order_only)) all_inputs.extend(order_only)
self._line('build %s: %s %s' % (' '.join(outputs), self._line('build %s: %s %s' % (' '.join(out_outputs),
rule, rule,
' '.join(all_inputs))) ' '.join(all_inputs)))
@ -62,28 +77,52 @@ class Writer(object):
def subninja(self, path): def subninja(self, path):
self._line('subninja %s' % path) self._line('subninja %s' % path)
def default(self, paths):
self._line('default %s' % ' '.join(self._as_list(paths)))
def _line(self, text, indent=0): def _line(self, text, indent=0):
"""Write 'text' word-wrapped at self.width characters.""" """Write 'text' word-wrapped at self.width characters."""
leading_space = ' ' * indent leading_space = ' ' * indent
while len(text) > self.width: while len(text) > self.width:
# The text is too wide; wrap if possible. # The text is too wide; wrap if possible.
# Find the rightmost space that would obey our width constraint. self.output.write(leading_space)
available_space = self.width - len(leading_space) - len(' $') available_space = self.width - len(leading_space) - len(' $')
space = text.rfind(' ', 0, available_space)
if space < 0:
# No such space; just use the first space we can find.
space = text.find(' ', available_space)
if space < 0:
# Give up on breaking.
break
self.output.write(leading_space + text[0:space] + ' $\n') # Write as much as we can into this line.
text = text[space+1:] done = False
written_stuff = False
while available_space > 0:
space = re.search('((\$\$)+([^$]|^)|[^$]|^) ', text)
if space:
space_idx = space.start() + 1
else:
# No spaces left.
done = True
break
if space_idx > available_space:
# We're out of space.
if written_stuff:
# See if we can fit it on the next line.
break
# If we haven't written anything yet on this line, don't
# try to wrap.
self.output.write(text[0:space_idx] + ' ')
written_stuff = True
text = text[space_idx+1:]
available_space -= space_idx+1
self.output.write('$\n')
# Subsequent lines are continuations, so indent them. # Subsequent lines are continuations, so indent them.
leading_space = ' ' * (indent+2) leading_space = ' ' * (indent+2)
if done:
# No more spaces, so bail.
break
self.output.write(leading_space + text + '\n') self.output.write(leading_space + text + '\n')
def _as_list(self, input): def _as_list(self, input):

7
tools/gyp/test/dependencies/b/b.gyp

@ -11,5 +11,12 @@
'b.c', 'b.c',
], ],
}, },
{
'target_name': 'b3',
'type': 'static_library',
'sources': [
'b3.c',
],
},
], ],
} }

9
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;
}

2
tools/gyp/test/dependencies/gyptest-lib-only.py

@ -29,7 +29,7 @@ if sys.platform == 'darwin':
if test.format == 'xcode': if test.format == 'xcode':
test.built_file_must_not_exist('b', type=test.STATIC_LIB) test.built_file_must_not_exist('b', type=test.STATIC_LIB)
else: else:
assert test.format == 'make' assert test.format in ('make', 'ninja')
test.built_file_must_exist('b', type=test.STATIC_LIB) test.built_file_must_exist('b', type=test.STATIC_LIB)
else: else:
# Make puts the resulting library in a directory matching the input gyp file; # Make puts the resulting library in a directory matching the input gyp file;

25
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()

14
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 <stdio.h>
extern int funcA();
int main() {
printf("%d\n", funcA());
return 0;
}

46
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'],
},
],
}

21
tools/gyp/test/lib/TestGyp.py

@ -449,6 +449,11 @@ class TestGypNinja(TestGypBase):
def run_built_executable(self, name, *args, **kw): def run_built_executable(self, name, *args, **kw):
# Enclosing the name in a list avoids prepending the original dir. # Enclosing the name in a list avoids prepending the original dir.
program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
if sys.platform == 'darwin':
libdir = os.path.join('out', 'Default', 'lib')
if self.configuration:
libdir = os.path.join('out', self.configuration, 'lib')
os.environ['DYLD_LIBRARY_PATH'] = libdir
return self.run(program=program, *args, **kw) return self.run(program=program, *args, **kw)
def built_file_path(self, name, type=None, **kw): def built_file_path(self, name, type=None, **kw):
@ -458,9 +463,10 @@ class TestGypNinja(TestGypBase):
result.append(chdir) result.append(chdir)
result.append('out') result.append('out')
result.append(self.configuration_dirname()) result.append(self.configuration_dirname())
if type in (self.STATIC_LIB,): if type == self.STATIC_LIB:
result.append('obj') if sys.platform != 'darwin':
elif type in (self.SHARED_LIB,): result.append('obj')
elif type == self.SHARED_LIB:
result.append('lib') result.append('lib')
subdir = kw.get('subdir') subdir = kw.get('subdir')
if subdir: if subdir:
@ -469,8 +475,13 @@ class TestGypNinja(TestGypBase):
return self.workpath(*result) return self.workpath(*result)
def up_to_date(self, gyp_file, target=None, **kw): def up_to_date(self, gyp_file, target=None, **kw):
kw['stdout'] = "ninja: no work to do.\n" result = self.build(gyp_file, target, **kw)
return 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(TestGypBase): class TestGypMSVS(TestGypBase):

Loading…
Cancel
Save