3
# Copyright (c) 2009 Google Inc. All rights reserved.
4
# Use of this source code is governed by a BSD-style license that can be
5
# found in the LICENSE file.
15
# Default debug modes for GYP
18
# List of "official" debug modes, but you can use anything you like.
19
DEBUG_GENERAL = 'general'
20
DEBUG_VARIABLES = 'variables'
21
DEBUG_INCLUDES = 'includes'
23
def DebugOutput(mode, message):
24
if mode in gyp.debug.keys():
25
print "%s: %s" % (mode.upper(), message)
29
files = os.listdir(os.getcwd())
32
if file[-len(extension):] == extension:
33
build_files.append(file)
37
def Load(build_files, format, default_variables={},
38
includes=[], depth='.', params={}, check=False, circular_check=True):
40
Loads one or more specified build files.
41
default_variables and includes will be copied before use.
42
Returns the generator for the specified format and the
43
data returned by loading the specified build files.
45
default_variables = copy.copy(default_variables)
47
# Default variables provided by this program and its modules should be
48
# named WITH_CAPITAL_LETTERS to provide a distinct "best practice" namespace,
49
# avoiding collisions with user and automatic variables.
50
default_variables['GENERATOR'] = format
52
generator_name = 'gyp.generator.' + format
53
# These parameters are passed in order (as opposed to by key)
54
# because ActivePython cannot handle key parameters to __import__.
55
generator = __import__(generator_name, globals(), locals(), generator_name)
56
for (key, val) in generator.generator_default_variables.items():
57
default_variables.setdefault(key, val)
59
# Give the generator the opportunity to set additional variables based on
60
# the params it will receive in the output phase.
61
if getattr(generator, 'CalculateVariables', None):
62
generator.CalculateVariables(default_variables, params)
64
# Fetch the generator specific info that gets fed to input, we use getattr
65
# so we can default things and the generators only have to provide what
67
generator_input_info = {
68
'generator_wants_absolute_build_file_paths':
69
getattr(generator, 'generator_wants_absolute_build_file_paths', False),
70
'generator_handles_variants':
71
getattr(generator, 'generator_handles_variants', False),
72
'non_configuration_keys':
73
getattr(generator, 'generator_additional_non_configuration_keys', []),
75
getattr(generator, 'generator_additional_path_sections', []),
76
'extra_sources_for_rules':
77
getattr(generator, 'generator_extra_sources_for_rules', []),
78
'generator_supports_multiple_toolsets':
79
getattr(generator, 'generator_supports_multiple_toolsets', False),
82
# Process the input specific to this generator.
83
result = gyp.input.Load(build_files, default_variables, includes[:],
84
depth, generator_input_info, check, circular_check)
85
return [generator] + result
87
def NameValueListToDict(name_value_list):
89
Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
90
of the pairs. If a string is simply NAME, then the value in the dictionary
91
is set to True. If VALUE can be converted to an integer, it is.
94
for item in name_value_list:
95
tokens = item.split('=', 1)
97
# If we can make it an int, use that, otherwise, use the string.
99
token_value = int(tokens[1])
101
token_value = tokens[1]
102
# Set the variable to the supplied value.
103
result[tokens[0]] = token_value
105
# No value supplied, treat it as a boolean and set it.
106
result[tokens[0]] = True
109
def ShlexEnv(env_name):
110
flags = os.environ.get(env_name, [])
112
flags = shlex.split(flags)
115
def FormatOpt(opt, value):
116
if opt.startswith('--'):
117
return '%s=%s' % (opt, value)
120
def RegenerateAppendFlag(flag, values, predicate, env_name, options):
121
"""Regenerate a list of command line flags, for an option of action='append'.
123
The |env_name|, if given, is checked in the environment and used to generate
124
an initial list of options, then the options that were specified on the
125
command line (given in |values|) are appended. This matches the handling of
126
environment variables and command line flags where command line flags override
127
the environment, while not requiring the environment to be set when the flags
131
if options.use_environment and env_name:
132
for flag_value in ShlexEnv(env_name):
133
flags.append(FormatOpt(flag, predicate(flag_value)))
135
for flag_value in values:
136
flags.append(FormatOpt(flag, predicate(flag_value)))
139
def RegenerateFlags(options):
140
"""Given a parsed options object, and taking the environment variables into
141
account, returns a list of flags that should regenerate an equivalent options
142
object (even in the absence of the environment variables.)
144
Any path options will be normalized relative to depth.
146
The format flag is not included, as it is assumed the calling generator will
147
set that as appropriate.
150
path = gyp.common.FixIfRelativePath(path, options.depth)
152
return os.path.curdir
158
# We always want to ignore the environment when regenerating, to avoid
159
# duplicate or changed flags in the environment at the time of regeneration.
160
flags = ['--ignore-environment']
161
for name, metadata in options._regeneration_metadata.iteritems():
162
opt = metadata['opt']
163
value = getattr(options, name)
164
value_predicate = metadata['type'] == 'path' and FixPath or Noop
165
action = metadata['action']
166
env_name = metadata['env_name']
167
if action == 'append':
168
flags.extend(RegenerateAppendFlag(opt, value, value_predicate,
170
elif action in ('store', None): # None is a synonym for 'store'.
172
flags.append(FormatOpt(opt, value_predicate(value)))
173
elif options.use_environment and env_name and os.environ.get(env_name):
174
flags.append(FormatOpt(opt, value_predicate(os.environ.get(env_name))))
175
elif action in ('store_true', 'store_false'):
176
if ((action == 'store_true' and value) or
177
(action == 'store_false' and not value)):
179
elif options.use_environment and env_name:
180
print >>sys.stderr, ('Warning: environment regeneration unimplemented '
181
'for %s flag %r env_name %r' % (action, opt,
184
print >>sys.stderr, ('Warning: regeneration unimplemented for action %r '
185
'flag %r' % (action, opt))
189
class RegeneratableOptionParser(optparse.OptionParser):
191
self.__regeneratable_options = {}
192
optparse.OptionParser.__init__(self)
194
def add_option(self, *args, **kw):
195
"""Add an option to the parser.
197
This accepts the same arguments as OptionParser.add_option, plus the
199
regenerate: can be set to False to prevent this option from being included
201
env_name: name of environment variable that additional values for this
203
type: adds type='path', to tell the regenerator that the values of
204
this option need to be made relative to options.depth
206
env_name = kw.pop('env_name', None)
207
if 'dest' in kw and kw.pop('regenerate', True):
210
# The path type is needed for regenerating, for optparse we can just treat
212
type = kw.get('type')
214
kw['type'] = 'string'
216
self.__regeneratable_options[dest] = {
217
'action': kw.get('action'),
219
'env_name': env_name,
223
optparse.OptionParser.add_option(self, *args, **kw)
225
def parse_args(self, *args):
226
values, args = optparse.OptionParser.parse_args(self, *args)
227
values._regeneration_metadata = self.__regeneratable_options
231
my_name = os.path.basename(sys.argv[0])
233
parser = RegeneratableOptionParser()
234
usage = 'usage: %s [options ...] [build_file ...]'
235
parser.set_usage(usage.replace('%s', '%prog'))
236
parser.add_option('-D', dest='defines', action='append', metavar='VAR=VAL',
237
env_name='GYP_DEFINES',
238
help='sets variable VAR to value VAL')
239
parser.add_option('-f', '--format', dest='formats', action='append',
240
env_name='GYP_GENERATORS', regenerate=False,
241
help='output formats to generate')
242
parser.add_option('--msvs-version', dest='msvs_version',
244
help='Deprecated; use -G msvs_version=MSVS_VERSION instead')
245
parser.add_option('-I', '--include', dest='includes', action='append',
246
metavar='INCLUDE', type='path',
247
help='files to include in all loaded .gyp files')
248
parser.add_option('--depth', dest='depth', metavar='PATH', type='path',
249
help='set DEPTH gyp variable to a relative path to PATH')
250
parser.add_option('-d', '--debug', dest='debug', metavar='DEBUGMODE',
251
action='append', default=[], help='turn on a debugging '
252
'mode for debugging GYP. Supported modes are "variables" '
254
parser.add_option('-S', '--suffix', dest='suffix', default='',
255
help='suffix to add to generated files')
256
parser.add_option('-G', dest='generator_flags', action='append', default=[],
257
metavar='FLAG=VAL', env_name='GYP_GENERATOR_FLAGS',
258
help='sets generator flag FLAG to VAL')
259
parser.add_option('--generator-output', dest='generator_output',
260
action='store', default=None, metavar='DIR', type='path',
261
env_name='GYP_GENERATOR_OUTPUT',
262
help='puts generated build files under DIR')
263
parser.add_option('--ignore-environment', dest='use_environment',
264
action='store_false', default=True, regenerate=False,
265
help='do not read options from environment variables')
266
parser.add_option('--check', dest='check', action='store_true',
267
help='check format of gyp files')
268
parser.add_option('--toplevel-dir', dest='toplevel_dir', action='store',
269
default=None, metavar='DIR', type='path',
270
help='directory to use as the root of the source tree')
271
# --no-circular-check disables the check for circular relationships between
272
# .gyp files. These relationships should not exist, but they've only been
273
# observed to be harmful with the Xcode generator. Chromium's .gyp files
274
# currently have some circular relationships on non-Mac platforms, so this
275
# option allows the strict behavior to be used on Macs and the lenient
276
# behavior to be used elsewhere.
277
# TODO(mark): Remove this option when http://crbug.com/35878 is fixed.
278
parser.add_option('--no-circular-check', dest='circular_check',
279
action='store_false', default=True, regenerate=False,
280
help="don't check for circular relationships between files")
282
# We read a few things from ~/.gyp, so set up a var for that.
284
if sys.platform in ('cygwin', 'win32'):
285
home_vars.append('USERPROFILE')
288
for home_var in home_vars:
289
home = os.getenv(home_var)
291
home_dot_gyp = os.path.join(home, '.gyp')
292
if not os.path.exists(home_dot_gyp):
297
# TODO(thomasvl): add support for ~/.gyp/defaults
299
options, build_files_arg = parser.parse_args(args)
300
build_files = build_files_arg
302
if not options.formats:
303
# If no format was given on the command line, then check the env variable.
304
generate_formats = []
305
if options.use_environment:
306
generate_formats = os.environ.get('GYP_GENERATORS', [])
308
generate_formats = re.split('[\s,]', generate_formats)
310
options.formats = generate_formats
312
# Nothing in the variable, default based on platform.
313
options.formats = [ {'darwin': 'xcode',
320
'sunos5': 'make',}[sys.platform] ]
322
if not options.generator_output and options.use_environment:
323
g_o = os.environ.get('GYP_GENERATOR_OUTPUT')
325
options.generator_output = g_o
327
for mode in options.debug:
330
# Do an extra check to avoid work when we're not debugging.
331
if DEBUG_GENERAL in gyp.debug.keys():
332
DebugOutput(DEBUG_GENERAL, 'running with these options:')
333
for option, value in sorted(options.__dict__.items()):
336
if isinstance(value, basestring):
337
DebugOutput(DEBUG_GENERAL, " %s: '%s'" % (option, value))
339
DebugOutput(DEBUG_GENERAL, " %s: %s" % (option, str(value)))
342
build_files = FindBuildFiles()
344
print >>sys.stderr, (usage + '\n\n%s: error: no build_file') % \
348
# TODO(mark): Chromium-specific hack!
349
# For Chromium, the gyp "depth" variable should always be a relative path
350
# to Chromium's top-level "src" directory. If no depth variable was set
351
# on the command line, try to find a "src" directory by looking at the
352
# absolute path to each build file's directory. The first "src" component
353
# found will be treated as though it were the path used for --depth.
354
if not options.depth:
355
for build_file in build_files:
356
build_file_dir = os.path.abspath(os.path.dirname(build_file))
357
build_file_dir_components = build_file_dir.split(os.path.sep)
358
components_len = len(build_file_dir_components)
359
for index in xrange(components_len - 1, -1, -1):
360
if build_file_dir_components[index] == 'src':
361
options.depth = os.path.sep.join(build_file_dir_components)
363
del build_file_dir_components[index]
365
# If the inner loop found something, break without advancing to another
370
if not options.depth:
372
'Could not automatically locate src directory. This is a ' + \
373
'temporary Chromium feature that will be removed. Use ' + \
374
'--depth as a workaround.'
376
# If toplevel-dir is not set, we assume that depth is the root of our source
378
if not options.toplevel_dir:
379
options.toplevel_dir = options.depth
381
# -D on the command line sets variable defaults - D isn't just for define,
382
# it's for default. Perhaps there should be a way to force (-F?) a
383
# variable's value so that it can't be overridden by anything else.
384
cmdline_default_variables = {}
386
if options.use_environment:
387
defines += ShlexEnv('GYP_DEFINES')
389
defines += options.defines
390
cmdline_default_variables = NameValueListToDict(defines)
391
if DEBUG_GENERAL in gyp.debug.keys():
392
DebugOutput(DEBUG_GENERAL,
393
"cmdline_default_variables: %s" % cmdline_default_variables)
398
# If ~/.gyp/include.gypi exists, it'll be forcibly included into every
399
# .gyp file that's loaded, before anything else is included.
400
if home_dot_gyp != None:
401
default_include = os.path.join(home_dot_gyp, 'include.gypi')
402
if os.path.exists(default_include):
403
includes.append(default_include)
405
# Command-line --include files come after the default include.
407
includes.extend(options.includes)
409
# Generator flags should be prefixed with the target generator since they
410
# are global across all generator runs.
412
if options.use_environment:
413
gen_flags += ShlexEnv('GYP_GENERATOR_FLAGS')
414
if options.generator_flags:
415
gen_flags += options.generator_flags
416
generator_flags = NameValueListToDict(gen_flags)
417
if DEBUG_GENERAL in gyp.debug.keys():
418
DebugOutput(DEBUG_GENERAL, "generator_flags: %s" % generator_flags)
420
# TODO: Remove this and the option after we've gotten folks to move to the
422
if options.msvs_version:
423
print >>sys.stderr, \
424
'DEPRECATED: Use generator flag (-G msvs_version=' + \
425
options.msvs_version + ') instead of --msvs-version=' + \
427
generator_flags['msvs_version'] = options.msvs_version
429
# Generate all requested formats (use a set in case we got one format request
431
for format in set(options.formats):
432
params = {'options': options,
433
'build_files': build_files,
434
'generator_flags': generator_flags,
436
'build_files_arg': build_files_arg,
437
'gyp_binary': sys.argv[0],
438
'home_dot_gyp': home_dot_gyp}
440
# Start with the default variables from the command line.
441
[generator, flat_list, targets, data] = Load(build_files, format,
442
cmdline_default_variables,
443
includes, options.depth,
444
params, options.check,
445
options.circular_check)
447
# TODO(mark): Pass |data| for now because the generator needs a list of
448
# build files that came in. In the future, maybe it should just accept
449
# a list, and not the whole data dict.
450
# NOTE: flat_list is the flattened dependency graph specifying the order
451
# that targets may be built. Build systems that operate serially or that
452
# need to have dependencies defined before dependents reference them should
453
# generate targets in the order specified in flat_list.
454
generator.GenerateOutput(flat_list, targets, data, params)
460
if __name__ == '__main__':
461
sys.exit(main(sys.argv[1:]))