Home | History | Annotate | Download | only in generator
      1 # Copyright (c) 2013 Google Inc. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import copy
      6 import hashlib
      7 import json
      8 import multiprocessing
      9 import os.path
     10 import re
     11 import signal
     12 import subprocess
     13 import sys
     14 import gyp
     15 import gyp.common
     16 import gyp.msvs_emulation
     17 import gyp.MSVSUtil as MSVSUtil
     18 import gyp.xcode_emulation
     19 from cStringIO import StringIO
     20 
     21 from gyp.common import GetEnvironFallback
     22 import gyp.ninja_syntax as ninja_syntax
     23 
     24 generator_default_variables = {
     25   'EXECUTABLE_PREFIX': '',
     26   'EXECUTABLE_SUFFIX': '',
     27   'STATIC_LIB_PREFIX': 'lib',
     28   'STATIC_LIB_SUFFIX': '.a',
     29   'SHARED_LIB_PREFIX': 'lib',
     30 
     31   # Gyp expects the following variables to be expandable by the build
     32   # system to the appropriate locations.  Ninja prefers paths to be
     33   # known at gyp time.  To resolve this, introduce special
     34   # variables starting with $! and $| (which begin with a $ so gyp knows it
     35   # should be treated specially, but is otherwise an invalid
     36   # ninja/shell variable) that are passed to gyp here but expanded
     37   # before writing out into the target .ninja files; see
     38   # ExpandSpecial.
     39   # $! is used for variables that represent a path and that can only appear at
     40   # the start of a string, while $| is used for variables that can appear
     41   # anywhere in a string.
     42   'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR',
     43   'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen',
     44   'PRODUCT_DIR': '$!PRODUCT_DIR',
     45   'CONFIGURATION_NAME': '$|CONFIGURATION_NAME',
     46 
     47   # Special variables that may be used by gyp 'rule' targets.
     48   # We generate definitions for these variables on the fly when processing a
     49   # rule.
     50   'RULE_INPUT_ROOT': '${root}',
     51   'RULE_INPUT_DIRNAME': '${dirname}',
     52   'RULE_INPUT_PATH': '${source}',
     53   'RULE_INPUT_EXT': '${ext}',
     54   'RULE_INPUT_NAME': '${name}',
     55 }
     56 
     57 # Placates pylint.
     58 generator_additional_non_configuration_keys = []
     59 generator_additional_path_sections = []
     60 generator_extra_sources_for_rules = []
     61 generator_filelist_paths = None
     62 
     63 # TODO: figure out how to not build extra host objects in the non-cross-compile
     64 # case when this is enabled, and enable unconditionally.
     65 generator_supports_multiple_toolsets = (
     66   os.environ.get('GYP_CROSSCOMPILE') or
     67   os.environ.get('AR_host') or
     68   os.environ.get('CC_host') or
     69   os.environ.get('CXX_host') or
     70   os.environ.get('AR_target') or
     71   os.environ.get('CC_target') or
     72   os.environ.get('CXX_target'))
     73 
     74 
     75 def StripPrefix(arg, prefix):
     76   if arg.startswith(prefix):
     77     return arg[len(prefix):]
     78   return arg
     79 
     80 
     81 def QuoteShellArgument(arg, flavor):
     82   """Quote a string such that it will be interpreted as a single argument
     83   by the shell."""
     84   # Rather than attempting to enumerate the bad shell characters, just
     85   # whitelist common OK ones and quote anything else.
     86   if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg):
     87     return arg  # No quoting necessary.
     88   if flavor == 'win':
     89     return gyp.msvs_emulation.QuoteForRspFile(arg)
     90   return "'" + arg.replace("'", "'" + '"\'"' + "'")  + "'"
     91 
     92 
     93 def Define(d, flavor):
     94   """Takes a preprocessor define and returns a -D parameter that's ninja- and
     95   shell-escaped."""
     96   if flavor == 'win':
     97     # cl.exe replaces literal # characters with = in preprocesor definitions for
     98     # some reason. Octal-encode to work around that.
     99     d = d.replace('#', '\\%03o' % ord('#'))
    100   return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
    101 
    102 
    103 def AddArch(output, arch):
    104   """Adds an arch string to an output path."""
    105   output, extension = os.path.splitext(output)
    106   return '%s.%s%s' % (output, arch, extension)
    107 
    108 
    109 class Target:
    110   """Target represents the paths used within a single gyp target.
    111 
    112   Conceptually, building a single target A is a series of steps:
    113 
    114   1) actions/rules/copies  generates source/resources/etc.
    115   2) compiles              generates .o files
    116   3) link                  generates a binary (library/executable)
    117   4) bundle                merges the above in a mac bundle
    118 
    119   (Any of these steps can be optional.)
    120 
    121   From a build ordering perspective, a dependent target B could just
    122   depend on the last output of this series of steps.
    123 
    124   But some dependent commands sometimes need to reach inside the box.
    125   For example, when linking B it needs to get the path to the static
    126   library generated by A.
    127 
    128   This object stores those paths.  To keep things simple, member
    129   variables only store concrete paths to single files, while methods
    130   compute derived values like "the last output of the target".
    131   """
    132   def __init__(self, type):
    133     # Gyp type ("static_library", etc.) of this target.
    134     self.type = type
    135     # File representing whether any input dependencies necessary for
    136     # dependent actions have completed.
    137     self.preaction_stamp = None
    138     # File representing whether any input dependencies necessary for
    139     # dependent compiles have completed.
    140     self.precompile_stamp = None
    141     # File representing the completion of actions/rules/copies, if any.
    142     self.actions_stamp = None
    143     # Path to the output of the link step, if any.
    144     self.binary = None
    145     # Path to the file representing the completion of building the bundle,
    146     # if any.
    147     self.bundle = None
    148     # On Windows, incremental linking requires linking against all the .objs
    149     # that compose a .lib (rather than the .lib itself). That list is stored
    150     # here.
    151     self.component_objs = None
    152     # Windows only. The import .lib is the output of a build step, but
    153     # because dependents only link against the lib (not both the lib and the
    154     # dll) we keep track of the import library here.
    155     self.import_lib = None
    156 
    157   def Linkable(self):
    158     """Return true if this is a target that can be linked against."""
    159     return self.type in ('static_library', 'shared_library')
    160 
    161   def UsesToc(self, flavor):
    162     """Return true if the target should produce a restat rule based on a TOC
    163     file."""
    164     # For bundles, the .TOC should be produced for the binary, not for
    165     # FinalOutput(). But the naive approach would put the TOC file into the
    166     # bundle, so don't do this for bundles for now.
    167     if flavor == 'win' or self.bundle:
    168       return False
    169     return self.type in ('shared_library', 'loadable_module')
    170 
    171   def PreActionInput(self, flavor):
    172     """Return the path, if any, that should be used as a dependency of
    173     any dependent action step."""
    174     if self.UsesToc(flavor):
    175       return self.FinalOutput() + '.TOC'
    176     return self.FinalOutput() or self.preaction_stamp
    177 
    178   def PreCompileInput(self):
    179     """Return the path, if any, that should be used as a dependency of
    180     any dependent compile step."""
    181     return self.actions_stamp or self.precompile_stamp
    182 
    183   def FinalOutput(self):
    184     """Return the last output of the target, which depends on all prior
    185     steps."""
    186     return self.bundle or self.binary or self.actions_stamp
    187 
    188 
    189 # A small discourse on paths as used within the Ninja build:
    190 # All files we produce (both at gyp and at build time) appear in the
    191 # build directory (e.g. out/Debug).
    192 #
    193 # Paths within a given .gyp file are always relative to the directory
    194 # containing the .gyp file.  Call these "gyp paths".  This includes
    195 # sources as well as the starting directory a given gyp rule/action
    196 # expects to be run from.  We call the path from the source root to
    197 # the gyp file the "base directory" within the per-.gyp-file
    198 # NinjaWriter code.
    199 #
    200 # All paths as written into the .ninja files are relative to the build
    201 # directory.  Call these paths "ninja paths".
    202 #
    203 # We translate between these two notions of paths with two helper
    204 # functions:
    205 #
    206 # - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file)
    207 #   into the equivalent ninja path.
    208 #
    209 # - GypPathToUniqueOutput translates a gyp path into a ninja path to write
    210 #   an output file; the result can be namespaced such that it is unique
    211 #   to the input file name as well as the output target name.
    212 
    213 class NinjaWriter:
    214   def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
    215                output_file, toplevel_build, output_file_name, flavor,
    216                toplevel_dir=None):
    217     """
    218     base_dir: path from source root to directory containing this gyp file,
    219               by gyp semantics, all input paths are relative to this
    220     build_dir: path from source root to build output
    221     toplevel_dir: path to the toplevel directory
    222     """
    223 
    224     self.qualified_target = qualified_target
    225     self.target_outputs = target_outputs
    226     self.base_dir = base_dir
    227     self.build_dir = build_dir
    228     self.ninja = ninja_syntax.Writer(output_file)
    229     self.toplevel_build = toplevel_build
    230     self.output_file_name = output_file_name
    231 
    232     self.flavor = flavor
    233     self.abs_build_dir = None
    234     if toplevel_dir is not None:
    235       self.abs_build_dir = os.path.abspath(os.path.join(toplevel_dir,
    236                                                         build_dir))
    237     self.obj_ext = '.obj' if flavor == 'win' else '.o'
    238     if flavor == 'win':
    239       # See docstring of msvs_emulation.GenerateEnvironmentFiles().
    240       self.win_env = {}
    241       for arch in ('x86', 'x64'):
    242         self.win_env[arch] = 'environment.' + arch
    243 
    244     # Relative path from build output dir to base dir.
    245     build_to_top = gyp.common.InvertRelativePath(build_dir, toplevel_dir)
    246     self.build_to_base = os.path.join(build_to_top, base_dir)
    247     # Relative path from base dir to build dir.
    248     base_to_top = gyp.common.InvertRelativePath(base_dir, toplevel_dir)
    249     self.base_to_build = os.path.join(base_to_top, build_dir)
    250 
    251   def ExpandSpecial(self, path, product_dir=None):
    252     """Expand specials like $!PRODUCT_DIR in |path|.
    253 
    254     If |product_dir| is None, assumes the cwd is already the product
    255     dir.  Otherwise, |product_dir| is the relative path to the product
    256     dir.
    257     """
    258 
    259     PRODUCT_DIR = '$!PRODUCT_DIR'
    260     if PRODUCT_DIR in path:
    261       if product_dir:
    262         path = path.replace(PRODUCT_DIR, product_dir)
    263       else:
    264         path = path.replace(PRODUCT_DIR + '/', '')
    265         path = path.replace(PRODUCT_DIR + '\\', '')
    266         path = path.replace(PRODUCT_DIR, '.')
    267 
    268     INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR'
    269     if INTERMEDIATE_DIR in path:
    270       int_dir = self.GypPathToUniqueOutput('gen')
    271       # GypPathToUniqueOutput generates a path relative to the product dir,
    272       # so insert product_dir in front if it is provided.
    273       path = path.replace(INTERMEDIATE_DIR,
    274                           os.path.join(product_dir or '', int_dir))
    275 
    276     CONFIGURATION_NAME = '$|CONFIGURATION_NAME'
    277     path = path.replace(CONFIGURATION_NAME, self.config_name)
    278 
    279     return path
    280 
    281   def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
    282     if self.flavor == 'win':
    283       path = self.msvs_settings.ConvertVSMacros(
    284           path, config=self.config_name)
    285     path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root)
    286     path = path.replace(generator_default_variables['RULE_INPUT_DIRNAME'],
    287                         dirname)
    288     path = path.replace(generator_default_variables['RULE_INPUT_PATH'], source)
    289     path = path.replace(generator_default_variables['RULE_INPUT_EXT'], ext)
    290     path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name)
    291     return path
    292 
    293   def GypPathToNinja(self, path, env=None):
    294     """Translate a gyp path to a ninja path, optionally expanding environment
    295     variable references in |path| with |env|.
    296 
    297     See the above discourse on path conversions."""
    298     if env:
    299       if self.flavor == 'mac':
    300         path = gyp.xcode_emulation.ExpandEnvVars(path, env)
    301       elif self.flavor == 'win':
    302         path = gyp.msvs_emulation.ExpandMacros(path, env)
    303     if path.startswith('$!'):
    304       expanded = self.ExpandSpecial(path)
    305       if self.flavor == 'win':
    306         expanded = os.path.normpath(expanded)
    307       return expanded
    308     if '$|' in path:
    309       path = self.ExpandSpecial(path)
    310     assert '$' not in path, path
    311     return os.path.normpath(os.path.join(self.build_to_base, path))
    312 
    313   def GypPathToUniqueOutput(self, path, qualified=True):
    314     """Translate a gyp path to a ninja path for writing output.
    315 
    316     If qualified is True, qualify the resulting filename with the name
    317     of the target.  This is necessary when e.g. compiling the same
    318     path twice for two separate output targets.
    319 
    320     See the above discourse on path conversions."""
    321 
    322     path = self.ExpandSpecial(path)
    323     assert not path.startswith('$'), path
    324 
    325     # Translate the path following this scheme:
    326     #   Input: foo/bar.gyp, target targ, references baz/out.o
    327     #   Output: obj/foo/baz/targ.out.o (if qualified)
    328     #           obj/foo/baz/out.o (otherwise)
    329     #     (and obj.host instead of obj for cross-compiles)
    330     #
    331     # Why this scheme and not some other one?
    332     # 1) for a given input, you can compute all derived outputs by matching
    333     #    its path, even if the input is brought via a gyp file with '..'.
    334     # 2) simple files like libraries and stamps have a simple filename.
    335 
    336     obj = 'obj'
    337     if self.toolset != 'target':
    338       obj += '.' + self.toolset
    339 
    340     path_dir, path_basename = os.path.split(path)
    341     if qualified:
    342       path_basename = self.name + '.' + path_basename
    343     return os.path.normpath(os.path.join(obj, self.base_dir, path_dir,
    344                                          path_basename))
    345 
    346   def WriteCollapsedDependencies(self, name, targets):
    347     """Given a list of targets, return a path for a single file
    348     representing the result of building all the targets or None.
    349 
    350     Uses a stamp file if necessary."""
    351 
    352     assert targets == filter(None, targets), targets
    353     if len(targets) == 0:
    354       return None
    355     if len(targets) > 1:
    356       stamp = self.GypPathToUniqueOutput(name + '.stamp')
    357       targets = self.ninja.build(stamp, 'stamp', targets)
    358       self.ninja.newline()
    359     return targets[0]
    360 
    361   def _SubninjaNameForArch(self, arch):
    362     output_file_base = os.path.splitext(self.output_file_name)[0]
    363     return '%s.%s.ninja' % (output_file_base, arch)
    364 
    365   def WriteSpec(self, spec, config_name, generator_flags):
    366     """The main entry point for NinjaWriter: write the build rules for a spec.
    367 
    368     Returns a Target object, which represents the output paths for this spec.
    369     Returns None if there are no outputs (e.g. a settings-only 'none' type
    370     target)."""
    371 
    372     self.config_name = config_name
    373     self.name = spec['target_name']
    374     self.toolset = spec['toolset']
    375     config = spec['configurations'][config_name]
    376     self.target = Target(spec['type'])
    377     self.is_standalone_static_library = bool(
    378         spec.get('standalone_static_library', 0))
    379     # Track if this target contains any C++ files, to decide if gcc or g++
    380     # should be used for linking.
    381     self.uses_cpp = False
    382 
    383     self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
    384     self.xcode_settings = self.msvs_settings = None
    385     if self.flavor == 'mac':
    386       self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
    387     if self.flavor == 'win':
    388       self.msvs_settings = gyp.msvs_emulation.MsvsSettings(spec,
    389                                                            generator_flags)
    390       arch = self.msvs_settings.GetArch(config_name)
    391       self.ninja.variable('arch', self.win_env[arch])
    392       self.ninja.variable('cc', '$cl_' + arch)
    393       self.ninja.variable('cxx', '$cl_' + arch)
    394 
    395     if self.flavor == 'mac':
    396       self.archs = self.xcode_settings.GetActiveArchs(config_name)
    397       if len(self.archs) > 1:
    398         self.arch_subninjas = dict(
    399             (arch, ninja_syntax.Writer(
    400                 OpenOutput(os.path.join(self.toplevel_build,
    401                                         self._SubninjaNameForArch(arch)),
    402                            'w')))
    403             for arch in self.archs)
    404 
    405     # Compute predepends for all rules.
    406     # actions_depends is the dependencies this target depends on before running
    407     # any of its action/rule/copy steps.
    408     # compile_depends is the dependencies this target depends on before running
    409     # any of its compile steps.
    410     actions_depends = []
    411     compile_depends = []
    412     # TODO(evan): it is rather confusing which things are lists and which
    413     # are strings.  Fix these.
    414     if 'dependencies' in spec:
    415       for dep in spec['dependencies']:
    416         if dep in self.target_outputs:
    417           target = self.target_outputs[dep]
    418           actions_depends.append(target.PreActionInput(self.flavor))
    419           compile_depends.append(target.PreCompileInput())
    420       actions_depends = filter(None, actions_depends)
    421       compile_depends = filter(None, compile_depends)
    422       actions_depends = self.WriteCollapsedDependencies('actions_depends',
    423                                                         actions_depends)
    424       compile_depends = self.WriteCollapsedDependencies('compile_depends',
    425                                                         compile_depends)
    426       self.target.preaction_stamp = actions_depends
    427       self.target.precompile_stamp = compile_depends
    428 
    429     # Write out actions, rules, and copies.  These must happen before we
    430     # compile any sources, so compute a list of predependencies for sources
    431     # while we do it.
    432     extra_sources = []
    433     mac_bundle_depends = []
    434     self.target.actions_stamp = self.WriteActionsRulesCopies(
    435         spec, extra_sources, actions_depends, mac_bundle_depends)
    436 
    437     # If we have actions/rules/copies, we depend directly on those, but
    438     # otherwise we depend on dependent target's actions/rules/copies etc.
    439     # We never need to explicitly depend on previous target's link steps,
    440     # because no compile ever depends on them.
    441     compile_depends_stamp = (self.target.actions_stamp or compile_depends)
    442 
    443     # Write out the compilation steps, if any.
    444     link_deps = []
    445     sources = extra_sources + spec.get('sources', [])
    446     if sources:
    447       if self.flavor == 'mac' and len(self.archs) > 1:
    448         # Write subninja file containing compile and link commands scoped to
    449         # a single arch if a fat binary is being built.
    450         for arch in self.archs:
    451           self.ninja.subninja(self._SubninjaNameForArch(arch))
    452 
    453       pch = None
    454       if self.flavor == 'win':
    455         gyp.msvs_emulation.VerifyMissingSources(
    456             sources, self.abs_build_dir, generator_flags, self.GypPathToNinja)
    457         pch = gyp.msvs_emulation.PrecompiledHeader(
    458             self.msvs_settings, config_name, self.GypPathToNinja,
    459             self.GypPathToUniqueOutput, self.obj_ext)
    460       else:
    461         pch = gyp.xcode_emulation.MacPrefixHeader(
    462             self.xcode_settings, self.GypPathToNinja,
    463             lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang))
    464       link_deps = self.WriteSources(
    465           self.ninja, config_name, config, sources, compile_depends_stamp, pch,
    466           spec)
    467       # Some actions/rules output 'sources' that are already object files.
    468       obj_outputs = [f for f in sources if f.endswith(self.obj_ext)]
    469       if obj_outputs:
    470         if self.flavor != 'mac' or len(self.archs) == 1:
    471           link_deps += [self.GypPathToNinja(o) for o in obj_outputs]
    472         else:
    473           print "Warning: Actions/rules writing object files don't work with " \
    474                 "multiarch targets, dropping. (target %s)" % spec['target_name']
    475 
    476 
    477     if self.flavor == 'win' and self.target.type == 'static_library':
    478       self.target.component_objs = link_deps
    479 
    480     # Write out a link step, if needed.
    481     output = None
    482     is_empty_bundle = not link_deps and not mac_bundle_depends
    483     if link_deps or self.target.actions_stamp or actions_depends:
    484       output = self.WriteTarget(spec, config_name, config, link_deps,
    485                                 self.target.actions_stamp or actions_depends)
    486       if self.is_mac_bundle:
    487         mac_bundle_depends.append(output)
    488 
    489     # Bundle all of the above together, if needed.
    490     if self.is_mac_bundle:
    491       output = self.WriteMacBundle(spec, mac_bundle_depends, is_empty_bundle)
    492 
    493     if not output:
    494       return None
    495 
    496     assert self.target.FinalOutput(), output
    497     return self.target
    498 
    499   def _WinIdlRule(self, source, prebuild, outputs):
    500     """Handle the implicit VS .idl rule for one source file. Fills |outputs|
    501     with files that are generated."""
    502     outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData(
    503         source, self.config_name)
    504     outdir = self.GypPathToNinja(outdir)
    505     def fix_path(path, rel=None):
    506       path = os.path.join(outdir, path)
    507       dirname, basename = os.path.split(source)
    508       root, ext = os.path.splitext(basename)
    509       path = self.ExpandRuleVariables(
    510           path, root, dirname, source, ext, basename)
    511       if rel:
    512         path = os.path.relpath(path, rel)
    513       return path
    514     vars = [(name, fix_path(value, outdir)) for name, value in vars]
    515     output = [fix_path(p) for p in output]
    516     vars.append(('outdir', outdir))
    517     vars.append(('idlflags', flags))
    518     input = self.GypPathToNinja(source)
    519     self.ninja.build(output, 'idl', input,
    520         variables=vars, order_only=prebuild)
    521     outputs.extend(output)
    522 
    523   def WriteWinIdlFiles(self, spec, prebuild):
    524     """Writes rules to match MSVS's implicit idl handling."""
    525     assert self.flavor == 'win'
    526     if self.msvs_settings.HasExplicitIdlRules(spec):
    527       return []
    528     outputs = []
    529     for source in filter(lambda x: x.endswith('.idl'), spec['sources']):
    530       self._WinIdlRule(source, prebuild, outputs)
    531     return outputs
    532 
    533   def WriteActionsRulesCopies(self, spec, extra_sources, prebuild,
    534                               mac_bundle_depends):
    535     """Write out the Actions, Rules, and Copies steps.  Return a path
    536     representing the outputs of these steps."""
    537     outputs = []
    538     if self.is_mac_bundle:
    539       mac_bundle_resources = spec.get('mac_bundle_resources', [])[:]
    540     else:
    541       mac_bundle_resources = []
    542     extra_mac_bundle_resources = []
    543 
    544     if 'actions' in spec:
    545       outputs += self.WriteActions(spec['actions'], extra_sources, prebuild,
    546                                    extra_mac_bundle_resources)
    547     if 'rules' in spec:
    548       outputs += self.WriteRules(spec['rules'], extra_sources, prebuild,
    549                                  mac_bundle_resources,
    550                                  extra_mac_bundle_resources)
    551     if 'copies' in spec:
    552       outputs += self.WriteCopies(spec['copies'], prebuild, mac_bundle_depends)
    553 
    554     if 'sources' in spec and self.flavor == 'win':
    555       outputs += self.WriteWinIdlFiles(spec, prebuild)
    556 
    557     stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
    558 
    559     if self.is_mac_bundle:
    560       self.WriteMacBundleResources(
    561           extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends)
    562       self.WriteMacInfoPlist(mac_bundle_depends)
    563 
    564     return stamp
    565 
    566   def GenerateDescription(self, verb, message, fallback):
    567     """Generate and return a description of a build step.
    568 
    569     |verb| is the short summary, e.g. ACTION or RULE.
    570     |message| is a hand-written description, or None if not available.
    571     |fallback| is the gyp-level name of the step, usable as a fallback.
    572     """
    573     if self.toolset != 'target':
    574       verb += '(%s)' % self.toolset
    575     if message:
    576       return '%s %s' % (verb, self.ExpandSpecial(message))
    577     else:
    578       return '%s %s: %s' % (verb, self.name, fallback)
    579 
    580   def WriteActions(self, actions, extra_sources, prebuild,
    581                    extra_mac_bundle_resources):
    582     # Actions cd into the base directory.
    583     env = self.GetSortedXcodeEnv()
    584     if self.flavor == 'win':
    585       env = self.msvs_settings.GetVSMacroEnv(
    586           '$!PRODUCT_DIR', config=self.config_name)
    587     all_outputs = []
    588     for action in actions:
    589       # First write out a rule for the action.
    590       name = '%s_%s' % (action['action_name'],
    591                         hashlib.md5(self.qualified_target).hexdigest())
    592       description = self.GenerateDescription('ACTION',
    593                                              action.get('message', None),
    594                                              name)
    595       is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action)
    596                    if self.flavor == 'win' else False)
    597       args = action['action']
    598       rule_name, _ = self.WriteNewNinjaRule(name, args, description,
    599                                             is_cygwin, env=env)
    600 
    601       inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
    602       if int(action.get('process_outputs_as_sources', False)):
    603         extra_sources += action['outputs']
    604       if int(action.get('process_outputs_as_mac_bundle_resources', False)):
    605         extra_mac_bundle_resources += action['outputs']
    606       outputs = [self.GypPathToNinja(o, env) for o in action['outputs']]
    607 
    608       # Then write out an edge using the rule.
    609       self.ninja.build(outputs, rule_name, inputs,
    610                        order_only=prebuild)
    611       all_outputs += outputs
    612 
    613       self.ninja.newline()
    614 
    615     return all_outputs
    616 
    617   def WriteRules(self, rules, extra_sources, prebuild,
    618                  mac_bundle_resources, extra_mac_bundle_resources):
    619     env = self.GetSortedXcodeEnv()
    620     all_outputs = []
    621     for rule in rules:
    622       # First write out a rule for the rule action.
    623       name = '%s_%s' % (rule['rule_name'],
    624                         hashlib.md5(self.qualified_target).hexdigest())
    625       # Skip a rule with no action and no inputs.
    626       if 'action' not in rule and not rule.get('rule_sources', []):
    627         continue
    628       args = rule['action']
    629       description = self.GenerateDescription(
    630           'RULE',
    631           rule.get('message', None),
    632           ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
    633       is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule)
    634                    if self.flavor == 'win' else False)
    635       rule_name, args = self.WriteNewNinjaRule(
    636           name, args, description, is_cygwin, env=env)
    637 
    638       # TODO: if the command references the outputs directly, we should
    639       # simplify it to just use $out.
    640 
    641       # Rules can potentially make use of some special variables which
    642       # must vary per source file.
    643       # Compute the list of variables we'll need to provide.
    644       special_locals = ('source', 'root', 'dirname', 'ext', 'name')
    645       needed_variables = set(['source'])
    646       for argument in args:
    647         for var in special_locals:
    648           if ('${%s}' % var) in argument:
    649             needed_variables.add(var)
    650 
    651       def cygwin_munge(path):
    652         if is_cygwin:
    653           return path.replace('\\', '/')
    654         return path
    655 
    656       # For each source file, write an edge that generates all the outputs.
    657       for source in rule.get('rule_sources', []):
    658         source = os.path.normpath(source)
    659         dirname, basename = os.path.split(source)
    660         root, ext = os.path.splitext(basename)
    661 
    662         # Gather the list of inputs and outputs, expanding $vars if possible.
    663         outputs = [self.ExpandRuleVariables(o, root, dirname,
    664                                             source, ext, basename)
    665                    for o in rule['outputs']]
    666         inputs = [self.ExpandRuleVariables(i, root, dirname,
    667                                            source, ext, basename)
    668                   for i in rule.get('inputs', [])]
    669 
    670         if int(rule.get('process_outputs_as_sources', False)):
    671           extra_sources += outputs
    672 
    673         was_mac_bundle_resource = source in mac_bundle_resources
    674         if was_mac_bundle_resource or \
    675             int(rule.get('process_outputs_as_mac_bundle_resources', False)):
    676           extra_mac_bundle_resources += outputs
    677           # Note: This is n_resources * n_outputs_in_rule.  Put to-be-removed
    678           # items in a set and remove them all in a single pass if this becomes
    679           # a performance issue.
    680           if was_mac_bundle_resource:
    681             mac_bundle_resources.remove(source)
    682 
    683         extra_bindings = []
    684         for var in needed_variables:
    685           if var == 'root':
    686             extra_bindings.append(('root', cygwin_munge(root)))
    687           elif var == 'dirname':
    688             # '$dirname' is a parameter to the rule action, which means
    689             # it shouldn't be converted to a Ninja path.  But we don't
    690             # want $!PRODUCT_DIR in there either.
    691             dirname_expanded = self.ExpandSpecial(dirname, self.base_to_build)
    692             extra_bindings.append(('dirname', cygwin_munge(dirname_expanded)))
    693           elif var == 'source':
    694             # '$source' is a parameter to the rule action, which means
    695             # it shouldn't be converted to a Ninja path.  But we don't
    696             # want $!PRODUCT_DIR in there either.
    697             source_expanded = self.ExpandSpecial(source, self.base_to_build)
    698             extra_bindings.append(('source', cygwin_munge(source_expanded)))
    699           elif var == 'ext':
    700             extra_bindings.append(('ext', ext))
    701           elif var == 'name':
    702             extra_bindings.append(('name', cygwin_munge(basename)))
    703           else:
    704             assert var == None, repr(var)
    705 
    706         inputs = [self.GypPathToNinja(i, env) for i in inputs]
    707         outputs = [self.GypPathToNinja(o, env) for o in outputs]
    708         extra_bindings.append(('unique_name',
    709             hashlib.md5(outputs[0]).hexdigest()))
    710         self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
    711                          implicit=inputs,
    712                          order_only=prebuild,
    713                          variables=extra_bindings)
    714 
    715         all_outputs.extend(outputs)
    716 
    717     return all_outputs
    718 
    719   def WriteCopies(self, copies, prebuild, mac_bundle_depends):
    720     outputs = []
    721     env = self.GetSortedXcodeEnv()
    722     for copy in copies:
    723       for path in copy['files']:
    724         # Normalize the path so trailing slashes don't confuse us.
    725         path = os.path.normpath(path)
    726         basename = os.path.split(path)[1]
    727         src = self.GypPathToNinja(path, env)
    728         dst = self.GypPathToNinja(os.path.join(copy['destination'], basename),
    729                                   env)
    730         outputs += self.ninja.build(dst, 'copy', src, order_only=prebuild)
    731         if self.is_mac_bundle:
    732           # gyp has mac_bundle_resources to copy things into a bundle's
    733           # Resources folder, but there's no built-in way to copy files to other
    734           # places in the bundle. Hence, some targets use copies for this. Check
    735           # if this file is copied into the current bundle, and if so add it to
    736           # the bundle depends so that dependent targets get rebuilt if the copy
    737           # input changes.
    738           if dst.startswith(self.xcode_settings.GetBundleContentsFolderPath()):
    739             mac_bundle_depends.append(dst)
    740 
    741     return outputs
    742 
    743   def WriteMacBundleResources(self, resources, bundle_depends):
    744     """Writes ninja edges for 'mac_bundle_resources'."""
    745     for output, res in gyp.xcode_emulation.GetMacBundleResources(
    746         generator_default_variables['PRODUCT_DIR'],
    747         self.xcode_settings, map(self.GypPathToNinja, resources)):
    748       output = self.ExpandSpecial(output)
    749       self.ninja.build(output, 'mac_tool', res,
    750                        variables=[('mactool_cmd', 'copy-bundle-resource')])
    751       bundle_depends.append(output)
    752 
    753   def WriteMacInfoPlist(self, bundle_depends):
    754     """Write build rules for bundle Info.plist files."""
    755     info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
    756         generator_default_variables['PRODUCT_DIR'],
    757         self.xcode_settings, self.GypPathToNinja)
    758     if not info_plist:
    759       return
    760     out = self.ExpandSpecial(out)
    761     if defines:
    762       # Create an intermediate file to store preprocessed results.
    763       intermediate_plist = self.GypPathToUniqueOutput(
    764           os.path.basename(info_plist))
    765       defines = ' '.join([Define(d, self.flavor) for d in defines])
    766       info_plist = self.ninja.build(
    767           intermediate_plist, 'preprocess_infoplist', info_plist,
    768           variables=[('defines',defines)])
    769 
    770     env = self.GetSortedXcodeEnv(additional_settings=extra_env)
    771     env = self.ComputeExportEnvString(env)
    772 
    773     keys = self.xcode_settings.GetExtraPlistItems(self.config_name)
    774     keys = QuoteShellArgument(json.dumps(keys), self.flavor)
    775     self.ninja.build(out, 'copy_infoplist', info_plist,
    776                      variables=[('env', env), ('keys', keys)])
    777     bundle_depends.append(out)
    778 
    779   def WriteSources(self, ninja_file, config_name, config, sources, predepends,
    780                    precompiled_header, spec):
    781     """Write build rules to compile all of |sources|."""
    782     if self.toolset == 'host':
    783       self.ninja.variable('ar', '$ar_host')
    784       self.ninja.variable('cc', '$cc_host')
    785       self.ninja.variable('cxx', '$cxx_host')
    786       self.ninja.variable('ld', '$ld_host')
    787       self.ninja.variable('ldxx', '$ldxx_host')
    788 
    789     if self.flavor != 'mac' or len(self.archs) == 1:
    790       return self.WriteSourcesForArch(
    791           self.ninja, config_name, config, sources, predepends,
    792           precompiled_header, spec)
    793     else:
    794       return dict((arch, self.WriteSourcesForArch(
    795             self.arch_subninjas[arch], config_name, config, sources, predepends,
    796             precompiled_header, spec, arch=arch))
    797           for arch in self.archs)
    798 
    799   def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
    800                           predepends, precompiled_header, spec, arch=None):
    801     """Write build rules to compile all of |sources|."""
    802 
    803     extra_defines = []
    804     if self.flavor == 'mac':
    805       cflags = self.xcode_settings.GetCflags(config_name, arch=arch)
    806       cflags_c = self.xcode_settings.GetCflagsC(config_name)
    807       cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
    808       cflags_objc = ['$cflags_c'] + \
    809                     self.xcode_settings.GetCflagsObjC(config_name)
    810       cflags_objcc = ['$cflags_cc'] + \
    811                      self.xcode_settings.GetCflagsObjCC(config_name)
    812     elif self.flavor == 'win':
    813       cflags = self.msvs_settings.GetCflags(config_name)
    814       cflags_c = self.msvs_settings.GetCflagsC(config_name)
    815       cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
    816       extra_defines = self.msvs_settings.GetComputedDefines(config_name)
    817       # See comment at cc_command for why there's two .pdb files.
    818       pdbpath_c = pdbpath_cc = self.msvs_settings.GetCompilerPdbName(
    819           config_name, self.ExpandSpecial)
    820       if not pdbpath_c:
    821         obj = 'obj'
    822         if self.toolset != 'target':
    823           obj += '.' + self.toolset
    824         pdbpath = os.path.normpath(os.path.join(obj, self.base_dir, self.name))
    825         pdbpath_c = pdbpath + '.c.pdb'
    826         pdbpath_cc = pdbpath + '.cc.pdb'
    827       self.WriteVariableList(ninja_file, 'pdbname_c', [pdbpath_c])
    828       self.WriteVariableList(ninja_file, 'pdbname_cc', [pdbpath_cc])
    829       self.WriteVariableList(ninja_file, 'pchprefix', [self.name])
    830     else:
    831       cflags = config.get('cflags', [])
    832       cflags_c = config.get('cflags_c', [])
    833       cflags_cc = config.get('cflags_cc', [])
    834 
    835     # Respect environment variables related to build, but target-specific
    836     # flags can still override them.
    837     if self.toolset == 'target':
    838       cflags_c = (os.environ.get('CPPFLAGS', '').split() +
    839                   os.environ.get('CFLAGS', '').split() + cflags_c)
    840       cflags_cc = (os.environ.get('CPPFLAGS', '').split() +
    841                    os.environ.get('CXXFLAGS', '').split() + cflags_cc)
    842 
    843     defines = config.get('defines', []) + extra_defines
    844     self.WriteVariableList(ninja_file, 'defines',
    845                            [Define(d, self.flavor) for d in defines])
    846     if self.flavor == 'win':
    847       self.WriteVariableList(ninja_file, 'rcflags',
    848           [QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
    849            for f in self.msvs_settings.GetRcflags(config_name,
    850                                                   self.GypPathToNinja)])
    851 
    852     include_dirs = config.get('include_dirs', [])
    853     env = self.GetSortedXcodeEnv()
    854     if self.flavor == 'win':
    855       env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
    856                                              config=config_name)
    857       include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
    858                                                           config_name)
    859     self.WriteVariableList(ninja_file, 'includes',
    860         [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
    861          for i in include_dirs])
    862 
    863     pch_commands = precompiled_header.GetPchBuildCommands(arch)
    864     if self.flavor == 'mac':
    865       # Most targets use no precompiled headers, so only write these if needed.
    866       for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'),
    867                        ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]:
    868         include = precompiled_header.GetInclude(ext, arch)
    869         if include: ninja_file.variable(var, include)
    870 
    871     self.WriteVariableList(ninja_file, 'cflags',
    872                            map(self.ExpandSpecial, cflags))
    873     self.WriteVariableList(ninja_file, 'cflags_c',
    874                            map(self.ExpandSpecial, cflags_c))
    875     self.WriteVariableList(ninja_file, 'cflags_cc',
    876                            map(self.ExpandSpecial, cflags_cc))
    877     if self.flavor == 'mac':
    878       self.WriteVariableList(ninja_file, 'cflags_objc',
    879                              map(self.ExpandSpecial, cflags_objc))
    880       self.WriteVariableList(ninja_file, 'cflags_objcc',
    881                              map(self.ExpandSpecial, cflags_objcc))
    882     ninja_file.newline()
    883     outputs = []
    884     has_rc_source = False
    885     for source in sources:
    886       filename, ext = os.path.splitext(source)
    887       ext = ext[1:]
    888       obj_ext = self.obj_ext
    889       if ext in ('cc', 'cpp', 'cxx'):
    890         command = 'cxx'
    891         self.uses_cpp = True
    892       elif ext == 'c' or (ext == 'S' and self.flavor != 'win'):
    893         command = 'cc'
    894       elif ext == 's' and self.flavor != 'win':  # Doesn't generate .o.d files.
    895         command = 'cc_s'
    896       elif (self.flavor == 'win' and ext == 'asm' and
    897             self.msvs_settings.GetArch(config_name) == 'x86' and
    898             not self.msvs_settings.HasExplicitAsmRules(spec)):
    899         # Asm files only get auto assembled for x86 (not x64).
    900         command = 'asm'
    901         # Add the _asm suffix as msvs is capable of handling .cc and
    902         # .asm files of the same name without collision.
    903         obj_ext = '_asm.obj'
    904       elif self.flavor == 'mac' and ext == 'm':
    905         command = 'objc'
    906       elif self.flavor == 'mac' and ext == 'mm':
    907         command = 'objcxx'
    908         self.uses_cpp = True
    909       elif self.flavor == 'win' and ext == 'rc':
    910         command = 'rc'
    911         obj_ext = '.res'
    912         has_rc_source = True
    913       else:
    914         # Ignore unhandled extensions.
    915         continue
    916       input = self.GypPathToNinja(source)
    917       output = self.GypPathToUniqueOutput(filename + obj_ext)
    918       if arch is not None:
    919         output = AddArch(output, arch)
    920       implicit = precompiled_header.GetObjDependencies([input], [output], arch)
    921       variables = []
    922       if self.flavor == 'win':
    923         variables, output, implicit = precompiled_header.GetFlagsModifications(
    924             input, output, implicit, command, cflags_c, cflags_cc,
    925             self.ExpandSpecial)
    926       ninja_file.build(output, command, input,
    927                        implicit=[gch for _, _, gch in implicit],
    928                        order_only=predepends, variables=variables)
    929       outputs.append(output)
    930 
    931     if has_rc_source:
    932       resource_include_dirs = config.get('resource_include_dirs', include_dirs)
    933       self.WriteVariableList(ninja_file, 'resource_includes',
    934           [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
    935            for i in resource_include_dirs])
    936 
    937     self.WritePchTargets(ninja_file, pch_commands)
    938 
    939     ninja_file.newline()
    940     return outputs
    941 
    942   def WritePchTargets(self, ninja_file, pch_commands):
    943     """Writes ninja rules to compile prefix headers."""
    944     if not pch_commands:
    945       return
    946 
    947     for gch, lang_flag, lang, input in pch_commands:
    948       var_name = {
    949         'c': 'cflags_pch_c',
    950         'cc': 'cflags_pch_cc',
    951         'm': 'cflags_pch_objc',
    952         'mm': 'cflags_pch_objcc',
    953       }[lang]
    954 
    955       map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
    956       cmd = map.get(lang)
    957       ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
    958 
    959   def WriteLink(self, spec, config_name, config, link_deps):
    960     """Write out a link step. Fills out target.binary. """
    961     if self.flavor != 'mac' or len(self.archs) == 1:
    962       return self.WriteLinkForArch(
    963           self.ninja, spec, config_name, config, link_deps)
    964     else:
    965       output = self.ComputeOutput(spec)
    966       inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec,
    967                                       config_name, config, link_deps[arch],
    968                                       arch=arch)
    969                 for arch in self.archs]
    970       extra_bindings = []
    971       if not self.is_mac_bundle:
    972         self.AppendPostbuildVariable(extra_bindings, spec, output, output)
    973       self.ninja.build(output, 'lipo', inputs, variables=extra_bindings)
    974       return output
    975 
    976   def WriteLinkForArch(self, ninja_file, spec, config_name, config,
    977                        link_deps, arch=None):
    978     """Write out a link step. Fills out target.binary. """
    979     command = {
    980       'executable':      'link',
    981       'loadable_module': 'solink_module',
    982       'shared_library':  'solink',
    983     }[spec['type']]
    984     command_suffix = ''
    985 
    986     implicit_deps = set()
    987     solibs = set()
    988 
    989     if 'dependencies' in spec:
    990       # Two kinds of dependencies:
    991       # - Linkable dependencies (like a .a or a .so): add them to the link line.
    992       # - Non-linkable dependencies (like a rule that generates a file
    993       #   and writes a stamp file): add them to implicit_deps
    994       extra_link_deps = set()
    995       for dep in spec['dependencies']:
    996         target = self.target_outputs.get(dep)
    997         if not target:
    998           continue
    999         linkable = target.Linkable()
   1000         if linkable:
   1001           new_deps = []
   1002           if (self.flavor == 'win' and
   1003               target.component_objs and
   1004               self.msvs_settings.IsUseLibraryDependencyInputs(config_name)):
   1005             new_deps = target.component_objs
   1006           elif self.flavor == 'win' and target.import_lib:
   1007             new_deps = [target.import_lib]
   1008           elif target.UsesToc(self.flavor):
   1009             solibs.add(target.binary)
   1010             implicit_deps.add(target.binary + '.TOC')
   1011           else:
   1012             new_deps = [target.binary]
   1013           for new_dep in new_deps:
   1014             if new_dep not in extra_link_deps:
   1015               extra_link_deps.add(new_dep)
   1016               link_deps.append(new_dep)
   1017 
   1018         final_output = target.FinalOutput()
   1019         if not linkable or final_output != target.binary:
   1020           implicit_deps.add(final_output)
   1021 
   1022     extra_bindings = []
   1023     if self.uses_cpp and self.flavor != 'win':
   1024       extra_bindings.append(('ld', '$ldxx'))
   1025 
   1026     output = self.ComputeOutput(spec, arch)
   1027     if arch is None and not self.is_mac_bundle:
   1028       self.AppendPostbuildVariable(extra_bindings, spec, output, output)
   1029 
   1030     is_executable = spec['type'] == 'executable'
   1031     # The ldflags config key is not used on mac or win. On those platforms
   1032     # linker flags are set via xcode_settings and msvs_settings, respectively.
   1033     env_ldflags = os.environ.get('LDFLAGS', '').split()
   1034     if self.flavor == 'mac':
   1035       ldflags = self.xcode_settings.GetLdflags(config_name,
   1036           self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
   1037           self.GypPathToNinja, arch)
   1038       ldflags = env_ldflags + ldflags
   1039     elif self.flavor == 'win':
   1040       manifest_name = self.GypPathToUniqueOutput(
   1041           self.ComputeOutputFileName(spec))
   1042       ldflags, intermediate_manifest, manifest_files = \
   1043           self.msvs_settings.GetLdflags(config_name, self.GypPathToNinja,
   1044                                         self.ExpandSpecial, manifest_name,
   1045                                         is_executable, self.toplevel_build)
   1046       ldflags = env_ldflags + ldflags
   1047       self.WriteVariableList(ninja_file, 'manifests', manifest_files)
   1048       implicit_deps = implicit_deps.union(manifest_files)
   1049       if intermediate_manifest:
   1050         self.WriteVariableList(
   1051             ninja_file, 'intermediatemanifest', [intermediate_manifest])
   1052       command_suffix = _GetWinLinkRuleNameSuffix(
   1053           self.msvs_settings.IsEmbedManifest(config_name))
   1054       def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja)
   1055       if def_file:
   1056         implicit_deps.add(def_file)
   1057     else:
   1058       # Respect environment variables related to build, but target-specific
   1059       # flags can still override them.
   1060       ldflags = env_ldflags + config.get('ldflags', [])
   1061       if is_executable and len(solibs):
   1062         rpath = 'lib/'
   1063         if self.toolset != 'target':
   1064           rpath += self.toolset
   1065         ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
   1066         ldflags.append('-Wl,-rpath-link=%s' % rpath)
   1067     self.WriteVariableList(ninja_file, 'ldflags',
   1068                            gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
   1069 
   1070     library_dirs = config.get('library_dirs', [])
   1071     if self.flavor == 'win':
   1072       library_dirs = [self.msvs_settings.ConvertVSMacros(l, config_name)
   1073                       for l in library_dirs]
   1074       library_dirs = ['/LIBPATH:' + QuoteShellArgument(self.GypPathToNinja(l),
   1075                                                        self.flavor)
   1076                       for l in library_dirs]
   1077     else:
   1078       library_dirs = [QuoteShellArgument('-L' + self.GypPathToNinja(l),
   1079                                          self.flavor)
   1080                       for l in library_dirs]
   1081 
   1082     libraries = gyp.common.uniquer(map(self.ExpandSpecial,
   1083                                        spec.get('libraries', [])))
   1084     if self.flavor == 'mac':
   1085       libraries = self.xcode_settings.AdjustLibraries(libraries, config_name)
   1086     elif self.flavor == 'win':
   1087       libraries = self.msvs_settings.AdjustLibraries(libraries)
   1088 
   1089     self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries)
   1090 
   1091     linked_binary = output
   1092 
   1093     if command in ('solink', 'solink_module'):
   1094       extra_bindings.append(('soname', os.path.split(output)[1]))
   1095       extra_bindings.append(('lib',
   1096                             gyp.common.EncodePOSIXShellArgument(output)))
   1097       if self.flavor == 'win':
   1098         extra_bindings.append(('dll', output))
   1099         if '/NOENTRY' not in ldflags:
   1100           self.target.import_lib = output + '.lib'
   1101           extra_bindings.append(('implibflag',
   1102                                  '/IMPLIB:%s' % self.target.import_lib))
   1103           output = [output, self.target.import_lib]
   1104       elif not self.is_mac_bundle:
   1105         output = [output, output + '.TOC']
   1106       else:
   1107         command = command + '_notoc'
   1108 
   1109     if len(solibs):
   1110       extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs)))
   1111 
   1112     ninja_file.build(output, command + command_suffix, link_deps,
   1113                      implicit=list(implicit_deps),
   1114                      variables=extra_bindings)
   1115     return linked_binary
   1116 
   1117   def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
   1118     extra_link_deps = any(self.target_outputs.get(dep).Linkable()
   1119                           for dep in spec.get('dependencies', [])
   1120                           if dep in self.target_outputs)
   1121     if spec['type'] == 'none' or (not link_deps and not extra_link_deps):
   1122       # TODO(evan): don't call this function for 'none' target types, as
   1123       # it doesn't do anything, and we fake out a 'binary' with a stamp file.
   1124       self.target.binary = compile_deps
   1125       self.target.type = 'none'
   1126     elif spec['type'] == 'static_library':
   1127       self.target.binary = self.ComputeOutput(spec)
   1128       if (self.flavor not in ('mac', 'openbsd', 'win') and not
   1129           self.is_standalone_static_library):
   1130         self.ninja.build(self.target.binary, 'alink_thin', link_deps,
   1131                          order_only=compile_deps)
   1132       else:
   1133         variables = []
   1134         if self.xcode_settings:
   1135           libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
   1136           if libtool_flags:
   1137             variables.append(('libtool_flags', libtool_flags))
   1138         if self.msvs_settings:
   1139           libflags = self.msvs_settings.GetLibFlags(config_name,
   1140                                                     self.GypPathToNinja)
   1141           variables.append(('libflags', libflags))
   1142 
   1143         if self.flavor != 'mac' or len(self.archs) == 1:
   1144           self.AppendPostbuildVariable(variables, spec,
   1145                                        self.target.binary, self.target.binary)
   1146           self.ninja.build(self.target.binary, 'alink', link_deps,
   1147                            order_only=compile_deps, variables=variables)
   1148         else:
   1149           inputs = []
   1150           for arch in self.archs:
   1151             output = self.ComputeOutput(spec, arch)
   1152             self.arch_subninjas[arch].build(output, 'alink', link_deps[arch],
   1153                                             order_only=compile_deps,
   1154                                             variables=variables)
   1155             inputs.append(output)
   1156           # TODO: It's not clear if libtool_flags should be passed to the alink
   1157           # call that combines single-arch .a files into a fat .a file.
   1158           self.AppendPostbuildVariable(variables, spec,
   1159                                        self.target.binary, self.target.binary)
   1160           self.ninja.build(self.target.binary, 'alink', inputs,
   1161                            # FIXME: test proving order_only=compile_deps isn't
   1162                            # needed.
   1163                            variables=variables)
   1164     else:
   1165       self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
   1166     return self.target.binary
   1167 
   1168   def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
   1169     assert self.is_mac_bundle
   1170     package_framework = spec['type'] in ('shared_library', 'loadable_module')
   1171     output = self.ComputeMacBundleOutput()
   1172     if is_empty:
   1173       output += '.stamp'
   1174     variables = []
   1175     self.AppendPostbuildVariable(variables, spec, output, self.target.binary,
   1176                                  is_command_start=not package_framework)
   1177     if package_framework and not is_empty:
   1178       variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
   1179       self.ninja.build(output, 'package_framework', mac_bundle_depends,
   1180                        variables=variables)
   1181     else:
   1182       self.ninja.build(output, 'stamp', mac_bundle_depends,
   1183                        variables=variables)
   1184     self.target.bundle = output
   1185     return output
   1186 
   1187   def GetSortedXcodeEnv(self, additional_settings=None):
   1188     """Returns the variables Xcode would set for build steps."""
   1189     assert self.abs_build_dir
   1190     abs_build_dir = self.abs_build_dir
   1191     return gyp.xcode_emulation.GetSortedXcodeEnv(
   1192         self.xcode_settings, abs_build_dir,
   1193         os.path.join(abs_build_dir, self.build_to_base), self.config_name,
   1194         additional_settings)
   1195 
   1196   def GetSortedXcodePostbuildEnv(self):
   1197     """Returns the variables Xcode would set for postbuild steps."""
   1198     postbuild_settings = {}
   1199     # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
   1200     # TODO(thakis): It would be nice to have some general mechanism instead.
   1201     strip_save_file = self.xcode_settings.GetPerTargetSetting(
   1202         'CHROMIUM_STRIP_SAVE_FILE')
   1203     if strip_save_file:
   1204       postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
   1205     return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
   1206 
   1207   def AppendPostbuildVariable(self, variables, spec, output, binary,
   1208                               is_command_start=False):
   1209     """Adds a 'postbuild' variable if there is a postbuild for |output|."""
   1210     postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
   1211     if postbuild:
   1212       variables.append(('postbuilds', postbuild))
   1213 
   1214   def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
   1215     """Returns a shell command that runs all the postbuilds, and removes
   1216     |output| if any of them fails. If |is_command_start| is False, then the
   1217     returned string will start with ' && '."""
   1218     if not self.xcode_settings or spec['type'] == 'none' or not output:
   1219       return ''
   1220     output = QuoteShellArgument(output, self.flavor)
   1221     postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True)
   1222     if output_binary is not None:
   1223       postbuilds = self.xcode_settings.AddImplicitPostbuilds(
   1224           self.config_name,
   1225           os.path.normpath(os.path.join(self.base_to_build, output)),
   1226           QuoteShellArgument(
   1227               os.path.normpath(os.path.join(self.base_to_build, output_binary)),
   1228               self.flavor),
   1229           postbuilds, quiet=True)
   1230 
   1231     if not postbuilds:
   1232       return ''
   1233     # Postbuilds expect to be run in the gyp file's directory, so insert an
   1234     # implicit postbuild to cd to there.
   1235     postbuilds.insert(0, gyp.common.EncodePOSIXShellList(
   1236         ['cd', self.build_to_base]))
   1237     env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
   1238     # G will be non-null if any postbuild fails. Run all postbuilds in a
   1239     # subshell.
   1240     commands = env + ' (' + \
   1241         ' && '.join([ninja_syntax.escape(command) for command in postbuilds])
   1242     command_string = (commands + '); G=$$?; '
   1243                       # Remove the final output if any postbuild failed.
   1244                       '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
   1245     if is_command_start:
   1246       return '(' + command_string + ' && '
   1247     else:
   1248       return '$ && (' + command_string
   1249 
   1250   def ComputeExportEnvString(self, env):
   1251     """Given an environment, returns a string looking like
   1252         'export FOO=foo; export BAR="${FOO} bar;'
   1253     that exports |env| to the shell."""
   1254     export_str = []
   1255     for k, v in env:
   1256       export_str.append('export %s=%s;' %
   1257           (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v))))
   1258     return ' '.join(export_str)
   1259 
   1260   def ComputeMacBundleOutput(self):
   1261     """Return the 'output' (full output path) to a bundle output directory."""
   1262     assert self.is_mac_bundle
   1263     path = generator_default_variables['PRODUCT_DIR']
   1264     return self.ExpandSpecial(
   1265         os.path.join(path, self.xcode_settings.GetWrapperName()))
   1266 
   1267   def ComputeOutputFileName(self, spec, type=None):
   1268     """Compute the filename of the final output for the current target."""
   1269     if not type:
   1270       type = spec['type']
   1271 
   1272     default_variables = copy.copy(generator_default_variables)
   1273     CalculateVariables(default_variables, {'flavor': self.flavor})
   1274 
   1275     # Compute filename prefix: the product prefix, or a default for
   1276     # the product type.
   1277     DEFAULT_PREFIX = {
   1278       'loadable_module': default_variables['SHARED_LIB_PREFIX'],
   1279       'shared_library': default_variables['SHARED_LIB_PREFIX'],
   1280       'static_library': default_variables['STATIC_LIB_PREFIX'],
   1281       'executable': default_variables['EXECUTABLE_PREFIX'],
   1282       }
   1283     prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
   1284 
   1285     # Compute filename extension: the product extension, or a default
   1286     # for the product type.
   1287     DEFAULT_EXTENSION = {
   1288         'loadable_module': default_variables['SHARED_LIB_SUFFIX'],
   1289         'shared_library': default_variables['SHARED_LIB_SUFFIX'],
   1290         'static_library': default_variables['STATIC_LIB_SUFFIX'],
   1291         'executable': default_variables['EXECUTABLE_SUFFIX'],
   1292       }
   1293     extension = spec.get('product_extension')
   1294     if extension:
   1295       extension = '.' + extension
   1296     else:
   1297       extension = DEFAULT_EXTENSION.get(type, '')
   1298 
   1299     if 'product_name' in spec:
   1300       # If we were given an explicit name, use that.
   1301       target = spec['product_name']
   1302     else:
   1303       # Otherwise, derive a name from the target name.
   1304       target = spec['target_name']
   1305       if prefix == 'lib':
   1306         # Snip out an extra 'lib' from libs if appropriate.
   1307         target = StripPrefix(target, 'lib')
   1308 
   1309     if type in ('static_library', 'loadable_module', 'shared_library',
   1310                         'executable'):
   1311       return '%s%s%s' % (prefix, target, extension)
   1312     elif type == 'none':
   1313       return '%s.stamp' % target
   1314     else:
   1315       raise Exception('Unhandled output type %s' % type)
   1316 
   1317   def ComputeOutput(self, spec, arch=None):
   1318     """Compute the path for the final output of the spec."""
   1319     type = spec['type']
   1320 
   1321     if self.flavor == 'win':
   1322       override = self.msvs_settings.GetOutputName(self.config_name,
   1323                                                   self.ExpandSpecial)
   1324       if override:
   1325         return override
   1326 
   1327     if arch is None and self.flavor == 'mac' and type in (
   1328         'static_library', 'executable', 'shared_library', 'loadable_module'):
   1329       filename = self.xcode_settings.GetExecutablePath()
   1330     else:
   1331       filename = self.ComputeOutputFileName(spec, type)
   1332 
   1333     if arch is None and 'product_dir' in spec:
   1334       path = os.path.join(spec['product_dir'], filename)
   1335       return self.ExpandSpecial(path)
   1336 
   1337     # Some products go into the output root, libraries go into shared library
   1338     # dir, and everything else goes into the normal place.
   1339     type_in_output_root = ['executable', 'loadable_module']
   1340     if self.flavor == 'mac' and self.toolset == 'target':
   1341       type_in_output_root += ['shared_library', 'static_library']
   1342     elif self.flavor == 'win' and self.toolset == 'target':
   1343       type_in_output_root += ['shared_library']
   1344 
   1345     if arch is not None:
   1346       # Make sure partial executables don't end up in a bundle or the regular
   1347       # output directory.
   1348       archdir = 'arch'
   1349       if self.toolset != 'target':
   1350         archdir = os.path.join('arch', '%s' % self.toolset)
   1351       return os.path.join(archdir, AddArch(filename, arch))
   1352     elif type in type_in_output_root or self.is_standalone_static_library:
   1353       return filename
   1354     elif type == 'shared_library':
   1355       libdir = 'lib'
   1356       if self.toolset != 'target':
   1357         libdir = os.path.join('lib', '%s' % self.toolset)
   1358       return os.path.join(libdir, filename)
   1359     else:
   1360       return self.GypPathToUniqueOutput(filename, qualified=False)
   1361 
   1362   def WriteVariableList(self, ninja_file, var, values):
   1363     assert not isinstance(values, str)
   1364     if values is None:
   1365       values = []
   1366     ninja_file.variable(var, ' '.join(values))
   1367 
   1368   def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
   1369     """Write out a new ninja "rule" statement for a given command.
   1370 
   1371     Returns the name of the new rule, and a copy of |args| with variables
   1372     expanded."""
   1373 
   1374     if self.flavor == 'win':
   1375       args = [self.msvs_settings.ConvertVSMacros(
   1376                   arg, self.base_to_build, config=self.config_name)
   1377               for arg in args]
   1378       description = self.msvs_settings.ConvertVSMacros(
   1379           description, config=self.config_name)
   1380     elif self.flavor == 'mac':
   1381       # |env| is an empty list on non-mac.
   1382       args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args]
   1383       description = gyp.xcode_emulation.ExpandEnvVars(description, env)
   1384 
   1385     # TODO: we shouldn't need to qualify names; we do it because
   1386     # currently the ninja rule namespace is global, but it really
   1387     # should be scoped to the subninja.
   1388     rule_name = self.name
   1389     if self.toolset == 'target':
   1390       rule_name += '.' + self.toolset
   1391     rule_name += '.' + name
   1392     rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name)
   1393 
   1394     # Remove variable references, but not if they refer to the magic rule
   1395     # variables.  This is not quite right, as it also protects these for
   1396     # actions, not just for rules where they are valid. Good enough.
   1397     protect = [ '${root}', '${dirname}', '${source}', '${ext}', '${name}' ]
   1398     protect = '(?!' + '|'.join(map(re.escape, protect)) + ')'
   1399     description = re.sub(protect + r'\$', '_', description)
   1400 
   1401     # gyp dictates that commands are run from the base directory.
   1402     # cd into the directory before running, and adjust paths in
   1403     # the arguments to point to the proper locations.
   1404     rspfile = None
   1405     rspfile_content = None
   1406     args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
   1407     if self.flavor == 'win':
   1408       rspfile = rule_name + '.$unique_name.rsp'
   1409       # The cygwin case handles this inside the bash sub-shell.
   1410       run_in = '' if is_cygwin else ' ' + self.build_to_base
   1411       if is_cygwin:
   1412         rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine(
   1413             args, self.build_to_base)
   1414       else:
   1415         rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args)
   1416       command = ('%s gyp-win-tool action-wrapper $arch ' % sys.executable +
   1417                  rspfile + run_in)
   1418     else:
   1419       env = self.ComputeExportEnvString(env)
   1420       command = gyp.common.EncodePOSIXShellList(args)
   1421       command = 'cd %s; ' % self.build_to_base + env + command
   1422 
   1423     # GYP rules/actions express being no-ops by not touching their outputs.
   1424     # Avoid executing downstream dependencies in this case by specifying
   1425     # restat=1 to ninja.
   1426     self.ninja.rule(rule_name, command, description, restat=True,
   1427                     rspfile=rspfile, rspfile_content=rspfile_content)
   1428     self.ninja.newline()
   1429 
   1430     return rule_name, args
   1431 
   1432 
   1433 def CalculateVariables(default_variables, params):
   1434   """Calculate additional variables for use in the build (called by gyp)."""
   1435   global generator_additional_non_configuration_keys
   1436   global generator_additional_path_sections
   1437   flavor = gyp.common.GetFlavor(params)
   1438   if flavor == 'mac':
   1439     default_variables.setdefault('OS', 'mac')
   1440     default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
   1441     default_variables.setdefault('SHARED_LIB_DIR',
   1442                                  generator_default_variables['PRODUCT_DIR'])
   1443     default_variables.setdefault('LIB_DIR',
   1444                                  generator_default_variables['PRODUCT_DIR'])
   1445 
   1446     # Copy additional generator configuration data from Xcode, which is shared
   1447     # by the Mac Ninja generator.
   1448     import gyp.generator.xcode as xcode_generator
   1449     generator_additional_non_configuration_keys = getattr(xcode_generator,
   1450         'generator_additional_non_configuration_keys', [])
   1451     generator_additional_path_sections = getattr(xcode_generator,
   1452         'generator_additional_path_sections', [])
   1453     global generator_extra_sources_for_rules
   1454     generator_extra_sources_for_rules = getattr(xcode_generator,
   1455         'generator_extra_sources_for_rules', [])
   1456   elif flavor == 'win':
   1457     default_variables.setdefault('OS', 'win')
   1458     default_variables['EXECUTABLE_SUFFIX'] = '.exe'
   1459     default_variables['STATIC_LIB_PREFIX'] = ''
   1460     default_variables['STATIC_LIB_SUFFIX'] = '.lib'
   1461     default_variables['SHARED_LIB_PREFIX'] = ''
   1462     default_variables['SHARED_LIB_SUFFIX'] = '.dll'
   1463 
   1464     # Copy additional generator configuration data from VS, which is shared
   1465     # by the Windows Ninja generator.
   1466     import gyp.generator.msvs as msvs_generator
   1467     generator_additional_non_configuration_keys = getattr(msvs_generator,
   1468         'generator_additional_non_configuration_keys', [])
   1469     generator_additional_path_sections = getattr(msvs_generator,
   1470         'generator_additional_path_sections', [])
   1471 
   1472     gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
   1473   else:
   1474     operating_system = flavor
   1475     if flavor == 'android':
   1476       operating_system = 'linux'  # Keep this legacy behavior for now.
   1477     default_variables.setdefault('OS', operating_system)
   1478     default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
   1479     default_variables.setdefault('SHARED_LIB_DIR',
   1480                                  os.path.join('$!PRODUCT_DIR', 'lib'))
   1481     default_variables.setdefault('LIB_DIR',
   1482                                  os.path.join('$!PRODUCT_DIR', 'obj'))
   1483 
   1484 def ComputeOutputDir(params):
   1485   """Returns the path from the toplevel_dir to the build output directory."""
   1486   # generator_dir: relative path from pwd to where make puts build files.
   1487   # Makes migrating from make to ninja easier, ninja doesn't put anything here.
   1488   generator_dir = os.path.relpath(params['options'].generator_output or '.')
   1489 
   1490   # output_dir: relative path from generator_dir to the build directory.
   1491   output_dir = params.get('generator_flags', {}).get('output_dir', 'out')
   1492 
   1493   # Relative path from source root to our output files.  e.g. "out"
   1494   return os.path.normpath(os.path.join(generator_dir, output_dir))
   1495 
   1496 
   1497 def CalculateGeneratorInputInfo(params):
   1498   """Called by __init__ to initialize generator values based on params."""
   1499   # E.g. "out/gypfiles"
   1500   toplevel = params['options'].toplevel_dir
   1501   qualified_out_dir = os.path.normpath(os.path.join(
   1502       toplevel, ComputeOutputDir(params), 'gypfiles'))
   1503 
   1504   global generator_filelist_paths
   1505   generator_filelist_paths = {
   1506       'toplevel': toplevel,
   1507       'qualified_out_dir': qualified_out_dir,
   1508   }
   1509 
   1510 
   1511 def OpenOutput(path, mode='w'):
   1512   """Open |path| for writing, creating directories if necessary."""
   1513   gyp.common.EnsureDirExists(path)
   1514   return open(path, mode)
   1515 
   1516 
   1517 def CommandWithWrapper(cmd, wrappers, prog):
   1518   wrapper = wrappers.get(cmd, '')
   1519   if wrapper:
   1520     return wrapper + ' ' + prog
   1521   return prog
   1522 
   1523 
   1524 def GetDefaultConcurrentLinks():
   1525   """Returns a best-guess for a number of concurrent links."""
   1526   if sys.platform in ('win32', 'cygwin'):
   1527     import ctypes
   1528 
   1529     class MEMORYSTATUSEX(ctypes.Structure):
   1530       _fields_ = [
   1531         ("dwLength", ctypes.c_ulong),
   1532         ("dwMemoryLoad", ctypes.c_ulong),
   1533         ("ullTotalPhys", ctypes.c_ulonglong),
   1534         ("ullAvailPhys", ctypes.c_ulonglong),
   1535         ("ullTotalPageFile", ctypes.c_ulonglong),
   1536         ("ullAvailPageFile", ctypes.c_ulonglong),
   1537         ("ullTotalVirtual", ctypes.c_ulonglong),
   1538         ("ullAvailVirtual", ctypes.c_ulonglong),
   1539         ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
   1540       ]
   1541 
   1542     stat = MEMORYSTATUSEX()
   1543     stat.dwLength = ctypes.sizeof(stat)
   1544     ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
   1545 
   1546     mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30)))  # total / 4GB
   1547     hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
   1548     return min(mem_limit, hard_cap)
   1549   elif sys.platform.startswith('linux'):
   1550     with open("/proc/meminfo") as meminfo:
   1551       memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
   1552       for line in meminfo:
   1553         match = memtotal_re.match(line)
   1554         if not match:
   1555           continue
   1556         # Allow 8Gb per link on Linux because Gold is quite memory hungry
   1557         return max(1, int(match.group(1)) / (8 * (2 ** 20)))
   1558     return 1
   1559   elif sys.platform == 'darwin':
   1560     try:
   1561       avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
   1562       # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
   1563       # 4GB per ld process allows for some more bloat.
   1564       return max(1, avail_bytes / (4 * (2 ** 30)))  # total / 4GB
   1565     except:
   1566       return 1
   1567   else:
   1568     # TODO(scottmg): Implement this for other platforms.
   1569     return 1
   1570 
   1571 
   1572 def _GetWinLinkRuleNameSuffix(embed_manifest):
   1573   """Returns the suffix used to select an appropriate linking rule depending on
   1574   whether the manifest embedding is enabled."""
   1575   return '_embed' if embed_manifest else ''
   1576 
   1577 
   1578 def _AddWinLinkRules(master_ninja, embed_manifest):
   1579   """Adds link rules for Windows platform to |master_ninja|."""
   1580   def FullLinkCommand(ldcmd, out, binary_type):
   1581     resource_name = {
   1582       'exe': '1',
   1583       'dll': '2',
   1584     }[binary_type]
   1585     return '%(python)s gyp-win-tool link-with-manifests $arch %(embed)s ' \
   1586            '%(out)s "%(ldcmd)s" %(resname)s $mt $rc "$intermediatemanifest" ' \
   1587            '$manifests' % {
   1588                'python': sys.executable,
   1589                'out': out,
   1590                'ldcmd': ldcmd,
   1591                'resname': resource_name,
   1592                'embed': embed_manifest }
   1593   rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest)
   1594   dlldesc = 'LINK%s(DLL) $dll' % rule_name_suffix.upper()
   1595   dllcmd = ('%s gyp-win-tool link-wrapper $arch '
   1596             '$ld /nologo $implibflag /DLL /OUT:$dll '
   1597             '/PDB:$dll.pdb @$dll.rsp' % sys.executable)
   1598   dllcmd = FullLinkCommand(dllcmd, '$dll', 'dll')
   1599   master_ninja.rule('solink' + rule_name_suffix,
   1600                     description=dlldesc, command=dllcmd,
   1601                     rspfile='$dll.rsp',
   1602                     rspfile_content='$libs $in_newline $ldflags',
   1603                     restat=True,
   1604                     pool='link_pool')
   1605   master_ninja.rule('solink_module' + rule_name_suffix,
   1606                     description=dlldesc, command=dllcmd,
   1607                     rspfile='$dll.rsp',
   1608                     rspfile_content='$libs $in_newline $ldflags',
   1609                     restat=True,
   1610                     pool='link_pool')
   1611   # Note that ldflags goes at the end so that it has the option of
   1612   # overriding default settings earlier in the command line.
   1613   exe_cmd = ('%s gyp-win-tool link-wrapper $arch '
   1614              '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
   1615               sys.executable)
   1616   exe_cmd = FullLinkCommand(exe_cmd, '$out', 'exe')
   1617   master_ninja.rule('link' + rule_name_suffix,
   1618                     description='LINK%s $out' % rule_name_suffix.upper(),
   1619                     command=exe_cmd,
   1620                     rspfile='$out.rsp',
   1621                     rspfile_content='$in_newline $libs $ldflags',
   1622                     pool='link_pool')
   1623 
   1624 
   1625 def GenerateOutputForConfig(target_list, target_dicts, data, params,
   1626                             config_name):
   1627   options = params['options']
   1628   flavor = gyp.common.GetFlavor(params)
   1629   generator_flags = params.get('generator_flags', {})
   1630 
   1631   # build_dir: relative path from source root to our output files.
   1632   # e.g. "out/Debug"
   1633   build_dir = os.path.normpath(
   1634       os.path.join(ComputeOutputDir(params), config_name))
   1635 
   1636   toplevel_build = os.path.join(options.toplevel_dir, build_dir)
   1637 
   1638   master_ninja_file = OpenOutput(os.path.join(toplevel_build, 'build.ninja'))
   1639   master_ninja = ninja_syntax.Writer(master_ninja_file, width=120)
   1640 
   1641   # Put build-time support tools in out/{config_name}.
   1642   gyp.common.CopyTool(flavor, toplevel_build)
   1643 
   1644   # Grab make settings for CC/CXX.
   1645   # The rules are
   1646   # - The priority from low to high is gcc/g++, the 'make_global_settings' in
   1647   #   gyp, the environment variable.
   1648   # - If there is no 'make_global_settings' for CC.host/CXX.host or
   1649   #   'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
   1650   #   to cc/cxx.
   1651   if flavor == 'win':
   1652     # Overridden by local arch choice in the use_deps case.
   1653     # Chromium's ffmpeg c99conv.py currently looks for a 'cc =' line in
   1654     # build.ninja so needs something valid here. http://crbug.com/233985
   1655     cc = 'cl.exe'
   1656     cxx = 'cl.exe'
   1657     ld = 'link.exe'
   1658     ld_host = '$ld'
   1659   else:
   1660     cc = 'cc'
   1661     cxx = 'c++'
   1662     ld = '$cc'
   1663     ldxx = '$cxx'
   1664     ld_host = '$cc_host'
   1665     ldxx_host = '$cxx_host'
   1666 
   1667   cc_host = None
   1668   cxx_host = None
   1669   cc_host_global_setting = None
   1670   cxx_host_global_setting = None
   1671 
   1672   build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
   1673   make_global_settings = data[build_file].get('make_global_settings', [])
   1674   build_to_root = gyp.common.InvertRelativePath(build_dir,
   1675                                                 options.toplevel_dir)
   1676   wrappers = {}
   1677   for key, value in make_global_settings:
   1678     if key == 'CC':
   1679       cc = os.path.join(build_to_root, value)
   1680     if key == 'CXX':
   1681       cxx = os.path.join(build_to_root, value)
   1682     if key == 'CC.host':
   1683       cc_host = os.path.join(build_to_root, value)
   1684       cc_host_global_setting = value
   1685     if key == 'CXX.host':
   1686       cxx_host = os.path.join(build_to_root, value)
   1687       cxx_host_global_setting = value
   1688     if key.endswith('_wrapper'):
   1689       wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
   1690 
   1691   # Support wrappers from environment variables too.
   1692   for key, value in os.environ.iteritems():
   1693     if key.lower().endswith('_wrapper'):
   1694       key_prefix = key[:-len('_wrapper')]
   1695       key_prefix = re.sub(r'\.HOST$', '.host', key_prefix)
   1696       wrappers[key_prefix] = os.path.join(build_to_root, value)
   1697 
   1698   if flavor == 'win':
   1699     cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
   1700         toplevel_build, generator_flags, OpenOutput)
   1701     for arch, path in cl_paths.iteritems():
   1702       master_ninja.variable(
   1703           'cl_' + arch, CommandWithWrapper('CC', wrappers,
   1704                                            QuoteShellArgument(path, flavor)))
   1705 
   1706   cc = GetEnvironFallback(['CC_target', 'CC'], cc)
   1707   master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
   1708   cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
   1709   master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx))
   1710 
   1711   if flavor == 'win':
   1712     master_ninja.variable('ld', ld)
   1713     master_ninja.variable('idl', 'midl.exe')
   1714     master_ninja.variable('ar', 'lib.exe')
   1715     master_ninja.variable('rc', 'rc.exe')
   1716     master_ninja.variable('asm', 'ml.exe')
   1717     master_ninja.variable('mt', 'mt.exe')
   1718   else:
   1719     master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
   1720     master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
   1721     master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
   1722 
   1723   if generator_supports_multiple_toolsets:
   1724     if not cc_host:
   1725       cc_host = cc
   1726     if not cxx_host:
   1727       cxx_host = cxx
   1728 
   1729     master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
   1730     cc_host = GetEnvironFallback(['CC_host'], cc_host)
   1731     cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
   1732 
   1733     # The environment variable could be used in 'make_global_settings', like
   1734     # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
   1735     if '$(CC)' in cc_host and cc_host_global_setting:
   1736       cc_host = cc_host_global_setting.replace('$(CC)', cc)
   1737     if '$(CXX)' in cxx_host and cxx_host_global_setting:
   1738       cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
   1739     master_ninja.variable('cc_host',
   1740                           CommandWithWrapper('CC.host', wrappers, cc_host))
   1741     master_ninja.variable('cxx_host',
   1742                           CommandWithWrapper('CXX.host', wrappers, cxx_host))
   1743     if flavor == 'win':
   1744       master_ninja.variable('ld_host', ld_host)
   1745     else:
   1746       master_ninja.variable('ld_host', CommandWithWrapper(
   1747           'LINK', wrappers, ld_host))
   1748       master_ninja.variable('ldxx_host', CommandWithWrapper(
   1749           'LINK', wrappers, ldxx_host))
   1750 
   1751   master_ninja.newline()
   1752 
   1753   master_ninja.pool('link_pool', depth=GetDefaultConcurrentLinks())
   1754   master_ninja.newline()
   1755 
   1756   deps = 'msvc' if flavor == 'win' else 'gcc'
   1757 
   1758   if flavor != 'win':
   1759     master_ninja.rule(
   1760       'cc',
   1761       description='CC $out',
   1762       command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
   1763               '$cflags_pch_c -c $in -o $out'),
   1764       depfile='$out.d',
   1765       deps=deps)
   1766     master_ninja.rule(
   1767       'cc_s',
   1768       description='CC $out',
   1769       command=('$cc $defines $includes $cflags $cflags_c '
   1770               '$cflags_pch_c -c $in -o $out'))
   1771     master_ninja.rule(
   1772       'cxx',
   1773       description='CXX $out',
   1774       command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
   1775               '$cflags_pch_cc -c $in -o $out'),
   1776       depfile='$out.d',
   1777       deps=deps)
   1778   else:
   1779     # TODO(scottmg) Separate pdb names is a test to see if it works around
   1780     # http://crbug.com/142362. It seems there's a race between the creation of
   1781     # the .pdb by the precompiled header step for .cc and the compilation of
   1782     # .c files. This should be handled by mspdbsrv, but rarely errors out with
   1783     #   c1xx : fatal error C1033: cannot open program database
   1784     # By making the rules target separate pdb files this might be avoided.
   1785     cc_command = ('ninja -t msvc -e $arch ' +
   1786                   '-- '
   1787                   '$cc /nologo /showIncludes /FC '
   1788                   '@$out.rsp /c $in /Fo$out /Fd$pdbname_c ')
   1789     cxx_command = ('ninja -t msvc -e $arch ' +
   1790                    '-- '
   1791                    '$cxx /nologo /showIncludes /FC '
   1792                    '@$out.rsp /c $in /Fo$out /Fd$pdbname_cc ')
   1793     master_ninja.rule(
   1794       'cc',
   1795       description='CC $out',
   1796       command=cc_command,
   1797       rspfile='$out.rsp',
   1798       rspfile_content='$defines $includes $cflags $cflags_c',
   1799       deps=deps)
   1800     master_ninja.rule(
   1801       'cxx',
   1802       description='CXX $out',
   1803       command=cxx_command,
   1804       rspfile='$out.rsp',
   1805       rspfile_content='$defines $includes $cflags $cflags_cc',
   1806       deps=deps)
   1807     master_ninja.rule(
   1808       'idl',
   1809       description='IDL $in',
   1810       command=('%s gyp-win-tool midl-wrapper $arch $outdir '
   1811                '$tlb $h $dlldata $iid $proxy $in '
   1812                '$idlflags' % sys.executable))
   1813     master_ninja.rule(
   1814       'rc',
   1815       description='RC $in',
   1816       # Note: $in must be last otherwise rc.exe complains.
   1817       command=('%s gyp-win-tool rc-wrapper '
   1818                '$arch $rc $defines $resource_includes $rcflags /fo$out $in' %
   1819                sys.executable))
   1820     master_ninja.rule(
   1821       'asm',
   1822       description='ASM $in',
   1823       command=('%s gyp-win-tool asm-wrapper '
   1824                '$arch $asm $defines $includes /c /Fo $out $in' %
   1825                sys.executable))
   1826 
   1827   if flavor != 'mac' and flavor != 'win':
   1828     master_ninja.rule(
   1829       'alink',
   1830       description='AR $out',
   1831       command='rm -f $out && $ar rcs $out $in')
   1832     master_ninja.rule(
   1833       'alink_thin',
   1834       description='AR $out',
   1835       command='rm -f $out && $ar rcsT $out $in')
   1836 
   1837     # This allows targets that only need to depend on $lib's API to declare an
   1838     # order-only dependency on $lib.TOC and avoid relinking such downstream
   1839     # dependencies when $lib changes only in non-public ways.
   1840     # The resulting string leaves an uninterpolated %{suffix} which
   1841     # is used in the final substitution below.
   1842     mtime_preserving_solink_base = (
   1843         'if [ ! -e $lib -o ! -e ${lib}.TOC ]; then '
   1844         '%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
   1845         '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
   1846         'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
   1847         'fi; fi'
   1848         % { 'solink':
   1849               '$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
   1850             'extract_toc':
   1851               ('{ readelf -d ${lib} | grep SONAME ; '
   1852                'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
   1853 
   1854     master_ninja.rule(
   1855       'solink',
   1856       description='SOLINK $lib',
   1857       restat=True,
   1858       command=(mtime_preserving_solink_base % {
   1859           'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
   1860           '$libs'}),
   1861       pool='link_pool')
   1862     master_ninja.rule(
   1863       'solink_module',
   1864       description='SOLINK(module) $lib',
   1865       restat=True,
   1866       command=(mtime_preserving_solink_base % {
   1867           'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
   1868           '$libs'}),
   1869       pool='link_pool')
   1870     master_ninja.rule(
   1871       'link',
   1872       description='LINK $out',
   1873       command=('$ld $ldflags -o $out '
   1874                '-Wl,--start-group $in $solibs -Wl,--end-group $libs'),
   1875       pool='link_pool')
   1876   elif flavor == 'win':
   1877     master_ninja.rule(
   1878         'alink',
   1879         description='LIB $out',
   1880         command=('%s gyp-win-tool link-wrapper $arch '
   1881                  '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
   1882                  sys.executable),
   1883         rspfile='$out.rsp',
   1884         rspfile_content='$in_newline $libflags')
   1885     _AddWinLinkRules(master_ninja, embed_manifest=True)
   1886     _AddWinLinkRules(master_ninja, embed_manifest=False)
   1887   else:
   1888     master_ninja.rule(
   1889       'objc',
   1890       description='OBJC $out',
   1891       command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
   1892                '$cflags_pch_objc -c $in -o $out'),
   1893       depfile='$out.d',
   1894       deps=deps)
   1895     master_ninja.rule(
   1896       'objcxx',
   1897       description='OBJCXX $out',
   1898       command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
   1899                '$cflags_pch_objcc -c $in -o $out'),
   1900       depfile='$out.d',
   1901       deps=deps)
   1902     master_ninja.rule(
   1903       'alink',
   1904       description='LIBTOOL-STATIC $out, POSTBUILDS',
   1905       command='rm -f $out && '
   1906               './gyp-mac-tool filter-libtool libtool $libtool_flags '
   1907               '-static -o $out $in'
   1908               '$postbuilds')
   1909     master_ninja.rule(
   1910       'lipo',
   1911       description='LIPO $out, POSTBUILDS',
   1912       command='rm -f $out && lipo -create $in -output $out$postbuilds')
   1913 
   1914     # Record the public interface of $lib in $lib.TOC. See the corresponding
   1915     # comment in the posix section above for details.
   1916     solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
   1917     mtime_preserving_solink_base = (
   1918         'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
   1919              # Always force dependent targets to relink if this library
   1920              # reexports something. Handling this correctly would require
   1921              # recursive TOC dumping but this is rare in practice, so punt.
   1922              'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
   1923           '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
   1924         'else '
   1925           '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
   1926           'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
   1927             'mv ${lib}.tmp ${lib}.TOC ; '
   1928           'fi; '
   1929         'fi'
   1930         % { 'solink': solink_base,
   1931             'extract_toc':
   1932               '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
   1933               'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
   1934 
   1935     solink_suffix = '$in $solibs $libs$postbuilds'
   1936     master_ninja.rule(
   1937       'solink',
   1938       description='SOLINK $lib, POSTBUILDS',
   1939       restat=True,
   1940       command=mtime_preserving_solink_base % {'suffix': solink_suffix,
   1941                                               'type': '-shared'},
   1942       pool='link_pool')
   1943     master_ninja.rule(
   1944       'solink_notoc',
   1945       description='SOLINK $lib, POSTBUILDS',
   1946       restat=True,
   1947       command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
   1948       pool='link_pool')
   1949 
   1950     solink_module_suffix = '$in $solibs $libs$postbuilds'
   1951     master_ninja.rule(
   1952       'solink_module',
   1953       description='SOLINK(module) $lib, POSTBUILDS',
   1954       restat=True,
   1955       command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
   1956                                               'type': '-bundle'},
   1957       pool='link_pool')
   1958     master_ninja.rule(
   1959       'solink_module_notoc',
   1960       description='SOLINK(module) $lib, POSTBUILDS',
   1961       restat=True,
   1962       command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
   1963       pool='link_pool')
   1964 
   1965     master_ninja.rule(
   1966       'link',
   1967       description='LINK $out, POSTBUILDS',
   1968       command=('$ld $ldflags -o $out '
   1969                '$in $solibs $libs$postbuilds'),
   1970       pool='link_pool')
   1971     master_ninja.rule(
   1972       'preprocess_infoplist',
   1973       description='PREPROCESS INFOPLIST $out',
   1974       command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
   1975                'plutil -convert xml1 $out $out'))
   1976     master_ninja.rule(
   1977       'copy_infoplist',
   1978       description='COPY INFOPLIST $in',
   1979       command='$env ./gyp-mac-tool copy-info-plist $in $out $keys')
   1980     master_ninja.rule(
   1981       'mac_tool',
   1982       description='MACTOOL $mactool_cmd $in',
   1983       command='$env ./gyp-mac-tool $mactool_cmd $in $out')
   1984     master_ninja.rule(
   1985       'package_framework',
   1986       description='PACKAGE FRAMEWORK $out, POSTBUILDS',
   1987       command='./gyp-mac-tool package-framework $out $version$postbuilds '
   1988               '&& touch $out')
   1989   if flavor == 'win':
   1990     master_ninja.rule(
   1991       'stamp',
   1992       description='STAMP $out',
   1993       command='%s gyp-win-tool stamp $out' % sys.executable)
   1994     master_ninja.rule(
   1995       'copy',
   1996       description='COPY $in $out',
   1997       command='%s gyp-win-tool recursive-mirror $in $out' % sys.executable)
   1998   else:
   1999     master_ninja.rule(
   2000       'stamp',
   2001       description='STAMP $out',
   2002       command='${postbuilds}touch $out')
   2003     master_ninja.rule(
   2004       'copy',
   2005       description='COPY $in $out',
   2006       command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
   2007   master_ninja.newline()
   2008 
   2009   all_targets = set()
   2010   for build_file in params['build_files']:
   2011     for target in gyp.common.AllTargets(target_list,
   2012                                         target_dicts,
   2013                                         os.path.normpath(build_file)):
   2014       all_targets.add(target)
   2015   all_outputs = set()
   2016 
   2017   # target_outputs is a map from qualified target name to a Target object.
   2018   target_outputs = {}
   2019   # target_short_names is a map from target short name to a list of Target
   2020   # objects.
   2021   target_short_names = {}
   2022 
   2023   for qualified_target in target_list:
   2024     # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
   2025     build_file, name, toolset = \
   2026         gyp.common.ParseQualifiedTarget(qualified_target)
   2027 
   2028     this_make_global_settings = data[build_file].get('make_global_settings', [])
   2029     assert make_global_settings == this_make_global_settings, (
   2030         "make_global_settings needs to be the same for all targets.")
   2031 
   2032     spec = target_dicts[qualified_target]
   2033     if flavor == 'mac':
   2034       gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
   2035 
   2036     build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
   2037 
   2038     base_path = os.path.dirname(build_file)
   2039     obj = 'obj'
   2040     if toolset != 'target':
   2041       obj += '.' + toolset
   2042     output_file = os.path.join(obj, base_path, name + '.ninja')
   2043 
   2044     ninja_output = StringIO()
   2045     writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
   2046                          ninja_output,
   2047                          toplevel_build, output_file,
   2048                          flavor, toplevel_dir=options.toplevel_dir)
   2049 
   2050     target = writer.WriteSpec(spec, config_name, generator_flags)
   2051 
   2052     if ninja_output.tell() > 0:
   2053       # Only create files for ninja files that actually have contents.
   2054       with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file:
   2055         ninja_file.write(ninja_output.getvalue())
   2056       ninja_output.close()
   2057       master_ninja.subninja(output_file)
   2058 
   2059     if target:
   2060       if name != target.FinalOutput() and spec['toolset'] == 'target':
   2061         target_short_names.setdefault(name, []).append(target)
   2062       target_outputs[qualified_target] = target
   2063       if qualified_target in all_targets:
   2064         all_outputs.add(target.FinalOutput())
   2065 
   2066   if target_short_names:
   2067     # Write a short name to build this target.  This benefits both the
   2068     # "build chrome" case as well as the gyp tests, which expect to be
   2069     # able to run actions and build libraries by their short name.
   2070     master_ninja.newline()
   2071     master_ninja.comment('Short names for targets.')
   2072     for short_name in target_short_names:
   2073       master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
   2074                                                target_short_names[short_name]])
   2075 
   2076   if all_outputs:
   2077     master_ninja.newline()
   2078     master_ninja.build('all', 'phony', list(all_outputs))
   2079     master_ninja.default(generator_flags.get('default_target', 'all'))
   2080 
   2081   master_ninja_file.close()
   2082 
   2083 
   2084 def PerformBuild(data, configurations, params):
   2085   options = params['options']
   2086   for config in configurations:
   2087     builddir = os.path.join(options.toplevel_dir, 'out', config)
   2088     arguments = ['ninja', '-C', builddir]
   2089     print 'Building [%s]: %s' % (config, arguments)
   2090     subprocess.check_call(arguments)
   2091 
   2092 
   2093 def CallGenerateOutputForConfig(arglist):
   2094   # Ignore the interrupt signal so that the parent process catches it and
   2095   # kills all multiprocessing children.
   2096   signal.signal(signal.SIGINT, signal.SIG_IGN)
   2097 
   2098   (target_list, target_dicts, data, params, config_name) = arglist
   2099   GenerateOutputForConfig(target_list, target_dicts, data, params, config_name)
   2100 
   2101 
   2102 def GenerateOutput(target_list, target_dicts, data, params):
   2103   # Update target_dicts for iOS device builds.
   2104   target_dicts = gyp.xcode_emulation.CloneConfigurationForDeviceAndEmulator(
   2105       target_dicts)
   2106 
   2107   user_config = params.get('generator_flags', {}).get('config', None)
   2108   if gyp.common.GetFlavor(params) == 'win':
   2109     target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
   2110     target_list, target_dicts = MSVSUtil.InsertLargePdbShims(
   2111         target_list, target_dicts, generator_default_variables)
   2112 
   2113   if user_config:
   2114     GenerateOutputForConfig(target_list, target_dicts, data, params,
   2115                             user_config)
   2116   else:
   2117     config_names = target_dicts[target_list[0]]['configurations'].keys()
   2118     if params['parallel']:
   2119       try:
   2120         pool = multiprocessing.Pool(len(config_names))
   2121         arglists = []
   2122         for config_name in config_names:
   2123           arglists.append(
   2124               (target_list, target_dicts, data, params, config_name))
   2125         pool.map(CallGenerateOutputForConfig, arglists)
   2126       except KeyboardInterrupt, e:
   2127         pool.terminate()
   2128         raise e
   2129     else:
   2130       for config_name in config_names:
   2131         GenerateOutputForConfig(target_list, target_dicts, data, params,
   2132                                 config_name)
   2133