initial import
[vuplus_webkit] / Source / ThirdParty / gyp / pylib / gyp / __init__.py
1 #!/usr/bin/python
2
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.
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
15 # Default debug modes for GYP
16 debug = {}
17
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'
22
23 def DebugOutput(mode, message):
24   if mode in gyp.debug.keys():
25     print "%s: %s" % (mode.upper(), message)
26
27 def FindBuildFiles():
28   extension = '.gyp'
29   files = os.listdir(os.getcwd())
30   build_files = []
31   for file in files:
32     if file[-len(extension):] == extension:
33       build_files.append(file)
34   return build_files
35
36
37 def Load(build_files, format, default_variables={},
38          includes=[], depth='.', params={}, check=False, circular_check=True):
39   """
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.
44   """
45   default_variables = copy.copy(default_variables)
46
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
51
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)
58
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)
63
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
66   # they need.
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', []),
74     'path_sections':
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),
80   }
81
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
86
87 def NameValueListToDict(name_value_list):
88   """
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.
92   """
93   result = { }
94   for item in name_value_list:
95     tokens = item.split('=', 1)
96     if len(tokens) == 2:
97       # If we can make it an int, use that, otherwise, use the string.
98       try:
99         token_value = int(tokens[1])
100       except ValueError:
101         token_value = tokens[1]
102       # Set the variable to the supplied value.
103       result[tokens[0]] = token_value
104     else:
105       # No value supplied, treat it as a boolean and set it.
106       result[tokens[0]] = True
107   return result
108
109 def ShlexEnv(env_name):
110   flags = os.environ.get(env_name, [])
111   if flags:
112     flags = shlex.split(flags)
113   return flags
114
115 def FormatOpt(opt, value):
116   if opt.startswith('--'):
117     return '%s=%s' % (opt, value)
118   return opt + value
119
120 def RegenerateAppendFlag(flag, values, predicate, env_name, options):
121   """Regenerate a list of command line flags, for an option of action='append'.
122
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
128   are used again.
129   """
130   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)))
134   if values:
135     for flag_value in values:
136       flags.append(FormatOpt(flag, predicate(flag_value)))
137   return flags
138
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.)
143
144   Any path options will be normalized relative to depth.
145
146   The format flag is not included, as it is assumed the calling generator will
147   set that as appropriate.
148   """
149   def FixPath(path):
150     path = gyp.common.FixIfRelativePath(path, options.depth)
151     if not path:
152       return os.path.curdir
153     return path
154
155   def Noop(value):
156     return value
157
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,
169                                         env_name, options))
170     elif action in ('store', None):  # None is a synonym for 'store'.
171       if value:
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)):
178         flags.append(opt)
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,
182                                                              env_name))
183     else:
184       print >>sys.stderr, ('Warning: regeneration unimplemented for action %r '
185                            'flag %r' % (action, opt))
186
187   return flags
188
189 class RegeneratableOptionParser(optparse.OptionParser):
190   def __init__(self):
191     self.__regeneratable_options = {}
192     optparse.OptionParser.__init__(self)
193
194   def add_option(self, *args, **kw):
195     """Add an option to the parser.
196
197     This accepts the same arguments as OptionParser.add_option, plus the
198     following:
199       regenerate: can be set to False to prevent this option from being included
200                   in regeneration.
201       env_name: name of environment variable that additional values for this
202                 option come from.
203       type: adds type='path', to tell the regenerator that the values of
204             this option need to be made relative to options.depth
205     """
206     env_name = kw.pop('env_name', None)
207     if 'dest' in kw and kw.pop('regenerate', True):
208       dest = kw['dest']
209
210       # The path type is needed for regenerating, for optparse we can just treat
211       # it as a string.
212       type = kw.get('type')
213       if type == 'path':
214         kw['type'] = 'string'
215
216       self.__regeneratable_options[dest] = {
217           'action': kw.get('action'),
218           'type': type,
219           'env_name': env_name,
220           'opt': args[0],
221         }
222
223     optparse.OptionParser.add_option(self, *args, **kw)
224
225   def parse_args(self, *args):
226     values, args = optparse.OptionParser.parse_args(self, *args)
227     values._regeneration_metadata = self.__regeneratable_options
228     return values, args
229
230 def main(args):
231   my_name = os.path.basename(sys.argv[0])
232
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',
243                     regenerate=False,
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" '
253                     'and "general"')
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")
281
282   # We read a few things from ~/.gyp, so set up a var for that.
283   home_vars = ['HOME']
284   if sys.platform in ('cygwin', 'win32'):
285     home_vars.append('USERPROFILE')
286   home = None
287   home_dot_gyp = None
288   for home_var in home_vars:
289     home = os.getenv(home_var)
290     if home != None:
291       home_dot_gyp = os.path.join(home, '.gyp')
292       if not os.path.exists(home_dot_gyp):
293         home_dot_gyp = None
294       else:
295         break
296
297   # TODO(thomasvl): add support for ~/.gyp/defaults
298
299   options, build_files_arg = parser.parse_args(args)
300   build_files = build_files_arg
301
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', [])
307     if generate_formats:
308       generate_formats = re.split('[\s,]', generate_formats)
309     if generate_formats:
310       options.formats = generate_formats
311     else:
312       # Nothing in the variable, default based on platform.
313       options.formats = [ {'darwin':   'xcode',
314                            'win32':    'msvs',
315                            'cygwin':   'msvs',
316                            'freebsd7': 'make',
317                            'freebsd8': 'make',
318                            'linux2':   'make',
319                            'openbsd4': 'make',
320                            'sunos5':   'make',}[sys.platform] ]
321
322   if not options.generator_output and options.use_environment:
323     g_o = os.environ.get('GYP_GENERATOR_OUTPUT')
324     if g_o:
325       options.generator_output = g_o
326
327   for mode in options.debug:
328     gyp.debug[mode] = 1
329
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()):
334       if option[0] == '_':
335         continue
336       if isinstance(value, basestring):
337         DebugOutput(DEBUG_GENERAL, "  %s: '%s'" % (option, value))
338       else:
339         DebugOutput(DEBUG_GENERAL, "  %s: %s" % (option, str(value)))
340
341   if not build_files:
342     build_files = FindBuildFiles()
343   if not build_files:
344     print >>sys.stderr, (usage + '\n\n%s: error: no build_file') % \
345                         (my_name, my_name)
346     return 1
347
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)
362           break
363         del build_file_dir_components[index]
364
365       # If the inner loop found something, break without advancing to another
366       # build file.
367       if options.depth:
368         break
369
370     if not options.depth:
371       raise Exception, \
372             'Could not automatically locate src directory.  This is a ' + \
373             'temporary Chromium feature that will be removed.  Use ' + \
374             '--depth as a workaround.'
375
376   # If toplevel-dir is not set, we assume that depth is the root of our source
377   # tree.
378   if not options.toplevel_dir:
379     options.toplevel_dir = options.depth
380
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 = {}
385   defines = []
386   if options.use_environment:
387     defines += ShlexEnv('GYP_DEFINES')
388   if options.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)
394
395   # Set up includes.
396   includes = []
397
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)
404
405   # Command-line --include files come after the default include.
406   if options.includes:
407     includes.extend(options.includes)
408
409   # Generator flags should be prefixed with the target generator since they
410   # are global across all generator runs.
411   gen_flags = []
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)
419
420   # TODO: Remove this and the option after we've gotten folks to move to the
421   # generator flag.
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=' + \
426       options.msvs_version
427     generator_flags['msvs_version'] = options.msvs_version
428
429   # Generate all requested formats (use a set in case we got one format request
430   # twice)
431   for format in set(options.formats):
432     params = {'options': options,
433               'build_files': build_files,
434               'generator_flags': generator_flags,
435               'cwd': os.getcwd(),
436               'build_files_arg': build_files_arg,
437               'gyp_binary': sys.argv[0],
438               'home_dot_gyp': home_dot_gyp}
439
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)
446
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)
455
456   # Done
457   return 0
458
459
460 if __name__ == '__main__':
461   sys.exit(main(sys.argv[1:]))