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:]))