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