Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / tools / 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):
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   flavor = None
63   if '-' in format:
64     format, params['flavor'] = format.split('-', 1)
65
66   default_variables = copy.copy(default_variables)
67
68   # Default variables provided by this program and its modules should be
69   # named WITH_CAPITAL_LETTERS to provide a distinct "best practice" namespace,
70   # avoiding collisions with user and automatic variables.
71   default_variables['GENERATOR'] = format
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                           params['parallel'], params['root_targets'])
130   return [generator] + result
131
132 def NameValueListToDict(name_value_list):
133   """
134   Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
135   of the pairs.  If a string is simply NAME, then the value in the dictionary
136   is set to True.  If VALUE can be converted to an integer, it is.
137   """
138   result = { }
139   for item in name_value_list:
140     tokens = item.split('=', 1)
141     if len(tokens) == 2:
142       # If we can make it an int, use that, otherwise, use the string.
143       try:
144         token_value = int(tokens[1])
145       except ValueError:
146         token_value = tokens[1]
147       # Set the variable to the supplied value.
148       result[tokens[0]] = token_value
149     else:
150       # No value supplied, treat it as a boolean and set it.
151       result[tokens[0]] = True
152   return result
153
154 def ShlexEnv(env_name):
155   flags = os.environ.get(env_name, [])
156   if flags:
157     flags = shlex.split(flags)
158   return flags
159
160 def FormatOpt(opt, value):
161   if opt.startswith('--'):
162     return '%s=%s' % (opt, value)
163   return opt + value
164
165 def RegenerateAppendFlag(flag, values, predicate, env_name, options):
166   """Regenerate a list of command line flags, for an option of action='append'.
167
168   The |env_name|, if given, is checked in the environment and used to generate
169   an initial list of options, then the options that were specified on the
170   command line (given in |values|) are appended.  This matches the handling of
171   environment variables and command line flags where command line flags override
172   the environment, while not requiring the environment to be set when the flags
173   are used again.
174   """
175   flags = []
176   if options.use_environment and env_name:
177     for flag_value in ShlexEnv(env_name):
178       value = FormatOpt(flag, predicate(flag_value))
179       if value in flags:
180         flags.remove(value)
181       flags.append(value)
182   if values:
183     for flag_value in values:
184       flags.append(FormatOpt(flag, predicate(flag_value)))
185   return flags
186
187 def RegenerateFlags(options):
188   """Given a parsed options object, and taking the environment variables into
189   account, returns a list of flags that should regenerate an equivalent options
190   object (even in the absence of the environment variables.)
191
192   Any path options will be normalized relative to depth.
193
194   The format flag is not included, as it is assumed the calling generator will
195   set that as appropriate.
196   """
197   def FixPath(path):
198     path = gyp.common.FixIfRelativePath(path, options.depth)
199     if not path:
200       return os.path.curdir
201     return path
202
203   def Noop(value):
204     return value
205
206   # We always want to ignore the environment when regenerating, to avoid
207   # duplicate or changed flags in the environment at the time of regeneration.
208   flags = ['--ignore-environment']
209   for name, metadata in options._regeneration_metadata.iteritems():
210     opt = metadata['opt']
211     value = getattr(options, name)
212     value_predicate = metadata['type'] == 'path' and FixPath or Noop
213     action = metadata['action']
214     env_name = metadata['env_name']
215     if action == 'append':
216       flags.extend(RegenerateAppendFlag(opt, value, value_predicate,
217                                         env_name, options))
218     elif action in ('store', None):  # None is a synonym for 'store'.
219       if value:
220         flags.append(FormatOpt(opt, value_predicate(value)))
221       elif options.use_environment and env_name and os.environ.get(env_name):
222         flags.append(FormatOpt(opt, value_predicate(os.environ.get(env_name))))
223     elif action in ('store_true', 'store_false'):
224       if ((action == 'store_true' and value) or
225           (action == 'store_false' and not value)):
226         flags.append(opt)
227       elif options.use_environment and env_name:
228         print >>sys.stderr, ('Warning: environment regeneration unimplemented '
229                              'for %s flag %r env_name %r' % (action, opt,
230                                                              env_name))
231     else:
232       print >>sys.stderr, ('Warning: regeneration unimplemented for action %r '
233                            'flag %r' % (action, opt))
234
235   return flags
236
237 class RegeneratableOptionParser(optparse.OptionParser):
238   def __init__(self):
239     self.__regeneratable_options = {}
240     optparse.OptionParser.__init__(self)
241
242   def add_option(self, *args, **kw):
243     """Add an option to the parser.
244
245     This accepts the same arguments as OptionParser.add_option, plus the
246     following:
247       regenerate: can be set to False to prevent this option from being included
248                   in regeneration.
249       env_name: name of environment variable that additional values for this
250                 option come from.
251       type: adds type='path', to tell the regenerator that the values of
252             this option need to be made relative to options.depth
253     """
254     env_name = kw.pop('env_name', None)
255     if 'dest' in kw and kw.pop('regenerate', True):
256       dest = kw['dest']
257
258       # The path type is needed for regenerating, for optparse we can just treat
259       # it as a string.
260       type = kw.get('type')
261       if type == 'path':
262         kw['type'] = 'string'
263
264       self.__regeneratable_options[dest] = {
265           'action': kw.get('action'),
266           'type': type,
267           'env_name': env_name,
268           'opt': args[0],
269         }
270
271     optparse.OptionParser.add_option(self, *args, **kw)
272
273   def parse_args(self, *args):
274     values, args = optparse.OptionParser.parse_args(self, *args)
275     values._regeneration_metadata = self.__regeneratable_options
276     return values, args
277
278 def gyp_main(args):
279   my_name = os.path.basename(sys.argv[0])
280
281   parser = RegeneratableOptionParser()
282   usage = 'usage: %s [options ...] [build_file ...]'
283   parser.set_usage(usage.replace('%s', '%prog'))
284   parser.add_option('--build', dest='configs', action='append',
285                     help='configuration for build after project generation')
286   parser.add_option('--check', dest='check', action='store_true',
287                     help='check format of gyp files')
288   parser.add_option('--config-dir', dest='config_dir', action='store',
289                     env_name='GYP_CONFIG_DIR', default=None,
290                     help='The location for configuration files like '
291                     'include.gypi.')
292   parser.add_option('-d', '--debug', dest='debug', metavar='DEBUGMODE',
293                     action='append', default=[], help='turn on a debugging '
294                     'mode for debugging GYP.  Supported modes are "variables", '
295                     '"includes" and "general" or "all" for all of them.')
296   parser.add_option('-D', dest='defines', action='append', metavar='VAR=VAL',
297                     env_name='GYP_DEFINES',
298                     help='sets variable VAR to value VAL')
299   parser.add_option('--depth', dest='depth', metavar='PATH', type='path',
300                     help='set DEPTH gyp variable to a relative path to PATH')
301   parser.add_option('-f', '--format', dest='formats', action='append',
302                     env_name='GYP_GENERATORS', regenerate=False,
303                     help='output formats to generate')
304   parser.add_option('-G', dest='generator_flags', action='append', default=[],
305                     metavar='FLAG=VAL', env_name='GYP_GENERATOR_FLAGS',
306                     help='sets generator flag FLAG to VAL')
307   parser.add_option('--generator-output', dest='generator_output',
308                     action='store', default=None, metavar='DIR', type='path',
309                     env_name='GYP_GENERATOR_OUTPUT',
310                     help='puts generated build files under DIR')
311   parser.add_option('--ignore-environment', dest='use_environment',
312                     action='store_false', default=True, regenerate=False,
313                     help='do not read options from environment variables')
314   parser.add_option('-I', '--include', dest='includes', action='append',
315                     metavar='INCLUDE', type='path',
316                     help='files to include in all loaded .gyp files')
317   # --no-circular-check disables the check for circular relationships between
318   # .gyp files.  These relationships should not exist, but they've only been
319   # observed to be harmful with the Xcode generator.  Chromium's .gyp files
320   # currently have some circular relationships on non-Mac platforms, so this
321   # option allows the strict behavior to be used on Macs and the lenient
322   # behavior to be used elsewhere.
323   # TODO(mark): Remove this option when http://crbug.com/35878 is fixed.
324   parser.add_option('--no-circular-check', dest='circular_check',
325                     action='store_false', default=True, regenerate=False,
326                     help="don't check for circular relationships between files")
327   parser.add_option('--no-parallel', action='store_true', default=False,
328                     help='Disable multiprocessing')
329   parser.add_option('-S', '--suffix', dest='suffix', default='',
330                     help='suffix to add to generated files')
331   parser.add_option('--toplevel-dir', dest='toplevel_dir', action='store',
332                     default=None, metavar='DIR', type='path',
333                     help='directory to use as the root of the source tree')
334   parser.add_option('-R', '--root-target', dest='root_targets',
335                     action='append', metavar='TARGET',
336                     help='include only TARGET and its deep dependencies')
337
338   options, build_files_arg = parser.parse_args(args)
339   build_files = build_files_arg
340
341   # Set up the configuration directory (defaults to ~/.gyp)
342   if not options.config_dir:
343     home = None
344     home_dot_gyp = None
345     if options.use_environment:
346       home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None)
347       if home_dot_gyp:
348         home_dot_gyp = os.path.expanduser(home_dot_gyp)
349
350     if not home_dot_gyp:
351       home_vars = ['HOME']
352       if sys.platform in ('cygwin', 'win32'):
353         home_vars.append('USERPROFILE')
354       for home_var in home_vars:
355         home = os.getenv(home_var)
356         if home != None:
357           home_dot_gyp = os.path.join(home, '.gyp')
358           if not os.path.exists(home_dot_gyp):
359             home_dot_gyp = None
360           else:
361             break
362   else:
363     home_dot_gyp = os.path.expanduser(options.config_dir)
364
365   if home_dot_gyp and not os.path.exists(home_dot_gyp):
366     home_dot_gyp = None
367
368   if not options.formats:
369     # If no format was given on the command line, then check the env variable.
370     generate_formats = []
371     if options.use_environment:
372       generate_formats = os.environ.get('GYP_GENERATORS', [])
373     if generate_formats:
374       generate_formats = re.split('[\s,]', generate_formats)
375     if generate_formats:
376       options.formats = generate_formats
377     else:
378       # Nothing in the variable, default based on platform.
379       if sys.platform == 'darwin':
380         options.formats = ['xcode']
381       elif sys.platform in ('win32', 'cygwin'):
382         options.formats = ['msvs']
383       else:
384         options.formats = ['make']
385
386   if not options.generator_output and options.use_environment:
387     g_o = os.environ.get('GYP_GENERATOR_OUTPUT')
388     if g_o:
389       options.generator_output = g_o
390
391   options.parallel = not options.no_parallel
392
393   for mode in options.debug:
394     gyp.debug[mode] = 1
395
396   # Do an extra check to avoid work when we're not debugging.
397   if DEBUG_GENERAL in gyp.debug:
398     DebugOutput(DEBUG_GENERAL, 'running with these options:')
399     for option, value in sorted(options.__dict__.items()):
400       if option[0] == '_':
401         continue
402       if isinstance(value, basestring):
403         DebugOutput(DEBUG_GENERAL, "  %s: '%s'", option, value)
404       else:
405         DebugOutput(DEBUG_GENERAL, "  %s: %s", option, value)
406
407   if not build_files:
408     build_files = FindBuildFiles()
409   if not build_files:
410     raise GypError((usage + '\n\n%s: error: no build_file') %
411                    (my_name, my_name))
412
413   # TODO(mark): Chromium-specific hack!
414   # For Chromium, the gyp "depth" variable should always be a relative path
415   # to Chromium's top-level "src" directory.  If no depth variable was set
416   # on the command line, try to find a "src" directory by looking at the
417   # absolute path to each build file's directory.  The first "src" component
418   # found will be treated as though it were the path used for --depth.
419   if not options.depth:
420     for build_file in build_files:
421       build_file_dir = os.path.abspath(os.path.dirname(build_file))
422       build_file_dir_components = build_file_dir.split(os.path.sep)
423       components_len = len(build_file_dir_components)
424       for index in xrange(components_len - 1, -1, -1):
425         if build_file_dir_components[index] == 'src':
426           options.depth = os.path.sep.join(build_file_dir_components)
427           break
428         del build_file_dir_components[index]
429
430       # If the inner loop found something, break without advancing to another
431       # build file.
432       if options.depth:
433         break
434
435     if not options.depth:
436       raise GypError('Could not automatically locate src directory.  This is'
437                      'a temporary Chromium feature that will be removed.  Use'
438                      '--depth as a workaround.')
439
440   # If toplevel-dir is not set, we assume that depth is the root of our source
441   # tree.
442   if not options.toplevel_dir:
443     options.toplevel_dir = options.depth
444
445   # -D on the command line sets variable defaults - D isn't just for define,
446   # it's for default.  Perhaps there should be a way to force (-F?) a
447   # variable's value so that it can't be overridden by anything else.
448   cmdline_default_variables = {}
449   defines = []
450   if options.use_environment:
451     defines += ShlexEnv('GYP_DEFINES')
452   if options.defines:
453     defines += options.defines
454   cmdline_default_variables = NameValueListToDict(defines)
455   if DEBUG_GENERAL in gyp.debug:
456     DebugOutput(DEBUG_GENERAL,
457                 "cmdline_default_variables: %s", cmdline_default_variables)
458
459   # Set up includes.
460   includes = []
461
462   # If ~/.gyp/include.gypi exists, it'll be forcibly included into every
463   # .gyp file that's loaded, before anything else is included.
464   if home_dot_gyp != None:
465     default_include = os.path.join(home_dot_gyp, 'include.gypi')
466     if os.path.exists(default_include):
467       print 'Using overrides found in ' + default_include
468       includes.append(default_include)
469
470   # Command-line --include files come after the default include.
471   if options.includes:
472     includes.extend(options.includes)
473
474   # Generator flags should be prefixed with the target generator since they
475   # are global across all generator runs.
476   gen_flags = []
477   if options.use_environment:
478     gen_flags += ShlexEnv('GYP_GENERATOR_FLAGS')
479   if options.generator_flags:
480     gen_flags += options.generator_flags
481   generator_flags = NameValueListToDict(gen_flags)
482   if DEBUG_GENERAL in gyp.debug.keys():
483     DebugOutput(DEBUG_GENERAL, "generator_flags: %s", generator_flags)
484
485   # Generate all requested formats (use a set in case we got one format request
486   # twice)
487   for format in set(options.formats):
488     params = {'options': options,
489               'build_files': build_files,
490               'generator_flags': generator_flags,
491               'cwd': os.getcwd(),
492               'build_files_arg': build_files_arg,
493               'gyp_binary': sys.argv[0],
494               'home_dot_gyp': home_dot_gyp,
495               'parallel': options.parallel,
496               'root_targets': options.root_targets}
497
498     # Start with the default variables from the command line.
499     [generator, flat_list, targets, data] = Load(
500         build_files, format, cmdline_default_variables, includes, options.depth,
501         params, options.check, options.circular_check)
502
503     # TODO(mark): Pass |data| for now because the generator needs a list of
504     # build files that came in.  In the future, maybe it should just accept
505     # a list, and not the whole data dict.
506     # NOTE: flat_list is the flattened dependency graph specifying the order
507     # that targets may be built.  Build systems that operate serially or that
508     # need to have dependencies defined before dependents reference them should
509     # generate targets in the order specified in flat_list.
510     generator.GenerateOutput(flat_list, targets, data, params)
511
512     if options.configs:
513       valid_configs = targets[flat_list[0]]['configurations'].keys()
514       for conf in options.configs:
515         if conf not in valid_configs:
516           raise GypError('Invalid config specified via --build: %s' % conf)
517       generator.PerformBuild(data, options.configs, params)
518
519   # Done
520   return 0
521
522
523 def main(args):
524   try:
525     return gyp_main(args)
526   except GypError, e:
527     sys.stderr.write("gyp: %s\n" % e)
528     return 1
529
530 # NOTE: setuptools generated console_scripts calls function with no arguments
531 def script_main():
532   return main(sys.argv[1:])
533
534 if __name__ == '__main__':
535   sys.exit(script_main())