Initial commit
[yaffs-website] / node_modules / node-gyp / gyp / pylib / gyp / __init__.py
1 #!/usr/bin/env python
2
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.
6
7 import copy
8 import gyp.input
9 import optparse
10 import os.path
11 import re
12 import shlex
13 import sys
14 import traceback
15 from gyp.common import GypError
16
17 # Default debug modes for GYP
18 debug = {}
19
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'
24
25
26 def DebugOutput(mode, message, *args):
27   if 'all' in gyp.debug or mode in gyp.debug:
28     ctx = ('unknown', 0, 'unknown')
29     try:
30       f = traceback.extract_stack(limit=2)
31       if f:
32         ctx = f[0][:3]
33     except:
34       pass
35     if args:
36       message %= args
37     print '%s:%s:%d:%s %s' % (mode.upper(), os.path.basename(ctx[0]),
38                               ctx[1], ctx[2], message)
39
40 def FindBuildFiles():
41   extension = '.gyp'
42   files = os.listdir(os.getcwd())
43   build_files = []
44   for file in files:
45     if file.endswith(extension):
46       build_files.append(file)
47   return build_files
48
49
50 def Load(build_files, format, default_variables={},
51          includes=[], depth='.', params=None, check=False,
52          circular_check=True, duplicate_basename_check=True):
53   """
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.
58   """
59   if params is None:
60     params = {}
61
62   if '-' in format:
63     format, params['flavor'] = format.split('-', 1)
64
65   default_variables = copy.copy(default_variables)
66
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', '')
72
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)
78
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)
86   else:
87     generator_name = 'gyp.generator.' + format
88
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)
94
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)
99
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)
104
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
107   # they need.
108   generator_input_info = {
109     'non_configuration_keys':
110         getattr(generator, 'generator_additional_non_configuration_keys', []),
111     'path_sections':
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':
118         getattr(generator,
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),
124   }
125
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
132
133 def NameValueListToDict(name_value_list):
134   """
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.
138   """
139   result = { }
140   for item in name_value_list:
141     tokens = item.split('=', 1)
142     if len(tokens) == 2:
143       # If we can make it an int, use that, otherwise, use the string.
144       try:
145         token_value = int(tokens[1])
146       except ValueError:
147         token_value = tokens[1]
148       # Set the variable to the supplied value.
149       result[tokens[0]] = token_value
150     else:
151       # No value supplied, treat it as a boolean and set it.
152       result[tokens[0]] = True
153   return result
154
155 def ShlexEnv(env_name):
156   flags = os.environ.get(env_name, [])
157   if flags:
158     flags = shlex.split(flags)
159   return flags
160
161 def FormatOpt(opt, value):
162   if opt.startswith('--'):
163     return '%s=%s' % (opt, value)
164   return opt + value
165
166 def RegenerateAppendFlag(flag, values, predicate, env_name, options):
167   """Regenerate a list of command line flags, for an option of action='append'.
168
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
174   are used again.
175   """
176   flags = []
177   if options.use_environment and env_name:
178     for flag_value in ShlexEnv(env_name):
179       value = FormatOpt(flag, predicate(flag_value))
180       if value in flags:
181         flags.remove(value)
182       flags.append(value)
183   if values:
184     for flag_value in values:
185       flags.append(FormatOpt(flag, predicate(flag_value)))
186   return flags
187
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.)
192
193   Any path options will be normalized relative to depth.
194
195   The format flag is not included, as it is assumed the calling generator will
196   set that as appropriate.
197   """
198   def FixPath(path):
199     path = gyp.common.FixIfRelativePath(path, options.depth)
200     if not path:
201       return os.path.curdir
202     return path
203
204   def Noop(value):
205     return value
206
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,
218                                         env_name, options))
219     elif action in ('store', None):  # None is a synonym for 'store'.
220       if value:
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)):
227         flags.append(opt)
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,
231                                                              env_name))
232     else:
233       print >>sys.stderr, ('Warning: regeneration unimplemented for action %r '
234                            'flag %r' % (action, opt))
235
236   return flags
237
238 class RegeneratableOptionParser(optparse.OptionParser):
239   def __init__(self):
240     self.__regeneratable_options = {}
241     optparse.OptionParser.__init__(self)
242
243   def add_option(self, *args, **kw):
244     """Add an option to the parser.
245
246     This accepts the same arguments as OptionParser.add_option, plus the
247     following:
248       regenerate: can be set to False to prevent this option from being included
249                   in regeneration.
250       env_name: name of environment variable that additional values for this
251                 option come from.
252       type: adds type='path', to tell the regenerator that the values of
253             this option need to be made relative to options.depth
254     """
255     env_name = kw.pop('env_name', None)
256     if 'dest' in kw and kw.pop('regenerate', True):
257       dest = kw['dest']
258
259       # The path type is needed for regenerating, for optparse we can just treat
260       # it as a string.
261       type = kw.get('type')
262       if type == 'path':
263         kw['type'] = 'string'
264
265       self.__regeneratable_options[dest] = {
266           'action': kw.get('action'),
267           'type': type,
268           'env_name': env_name,
269           'opt': args[0],
270         }
271
272     optparse.OptionParser.add_option(self, *args, **kw)
273
274   def parse_args(self, *args):
275     values, args = optparse.OptionParser.parse_args(self, *args)
276     values._regeneration_metadata = self.__regeneratable_options
277     return values, args
278
279 def gyp_main(args):
280   my_name = os.path.basename(sys.argv[0])
281
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 '
292                     'include.gypi.')
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
333   # deprecated.
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')
348
349   options, build_files_arg = parser.parse_args(args)
350   build_files = build_files_arg
351
352   # Set up the configuration directory (defaults to ~/.gyp)
353   if not options.config_dir:
354     home = None
355     home_dot_gyp = None
356     if options.use_environment:
357       home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None)
358       if home_dot_gyp:
359         home_dot_gyp = os.path.expanduser(home_dot_gyp)
360
361     if not home_dot_gyp:
362       home_vars = ['HOME']
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)
367         if home != None:
368           home_dot_gyp = os.path.join(home, '.gyp')
369           if not os.path.exists(home_dot_gyp):
370             home_dot_gyp = None
371           else:
372             break
373   else:
374     home_dot_gyp = os.path.expanduser(options.config_dir)
375
376   if home_dot_gyp and not os.path.exists(home_dot_gyp):
377     home_dot_gyp = None
378
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', [])
384     if generate_formats:
385       generate_formats = re.split(r'[\s,]', generate_formats)
386     if generate_formats:
387       options.formats = generate_formats
388     else:
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']
394       else:
395         options.formats = ['make']
396
397   if not options.generator_output and options.use_environment:
398     g_o = os.environ.get('GYP_GENERATOR_OUTPUT')
399     if g_o:
400       options.generator_output = g_o
401
402   options.parallel = not options.no_parallel
403
404   for mode in options.debug:
405     gyp.debug[mode] = 1
406
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()):
411       if option[0] == '_':
412         continue
413       if isinstance(value, basestring):
414         DebugOutput(DEBUG_GENERAL, "  %s: '%s'", option, value)
415       else:
416         DebugOutput(DEBUG_GENERAL, "  %s: %s", option, value)
417
418   if not build_files:
419     build_files = FindBuildFiles()
420   if not build_files:
421     raise GypError((usage + '\n\n%s: error: no build_file') %
422                    (my_name, my_name))
423
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)
438           break
439         del build_file_dir_components[index]
440
441       # If the inner loop found something, break without advancing to another
442       # build file.
443       if options.depth:
444         break
445
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.')
450
451   # If toplevel-dir is not set, we assume that depth is the root of our source
452   # tree.
453   if not options.toplevel_dir:
454     options.toplevel_dir = options.depth
455
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 = {}
460   defines = []
461   if options.use_environment:
462     defines += ShlexEnv('GYP_DEFINES')
463   if options.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)
469
470   # Set up includes.
471   includes = []
472
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)
480
481   # Command-line --include files come after the default include.
482   if options.includes:
483     includes.extend(options.includes)
484
485   # Generator flags should be prefixed with the target generator since they
486   # are global across all generator runs.
487   gen_flags = []
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)
495
496   # Generate all requested formats (use a set in case we got one format request
497   # twice)
498   for format in set(options.formats):
499     params = {'options': options,
500               'build_files': build_files,
501               'generator_flags': generator_flags,
502               'cwd': os.getcwd(),
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', '')}
509
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)
515
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)
524
525     if options.configs:
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)
531
532   # Done
533   return 0
534
535
536 def main(args):
537   try:
538     return gyp_main(args)
539   except GypError, e:
540     sys.stderr.write("gyp: %s\n" % e)
541     return 1
542
543 # NOTE: setuptools generated console_scripts calls function with no arguments
544 def script_main():
545   return main(sys.argv[1:])
546
547 if __name__ == '__main__':
548   sys.exit(script_main())