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