mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-12-29 01:27:00 +03:00
Rename run.sh -> build.sh. Add tools directory w/ vendored GYP
This commit is contained in:
parent
fcc3c9a2f8
commit
db6230fbdc
1
build.sh
Executable file
1
build.sh
Executable file
@ -0,0 +1 @@
|
||||
python tools/gyp/gyp --depth=. atom.gyp && xcodebuild -project atom.xcodeproj/ -scheme Atom clean build
|
1
run.sh
1
run.sh
@ -1 +0,0 @@
|
||||
python ~/code/gyp/gyp --depth=. atom.gyp && xcodebuild -project atom.xcodeproj/ -configuration Debug -target Atom clean build && open build/Default/Atom.app
|
1
tools/gyp/.gitignore
vendored
Normal file
1
tools/gyp/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pyc
|
6
tools/gyp/AUTHORS
Normal file
6
tools/gyp/AUTHORS
Normal file
@ -0,0 +1,6 @@
|
||||
# Names should be added to this file like so:
|
||||
# Name or Organization <email address>
|
||||
|
||||
Google Inc.
|
||||
Steven Knight <knight@baldmt.com>
|
||||
Ryan Norton <rnorton10@gmail.com>
|
26
tools/gyp/DEPS
Normal file
26
tools/gyp/DEPS
Normal file
@ -0,0 +1,26 @@
|
||||
# DEPS file for gclient use in buildbot execution of gyp tests.
|
||||
#
|
||||
# (You don't need to use gclient for normal GYP development work.)
|
||||
|
||||
vars = {
|
||||
"chrome_trunk": "http://src.chromium.org/svn/trunk",
|
||||
"googlecode_url": "http://%s.googlecode.com/svn",
|
||||
}
|
||||
|
||||
deps = {
|
||||
"scons":
|
||||
Var("chrome_trunk") + "/src/third_party/scons@44099",
|
||||
}
|
||||
|
||||
deps_os = {
|
||||
"win": {
|
||||
"third_party/cygwin":
|
||||
Var("chrome_trunk") + "/deps/third_party/cygwin@66844",
|
||||
|
||||
"third_party/python_26":
|
||||
Var("chrome_trunk") + "/tools/third_party/python_26@89111",
|
||||
|
||||
"src/third_party/pefile":
|
||||
(Var("googlecode_url") % "pefile") + "/trunk@63",
|
||||
},
|
||||
}
|
27
tools/gyp/LICENSE
Normal file
27
tools/gyp/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
21
tools/gyp/MANIFEST
Normal file
21
tools/gyp/MANIFEST
Normal file
@ -0,0 +1,21 @@
|
||||
setup.py
|
||||
gyp
|
||||
LICENSE
|
||||
AUTHORS
|
||||
pylib/gyp/MSVSNew.py
|
||||
pylib/gyp/MSVSProject.py
|
||||
pylib/gyp/MSVSToolFile.py
|
||||
pylib/gyp/MSVSUserFile.py
|
||||
pylib/gyp/MSVSVersion.py
|
||||
pylib/gyp/SCons.py
|
||||
pylib/gyp/__init__.py
|
||||
pylib/gyp/common.py
|
||||
pylib/gyp/input.py
|
||||
pylib/gyp/xcodeproj_file.py
|
||||
pylib/gyp/generator/__init__.py
|
||||
pylib/gyp/generator/gypd.py
|
||||
pylib/gyp/generator/gypsh.py
|
||||
pylib/gyp/generator/make.py
|
||||
pylib/gyp/generator/msvs.py
|
||||
pylib/gyp/generator/scons.py
|
||||
pylib/gyp/generator/xcode.py
|
1
tools/gyp/OWNERS
Normal file
1
tools/gyp/OWNERS
Normal file
@ -0,0 +1 @@
|
||||
*
|
109
tools/gyp/PRESUBMIT.py
Normal file
109
tools/gyp/PRESUBMIT.py
Normal file
@ -0,0 +1,109 @@
|
||||
# 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.
|
||||
|
||||
|
||||
"""Top-level presubmit script for GYP.
|
||||
|
||||
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
|
||||
for more details about the presubmit API built into gcl.
|
||||
"""
|
||||
|
||||
|
||||
PYLINT_BLACKLIST = [
|
||||
# TODO: fix me.
|
||||
# From SCons, not done in google style.
|
||||
'test/lib/TestCmd.py',
|
||||
'test/lib/TestCommon.py',
|
||||
'test/lib/TestGyp.py',
|
||||
# Needs style fix.
|
||||
'pylib/gyp/generator/scons.py',
|
||||
'pylib/gyp/generator/xcode.py',
|
||||
]
|
||||
|
||||
|
||||
PYLINT_DISABLED_WARNINGS = [
|
||||
# TODO: fix me.
|
||||
# Many tests include modules they don't use.
|
||||
'W0611',
|
||||
# Include order doesn't properly include local files?
|
||||
'F0401',
|
||||
# Some use of built-in names.
|
||||
'W0622',
|
||||
# Some unused variables.
|
||||
'W0612',
|
||||
# Operator not preceded/followed by space.
|
||||
'C0323',
|
||||
'C0322',
|
||||
# Unnecessary semicolon.
|
||||
'W0301',
|
||||
# Unused argument.
|
||||
'W0613',
|
||||
# String has no effect (docstring in wrong place).
|
||||
'W0105',
|
||||
# Comma not followed by space.
|
||||
'C0324',
|
||||
# Access to a protected member.
|
||||
'W0212',
|
||||
# Bad indent.
|
||||
'W0311',
|
||||
# Line too long.
|
||||
'C0301',
|
||||
# Undefined variable.
|
||||
'E0602',
|
||||
# Not exception type specified.
|
||||
'W0702',
|
||||
# No member of that name.
|
||||
'E1101',
|
||||
# Dangerous default {}.
|
||||
'W0102',
|
||||
# Others, too many to sort.
|
||||
'W0201', 'W0232', 'E1103', 'W0621', 'W0108', 'W0223', 'W0231',
|
||||
'R0201', 'E0101', 'C0321',
|
||||
# ************* Module copy
|
||||
# W0104:427,12:_test.odict.__setitem__: Statement seems to have no effect
|
||||
'W0104',
|
||||
]
|
||||
|
||||
|
||||
def CheckChangeOnUpload(input_api, output_api):
|
||||
report = []
|
||||
report.extend(input_api.canned_checks.PanProjectChecks(
|
||||
input_api, output_api))
|
||||
return report
|
||||
|
||||
|
||||
def CheckChangeOnCommit(input_api, output_api):
|
||||
report = []
|
||||
license = (
|
||||
r'.*? Copyright \(c\) %(year)s Google Inc\. All rights reserved\.\n'
|
||||
r'.*? Use of this source code is governed by a BSD-style license that '
|
||||
r'can be\n'
|
||||
r'.*? found in the LICENSE file\.\n'
|
||||
) % {
|
||||
'year': input_api.time.strftime('%Y'),
|
||||
}
|
||||
|
||||
report.extend(input_api.canned_checks.PanProjectChecks(
|
||||
input_api, output_api, license_header=license))
|
||||
report.extend(input_api.canned_checks.CheckTreeIsOpen(
|
||||
input_api, output_api,
|
||||
'http://gyp-status.appspot.com/status',
|
||||
'http://gyp-status.appspot.com/current'))
|
||||
|
||||
import sys
|
||||
old_sys_path = sys.path
|
||||
try:
|
||||
sys.path = ['pylib', 'test/lib'] + sys.path
|
||||
report.extend(input_api.canned_checks.RunPylint(
|
||||
input_api,
|
||||
output_api,
|
||||
black_list=PYLINT_BLACKLIST,
|
||||
disabled_warnings=PYLINT_DISABLED_WARNINGS))
|
||||
finally:
|
||||
sys.path = old_sys_path
|
||||
return report
|
||||
|
||||
|
||||
def GetPreferredTrySlaves():
|
||||
return ['gyp-win32', 'gyp-win64', 'gyp-linux', 'gyp-mac']
|
98
tools/gyp/buildbot/buildbot_run.py
Executable file
98
tools/gyp/buildbot/buildbot_run.py
Executable file
@ -0,0 +1,98 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
"""Argument-less script to select what to run on the buildbots."""
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
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')
|
||||
|
||||
|
||||
def GypTestFormat(title, format=None, msvs_version=None):
|
||||
"""Run the gyp tests for a given format, emitting annotator tags.
|
||||
|
||||
See annotator docs at:
|
||||
https://sites.google.com/a/chromium.org/dev/developers/testing/chromium-build-infrastructure/buildbot-annotations
|
||||
Args:
|
||||
format: gyp format to test.
|
||||
Returns:
|
||||
0 for sucesss, 1 for failure.
|
||||
"""
|
||||
if not format:
|
||||
format = title
|
||||
|
||||
print '@@@BUILD_STEP ' + title + '@@@'
|
||||
sys.stdout.flush()
|
||||
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:
|
||||
env['GYP_MSVS_VERSION'] = msvs_version
|
||||
retcode = subprocess.call(' '.join(
|
||||
[sys.executable, 'trunk/gyptest.py',
|
||||
'--all',
|
||||
'--passed',
|
||||
'--format', format,
|
||||
'--chdir', 'trunk',
|
||||
'--path', '../scons']),
|
||||
cwd=ROOT_DIR, env=env, shell=True)
|
||||
if retcode:
|
||||
# Emit failure tag, and keep going.
|
||||
print '@@@STEP_FAILURE@@@'
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def GypBuild():
|
||||
# Dump out/ directory.
|
||||
print '@@@BUILD_STEP cleanup@@@'
|
||||
print 'Removing %s...' % OUT_DIR
|
||||
shutil.rmtree(OUT_DIR, ignore_errors=True)
|
||||
print 'Done.'
|
||||
|
||||
retcode = 0
|
||||
if sys.platform.startswith('linux'):
|
||||
retcode += GypTestFormat('ninja')
|
||||
retcode += GypTestFormat('scons')
|
||||
retcode += GypTestFormat('make')
|
||||
elif sys.platform == 'darwin':
|
||||
retcode += GypTestFormat('ninja')
|
||||
retcode += GypTestFormat('xcode')
|
||||
retcode += GypTestFormat('make')
|
||||
elif sys.platform == 'win32':
|
||||
retcode += GypTestFormat('ninja')
|
||||
retcode += GypTestFormat('msvs-2008', format='msvs', msvs_version='2008')
|
||||
if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64':
|
||||
retcode += GypTestFormat('msvs-2010', format='msvs', msvs_version='2010')
|
||||
else:
|
||||
raise Exception('Unknown platform')
|
||||
if retcode:
|
||||
# TODO(bradnelson): once the annotator supports a postscript (section for
|
||||
# after the build proper that could be used for cumulative failures),
|
||||
# use that instead of this. This isolates the final return value so
|
||||
# that it isn't misattributed to the last stage.
|
||||
print '@@@BUILD_STEP failures@@@'
|
||||
sys.exit(retcode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
GypBuild()
|
10
tools/gyp/codereview.settings
Normal file
10
tools/gyp/codereview.settings
Normal file
@ -0,0 +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
|
||||
TRYSERVER_PROJECT: gyp
|
||||
TRYSERVER_PATCHLEVEL: 0
|
||||
TRYSERVER_ROOT: trunk
|
||||
TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try-nacl
|
||||
|
18
tools/gyp/gyp
Executable file
18
tools/gyp/gyp
Executable file
@ -0,0 +1,18 @@
|
||||
#!/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
|
||||
|
||||
# TODO(mark): sys.path manipulation is some temporary testing stuff.
|
||||
try:
|
||||
import gyp
|
||||
except ImportError, e:
|
||||
import os.path
|
||||
sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'pylib'))
|
||||
import gyp
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(gyp.main(sys.argv[1:]))
|
5
tools/gyp/gyp.bat
Executable file
5
tools/gyp/gyp.bat
Executable file
@ -0,0 +1,5 @@
|
||||
@rem Copyright (c) 2009 Google Inc. All rights reserved.
|
||||
@rem Use of this source code is governed by a BSD-style license that can be
|
||||
@rem found in the LICENSE file.
|
||||
|
||||
@python "%~dp0/gyp" %*
|
7
tools/gyp/gyp_dummy.c
Normal file
7
tools/gyp/gyp_dummy.c
Normal file
@ -0,0 +1,7 @@
|
||||
/* 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. */
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
256
tools/gyp/gyptest.py
Executable file
256
tools/gyp/gyptest.py
Executable file
@ -0,0 +1,256 @@
|
||||
#!/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.
|
||||
|
||||
__doc__ = """
|
||||
gyptest.py -- test runner for GYP tests.
|
||||
"""
|
||||
|
||||
import os
|
||||
import optparse
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
class CommandRunner:
|
||||
"""
|
||||
Executor class for commands, including "commands" implemented by
|
||||
Python functions.
|
||||
"""
|
||||
verbose = True
|
||||
active = True
|
||||
|
||||
def __init__(self, dictionary={}):
|
||||
self.subst_dictionary(dictionary)
|
||||
|
||||
def subst_dictionary(self, dictionary):
|
||||
self._subst_dictionary = dictionary
|
||||
|
||||
def subst(self, string, dictionary=None):
|
||||
"""
|
||||
Substitutes (via the format operator) the values in the specified
|
||||
dictionary into the specified command.
|
||||
|
||||
The command can be an (action, string) tuple. In all cases, we
|
||||
perform substitution on strings and don't worry if something isn't
|
||||
a string. (It's probably a Python function to be executed.)
|
||||
"""
|
||||
if dictionary is None:
|
||||
dictionary = self._subst_dictionary
|
||||
if dictionary:
|
||||
try:
|
||||
string = string % dictionary
|
||||
except TypeError:
|
||||
pass
|
||||
return string
|
||||
|
||||
def display(self, command, stdout=None, stderr=None):
|
||||
if not self.verbose:
|
||||
return
|
||||
if type(command) == type(()):
|
||||
func = command[0]
|
||||
args = command[1:]
|
||||
s = '%s(%s)' % (func.__name__, ', '.join(map(repr, args)))
|
||||
if type(command) == type([]):
|
||||
# TODO: quote arguments containing spaces
|
||||
# TODO: handle meta characters?
|
||||
s = ' '.join(command)
|
||||
else:
|
||||
s = self.subst(command)
|
||||
if not s.endswith('\n'):
|
||||
s += '\n'
|
||||
sys.stdout.write(s)
|
||||
sys.stdout.flush()
|
||||
|
||||
def execute(self, command, stdout=None, stderr=None):
|
||||
"""
|
||||
Executes a single command.
|
||||
"""
|
||||
if not self.active:
|
||||
return 0
|
||||
if type(command) == type(''):
|
||||
command = self.subst(command)
|
||||
cmdargs = shlex.split(command)
|
||||
if cmdargs[0] == 'cd':
|
||||
command = (os.chdir,) + tuple(cmdargs[1:])
|
||||
if type(command) == type(()):
|
||||
func = command[0]
|
||||
args = command[1:]
|
||||
return func(*args)
|
||||
else:
|
||||
if stdout is sys.stdout:
|
||||
# Same as passing sys.stdout, except python2.4 doesn't fail on it.
|
||||
subout = None
|
||||
else:
|
||||
# Open pipe for anything else so Popen works on python2.4.
|
||||
subout = subprocess.PIPE
|
||||
if stderr is sys.stderr:
|
||||
# Same as passing sys.stderr, except python2.4 doesn't fail on it.
|
||||
suberr = None
|
||||
elif stderr is None:
|
||||
# Merge with stdout if stderr isn't specified.
|
||||
suberr = subprocess.STDOUT
|
||||
else:
|
||||
# Open pipe for anything else so Popen works on python2.4.
|
||||
suberr = subprocess.PIPE
|
||||
p = subprocess.Popen(command,
|
||||
shell=(sys.platform == 'win32'),
|
||||
stdout=subout,
|
||||
stderr=suberr)
|
||||
p.wait()
|
||||
if stdout is None:
|
||||
self.stdout = p.stdout.read()
|
||||
elif stdout is not sys.stdout:
|
||||
stdout.write(p.stdout.read())
|
||||
if stderr not in (None, sys.stderr):
|
||||
stderr.write(p.stderr.read())
|
||||
return p.returncode
|
||||
|
||||
def run(self, command, display=None, stdout=None, stderr=None):
|
||||
"""
|
||||
Runs a single command, displaying it first.
|
||||
"""
|
||||
if display is None:
|
||||
display = command
|
||||
self.display(display)
|
||||
return self.execute(command, stdout, stderr)
|
||||
|
||||
|
||||
class Unbuffered:
|
||||
def __init__(self, fp):
|
||||
self.fp = fp
|
||||
def write(self, arg):
|
||||
self.fp.write(arg)
|
||||
self.fp.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.fp, attr)
|
||||
|
||||
sys.stdout = Unbuffered(sys.stdout)
|
||||
sys.stderr = Unbuffered(sys.stderr)
|
||||
|
||||
|
||||
def find_all_gyptest_files(directory):
|
||||
result = []
|
||||
for root, dirs, files in os.walk(directory):
|
||||
if '.svn' in dirs:
|
||||
dirs.remove('.svn')
|
||||
result.extend([ os.path.join(root, f) for f in files
|
||||
if f.startswith('gyptest') and f.endswith('.py') ])
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
usage = "gyptest.py [-ahlnq] [-f formats] [test ...]"
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option("-a", "--all", action="store_true",
|
||||
help="run all tests")
|
||||
parser.add_option("-C", "--chdir", action="store", default=None,
|
||||
help="chdir to the specified directory")
|
||||
parser.add_option("-f", "--format", action="store", default='',
|
||||
help="run tests with the specified formats")
|
||||
parser.add_option("-l", "--list", action="store_true",
|
||||
help="list available tests and exit")
|
||||
parser.add_option("-n", "--no-exec", action="store_true",
|
||||
help="no execute, just print the command line")
|
||||
parser.add_option("--passed", action="store_true",
|
||||
help="report passed tests")
|
||||
parser.add_option("--path", action="append", default=[],
|
||||
help="additional $PATH directory")
|
||||
parser.add_option("-q", "--quiet", action="store_true",
|
||||
help="quiet, don't print test command lines")
|
||||
opts, args = parser.parse_args(argv[1:])
|
||||
|
||||
if opts.chdir:
|
||||
os.chdir(opts.chdir)
|
||||
|
||||
if opts.path:
|
||||
os.environ['PATH'] += ':' + ':'.join(opts.path)
|
||||
|
||||
if not args:
|
||||
if not opts.all:
|
||||
sys.stderr.write('Specify -a to get all tests.\n')
|
||||
return 1
|
||||
args = ['test']
|
||||
|
||||
tests = []
|
||||
for arg in args:
|
||||
if os.path.isdir(arg):
|
||||
tests.extend(find_all_gyptest_files(os.path.normpath(arg)))
|
||||
else:
|
||||
tests.append(arg)
|
||||
|
||||
if opts.list:
|
||||
for test in tests:
|
||||
print test
|
||||
sys.exit(0)
|
||||
|
||||
CommandRunner.verbose = not opts.quiet
|
||||
CommandRunner.active = not opts.no_exec
|
||||
cr = CommandRunner()
|
||||
|
||||
os.environ['PYTHONPATH'] = os.path.abspath('test/lib')
|
||||
if not opts.quiet:
|
||||
sys.stdout.write('PYTHONPATH=%s\n' % os.environ['PYTHONPATH'])
|
||||
|
||||
passed = []
|
||||
failed = []
|
||||
no_result = []
|
||||
|
||||
if opts.format:
|
||||
format_list = opts.format.split(',')
|
||||
else:
|
||||
# TODO: not duplicate this mapping from pylib/gyp/__init__.py
|
||||
format_list = {
|
||||
'freebsd7': ['make'],
|
||||
'freebsd8': ['make'],
|
||||
'cygwin': ['msvs'],
|
||||
'win32': ['msvs', 'ninja'],
|
||||
'linux2': ['make', 'ninja'],
|
||||
'linux3': ['make', 'ninja'],
|
||||
'darwin': ['make', 'ninja', 'xcode'],
|
||||
}[sys.platform]
|
||||
|
||||
for format in format_list:
|
||||
os.environ['TESTGYP_FORMAT'] = format
|
||||
if not opts.quiet:
|
||||
sys.stdout.write('TESTGYP_FORMAT=%s\n' % format)
|
||||
|
||||
for test in tests:
|
||||
status = cr.run([sys.executable, test],
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr)
|
||||
if status == 2:
|
||||
no_result.append(test)
|
||||
elif status:
|
||||
failed.append(test)
|
||||
else:
|
||||
passed.append(test)
|
||||
|
||||
if not opts.quiet:
|
||||
def report(description, tests):
|
||||
if tests:
|
||||
if len(tests) == 1:
|
||||
sys.stdout.write("\n%s the following test:\n" % description)
|
||||
else:
|
||||
fmt = "\n%s the following %d tests:\n"
|
||||
sys.stdout.write(fmt % (description, len(tests)))
|
||||
sys.stdout.write("\t" + "\n\t".join(tests) + "\n")
|
||||
|
||||
if opts.passed:
|
||||
report("Passed", passed)
|
||||
report("Failed", failed)
|
||||
report("No result from", no_result)
|
||||
|
||||
if failed:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
341
tools/gyp/pylib/gyp/MSVSNew.py
Normal file
341
tools/gyp/pylib/gyp/MSVSNew.py
Normal file
@ -0,0 +1,341 @@
|
||||
# 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.
|
||||
|
||||
"""New implementation of Visual Studio project generation for SCons."""
|
||||
|
||||
import os
|
||||
import random
|
||||
|
||||
import gyp.common
|
||||
|
||||
# hashlib is supplied as of Python 2.5 as the replacement interface for md5
|
||||
# and other secure hashes. In 2.6, md5 is deprecated. Import hashlib if
|
||||
# available, avoiding a deprecation warning under 2.6. Import md5 otherwise,
|
||||
# preserving 2.4 compatibility.
|
||||
try:
|
||||
import hashlib
|
||||
_new_md5 = hashlib.md5
|
||||
except ImportError:
|
||||
import md5
|
||||
_new_md5 = md5.new
|
||||
|
||||
|
||||
# Initialize random number generator
|
||||
random.seed()
|
||||
|
||||
# GUIDs for project types
|
||||
ENTRY_TYPE_GUIDS = {
|
||||
'project': '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}',
|
||||
'folder': '{2150E333-8FDC-42A3-9474-1A3956D46DE8}',
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Helper functions
|
||||
|
||||
|
||||
def MakeGuid(name, seed='msvs_new'):
|
||||
"""Returns a GUID for the specified target name.
|
||||
|
||||
Args:
|
||||
name: Target name.
|
||||
seed: Seed for MD5 hash.
|
||||
Returns:
|
||||
A GUID-line string calculated from the name and seed.
|
||||
|
||||
This generates something which looks like a GUID, but depends only on the
|
||||
name and seed. This means the same name/seed will always generate the same
|
||||
GUID, so that projects and solutions which refer to each other can explicitly
|
||||
determine the GUID to refer to explicitly. It also means that the GUID will
|
||||
not change when the project for a target is rebuilt.
|
||||
"""
|
||||
# Calculate a MD5 signature for the seed and name.
|
||||
d = _new_md5(str(seed) + str(name)).hexdigest().upper()
|
||||
# Convert most of the signature to GUID form (discard the rest)
|
||||
guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20]
|
||||
+ '-' + d[20:32] + '}')
|
||||
return guid
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MSVSFolder(object):
|
||||
"""Folder in a Visual Studio project or solution."""
|
||||
|
||||
def __init__(self, path, name = None, entries = None,
|
||||
guid = None, items = None):
|
||||
"""Initializes the folder.
|
||||
|
||||
Args:
|
||||
path: Full path to the folder.
|
||||
name: Name of the folder.
|
||||
entries: List of folder entries to nest inside this folder. May contain
|
||||
Folder or Project objects. May be None, if the folder is empty.
|
||||
guid: GUID to use for folder, if not None.
|
||||
items: List of solution items to include in the folder project. May be
|
||||
None, if the folder does not directly contain items.
|
||||
"""
|
||||
if name:
|
||||
self.name = name
|
||||
else:
|
||||
# Use last layer.
|
||||
self.name = os.path.basename(path)
|
||||
|
||||
self.path = path
|
||||
self.guid = guid
|
||||
|
||||
# Copy passed lists (or set to empty lists)
|
||||
self.entries = list(entries or [])
|
||||
self.items = list(items or [])
|
||||
|
||||
self.entry_type_guid = ENTRY_TYPE_GUIDS['folder']
|
||||
|
||||
def get_guid(self):
|
||||
if self.guid is None:
|
||||
# Use consistent guids for folders (so things don't regenerate).
|
||||
self.guid = MakeGuid(self.path, seed='msvs_folder')
|
||||
return self.guid
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MSVSProject(object):
|
||||
"""Visual Studio project."""
|
||||
|
||||
def __init__(self, path, name = None, dependencies = None, guid = None,
|
||||
spec = None, build_file = None, config_platform_overrides = None,
|
||||
fixpath_prefix = None):
|
||||
"""Initializes the project.
|
||||
|
||||
Args:
|
||||
path: Absolute path to the project file.
|
||||
name: Name of project. If None, the name will be the same as the base
|
||||
name of the project file.
|
||||
dependencies: List of other Project objects this project is dependent
|
||||
upon, if not None.
|
||||
guid: GUID to use for project, if not None.
|
||||
spec: Dictionary specifying how to build this project.
|
||||
build_file: Filename of the .gyp file that the vcproj file comes from.
|
||||
config_platform_overrides: optional dict of configuration platforms to
|
||||
used in place of the default for this target.
|
||||
fixpath_prefix: the path used to adjust the behavior of _fixpath
|
||||
"""
|
||||
self.path = path
|
||||
self.guid = guid
|
||||
self.spec = spec
|
||||
self.build_file = build_file
|
||||
# Use project filename if name not specified
|
||||
self.name = name or os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
# Copy passed lists (or set to empty lists)
|
||||
self.dependencies = list(dependencies or [])
|
||||
|
||||
self.entry_type_guid = ENTRY_TYPE_GUIDS['project']
|
||||
|
||||
if config_platform_overrides:
|
||||
self.config_platform_overrides = config_platform_overrides
|
||||
else:
|
||||
self.config_platform_overrides = {}
|
||||
self.fixpath_prefix = fixpath_prefix
|
||||
self.msbuild_toolset = None
|
||||
|
||||
def set_dependencies(self, dependencies):
|
||||
self.dependencies = list(dependencies or [])
|
||||
|
||||
def get_guid(self):
|
||||
if self.guid is None:
|
||||
# Set GUID from path
|
||||
# TODO(rspangler): This is fragile.
|
||||
# 1. We can't just use the project filename sans path, since there could
|
||||
# be multiple projects with the same base name (for example,
|
||||
# foo/unittest.vcproj and bar/unittest.vcproj).
|
||||
# 2. The path needs to be relative to $SOURCE_ROOT, so that the project
|
||||
# GUID is the same whether it's included from base/base.sln or
|
||||
# foo/bar/baz/baz.sln.
|
||||
# 3. The GUID needs to be the same each time this builder is invoked, so
|
||||
# that we don't need to rebuild the solution when the project changes.
|
||||
# 4. We should be able to handle pre-built project files by reading the
|
||||
# GUID from the files.
|
||||
self.guid = MakeGuid(self.name)
|
||||
return self.guid
|
||||
|
||||
def set_msbuild_toolset(self, msbuild_toolset):
|
||||
self.msbuild_toolset = msbuild_toolset
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MSVSSolution:
|
||||
"""Visual Studio solution."""
|
||||
|
||||
def __init__(self, path, version, entries=None, variants=None,
|
||||
websiteProperties=True):
|
||||
"""Initializes the solution.
|
||||
|
||||
Args:
|
||||
path: Path to solution file.
|
||||
version: Format version to emit.
|
||||
entries: List of entries in solution. May contain Folder or Project
|
||||
objects. May be None, if the folder is empty.
|
||||
variants: List of build variant strings. If none, a default list will
|
||||
be used.
|
||||
websiteProperties: Flag to decide if the website properties section
|
||||
is generated.
|
||||
"""
|
||||
self.path = path
|
||||
self.websiteProperties = websiteProperties
|
||||
self.version = version
|
||||
|
||||
# Copy passed lists (or set to empty lists)
|
||||
self.entries = list(entries or [])
|
||||
|
||||
if variants:
|
||||
# Copy passed list
|
||||
self.variants = variants[:]
|
||||
else:
|
||||
# Use default
|
||||
self.variants = ['Debug|Win32', 'Release|Win32']
|
||||
# TODO(rspangler): Need to be able to handle a mapping of solution config
|
||||
# to project config. Should we be able to handle variants being a dict,
|
||||
# or add a separate variant_map variable? If it's a dict, we can't
|
||||
# guarantee the order of variants since dict keys aren't ordered.
|
||||
|
||||
|
||||
# TODO(rspangler): Automatically write to disk for now; should delay until
|
||||
# node-evaluation time.
|
||||
self.Write()
|
||||
|
||||
|
||||
def Write(self, writer=gyp.common.WriteOnDiff):
|
||||
"""Writes the solution file to disk.
|
||||
|
||||
Raises:
|
||||
IndexError: An entry appears multiple times.
|
||||
"""
|
||||
# Walk the entry tree and collect all the folders and projects.
|
||||
all_entries = set()
|
||||
entries_to_check = self.entries[:]
|
||||
while entries_to_check:
|
||||
e = entries_to_check.pop(0)
|
||||
|
||||
# If this entry has been visited, nothing to do.
|
||||
if e in all_entries:
|
||||
continue
|
||||
|
||||
all_entries.add(e)
|
||||
|
||||
# If this is a folder, check its entries too.
|
||||
if isinstance(e, MSVSFolder):
|
||||
entries_to_check += e.entries
|
||||
|
||||
# Sort by name then guid (so things are in order on vs2008).
|
||||
def NameThenGuid(a, b):
|
||||
if a.name < b.name: return -1
|
||||
if a.name > b.name: return 1
|
||||
if a.get_guid() < b.get_guid(): return -1
|
||||
if a.get_guid() > b.get_guid(): return 1
|
||||
return 0
|
||||
|
||||
all_entries = sorted(all_entries, NameThenGuid)
|
||||
|
||||
# Open file and print header
|
||||
f = writer(self.path)
|
||||
f.write('Microsoft Visual Studio Solution File, '
|
||||
'Format Version %s\r\n' % self.version.SolutionVersion())
|
||||
f.write('# %s\r\n' % self.version.Description())
|
||||
|
||||
# Project entries
|
||||
sln_root = os.path.split(self.path)[0]
|
||||
for e in all_entries:
|
||||
relative_path = gyp.common.RelativePath(e.path, sln_root)
|
||||
# msbuild does not accept an empty folder_name.
|
||||
# use '.' in case relative_path is empty.
|
||||
folder_name = relative_path.replace('/', '\\') or '.'
|
||||
f.write('Project("%s") = "%s", "%s", "%s"\r\n' % (
|
||||
e.entry_type_guid, # Entry type GUID
|
||||
e.name, # Folder name
|
||||
folder_name, # Folder name (again)
|
||||
e.get_guid(), # Entry GUID
|
||||
))
|
||||
|
||||
# TODO(rspangler): Need a way to configure this stuff
|
||||
if self.websiteProperties:
|
||||
f.write('\tProjectSection(WebsiteProperties) = preProject\r\n'
|
||||
'\t\tDebug.AspNetCompiler.Debug = "True"\r\n'
|
||||
'\t\tRelease.AspNetCompiler.Debug = "False"\r\n'
|
||||
'\tEndProjectSection\r\n')
|
||||
|
||||
if isinstance(e, MSVSFolder):
|
||||
if e.items:
|
||||
f.write('\tProjectSection(SolutionItems) = preProject\r\n')
|
||||
for i in e.items:
|
||||
f.write('\t\t%s = %s\r\n' % (i, i))
|
||||
f.write('\tEndProjectSection\r\n')
|
||||
|
||||
if isinstance(e, MSVSProject):
|
||||
if e.dependencies:
|
||||
f.write('\tProjectSection(ProjectDependencies) = postProject\r\n')
|
||||
for d in e.dependencies:
|
||||
f.write('\t\t%s = %s\r\n' % (d.get_guid(), d.get_guid()))
|
||||
f.write('\tEndProjectSection\r\n')
|
||||
|
||||
f.write('EndProject\r\n')
|
||||
|
||||
# Global section
|
||||
f.write('Global\r\n')
|
||||
|
||||
# Configurations (variants)
|
||||
f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n')
|
||||
for v in self.variants:
|
||||
f.write('\t\t%s = %s\r\n' % (v, v))
|
||||
f.write('\tEndGlobalSection\r\n')
|
||||
|
||||
# Sort config guids for easier diffing of solution changes.
|
||||
config_guids = []
|
||||
config_guids_overrides = {}
|
||||
for e in all_entries:
|
||||
if isinstance(e, MSVSProject):
|
||||
config_guids.append(e.get_guid())
|
||||
config_guids_overrides[e.get_guid()] = e.config_platform_overrides
|
||||
config_guids.sort()
|
||||
|
||||
f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n')
|
||||
for g in config_guids:
|
||||
for v in self.variants:
|
||||
nv = config_guids_overrides[g].get(v, v)
|
||||
# Pick which project configuration to build for this solution
|
||||
# configuration.
|
||||
f.write('\t\t%s.%s.ActiveCfg = %s\r\n' % (
|
||||
g, # Project GUID
|
||||
v, # Solution build configuration
|
||||
nv, # Project build config for that solution config
|
||||
))
|
||||
|
||||
# Enable project in this solution configuration.
|
||||
f.write('\t\t%s.%s.Build.0 = %s\r\n' % (
|
||||
g, # Project GUID
|
||||
v, # Solution build configuration
|
||||
nv, # Project build config for that solution config
|
||||
))
|
||||
f.write('\tEndGlobalSection\r\n')
|
||||
|
||||
# TODO(rspangler): Should be able to configure this stuff too (though I've
|
||||
# never seen this be any different)
|
||||
f.write('\tGlobalSection(SolutionProperties) = preSolution\r\n')
|
||||
f.write('\t\tHideSolutionNode = FALSE\r\n')
|
||||
f.write('\tEndGlobalSection\r\n')
|
||||
|
||||
# Folder mappings
|
||||
# TODO(rspangler): Should omit this section if there are no folders
|
||||
f.write('\tGlobalSection(NestedProjects) = preSolution\r\n')
|
||||
for e in all_entries:
|
||||
if not isinstance(e, MSVSFolder):
|
||||
continue # Does not apply to projects, only folders
|
||||
for subentry in e.entries:
|
||||
f.write('\t\t%s = %s\r\n' % (subentry.get_guid(), e.get_guid()))
|
||||
f.write('\tEndGlobalSection\r\n')
|
||||
|
||||
f.write('EndGlobal\r\n')
|
||||
|
||||
f.close()
|
208
tools/gyp/pylib/gyp/MSVSProject.py
Normal file
208
tools/gyp/pylib/gyp/MSVSProject.py
Normal file
@ -0,0 +1,208 @@
|
||||
# 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.
|
||||
|
||||
"""Visual Studio project reader/writer."""
|
||||
|
||||
import gyp.common
|
||||
import gyp.easy_xml as easy_xml
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Tool(object):
|
||||
"""Visual Studio tool."""
|
||||
|
||||
def __init__(self, name, attrs=None):
|
||||
"""Initializes the tool.
|
||||
|
||||
Args:
|
||||
name: Tool name.
|
||||
attrs: Dict of tool attributes; may be None.
|
||||
"""
|
||||
self._attrs = attrs or {}
|
||||
self._attrs['Name'] = name
|
||||
|
||||
def _GetSpecification(self):
|
||||
"""Creates an element for the tool.
|
||||
|
||||
Returns:
|
||||
A new xml.dom.Element for the tool.
|
||||
"""
|
||||
return ['Tool', self._attrs]
|
||||
|
||||
class Filter(object):
|
||||
"""Visual Studio filter - that is, a virtual folder."""
|
||||
|
||||
def __init__(self, name, contents=None):
|
||||
"""Initializes the folder.
|
||||
|
||||
Args:
|
||||
name: Filter (folder) name.
|
||||
contents: List of filenames and/or Filter objects contained.
|
||||
"""
|
||||
self.name = name
|
||||
self.contents = list(contents or [])
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Writer(object):
|
||||
"""Visual Studio XML project writer."""
|
||||
|
||||
def __init__(self, project_path, version, name, guid=None, platforms=None):
|
||||
"""Initializes the project.
|
||||
|
||||
Args:
|
||||
project_path: Path to the project file.
|
||||
version: Format version to emit.
|
||||
name: Name of the project.
|
||||
guid: GUID to use for project, if not None.
|
||||
platforms: Array of string, the supported platforms. If null, ['Win32']
|
||||
"""
|
||||
self.project_path = project_path
|
||||
self.version = version
|
||||
self.name = name
|
||||
self.guid = guid
|
||||
|
||||
# Default to Win32 for platforms.
|
||||
if not platforms:
|
||||
platforms = ['Win32']
|
||||
|
||||
# Initialize the specifications of the various sections.
|
||||
self.platform_section = ['Platforms']
|
||||
for platform in platforms:
|
||||
self.platform_section.append(['Platform', {'Name': platform}])
|
||||
self.tool_files_section = ['ToolFiles']
|
||||
self.configurations_section = ['Configurations']
|
||||
self.files_section = ['Files']
|
||||
|
||||
# Keep a dict keyed on filename to speed up access.
|
||||
self.files_dict = dict()
|
||||
|
||||
def AddToolFile(self, path):
|
||||
"""Adds a tool file to the project.
|
||||
|
||||
Args:
|
||||
path: Relative path from project to tool file.
|
||||
"""
|
||||
self.tool_files_section.append(['ToolFile', {'RelativePath': path}])
|
||||
|
||||
def _GetSpecForConfiguration(self, config_type, config_name, attrs, tools):
|
||||
"""Returns the specification for a configuration.
|
||||
|
||||
Args:
|
||||
config_type: Type of configuration node.
|
||||
config_name: Configuration name.
|
||||
attrs: Dict of configuration attributes; may be None.
|
||||
tools: List of tools (strings or Tool objects); may be None.
|
||||
Returns:
|
||||
"""
|
||||
# Handle defaults
|
||||
if not attrs:
|
||||
attrs = {}
|
||||
if not tools:
|
||||
tools = []
|
||||
|
||||
# Add configuration node and its attributes
|
||||
node_attrs = attrs.copy()
|
||||
node_attrs['Name'] = config_name
|
||||
specification = [config_type, node_attrs]
|
||||
|
||||
# Add tool nodes and their attributes
|
||||
if tools:
|
||||
for t in tools:
|
||||
if isinstance(t, Tool):
|
||||
specification.append(t._GetSpecification())
|
||||
else:
|
||||
specification.append(Tool(t)._GetSpecification())
|
||||
return specification
|
||||
|
||||
|
||||
def AddConfig(self, name, attrs=None, tools=None):
|
||||
"""Adds a configuration to the project.
|
||||
|
||||
Args:
|
||||
name: Configuration name.
|
||||
attrs: Dict of configuration attributes; may be None.
|
||||
tools: List of tools (strings or Tool objects); may be None.
|
||||
"""
|
||||
spec = self._GetSpecForConfiguration('Configuration', name, attrs, tools)
|
||||
self.configurations_section.append(spec)
|
||||
|
||||
def _AddFilesToNode(self, parent, files):
|
||||
"""Adds files and/or filters to the parent node.
|
||||
|
||||
Args:
|
||||
parent: Destination node
|
||||
files: A list of Filter objects and/or relative paths to files.
|
||||
|
||||
Will call itself recursively, if the files list contains Filter objects.
|
||||
"""
|
||||
for f in files:
|
||||
if isinstance(f, Filter):
|
||||
node = ['Filter', {'Name': f.name}]
|
||||
self._AddFilesToNode(node, f.contents)
|
||||
else:
|
||||
node = ['File', {'RelativePath': f}]
|
||||
self.files_dict[f] = node
|
||||
parent.append(node)
|
||||
|
||||
def AddFiles(self, files):
|
||||
"""Adds files to the project.
|
||||
|
||||
Args:
|
||||
files: A list of Filter objects and/or relative paths to files.
|
||||
|
||||
This makes a copy of the file/filter tree at the time of this call. If you
|
||||
later add files to a Filter object which was passed into a previous call
|
||||
to AddFiles(), it will not be reflected in this project.
|
||||
"""
|
||||
self._AddFilesToNode(self.files_section, files)
|
||||
# TODO(rspangler) This also doesn't handle adding files to an existing
|
||||
# filter. That is, it doesn't merge the trees.
|
||||
|
||||
def AddFileConfig(self, path, config, attrs=None, tools=None):
|
||||
"""Adds a configuration to a file.
|
||||
|
||||
Args:
|
||||
path: Relative path to the file.
|
||||
config: Name of configuration to add.
|
||||
attrs: Dict of configuration attributes; may be None.
|
||||
tools: List of tools (strings or Tool objects); may be None.
|
||||
|
||||
Raises:
|
||||
ValueError: Relative path does not match any file added via AddFiles().
|
||||
"""
|
||||
# Find the file node with the right relative path
|
||||
parent = self.files_dict.get(path)
|
||||
if not parent:
|
||||
raise ValueError('AddFileConfig: file "%s" not in project.' % path)
|
||||
|
||||
# Add the config to the file node
|
||||
spec = self._GetSpecForConfiguration('FileConfiguration', config, attrs,
|
||||
tools)
|
||||
parent.append(spec)
|
||||
|
||||
def WriteIfChanged(self):
|
||||
"""Writes the project file."""
|
||||
# First create XML content definition
|
||||
content = [
|
||||
'VisualStudioProject',
|
||||
{'ProjectType': 'Visual C++',
|
||||
'Version': self.version.ProjectVersion(),
|
||||
'Name': self.name,
|
||||
'ProjectGUID': self.guid,
|
||||
'RootNamespace': self.name,
|
||||
'Keyword': 'Win32Proj'
|
||||
},
|
||||
self.platform_section,
|
||||
self.tool_files_section,
|
||||
self.configurations_section,
|
||||
['References'], # empty section
|
||||
self.files_section,
|
||||
['Globals'] # empty section
|
||||
]
|
||||
easy_xml.WriteXmlIfChanged(content, self.project_path,
|
||||
encoding="Windows-1252")
|
1046
tools/gyp/pylib/gyp/MSVSSettings.py
Normal file
1046
tools/gyp/pylib/gyp/MSVSSettings.py
Normal file
File diff suppressed because it is too large
Load Diff
1482
tools/gyp/pylib/gyp/MSVSSettings_test.py
Executable file
1482
tools/gyp/pylib/gyp/MSVSSettings_test.py
Executable file
File diff suppressed because it is too large
Load Diff
58
tools/gyp/pylib/gyp/MSVSToolFile.py
Normal file
58
tools/gyp/pylib/gyp/MSVSToolFile.py
Normal file
@ -0,0 +1,58 @@
|
||||
# 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.
|
||||
|
||||
"""Visual Studio project reader/writer."""
|
||||
|
||||
import gyp.common
|
||||
import gyp.easy_xml as easy_xml
|
||||
|
||||
|
||||
class Writer(object):
|
||||
"""Visual Studio XML tool file writer."""
|
||||
|
||||
def __init__(self, tool_file_path, name):
|
||||
"""Initializes the tool file.
|
||||
|
||||
Args:
|
||||
tool_file_path: Path to the tool file.
|
||||
name: Name of the tool file.
|
||||
"""
|
||||
self.tool_file_path = tool_file_path
|
||||
self.name = name
|
||||
self.rules_section = ['Rules']
|
||||
|
||||
def AddCustomBuildRule(self, name, cmd, description,
|
||||
additional_dependencies,
|
||||
outputs, extensions):
|
||||
"""Adds a rule to the tool file.
|
||||
|
||||
Args:
|
||||
name: Name of the rule.
|
||||
description: Description of the rule.
|
||||
cmd: Command line of the rule.
|
||||
additional_dependencies: other files which may trigger the rule.
|
||||
outputs: outputs of the rule.
|
||||
extensions: extensions handled by the rule.
|
||||
"""
|
||||
rule = ['CustomBuildRule',
|
||||
{'Name': name,
|
||||
'ExecutionDescription': description,
|
||||
'CommandLine': cmd,
|
||||
'Outputs': ';'.join(outputs),
|
||||
'FileExtensions': ';'.join(extensions),
|
||||
'AdditionalDependencies':
|
||||
';'.join(additional_dependencies)
|
||||
}]
|
||||
self.rules_section.append(rule)
|
||||
|
||||
def WriteIfChanged(self):
|
||||
"""Writes the tool file."""
|
||||
content = ['VisualStudioToolFile',
|
||||
{'Version': '8.00',
|
||||
'Name': self.name
|
||||
},
|
||||
self.rules_section
|
||||
]
|
||||
easy_xml.WriteXmlIfChanged(content, self.tool_file_path,
|
||||
encoding="Windows-1252")
|
147
tools/gyp/pylib/gyp/MSVSUserFile.py
Normal file
147
tools/gyp/pylib/gyp/MSVSUserFile.py
Normal file
@ -0,0 +1,147 @@
|
||||
# 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.
|
||||
|
||||
"""Visual Studio user preferences file writer."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import socket # for gethostname
|
||||
|
||||
import gyp.common
|
||||
import gyp.easy_xml as easy_xml
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def _FindCommandInPath(command):
|
||||
"""If there are no slashes in the command given, this function
|
||||
searches the PATH env to find the given command, and converts it
|
||||
to an absolute path. We have to do this because MSVS is looking
|
||||
for an actual file to launch a debugger on, not just a command
|
||||
line. Note that this happens at GYP time, so anything needing to
|
||||
be built needs to have a full path."""
|
||||
if '/' in command or '\\' in command:
|
||||
# If the command already has path elements (either relative or
|
||||
# absolute), then assume it is constructed properly.
|
||||
return command
|
||||
else:
|
||||
# Search through the path list and find an existing file that
|
||||
# we can access.
|
||||
paths = os.environ.get('PATH','').split(os.pathsep)
|
||||
for path in paths:
|
||||
item = os.path.join(path, command)
|
||||
if os.path.isfile(item) and os.access(item, os.X_OK):
|
||||
return item
|
||||
return command
|
||||
|
||||
def _QuoteWin32CommandLineArgs(args):
|
||||
new_args = []
|
||||
for arg in args:
|
||||
# Replace all double-quotes with double-double-quotes to escape
|
||||
# them for cmd shell, and then quote the whole thing if there
|
||||
# are any.
|
||||
if arg.find('"') != -1:
|
||||
arg = '""'.join(arg.split('"'))
|
||||
arg = '"%s"' % arg
|
||||
|
||||
# Otherwise, if there are any spaces, quote the whole arg.
|
||||
elif re.search(r'[ \t\n]', arg):
|
||||
arg = '"%s"' % arg
|
||||
new_args.append(arg)
|
||||
return new_args
|
||||
|
||||
class Writer(object):
|
||||
"""Visual Studio XML user user file writer."""
|
||||
|
||||
def __init__(self, user_file_path, version, name):
|
||||
"""Initializes the user file.
|
||||
|
||||
Args:
|
||||
user_file_path: Path to the user file.
|
||||
version: Version info.
|
||||
name: Name of the user file.
|
||||
"""
|
||||
self.user_file_path = user_file_path
|
||||
self.version = version
|
||||
self.name = name
|
||||
self.configurations = {}
|
||||
|
||||
def AddConfig(self, name):
|
||||
"""Adds a configuration to the project.
|
||||
|
||||
Args:
|
||||
name: Configuration name.
|
||||
"""
|
||||
self.configurations[name] = ['Configuration', {'Name': name}]
|
||||
|
||||
def AddDebugSettings(self, config_name, command, environment = {},
|
||||
working_directory=""):
|
||||
"""Adds a DebugSettings node to the user file for a particular config.
|
||||
|
||||
Args:
|
||||
command: command line to run. First element in the list is the
|
||||
executable. All elements of the command will be quoted if
|
||||
necessary.
|
||||
working_directory: other files which may trigger the rule. (optional)
|
||||
"""
|
||||
command = _QuoteWin32CommandLineArgs(command)
|
||||
|
||||
abs_command = _FindCommandInPath(command[0])
|
||||
|
||||
if environment and isinstance(environment, dict):
|
||||
env_list = ['%s="%s"' % (key, val)
|
||||
for (key,val) in environment.iteritems()]
|
||||
environment = ' '.join(env_list)
|
||||
else:
|
||||
environment = ''
|
||||
|
||||
n_cmd = ['DebugSettings',
|
||||
{'Command': abs_command,
|
||||
'WorkingDirectory': working_directory,
|
||||
'CommandArguments': " ".join(command[1:]),
|
||||
'RemoteMachine': socket.gethostname(),
|
||||
'Environment': environment,
|
||||
'EnvironmentMerge': 'true',
|
||||
# Currently these are all "dummy" values that we're just setting
|
||||
# in the default manner that MSVS does it. We could use some of
|
||||
# these to add additional capabilities, I suppose, but they might
|
||||
# not have parity with other platforms then.
|
||||
'Attach': 'false',
|
||||
'DebuggerType': '3', # 'auto' debugger
|
||||
'Remote': '1',
|
||||
'RemoteCommand': '',
|
||||
'HttpUrl': '',
|
||||
'PDBPath': '',
|
||||
'SQLDebugging': '',
|
||||
'DebuggerFlavor': '0',
|
||||
'MPIRunCommand': '',
|
||||
'MPIRunArguments': '',
|
||||
'MPIRunWorkingDirectory': '',
|
||||
'ApplicationCommand': '',
|
||||
'ApplicationArguments': '',
|
||||
'ShimCommand': '',
|
||||
'MPIAcceptMode': '',
|
||||
'MPIAcceptFilter': ''
|
||||
}]
|
||||
|
||||
# Find the config, and add it if it doesn't exist.
|
||||
if config_name not in self.configurations:
|
||||
self.AddConfig(config_name)
|
||||
|
||||
# Add the DebugSettings onto the appropriate config.
|
||||
self.configurations[config_name].append(n_cmd)
|
||||
|
||||
def WriteIfChanged(self):
|
||||
"""Writes the user file."""
|
||||
configs = ['Configurations']
|
||||
for config, spec in sorted(self.configurations.iteritems()):
|
||||
configs.append(spec)
|
||||
|
||||
content = ['VisualStudioUserFile',
|
||||
{'Version': self.version.ProjectVersion(),
|
||||
'Name': self.name
|
||||
},
|
||||
configs]
|
||||
easy_xml.WriteXmlIfChanged(content, self.user_file_path,
|
||||
encoding="Windows-1252")
|
325
tools/gyp/pylib/gyp/MSVSVersion.py
Normal file
325
tools/gyp/pylib/gyp/MSVSVersion.py
Normal file
@ -0,0 +1,325 @@
|
||||
# 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.
|
||||
|
||||
"""Handle version information related to Visual Stuio."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
class VisualStudioVersion(object):
|
||||
"""Information regarding a version of Visual Studio."""
|
||||
|
||||
def __init__(self, short_name, description,
|
||||
solution_version, project_version, flat_sln, uses_vcxproj,
|
||||
path, sdk_based):
|
||||
self.short_name = short_name
|
||||
self.description = description
|
||||
self.solution_version = solution_version
|
||||
self.project_version = project_version
|
||||
self.flat_sln = flat_sln
|
||||
self.uses_vcxproj = uses_vcxproj
|
||||
self.path = path
|
||||
self.sdk_based = sdk_based
|
||||
|
||||
def ShortName(self):
|
||||
return self.short_name
|
||||
|
||||
def Description(self):
|
||||
"""Get the full description of the version."""
|
||||
return self.description
|
||||
|
||||
def SolutionVersion(self):
|
||||
"""Get the version number of the sln files."""
|
||||
return self.solution_version
|
||||
|
||||
def ProjectVersion(self):
|
||||
"""Get the version number of the vcproj or vcxproj files."""
|
||||
return self.project_version
|
||||
|
||||
def FlatSolution(self):
|
||||
return self.flat_sln
|
||||
|
||||
def UsesVcxproj(self):
|
||||
"""Returns true if this version uses a vcxproj file."""
|
||||
return self.uses_vcxproj
|
||||
|
||||
def ProjectExtension(self):
|
||||
"""Returns the file extension for the project."""
|
||||
return self.uses_vcxproj and '.vcxproj' or '.vcproj'
|
||||
|
||||
def Path(self):
|
||||
"""Returns the path to Visual Studio installation."""
|
||||
return self.path
|
||||
|
||||
def ToolPath(self, tool):
|
||||
"""Returns the path to a given compiler tool. """
|
||||
return os.path.normpath(os.path.join(self.path, "VC/bin", tool))
|
||||
|
||||
def SetupScript(self, target_arch):
|
||||
"""Returns a command (with arguments) to be used to set up the
|
||||
environment."""
|
||||
# Check if we are running in the SDK command line environment and use
|
||||
# the setup script from the SDK if so. |target_arch| should be either
|
||||
# 'x86' or 'x64'.
|
||||
assert target_arch in ('x86', 'x64')
|
||||
sdk_dir = os.environ.get('WindowsSDKDir')
|
||||
if self.sdk_based and sdk_dir:
|
||||
return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
|
||||
'/' + target_arch]
|
||||
else:
|
||||
# We don't use VC/vcvarsall.bat for x86 because vcvarsall calls
|
||||
# vcvars32, which it can only find if VS??COMNTOOLS is set, which it
|
||||
# isn't always.
|
||||
if target_arch == 'x86':
|
||||
return [os.path.normpath(
|
||||
os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
|
||||
else:
|
||||
assert target_arch == 'x64'
|
||||
arg = 'x86_amd64'
|
||||
if (os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
|
||||
os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
|
||||
# Use the 64-on-64 compiler if we can.
|
||||
arg = 'amd64'
|
||||
return [os.path.normpath(
|
||||
os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
|
||||
|
||||
|
||||
def _RegistryQueryBase(sysdir, key, value):
|
||||
"""Use reg.exe to read a particular key.
|
||||
|
||||
While ideally we might use the win32 module, we would like gyp to be
|
||||
python neutral, so for instance cygwin python lacks this module.
|
||||
|
||||
Arguments:
|
||||
sysdir: The system subdirectory to attempt to launch reg.exe from.
|
||||
key: The registry key to read from.
|
||||
value: The particular value to read.
|
||||
Return:
|
||||
stdout from reg.exe, or None for failure.
|
||||
"""
|
||||
# Skip if not on Windows or Python Win32 setup issue
|
||||
if sys.platform not in ('win32', 'cygwin'):
|
||||
return None
|
||||
# Setup params to pass to and attempt to launch reg.exe
|
||||
cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
|
||||
'query', key]
|
||||
if value:
|
||||
cmd.extend(['/v', value])
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
# Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
|
||||
# Note that the error text may be in [1] in some cases
|
||||
text = p.communicate()[0]
|
||||
# Check return code from reg.exe; officially 0==success and 1==error
|
||||
if p.returncode:
|
||||
return None
|
||||
return text
|
||||
|
||||
|
||||
def _RegistryQuery(key, value=None):
|
||||
"""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
|
||||
up and available on Windows Server 2003 and XP through KB patch 942589. Note
|
||||
that Sysnative will always fail if using 64-bit python due to it being a
|
||||
virtual directory and System32 will work correctly in the first place.
|
||||
|
||||
KB 942589 - http://support.microsoft.com/kb/942589/en-us.
|
||||
|
||||
Arguments:
|
||||
key: The registry key.
|
||||
value: The particular registry value to read (optional).
|
||||
Return:
|
||||
stdout from reg.exe, or None for failure.
|
||||
"""
|
||||
text = None
|
||||
try:
|
||||
text = _RegistryQueryBase('Sysnative', key, value)
|
||||
except OSError, e:
|
||||
if e.errno == errno.ENOENT:
|
||||
text = _RegistryQueryBase('System32', key, value)
|
||||
else:
|
||||
raise
|
||||
return text
|
||||
|
||||
|
||||
def _RegistryGetValue(key, value):
|
||||
"""Use reg.exe 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.
|
||||
"""
|
||||
text = _RegistryQuery(key, value)
|
||||
if not text:
|
||||
return None
|
||||
# Extract value.
|
||||
match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
|
||||
if not match:
|
||||
return None
|
||||
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.
|
||||
|
||||
Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
|
||||
autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
|
||||
passed in that doesn't match a value in versions python will throw a error.
|
||||
"""
|
||||
versions = {
|
||||
'2010': VisualStudioVersion('2010',
|
||||
'Visual Studio 2010',
|
||||
solution_version='11.00',
|
||||
project_version='4.0',
|
||||
flat_sln=False,
|
||||
uses_vcxproj=True,
|
||||
path=path,
|
||||
sdk_based=sdk_based),
|
||||
'2010e': VisualStudioVersion('2010e',
|
||||
'Visual Studio 2010',
|
||||
solution_version='11.00',
|
||||
project_version='4.0',
|
||||
flat_sln=True,
|
||||
uses_vcxproj=True,
|
||||
path=path,
|
||||
sdk_based=sdk_based),
|
||||
'2008': VisualStudioVersion('2008',
|
||||
'Visual Studio 2008',
|
||||
solution_version='10.00',
|
||||
project_version='9.00',
|
||||
flat_sln=False,
|
||||
uses_vcxproj=False,
|
||||
path=path,
|
||||
sdk_based=sdk_based),
|
||||
'2008e': VisualStudioVersion('2008e',
|
||||
'Visual Studio 2008',
|
||||
solution_version='10.00',
|
||||
project_version='9.00',
|
||||
flat_sln=True,
|
||||
uses_vcxproj=False,
|
||||
path=path,
|
||||
sdk_based=sdk_based),
|
||||
'2005': VisualStudioVersion('2005',
|
||||
'Visual Studio 2005',
|
||||
solution_version='9.00',
|
||||
project_version='8.00',
|
||||
flat_sln=False,
|
||||
uses_vcxproj=False,
|
||||
path=path,
|
||||
sdk_based=sdk_based),
|
||||
'2005e': VisualStudioVersion('2005e',
|
||||
'Visual Studio 2005',
|
||||
solution_version='9.00',
|
||||
project_version='8.00',
|
||||
flat_sln=True,
|
||||
uses_vcxproj=False,
|
||||
path=path,
|
||||
sdk_based=sdk_based),
|
||||
}
|
||||
return versions[str(name)]
|
||||
|
||||
|
||||
def _DetectVisualStudioVersions(versions_to_check, force_express):
|
||||
"""Collect the list of installed visual studio versions.
|
||||
|
||||
Returns:
|
||||
A list of visual studio versions installed in descending order of
|
||||
usage preference.
|
||||
Base this on the registry and a quick check if devenv.exe exists.
|
||||
Only versions 8-10 are considered.
|
||||
Possibilities are:
|
||||
2005(e) - Visual Studio 2005 (8)
|
||||
2008(e) - Visual Studio 2008 (9)
|
||||
2010(e) - Visual Studio 2010 (10)
|
||||
Where (e) is e for express editions of MSVS and blank otherwise.
|
||||
"""
|
||||
version_to_year = {'8.0': '2005', '9.0': '2008', '10.0': '2010'}
|
||||
versions = []
|
||||
for version in versions_to_check:
|
||||
# Old method of searching for which VS version is installed
|
||||
# We don't use the 2010-encouraged-way because we also want to get the
|
||||
# path to the binaries, which it doesn't offer.
|
||||
keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
|
||||
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
|
||||
r'HKLM\Software\Microsoft\VCExpress\%s' % version,
|
||||
r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
|
||||
for index in range(len(keys)):
|
||||
path = _RegistryGetValue(keys[index], 'InstallDir')
|
||||
if not path:
|
||||
continue
|
||||
# Check for full.
|
||||
full_path = os.path.join(path, 'devenv.exe')
|
||||
express_path = os.path.join(path, 'vcexpress.exe')
|
||||
if not force_express and os.path.exists(full_path):
|
||||
# Add this one.
|
||||
versions.append(_CreateVersion(version_to_year[version],
|
||||
os.path.join(path, '..', '..')))
|
||||
# Check for express.
|
||||
elif os.path.exists(express_path):
|
||||
# Add this one.
|
||||
versions.append(_CreateVersion(version_to_year[version] + 'e',
|
||||
os.path.join(path, '..', '..')))
|
||||
|
||||
# The old method above does not work when only SDK is installed.
|
||||
keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
|
||||
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
|
||||
for index in range(len(keys)):
|
||||
path = _RegistryGetValue(keys[index], version)
|
||||
if not path:
|
||||
continue
|
||||
versions.append(_CreateVersion(version_to_year[version] + 'e',
|
||||
os.path.join(path, '..'), sdk_based=True))
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
def SelectVisualStudioVersion(version='auto'):
|
||||
"""Select which version of Visual Studio projects to generate.
|
||||
|
||||
Arguments:
|
||||
version: Hook to allow caller to force a particular version (vs auto).
|
||||
Returns:
|
||||
An object representing a visual studio project format version.
|
||||
"""
|
||||
# In auto mode, check environment variable for override.
|
||||
if version == 'auto':
|
||||
version = os.environ.get('GYP_MSVS_VERSION', 'auto')
|
||||
version_map = {
|
||||
'auto': ('10.0', '9.0', '8.0'),
|
||||
'2005': ('8.0',),
|
||||
'2005e': ('8.0',),
|
||||
'2008': ('9.0',),
|
||||
'2008e': ('9.0',),
|
||||
'2010': ('10.0',),
|
||||
'2010e': ('10.0',),
|
||||
}
|
||||
version = str(version)
|
||||
versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
|
||||
if not versions:
|
||||
if version == 'auto':
|
||||
# Default to 2005 if we couldn't find anything
|
||||
return _CreateVersion('2005', None)
|
||||
else:
|
||||
return _CreateVersion(version, None)
|
||||
return versions[0]
|
199
tools/gyp/pylib/gyp/SCons.py
Normal file
199
tools/gyp/pylib/gyp/SCons.py
Normal file
@ -0,0 +1,199 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
SCons generator.
|
||||
|
||||
This contains class definitions and supporting functions for generating
|
||||
pieces of SCons files for the different types of GYP targets.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def WriteList(fp, list, prefix='',
|
||||
separator=',\n ',
|
||||
preamble=None,
|
||||
postamble=None):
|
||||
fp.write(preamble or '')
|
||||
fp.write((separator or ' ').join([prefix + l for l in list]))
|
||||
fp.write(postamble or '')
|
||||
|
||||
|
||||
class TargetBase(object):
|
||||
"""
|
||||
Base class for a SCons representation of a GYP target.
|
||||
"""
|
||||
is_ignored = False
|
||||
target_prefix = ''
|
||||
target_suffix = ''
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
def full_product_name(self):
|
||||
"""
|
||||
Returns the full name of the product being built:
|
||||
|
||||
* Uses 'product_name' if it's set, else prefix + 'target_name'.
|
||||
* Prepends 'product_dir' if set.
|
||||
* Appends SCons suffix variables for the target type (or
|
||||
product_extension).
|
||||
"""
|
||||
suffix = self.target_suffix
|
||||
product_extension = self.spec.get('product_extension')
|
||||
if product_extension:
|
||||
suffix = '.' + product_extension
|
||||
prefix = self.spec.get('product_prefix', self.target_prefix)
|
||||
name = self.spec['target_name']
|
||||
name = prefix + self.spec.get('product_name', name) + suffix
|
||||
product_dir = self.spec.get('product_dir')
|
||||
if product_dir:
|
||||
name = os.path.join(product_dir, name)
|
||||
else:
|
||||
name = os.path.join(self.out_dir, name)
|
||||
return name
|
||||
|
||||
def write_input_files(self, fp):
|
||||
"""
|
||||
Writes the definition of the input files (sources).
|
||||
"""
|
||||
sources = self.spec.get('sources')
|
||||
if not sources:
|
||||
fp.write('\ninput_files = []\n')
|
||||
return
|
||||
preamble = '\ninput_files = [\n '
|
||||
postamble = ',\n]\n'
|
||||
WriteList(fp, map(repr, sources), preamble=preamble, postamble=postamble)
|
||||
|
||||
def builder_call(self):
|
||||
"""
|
||||
Returns the actual SCons builder call to build this target.
|
||||
"""
|
||||
name = self.full_product_name()
|
||||
return 'env.%s(env.File(%r), input_files)' % (self.builder_name, name)
|
||||
def write_target(self, fp, src_dir='', pre=''):
|
||||
"""
|
||||
Writes the lines necessary to build this target.
|
||||
"""
|
||||
fp.write('\n' + pre)
|
||||
fp.write('_outputs = %s\n' % self.builder_call())
|
||||
fp.write('target_files.extend(_outputs)\n')
|
||||
|
||||
|
||||
class NoneTarget(TargetBase):
|
||||
"""
|
||||
A GYP target type of 'none', implicitly or explicitly.
|
||||
"""
|
||||
def write_target(self, fp, src_dir='', pre=''):
|
||||
fp.write('\ntarget_files.extend(input_files)\n')
|
||||
|
||||
|
||||
class SettingsTarget(TargetBase):
|
||||
"""
|
||||
A GYP target type of 'settings'.
|
||||
"""
|
||||
is_ignored = True
|
||||
|
||||
|
||||
compilable_sources_template = """
|
||||
_result = []
|
||||
for infile in input_files:
|
||||
if env.compilable(infile):
|
||||
if (type(infile) == type('')
|
||||
and (infile.startswith(%(src_dir)r)
|
||||
or not os.path.isabs(env.subst(infile)))):
|
||||
# Force files below the build directory by replacing all '..'
|
||||
# elements in the path with '__':
|
||||
base, ext = os.path.splitext(os.path.normpath(infile))
|
||||
base = [d == '..' and '__' or d for d in base.split('/')]
|
||||
base = os.path.join(*base)
|
||||
object = '${OBJ_DIR}/${COMPONENT_NAME}/${TARGET_NAME}/' + base
|
||||
if not infile.startswith(%(src_dir)r):
|
||||
infile = %(src_dir)r + infile
|
||||
infile = env.%(name)s(object, infile)[0]
|
||||
else:
|
||||
infile = env.%(name)s(infile)[0]
|
||||
_result.append(infile)
|
||||
input_files = _result
|
||||
"""
|
||||
|
||||
class CompilableSourcesTargetBase(TargetBase):
|
||||
"""
|
||||
An abstract base class for targets that compile their source files.
|
||||
|
||||
We explicitly transform compilable files into object files,
|
||||
even though SCons could infer that for us, because we want
|
||||
to control where the object file ends up. (The implicit rules
|
||||
in SCons always put the object file next to the source file.)
|
||||
"""
|
||||
intermediate_builder_name = None
|
||||
def write_target(self, fp, src_dir='', pre=''):
|
||||
if self.intermediate_builder_name is None:
|
||||
raise NotImplementedError
|
||||
if src_dir and not src_dir.endswith('/'):
|
||||
src_dir += '/'
|
||||
variables = {
|
||||
'src_dir': src_dir,
|
||||
'name': self.intermediate_builder_name,
|
||||
}
|
||||
fp.write(compilable_sources_template % variables)
|
||||
super(CompilableSourcesTargetBase, self).write_target(fp)
|
||||
|
||||
|
||||
class ProgramTarget(CompilableSourcesTargetBase):
|
||||
"""
|
||||
A GYP target type of 'executable'.
|
||||
"""
|
||||
builder_name = 'GypProgram'
|
||||
intermediate_builder_name = 'StaticObject'
|
||||
target_prefix = '${PROGPREFIX}'
|
||||
target_suffix = '${PROGSUFFIX}'
|
||||
out_dir = '${TOP_BUILDDIR}'
|
||||
|
||||
|
||||
class StaticLibraryTarget(CompilableSourcesTargetBase):
|
||||
"""
|
||||
A GYP target type of 'static_library'.
|
||||
"""
|
||||
builder_name = 'GypStaticLibrary'
|
||||
intermediate_builder_name = 'StaticObject'
|
||||
target_prefix = '${LIBPREFIX}'
|
||||
target_suffix = '${LIBSUFFIX}'
|
||||
out_dir = '${LIB_DIR}'
|
||||
|
||||
|
||||
class SharedLibraryTarget(CompilableSourcesTargetBase):
|
||||
"""
|
||||
A GYP target type of 'shared_library'.
|
||||
"""
|
||||
builder_name = 'GypSharedLibrary'
|
||||
intermediate_builder_name = 'SharedObject'
|
||||
target_prefix = '${SHLIBPREFIX}'
|
||||
target_suffix = '${SHLIBSUFFIX}'
|
||||
out_dir = '${LIB_DIR}'
|
||||
|
||||
|
||||
class LoadableModuleTarget(CompilableSourcesTargetBase):
|
||||
"""
|
||||
A GYP target type of 'loadable_module'.
|
||||
"""
|
||||
builder_name = 'GypLoadableModule'
|
||||
intermediate_builder_name = 'SharedObject'
|
||||
target_prefix = '${SHLIBPREFIX}'
|
||||
target_suffix = '${SHLIBSUFFIX}'
|
||||
out_dir = '${TOP_BUILDDIR}'
|
||||
|
||||
|
||||
TargetMap = {
|
||||
None : NoneTarget,
|
||||
'none' : NoneTarget,
|
||||
'settings' : SettingsTarget,
|
||||
'executable' : ProgramTarget,
|
||||
'static_library' : StaticLibraryTarget,
|
||||
'shared_library' : SharedLibraryTarget,
|
||||
'loadable_module' : LoadableModuleTarget,
|
||||
}
|
||||
|
||||
|
||||
def Target(spec):
|
||||
return TargetMap[spec.get('type')](spec)
|
517
tools/gyp/pylib/gyp/__init__.py
Executable file
517
tools/gyp/pylib/gyp/__init__.py
Executable file
@ -0,0 +1,517 @@
|
||||
#!/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 copy
|
||||
import gyp.input
|
||||
import optparse
|
||||
import os.path
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
# Default debug modes for GYP
|
||||
debug = {}
|
||||
|
||||
# List of "official" debug modes, but you can use anything you like.
|
||||
DEBUG_GENERAL = 'general'
|
||||
DEBUG_VARIABLES = 'variables'
|
||||
DEBUG_INCLUDES = 'includes'
|
||||
|
||||
|
||||
def DebugOutput(mode, message):
|
||||
if 'all' in gyp.debug.keys() or mode in gyp.debug.keys():
|
||||
ctx = ('unknown', 0, 'unknown')
|
||||
try:
|
||||
f = traceback.extract_stack(limit=2)
|
||||
if f:
|
||||
ctx = f[0][:3]
|
||||
except:
|
||||
pass
|
||||
print '%s:%s:%d:%s %s' % (mode.upper(), os.path.basename(ctx[0]),
|
||||
ctx[1], ctx[2], message)
|
||||
|
||||
def FindBuildFiles():
|
||||
extension = '.gyp'
|
||||
files = os.listdir(os.getcwd())
|
||||
build_files = []
|
||||
for file in files:
|
||||
if file.endswith(extension):
|
||||
build_files.append(file)
|
||||
return build_files
|
||||
|
||||
|
||||
class GypError(Exception):
|
||||
"""Error class representing an error, which is to be presented
|
||||
to the user. The main entry point will catch and display this.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def Load(build_files, format, default_variables={},
|
||||
includes=[], depth='.', params=None, check=False, circular_check=True):
|
||||
"""
|
||||
Loads one or more specified build files.
|
||||
default_variables and includes will be copied before use.
|
||||
Returns the generator for the specified format and the
|
||||
data returned by loading the specified build files.
|
||||
"""
|
||||
if params is None:
|
||||
params = {}
|
||||
|
||||
flavor = None
|
||||
if '-' in format:
|
||||
format, params['flavor'] = format.split('-', 1)
|
||||
|
||||
default_variables = copy.copy(default_variables)
|
||||
|
||||
# Default variables provided by this program and its modules should be
|
||||
# named WITH_CAPITAL_LETTERS to provide a distinct "best practice" namespace,
|
||||
# avoiding collisions with user and automatic variables.
|
||||
default_variables['GENERATOR'] = format
|
||||
|
||||
# Format can be a custom python file, or by default the name of a module
|
||||
# within gyp.generator.
|
||||
if format.endswith('.py'):
|
||||
generator_name = os.path.splitext(format)[0]
|
||||
path, generator_name = os.path.split(generator_name)
|
||||
|
||||
# Make sure the path to the custom generator is in sys.path
|
||||
# Don't worry about removing it once we are done. Keeping the path
|
||||
# to each generator that is used in sys.path is likely harmless and
|
||||
# arguably a good idea.
|
||||
path = os.path.abspath(path)
|
||||
if path not in sys.path:
|
||||
sys.path.insert(0, path)
|
||||
else:
|
||||
generator_name = 'gyp.generator.' + format
|
||||
|
||||
# These parameters are passed in order (as opposed to by key)
|
||||
# because ActivePython cannot handle key parameters to __import__.
|
||||
generator = __import__(generator_name, globals(), locals(), generator_name)
|
||||
for (key, val) in generator.generator_default_variables.items():
|
||||
default_variables.setdefault(key, val)
|
||||
|
||||
# Give the generator the opportunity to set additional variables based on
|
||||
# the params it will receive in the output phase.
|
||||
if getattr(generator, 'CalculateVariables', None):
|
||||
generator.CalculateVariables(default_variables, params)
|
||||
|
||||
# Give the generator the opportunity to set generator_input_info based on
|
||||
# the params it will receive in the output phase.
|
||||
if getattr(generator, 'CalculateGeneratorInputInfo', None):
|
||||
generator.CalculateGeneratorInputInfo(params)
|
||||
|
||||
# Fetch the generator specific info that gets fed to input, we use getattr
|
||||
# so we can default things and the generators only have to provide what
|
||||
# they need.
|
||||
generator_input_info = {
|
||||
'generator_wants_absolute_build_file_paths':
|
||||
getattr(generator, 'generator_wants_absolute_build_file_paths', False),
|
||||
'generator_handles_variants':
|
||||
getattr(generator, 'generator_handles_variants', False),
|
||||
'non_configuration_keys':
|
||||
getattr(generator, 'generator_additional_non_configuration_keys', []),
|
||||
'path_sections':
|
||||
getattr(generator, 'generator_additional_path_sections', []),
|
||||
'extra_sources_for_rules':
|
||||
getattr(generator, 'generator_extra_sources_for_rules', []),
|
||||
'generator_supports_multiple_toolsets':
|
||||
getattr(generator, 'generator_supports_multiple_toolsets', False),
|
||||
'generator_wants_static_library_dependencies_adjusted':
|
||||
getattr(generator,
|
||||
'generator_wants_static_library_dependencies_adjusted', True),
|
||||
'generator_wants_sorted_dependencies':
|
||||
getattr(generator, 'generator_wants_sorted_dependencies', False),
|
||||
}
|
||||
|
||||
# Process the input specific to this generator.
|
||||
result = gyp.input.Load(build_files, default_variables, includes[:],
|
||||
depth, generator_input_info, check, circular_check)
|
||||
return [generator] + result
|
||||
|
||||
def NameValueListToDict(name_value_list):
|
||||
"""
|
||||
Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
|
||||
of the pairs. If a string is simply NAME, then the value in the dictionary
|
||||
is set to True. If VALUE can be converted to an integer, it is.
|
||||
"""
|
||||
result = { }
|
||||
for item in name_value_list:
|
||||
tokens = item.split('=', 1)
|
||||
if len(tokens) == 2:
|
||||
# If we can make it an int, use that, otherwise, use the string.
|
||||
try:
|
||||
token_value = int(tokens[1])
|
||||
except ValueError:
|
||||
token_value = tokens[1]
|
||||
# Set the variable to the supplied value.
|
||||
result[tokens[0]] = token_value
|
||||
else:
|
||||
# No value supplied, treat it as a boolean and set it.
|
||||
result[tokens[0]] = True
|
||||
return result
|
||||
|
||||
def ShlexEnv(env_name):
|
||||
flags = os.environ.get(env_name, [])
|
||||
if flags:
|
||||
flags = shlex.split(flags)
|
||||
return flags
|
||||
|
||||
def FormatOpt(opt, value):
|
||||
if opt.startswith('--'):
|
||||
return '%s=%s' % (opt, value)
|
||||
return opt + value
|
||||
|
||||
def RegenerateAppendFlag(flag, values, predicate, env_name, options):
|
||||
"""Regenerate a list of command line flags, for an option of action='append'.
|
||||
|
||||
The |env_name|, if given, is checked in the environment and used to generate
|
||||
an initial list of options, then the options that were specified on the
|
||||
command line (given in |values|) are appended. This matches the handling of
|
||||
environment variables and command line flags where command line flags override
|
||||
the environment, while not requiring the environment to be set when the flags
|
||||
are used again.
|
||||
"""
|
||||
flags = []
|
||||
if options.use_environment and env_name:
|
||||
for flag_value in ShlexEnv(env_name):
|
||||
value = FormatOpt(flag, predicate(flag_value))
|
||||
if value in flags:
|
||||
flags.remove(value)
|
||||
flags.append(value)
|
||||
if values:
|
||||
for flag_value in values:
|
||||
flags.append(FormatOpt(flag, predicate(flag_value)))
|
||||
return flags
|
||||
|
||||
def RegenerateFlags(options):
|
||||
"""Given a parsed options object, and taking the environment variables into
|
||||
account, returns a list of flags that should regenerate an equivalent options
|
||||
object (even in the absence of the environment variables.)
|
||||
|
||||
Any path options will be normalized relative to depth.
|
||||
|
||||
The format flag is not included, as it is assumed the calling generator will
|
||||
set that as appropriate.
|
||||
"""
|
||||
def FixPath(path):
|
||||
path = gyp.common.FixIfRelativePath(path, options.depth)
|
||||
if not path:
|
||||
return os.path.curdir
|
||||
return path
|
||||
|
||||
def Noop(value):
|
||||
return value
|
||||
|
||||
# We always want to ignore the environment when regenerating, to avoid
|
||||
# duplicate or changed flags in the environment at the time of regeneration.
|
||||
flags = ['--ignore-environment']
|
||||
for name, metadata in options._regeneration_metadata.iteritems():
|
||||
opt = metadata['opt']
|
||||
value = getattr(options, name)
|
||||
value_predicate = metadata['type'] == 'path' and FixPath or Noop
|
||||
action = metadata['action']
|
||||
env_name = metadata['env_name']
|
||||
if action == 'append':
|
||||
flags.extend(RegenerateAppendFlag(opt, value, value_predicate,
|
||||
env_name, options))
|
||||
elif action in ('store', None): # None is a synonym for 'store'.
|
||||
if value:
|
||||
flags.append(FormatOpt(opt, value_predicate(value)))
|
||||
elif options.use_environment and env_name and os.environ.get(env_name):
|
||||
flags.append(FormatOpt(opt, value_predicate(os.environ.get(env_name))))
|
||||
elif action in ('store_true', 'store_false'):
|
||||
if ((action == 'store_true' and value) or
|
||||
(action == 'store_false' and not value)):
|
||||
flags.append(opt)
|
||||
elif options.use_environment and env_name:
|
||||
print >>sys.stderr, ('Warning: environment regeneration unimplemented '
|
||||
'for %s flag %r env_name %r' % (action, opt,
|
||||
env_name))
|
||||
else:
|
||||
print >>sys.stderr, ('Warning: regeneration unimplemented for action %r '
|
||||
'flag %r' % (action, opt))
|
||||
|
||||
return flags
|
||||
|
||||
class RegeneratableOptionParser(optparse.OptionParser):
|
||||
def __init__(self):
|
||||
self.__regeneratable_options = {}
|
||||
optparse.OptionParser.__init__(self)
|
||||
|
||||
def add_option(self, *args, **kw):
|
||||
"""Add an option to the parser.
|
||||
|
||||
This accepts the same arguments as OptionParser.add_option, plus the
|
||||
following:
|
||||
regenerate: can be set to False to prevent this option from being included
|
||||
in regeneration.
|
||||
env_name: name of environment variable that additional values for this
|
||||
option come from.
|
||||
type: adds type='path', to tell the regenerator that the values of
|
||||
this option need to be made relative to options.depth
|
||||
"""
|
||||
env_name = kw.pop('env_name', None)
|
||||
if 'dest' in kw and kw.pop('regenerate', True):
|
||||
dest = kw['dest']
|
||||
|
||||
# The path type is needed for regenerating, for optparse we can just treat
|
||||
# it as a string.
|
||||
type = kw.get('type')
|
||||
if type == 'path':
|
||||
kw['type'] = 'string'
|
||||
|
||||
self.__regeneratable_options[dest] = {
|
||||
'action': kw.get('action'),
|
||||
'type': type,
|
||||
'env_name': env_name,
|
||||
'opt': args[0],
|
||||
}
|
||||
|
||||
optparse.OptionParser.add_option(self, *args, **kw)
|
||||
|
||||
def parse_args(self, *args):
|
||||
values, args = optparse.OptionParser.parse_args(self, *args)
|
||||
values._regeneration_metadata = self.__regeneratable_options
|
||||
return values, args
|
||||
|
||||
def gyp_main(args):
|
||||
my_name = os.path.basename(sys.argv[0])
|
||||
|
||||
parser = RegeneratableOptionParser()
|
||||
usage = 'usage: %s [options ...] [build_file ...]'
|
||||
parser.set_usage(usage.replace('%s', '%prog'))
|
||||
parser.add_option('-D', dest='defines', action='append', metavar='VAR=VAL',
|
||||
env_name='GYP_DEFINES',
|
||||
help='sets variable VAR to value VAL')
|
||||
parser.add_option('-f', '--format', dest='formats', action='append',
|
||||
env_name='GYP_GENERATORS', regenerate=False,
|
||||
help='output formats to generate')
|
||||
parser.add_option('--msvs-version', dest='msvs_version',
|
||||
regenerate=False,
|
||||
help='Deprecated; use -G msvs_version=MSVS_VERSION instead')
|
||||
parser.add_option('-I', '--include', dest='includes', action='append',
|
||||
metavar='INCLUDE', type='path',
|
||||
help='files to include in all loaded .gyp files')
|
||||
parser.add_option('--depth', dest='depth', metavar='PATH', type='path',
|
||||
help='set DEPTH gyp variable to a relative path to PATH')
|
||||
parser.add_option('-d', '--debug', dest='debug', metavar='DEBUGMODE',
|
||||
action='append', default=[], help='turn on a debugging '
|
||||
'mode for debugging GYP. Supported modes are "variables", '
|
||||
'"includes" and "general" or "all" for all of them.')
|
||||
parser.add_option('-S', '--suffix', dest='suffix', default='',
|
||||
help='suffix to add to generated files')
|
||||
parser.add_option('-G', dest='generator_flags', action='append', default=[],
|
||||
metavar='FLAG=VAL', env_name='GYP_GENERATOR_FLAGS',
|
||||
help='sets generator flag FLAG to VAL')
|
||||
parser.add_option('--generator-output', dest='generator_output',
|
||||
action='store', default=None, metavar='DIR', type='path',
|
||||
env_name='GYP_GENERATOR_OUTPUT',
|
||||
help='puts generated build files under DIR')
|
||||
parser.add_option('--ignore-environment', dest='use_environment',
|
||||
action='store_false', default=True, regenerate=False,
|
||||
help='do not read options from environment variables')
|
||||
parser.add_option('--check', dest='check', action='store_true',
|
||||
help='check format of gyp files')
|
||||
parser.add_option('--toplevel-dir', dest='toplevel_dir', action='store',
|
||||
default=None, metavar='DIR', type='path',
|
||||
help='directory to use as the root of the source tree')
|
||||
# --no-circular-check disables the check for circular relationships between
|
||||
# .gyp files. These relationships should not exist, but they've only been
|
||||
# observed to be harmful with the Xcode generator. Chromium's .gyp files
|
||||
# currently have some circular relationships on non-Mac platforms, so this
|
||||
# option allows the strict behavior to be used on Macs and the lenient
|
||||
# behavior to be used elsewhere.
|
||||
# TODO(mark): Remove this option when http://crbug.com/35878 is fixed.
|
||||
parser.add_option('--no-circular-check', dest='circular_check',
|
||||
action='store_false', default=True, regenerate=False,
|
||||
help="don't check for circular relationships between files")
|
||||
|
||||
# We read a few things from ~/.gyp, so set up a var for that.
|
||||
home_vars = ['HOME']
|
||||
if sys.platform in ('cygwin', 'win32'):
|
||||
home_vars.append('USERPROFILE')
|
||||
home = None
|
||||
home_dot_gyp = None
|
||||
for home_var in home_vars:
|
||||
home = os.getenv(home_var)
|
||||
if home != None:
|
||||
home_dot_gyp = os.path.join(home, '.gyp')
|
||||
if not os.path.exists(home_dot_gyp):
|
||||
home_dot_gyp = None
|
||||
else:
|
||||
break
|
||||
|
||||
# TODO(thomasvl): add support for ~/.gyp/defaults
|
||||
|
||||
options, build_files_arg = parser.parse_args(args)
|
||||
build_files = build_files_arg
|
||||
|
||||
if not options.formats:
|
||||
# If no format was given on the command line, then check the env variable.
|
||||
generate_formats = []
|
||||
if options.use_environment:
|
||||
generate_formats = os.environ.get('GYP_GENERATORS', [])
|
||||
if generate_formats:
|
||||
generate_formats = re.split('[\s,]', generate_formats)
|
||||
if generate_formats:
|
||||
options.formats = generate_formats
|
||||
else:
|
||||
# Nothing in the variable, default based on platform.
|
||||
if sys.platform == 'darwin':
|
||||
options.formats = ['xcode']
|
||||
elif sys.platform in ('win32', 'cygwin'):
|
||||
options.formats = ['msvs']
|
||||
else:
|
||||
options.formats = ['make']
|
||||
|
||||
if not options.generator_output and options.use_environment:
|
||||
g_o = os.environ.get('GYP_GENERATOR_OUTPUT')
|
||||
if g_o:
|
||||
options.generator_output = g_o
|
||||
|
||||
for mode in options.debug:
|
||||
gyp.debug[mode] = 1
|
||||
|
||||
# Do an extra check to avoid work when we're not debugging.
|
||||
if DEBUG_GENERAL in gyp.debug.keys():
|
||||
DebugOutput(DEBUG_GENERAL, 'running with these options:')
|
||||
for option, value in sorted(options.__dict__.items()):
|
||||
if option[0] == '_':
|
||||
continue
|
||||
if isinstance(value, basestring):
|
||||
DebugOutput(DEBUG_GENERAL, " %s: '%s'" % (option, value))
|
||||
else:
|
||||
DebugOutput(DEBUG_GENERAL, " %s: %s" % (option, str(value)))
|
||||
|
||||
if not build_files:
|
||||
build_files = FindBuildFiles()
|
||||
if not build_files:
|
||||
raise GypError((usage + '\n\n%s: error: no build_file') %
|
||||
(my_name, my_name))
|
||||
|
||||
# TODO(mark): Chromium-specific hack!
|
||||
# For Chromium, the gyp "depth" variable should always be a relative path
|
||||
# to Chromium's top-level "src" directory. If no depth variable was set
|
||||
# on the command line, try to find a "src" directory by looking at the
|
||||
# absolute path to each build file's directory. The first "src" component
|
||||
# found will be treated as though it were the path used for --depth.
|
||||
if not options.depth:
|
||||
for build_file in build_files:
|
||||
build_file_dir = os.path.abspath(os.path.dirname(build_file))
|
||||
build_file_dir_components = build_file_dir.split(os.path.sep)
|
||||
components_len = len(build_file_dir_components)
|
||||
for index in xrange(components_len - 1, -1, -1):
|
||||
if build_file_dir_components[index] == 'src':
|
||||
options.depth = os.path.sep.join(build_file_dir_components)
|
||||
break
|
||||
del build_file_dir_components[index]
|
||||
|
||||
# If the inner loop found something, break without advancing to another
|
||||
# build file.
|
||||
if options.depth:
|
||||
break
|
||||
|
||||
if not options.depth:
|
||||
raise GypError('Could not automatically locate src directory. This is'
|
||||
'a temporary Chromium feature that will be removed. Use'
|
||||
'--depth as a workaround.')
|
||||
|
||||
# If toplevel-dir is not set, we assume that depth is the root of our source
|
||||
# tree.
|
||||
if not options.toplevel_dir:
|
||||
options.toplevel_dir = options.depth
|
||||
|
||||
# -D on the command line sets variable defaults - D isn't just for define,
|
||||
# it's for default. Perhaps there should be a way to force (-F?) a
|
||||
# variable's value so that it can't be overridden by anything else.
|
||||
cmdline_default_variables = {}
|
||||
defines = []
|
||||
if options.use_environment:
|
||||
defines += ShlexEnv('GYP_DEFINES')
|
||||
if options.defines:
|
||||
defines += options.defines
|
||||
cmdline_default_variables = NameValueListToDict(defines)
|
||||
if DEBUG_GENERAL in gyp.debug.keys():
|
||||
DebugOutput(DEBUG_GENERAL,
|
||||
"cmdline_default_variables: %s" % cmdline_default_variables)
|
||||
|
||||
# Set up includes.
|
||||
includes = []
|
||||
|
||||
# If ~/.gyp/include.gypi exists, it'll be forcibly included into every
|
||||
# .gyp file that's loaded, before anything else is included.
|
||||
if home_dot_gyp != None:
|
||||
default_include = os.path.join(home_dot_gyp, 'include.gypi')
|
||||
if os.path.exists(default_include):
|
||||
print 'Using overrides found in ' + default_include
|
||||
includes.append(default_include)
|
||||
|
||||
# Command-line --include files come after the default include.
|
||||
if options.includes:
|
||||
includes.extend(options.includes)
|
||||
|
||||
# Generator flags should be prefixed with the target generator since they
|
||||
# are global across all generator runs.
|
||||
gen_flags = []
|
||||
if options.use_environment:
|
||||
gen_flags += ShlexEnv('GYP_GENERATOR_FLAGS')
|
||||
if options.generator_flags:
|
||||
gen_flags += options.generator_flags
|
||||
generator_flags = NameValueListToDict(gen_flags)
|
||||
if DEBUG_GENERAL in gyp.debug.keys():
|
||||
DebugOutput(DEBUG_GENERAL, "generator_flags: %s" % generator_flags)
|
||||
|
||||
# TODO: Remove this and the option after we've gotten folks to move to the
|
||||
# generator flag.
|
||||
if options.msvs_version:
|
||||
print >>sys.stderr, \
|
||||
'DEPRECATED: Use generator flag (-G msvs_version=' + \
|
||||
options.msvs_version + ') instead of --msvs-version=' + \
|
||||
options.msvs_version
|
||||
generator_flags['msvs_version'] = options.msvs_version
|
||||
|
||||
# Generate all requested formats (use a set in case we got one format request
|
||||
# twice)
|
||||
for format in set(options.formats):
|
||||
params = {'options': options,
|
||||
'build_files': build_files,
|
||||
'generator_flags': generator_flags,
|
||||
'cwd': os.getcwd(),
|
||||
'build_files_arg': build_files_arg,
|
||||
'gyp_binary': sys.argv[0],
|
||||
'home_dot_gyp': home_dot_gyp}
|
||||
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
# a list, and not the whole data dict.
|
||||
# NOTE: flat_list is the flattened dependency graph specifying the order
|
||||
# that targets may be built. Build systems that operate serially or that
|
||||
# need to have dependencies defined before dependents reference them should
|
||||
# generate targets in the order specified in flat_list.
|
||||
generator.GenerateOutput(flat_list, targets, data, params)
|
||||
|
||||
# Done
|
||||
return 0
|
||||
|
||||
|
||||
def main(args):
|
||||
try:
|
||||
return gyp_main(args)
|
||||
except GypError, e:
|
||||
sys.stderr.write("gyp: %s\n" % e)
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
452
tools/gyp/pylib/gyp/common.py
Normal file
452
tools/gyp/pylib/gyp/common.py
Normal file
@ -0,0 +1,452 @@
|
||||
# Copyright (c) 2012 Google Inc. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import errno
|
||||
import filecmp
|
||||
import os.path
|
||||
import re
|
||||
import tempfile
|
||||
import sys
|
||||
|
||||
|
||||
# A minimal memoizing decorator. It'll blow up if the args aren't immutable,
|
||||
# among other "problems".
|
||||
class memoize(object):
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
self.cache = {}
|
||||
def __call__(self, *args):
|
||||
try:
|
||||
return self.cache[args]
|
||||
except KeyError:
|
||||
result = self.func(*args)
|
||||
self.cache[args] = result
|
||||
return result
|
||||
|
||||
|
||||
def ExceptionAppend(e, msg):
|
||||
"""Append a message to the given exception's message."""
|
||||
if not e.args:
|
||||
e.args = (msg,)
|
||||
elif len(e.args) == 1:
|
||||
e.args = (str(e.args[0]) + ' ' + msg,)
|
||||
else:
|
||||
e.args = (str(e.args[0]) + ' ' + msg,) + e.args[1:]
|
||||
|
||||
|
||||
def ParseQualifiedTarget(target):
|
||||
# Splits a qualified target into a build file, target name and toolset.
|
||||
|
||||
# NOTE: rsplit is used to disambiguate the Windows drive letter separator.
|
||||
target_split = target.rsplit(':', 1)
|
||||
if len(target_split) == 2:
|
||||
[build_file, target] = target_split
|
||||
else:
|
||||
build_file = None
|
||||
|
||||
target_split = target.rsplit('#', 1)
|
||||
if len(target_split) == 2:
|
||||
[target, toolset] = target_split
|
||||
else:
|
||||
toolset = None
|
||||
|
||||
return [build_file, target, toolset]
|
||||
|
||||
|
||||
def ResolveTarget(build_file, target, toolset):
|
||||
# This function resolves a target into a canonical form:
|
||||
# - a fully defined build file, either absolute or relative to the current
|
||||
# directory
|
||||
# - a target name
|
||||
# - a toolset
|
||||
#
|
||||
# build_file is the file relative to which 'target' is defined.
|
||||
# target is the qualified target.
|
||||
# toolset is the default toolset for that target.
|
||||
[parsed_build_file, target, parsed_toolset] = ParseQualifiedTarget(target)
|
||||
|
||||
if parsed_build_file:
|
||||
if build_file:
|
||||
# If a relative path, parsed_build_file is relative to the directory
|
||||
# containing build_file. If build_file is not in the current directory,
|
||||
# parsed_build_file is not a usable path as-is. Resolve it by
|
||||
# interpreting it as relative to build_file. If parsed_build_file is
|
||||
# absolute, it is usable as a path regardless of the current directory,
|
||||
# and os.path.join will return it as-is.
|
||||
build_file = os.path.normpath(os.path.join(os.path.dirname(build_file),
|
||||
parsed_build_file))
|
||||
# Further (to handle cases like ../cwd), make it relative to cwd)
|
||||
if not os.path.isabs(build_file):
|
||||
build_file = RelativePath(build_file, '.')
|
||||
else:
|
||||
build_file = parsed_build_file
|
||||
|
||||
if parsed_toolset:
|
||||
toolset = parsed_toolset
|
||||
|
||||
return [build_file, target, toolset]
|
||||
|
||||
|
||||
def BuildFile(fully_qualified_target):
|
||||
# Extracts the build file from the fully qualified target.
|
||||
return ParseQualifiedTarget(fully_qualified_target)[0]
|
||||
|
||||
|
||||
def QualifiedTarget(build_file, target, toolset):
|
||||
# "Qualified" means the file that a target was defined in and the target
|
||||
# name, separated by a colon, suffixed by a # and the toolset name:
|
||||
# /path/to/file.gyp:target_name#toolset
|
||||
fully_qualified = build_file + ':' + target
|
||||
if toolset:
|
||||
fully_qualified = fully_qualified + '#' + toolset
|
||||
return fully_qualified
|
||||
|
||||
|
||||
@memoize
|
||||
def RelativePath(path, relative_to):
|
||||
# Assuming both |path| and |relative_to| are relative to the current
|
||||
# directory, returns a relative path that identifies path relative to
|
||||
# relative_to.
|
||||
|
||||
# Convert to absolute (and therefore normalized paths).
|
||||
path = os.path.abspath(path)
|
||||
relative_to = os.path.abspath(relative_to)
|
||||
|
||||
# Split the paths into components.
|
||||
path_split = path.split(os.path.sep)
|
||||
relative_to_split = relative_to.split(os.path.sep)
|
||||
|
||||
# Determine how much of the prefix the two paths share.
|
||||
prefix_len = len(os.path.commonprefix([path_split, relative_to_split]))
|
||||
|
||||
# Put enough ".." components to back up out of relative_to to the common
|
||||
# prefix, and then append the part of path_split after the common prefix.
|
||||
relative_split = [os.path.pardir] * (len(relative_to_split) - prefix_len) + \
|
||||
path_split[prefix_len:]
|
||||
|
||||
if len(relative_split) == 0:
|
||||
# The paths were the same.
|
||||
return ''
|
||||
|
||||
# Turn it back into a string and we're done.
|
||||
return os.path.join(*relative_split)
|
||||
|
||||
|
||||
def FixIfRelativePath(path, relative_to):
|
||||
# Like RelativePath but returns |path| unchanged if it is absolute.
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
return RelativePath(path, relative_to)
|
||||
|
||||
|
||||
def UnrelativePath(path, relative_to):
|
||||
# Assuming that |relative_to| is relative to the current directory, and |path|
|
||||
# is a path relative to the dirname of |relative_to|, returns a path that
|
||||
# identifies |path| relative to the current directory.
|
||||
rel_dir = os.path.dirname(relative_to)
|
||||
return os.path.normpath(os.path.join(rel_dir, path))
|
||||
|
||||
|
||||
# re objects used by EncodePOSIXShellArgument. See IEEE 1003.1 XCU.2.2 at
|
||||
# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_02
|
||||
# and the documentation for various shells.
|
||||
|
||||
# _quote is a pattern that should match any argument that needs to be quoted
|
||||
# with double-quotes by EncodePOSIXShellArgument. It matches the following
|
||||
# characters appearing anywhere in an argument:
|
||||
# \t, \n, space parameter separators
|
||||
# # comments
|
||||
# $ expansions (quoted to always expand within one argument)
|
||||
# % called out by IEEE 1003.1 XCU.2.2
|
||||
# & job control
|
||||
# ' quoting
|
||||
# (, ) subshell execution
|
||||
# *, ?, [ pathname expansion
|
||||
# ; command delimiter
|
||||
# <, >, | redirection
|
||||
# = assignment
|
||||
# {, } brace expansion (bash)
|
||||
# ~ tilde expansion
|
||||
# It also matches the empty string, because "" (or '') is the only way to
|
||||
# represent an empty string literal argument to a POSIX shell.
|
||||
#
|
||||
# This does not match the characters in _escape, because those need to be
|
||||
# backslash-escaped regardless of whether they appear in a double-quoted
|
||||
# string.
|
||||
_quote = re.compile('[\t\n #$%&\'()*;<=>?[{|}~]|^$')
|
||||
|
||||
# _escape is a pattern that should match any character that needs to be
|
||||
# escaped with a backslash, whether or not the argument matched the _quote
|
||||
# pattern. _escape is used with re.sub to backslash anything in _escape's
|
||||
# first match group, hence the (parentheses) in the regular expression.
|
||||
#
|
||||
# _escape matches the following characters appearing anywhere in an argument:
|
||||
# " to prevent POSIX shells from interpreting this character for quoting
|
||||
# \ to prevent POSIX shells from interpreting this character for escaping
|
||||
# ` to prevent POSIX shells from interpreting this character for command
|
||||
# substitution
|
||||
# Missing from this list is $, because the desired behavior of
|
||||
# EncodePOSIXShellArgument is to permit parameter (variable) expansion.
|
||||
#
|
||||
# Also missing from this list is !, which bash will interpret as the history
|
||||
# expansion character when history is enabled. bash does not enable history
|
||||
# by default in non-interactive shells, so this is not thought to be a problem.
|
||||
# ! was omitted from this list because bash interprets "\!" as a literal string
|
||||
# including the backslash character (avoiding history expansion but retaining
|
||||
# the backslash), which would not be correct for argument encoding. Handling
|
||||
# this case properly would also be problematic because bash allows the history
|
||||
# character to be changed with the histchars shell variable. Fortunately,
|
||||
# as history is not enabled in non-interactive shells and
|
||||
# EncodePOSIXShellArgument is only expected to encode for non-interactive
|
||||
# shells, there is no room for error here by ignoring !.
|
||||
_escape = re.compile(r'(["\\`])')
|
||||
|
||||
def EncodePOSIXShellArgument(argument):
|
||||
"""Encodes |argument| suitably for consumption by POSIX shells.
|
||||
|
||||
argument may be quoted and escaped as necessary to ensure that POSIX shells
|
||||
treat the returned value as a literal representing the argument passed to
|
||||
this function. Parameter (variable) expansions beginning with $ are allowed
|
||||
to remain intact without escaping the $, to allow the argument to contain
|
||||
references to variables to be expanded by the shell.
|
||||
"""
|
||||
|
||||
if not isinstance(argument, str):
|
||||
argument = str(argument)
|
||||
|
||||
if _quote.search(argument):
|
||||
quote = '"'
|
||||
else:
|
||||
quote = ''
|
||||
|
||||
encoded = quote + re.sub(_escape, r'\\\1', argument) + quote
|
||||
|
||||
return encoded
|
||||
|
||||
|
||||
def EncodePOSIXShellList(list):
|
||||
"""Encodes |list| suitably for consumption by POSIX shells.
|
||||
|
||||
Returns EncodePOSIXShellArgument for each item in list, and joins them
|
||||
together using the space character as an argument separator.
|
||||
"""
|
||||
|
||||
encoded_arguments = []
|
||||
for argument in list:
|
||||
encoded_arguments.append(EncodePOSIXShellArgument(argument))
|
||||
return ' '.join(encoded_arguments)
|
||||
|
||||
|
||||
def DeepDependencyTargets(target_dicts, roots):
|
||||
"""Returns the recursive list of target dependencies."""
|
||||
dependencies = set()
|
||||
pending = set(roots)
|
||||
while pending:
|
||||
# Pluck out one.
|
||||
r = pending.pop()
|
||||
# Skip if visited already.
|
||||
if r in dependencies:
|
||||
continue
|
||||
# Add it.
|
||||
dependencies.add(r)
|
||||
# Add its children.
|
||||
spec = target_dicts[r]
|
||||
pending.update(set(spec.get('dependencies', [])))
|
||||
pending.update(set(spec.get('dependencies_original', [])))
|
||||
return list(dependencies - set(roots))
|
||||
|
||||
|
||||
def BuildFileTargets(target_list, build_file):
|
||||
"""From a target_list, returns the subset from the specified build_file.
|
||||
"""
|
||||
return [p for p in target_list if BuildFile(p) == build_file]
|
||||
|
||||
|
||||
def AllTargets(target_list, target_dicts, build_file):
|
||||
"""Returns all targets (direct and dependencies) for the specified build_file.
|
||||
"""
|
||||
bftargets = BuildFileTargets(target_list, build_file)
|
||||
deptargets = DeepDependencyTargets(target_dicts, bftargets)
|
||||
return bftargets + deptargets
|
||||
|
||||
|
||||
def WriteOnDiff(filename):
|
||||
"""Write to a file only if the new contents differ.
|
||||
|
||||
Arguments:
|
||||
filename: name of the file to potentially write to.
|
||||
Returns:
|
||||
A file like object which will write to temporary file and only overwrite
|
||||
the target if it differs (on close).
|
||||
"""
|
||||
|
||||
class Writer:
|
||||
"""Wrapper around file which only covers the target if it differs."""
|
||||
def __init__(self):
|
||||
# Pick temporary file.
|
||||
tmp_fd, self.tmp_path = tempfile.mkstemp(
|
||||
suffix='.tmp',
|
||||
prefix=os.path.split(filename)[1] + '.gyp.',
|
||||
dir=os.path.split(filename)[0])
|
||||
try:
|
||||
self.tmp_file = os.fdopen(tmp_fd, 'wb')
|
||||
except Exception:
|
||||
# Don't leave turds behind.
|
||||
os.unlink(self.tmp_path)
|
||||
raise
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
# Delegate everything else to self.tmp_file
|
||||
return getattr(self.tmp_file, attrname)
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
# Close tmp file.
|
||||
self.tmp_file.close()
|
||||
# Determine if different.
|
||||
same = False
|
||||
try:
|
||||
same = filecmp.cmp(self.tmp_path, filename, False)
|
||||
except OSError, e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
if same:
|
||||
# The new file is identical to the old one, just get rid of the new
|
||||
# one.
|
||||
os.unlink(self.tmp_path)
|
||||
else:
|
||||
# The new file is different from the old one, or there is no old one.
|
||||
# Rename the new file to the permanent name.
|
||||
#
|
||||
# tempfile.mkstemp uses an overly restrictive mode, resulting in a
|
||||
# file that can only be read by the owner, regardless of the umask.
|
||||
# There's no reason to not respect the umask here, which means that
|
||||
# an extra hoop is required to fetch it and reset the new file's mode.
|
||||
#
|
||||
# No way to get the umask without setting a new one? Set a safe one
|
||||
# and then set it back to the old value.
|
||||
umask = os.umask(077)
|
||||
os.umask(umask)
|
||||
os.chmod(self.tmp_path, 0666 & ~umask)
|
||||
if sys.platform == 'win32' and os.path.exists(filename):
|
||||
# NOTE: on windows (but not cygwin) rename will not replace an
|
||||
# existing file, so it must be preceded with a remove. Sadly there
|
||||
# is no way to make the switch atomic.
|
||||
os.remove(filename)
|
||||
os.rename(self.tmp_path, filename)
|
||||
except Exception:
|
||||
# Don't leave turds behind.
|
||||
os.unlink(self.tmp_path)
|
||||
raise
|
||||
|
||||
return Writer()
|
||||
|
||||
|
||||
def GetFlavor(params):
|
||||
"""Returns |params.flavor| if it's set, the system's default flavor else."""
|
||||
flavors = {
|
||||
'cygwin': 'win',
|
||||
'win32': 'win',
|
||||
'darwin': 'mac',
|
||||
'sunos5': 'solaris',
|
||||
'freebsd7': 'freebsd',
|
||||
'freebsd8': 'freebsd',
|
||||
'freebsd9': 'freebsd',
|
||||
}
|
||||
flavor = flavors.get(sys.platform, 'linux')
|
||||
return params.get('flavor', flavor)
|
||||
|
||||
|
||||
def CopyTool(flavor, out_path):
|
||||
"""Finds (mac|sun|win)_tool.gyp in the gyp directory and copies it
|
||||
to |out_path|."""
|
||||
prefix = { 'solaris': 'sun', 'mac': 'mac', 'win': 'win' }.get(flavor, None)
|
||||
if not prefix:
|
||||
return
|
||||
|
||||
# Slurp input file.
|
||||
source_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), '%s_tool.py' % prefix)
|
||||
with open(source_path) as source_file:
|
||||
source = source_file.readlines()
|
||||
|
||||
# Add header and write it out.
|
||||
tool_path = os.path.join(out_path, 'gyp-%s-tool' % prefix)
|
||||
with open(tool_path, 'w') as tool_file:
|
||||
tool_file.write(
|
||||
''.join([source[0], '# Generated by gyp. Do not edit.\n'] + source[1:]))
|
||||
|
||||
# Make file executable.
|
||||
os.chmod(tool_path, 0755)
|
||||
|
||||
|
||||
# From Alex Martelli,
|
||||
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
|
||||
# ASPN: Python Cookbook: Remove duplicates from a sequence
|
||||
# First comment, dated 2001/10/13.
|
||||
# (Also in the printed Python Cookbook.)
|
||||
|
||||
def uniquer(seq, idfun=None):
|
||||
if idfun is None:
|
||||
idfun = lambda x: x
|
||||
seen = {}
|
||||
result = []
|
||||
for item in seq:
|
||||
marker = idfun(item)
|
||||
if marker in seen: continue
|
||||
seen[marker] = 1
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
|
||||
class CycleError(Exception):
|
||||
"""An exception raised when an unexpected cycle is detected."""
|
||||
def __init__(self, nodes):
|
||||
self.nodes = nodes
|
||||
def __str__(self):
|
||||
return 'CycleError: cycle involving: ' + str(self.nodes)
|
||||
|
||||
|
||||
def TopologicallySorted(graph, get_edges):
|
||||
"""Topologically sort based on a user provided edge definition.
|
||||
|
||||
Args:
|
||||
graph: A list of node names.
|
||||
get_edges: A function mapping from node name to a hashable collection
|
||||
of node names which this node has outgoing edges to.
|
||||
Returns:
|
||||
A list containing all of the node in graph in topological order.
|
||||
It is assumed that calling get_edges once for each node and caching is
|
||||
cheaper than repeatedly calling get_edges.
|
||||
Raises:
|
||||
CycleError in the event of a cycle.
|
||||
Example:
|
||||
graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'}
|
||||
def GetEdges(node):
|
||||
return re.findall(r'\$\(([^))]\)', graph[node])
|
||||
print TopologicallySorted(graph.keys(), GetEdges)
|
||||
==>
|
||||
['a', 'c', b']
|
||||
"""
|
||||
get_edges = memoize(get_edges)
|
||||
visited = set()
|
||||
visiting = set()
|
||||
ordered_nodes = []
|
||||
def Visit(node):
|
||||
if node in visiting:
|
||||
raise CycleError(visiting)
|
||||
if node in visited:
|
||||
return
|
||||
visited.add(node)
|
||||
visiting.add(node)
|
||||
for neighbor in get_edges(node):
|
||||
Visit(neighbor)
|
||||
visiting.remove(node)
|
||||
ordered_nodes.insert(0, node)
|
||||
for node in sorted(graph):
|
||||
Visit(node)
|
||||
return ordered_nodes
|
44
tools/gyp/pylib/gyp/common_test.py
Executable file
44
tools/gyp/pylib/gyp/common_test.py
Executable file
@ -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.
|
||||
|
||||
"""Unit tests for the common.py file."""
|
||||
|
||||
import gyp.common
|
||||
import unittest
|
||||
|
||||
|
||||
class TestTopologicallySorted(unittest.TestCase):
|
||||
def test_Valid(self):
|
||||
"""Test that sorting works on a valid graph with one possible order."""
|
||||
graph = {
|
||||
'a': ['b', 'c'],
|
||||
'b': [],
|
||||
'c': ['d'],
|
||||
'd': ['b'],
|
||||
}
|
||||
def GetEdge(node):
|
||||
return tuple(graph[node])
|
||||
self.assertEqual(
|
||||
gyp.common.TopologicallySorted(graph.keys(), GetEdge),
|
||||
['a', 'c', 'd', 'b'])
|
||||
|
||||
def test_Cycle(self):
|
||||
"""Test that an exception is thrown on a cyclic graph."""
|
||||
graph = {
|
||||
'a': ['b'],
|
||||
'b': ['c'],
|
||||
'c': ['d'],
|
||||
'd': ['a'],
|
||||
}
|
||||
def GetEdge(node):
|
||||
return tuple(graph[node])
|
||||
self.assertRaises(
|
||||
gyp.common.CycleError, gyp.common.TopologicallySorted,
|
||||
graph.keys(), GetEdge)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
148
tools/gyp/pylib/gyp/easy_xml.py
Normal file
148
tools/gyp/pylib/gyp/easy_xml.py
Normal file
@ -0,0 +1,148 @@
|
||||
# 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 re
|
||||
|
||||
|
||||
def XmlToString(content, encoding='utf-8', pretty=False):
|
||||
""" Writes the XML content to disk, touching the file only if it has changed.
|
||||
|
||||
Visual Studio files have a lot of pre-defined structures. This function makes
|
||||
it easy to represent these structures as Python data structures, instead of
|
||||
having to create a lot of function calls.
|
||||
|
||||
Each XML element of the content is represented as a list composed of:
|
||||
1. The name of the element, a string,
|
||||
2. The attributes of the element, a dictionary (optional), and
|
||||
3+. The content of the element, if any. Strings are simple text nodes and
|
||||
lists are child elements.
|
||||
|
||||
Example 1:
|
||||
<test/>
|
||||
becomes
|
||||
['test']
|
||||
|
||||
Example 2:
|
||||
<myelement a='value1' b='value2'>
|
||||
<childtype>This is</childtype>
|
||||
<childtype>it!</childtype>
|
||||
</myelement>
|
||||
|
||||
becomes
|
||||
['myelement', {'a':'value1', 'b':'value2'},
|
||||
['childtype', 'This is'],
|
||||
['childtype', 'it!'],
|
||||
]
|
||||
|
||||
Args:
|
||||
content: The structured content to be converted.
|
||||
encoding: The encoding to report on the first XML line.
|
||||
pretty: True if we want pretty printing with indents and new lines.
|
||||
|
||||
Returns:
|
||||
The XML content as a string.
|
||||
"""
|
||||
# We create a huge list of all the elements of the file.
|
||||
xml_parts = ['<?xml version="1.0" encoding="%s"?>' % encoding]
|
||||
if pretty:
|
||||
xml_parts.append('\n')
|
||||
_ConstructContentList(xml_parts, content, pretty)
|
||||
|
||||
# Convert it to a string
|
||||
return ''.join(xml_parts)
|
||||
|
||||
|
||||
def _ConstructContentList(xml_parts, specification, pretty, level=0):
|
||||
""" Appends the XML parts corresponding to the specification.
|
||||
|
||||
Args:
|
||||
xml_parts: A list of XML parts to be appended to.
|
||||
specification: The specification of the element. See EasyXml docs.
|
||||
pretty: True if we want pretty printing with indents and new lines.
|
||||
level: Indentation level.
|
||||
"""
|
||||
# The first item in a specification is the name of the element.
|
||||
if pretty:
|
||||
indentation = ' ' * level
|
||||
new_line = '\n'
|
||||
else:
|
||||
indentation = ''
|
||||
new_line = ''
|
||||
name = specification[0]
|
||||
if not isinstance(name, str):
|
||||
raise Exception('The first item of an EasyXml specification should be '
|
||||
'a string. Specification was ' + str(specification))
|
||||
xml_parts.append(indentation + '<' + name)
|
||||
|
||||
# Optionally in second position is a dictionary of the attributes.
|
||||
rest = specification[1:]
|
||||
if rest and isinstance(rest[0], dict):
|
||||
for at, val in sorted(rest[0].iteritems()):
|
||||
xml_parts.append(' %s="%s"' % (at, _XmlEscape(val)))
|
||||
rest = rest[1:]
|
||||
if rest:
|
||||
xml_parts.append('>')
|
||||
all_strings = reduce(lambda x, y: x and isinstance(y, str), rest, True)
|
||||
multi_line = not all_strings
|
||||
if multi_line and new_line:
|
||||
xml_parts.append(new_line)
|
||||
for child_spec in rest:
|
||||
# If it's a string, append a text node.
|
||||
# Otherwise recurse over that child definition
|
||||
if isinstance(child_spec, str):
|
||||
xml_parts.append(_XmlEscape(child_spec))
|
||||
else:
|
||||
_ConstructContentList(xml_parts, child_spec, pretty, level + 1)
|
||||
if multi_line and indentation:
|
||||
xml_parts.append(indentation)
|
||||
xml_parts.append('</%s>%s' % (name, new_line))
|
||||
else:
|
||||
xml_parts.append('/>%s' % new_line)
|
||||
|
||||
|
||||
def WriteXmlIfChanged(content, path, encoding='utf-8', pretty=False):
|
||||
""" Writes the XML content to disk, touching the file only if it has changed.
|
||||
|
||||
Args:
|
||||
content: The structured content to be written.
|
||||
path: Location of the file.
|
||||
encoding: The encoding to report on the first line of the XML file.
|
||||
pretty: True if we want pretty printing with indents and new lines.
|
||||
"""
|
||||
xml_string = XmlToString(content, encoding, pretty)
|
||||
|
||||
# Get the old content
|
||||
try:
|
||||
f = open(path, 'r')
|
||||
existing = f.read()
|
||||
f.close()
|
||||
except:
|
||||
existing = None
|
||||
|
||||
# It has changed, write it
|
||||
if existing != xml_string:
|
||||
f = open(path, 'w')
|
||||
f.write(xml_string)
|
||||
f.close()
|
||||
|
||||
|
||||
_xml_escape_map = {
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'&': '&',
|
||||
'\n': '
',
|
||||
'\r': '
',
|
||||
}
|
||||
|
||||
|
||||
_xml_escape_re = re.compile(
|
||||
"(%s)" % "|".join(map(re.escape, _xml_escape_map.keys())))
|
||||
|
||||
|
||||
def _XmlEscape(value):
|
||||
""" Escape a string for inclusion in XML."""
|
||||
replace = lambda m: _xml_escape_map[m.string[m.start() : m.end()]]
|
||||
return _xml_escape_re.sub(replace, value)
|
102
tools/gyp/pylib/gyp/easy_xml_test.py
Executable file
102
tools/gyp/pylib/gyp/easy_xml_test.py
Executable file
@ -0,0 +1,102 @@
|
||||
#!/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.
|
||||
|
||||
""" Unit tests for the easy_xml.py file. """
|
||||
|
||||
import gyp.easy_xml as easy_xml
|
||||
import unittest
|
||||
import StringIO
|
||||
|
||||
|
||||
class TestSequenceFunctions(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.stderr = StringIO.StringIO()
|
||||
|
||||
def test_EasyXml_simple(self):
|
||||
self.assertEqual(
|
||||
easy_xml.XmlToString(['test']),
|
||||
'<?xml version="1.0" encoding="utf-8"?><test/>')
|
||||
|
||||
self.assertEqual(
|
||||
easy_xml.XmlToString(['test'], encoding='Windows-1252'),
|
||||
'<?xml version="1.0" encoding="Windows-1252"?><test/>')
|
||||
|
||||
def test_EasyXml_simple_with_attributes(self):
|
||||
self.assertEqual(
|
||||
easy_xml.XmlToString(['test2', {'a': 'value1', 'b': 'value2'}]),
|
||||
'<?xml version="1.0" encoding="utf-8"?><test2 a="value1" b="value2"/>')
|
||||
|
||||
def test_EasyXml_escaping(self):
|
||||
original = '<test>\'"\r&\nfoo'
|
||||
converted = '<test>'"
&
foo'
|
||||
self.assertEqual(
|
||||
easy_xml.XmlToString(['test3', {'a': original}, original]),
|
||||
'<?xml version="1.0" encoding="utf-8"?><test3 a="%s">%s</test3>' %
|
||||
(converted, converted))
|
||||
|
||||
def test_EasyXml_pretty(self):
|
||||
self.assertEqual(
|
||||
easy_xml.XmlToString(
|
||||
['test3',
|
||||
['GrandParent',
|
||||
['Parent1',
|
||||
['Child']
|
||||
],
|
||||
['Parent2']
|
||||
]
|
||||
],
|
||||
pretty=True),
|
||||
'<?xml version="1.0" encoding="utf-8"?>\n'
|
||||
'<test3>\n'
|
||||
' <GrandParent>\n'
|
||||
' <Parent1>\n'
|
||||
' <Child/>\n'
|
||||
' </Parent1>\n'
|
||||
' <Parent2/>\n'
|
||||
' </GrandParent>\n'
|
||||
'</test3>\n')
|
||||
|
||||
|
||||
def test_EasyXml_complex(self):
|
||||
# We want to create:
|
||||
target = (
|
||||
'<?xml version="1.0" encoding="utf-8"?>'
|
||||
'<Project>'
|
||||
'<PropertyGroup Label="Globals">'
|
||||
'<ProjectGuid>{D2250C20-3A94-4FB9-AF73-11BC5B73884B}</ProjectGuid>'
|
||||
'<Keyword>Win32Proj</Keyword>'
|
||||
'<RootNamespace>automated_ui_tests</RootNamespace>'
|
||||
'</PropertyGroup>'
|
||||
'<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props"/>'
|
||||
'<PropertyGroup '
|
||||
'Condition="'$(Configuration)|$(Platform)'=='
|
||||
''Debug|Win32'" Label="Configuration">'
|
||||
'<ConfigurationType>Application</ConfigurationType>'
|
||||
'<CharacterSet>Unicode</CharacterSet>'
|
||||
'</PropertyGroup>'
|
||||
'</Project>')
|
||||
|
||||
xml = easy_xml.XmlToString(
|
||||
['Project',
|
||||
['PropertyGroup', {'Label': 'Globals'},
|
||||
['ProjectGuid', '{D2250C20-3A94-4FB9-AF73-11BC5B73884B}'],
|
||||
['Keyword', 'Win32Proj'],
|
||||
['RootNamespace', 'automated_ui_tests']
|
||||
],
|
||||
['Import', {'Project': '$(VCTargetsPath)\\Microsoft.Cpp.props'}],
|
||||
['PropertyGroup',
|
||||
{'Condition': "'$(Configuration)|$(Platform)'=='Debug|Win32'",
|
||||
'Label': 'Configuration'},
|
||||
['ConfigurationType', 'Application'],
|
||||
['CharacterSet', 'Unicode']
|
||||
]
|
||||
])
|
||||
self.assertEqual(xml, target)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
0
tools/gyp/pylib/gyp/generator/__init__.py
Normal file
0
tools/gyp/pylib/gyp/generator/__init__.py
Normal file
1077
tools/gyp/pylib/gyp/generator/android.py
Normal file
1077
tools/gyp/pylib/gyp/generator/android.py
Normal file
File diff suppressed because it is too large
Load Diff
66
tools/gyp/pylib/gyp/generator/dump_dependency_json.py
Normal file
66
tools/gyp/pylib/gyp/generator/dump_dependency_json.py
Normal file
@ -0,0 +1,66 @@
|
||||
# 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 collections
|
||||
import gyp
|
||||
import gyp.common
|
||||
import json
|
||||
import sys
|
||||
|
||||
generator_supports_multiple_toolsets = True
|
||||
|
||||
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']:
|
||||
# Some gyp steps fail if these are empty(!).
|
||||
generator_default_variables[dirname] = 'dir'
|
||||
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']:
|
||||
generator_default_variables[unused] = ''
|
||||
|
||||
|
||||
def CalculateVariables(default_variables, params):
|
||||
generator_flags = params.get('generator_flags', {})
|
||||
for key, val in generator_flags.items():
|
||||
default_variables.setdefault(key, val)
|
||||
default_variables.setdefault('OS', gyp.common.GetFlavor(params))
|
||||
|
||||
|
||||
def CalculateGeneratorInputInfo(params):
|
||||
"""Calculate the generator specific info that gets fed to input (called by
|
||||
gyp)."""
|
||||
generator_flags = params.get('generator_flags', {})
|
||||
if generator_flags.get('adjust_static_libraries', False):
|
||||
global generator_wants_static_library_dependencies_adjusted
|
||||
generator_wants_static_library_dependencies_adjusted = True
|
||||
|
||||
|
||||
def GenerateOutput(target_list, target_dicts, data, params):
|
||||
# Map of target -> list of targets it depends on.
|
||||
edges = {}
|
||||
|
||||
# Queue of targets to visit.
|
||||
targets_to_visit = target_list[:]
|
||||
|
||||
while len(targets_to_visit) > 0:
|
||||
target = targets_to_visit.pop()
|
||||
if target in edges:
|
||||
continue
|
||||
edges[target] = []
|
||||
|
||||
for dep in target_dicts[target].get('dependencies', []):
|
||||
edges[target].append(dep)
|
||||
targets_to_visit.append(dep)
|
||||
|
||||
filename = 'dump.json'
|
||||
f = open(filename, 'w')
|
||||
json.dump(edges, f)
|
||||
f.close()
|
||||
print 'Wrote json to %s.' % filename
|
271
tools/gyp/pylib/gyp/generator/eclipse.py
Normal file
271
tools/gyp/pylib/gyp/generator/eclipse.py
Normal file
@ -0,0 +1,271 @@
|
||||
# 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.
|
||||
|
||||
"""GYP backend that generates Eclipse CDT settings files.
|
||||
|
||||
This backend DOES NOT generate Eclipse CDT projects. Instead, it generates XML
|
||||
files that can be imported into an Eclipse CDT project. The XML file contains a
|
||||
list of include paths and symbols (i.e. defines).
|
||||
|
||||
Because a full .cproject definition is not created by this generator, it's not
|
||||
possible to properly define the include dirs and symbols for each file
|
||||
individually. Instead, one set of includes/symbols is generated for the entire
|
||||
project. This works fairly well (and is a vast improvement in general), but may
|
||||
still result in a few indexer issues here and there.
|
||||
|
||||
This generator has no automated tests, so expect it to be broken.
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import subprocess
|
||||
import gyp
|
||||
import gyp.common
|
||||
import shlex
|
||||
|
||||
generator_wants_static_library_dependencies_adjusted = False
|
||||
|
||||
generator_default_variables = {
|
||||
}
|
||||
|
||||
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'
|
||||
|
||||
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']:
|
||||
generator_default_variables[unused] = ''
|
||||
|
||||
# Include dirs will occasionaly use the SHARED_INTERMEDIATE_DIR variable as
|
||||
# part of the path when dealing with generated headers. This value will be
|
||||
# replaced dynamically for each configuration.
|
||||
generator_default_variables['SHARED_INTERMEDIATE_DIR'] = \
|
||||
'$SHARED_INTERMEDIATES_DIR'
|
||||
|
||||
|
||||
def CalculateVariables(default_variables, params):
|
||||
generator_flags = params.get('generator_flags', {})
|
||||
for key, val in generator_flags.items():
|
||||
default_variables.setdefault(key, val)
|
||||
default_variables.setdefault('OS', gyp.common.GetFlavor(params))
|
||||
|
||||
|
||||
def CalculateGeneratorInputInfo(params):
|
||||
"""Calculate the generator specific info that gets fed to input (called by
|
||||
gyp)."""
|
||||
generator_flags = params.get('generator_flags', {})
|
||||
if generator_flags.get('adjust_static_libraries', False):
|
||||
global generator_wants_static_library_dependencies_adjusted
|
||||
generator_wants_static_library_dependencies_adjusted = True
|
||||
|
||||
|
||||
def GetAllIncludeDirectories(target_list, target_dicts,
|
||||
shared_intermediates_dir, config_name):
|
||||
"""Calculate the set of include directories to be used.
|
||||
|
||||
Returns:
|
||||
A list including all the include_dir's specified for every target followed
|
||||
by any include directories that were added as cflag compiler options.
|
||||
"""
|
||||
|
||||
gyp_includes_set = set()
|
||||
compiler_includes_list = []
|
||||
|
||||
for target_name in target_list:
|
||||
target = target_dicts[target_name]
|
||||
if config_name in target['configurations']:
|
||||
config = target['configurations'][config_name]
|
||||
|
||||
# Look for any include dirs that were explicitly added via cflags. This
|
||||
# may be done in gyp files to force certain includes to come at the end.
|
||||
# TODO(jgreenwald): Change the gyp files to not abuse cflags for this, and
|
||||
# remove this.
|
||||
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)
|
||||
|
||||
# Find standard gyp include dirs.
|
||||
if config.has_key('include_dirs'):
|
||||
include_dirs = config['include_dirs']
|
||||
for include_dir in include_dirs:
|
||||
include_dir = include_dir.replace('$SHARED_INTERMEDIATES_DIR',
|
||||
shared_intermediates_dir)
|
||||
if not os.path.isabs(include_dir):
|
||||
base_dir = os.path.dirname(target_name)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# Generate a list that has all the include dirs.
|
||||
all_includes_list = list(gyp_includes_set)
|
||||
all_includes_list.sort()
|
||||
for compiler_include in compiler_includes_list:
|
||||
if not compiler_include in gyp_includes_set:
|
||||
all_includes_list.append(compiler_include)
|
||||
|
||||
# All done.
|
||||
return all_includes_list
|
||||
|
||||
|
||||
def GetCompilerPath(target_list, target_dicts, data):
|
||||
"""Determine a command that can be used to invoke the compiler.
|
||||
|
||||
Returns:
|
||||
If this is a gyp project that has explicit make settings, try to determine
|
||||
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
|
||||
|
||||
# Check to see if the compiler was specified as an environment variable.
|
||||
for key in ['CC_target', 'CC', 'CXX']:
|
||||
compiler = os.environ.get(key)
|
||||
if compiler:
|
||||
return compiler
|
||||
|
||||
return 'gcc'
|
||||
|
||||
|
||||
def GetAllDefines(target_list, target_dicts, data, config_name):
|
||||
"""Calculate the defines for a project.
|
||||
|
||||
Returns:
|
||||
A dict that includes explict defines declared in gyp files along with all of
|
||||
the default defines that the compiler uses.
|
||||
"""
|
||||
|
||||
# Get defines declared in the gyp files.
|
||||
all_defines = {}
|
||||
for target_name in target_list:
|
||||
target = target_dicts[target_name]
|
||||
|
||||
if config_name in target['configurations']:
|
||||
config = target['configurations'][config_name]
|
||||
for define in config['defines']:
|
||||
split_define = define.split('=', 1)
|
||||
if len(split_define) == 1:
|
||||
split_define.append('1')
|
||||
if split_define[0].strip() in all_defines:
|
||||
# Already defined
|
||||
continue
|
||||
|
||||
all_defines[split_define[0].strip()] = split_define[1].strip()
|
||||
|
||||
# Get default compiler defines (if possible).
|
||||
cc_target = GetCompilerPath(target_list, target_dicts, data)
|
||||
if cc_target:
|
||||
command = shlex.split(cc_target)
|
||||
command.extend(['-E', '-dM', '-'])
|
||||
cpp_proc = subprocess.Popen(args=command, cwd='.',
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
cpp_output = cpp_proc.communicate()[0]
|
||||
cpp_lines = cpp_output.split('\n')
|
||||
for cpp_line in cpp_lines:
|
||||
if not cpp_line.strip():
|
||||
continue
|
||||
cpp_line_parts = cpp_line.split(' ', 2)
|
||||
key = cpp_line_parts[1]
|
||||
if len(cpp_line_parts) >= 3:
|
||||
val = cpp_line_parts[2]
|
||||
else:
|
||||
val = '1'
|
||||
all_defines[key] = val
|
||||
|
||||
return all_defines
|
||||
|
||||
|
||||
def WriteIncludePaths(out, eclipse_langs, include_dirs):
|
||||
"""Write the includes section of a CDT settings export file."""
|
||||
|
||||
out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \
|
||||
'settingswizards.IncludePaths">\n')
|
||||
out.write(' <language name="holder for library settings"></language>\n')
|
||||
for lang in eclipse_langs:
|
||||
out.write(' <language name="%s">\n' % lang)
|
||||
for include_dir in include_dirs:
|
||||
out.write(' <includepath workspace_path="false">%s</includepath>\n' %
|
||||
include_dir)
|
||||
out.write(' </language>\n')
|
||||
out.write(' </section>\n')
|
||||
|
||||
|
||||
def WriteMacros(out, eclipse_langs, defines):
|
||||
"""Write the macros section of a CDT settings export file."""
|
||||
|
||||
out.write(' <section name="org.eclipse.cdt.internal.ui.wizards.' \
|
||||
'settingswizards.Macros">\n')
|
||||
out.write(' <language name="holder for library settings"></language>\n')
|
||||
for lang in eclipse_langs:
|
||||
out.write(' <language name="%s">\n' % lang)
|
||||
for key in sorted(defines.iterkeys()):
|
||||
out.write(' <macro><name>%s</name><value>%s</value></macro>\n' %
|
||||
(key, defines[key]))
|
||||
out.write(' </language>\n')
|
||||
out.write(' </section>\n')
|
||||
|
||||
|
||||
def GenerateOutputForConfig(target_list, target_dicts, data, params,
|
||||
config_name):
|
||||
options = params['options']
|
||||
generator_flags = params.get('generator_flags', {})
|
||||
|
||||
# build_dir: relative path from source root to our output files.
|
||||
# e.g. "out/Debug"
|
||||
build_dir = os.path.join(generator_flags.get('output_dir', 'out'),
|
||||
config_name)
|
||||
|
||||
toplevel_build = os.path.join(options.toplevel_dir, build_dir)
|
||||
shared_intermediate_dir = os.path.join(toplevel_build, 'obj', 'gen')
|
||||
|
||||
if not os.path.exists(toplevel_build):
|
||||
os.makedirs(toplevel_build)
|
||||
out = open(os.path.join(toplevel_build, 'eclipse-cdt-settings.xml'), 'w')
|
||||
|
||||
out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||
out.write('<cdtprojectproperties>\n')
|
||||
|
||||
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_dir, config_name)
|
||||
WriteIncludePaths(out, eclipse_langs, include_dirs)
|
||||
defines = GetAllDefines(target_list, target_dicts, data, config_name)
|
||||
WriteMacros(out, eclipse_langs, defines)
|
||||
|
||||
out.write('</cdtprojectproperties>\n')
|
||||
out.close()
|
||||
|
||||
|
||||
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"
|
||||
|
||||
user_config = params.get('generator_flags', {}).get('config', None)
|
||||
if user_config:
|
||||
GenerateOutputForConfig(target_list, target_dicts, data, params,
|
||||
user_config)
|
||||
else:
|
||||
config_names = target_dicts[target_list[0]]['configurations'].keys()
|
||||
for config_name in config_names:
|
||||
GenerateOutputForConfig(target_list, target_dicts, data, params,
|
||||
config_name)
|
||||
|
87
tools/gyp/pylib/gyp/generator/gypd.py
Normal file
87
tools/gyp/pylib/gyp/generator/gypd.py
Normal file
@ -0,0 +1,87 @@
|
||||
# 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.
|
||||
|
||||
"""gypd output module
|
||||
|
||||
This module produces gyp input as its output. Output files are given the
|
||||
.gypd extension to avoid overwriting the .gyp files that they are generated
|
||||
from. Internal references to .gyp files (such as those found in
|
||||
"dependencies" sections) are not adjusted to point to .gypd files instead;
|
||||
unlike other paths, which are relative to the .gyp or .gypd file, such paths
|
||||
are relative to the directory from which gyp was run to create the .gypd file.
|
||||
|
||||
This generator module is intended to be a sample and a debugging aid, hence
|
||||
the "d" for "debug" in .gypd. It is useful to inspect the results of the
|
||||
various merges, expansions, and conditional evaluations performed by gyp
|
||||
and to see a representation of what would be fed to a generator module.
|
||||
|
||||
It's not advisable to rename .gypd files produced by this module to .gyp,
|
||||
because they will have all merges, expansions, and evaluations already
|
||||
performed and the relevant constructs not present in the output; paths to
|
||||
dependencies may be wrong; and various sections that do not belong in .gyp
|
||||
files such as such as "included_files" and "*_excluded" will be present.
|
||||
Output will also be stripped of comments. This is not intended to be a
|
||||
general-purpose gyp pretty-printer; for that, you probably just want to
|
||||
run "pprint.pprint(eval(open('source.gyp').read()))", which will still strip
|
||||
comments but won't do all of the other things done to this module's output.
|
||||
|
||||
The specific formatting of the output generated by this module is subject
|
||||
to change.
|
||||
"""
|
||||
|
||||
|
||||
import gyp.common
|
||||
import errno
|
||||
import os
|
||||
import pprint
|
||||
|
||||
|
||||
# These variables should just be spit back out as variable references.
|
||||
_generator_identity_variables = [
|
||||
'EXECUTABLE_PREFIX',
|
||||
'EXECUTABLE_SUFFIX',
|
||||
'INTERMEDIATE_DIR',
|
||||
'PRODUCT_DIR',
|
||||
'RULE_INPUT_ROOT',
|
||||
'RULE_INPUT_DIRNAME',
|
||||
'RULE_INPUT_EXT',
|
||||
'RULE_INPUT_NAME',
|
||||
'RULE_INPUT_PATH',
|
||||
'SHARED_INTERMEDIATE_DIR',
|
||||
]
|
||||
|
||||
# gypd doesn't define a default value for OS like many other generator
|
||||
# modules. Specify "-D OS=whatever" on the command line to provide a value.
|
||||
generator_default_variables = {
|
||||
}
|
||||
|
||||
# gypd supports multiple toolsets
|
||||
generator_supports_multiple_toolsets = True
|
||||
|
||||
# TODO(mark): This always uses <, which isn't right. The input module should
|
||||
# notify the generator to tell it which phase it is operating in, and this
|
||||
# module should use < for the early phase and then switch to > for the late
|
||||
# phase. Bonus points for carrying @ back into the output too.
|
||||
for v in _generator_identity_variables:
|
||||
generator_default_variables[v] = '<(%s)' % v
|
||||
|
||||
|
||||
def GenerateOutput(target_list, target_dicts, data, params):
|
||||
output_files = {}
|
||||
for qualified_target in target_list:
|
||||
[input_file, target] = \
|
||||
gyp.common.ParseQualifiedTarget(qualified_target)[0:2]
|
||||
|
||||
if input_file[-4:] != '.gyp':
|
||||
continue
|
||||
input_file_stem = input_file[:-4]
|
||||
output_file = input_file_stem + params['options'].suffix + '.gypd'
|
||||
|
||||
if not output_file in output_files:
|
||||
output_files[output_file] = input_file
|
||||
|
||||
for output_file, input_file in output_files.iteritems():
|
||||
output = open(output_file, 'w')
|
||||
pprint.pprint(data[input_file], output)
|
||||
output.close()
|
56
tools/gyp/pylib/gyp/generator/gypsh.py
Normal file
56
tools/gyp/pylib/gyp/generator/gypsh.py
Normal file
@ -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.
|
||||
|
||||
"""gypsh output module
|
||||
|
||||
gypsh is a GYP shell. It's not really a generator per se. All it does is
|
||||
fire up an interactive Python session with a few local variables set to the
|
||||
variables passed to the generator. Like gypd, it's intended as a debugging
|
||||
aid, to facilitate the exploration of .gyp structures after being processed
|
||||
by the input module.
|
||||
|
||||
The expected usage is "gyp -f gypsh -D OS=desired_os".
|
||||
"""
|
||||
|
||||
|
||||
import code
|
||||
import sys
|
||||
|
||||
|
||||
# All of this stuff about generator variables was lovingly ripped from gypd.py.
|
||||
# That module has a much better description of what's going on and why.
|
||||
_generator_identity_variables = [
|
||||
'EXECUTABLE_PREFIX',
|
||||
'EXECUTABLE_SUFFIX',
|
||||
'INTERMEDIATE_DIR',
|
||||
'PRODUCT_DIR',
|
||||
'RULE_INPUT_ROOT',
|
||||
'RULE_INPUT_DIRNAME',
|
||||
'RULE_INPUT_EXT',
|
||||
'RULE_INPUT_NAME',
|
||||
'RULE_INPUT_PATH',
|
||||
'SHARED_INTERMEDIATE_DIR',
|
||||
]
|
||||
|
||||
generator_default_variables = {
|
||||
}
|
||||
|
||||
for v in _generator_identity_variables:
|
||||
generator_default_variables[v] = '<(%s)' % v
|
||||
|
||||
|
||||
def GenerateOutput(target_list, target_dicts, data, params):
|
||||
locals = {
|
||||
'target_list': target_list,
|
||||
'target_dicts': target_dicts,
|
||||
'data': data,
|
||||
}
|
||||
|
||||
# Use a banner that looks like the stock Python one and like what
|
||||
# code.interact uses by default, but tack on something to indicate what
|
||||
# locals are available, and identify gypsh.
|
||||
banner='Python %s on %s\nlocals.keys() = %s\ngypsh' % \
|
||||
(sys.version, sys.platform, repr(sorted(locals.keys())))
|
||||
|
||||
code.interact(banner, local=locals)
|
2133
tools/gyp/pylib/gyp/generator/make.py
Normal file
2133
tools/gyp/pylib/gyp/generator/make.py
Normal file
File diff suppressed because it is too large
Load Diff
3126
tools/gyp/pylib/gyp/generator/msvs.py
Normal file
3126
tools/gyp/pylib/gyp/generator/msvs.py
Normal file
File diff suppressed because it is too large
Load Diff
35
tools/gyp/pylib/gyp/generator/msvs_test.py
Executable file
35
tools/gyp/pylib/gyp/generator/msvs_test.py
Executable file
@ -0,0 +1,35 @@
|
||||
#!/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.
|
||||
|
||||
""" Unit tests for the msvs.py file. """
|
||||
|
||||
import gyp.generator.msvs as msvs
|
||||
import unittest
|
||||
import StringIO
|
||||
|
||||
|
||||
class TestSequenceFunctions(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.stderr = StringIO.StringIO()
|
||||
|
||||
def test_GetLibraries(self):
|
||||
self.assertEqual(
|
||||
msvs._GetLibraries({}),
|
||||
[])
|
||||
self.assertEqual(
|
||||
msvs._GetLibraries({'libraries': []}),
|
||||
[])
|
||||
self.assertEqual(
|
||||
msvs._GetLibraries({'other':'foo', 'libraries': ['a.lib']}),
|
||||
['a.lib'])
|
||||
self.assertEqual(
|
||||
msvs._GetLibraries({'libraries': ['a.lib', 'b.lib', 'c.lib', '-lb.lib',
|
||||
'-lb.lib', 'd.lib', 'a.lib']}),
|
||||
['c.lib', 'b.lib', 'd.lib', 'a.lib'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
1699
tools/gyp/pylib/gyp/generator/ninja.py
Normal file
1699
tools/gyp/pylib/gyp/generator/ninja.py
Normal file
File diff suppressed because it is too large
Load Diff
44
tools/gyp/pylib/gyp/generator/ninja_test.py
Normal file
44
tools/gyp/pylib/gyp/generator/ninja_test.py
Normal file
@ -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.
|
||||
|
||||
""" Unit tests for the ninja.py file. """
|
||||
|
||||
import gyp.generator.ninja as ninja
|
||||
import unittest
|
||||
import StringIO
|
||||
import sys
|
||||
import TestCommon
|
||||
|
||||
|
||||
class TestPrefixesAndSuffixes(unittest.TestCase):
|
||||
if sys.platform in ('win32', 'cygwin'):
|
||||
def test_BinaryNamesWindows(self):
|
||||
writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'ninja.build', 'win')
|
||||
spec = { 'target_name': 'wee' }
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'executable').
|
||||
endswith('.exe'))
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
|
||||
endswith('.dll'))
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
|
||||
endswith('.lib'))
|
||||
|
||||
if sys.platform == 'linux2':
|
||||
def test_BinaryNamesLinux(self):
|
||||
writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'ninja.build', 'linux')
|
||||
spec = { 'target_name': 'wee' }
|
||||
self.assertTrue('.' not in writer.ComputeOutputFileName(spec,
|
||||
'executable'))
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
|
||||
startswith('lib'))
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
|
||||
startswith('lib'))
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library').
|
||||
endswith('.so'))
|
||||
self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library').
|
||||
endswith('.a'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
1047
tools/gyp/pylib/gyp/generator/scons.py
Normal file
1047
tools/gyp/pylib/gyp/generator/scons.py
Normal file
File diff suppressed because it is too large
Load Diff
1209
tools/gyp/pylib/gyp/generator/xcode.py
Normal file
1209
tools/gyp/pylib/gyp/generator/xcode.py
Normal file
File diff suppressed because it is too large
Load Diff
2485
tools/gyp/pylib/gyp/input.py
Normal file
2485
tools/gyp/pylib/gyp/input.py
Normal file
File diff suppressed because it is too large
Load Diff
209
tools/gyp/pylib/gyp/mac_tool.py
Executable file
209
tools/gyp/pylib/gyp/mac_tool.py
Executable file
@ -0,0 +1,209 @@
|
||||
#!/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.
|
||||
|
||||
"""Utility functions to perform Xcode-style build steps.
|
||||
|
||||
These functions are executed via gyp-mac-tool when using the Makefile generator.
|
||||
"""
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
import plistlib
|
||||
import re
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main(args):
|
||||
executor = MacTool()
|
||||
exit_code = executor.Dispatch(args)
|
||||
if exit_code is not None:
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
class MacTool(object):
|
||||
"""This class performs all the Mac tooling steps. The methods can either be
|
||||
executed directly, or dispatched from an argument list."""
|
||||
|
||||
def Dispatch(self, args):
|
||||
"""Dispatches a string command to a method."""
|
||||
if len(args) < 1:
|
||||
raise Exception("Not enough arguments")
|
||||
|
||||
method = "Exec%s" % self._CommandifyName(args[0])
|
||||
return getattr(self, method)(*args[1:])
|
||||
|
||||
def _CommandifyName(self, name_string):
|
||||
"""Transforms a tool name like copy-info-plist to CopyInfoPlist"""
|
||||
return name_string.title().replace('-', '')
|
||||
|
||||
def ExecCopyBundleResource(self, source, dest):
|
||||
"""Copies a resource file to the bundle/Resources directory, performing any
|
||||
necessary compilation on each resource."""
|
||||
extension = os.path.splitext(source)[1].lower()
|
||||
if os.path.isdir(source):
|
||||
# Copy tree.
|
||||
if os.path.exists(dest):
|
||||
shutil.rmtree(dest)
|
||||
shutil.copytree(source, dest)
|
||||
elif extension == '.xib':
|
||||
return self._CopyXIBFile(source, dest)
|
||||
elif extension == '.strings':
|
||||
self._CopyStringsFile(source, dest)
|
||||
else:
|
||||
shutil.copyfile(source, dest)
|
||||
|
||||
def _CopyXIBFile(self, source, dest):
|
||||
"""Compiles a XIB file with ibtool into a binary plist in the bundle."""
|
||||
tools_dir = os.environ.get('DEVELOPER_BIN_DIR', '/usr/bin')
|
||||
args = [os.path.join(tools_dir, 'ibtool'), '--errors', '--warnings',
|
||||
'--notices', '--output-format', 'human-readable-text', '--compile',
|
||||
dest, source]
|
||||
ibtool_section_re = re.compile(r'/\*.*\*/')
|
||||
ibtool_re = re.compile(r'.*note:.*is clipping its content')
|
||||
ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
current_section_header = None
|
||||
for line in ibtoolout.stdout:
|
||||
if ibtool_section_re.match(line):
|
||||
current_section_header = line
|
||||
elif not ibtool_re.match(line):
|
||||
if current_section_header:
|
||||
sys.stdout.write(current_section_header)
|
||||
current_section_header = None
|
||||
sys.stdout.write(line)
|
||||
return ibtoolout.returncode
|
||||
|
||||
def _CopyStringsFile(self, source, dest):
|
||||
"""Copies a .strings file using iconv to reconvert the input into UTF-16."""
|
||||
input_code = self._DetectInputEncoding(source) or "UTF-8"
|
||||
fp = open(dest, 'w')
|
||||
args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code',
|
||||
'UTF-16', source]
|
||||
subprocess.call(args, stdout=fp)
|
||||
fp.close()
|
||||
|
||||
def _DetectInputEncoding(self, file_name):
|
||||
"""Reads the first few bytes from file_name and tries to guess the text
|
||||
encoding. Returns None as a guess if it can't detect it."""
|
||||
fp = open(file_name, 'rb')
|
||||
try:
|
||||
header = fp.read(3)
|
||||
except e:
|
||||
fp.close()
|
||||
return None
|
||||
fp.close()
|
||||
if header.startswith("\xFE\xFF"):
|
||||
return "UTF-16BE"
|
||||
elif header.startswith("\xFF\xFE"):
|
||||
return "UTF-16LE"
|
||||
elif header.startswith("\xEF\xBB\xBF"):
|
||||
return "UTF-8"
|
||||
else:
|
||||
return None
|
||||
|
||||
def ExecCopyInfoPlist(self, source, dest):
|
||||
"""Copies the |source| Info.plist to the destination directory |dest|."""
|
||||
# Read the source Info.plist into memory.
|
||||
fd = open(source, 'r')
|
||||
lines = fd.read()
|
||||
fd.close()
|
||||
|
||||
# Go through all the environment variables and replace them as variables in
|
||||
# the file.
|
||||
for key in os.environ:
|
||||
if key.startswith('_'):
|
||||
continue
|
||||
evar = '${%s}' % key
|
||||
lines = string.replace(lines, evar, os.environ[key])
|
||||
|
||||
# Write out the file with variables replaced.
|
||||
fd = open(dest, 'w')
|
||||
fd.write(lines)
|
||||
fd.close()
|
||||
|
||||
# Now write out PkgInfo file now that the Info.plist file has been
|
||||
# "compiled".
|
||||
self._WritePkgInfo(dest)
|
||||
|
||||
def _WritePkgInfo(self, info_plist):
|
||||
"""This writes the PkgInfo file from the data stored in Info.plist."""
|
||||
plist = plistlib.readPlist(info_plist)
|
||||
if not plist:
|
||||
return
|
||||
|
||||
# Only create PkgInfo for executable types.
|
||||
package_type = plist['CFBundlePackageType']
|
||||
if package_type != 'APPL':
|
||||
return
|
||||
|
||||
# The format of PkgInfo is eight characters, representing the bundle type
|
||||
# and bundle signature, each four characters. If that is missing, four
|
||||
# '?' characters are used instead.
|
||||
signature_code = plist.get('CFBundleSignature', '????')
|
||||
if len(signature_code) != 4: # Wrong length resets everything, too.
|
||||
signature_code = '?' * 4
|
||||
|
||||
dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo')
|
||||
fp = open(dest, 'w')
|
||||
fp.write('%s%s' % (package_type, signature_code))
|
||||
fp.close()
|
||||
|
||||
def ExecFlock(self, lockfile, *cmd_list):
|
||||
"""Emulates the most basic behavior of Linux's flock(1)."""
|
||||
# Rely on exception handling to report errors.
|
||||
fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666)
|
||||
fcntl.flock(fd, fcntl.LOCK_EX)
|
||||
return subprocess.call(cmd_list)
|
||||
|
||||
def ExecFilterLibtool(self, *cmd_list):
|
||||
"""Calls libtool and filters out 'libtool: file: foo.o has no symbols'."""
|
||||
libtool_re = re.compile(r'^libtool: file: .* has no symbols$')
|
||||
libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE)
|
||||
for line in libtoolout.stderr:
|
||||
if not libtool_re.match(line):
|
||||
sys.stderr.write(line)
|
||||
return libtoolout.returncode
|
||||
|
||||
def ExecPackageFramework(self, framework, version):
|
||||
"""Takes a path to Something.framework and the Current version of that and
|
||||
sets up all the symlinks."""
|
||||
# Find the name of the binary based on the part before the ".framework".
|
||||
binary = os.path.basename(framework).split('.')[0]
|
||||
|
||||
CURRENT = 'Current'
|
||||
RESOURCES = 'Resources'
|
||||
VERSIONS = 'Versions'
|
||||
|
||||
if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)):
|
||||
# Binary-less frameworks don't seem to contain symlinks (see e.g.
|
||||
# chromium's out/Debug/org.chromium.Chromium.manifest/ bundle).
|
||||
return
|
||||
|
||||
# Move into the framework directory to set the symlinks correctly.
|
||||
pwd = os.getcwd()
|
||||
os.chdir(framework)
|
||||
|
||||
# Set up the Current version.
|
||||
self._Relink(version, os.path.join(VERSIONS, CURRENT))
|
||||
|
||||
# Set up the root symlinks.
|
||||
self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary)
|
||||
self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES)
|
||||
|
||||
# Back to where we were before!
|
||||
os.chdir(pwd)
|
||||
|
||||
def _Relink(self, dest, link):
|
||||
"""Creates a symlink to |dest| named |link|. If |link| already exists,
|
||||
it is overwritten."""
|
||||
if os.path.lexists(link):
|
||||
os.remove(link)
|
||||
os.symlink(dest, link)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
691
tools/gyp/pylib/gyp/msvs_emulation.py
Normal file
691
tools/gyp/pylib/gyp/msvs_emulation.py
Normal file
@ -0,0 +1,691 @@
|
||||
# Copyright (c) 2012 Google Inc. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""
|
||||
This module helps emulate Visual Studio 2008 behavior on top of other
|
||||
build systems, primarily ninja.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
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
|
||||
Windows programs)."""
|
||||
# See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment
|
||||
# threads. This is actually the quoting rules for CommandLineToArgvW, not
|
||||
# for the shell, because the shell doesn't do anything in Windows. This
|
||||
# works more or less because most programs (including the compiler, etc.)
|
||||
# use that function to handle command line arguments.
|
||||
|
||||
# For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
|
||||
# preceding it, and results in n backslashes + the quote. So we substitute
|
||||
# in 2* what we match, +1 more, plus the quote.
|
||||
arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg)
|
||||
|
||||
# %'s also need to be doubled otherwise they're interpreted as batch
|
||||
# positional arguments. Also make sure to escape the % so that they're
|
||||
# passed literally through escaping so they can be singled to just the
|
||||
# original %. Otherwise, trying to pass the literal representation that
|
||||
# looks like an environment variable to the shell (e.g. %PATH%) would fail.
|
||||
arg = arg.replace('%', '%%')
|
||||
|
||||
# These commands are used in rsp files, so no escaping for the shell (via ^)
|
||||
# is necessary.
|
||||
|
||||
# Finally, wrap the whole thing in quotes so that the above quote rule
|
||||
# applies and whitespace isn't a word break.
|
||||
return '"' + arg + '"'
|
||||
|
||||
|
||||
def EncodeRspFileList(args):
|
||||
"""Process a list of arguments using QuoteCmdExeArgument."""
|
||||
# Note that the first argument is assumed to be the command. Don't add
|
||||
# quotes around it because then built-ins like 'echo', etc. won't work.
|
||||
# Take care to normpath only the path in the case of 'call ../x.bat' because
|
||||
# otherwise the whole thing is incorrectly interpreted as a path and not
|
||||
# normalized correctly.
|
||||
if not args: return ''
|
||||
if args[0].startswith('call '):
|
||||
call, program = args[0].split(' ', 1)
|
||||
program = call + ' ' + os.path.normpath(program)
|
||||
else:
|
||||
program = os.path.normpath(args[0])
|
||||
return program + ' ' + ' '.join(QuoteForRspFile(arg) for arg in args[1:])
|
||||
|
||||
|
||||
def _GenericRetrieve(root, default, path):
|
||||
"""Given a list of dictionary keys |path| and a tree of dicts |root|, find
|
||||
value at path, or return |default| if any of the path doesn't exist."""
|
||||
if not root:
|
||||
return default
|
||||
if not path:
|
||||
return root
|
||||
return _GenericRetrieve(root.get(path[0]), default, path[1:])
|
||||
|
||||
|
||||
def _AddPrefix(element, prefix):
|
||||
"""Add |prefix| to |element| or each subelement if element is iterable."""
|
||||
if element is None:
|
||||
return element
|
||||
# Note, not Iterable because we don't want to handle strings like that.
|
||||
if isinstance(element, list) or isinstance(element, tuple):
|
||||
return [prefix + e for e in element]
|
||||
else:
|
||||
return prefix + element
|
||||
|
||||
|
||||
def _DoRemapping(element, map):
|
||||
"""If |element| then remap it through |map|. If |element| is iterable then
|
||||
each item will be remapped. Any elements not found will be removed."""
|
||||
if map is not None and element is not None:
|
||||
if not callable(map):
|
||||
map = map.get # Assume it's a dict, otherwise a callable to do the remap.
|
||||
if isinstance(element, list) or isinstance(element, tuple):
|
||||
element = filter(None, [map(elem) for elem in element])
|
||||
else:
|
||||
element = map(element)
|
||||
return element
|
||||
|
||||
|
||||
def _AppendOrReturn(append, element):
|
||||
"""If |append| is None, simply return |element|. If |append| is not None,
|
||||
then add |element| to it, adding each item in |element| if it's a list or
|
||||
tuple."""
|
||||
if append is not None and element is not None:
|
||||
if isinstance(element, list) or isinstance(element, tuple):
|
||||
append.extend(element)
|
||||
else:
|
||||
append.append(element)
|
||||
else:
|
||||
return element
|
||||
|
||||
|
||||
def _FindDirectXInstallation():
|
||||
"""Try to find an installation location for the DirectX SDK. Check for the
|
||||
standard environment variable, and if that doesn't exist, try to find
|
||||
via the registry. May return None if not found in either location."""
|
||||
# Return previously calculated value, if there is one
|
||||
if hasattr(_FindDirectXInstallation, 'dxsdk_dir'):
|
||||
return _FindDirectXInstallation.dxsdk_dir
|
||||
|
||||
dxsdk_dir = os.environ.get('DXSDK_DIR')
|
||||
if not dxsdk_dir:
|
||||
# Setup params to pass to and attempt to launch reg.exe.
|
||||
cmd = ['reg.exe', 'query', r'HKLM\Software\Microsoft\DirectX', '/s']
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
for line in p.communicate()[0].splitlines():
|
||||
if 'InstallPath' in line:
|
||||
dxsdk_dir = line.split(' ')[3] + "\\"
|
||||
|
||||
# Cache return value
|
||||
_FindDirectXInstallation.dxsdk_dir = dxsdk_dir
|
||||
return dxsdk_dir
|
||||
|
||||
|
||||
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
|
||||
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),
|
||||
('msvs_settings', dict),
|
||||
('msvs_system_include_dirs', list),
|
||||
('msvs_disabled_warnings', list),
|
||||
('msvs_precompiled_header', str),
|
||||
('msvs_precompiled_source', str),
|
||||
('msvs_target_platform', str),
|
||||
]
|
||||
configs = spec['configurations']
|
||||
for field, default in supported_fields:
|
||||
setattr(self, field, {})
|
||||
for configname, config in configs.iteritems():
|
||||
getattr(self, field)[configname] = config.get(field, default())
|
||||
|
||||
self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.'])
|
||||
|
||||
def GetVSMacroEnv(self, base_to_build=None, config=None):
|
||||
"""Get a dict of variables mapping internal VS macro names to their gyp
|
||||
equivalents."""
|
||||
target_platform = self.GetTargetPlatform(config)
|
||||
target_platform = {'x86': 'Win32'}.get(target_platform, target_platform)
|
||||
replacements = {
|
||||
'$(VSInstallDir)': self.vs_version.Path(),
|
||||
'$(VCInstallDir)': os.path.join(self.vs_version.Path(), 'VC') + '\\',
|
||||
'$(OutDir)\\': base_to_build + '\\' if base_to_build else '',
|
||||
'$(IntDir)': '$!INTERMEDIATE_DIR',
|
||||
'$(InputPath)': '${source}',
|
||||
'$(InputName)': '${root}',
|
||||
'$(ProjectName)': self.spec['target_name'],
|
||||
'$(PlatformName)': target_platform,
|
||||
'$(ProjectDir)\\': '',
|
||||
}
|
||||
# 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 ''
|
||||
return replacements
|
||||
|
||||
def ConvertVSMacros(self, s, base_to_build=None, config=None):
|
||||
"""Convert from VS macro names to something equivalent."""
|
||||
env = self.GetVSMacroEnv(base_to_build, config=config)
|
||||
return ExpandMacros(s, env)
|
||||
|
||||
def AdjustLibraries(self, libraries):
|
||||
"""Strip -l from library if it's specified with that."""
|
||||
return [lib[2:] if lib.startswith('-l') else lib for lib in libraries]
|
||||
|
||||
def _GetAndMunge(self, field, path, default, prefix, append, map):
|
||||
"""Retrieve a value from |field| at |path| or return |default|. If
|
||||
|append| is specified, and the item is found, it will be appended to that
|
||||
object instead of returned. If |map| is specified, results will be
|
||||
remapped through |map| before being returned or appended."""
|
||||
result = _GenericRetrieve(field, default, path)
|
||||
result = _DoRemapping(result, map)
|
||||
result = _AddPrefix(result, prefix)
|
||||
return _AppendOrReturn(append, result)
|
||||
|
||||
class _GetWrapper(object):
|
||||
def __init__(self, parent, field, base_path, append=None):
|
||||
self.parent = parent
|
||||
self.field = field
|
||||
self.base_path = [base_path]
|
||||
self.append = append
|
||||
def __call__(self, name, map=None, prefix='', default=None):
|
||||
return self.parent._GetAndMunge(self.field, self.base_path + [name],
|
||||
default=default, prefix=prefix, append=self.append, map=map)
|
||||
|
||||
def GetTargetPlatform(self, config):
|
||||
target_platform = self.msvs_target_platform.get(config, '')
|
||||
if not target_platform:
|
||||
target_platform = 'Win32'
|
||||
return {'Win32': 'x86'}.get(target_platform, target_platform)
|
||||
|
||||
def _RealConfig(self, config):
|
||||
target_platform = self.GetTargetPlatform(config)
|
||||
if target_platform == 'x64' and not config.endswith('_x64'):
|
||||
config += '_x64'
|
||||
return config
|
||||
|
||||
def _Setting(self, path, config,
|
||||
default=None, prefix='', append=None, map=None):
|
||||
"""_GetAndMunge for msvs_settings."""
|
||||
config = self._RealConfig(config)
|
||||
return self._GetAndMunge(
|
||||
self.msvs_settings[config], path, default, prefix, append, map)
|
||||
|
||||
def _ConfigAttrib(self, path, config,
|
||||
default=None, prefix='', append=None, map=None):
|
||||
"""_GetAndMunge for msvs_configuration_attributes."""
|
||||
config = self._RealConfig(config)
|
||||
return self._GetAndMunge(
|
||||
self.msvs_configuration_attributes[config],
|
||||
path, default, prefix, append, map)
|
||||
|
||||
def AdjustIncludeDirs(self, include_dirs, config):
|
||||
"""Updates include_dirs to expand VS specific paths, and adds the system
|
||||
include dirs used for platform SDK and similar."""
|
||||
config = self._RealConfig(config)
|
||||
includes = include_dirs + self.msvs_system_include_dirs[config]
|
||||
includes.extend(self._Setting(
|
||||
('VCCLCompilerTool', '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."""
|
||||
config = self._RealConfig(config)
|
||||
defines = []
|
||||
if self._ConfigAttrib(['CharacterSet'], config) == '1':
|
||||
defines.extend(('_UNICODE', 'UNICODE'))
|
||||
if self._ConfigAttrib(['CharacterSet'], config) == '2':
|
||||
defines.append('_MBCS')
|
||||
defines.extend(self._Setting(
|
||||
('VCCLCompilerTool', 'PreprocessorDefinitions'), config, default=[]))
|
||||
return defines
|
||||
|
||||
def GetOutputName(self, config, expand_special):
|
||||
"""Gets the explicitly overridden output name for a target or returns None
|
||||
if it's not overridden."""
|
||||
config = self._RealConfig(config)
|
||||
type = self.spec['type']
|
||||
root = 'VCLibrarianTool' if type == 'static_library' else 'VCLinkerTool'
|
||||
# TODO(scottmg): Handle OutputDirectory without OutputFile.
|
||||
output_file = self._Setting((root, 'OutputFile'), config)
|
||||
if output_file:
|
||||
output_file = expand_special(self.ConvertVSMacros(
|
||||
output_file, config=config))
|
||||
return output_file
|
||||
|
||||
def GetCflags(self, config):
|
||||
"""Returns the flags that need to be added to .c and .cc compilations."""
|
||||
config = self._RealConfig(config)
|
||||
cflags = []
|
||||
cflags.extend(['/wd' + w for w in self.msvs_disabled_warnings[config]])
|
||||
cl = self._GetWrapper(self, self.msvs_settings[config],
|
||||
'VCCLCompilerTool', append=cflags)
|
||||
cl('Optimization',
|
||||
map={'0': 'd', '1': '1', '2': '2', '3': 'x'}, prefix='/O')
|
||||
cl('InlineFunctionExpansion', prefix='/Ob')
|
||||
cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy')
|
||||
cl('FavorSizeOrSpeed', map={'1': 't', '2': 's'}, prefix='/O')
|
||||
cl('WholeProgramOptimization', map={'true': '/GL'})
|
||||
cl('WarningLevel', prefix='/W')
|
||||
cl('WarnAsError', map={'true': '/WX'})
|
||||
cl('DebugInformationFormat',
|
||||
map={'1': '7', '3': 'i', '4': 'I'}, prefix='/Z')
|
||||
cl('RuntimeTypeInfo', map={'true': '/GR', 'false': '/GR-'})
|
||||
cl('EnableFunctionLevelLinking', map={'true': '/Gy', 'false': '/Gy-'})
|
||||
cl('MinimalRebuild', map={'true': '/Gm'})
|
||||
cl('BufferSecurityCheck', map={'true': '/GS', 'false': '/GS-'})
|
||||
cl('BasicRuntimeChecks', map={'1': 's', '2': 'u', '3': '1'}, prefix='/RTC')
|
||||
cl('RuntimeLibrary',
|
||||
map={'0': 'T', '1': 'Td', '2': 'D', '3': 'Dd'}, prefix='/M')
|
||||
cl('ExceptionHandling', map={'1': 'sc','2': 'a'}, prefix='/EH')
|
||||
cl('AdditionalOptions', prefix='')
|
||||
# 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._RealConfig(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.
|
||||
"""
|
||||
config = self._RealConfig(config)
|
||||
# The PCH is only built once by a particular source file. Usage of PCH must
|
||||
# only be for the same language (i.e. C vs. C++), so only include the pch
|
||||
# flags when the language matches.
|
||||
if self.msvs_precompiled_header[config]:
|
||||
source_ext = os.path.splitext(self.msvs_precompiled_source[config])[1]
|
||||
if _LanguageMatchesForPch(source_ext, extension):
|
||||
pch = os.path.split(self.msvs_precompiled_header[config])[1]
|
||||
return ['/Yu' + pch, '/FI' + pch, '/Fp${pchprefix}.' + pch + '.pch']
|
||||
return []
|
||||
|
||||
def GetCflagsC(self, config):
|
||||
"""Returns the flags that need to be added to .c compilations."""
|
||||
config = self._RealConfig(config)
|
||||
return self._GetPchFlags(config, '.c')
|
||||
|
||||
def GetCflagsCC(self, config):
|
||||
"""Returns the flags that need to be added to .cc compilations."""
|
||||
config = self._RealConfig(config)
|
||||
return ['/TP'] + self._GetPchFlags(config, '.cc')
|
||||
|
||||
def _GetAdditionalLibraryDirectories(self, root, config, gyp_to_build_path):
|
||||
"""Get and normalize the list of paths in AdditionalLibraryDirectories
|
||||
setting."""
|
||||
config = self._RealConfig(config)
|
||||
libpaths = self._Setting((root, 'AdditionalLibraryDirectories'),
|
||||
config, default=[])
|
||||
libpaths = [os.path.normpath(
|
||||
gyp_to_build_path(self.ConvertVSMacros(p, config=config)))
|
||||
for p in libpaths]
|
||||
return ['/LIBPATH:"' + p + '"' for p in libpaths]
|
||||
|
||||
def GetLibFlags(self, config, gyp_to_build_path):
|
||||
"""Returns the flags that need to be added to lib commands."""
|
||||
config = self._RealConfig(config)
|
||||
libflags = []
|
||||
lib = self._GetWrapper(self, self.msvs_settings[config],
|
||||
'VCLibrarianTool', append=libflags)
|
||||
libflags.extend(self._GetAdditionalLibraryDirectories(
|
||||
'VCLibrarianTool', config, gyp_to_build_path))
|
||||
lib('AdditionalOptions')
|
||||
return libflags
|
||||
|
||||
def _GetDefFileAsLdflags(self, spec, ldflags, gyp_to_build_path):
|
||||
""".def files get implicitly converted to a ModuleDefinitionFile for the
|
||||
linker in the VS generator. Emulate that behaviour here."""
|
||||
def_file = ''
|
||||
if spec['type'] in ('shared_library', 'loadable_module', 'executable'):
|
||||
def_files = [s for s in spec.get('sources', []) if s.endswith('.def')]
|
||||
if len(def_files) == 1:
|
||||
ldflags.append('/DEF:"%s"' % gyp_to_build_path(def_files[0]))
|
||||
elif len(def_files) > 1:
|
||||
raise Exception("Multiple .def files")
|
||||
|
||||
def GetLdflags(self, config, gyp_to_build_path, expand_special,
|
||||
manifest_base_name, is_executable):
|
||||
"""Returns the flags that need to be added to link commands, and the
|
||||
manifest files."""
|
||||
config = self._RealConfig(config)
|
||||
ldflags = []
|
||||
ld = self._GetWrapper(self, self.msvs_settings[config],
|
||||
'VCLinkerTool', append=ldflags)
|
||||
self._GetDefFileAsLdflags(self.spec, ldflags, gyp_to_build_path)
|
||||
ld('GenerateDebugInformation', map={'true': '/DEBUG'})
|
||||
ld('TargetMachine', map={'1': 'X86', '17': 'X64'}, prefix='/MACHINE:')
|
||||
ldflags.extend(self._GetAdditionalLibraryDirectories(
|
||||
'VCLinkerTool', config, gyp_to_build_path))
|
||||
ld('DelayLoadDLLs', prefix='/DELAYLOAD:')
|
||||
out = self.GetOutputName(config, expand_special)
|
||||
if out:
|
||||
ldflags.append('/OUT:' + out)
|
||||
ld('AdditionalOptions', prefix='')
|
||||
ld('SubSystem', map={'1': 'CONSOLE', '2': 'WINDOWS'}, prefix='/SUBSYSTEM:')
|
||||
ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL')
|
||||
ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED')
|
||||
ld('RandomizedBaseAddress',
|
||||
map={'1': ':NO', '2': ''}, prefix='/DYNAMICBASE')
|
||||
ld('DataExecutionPrevention',
|
||||
map={'1': ':NO', '2': ''}, prefix='/NXCOMPAT')
|
||||
ld('OptimizeReferences', map={'1': 'NOREF', '2': 'REF'}, prefix='/OPT:')
|
||||
ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:')
|
||||
ld('LinkTimeCodeGeneration', map={'1': '/LTCG'})
|
||||
ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
|
||||
ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
|
||||
ld('EntryPointSymbol', prefix='/ENTRY:')
|
||||
# TODO(scottmg): This should sort of be somewhere else (not really a flag).
|
||||
ld('AdditionalDependencies', prefix='')
|
||||
# TODO(scottmg): These too.
|
||||
ldflags.extend(('kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib',
|
||||
'comdlg32.lib', 'advapi32.lib', 'shell32.lib', 'ole32.lib',
|
||||
'oleaut32.lib', 'uuid.lib', 'odbc32.lib', 'DelayImp.lib'))
|
||||
|
||||
# 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',
|
||||
ldflags)
|
||||
if not base_flags:
|
||||
ldflags.append('/DYNAMICBASE')
|
||||
|
||||
# If the NXCOMPAT flag has not been specified, default to on. Despite the
|
||||
# documentation that says this only defaults to on when the subsystem is
|
||||
# Vista or greater (which applies to the linker), the IDE defaults it on
|
||||
# unless it's explicitly off.
|
||||
if not filter(lambda x: 'NXCOMPAT' in x, ldflags):
|
||||
ldflags.append('/NXCOMPAT')
|
||||
|
||||
have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags)
|
||||
manifest_flags, intermediate_manifest_file = self._GetLdManifestFlags(
|
||||
config, manifest_base_name, is_executable and not have_def_file)
|
||||
ldflags.extend(manifest_flags)
|
||||
manifest_files = self._GetAdditionalManifestFiles(config, gyp_to_build_path)
|
||||
manifest_files.append(intermediate_manifest_file)
|
||||
|
||||
return ldflags, manifest_files
|
||||
|
||||
def _GetLdManifestFlags(self, config, name, allow_isolation):
|
||||
"""Returns the set of flags that need to be added to the link to generate
|
||||
a default manifest, as well as the name of the generated file."""
|
||||
# Add manifest flags that mirror the defaults in VS. Chromium dev builds
|
||||
# do not currently use any non-default settings, but we could parse
|
||||
# VCManifestTool blocks if Chromium or other projects need them in the
|
||||
# future. Of particular note, we do not yet support EmbedManifest because
|
||||
# it complicates incremental linking.
|
||||
output_name = name + '.intermediate.manifest'
|
||||
flags = [
|
||||
'/MANIFEST',
|
||||
'/ManifestFile:' + output_name,
|
||||
'''/MANIFESTUAC:"level='asInvoker' uiAccess='false'"'''
|
||||
]
|
||||
if allow_isolation:
|
||||
flags.append('/ALLOWISOLATION')
|
||||
return flags, output_name
|
||||
|
||||
def _GetAdditionalManifestFiles(self, config, gyp_to_build_path):
|
||||
"""Gets additional manifest files that are added to the default one
|
||||
generated by the linker."""
|
||||
files = self._Setting(('VCManifestTool', 'AdditionalManifestFiles'), config,
|
||||
default=[])
|
||||
if (self._Setting(
|
||||
('VCManifestTool', 'EmbedManifest'), config, default='') == 'true'):
|
||||
print 'gyp/msvs_emulation.py: "EmbedManifest: true" not yet supported.'
|
||||
if isinstance(files, str):
|
||||
files = files.split(';')
|
||||
return [os.path.normpath(
|
||||
gyp_to_build_path(self.ConvertVSMacros(f, config=config)))
|
||||
for f in files]
|
||||
|
||||
def IsUseLibraryDependencyInputs(self, config):
|
||||
"""Returns whether the target should be linked via Use Library Dependency
|
||||
Inputs (using component .objs of a given .lib)."""
|
||||
config = self._RealConfig(config)
|
||||
uldi = self._Setting(('VCLinkerTool', 'UseLibraryDependencyInputs'), config)
|
||||
return uldi == 'true'
|
||||
|
||||
def GetRcflags(self, config, gyp_to_ninja_path):
|
||||
"""Returns the flags that need to be added to invocations of the resource
|
||||
compiler."""
|
||||
config = self._RealConfig(config)
|
||||
rcflags = []
|
||||
rc = self._GetWrapper(self, self.msvs_settings[config],
|
||||
'VCResourceCompilerTool', append=rcflags)
|
||||
rc('AdditionalIncludeDirectories', map=gyp_to_ninja_path, prefix='/I')
|
||||
rcflags.append('/I' + gyp_to_ninja_path('.'))
|
||||
rc('PreprocessorDefinitions', prefix='/d')
|
||||
# /l arg must be in hex without leading '0x'
|
||||
rc('Culture', prefix='/l', map=lambda x: hex(int(x))[2:])
|
||||
return rcflags
|
||||
|
||||
def BuildCygwinBashCommandLine(self, args, path_to_base):
|
||||
"""Build a command line that runs args via cygwin bash. We assume that all
|
||||
incoming paths are in Windows normpath'd form, so they need to be
|
||||
converted to posix style for the part of the command line that's passed to
|
||||
bash. We also have to do some Visual Studio macro emulation here because
|
||||
various rules use magic VS names for things. Also note that rules that
|
||||
contain ninja variables cannot be fixed here (for example ${source}), so
|
||||
the outer generator needs to make sure that the paths that are written out
|
||||
are in posix style, if the command line will be used here."""
|
||||
cygwin_dir = os.path.normpath(
|
||||
os.path.join(path_to_base, self.msvs_cygwin_dirs[0]))
|
||||
cd = ('cd %s' % path_to_base).replace('\\', '/')
|
||||
args = [a.replace('\\', '/').replace('"', '\\"') for a in args]
|
||||
args = ["'%s'" % a.replace("'", "'\\''") for a in args]
|
||||
bash_cmd = ' '.join(args)
|
||||
cmd = (
|
||||
'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir +
|
||||
'bash -c "%s ; %s"' % (cd, bash_cmd))
|
||||
return cmd
|
||||
|
||||
def IsRuleRunUnderCygwin(self, rule):
|
||||
"""Determine if an action should be run under cygwin. If the variable is
|
||||
unset, or set to 1 we use cygwin."""
|
||||
return int(rule.get('msvs_cygwin_shell',
|
||||
self.spec.get('msvs_cygwin_shell', 1))) != 0
|
||||
|
||||
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."""
|
||||
for rule in spec.get('rules', []):
|
||||
if rule['extension'] == 'idl' and int(rule.get('msvs_external_rule', 0)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def GetIdlBuildData(self, source, config):
|
||||
"""Determine the implicit outputs for an idl file. Returns output
|
||||
directory, outputs, and variables and flags that are required."""
|
||||
config = self._RealConfig(config)
|
||||
midl_get = self._GetWrapper(self, self.msvs_settings[config], 'VCMIDLTool')
|
||||
def midl(name, default=None):
|
||||
return self.ConvertVSMacros(midl_get(name, default=default),
|
||||
config=config)
|
||||
tlb = midl('TypeLibraryName', default='${root}.tlb')
|
||||
header = midl('HeaderFileName', default='${root}.h')
|
||||
dlldata = midl('DLLDataFileName', default='dlldata.c')
|
||||
iid = midl('InterfaceIdentifierFileName', default='${root}_i.c')
|
||||
proxy = midl('ProxyFileName', default='${root}_p.c')
|
||||
# Note that .tlb is not included in the outputs as it is not always
|
||||
# generated depending on the content of the input idl file.
|
||||
outdir = midl('OutputDirectory', default='')
|
||||
output = [header, dlldata, iid, proxy]
|
||||
variables = [('tlb', tlb),
|
||||
('h', header),
|
||||
('dlldata', dlldata),
|
||||
('iid', iid),
|
||||
('proxy', proxy)]
|
||||
# TODO(scottmg): Are there configuration settings to set these flags?
|
||||
flags = ['/char', 'signed', '/env', 'win32', '/Oicf']
|
||||
return outdir, output, variables, flags
|
||||
|
||||
|
||||
def _LanguageMatchesForPch(source_ext, pch_source_ext):
|
||||
c_exts = ('.c',)
|
||||
cc_exts = ('.cc', '.cxx', '.cpp')
|
||||
return ((source_ext in c_exts and pch_source_ext in c_exts) or
|
||||
(source_ext in cc_exts and pch_source_ext in cc_exts))
|
||||
|
||||
class PrecompiledHeader(object):
|
||||
"""Helper to generate dependencies and build rules to handle generation of
|
||||
precompiled headers. Interface matches the GCH handler in xcode_emulation.py.
|
||||
"""
|
||||
def __init__(self, settings, config, gyp_to_build_path):
|
||||
self.settings = settings
|
||||
self.config = config
|
||||
self.gyp_to_build_path = gyp_to_build_path
|
||||
|
||||
def _PchHeader(self):
|
||||
"""Get the header that will appear in an #include line for all source
|
||||
files."""
|
||||
return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
|
||||
|
||||
def _PchSource(self):
|
||||
"""Get the source file that is built once to compile the pch data."""
|
||||
return self.gyp_to_build_path(
|
||||
self.settings.msvs_precompiled_source[self.config])
|
||||
|
||||
def _PchOutput(self):
|
||||
"""Get the name of the output of the compiled pch data."""
|
||||
return '${pchprefix}.' + self._PchHeader() + '.pch'
|
||||
|
||||
def GetObjDependencies(self, sources, objs):
|
||||
"""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
|
||||
with make.py on Mac, and xcode_emulation.py."""
|
||||
if not self._PchHeader():
|
||||
return []
|
||||
source = self._PchSource()
|
||||
assert source
|
||||
pch_ext = os.path.splitext(self._PchSource())[1]
|
||||
for source in sources:
|
||||
if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
|
||||
return [(None, None, self._PchOutput())]
|
||||
return []
|
||||
|
||||
def GetPchBuildCommands(self):
|
||||
"""Returns [(path_to_pch, language_flag, language, header)].
|
||||
|path_to_gch| and |header| are relative to the build directory."""
|
||||
header = self._PchHeader()
|
||||
source = self._PchSource()
|
||||
if not source or not header:
|
||||
return []
|
||||
ext = os.path.splitext(source)[1]
|
||||
lang = 'c' if ext == '.c' else 'cc'
|
||||
return [(self._PchOutput(), '/Yc' + header, lang, source)]
|
||||
|
||||
|
||||
vs_version = None
|
||||
def GetVSVersion(generator_flags):
|
||||
global vs_version
|
||||
if not vs_version:
|
||||
vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
|
||||
generator_flags.get('msvs_version', 'auto'))
|
||||
return vs_version
|
||||
|
||||
def _GetVsvarsSetupArgs(generator_flags, arch):
|
||||
vs = GetVSVersion(generator_flags)
|
||||
return vs.SetupScript()
|
||||
|
||||
def ExpandMacros(string, expansions):
|
||||
"""Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
|
||||
for the canonical way to retrieve a suitable dict."""
|
||||
if '$' in string:
|
||||
for old, new in expansions.iteritems():
|
||||
assert '$(' not in new, new
|
||||
string = string.replace(old, new)
|
||||
return string
|
||||
|
||||
def _ExtractImportantEnvironment(output_of_set):
|
||||
"""Extracts environment variables required for the toolchain to run from
|
||||
a textual dump output by the cmd.exe 'set' command."""
|
||||
envvars_to_save = (
|
||||
'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
|
||||
'include',
|
||||
'lib',
|
||||
'libpath',
|
||||
'path',
|
||||
'pathext',
|
||||
'systemroot',
|
||||
'temp',
|
||||
'tmp',
|
||||
)
|
||||
env = {}
|
||||
for line in output_of_set.splitlines():
|
||||
for envvar in envvars_to_save:
|
||||
if re.match(envvar + '=', line.lower()):
|
||||
var, setting = line.split('=', 1)
|
||||
if envvar == 'path':
|
||||
# Our own rules (for running gyp-win-tool) and other actions in
|
||||
# Chromium rely on python being in the path. Add the path to this
|
||||
# python here so that if it's not in the path when ninja is run
|
||||
# later, python will still be found.
|
||||
setting = os.path.dirname(sys.executable) + os.pathsep + setting
|
||||
env[var.upper()] = setting
|
||||
break
|
||||
for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
|
||||
if required not in env:
|
||||
raise Exception('Environment variable "%s" '
|
||||
'required to be set to valid path' % required)
|
||||
return env
|
||||
|
||||
def _FormatAsEnvironmentBlock(envvar_dict):
|
||||
"""Format as an 'environment block' directly suitable for CreateProcess.
|
||||
Briefly this is a list of key=value\0, terminated by an additional \0. See
|
||||
CreateProcess documentation for more details."""
|
||||
block = ''
|
||||
nul = '\0'
|
||||
for key, value in envvar_dict.iteritems():
|
||||
block += key + '=' + value + nul
|
||||
block += nul
|
||||
return block
|
||||
|
||||
def GenerateEnvironmentFiles(toplevel_build_dir, generator_flags, 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
|
||||
msvs_target_platform hackery). Different architectures require a different
|
||||
compiler binary, and different supporting environment variables (INCLUDE,
|
||||
LIB, LIBPATH). So, we extract the environment here, wrap all invocations
|
||||
of compiler tools (cl, link, lib, rc, midl, etc.) via win_tool.py which
|
||||
sets up the environment, and then we do not prefix the compiler with
|
||||
an absolute path, instead preferring something like "cl.exe" in the rule
|
||||
which will then run whichever the environment setup has put in the path."""
|
||||
vs = GetVSVersion(generator_flags)
|
||||
for arch in ('x86', 'x64'):
|
||||
args = vs.SetupScript(arch)
|
||||
args.extend(('&&', 'set'))
|
||||
popen = subprocess.Popen(
|
||||
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
variables, _ = popen.communicate()
|
||||
env = _ExtractImportantEnvironment(variables)
|
||||
env_block = _FormatAsEnvironmentBlock(env)
|
||||
f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
|
||||
f.write(env_block)
|
||||
f.close()
|
153
tools/gyp/pylib/gyp/ninja_syntax.py
Normal file
153
tools/gyp/pylib/gyp/ninja_syntax.py
Normal file
@ -0,0 +1,153 @@
|
||||
# This file comes from
|
||||
# https://github.com/martine/ninja/blob/master/misc/ninja_syntax.py
|
||||
# Do not edit! Edit the upstream one instead.
|
||||
|
||||
"""Python module for generating .ninja files.
|
||||
|
||||
Note that this is emphatically not a required piece of Ninja; it's
|
||||
just a helpful utility for build-file-generation systems that already
|
||||
use Python.
|
||||
"""
|
||||
|
||||
import textwrap
|
||||
import re
|
||||
|
||||
def escape_spaces(word):
|
||||
return word.replace('$ ','$$ ').replace(' ','$ ')
|
||||
|
||||
class Writer(object):
|
||||
def __init__(self, output, width=78):
|
||||
self.output = output
|
||||
self.width = width
|
||||
|
||||
def newline(self):
|
||||
self.output.write('\n')
|
||||
|
||||
def comment(self, text):
|
||||
for line in textwrap.wrap(text, self.width - 2):
|
||||
self.output.write('# ' + line + '\n')
|
||||
|
||||
def variable(self, key, value, indent=0):
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, list):
|
||||
value = ' '.join(filter(None, value)) # Filter out empty strings.
|
||||
self._line('%s = %s' % (key, value), indent)
|
||||
|
||||
def rule(self, name, command, description=None, depfile=None,
|
||||
generator=False, restat=False, rspfile=None,
|
||||
rspfile_content=None):
|
||||
self._line('rule %s' % name)
|
||||
self.variable('command', command, indent=1)
|
||||
if description:
|
||||
self.variable('description', description, indent=1)
|
||||
if depfile:
|
||||
self.variable('depfile', depfile, indent=1)
|
||||
if generator:
|
||||
self.variable('generator', '1', indent=1)
|
||||
if restat:
|
||||
self.variable('restat', '1', indent=1)
|
||||
if rspfile:
|
||||
self.variable('rspfile', rspfile, indent=1)
|
||||
if rspfile_content:
|
||||
self.variable('rspfile_content', rspfile_content, indent=1)
|
||||
|
||||
def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
|
||||
variables=None):
|
||||
outputs = self._as_list(outputs)
|
||||
all_inputs = self._as_list(inputs)[:]
|
||||
out_outputs = list(map(escape_spaces, outputs))
|
||||
all_inputs = list(map(escape_spaces, all_inputs))
|
||||
|
||||
if implicit:
|
||||
implicit = map(escape_spaces, self._as_list(implicit))
|
||||
all_inputs.append('|')
|
||||
all_inputs.extend(implicit)
|
||||
if order_only:
|
||||
order_only = map(escape_spaces, self._as_list(order_only))
|
||||
all_inputs.append('||')
|
||||
all_inputs.extend(order_only)
|
||||
|
||||
self._line('build %s: %s %s' % (' '.join(out_outputs),
|
||||
rule,
|
||||
' '.join(all_inputs)))
|
||||
|
||||
if variables:
|
||||
if isinstance(variables, dict):
|
||||
iterator = variables.iteritems()
|
||||
else:
|
||||
iterator = iter(variables)
|
||||
|
||||
for key, val in iterator:
|
||||
self.variable(key, val, indent=1)
|
||||
|
||||
return outputs
|
||||
|
||||
def include(self, path):
|
||||
self._line('include %s' % path)
|
||||
|
||||
def subninja(self, path):
|
||||
self._line('subninja %s' % path)
|
||||
|
||||
def default(self, paths):
|
||||
self._line('default %s' % ' '.join(self._as_list(paths)))
|
||||
|
||||
def _count_dollars_before_index(self, s, i):
|
||||
"""Returns the number of '$' characters right in front of s[i]."""
|
||||
dollar_count = 0
|
||||
dollar_index = i - 1
|
||||
while dollar_index > 0 and s[dollar_index] == '$':
|
||||
dollar_count += 1
|
||||
dollar_index -= 1
|
||||
return dollar_count
|
||||
|
||||
def _line(self, text, indent=0):
|
||||
"""Write 'text' word-wrapped at self.width characters."""
|
||||
leading_space = ' ' * indent
|
||||
while len(leading_space) + len(text) > self.width:
|
||||
# The text is too wide; wrap if possible.
|
||||
|
||||
# Find the rightmost space that would obey our width constraint and
|
||||
# that's not an escaped space.
|
||||
available_space = self.width - len(leading_space) - len(' $')
|
||||
space = available_space
|
||||
while True:
|
||||
space = text.rfind(' ', 0, space)
|
||||
if space < 0 or \
|
||||
self._count_dollars_before_index(text, space) % 2 == 0:
|
||||
break
|
||||
|
||||
if space < 0:
|
||||
# No such space; just use the first unescaped space we can find.
|
||||
space = available_space - 1
|
||||
while True:
|
||||
space = text.find(' ', space + 1)
|
||||
if space < 0 or \
|
||||
self._count_dollars_before_index(text, space) % 2 == 0:
|
||||
break
|
||||
if space < 0:
|
||||
# Give up on breaking.
|
||||
break
|
||||
|
||||
self.output.write(leading_space + text[0:space] + ' $\n')
|
||||
text = text[space+1:]
|
||||
|
||||
# Subsequent lines are continuations, so indent them.
|
||||
leading_space = ' ' * (indent+2)
|
||||
|
||||
self.output.write(leading_space + text + '\n')
|
||||
|
||||
def _as_list(self, input):
|
||||
if input is None:
|
||||
return []
|
||||
if isinstance(input, list):
|
||||
return input
|
||||
return [input]
|
||||
|
||||
|
||||
def escape(string):
|
||||
"""Escape a string such that it can be embedded into a Ninja file without
|
||||
further interpretation."""
|
||||
assert '\n' not in string, 'Ninja syntax does not allow newlines'
|
||||
# We only have one special metacharacter: '$'.
|
||||
return string.replace('$', '$$')
|
51
tools/gyp/pylib/gyp/sun_tool.py
Executable file
51
tools/gyp/pylib/gyp/sun_tool.py
Executable file
@ -0,0 +1,51 @@
|
||||
#!/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.
|
||||
|
||||
"""These functions are executed via gyp-sun-tool when using the Makefile
|
||||
generator."""
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main(args):
|
||||
executor = SunTool()
|
||||
executor.Dispatch(args)
|
||||
|
||||
|
||||
class SunTool(object):
|
||||
"""This class performs all the SunOS tooling steps. The methods can either be
|
||||
executed directly, or dispatched from an argument list."""
|
||||
|
||||
def Dispatch(self, args):
|
||||
"""Dispatches a string command to a method."""
|
||||
if len(args) < 1:
|
||||
raise Exception("Not enough arguments")
|
||||
|
||||
method = "Exec%s" % self._CommandifyName(args[0])
|
||||
getattr(self, method)(*args[1:])
|
||||
|
||||
def _CommandifyName(self, name_string):
|
||||
"""Transforms a tool name like copy-info-plist to CopyInfoPlist"""
|
||||
return name_string.title().replace('-', '')
|
||||
|
||||
def ExecFlock(self, lockfile, *cmd_list):
|
||||
"""Emulates the most basic behavior of Linux's flock(1)."""
|
||||
# Rely on exception handling to report errors.
|
||||
# Note that the stock python on SunOS has a bug
|
||||
# where fcntl.flock(fd, LOCK_EX) always fails
|
||||
# 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)
|
||||
fcntl.fcntl(fd, fcntl.F_SETLK, op)
|
||||
return subprocess.call(cmd_list)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
68
tools/gyp/pylib/gyp/system_test.py
Executable file
68
tools/gyp/pylib/gyp/system_test.py
Executable file
@ -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.
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
|
||||
def TestCommands(commands, files={}, env={}):
|
||||
"""Run commands in a temporary directory, returning true if they all succeed.
|
||||
Return false on failures or if any commands produce output.
|
||||
|
||||
Arguments:
|
||||
commands: an array of shell-interpretable commands, e.g. ['ls -l', 'pwd']
|
||||
each will be expanded with Python %-expansion using env first.
|
||||
files: a dictionary mapping filename to contents;
|
||||
files will be created in the temporary directory before running
|
||||
the command.
|
||||
env: a dictionary of strings to expand commands with.
|
||||
"""
|
||||
tempdir = tempfile.mkdtemp()
|
||||
try:
|
||||
for name, contents in files.items():
|
||||
f = open(os.path.join(tempdir, name), 'wb')
|
||||
f.write(contents)
|
||||
f.close()
|
||||
for command in commands:
|
||||
proc = subprocess.Popen(command % env, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=tempdir)
|
||||
output = proc.communicate()[0]
|
||||
if proc.returncode != 0 or output:
|
||||
return False
|
||||
return True
|
||||
finally:
|
||||
shutil.rmtree(tempdir)
|
||||
return False
|
||||
|
||||
|
||||
def TestArSupportsT(ar_command='ar', cc_command='cc'):
|
||||
"""Test whether 'ar' supports the 'T' flag."""
|
||||
return TestCommands(['%(cc)s -c test.c',
|
||||
'%(ar)s crsT test.a test.o',
|
||||
'%(cc)s test.a'],
|
||||
files={'test.c': 'int main(){}'},
|
||||
env={'ar': ar_command, 'cc': cc_command})
|
||||
|
||||
|
||||
def main():
|
||||
# Run the various test functions and print the results.
|
||||
def RunTest(description, function, **kwargs):
|
||||
print "Testing " + description + ':',
|
||||
if function(**kwargs):
|
||||
print 'ok'
|
||||
else:
|
||||
print 'fail'
|
||||
RunTest("ar 'T' flag", TestArSupportsT)
|
||||
RunTest("ar 'T' flag with ccache", TestArSupportsT, cc_command='ccache cc')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
194
tools/gyp/pylib/gyp/win_tool.py
Normal file
194
tools/gyp/pylib/gyp/win_tool.py
Normal file
@ -0,0 +1,194 @@
|
||||
#!/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.
|
||||
|
||||
"""Utility functions for Windows builds.
|
||||
|
||||
These functions are executed via gyp-win-tool when using the ninja generator.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import win32con
|
||||
import win32file
|
||||
import pywintypes
|
||||
|
||||
|
||||
def main(args):
|
||||
executor = WinTool()
|
||||
exit_code = executor.Dispatch(args)
|
||||
if exit_code is not None:
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
class LinkLock(object):
|
||||
"""A flock-style lock to limit the number of concurrent links to one. Based on
|
||||
http://code.activestate.com/recipes/65203-portalocker-cross-platform-posixnt-api-for-flock-s/
|
||||
"""
|
||||
def __enter__(self):
|
||||
self.file = open('LinkLock', 'w+')
|
||||
self.file_handle = win32file._get_osfhandle(self.file.fileno())
|
||||
win32file.LockFileEx(self.file_handle, win32con.LOCKFILE_EXCLUSIVE_LOCK,
|
||||
0, -0x10000, pywintypes.OVERLAPPED())
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
win32file.UnlockFileEx(
|
||||
self.file_handle, 0, -0x10000, pywintypes.OVERLAPPED())
|
||||
self.file.close()
|
||||
|
||||
|
||||
class WinTool(object):
|
||||
"""This class performs all the Windows tooling steps. The methods can either
|
||||
be executed directly, or dispatched from an argument list."""
|
||||
|
||||
def Dispatch(self, args):
|
||||
"""Dispatches a string command to a method."""
|
||||
if len(args) < 1:
|
||||
raise Exception("Not enough arguments")
|
||||
|
||||
method = "Exec%s" % self._CommandifyName(args[0])
|
||||
return getattr(self, method)(*args[1:])
|
||||
|
||||
def _CommandifyName(self, name_string):
|
||||
"""Transforms a tool name like recursive-mirror to RecursiveMirror."""
|
||||
return name_string.title().replace('-', '')
|
||||
|
||||
def _GetEnv(self, arch):
|
||||
"""Gets the saved environment from a file for a given architecture."""
|
||||
# The environment is saved as an "environment block" (see CreateProcess
|
||||
# and msvs_emulation for details). We convert to a dict here.
|
||||
# Drop last 2 NULs, one for list terminator, one for trailing vs. separator.
|
||||
pairs = open(arch).read()[:-2].split('\0')
|
||||
kvs = [item.split('=', 1) for item in pairs]
|
||||
return dict(kvs)
|
||||
|
||||
def ExecStamp(self, path):
|
||||
"""Simple stamp command."""
|
||||
open(path, 'w').close()
|
||||
|
||||
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)
|
||||
else:
|
||||
os.unlink(dest)
|
||||
if os.path.isdir(source):
|
||||
shutil.copytree(source, dest)
|
||||
else:
|
||||
shutil.copy2(source, dest)
|
||||
|
||||
def ExecLinkWrapper(self, arch, *args):
|
||||
"""Filter diagnostic output from link that looks like:
|
||||
' Creating library ui.dll.lib and object ui.dll.exp'
|
||||
This happens when there are exports from the dll or exe.
|
||||
"""
|
||||
with LinkLock():
|
||||
env = self._GetEnv(arch)
|
||||
popen = subprocess.Popen(args, shell=True, env=env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
out, _ = popen.communicate()
|
||||
for line in out.splitlines():
|
||||
if not line.startswith(' Creating library '):
|
||||
print line
|
||||
return popen.returncode
|
||||
|
||||
def ExecManifestWrapper(self, arch, *args):
|
||||
"""Run manifest tool with environment set. Strip out undesirable warning
|
||||
(some XML blocks are recognized by the OS loader, but not the manifest
|
||||
tool)."""
|
||||
env = self._GetEnv(arch)
|
||||
popen = subprocess.Popen(args, shell=True, env=env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
out, _ = popen.communicate()
|
||||
for line in out.splitlines():
|
||||
if line and 'manifest authoring warning 81010002' not in line:
|
||||
print line
|
||||
return popen.returncode
|
||||
|
||||
def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
|
||||
*flags):
|
||||
"""Filter noisy filenames output from MIDL compile step that isn't
|
||||
quietable via command line flags.
|
||||
"""
|
||||
args = ['midl', '/nologo'] + list(flags) + [
|
||||
'/out', outdir,
|
||||
'/tlb', tlb,
|
||||
'/h', h,
|
||||
'/dlldata', dlldata,
|
||||
'/iid', iid,
|
||||
'/proxy', proxy,
|
||||
idl]
|
||||
env = self._GetEnv(arch)
|
||||
popen = subprocess.Popen(args, shell=True, env=env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
out, _ = popen.communicate()
|
||||
# Filter junk out of stdout, and write filtered versions. Output we want
|
||||
# to filter is pairs of lines that look like this:
|
||||
# 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))
|
||||
for line in lines:
|
||||
if not line.startswith(prefix) 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()
|
||||
for line in out.splitlines():
|
||||
if (not line.startswith('Copyright (C) Microsoft Corporation') and
|
||||
not line.startswith('Microsoft (R) Macro Assembler') and
|
||||
not line.startswith(' Assembling: ') and
|
||||
line):
|
||||
print line
|
||||
return popen.returncode
|
||||
|
||||
def ExecRcWrapper(self, arch, *args):
|
||||
"""Filter logo banner from invocations of rc.exe. Older versions of RC
|
||||
don't support the /nologo flag."""
|
||||
env = self._GetEnv(arch)
|
||||
popen = subprocess.Popen(args, shell=True, env=env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
out, _ = popen.communicate()
|
||||
for line in out.splitlines():
|
||||
if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and
|
||||
not line.startswith('Copyright (C) Microsoft Corporation') and
|
||||
line):
|
||||
print line
|
||||
return popen.returncode
|
||||
|
||||
def ExecClWrapper(self, arch, depname, *args):
|
||||
"""Runs cl.exe and filters output through ninja-deplist-helper to get
|
||||
dependendency information which is stored in |depname|."""
|
||||
env = self._GetEnv(arch)
|
||||
args = ' '.join(args) + \
|
||||
'| ninja-deplist-helper -r . -q -f cl -o ' + depname + '"'
|
||||
popen = subprocess.Popen(args, shell=True, env=env)
|
||||
popen.wait()
|
||||
return popen.returncode
|
||||
|
||||
def ExecActionWrapper(self, arch, rspfile, *dir):
|
||||
"""Runs an action command line from a response file using the environment
|
||||
for |arch|. If |dir| is supplied, use that as the working directory."""
|
||||
env = self._GetEnv(arch)
|
||||
args = open(rspfile).read()
|
||||
dir = dir[0] if dir else None
|
||||
popen = subprocess.Popen(args, shell=True, env=env, cwd=dir)
|
||||
popen.wait()
|
||||
return popen.returncode
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
1044
tools/gyp/pylib/gyp/xcode_emulation.py
Normal file
1044
tools/gyp/pylib/gyp/xcode_emulation.py
Normal file
File diff suppressed because it is too large
Load Diff
2834
tools/gyp/pylib/gyp/xcodeproj_file.py
Normal file
2834
tools/gyp/pylib/gyp/xcodeproj_file.py
Normal file
File diff suppressed because it is too large
Load Diff
69
tools/gyp/pylib/gyp/xml_fix.py
Normal file
69
tools/gyp/pylib/gyp/xml_fix.py
Normal file
@ -0,0 +1,69 @@
|
||||
# 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.
|
||||
|
||||
"""Applies a fix to CR LF TAB handling in xml.dom.
|
||||
|
||||
Fixes this: http://code.google.com/p/chromium/issues/detail?id=76293
|
||||
Working around this: http://bugs.python.org/issue5752
|
||||
TODO(bradnelson): Consider dropping this when we drop XP support.
|
||||
"""
|
||||
|
||||
|
||||
import xml.dom.minidom
|
||||
|
||||
|
||||
def _Replacement_write_data(writer, data, is_attrib=False):
|
||||
"""Writes datachars to writer."""
|
||||
data = data.replace("&", "&").replace("<", "<")
|
||||
data = data.replace("\"", """).replace(">", ">")
|
||||
if is_attrib:
|
||||
data = data.replace(
|
||||
"\r", "
").replace(
|
||||
"\n", "
").replace(
|
||||
"\t", "	")
|
||||
writer.write(data)
|
||||
|
||||
|
||||
def _Replacement_writexml(self, writer, indent="", addindent="", newl=""):
|
||||
# indent = current indentation
|
||||
# addindent = indentation to add to higher levels
|
||||
# newl = newline string
|
||||
writer.write(indent+"<" + self.tagName)
|
||||
|
||||
attrs = self._get_attributes()
|
||||
a_names = attrs.keys()
|
||||
a_names.sort()
|
||||
|
||||
for a_name in a_names:
|
||||
writer.write(" %s=\"" % a_name)
|
||||
_Replacement_write_data(writer, attrs[a_name].value, is_attrib=True)
|
||||
writer.write("\"")
|
||||
if self.childNodes:
|
||||
writer.write(">%s" % newl)
|
||||
for node in self.childNodes:
|
||||
node.writexml(writer, indent + addindent, addindent, newl)
|
||||
writer.write("%s</%s>%s" % (indent, self.tagName, newl))
|
||||
else:
|
||||
writer.write("/>%s" % newl)
|
||||
|
||||
|
||||
class XmlFix(object):
|
||||
"""Object to manage temporary patching of xml.dom.minidom."""
|
||||
|
||||
def __init__(self):
|
||||
# Preserve current xml.dom.minidom functions.
|
||||
self.write_data = xml.dom.minidom._write_data
|
||||
self.writexml = xml.dom.minidom.Element.writexml
|
||||
# Inject replacement versions of a function and a method.
|
||||
xml.dom.minidom._write_data = _Replacement_write_data
|
||||
xml.dom.minidom.Element.writexml = _Replacement_writexml
|
||||
|
||||
def Cleanup(self):
|
||||
if self.write_data:
|
||||
xml.dom.minidom._write_data = self.write_data
|
||||
xml.dom.minidom.Element.writexml = self.writexml
|
||||
self.write_data = None
|
||||
|
||||
def __del__(self):
|
||||
self.Cleanup()
|
307
tools/gyp/pylintrc
Normal file
307
tools/gyp/pylintrc
Normal file
@ -0,0 +1,307 @@
|
||||
[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
|
81
tools/gyp/samples/samples
Executable file
81
tools/gyp/samples/samples
Executable file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/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 os.path
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
|
||||
gyps = [
|
||||
'app/app.gyp',
|
||||
'base/base.gyp',
|
||||
'build/temp_gyp/googleurl.gyp',
|
||||
'build/all.gyp',
|
||||
'build/common.gypi',
|
||||
'build/external_code.gypi',
|
||||
'chrome/test/security_tests/security_tests.gyp',
|
||||
'chrome/third_party/hunspell/hunspell.gyp',
|
||||
'chrome/chrome.gyp',
|
||||
'media/media.gyp',
|
||||
'net/net.gyp',
|
||||
'printing/printing.gyp',
|
||||
'sdch/sdch.gyp',
|
||||
'skia/skia.gyp',
|
||||
'testing/gmock.gyp',
|
||||
'testing/gtest.gyp',
|
||||
'third_party/bzip2/bzip2.gyp',
|
||||
'third_party/icu38/icu38.gyp',
|
||||
'third_party/libevent/libevent.gyp',
|
||||
'third_party/libjpeg/libjpeg.gyp',
|
||||
'third_party/libpng/libpng.gyp',
|
||||
'third_party/libxml/libxml.gyp',
|
||||
'third_party/libxslt/libxslt.gyp',
|
||||
'third_party/lzma_sdk/lzma_sdk.gyp',
|
||||
'third_party/modp_b64/modp_b64.gyp',
|
||||
'third_party/npapi/npapi.gyp',
|
||||
'third_party/sqlite/sqlite.gyp',
|
||||
'third_party/zlib/zlib.gyp',
|
||||
'v8/tools/gyp/v8.gyp',
|
||||
'webkit/activex_shim/activex_shim.gyp',
|
||||
'webkit/activex_shim_dll/activex_shim_dll.gyp',
|
||||
'webkit/build/action_csspropertynames.py',
|
||||
'webkit/build/action_cssvaluekeywords.py',
|
||||
'webkit/build/action_jsconfig.py',
|
||||
'webkit/build/action_makenames.py',
|
||||
'webkit/build/action_maketokenizer.py',
|
||||
'webkit/build/action_useragentstylesheets.py',
|
||||
'webkit/build/rule_binding.py',
|
||||
'webkit/build/rule_bison.py',
|
||||
'webkit/build/rule_gperf.py',
|
||||
'webkit/tools/test_shell/test_shell.gyp',
|
||||
'webkit/webkit.gyp',
|
||||
]
|
||||
|
||||
|
||||
def Main(argv):
|
||||
if len(argv) != 3 or argv[1] not in ['push', 'pull']:
|
||||
print 'Usage: %s push/pull PATH_TO_CHROME' % argv[0]
|
||||
return 1
|
||||
|
||||
path_to_chrome = argv[2]
|
||||
|
||||
for g in gyps:
|
||||
chrome_file = os.path.join(path_to_chrome, g)
|
||||
local_file = os.path.join(os.path.dirname(argv[0]), os.path.split(g)[1])
|
||||
if argv[1] == 'push':
|
||||
print 'Copying %s to %s' % (local_file, chrome_file)
|
||||
shutil.copyfile(local_file, chrome_file)
|
||||
elif argv[1] == 'pull':
|
||||
print 'Copying %s to %s' % (chrome_file, local_file)
|
||||
shutil.copyfile(chrome_file, local_file)
|
||||
else:
|
||||
assert False
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(Main(sys.argv))
|
5
tools/gyp/samples/samples.bat
Normal file
5
tools/gyp/samples/samples.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@rem Copyright (c) 2009 Google Inc. All rights reserved.
|
||||
@rem Use of this source code is governed by a BSD-style license that can be
|
||||
@rem found in the LICENSE file.
|
||||
|
||||
@python %~dp0/samples %*
|
26
tools/gyp/setup.py
Executable file
26
tools/gyp/setup.py
Executable file
@ -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.
|
||||
|
||||
from distutils.core import setup
|
||||
from distutils.command.install import install
|
||||
from distutils.command.install_lib import install_lib
|
||||
from distutils.command.install_scripts import install_scripts
|
||||
|
||||
setup(
|
||||
name='gyp',
|
||||
version='0.1',
|
||||
description='Generate Your Projects',
|
||||
author='Chromium Authors',
|
||||
author_email='chromium-dev@googlegroups.com',
|
||||
url='http://code.google.com/p/gyp',
|
||||
package_dir = {'': 'pylib'},
|
||||
packages=['gyp', 'gyp.generator'],
|
||||
|
||||
scripts = ['gyp'],
|
||||
cmdclass = {'install': install,
|
||||
'install_lib': install_lib,
|
||||
'install_scripts': install_scripts},
|
||||
)
|
23
tools/gyp/test/actions-bare/gyptest-bare.py
Executable file
23
tools/gyp/test/actions-bare/gyptest-bare.py
Executable file
@ -0,0 +1,23 @@
|
||||
#!/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()
|
25
tools/gyp/test/actions-bare/src/bare.gyp
Normal file
25
tools/gyp/test/actions-bare/src/bare.gyp
Normal file
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
11
tools/gyp/test/actions-bare/src/bare.py
Executable file
11
tools/gyp/test/actions-bare/src/bare.py
Executable file
@ -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()
|
72
tools/gyp/test/actions-multiple/gyptest-all.py
Executable file
72
tools/gyp/test/actions-multiple/gyptest-all.py
Executable file
@ -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()
|
226
tools/gyp/test/actions-multiple/src/actions.gyp
Normal file
226
tools/gyp/test/actions-multiple/src/actions.gyp
Normal file
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
9
tools/gyp/test/actions-multiple/src/copy.py
Executable file
9
tools/gyp/test/actions-multiple/src/copy.py
Executable file
@ -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])
|
12
tools/gyp/test/actions-multiple/src/filter.py
Executable file
12
tools/gyp/test/actions-multiple/src/filter.py
Executable file
@ -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()
|
11
tools/gyp/test/actions-multiple/src/foo.c
Normal file
11
tools/gyp/test/actions-multiple/src/foo.c
Normal file
@ -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 <stdio.h>
|
||||
|
||||
void foo(void) {
|
||||
printf("foo\n");
|
||||
}
|
1
tools/gyp/test/actions-multiple/src/input.txt
Normal file
1
tools/gyp/test/actions-multiple/src/input.txt
Normal file
@ -0,0 +1 @@
|
||||
hello there
|
22
tools/gyp/test/actions-multiple/src/main.c
Normal file
22
tools/gyp/test/actions-multiple/src/main.c
Normal file
@ -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 <stdio.h>
|
||||
|
||||
void bar(void);
|
||||
void car(void);
|
||||
void dar(void);
|
||||
void ear(void);
|
||||
|
||||
int main() {
|
||||
printf("{\n");
|
||||
bar();
|
||||
car();
|
||||
dar();
|
||||
ear();
|
||||
printf("}\n");
|
||||
return 0;
|
||||
}
|
26
tools/gyp/test/actions-none/gyptest-none.py
Executable file
26
tools/gyp/test/actions-none/gyptest-none.py
Executable file
@ -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 actions can be in 'none' type targets with source files.
|
||||
"""
|
||||
|
||||
import TestGyp
|
||||
|
||||
test = TestGyp.TestGyp()
|
||||
# TODO(bradnelson): fix scons.
|
||||
if test.format == 'scons':
|
||||
test.skip_test()
|
||||
|
||||
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()
|
12
tools/gyp/test/actions-none/src/fake_cross.py
Normal file
12
tools/gyp/test/actions-none/src/fake_cross.py
Normal file
@ -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()
|
1
tools/gyp/test/actions-none/src/foo.cc
Normal file
1
tools/gyp/test/actions-none/src/foo.cc
Normal file
@ -0,0 +1 @@
|
||||
foo.cc
|
35
tools/gyp/test/actions-none/src/none_with_source_files.gyp
Normal file
35
tools/gyp/test/actions-none/src/none_with_source_files.gyp
Normal file
@ -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,
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
26
tools/gyp/test/actions-subdir/gyptest-action.py
Executable file
26
tools/gyp/test/actions-subdir/gyptest-action.py
Executable file
@ -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()
|
11
tools/gyp/test/actions-subdir/src/make-file.py
Executable file
11
tools/gyp/test/actions-subdir/src/make-file.py
Executable file
@ -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)
|
31
tools/gyp/test/actions-subdir/src/none.gyp
Normal file
31
tools/gyp/test/actions-subdir/src/none.gyp
Normal file
@ -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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
11
tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py
Executable file
11
tools/gyp/test/actions-subdir/src/subdir/make-subdir-file.py
Executable file
@ -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)
|
28
tools/gyp/test/actions-subdir/src/subdir/subdir.gyp
Normal file
28
tools/gyp/test/actions-subdir/src/subdir/subdir.gyp
Normal file
@ -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,
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
102
tools/gyp/test/actions/gyptest-all.py
Executable file
102
tools/gyp/test/actions/gyptest-all.py
Executable file
@ -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']:
|
||||
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()
|
69
tools/gyp/test/actions/gyptest-default.py
Executable file
69
tools/gyp/test/actions/gyptest-default.py
Executable file
@ -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']:
|
||||
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()
|
24
tools/gyp/test/actions/gyptest-errors.py
Executable file
24
tools/gyp/test/actions/gyptest-errors.py
Executable file
@ -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()
|
24
tools/gyp/test/actions/src/action_missing_name.gyp
Normal file
24
tools/gyp/test/actions/src/action_missing_name.gyp
Normal file
@ -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\'',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
114
tools/gyp/test/actions/src/actions.gyp
Normal file
114
tools/gyp/test/actions/src/actions.gyp
Normal file
@ -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,
|
||||
}],
|
||||
},
|
||||
|
||||
],
|
||||
}
|
21
tools/gyp/test/actions/src/confirm-dep-files.py
Executable file
21
tools/gyp/test/actions/src/confirm-dep-files.py
Executable file
@ -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")
|
46
tools/gyp/test/actions/src/subdir1/counter.py
Executable file
46
tools/gyp/test/actions/src/subdir1/counter.py
Executable file
@ -0,0 +1,46 @@
|
||||
#!/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, scons deletes any declared
|
||||
# outputs, so we would lose our count if we just wrote to the given output file.
|
||||
# (The other option is to use Precious() in the scons generator, but that seems
|
||||
# too heavy-handed just to support this somewhat unrealistic test case, and
|
||||
# might lead to unintended side-effects).
|
||||
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)
|
74
tools/gyp/test/actions/src/subdir1/executable.gyp
Normal file
74
tools/gyp/test/actions/src/subdir1/executable.gyp
Normal file
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
20
tools/gyp/test/actions/src/subdir1/make-prog1.py
Executable file
20
tools/gyp/test/actions/src/subdir1/make-prog1.py
Executable file
@ -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 <stdio.h>
|
||||
|
||||
void prog1(void)
|
||||
{
|
||||
printf("Hello from make-prog1.py\n");
|
||||
}
|
||||
"""
|
||||
|
||||
open(sys.argv[1], 'w').write(contents)
|
||||
|
||||
sys.exit(0)
|
20
tools/gyp/test/actions/src/subdir1/make-prog2.py
Executable file
20
tools/gyp/test/actions/src/subdir1/make-prog2.py
Executable file
@ -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 <stdio.h>
|
||||
|
||||
void prog2(void)
|
||||
{
|
||||
printf("Hello from make-prog2.py\n");
|
||||
}
|
||||
"""
|
||||
|
||||
open(sys.argv[1], 'w').write(contents)
|
||||
|
||||
sys.exit(0)
|
12
tools/gyp/test/actions/src/subdir1/program.c
Normal file
12
tools/gyp/test/actions/src/subdir1/program.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern void prog1(void);
|
||||
extern void prog2(void);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("Hello from program.c\n");
|
||||
prog1();
|
||||
prog2();
|
||||
return 0;
|
||||
}
|
11
tools/gyp/test/actions/src/subdir2/make-file.py
Executable file
11
tools/gyp/test/actions/src/subdir2/make-file.py
Executable file
@ -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)
|
33
tools/gyp/test/actions/src/subdir2/none.gyp
Normal file
33
tools/gyp/test/actions/src/subdir2/none.gyp
Normal file
@ -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,
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
21
tools/gyp/test/actions/src/subdir3/generate_main.py
Executable file
21
tools/gyp/test/actions/src/subdir3/generate_main.py
Executable file
@ -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 <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("Hello from generate_main.py\\n");
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
|
||||
open(sys.argv[1], 'w').write(contents)
|
||||
|
||||
sys.exit(0)
|
29
tools/gyp/test/actions/src/subdir3/null_input.gyp
Normal file
29
tools/gyp/test/actions/src/subdir3/null_input.gyp
Normal file
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
56
tools/gyp/test/additional-targets/gyptest-additional.py
Executable file
56
tools/gyp/test/additional-targets/gyptest-additional.py
Executable file
@ -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 simple actions when using an explicit build target of 'all'.
|
||||
"""
|
||||
|
||||
import TestGyp
|
||||
|
||||
test = TestGyp.TestGyp()
|
||||
|
||||
test.run_gyp('all.gyp', 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)
|
||||
|
||||
# TODO(mmoss) Make consistent with scons, with 'dir1' before 'out/Default'?
|
||||
if test.format in ('make', 'ninja', 'android'):
|
||||
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()
|
13
tools/gyp/test/additional-targets/src/all.gyp
Normal file
13
tools/gyp/test/additional-targets/src/all.gyp
Normal file
@ -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:*'],
|
||||
},
|
||||
],
|
||||
}
|
56
tools/gyp/test/additional-targets/src/dir1/actions.gyp
Normal file
56
tools/gyp/test/additional-targets/src/dir1/actions.gyp
Normal file
@ -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'],
|
||||
},
|
||||
}],
|
||||
],
|
||||
}
|
11
tools/gyp/test/additional-targets/src/dir1/emit.py
Executable file
11
tools/gyp/test/additional-targets/src/dir1/emit.py
Executable file
@ -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()
|
6
tools/gyp/test/additional-targets/src/dir1/lib1.c
Normal file
6
tools/gyp/test/additional-targets/src/dir1/lib1.c
Normal file
@ -0,0 +1,6 @@
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int func1(void) {
|
||||
return 42;
|
||||
}
|
31
tools/gyp/test/assembly/gyptest-assembly.py
Executable file
31
tools/gyp/test/assembly/gyptest-assembly.py
Executable file
@ -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()
|
4
tools/gyp/test/assembly/src/as.bat
Normal file
4
tools/gyp/test/assembly/src/as.bat
Normal file
@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
:: Mock windows assembler.
|
||||
cl /c %1 /Fo"%2"
|
||||
|
62
tools/gyp/test/assembly/src/assembly.gyp
Normal file
62
tools/gyp/test/assembly/src/assembly.gyp
Normal file
@ -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"', {
|
||||
'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,
|
||||
},
|
||||
],
|
||||
},
|
||||
},],
|
||||
],
|
||||
}
|
15
tools/gyp/test/assembly/src/lib1.S
Normal file
15
tools/gyp/test/assembly/src/lib1.S
Normal file
@ -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
|
3
tools/gyp/test/assembly/src/lib1.c
Normal file
3
tools/gyp/test/assembly/src/lib1.c
Normal file
@ -0,0 +1,3 @@
|
||||
int lib1_function(void) {
|
||||
return 42;
|
||||
}
|
12
tools/gyp/test/assembly/src/program.c
Normal file
12
tools/gyp/test/assembly/src/program.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern int lib1_function(void);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
fprintf(stdout, "Hello from program.c\n");
|
||||
fflush(stdout);
|
||||
fprintf(stdout, "Got %d.\n", lib1_function());
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user