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