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