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