3 # Copyright (c) 2012 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 from gyp.common import GypError
17 # Default debug modes for GYP
20 # List of "official" debug modes, but you can use anything you like.
21 DEBUG_GENERAL = 'general'
22 DEBUG_VARIABLES = 'variables'
23 DEBUG_INCLUDES = 'includes'
26 def DebugOutput(mode, message, *args):
27 if 'all' in gyp.debug or mode in gyp.debug:
28 ctx = ('unknown', 0, 'unknown')
30 f = traceback.extract_stack(limit=2)
37 print '%s:%s:%d:%s %s' % (mode.upper(), os.path.basename(ctx[0]),
38 ctx[1], ctx[2], message)
42 files = os.listdir(os.getcwd())
45 if file.endswith(extension):
46 build_files.append(file)
50 def Load(build_files, format, default_variables={},
51 includes=[], depth='.', params=None, check=False,
52 circular_check=True, duplicate_basename_check=True):
54 Loads one or more specified build files.
55 default_variables and includes will be copied before use.
56 Returns the generator for the specified format and the
57 data returned by loading the specified build files.
63 format, params['flavor'] = format.split('-', 1)
65 default_variables = copy.copy(default_variables)
67 # Default variables provided by this program and its modules should be
68 # named WITH_CAPITAL_LETTERS to provide a distinct "best practice" namespace,
69 # avoiding collisions with user and automatic variables.
70 default_variables['GENERATOR'] = format
71 default_variables['GENERATOR_FLAVOR'] = params.get('flavor', '')
73 # Format can be a custom python file, or by default the name of a module
74 # within gyp.generator.
75 if format.endswith('.py'):
76 generator_name = os.path.splitext(format)[0]
77 path, generator_name = os.path.split(generator_name)
79 # Make sure the path to the custom generator is in sys.path
80 # Don't worry about removing it once we are done. Keeping the path
81 # to each generator that is used in sys.path is likely harmless and
82 # arguably a good idea.
83 path = os.path.abspath(path)
84 if path not in sys.path:
85 sys.path.insert(0, path)
87 generator_name = 'gyp.generator.' + format
89 # These parameters are passed in order (as opposed to by key)
90 # because ActivePython cannot handle key parameters to __import__.
91 generator = __import__(generator_name, globals(), locals(), generator_name)
92 for (key, val) in generator.generator_default_variables.items():
93 default_variables.setdefault(key, val)
95 # Give the generator the opportunity to set additional variables based on
96 # the params it will receive in the output phase.
97 if getattr(generator, 'CalculateVariables', None):
98 generator.CalculateVariables(default_variables, params)
100 # Give the generator the opportunity to set generator_input_info based on
101 # the params it will receive in the output phase.
102 if getattr(generator, 'CalculateGeneratorInputInfo', None):
103 generator.CalculateGeneratorInputInfo(params)
105 # Fetch the generator specific info that gets fed to input, we use getattr
106 # so we can default things and the generators only have to provide what
108 generator_input_info = {
109 'non_configuration_keys':
110 getattr(generator, 'generator_additional_non_configuration_keys', []),
112 getattr(generator, 'generator_additional_path_sections', []),
113 'extra_sources_for_rules':
114 getattr(generator, 'generator_extra_sources_for_rules', []),
115 'generator_supports_multiple_toolsets':
116 getattr(generator, 'generator_supports_multiple_toolsets', False),
117 'generator_wants_static_library_dependencies_adjusted':
119 'generator_wants_static_library_dependencies_adjusted', True),
120 'generator_wants_sorted_dependencies':
121 getattr(generator, 'generator_wants_sorted_dependencies', False),
122 'generator_filelist_paths':
123 getattr(generator, 'generator_filelist_paths', None),
126 # Process the input specific to this generator.
127 result = gyp.input.Load(build_files, default_variables, includes[:],
128 depth, generator_input_info, check, circular_check,
129 duplicate_basename_check,
130 params['parallel'], params['root_targets'])
131 return [generator] + result
133 def NameValueListToDict(name_value_list):
135 Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
136 of the pairs. If a string is simply NAME, then the value in the dictionary
137 is set to True. If VALUE can be converted to an integer, it is.
140 for item in name_value_list:
141 tokens = item.split('=', 1)
143 # If we can make it an int, use that, otherwise, use the string.
145 token_value = int(tokens[1])
147 token_value = tokens[1]
148 # Set the variable to the supplied value.
149 result[tokens[0]] = token_value
151 # No value supplied, treat it as a boolean and set it.
152 result[tokens[0]] = True
155 def ShlexEnv(env_name):
156 flags = os.environ.get(env_name, [])
158 flags = shlex.split(flags)
161 def FormatOpt(opt, value):
162 if opt.startswith('--'):
163 return '%s=%s' % (opt, value)
166 def RegenerateAppendFlag(flag, values, predicate, env_name, options):
167 """Regenerate a list of command line flags, for an option of action='append'.
169 The |env_name|, if given, is checked in the environment and used to generate
170 an initial list of options, then the options that were specified on the
171 command line (given in |values|) are appended. This matches the handling of
172 environment variables and command line flags where command line flags override
173 the environment, while not requiring the environment to be set when the flags
177 if options.use_environment and env_name:
178 for flag_value in ShlexEnv(env_name):
179 value = FormatOpt(flag, predicate(flag_value))
184 for flag_value in values:
185 flags.append(FormatOpt(flag, predicate(flag_value)))
188 def RegenerateFlags(options):
189 """Given a parsed options object, and taking the environment variables into
190 account, returns a list of flags that should regenerate an equivalent options
191 object (even in the absence of the environment variables.)
193 Any path options will be normalized relative to depth.
195 The format flag is not included, as it is assumed the calling generator will
196 set that as appropriate.
199 path = gyp.common.FixIfRelativePath(path, options.depth)
201 return os.path.curdir
207 # We always want to ignore the environment when regenerating, to avoid
208 # duplicate or changed flags in the environment at the time of regeneration.
209 flags = ['--ignore-environment']
210 for name, metadata in options._regeneration_metadata.iteritems():
211 opt = metadata['opt']
212 value = getattr(options, name)
213 value_predicate = metadata['type'] == 'path' and FixPath or Noop
214 action = metadata['action']
215 env_name = metadata['env_name']
216 if action == 'append':
217 flags.extend(RegenerateAppendFlag(opt, value, value_predicate,
219 elif action in ('store', None): # None is a synonym for 'store'.
221 flags.append(FormatOpt(opt, value_predicate(value)))
222 elif options.use_environment and env_name and os.environ.get(env_name):
223 flags.append(FormatOpt(opt, value_predicate(os.environ.get(env_name))))
224 elif action in ('store_true', 'store_false'):
225 if ((action == 'store_true' and value) or
226 (action == 'store_false' and not value)):
228 elif options.use_environment and env_name:
229 print >>sys.stderr, ('Warning: environment regeneration unimplemented '
230 'for %s flag %r env_name %r' % (action, opt,
233 print >>sys.stderr, ('Warning: regeneration unimplemented for action %r '
234 'flag %r' % (action, opt))
238 class RegeneratableOptionParser(optparse.OptionParser):
240 self.__regeneratable_options = {}
241 optparse.OptionParser.__init__(self)
243 def add_option(self, *args, **kw):
244 """Add an option to the parser.
246 This accepts the same arguments as OptionParser.add_option, plus the
248 regenerate: can be set to False to prevent this option from being included
250 env_name: name of environment variable that additional values for this
252 type: adds type='path', to tell the regenerator that the values of
253 this option need to be made relative to options.depth
255 env_name = kw.pop('env_name', None)
256 if 'dest' in kw and kw.pop('regenerate', True):
259 # The path type is needed for regenerating, for optparse we can just treat
261 type = kw.get('type')
263 kw['type'] = 'string'
265 self.__regeneratable_options[dest] = {
266 'action': kw.get('action'),
268 'env_name': env_name,
272 optparse.OptionParser.add_option(self, *args, **kw)
274 def parse_args(self, *args):
275 values, args = optparse.OptionParser.parse_args(self, *args)
276 values._regeneration_metadata = self.__regeneratable_options
280 my_name = os.path.basename(sys.argv[0])
282 parser = RegeneratableOptionParser()
283 usage = 'usage: %s [options ...] [build_file ...]'
284 parser.set_usage(usage.replace('%s', '%prog'))
285 parser.add_option('--build', dest='configs', action='append',
286 help='configuration for build after project generation')
287 parser.add_option('--check', dest='check', action='store_true',
288 help='check format of gyp files')
289 parser.add_option('--config-dir', dest='config_dir', action='store',
290 env_name='GYP_CONFIG_DIR', default=None,
291 help='The location for configuration files like '
293 parser.add_option('-d', '--debug', dest='debug', metavar='DEBUGMODE',
294 action='append', default=[], help='turn on a debugging '
295 'mode for debugging GYP. Supported modes are "variables", '
296 '"includes" and "general" or "all" for all of them.')
297 parser.add_option('-D', dest='defines', action='append', metavar='VAR=VAL',
298 env_name='GYP_DEFINES',
299 help='sets variable VAR to value VAL')
300 parser.add_option('--depth', dest='depth', metavar='PATH', type='path',
301 help='set DEPTH gyp variable to a relative path to PATH')
302 parser.add_option('-f', '--format', dest='formats', action='append',
303 env_name='GYP_GENERATORS', regenerate=False,
304 help='output formats to generate')
305 parser.add_option('-G', dest='generator_flags', action='append', default=[],
306 metavar='FLAG=VAL', env_name='GYP_GENERATOR_FLAGS',
307 help='sets generator flag FLAG to VAL')
308 parser.add_option('--generator-output', dest='generator_output',
309 action='store', default=None, metavar='DIR', type='path',
310 env_name='GYP_GENERATOR_OUTPUT',
311 help='puts generated build files under DIR')
312 parser.add_option('--ignore-environment', dest='use_environment',
313 action='store_false', default=True, regenerate=False,
314 help='do not read options from environment variables')
315 parser.add_option('-I', '--include', dest='includes', action='append',
316 metavar='INCLUDE', type='path',
317 help='files to include in all loaded .gyp files')
318 # --no-circular-check disables the check for circular relationships between
319 # .gyp files. These relationships should not exist, but they've only been
320 # observed to be harmful with the Xcode generator. Chromium's .gyp files
321 # currently have some circular relationships on non-Mac platforms, so this
322 # option allows the strict behavior to be used on Macs and the lenient
323 # behavior to be used elsewhere.
324 # TODO(mark): Remove this option when http://crbug.com/35878 is fixed.
325 parser.add_option('--no-circular-check', dest='circular_check',
326 action='store_false', default=True, regenerate=False,
327 help="don't check for circular relationships between files")
328 # --no-duplicate-basename-check disables the check for duplicate basenames
329 # in a static_library/shared_library project. Visual C++ 2008 generator
330 # doesn't support this configuration. Libtool on Mac also generates warnings
331 # when duplicate basenames are passed into Make generator on Mac.
332 # TODO(yukawa): Remove this option when these legacy generators are
334 parser.add_option('--no-duplicate-basename-check',
335 dest='duplicate_basename_check', action='store_false',
336 default=True, regenerate=False,
337 help="don't check for duplicate basenames")
338 parser.add_option('--no-parallel', action='store_true', default=False,
339 help='Disable multiprocessing')
340 parser.add_option('-S', '--suffix', dest='suffix', default='',
341 help='suffix to add to generated files')
342 parser.add_option('--toplevel-dir', dest='toplevel_dir', action='store',
343 default=None, metavar='DIR', type='path',
344 help='directory to use as the root of the source tree')
345 parser.add_option('-R', '--root-target', dest='root_targets',
346 action='append', metavar='TARGET',
347 help='include only TARGET and its deep dependencies')
349 options, build_files_arg = parser.parse_args(args)
350 build_files = build_files_arg
352 # Set up the configuration directory (defaults to ~/.gyp)
353 if not options.config_dir:
356 if options.use_environment:
357 home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None)
359 home_dot_gyp = os.path.expanduser(home_dot_gyp)
363 if sys.platform in ('cygwin', 'win32'):
364 home_vars.append('USERPROFILE')
365 for home_var in home_vars:
366 home = os.getenv(home_var)
368 home_dot_gyp = os.path.join(home, '.gyp')
369 if not os.path.exists(home_dot_gyp):
374 home_dot_gyp = os.path.expanduser(options.config_dir)
376 if home_dot_gyp and not os.path.exists(home_dot_gyp):
379 if not options.formats:
380 # If no format was given on the command line, then check the env variable.
381 generate_formats = []
382 if options.use_environment:
383 generate_formats = os.environ.get('GYP_GENERATORS', [])
385 generate_formats = re.split(r'[\s,]', generate_formats)
387 options.formats = generate_formats
389 # Nothing in the variable, default based on platform.
390 if sys.platform == 'darwin':
391 options.formats = ['xcode']
392 elif sys.platform in ('win32', 'cygwin'):
393 options.formats = ['msvs']
395 options.formats = ['make']
397 if not options.generator_output and options.use_environment:
398 g_o = os.environ.get('GYP_GENERATOR_OUTPUT')
400 options.generator_output = g_o
402 options.parallel = not options.no_parallel
404 for mode in options.debug:
407 # Do an extra check to avoid work when we're not debugging.
408 if DEBUG_GENERAL in gyp.debug:
409 DebugOutput(DEBUG_GENERAL, 'running with these options:')
410 for option, value in sorted(options.__dict__.items()):
413 if isinstance(value, basestring):
414 DebugOutput(DEBUG_GENERAL, " %s: '%s'", option, value)
416 DebugOutput(DEBUG_GENERAL, " %s: %s", option, value)
419 build_files = FindBuildFiles()
421 raise GypError((usage + '\n\n%s: error: no build_file') %
424 # TODO(mark): Chromium-specific hack!
425 # For Chromium, the gyp "depth" variable should always be a relative path
426 # to Chromium's top-level "src" directory. If no depth variable was set
427 # on the command line, try to find a "src" directory by looking at the
428 # absolute path to each build file's directory. The first "src" component
429 # found will be treated as though it were the path used for --depth.
430 if not options.depth:
431 for build_file in build_files:
432 build_file_dir = os.path.abspath(os.path.dirname(build_file))
433 build_file_dir_components = build_file_dir.split(os.path.sep)
434 components_len = len(build_file_dir_components)
435 for index in xrange(components_len - 1, -1, -1):
436 if build_file_dir_components[index] == 'src':
437 options.depth = os.path.sep.join(build_file_dir_components)
439 del build_file_dir_components[index]
441 # If the inner loop found something, break without advancing to another
446 if not options.depth:
447 raise GypError('Could not automatically locate src directory. This is'
448 'a temporary Chromium feature that will be removed. Use'
449 '--depth as a workaround.')
451 # If toplevel-dir is not set, we assume that depth is the root of our source
453 if not options.toplevel_dir:
454 options.toplevel_dir = options.depth
456 # -D on the command line sets variable defaults - D isn't just for define,
457 # it's for default. Perhaps there should be a way to force (-F?) a
458 # variable's value so that it can't be overridden by anything else.
459 cmdline_default_variables = {}
461 if options.use_environment:
462 defines += ShlexEnv('GYP_DEFINES')
464 defines += options.defines
465 cmdline_default_variables = NameValueListToDict(defines)
466 if DEBUG_GENERAL in gyp.debug:
467 DebugOutput(DEBUG_GENERAL,
468 "cmdline_default_variables: %s", cmdline_default_variables)
473 # If ~/.gyp/include.gypi exists, it'll be forcibly included into every
474 # .gyp file that's loaded, before anything else is included.
475 if home_dot_gyp != None:
476 default_include = os.path.join(home_dot_gyp, 'include.gypi')
477 if os.path.exists(default_include):
478 print 'Using overrides found in ' + default_include
479 includes.append(default_include)
481 # Command-line --include files come after the default include.
483 includes.extend(options.includes)
485 # Generator flags should be prefixed with the target generator since they
486 # are global across all generator runs.
488 if options.use_environment:
489 gen_flags += ShlexEnv('GYP_GENERATOR_FLAGS')
490 if options.generator_flags:
491 gen_flags += options.generator_flags
492 generator_flags = NameValueListToDict(gen_flags)
493 if DEBUG_GENERAL in gyp.debug.keys():
494 DebugOutput(DEBUG_GENERAL, "generator_flags: %s", generator_flags)
496 # Generate all requested formats (use a set in case we got one format request
498 for format in set(options.formats):
499 params = {'options': options,
500 'build_files': build_files,
501 'generator_flags': generator_flags,
503 'build_files_arg': build_files_arg,
504 'gyp_binary': sys.argv[0],
505 'home_dot_gyp': home_dot_gyp,
506 'parallel': options.parallel,
507 'root_targets': options.root_targets,
508 'target_arch': cmdline_default_variables.get('target_arch', '')}
510 # Start with the default variables from the command line.
511 [generator, flat_list, targets, data] = Load(
512 build_files, format, cmdline_default_variables, includes, options.depth,
513 params, options.check, options.circular_check,
514 options.duplicate_basename_check)
516 # TODO(mark): Pass |data| for now because the generator needs a list of
517 # build files that came in. In the future, maybe it should just accept
518 # a list, and not the whole data dict.
519 # NOTE: flat_list is the flattened dependency graph specifying the order
520 # that targets may be built. Build systems that operate serially or that
521 # need to have dependencies defined before dependents reference them should
522 # generate targets in the order specified in flat_list.
523 generator.GenerateOutput(flat_list, targets, data, params)
526 valid_configs = targets[flat_list[0]]['configurations'].keys()
527 for conf in options.configs:
528 if conf not in valid_configs:
529 raise GypError('Invalid config specified via --build: %s' % conf)
530 generator.PerformBuild(data, options.configs, params)
538 return gyp_main(args)
540 sys.stderr.write("gyp: %s\n" % e)
543 # NOTE: setuptools generated console_scripts calls function with no arguments
545 return main(sys.argv[1:])
547 if __name__ == '__main__':
548 sys.exit(script_main())