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