"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py" (11 Apr 2017, 100329 Bytes) of package /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

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