23fb9b8afd90979394ce408156217dd75c589538
[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       pdbpath = self.msvs_settings.GetCompilerPdbName(
818           config_name, self.ExpandSpecial)
819       if not pdbpath:
820         obj = 'obj'
821         if self.toolset != 'target':
822           obj += '.' + self.toolset
823         pdbpath = os.path.normpath(os.path.join(obj, self.base_dir,
824                                                 self.name + '.pdb'))
825       self.WriteVariableList(ninja_file, 'pdbname', [pdbpath])
826       self.WriteVariableList(ninja_file, 'pchprefix', [self.name])
827     else:
828       cflags = config.get('cflags', [])
829       cflags_c = config.get('cflags_c', [])
830       cflags_cc = config.get('cflags_cc', [])
831
832     # Respect environment variables related to build, but target-specific
833     # flags can still override them.
834     if self.toolset == 'target':
835       cflags_c = (os.environ.get('CPPFLAGS', '').split() +
836                   os.environ.get('CFLAGS', '').split() + cflags_c)
837       cflags_cc = (os.environ.get('CPPFLAGS', '').split() +
838                    os.environ.get('CXXFLAGS', '').split() + cflags_cc)
839
840     defines = config.get('defines', []) + extra_defines
841     self.WriteVariableList(ninja_file, 'defines',
842                            [Define(d, self.flavor) for d in defines])
843     if self.flavor == 'win':
844       self.WriteVariableList(ninja_file, 'rcflags',
845           [QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
846            for f in self.msvs_settings.GetRcflags(config_name,
847                                                   self.GypPathToNinja)])
848
849     include_dirs = config.get('include_dirs', [])
850     env = self.GetSortedXcodeEnv()
851     if self.flavor == 'win':
852       env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
853                                              config=config_name)
854       include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
855                                                           config_name)
856     self.WriteVariableList(ninja_file, 'includes',
857         [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
858          for i in include_dirs])
859
860     pch_commands = precompiled_header.GetPchBuildCommands(arch)
861     if self.flavor == 'mac':
862       # Most targets use no precompiled headers, so only write these if needed.
863       for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'),
864                        ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]:
865         include = precompiled_header.GetInclude(ext, arch)
866         if include: ninja_file.variable(var, include)
867
868     self.WriteVariableList(ninja_file, 'cflags',
869                            map(self.ExpandSpecial, cflags))
870     self.WriteVariableList(ninja_file, 'cflags_c',
871                            map(self.ExpandSpecial, cflags_c))
872     self.WriteVariableList(ninja_file, 'cflags_cc',
873                            map(self.ExpandSpecial, cflags_cc))
874     if self.flavor == 'mac':
875       self.WriteVariableList(ninja_file, 'cflags_objc',
876                              map(self.ExpandSpecial, cflags_objc))
877       self.WriteVariableList(ninja_file, 'cflags_objcc',
878                              map(self.ExpandSpecial, cflags_objcc))
879     ninja_file.newline()
880     outputs = []
881     has_rc_source = False
882     for source in sources:
883       filename, ext = os.path.splitext(source)
884       ext = ext[1:]
885       obj_ext = self.obj_ext
886       if ext in ('cc', 'cpp', 'cxx'):
887         command = 'cxx'
888         self.uses_cpp = True
889       elif ext == 'c' or (ext == 'S' and self.flavor != 'win'):
890         command = 'cc'
891       elif ext == 's' and self.flavor != 'win':  # Doesn't generate .o.d files.
892         command = 'cc_s'
893       elif (self.flavor == 'win' and ext == 'asm' and
894             self.msvs_settings.GetArch(config_name) == 'x86' and
895             not self.msvs_settings.HasExplicitAsmRules(spec)):
896         # Asm files only get auto assembled for x86 (not x64).
897         command = 'asm'
898         # Add the _asm suffix as msvs is capable of handling .cc and
899         # .asm files of the same name without collision.
900         obj_ext = '_asm.obj'
901       elif self.flavor == 'mac' and ext == 'm':
902         command = 'objc'
903       elif self.flavor == 'mac' and ext == 'mm':
904         command = 'objcxx'
905         self.uses_cpp = True
906       elif self.flavor == 'win' and ext == 'rc':
907         command = 'rc'
908         obj_ext = '.res'
909         has_rc_source = True
910       else:
911         # Ignore unhandled extensions.
912         continue
913       input = self.GypPathToNinja(source)
914       output = self.GypPathToUniqueOutput(filename + obj_ext)
915       if arch is not None:
916         output = AddArch(output, arch)
917       implicit = precompiled_header.GetObjDependencies([input], [output], arch)
918       variables = []
919       if self.flavor == 'win':
920         variables, output, implicit = precompiled_header.GetFlagsModifications(
921             input, output, implicit, command, cflags_c, cflags_cc,
922             self.ExpandSpecial)
923       ninja_file.build(output, command, input,
924                        implicit=[gch for _, _, gch in implicit],
925                        order_only=predepends, variables=variables)
926       outputs.append(output)
927
928     if has_rc_source:
929       resource_include_dirs = config.get('resource_include_dirs', include_dirs)
930       self.WriteVariableList(ninja_file, 'resource_includes',
931           [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
932            for i in resource_include_dirs])
933
934     self.WritePchTargets(ninja_file, pch_commands)
935
936     ninja_file.newline()
937     return outputs
938
939   def WritePchTargets(self, ninja_file, pch_commands):
940     """Writes ninja rules to compile prefix headers."""
941     if not pch_commands:
942       return
943
944     for gch, lang_flag, lang, input in pch_commands:
945       var_name = {
946         'c': 'cflags_pch_c',
947         'cc': 'cflags_pch_cc',
948         'm': 'cflags_pch_objc',
949         'mm': 'cflags_pch_objcc',
950       }[lang]
951
952       map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
953       cmd = map.get(lang)
954       ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
955
956   def WriteLink(self, spec, config_name, config, link_deps):
957     """Write out a link step. Fills out target.binary. """
958     if self.flavor != 'mac' or len(self.archs) == 1:
959       return self.WriteLinkForArch(
960           self.ninja, spec, config_name, config, link_deps)
961     else:
962       output = self.ComputeOutput(spec)
963       inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec,
964                                       config_name, config, link_deps[arch],
965                                       arch=arch)
966                 for arch in self.archs]
967       extra_bindings = []
968       if not self.is_mac_bundle:
969         self.AppendPostbuildVariable(extra_bindings, spec, output, output)
970       self.ninja.build(output, 'lipo', inputs, variables=extra_bindings)
971       return output
972
973   def WriteLinkForArch(self, ninja_file, spec, config_name, config,
974                        link_deps, arch=None):
975     """Write out a link step. Fills out target.binary. """
976     command = {
977       'executable':      'link',
978       'loadable_module': 'solink_module',
979       'shared_library':  'solink',
980     }[spec['type']]
981     command_suffix = ''
982
983     implicit_deps = set()
984     solibs = set()
985
986     if 'dependencies' in spec:
987       # Two kinds of dependencies:
988       # - Linkable dependencies (like a .a or a .so): add them to the link line.
989       # - Non-linkable dependencies (like a rule that generates a file
990       #   and writes a stamp file): add them to implicit_deps
991       extra_link_deps = set()
992       for dep in spec['dependencies']:
993         target = self.target_outputs.get(dep)
994         if not target:
995           continue
996         linkable = target.Linkable()
997         if linkable:
998           new_deps = []
999           if (self.flavor == 'win' and
1000               target.component_objs and
1001               self.msvs_settings.IsUseLibraryDependencyInputs(config_name)):
1002             new_deps = target.component_objs
1003           elif self.flavor == 'win' and target.import_lib:
1004             new_deps = [target.import_lib]
1005           elif target.UsesToc(self.flavor):
1006             solibs.add(target.binary)
1007             implicit_deps.add(target.binary + '.TOC')
1008           else:
1009             new_deps = [target.binary]
1010           for new_dep in new_deps:
1011             if new_dep not in extra_link_deps:
1012               extra_link_deps.add(new_dep)
1013               link_deps.append(new_dep)
1014
1015         final_output = target.FinalOutput()
1016         if not linkable or final_output != target.binary:
1017           implicit_deps.add(final_output)
1018
1019     extra_bindings = []
1020     if self.uses_cpp and self.flavor != 'win':
1021       extra_bindings.append(('ld', '$ldxx'))
1022
1023     output = self.ComputeOutput(spec, arch)
1024     if arch is None and not self.is_mac_bundle:
1025       self.AppendPostbuildVariable(extra_bindings, spec, output, output)
1026
1027     is_executable = spec['type'] == 'executable'
1028     # The ldflags config key is not used on mac or win. On those platforms
1029     # linker flags are set via xcode_settings and msvs_settings, respectively.
1030     env_ldflags = os.environ.get('LDFLAGS', '').split()
1031     if self.flavor == 'mac':
1032       ldflags = self.xcode_settings.GetLdflags(config_name,
1033           self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
1034           self.GypPathToNinja, arch)
1035       ldflags = env_ldflags + ldflags
1036     elif self.flavor == 'win':
1037       manifest_name = self.GypPathToUniqueOutput(
1038           self.ComputeOutputFileName(spec))
1039       ldflags, manifest_files = self.msvs_settings.GetLdflags(config_name,
1040           self.GypPathToNinja, self.ExpandSpecial, manifest_name, is_executable)
1041       ldflags = env_ldflags + ldflags
1042       self.WriteVariableList(ninja_file, 'manifests', manifest_files)
1043       command_suffix = _GetWinLinkRuleNameSuffix(
1044           self.msvs_settings.IsEmbedManifest(config_name),
1045           self.msvs_settings.IsLinkIncremental(config_name))
1046       def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja)
1047       if def_file:
1048         implicit_deps.add(def_file)
1049     else:
1050       # Respect environment variables related to build, but target-specific
1051       # flags can still override them.
1052       ldflags = env_ldflags + config.get('ldflags', [])
1053       if is_executable and len(solibs):
1054         rpath = 'lib/'
1055         if self.toolset != 'target':
1056           rpath += self.toolset
1057         ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
1058         ldflags.append('-Wl,-rpath-link=%s' % rpath)
1059     self.WriteVariableList(ninja_file, 'ldflags',
1060                            gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
1061
1062     library_dirs = config.get('library_dirs', [])
1063     if self.flavor == 'win':
1064       library_dirs = [self.msvs_settings.ConvertVSMacros(l, config_name)
1065                       for l in library_dirs]
1066       library_dirs = ['/LIBPATH:' + QuoteShellArgument(self.GypPathToNinja(l),
1067                                                        self.flavor)
1068                       for l in library_dirs]
1069     else:
1070       library_dirs = [QuoteShellArgument('-L' + self.GypPathToNinja(l),
1071                                          self.flavor)
1072                       for l in library_dirs]
1073
1074     libraries = gyp.common.uniquer(map(self.ExpandSpecial,
1075                                        spec.get('libraries', [])))
1076     if self.flavor == 'mac':
1077       libraries = self.xcode_settings.AdjustLibraries(libraries, config_name)
1078     elif self.flavor == 'win':
1079       libraries = self.msvs_settings.AdjustLibraries(libraries)
1080
1081     self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries)
1082
1083     linked_binary = output
1084
1085     if command in ('solink', 'solink_module'):
1086       extra_bindings.append(('soname', os.path.split(output)[1]))
1087       extra_bindings.append(('lib',
1088                             gyp.common.EncodePOSIXShellArgument(output)))
1089       if self.flavor == 'win':
1090         extra_bindings.append(('dll', output))
1091         if '/NOENTRY' not in ldflags:
1092           self.target.import_lib = output + '.lib'
1093           extra_bindings.append(('implibflag',
1094                                  '/IMPLIB:%s' % self.target.import_lib))
1095           output = [output, self.target.import_lib]
1096       elif not self.is_mac_bundle:
1097         output = [output, output + '.TOC']
1098       else:
1099         command = command + '_notoc'
1100
1101     if len(solibs):
1102       extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs)))
1103
1104     ninja_file.build(output, command + command_suffix, link_deps,
1105                      implicit=list(implicit_deps),
1106                      variables=extra_bindings)
1107     return linked_binary
1108
1109   def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
1110     extra_link_deps = any(self.target_outputs.get(dep).Linkable()
1111                           for dep in spec.get('dependencies', [])
1112                           if dep in self.target_outputs)
1113     if spec['type'] == 'none' or (not link_deps and not extra_link_deps):
1114       # TODO(evan): don't call this function for 'none' target types, as
1115       # it doesn't do anything, and we fake out a 'binary' with a stamp file.
1116       self.target.binary = compile_deps
1117       self.target.type = 'none'
1118     elif spec['type'] == 'static_library':
1119       self.target.binary = self.ComputeOutput(spec)
1120       if (self.flavor not in ('mac', 'openbsd', 'win') and not
1121           self.is_standalone_static_library):
1122         self.ninja.build(self.target.binary, 'alink_thin', link_deps,
1123                          order_only=compile_deps)
1124       else:
1125         variables = []
1126         if self.xcode_settings:
1127           libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
1128           if libtool_flags:
1129             variables.append(('libtool_flags', libtool_flags))
1130         if self.msvs_settings:
1131           libflags = self.msvs_settings.GetLibFlags(config_name,
1132                                                     self.GypPathToNinja)
1133           variables.append(('libflags', libflags))
1134
1135         if self.flavor != 'mac' or len(self.archs) == 1:
1136           self.AppendPostbuildVariable(variables, spec,
1137                                        self.target.binary, self.target.binary)
1138           self.ninja.build(self.target.binary, 'alink', link_deps,
1139                            order_only=compile_deps, variables=variables)
1140         else:
1141           inputs = []
1142           for arch in self.archs:
1143             output = self.ComputeOutput(spec, arch)
1144             self.arch_subninjas[arch].build(output, 'alink', link_deps[arch],
1145                                             order_only=compile_deps,
1146                                             variables=variables)
1147             inputs.append(output)
1148           # TODO: It's not clear if libtool_flags should be passed to the alink
1149           # call that combines single-arch .a files into a fat .a file.
1150           self.AppendPostbuildVariable(variables, spec,
1151                                        self.target.binary, self.target.binary)
1152           self.ninja.build(self.target.binary, 'alink', inputs,
1153                            # FIXME: test proving order_only=compile_deps isn't
1154                            # needed.
1155                            variables=variables)
1156     else:
1157       self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
1158     return self.target.binary
1159
1160   def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
1161     assert self.is_mac_bundle
1162     package_framework = spec['type'] in ('shared_library', 'loadable_module')
1163     output = self.ComputeMacBundleOutput()
1164     if is_empty:
1165       output += '.stamp'
1166     variables = []
1167     self.AppendPostbuildVariable(variables, spec, output, self.target.binary,
1168                                  is_command_start=not package_framework)
1169     if package_framework and not is_empty:
1170       variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
1171       self.ninja.build(output, 'package_framework', mac_bundle_depends,
1172                        variables=variables)
1173     else:
1174       self.ninja.build(output, 'stamp', mac_bundle_depends,
1175                        variables=variables)
1176     self.target.bundle = output
1177     return output
1178
1179   def GetSortedXcodeEnv(self, additional_settings=None):
1180     """Returns the variables Xcode would set for build steps."""
1181     assert self.abs_build_dir
1182     abs_build_dir = self.abs_build_dir
1183     return gyp.xcode_emulation.GetSortedXcodeEnv(
1184         self.xcode_settings, abs_build_dir,
1185         os.path.join(abs_build_dir, self.build_to_base), self.config_name,
1186         additional_settings)
1187
1188   def GetSortedXcodePostbuildEnv(self):
1189     """Returns the variables Xcode would set for postbuild steps."""
1190     postbuild_settings = {}
1191     # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
1192     # TODO(thakis): It would be nice to have some general mechanism instead.
1193     strip_save_file = self.xcode_settings.GetPerTargetSetting(
1194         'CHROMIUM_STRIP_SAVE_FILE')
1195     if strip_save_file:
1196       postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
1197     return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
1198
1199   def AppendPostbuildVariable(self, variables, spec, output, binary,
1200                               is_command_start=False):
1201     """Adds a 'postbuild' variable if there is a postbuild for |output|."""
1202     postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
1203     if postbuild:
1204       variables.append(('postbuilds', postbuild))
1205
1206   def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
1207     """Returns a shell command that runs all the postbuilds, and removes
1208     |output| if any of them fails. If |is_command_start| is False, then the
1209     returned string will start with ' && '."""
1210     if not self.xcode_settings or spec['type'] == 'none' or not output:
1211       return ''
1212     output = QuoteShellArgument(output, self.flavor)
1213     postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True)
1214     if output_binary is not None:
1215       postbuilds = self.xcode_settings.AddImplicitPostbuilds(
1216           self.config_name,
1217           os.path.normpath(os.path.join(self.base_to_build, output)),
1218           QuoteShellArgument(
1219               os.path.normpath(os.path.join(self.base_to_build, output_binary)),
1220               self.flavor),
1221           postbuilds, quiet=True)
1222
1223     if not postbuilds:
1224       return ''
1225     # Postbuilds expect to be run in the gyp file's directory, so insert an
1226     # implicit postbuild to cd to there.
1227     postbuilds.insert(0, gyp.common.EncodePOSIXShellList(
1228         ['cd', self.build_to_base]))
1229     env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
1230     # G will be non-null if any postbuild fails. Run all postbuilds in a
1231     # subshell.
1232     commands = env + ' (' + \
1233         ' && '.join([ninja_syntax.escape(command) for command in postbuilds])
1234     command_string = (commands + '); G=$$?; '
1235                       # Remove the final output if any postbuild failed.
1236                       '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
1237     if is_command_start:
1238       return '(' + command_string + ' && '
1239     else:
1240       return '$ && (' + command_string
1241
1242   def ComputeExportEnvString(self, env):
1243     """Given an environment, returns a string looking like
1244         'export FOO=foo; export BAR="${FOO} bar;'
1245     that exports |env| to the shell."""
1246     export_str = []
1247     for k, v in env:
1248       export_str.append('export %s=%s;' %
1249           (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v))))
1250     return ' '.join(export_str)
1251
1252   def ComputeMacBundleOutput(self):
1253     """Return the 'output' (full output path) to a bundle output directory."""
1254     assert self.is_mac_bundle
1255     path = generator_default_variables['PRODUCT_DIR']
1256     return self.ExpandSpecial(
1257         os.path.join(path, self.xcode_settings.GetWrapperName()))
1258
1259   def ComputeOutputFileName(self, spec, type=None):
1260     """Compute the filename of the final output for the current target."""
1261     if not type:
1262       type = spec['type']
1263
1264     default_variables = copy.copy(generator_default_variables)
1265     CalculateVariables(default_variables, {'flavor': self.flavor})
1266
1267     # Compute filename prefix: the product prefix, or a default for
1268     # the product type.
1269     DEFAULT_PREFIX = {
1270       'loadable_module': default_variables['SHARED_LIB_PREFIX'],
1271       'shared_library': default_variables['SHARED_LIB_PREFIX'],
1272       'static_library': default_variables['STATIC_LIB_PREFIX'],
1273       'executable': default_variables['EXECUTABLE_PREFIX'],
1274       }
1275     prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
1276
1277     # Compute filename extension: the product extension, or a default
1278     # for the product type.
1279     DEFAULT_EXTENSION = {
1280         'loadable_module': default_variables['SHARED_LIB_SUFFIX'],
1281         'shared_library': default_variables['SHARED_LIB_SUFFIX'],
1282         'static_library': default_variables['STATIC_LIB_SUFFIX'],
1283         'executable': default_variables['EXECUTABLE_SUFFIX'],
1284       }
1285     extension = spec.get('product_extension')
1286     if extension:
1287       extension = '.' + extension
1288     else:
1289       extension = DEFAULT_EXTENSION.get(type, '')
1290
1291     if 'product_name' in spec:
1292       # If we were given an explicit name, use that.
1293       target = spec['product_name']
1294     else:
1295       # Otherwise, derive a name from the target name.
1296       target = spec['target_name']
1297       if prefix == 'lib':
1298         # Snip out an extra 'lib' from libs if appropriate.
1299         target = StripPrefix(target, 'lib')
1300
1301     if type in ('static_library', 'loadable_module', 'shared_library',
1302                         'executable'):
1303       return '%s%s%s' % (prefix, target, extension)
1304     elif type == 'none':
1305       return '%s.stamp' % target
1306     else:
1307       raise Exception('Unhandled output type %s' % type)
1308
1309   def ComputeOutput(self, spec, arch=None):
1310     """Compute the path for the final output of the spec."""
1311     type = spec['type']
1312
1313     if self.flavor == 'win':
1314       override = self.msvs_settings.GetOutputName(self.config_name,
1315                                                   self.ExpandSpecial)
1316       if override:
1317         return override
1318
1319     if arch is None and self.flavor == 'mac' and type in (
1320         'static_library', 'executable', 'shared_library', 'loadable_module'):
1321       filename = self.xcode_settings.GetExecutablePath()
1322     else:
1323       filename = self.ComputeOutputFileName(spec, type)
1324
1325     if arch is None and 'product_dir' in spec:
1326       path = os.path.join(spec['product_dir'], filename)
1327       return self.ExpandSpecial(path)
1328
1329     # Some products go into the output root, libraries go into shared library
1330     # dir, and everything else goes into the normal place.
1331     type_in_output_root = ['executable', 'loadable_module']
1332     if self.flavor == 'mac' and self.toolset == 'target':
1333       type_in_output_root += ['shared_library', 'static_library']
1334     elif self.flavor == 'win' and self.toolset == 'target':
1335       type_in_output_root += ['shared_library']
1336
1337     if arch is not None:
1338       # Make sure partial executables don't end up in a bundle or the regular
1339       # output directory.
1340       archdir = 'arch'
1341       if self.toolset != 'target':
1342         archdir = os.path.join('arch', '%s' % self.toolset)
1343       return os.path.join(archdir, AddArch(filename, arch))
1344     elif type in type_in_output_root or self.is_standalone_static_library:
1345       return filename
1346     elif type == 'shared_library':
1347       libdir = 'lib'
1348       if self.toolset != 'target':
1349         libdir = os.path.join('lib', '%s' % self.toolset)
1350       return os.path.join(libdir, filename)
1351     else:
1352       return self.GypPathToUniqueOutput(filename, qualified=False)
1353
1354   def WriteVariableList(self, ninja_file, var, values):
1355     assert not isinstance(values, str)
1356     if values is None:
1357       values = []
1358     ninja_file.variable(var, ' '.join(values))
1359
1360   def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
1361     """Write out a new ninja "rule" statement for a given command.
1362
1363     Returns the name of the new rule, and a copy of |args| with variables
1364     expanded."""
1365
1366     if self.flavor == 'win':
1367       args = [self.msvs_settings.ConvertVSMacros(
1368                   arg, self.base_to_build, config=self.config_name)
1369               for arg in args]
1370       description = self.msvs_settings.ConvertVSMacros(
1371           description, config=self.config_name)
1372     elif self.flavor == 'mac':
1373       # |env| is an empty list on non-mac.
1374       args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args]
1375       description = gyp.xcode_emulation.ExpandEnvVars(description, env)
1376
1377     # TODO: we shouldn't need to qualify names; we do it because
1378     # currently the ninja rule namespace is global, but it really
1379     # should be scoped to the subninja.
1380     rule_name = self.name
1381     if self.toolset == 'target':
1382       rule_name += '.' + self.toolset
1383     rule_name += '.' + name
1384     rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name)
1385
1386     # Remove variable references, but not if they refer to the magic rule
1387     # variables.  This is not quite right, as it also protects these for
1388     # actions, not just for rules where they are valid. Good enough.
1389     protect = [ '${root}', '${dirname}', '${source}', '${ext}', '${name}' ]
1390     protect = '(?!' + '|'.join(map(re.escape, protect)) + ')'
1391     description = re.sub(protect + r'\$', '_', description)
1392
1393     # gyp dictates that commands are run from the base directory.
1394     # cd into the directory before running, and adjust paths in
1395     # the arguments to point to the proper locations.
1396     rspfile = None
1397     rspfile_content = None
1398     args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
1399     if self.flavor == 'win':
1400       rspfile = rule_name + '.$unique_name.rsp'
1401       # The cygwin case handles this inside the bash sub-shell.
1402       run_in = '' if is_cygwin else ' ' + self.build_to_base
1403       if is_cygwin:
1404         rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine(
1405             args, self.build_to_base)
1406       else:
1407         rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args)
1408       command = ('%s gyp-win-tool action-wrapper $arch ' % sys.executable +
1409                  rspfile + run_in)
1410     else:
1411       env = self.ComputeExportEnvString(env)
1412       command = gyp.common.EncodePOSIXShellList(args)
1413       command = 'cd %s; ' % self.build_to_base + env + command
1414
1415     # GYP rules/actions express being no-ops by not touching their outputs.
1416     # Avoid executing downstream dependencies in this case by specifying
1417     # restat=1 to ninja.
1418     self.ninja.rule(rule_name, command, description, restat=True,
1419                     rspfile=rspfile, rspfile_content=rspfile_content)
1420     self.ninja.newline()
1421
1422     return rule_name, args
1423
1424
1425 def CalculateVariables(default_variables, params):
1426   """Calculate additional variables for use in the build (called by gyp)."""
1427   global generator_additional_non_configuration_keys
1428   global generator_additional_path_sections
1429   flavor = gyp.common.GetFlavor(params)
1430   if flavor == 'mac':
1431     default_variables.setdefault('OS', 'mac')
1432     default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
1433     default_variables.setdefault('SHARED_LIB_DIR',
1434                                  generator_default_variables['PRODUCT_DIR'])
1435     default_variables.setdefault('LIB_DIR',
1436                                  generator_default_variables['PRODUCT_DIR'])
1437
1438     # Copy additional generator configuration data from Xcode, which is shared
1439     # by the Mac Ninja generator.
1440     import gyp.generator.xcode as xcode_generator
1441     generator_additional_non_configuration_keys = getattr(xcode_generator,
1442         'generator_additional_non_configuration_keys', [])
1443     generator_additional_path_sections = getattr(xcode_generator,
1444         'generator_additional_path_sections', [])
1445     global generator_extra_sources_for_rules
1446     generator_extra_sources_for_rules = getattr(xcode_generator,
1447         'generator_extra_sources_for_rules', [])
1448   elif flavor == 'win':
1449     default_variables.setdefault('OS', 'win')
1450     default_variables['EXECUTABLE_SUFFIX'] = '.exe'
1451     default_variables['STATIC_LIB_PREFIX'] = ''
1452     default_variables['STATIC_LIB_SUFFIX'] = '.lib'
1453     default_variables['SHARED_LIB_PREFIX'] = ''
1454     default_variables['SHARED_LIB_SUFFIX'] = '.dll'
1455
1456     # Copy additional generator configuration data from VS, which is shared
1457     # by the Windows Ninja generator.
1458     import gyp.generator.msvs as msvs_generator
1459     generator_additional_non_configuration_keys = getattr(msvs_generator,
1460         'generator_additional_non_configuration_keys', [])
1461     generator_additional_path_sections = getattr(msvs_generator,
1462         'generator_additional_path_sections', [])
1463
1464     gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
1465   else:
1466     operating_system = flavor
1467     if flavor == 'android':
1468       operating_system = 'linux'  # Keep this legacy behavior for now.
1469     default_variables.setdefault('OS', operating_system)
1470     default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
1471     default_variables.setdefault('SHARED_LIB_DIR',
1472                                  os.path.join('$!PRODUCT_DIR', 'lib'))
1473     default_variables.setdefault('LIB_DIR',
1474                                  os.path.join('$!PRODUCT_DIR', 'obj'))
1475
1476 def ComputeOutputDir(params):
1477   """Returns the path from the toplevel_dir to the build output directory."""
1478   # generator_dir: relative path from pwd to where make puts build files.
1479   # Makes migrating from make to ninja easier, ninja doesn't put anything here.
1480   generator_dir = os.path.relpath(params['options'].generator_output or '.')
1481
1482   # output_dir: relative path from generator_dir to the build directory.
1483   output_dir = params.get('generator_flags', {}).get('output_dir', 'out')
1484
1485   # Relative path from source root to our output files.  e.g. "out"
1486   return os.path.normpath(os.path.join(generator_dir, output_dir))
1487
1488
1489 def CalculateGeneratorInputInfo(params):
1490   """Called by __init__ to initialize generator values based on params."""
1491   # E.g. "out/gypfiles"
1492   toplevel = params['options'].toplevel_dir
1493   qualified_out_dir = os.path.normpath(os.path.join(
1494       toplevel, ComputeOutputDir(params), 'gypfiles'))
1495
1496   global generator_filelist_paths
1497   generator_filelist_paths = {
1498       'toplevel': toplevel,
1499       'qualified_out_dir': qualified_out_dir,
1500   }
1501
1502
1503 def OpenOutput(path, mode='w'):
1504   """Open |path| for writing, creating directories if necessary."""
1505   try:
1506     os.makedirs(os.path.dirname(path))
1507   except OSError:
1508     pass
1509   return open(path, mode)
1510
1511
1512 def CommandWithWrapper(cmd, wrappers, prog):
1513   wrapper = wrappers.get(cmd, '')
1514   if wrapper:
1515     return wrapper + ' ' + prog
1516   return prog
1517
1518
1519 def GetDefaultConcurrentLinks():
1520   """Returns a best-guess for a number of concurrent links."""
1521   if sys.platform in ('win32', 'cygwin'):
1522     import ctypes
1523
1524     class MEMORYSTATUSEX(ctypes.Structure):
1525       _fields_ = [
1526         ("dwLength", ctypes.c_ulong),
1527         ("dwMemoryLoad", ctypes.c_ulong),
1528         ("ullTotalPhys", ctypes.c_ulonglong),
1529         ("ullAvailPhys", ctypes.c_ulonglong),
1530         ("ullTotalPageFile", ctypes.c_ulonglong),
1531         ("ullAvailPageFile", ctypes.c_ulonglong),
1532         ("ullTotalVirtual", ctypes.c_ulonglong),
1533         ("ullAvailVirtual", ctypes.c_ulonglong),
1534         ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
1535       ]
1536
1537     stat = MEMORYSTATUSEX()
1538     stat.dwLength = ctypes.sizeof(stat)
1539     ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
1540
1541     mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30)))  # total / 4GB
1542     hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
1543     return min(mem_limit, hard_cap)
1544   elif sys.platform.startswith('linux'):
1545     with open("/proc/meminfo") as meminfo:
1546       memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
1547       for line in meminfo:
1548         match = memtotal_re.match(line)
1549         if not match:
1550           continue
1551         # Allow 8Gb per link on Linux because Gold is quite memory hungry
1552         return max(1, int(match.group(1)) / (8 * (2 ** 20)))
1553     return 1
1554   elif sys.platform == 'darwin':
1555     try:
1556       avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
1557       # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
1558       # 4GB per ld process allows for some more bloat.
1559       return max(1, avail_bytes / (4 * (2 ** 30)))  # total / 4GB
1560     except:
1561       return 1
1562   else:
1563     # TODO(scottmg): Implement this for other platforms.
1564     return 1
1565
1566
1567 def _GetWinLinkRuleNameSuffix(embed_manifest, link_incremental):
1568   """Returns the suffix used to select an appropriate linking rule depending on
1569   whether the manifest embedding and/or incremental linking is enabled."""
1570   suffix = ''
1571   if embed_manifest:
1572     suffix += '_embed'
1573     if link_incremental:
1574       suffix += '_inc'
1575   return suffix
1576
1577
1578 def _AddWinLinkRules(master_ninja, embed_manifest, link_incremental):
1579   """Adds link rules for Windows platform to |master_ninja|."""
1580   def FullLinkCommand(ldcmd, out, binary_type):
1581     cmd = ('cmd /c %(ldcmd)s'
1582            ' && %(python)s gyp-win-tool manifest-wrapper $arch'
1583            ' cmd /c if exist %(out)s.manifest del %(out)s.manifest'
1584            ' && %(python)s gyp-win-tool manifest-wrapper $arch'
1585            ' $mt -nologo -manifest $manifests')
1586     if embed_manifest and not link_incremental:
1587       # Embed manifest into a binary. If incremental linking is enabled,
1588       # embedding is postponed to the re-linking stage (see below).
1589       cmd += ' -outputresource:%(out)s;%(resname)s'
1590     else:
1591       # Save manifest as an external file.
1592       cmd += ' -out:%(out)s.manifest'
1593     if link_incremental:
1594       # There is no point in generating separate rule for the case when
1595       # incremental linking is enabled, but manifest embedding is disabled.
1596       # In that case the basic rule should be used (e.g. 'link').
1597       # See also implementation of _GetWinLinkRuleNameSuffix().
1598       assert embed_manifest
1599       # Make .rc file out of manifest, compile it to .res file and re-link.
1600       cmd += (' && %(python)s gyp-win-tool manifest-to-rc $arch'
1601               ' %(out)s.manifest %(out)s.manifest.rc %(resname)s'
1602               ' && %(python)s gyp-win-tool rc-wrapper $arch $rc'
1603               ' %(out)s.manifest.rc'
1604               ' && %(ldcmd)s %(out)s.manifest.res')
1605     resource_name = {
1606       'exe': '1',
1607       'dll': '2',
1608     }[binary_type]
1609     return cmd % {'python': sys.executable,
1610                   'out': out,
1611                   'ldcmd': ldcmd,
1612                   'resname': resource_name}
1613
1614   rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest, link_incremental)
1615   dlldesc = 'LINK%s(DLL) $dll' % rule_name_suffix.upper()
1616   dllcmd = ('%s gyp-win-tool link-wrapper $arch '
1617             '$ld /nologo $implibflag /DLL /OUT:$dll '
1618             '/PDB:$dll.pdb @$dll.rsp' % sys.executable)
1619   dllcmd = FullLinkCommand(dllcmd, '$dll', 'dll')
1620   master_ninja.rule('solink' + rule_name_suffix,
1621                     description=dlldesc, command=dllcmd,
1622                     rspfile='$dll.rsp',
1623                     rspfile_content='$libs $in_newline $ldflags',
1624                     restat=True,
1625                     pool='link_pool')
1626   master_ninja.rule('solink_module' + rule_name_suffix,
1627                     description=dlldesc, command=dllcmd,
1628                     rspfile='$dll.rsp',
1629                     rspfile_content='$libs $in_newline $ldflags',
1630                     restat=True,
1631                     pool='link_pool')
1632   # Note that ldflags goes at the end so that it has the option of
1633   # overriding default settings earlier in the command line.
1634   exe_cmd = ('%s gyp-win-tool link-wrapper $arch '
1635              '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
1636               sys.executable)
1637   exe_cmd = FullLinkCommand(exe_cmd, '$out', 'exe')
1638   master_ninja.rule('link' + rule_name_suffix,
1639                     description='LINK%s $out' % rule_name_suffix.upper(),
1640                     command=exe_cmd,
1641                     rspfile='$out.rsp',
1642                     rspfile_content='$in_newline $libs $ldflags',
1643                     pool='link_pool')
1644
1645
1646 def GenerateOutputForConfig(target_list, target_dicts, data, params,
1647                             config_name):
1648   options = params['options']
1649   flavor = gyp.common.GetFlavor(params)
1650   generator_flags = params.get('generator_flags', {})
1651
1652   # build_dir: relative path from source root to our output files.
1653   # e.g. "out/Debug"
1654   build_dir = os.path.normpath(
1655       os.path.join(ComputeOutputDir(params), config_name))
1656
1657   toplevel_build = os.path.join(options.toplevel_dir, build_dir)
1658
1659   master_ninja = ninja_syntax.Writer(
1660       OpenOutput(os.path.join(toplevel_build, 'build.ninja')),
1661       width=120)
1662
1663   # Put build-time support tools in out/{config_name}.
1664   gyp.common.CopyTool(flavor, toplevel_build)
1665
1666   # Grab make settings for CC/CXX.
1667   # The rules are
1668   # - The priority from low to high is gcc/g++, the 'make_global_settings' in
1669   #   gyp, the environment variable.
1670   # - If there is no 'make_global_settings' for CC.host/CXX.host or
1671   #   'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
1672   #   to cc/cxx.
1673   if flavor == 'win':
1674     # Overridden by local arch choice in the use_deps case.
1675     # Chromium's ffmpeg c99conv.py currently looks for a 'cc =' line in
1676     # build.ninja so needs something valid here. http://crbug.com/233985
1677     cc = 'cl.exe'
1678     cxx = 'cl.exe'
1679     ld = 'link.exe'
1680     ld_host = '$ld'
1681   else:
1682     cc = 'gcc'
1683     cxx = 'g++'
1684     ld = '$cc'
1685     ldxx = '$cxx'
1686     ld_host = '$cc_host'
1687     ldxx_host = '$cxx_host'
1688
1689   cc_host = None
1690   cxx_host = None
1691   cc_host_global_setting = None
1692   cxx_host_global_setting = None
1693
1694   build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
1695   make_global_settings = data[build_file].get('make_global_settings', [])
1696   build_to_root = gyp.common.InvertRelativePath(build_dir,
1697                                                 options.toplevel_dir)
1698   wrappers = {}
1699   for key, value in make_global_settings:
1700     if key == 'CC':
1701       cc = os.path.join(build_to_root, value)
1702     if key == 'CXX':
1703       cxx = os.path.join(build_to_root, value)
1704     if key == 'CC.host':
1705       cc_host = os.path.join(build_to_root, value)
1706       cc_host_global_setting = value
1707     if key == 'CXX.host':
1708       cxx_host = os.path.join(build_to_root, value)
1709       cxx_host_global_setting = value
1710     if key.endswith('_wrapper'):
1711       wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
1712
1713   # Support wrappers from environment variables too.
1714   for key, value in os.environ.iteritems():
1715     if key.lower().endswith('_wrapper'):
1716       key_prefix = key[:-len('_wrapper')]
1717       key_prefix = re.sub(r'\.HOST$', '.host', key_prefix)
1718       wrappers[key_prefix] = os.path.join(build_to_root, value)
1719
1720   if flavor == 'win':
1721     cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
1722         toplevel_build, generator_flags, OpenOutput)
1723     for arch, path in cl_paths.iteritems():
1724       master_ninja.variable(
1725           'cl_' + arch, CommandWithWrapper('CC', wrappers,
1726                                            QuoteShellArgument(path, flavor)))
1727
1728   cc = GetEnvironFallback(['CC_target', 'CC'], cc)
1729   master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
1730   cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
1731   master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx))
1732
1733   if flavor == 'win':
1734     master_ninja.variable('ld', ld)
1735     master_ninja.variable('idl', 'midl.exe')
1736     master_ninja.variable('ar', 'lib.exe')
1737     master_ninja.variable('rc', 'rc.exe')
1738     master_ninja.variable('asm', 'ml.exe')
1739     master_ninja.variable('mt', 'mt.exe')
1740   else:
1741     master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
1742     master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
1743     master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
1744
1745   if generator_supports_multiple_toolsets:
1746     if not cc_host:
1747       cc_host = cc
1748     if not cxx_host:
1749       cxx_host = cxx
1750
1751     master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
1752     cc_host = GetEnvironFallback(['CC_host'], cc_host)
1753     cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
1754
1755     # The environment variable could be used in 'make_global_settings', like
1756     # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
1757     if '$(CC)' in cc_host and cc_host_global_setting:
1758       cc_host = cc_host_global_setting.replace('$(CC)', cc)
1759     if '$(CXX)' in cxx_host and cxx_host_global_setting:
1760       cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
1761     master_ninja.variable('cc_host',
1762                           CommandWithWrapper('CC.host', wrappers, cc_host))
1763     master_ninja.variable('cxx_host',
1764                           CommandWithWrapper('CXX.host', wrappers, cxx_host))
1765     if flavor == 'win':
1766       master_ninja.variable('ld_host', ld_host)
1767     else:
1768       master_ninja.variable('ld_host', CommandWithWrapper(
1769           'LINK', wrappers, ld_host))
1770       master_ninja.variable('ldxx_host', CommandWithWrapper(
1771           'LINK', wrappers, ldxx_host))
1772
1773   master_ninja.newline()
1774
1775   master_ninja.pool('link_pool', depth=GetDefaultConcurrentLinks())
1776   master_ninja.newline()
1777
1778   deps = 'msvc' if flavor == 'win' else 'gcc'
1779
1780   if flavor != 'win':
1781     master_ninja.rule(
1782       'cc',
1783       description='CC $out',
1784       command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
1785               '$cflags_pch_c -c $in -o $out'),
1786       depfile='$out.d',
1787       deps=deps)
1788     master_ninja.rule(
1789       'cc_s',
1790       description='CC $out',
1791       command=('$cc $defines $includes $cflags $cflags_c '
1792               '$cflags_pch_c -c $in -o $out'))
1793     master_ninja.rule(
1794       'cxx',
1795       description='CXX $out',
1796       command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
1797               '$cflags_pch_cc -c $in -o $out'),
1798       depfile='$out.d',
1799       deps=deps)
1800   else:
1801     cc_command = ('ninja -t msvc -e $arch ' +
1802                   '-- '
1803                   '$cc /nologo /showIncludes /FC '
1804                   '@$out.rsp /c $in /Fo$out /Fd$pdbname ')
1805     cxx_command = ('ninja -t msvc -e $arch ' +
1806                    '-- '
1807                    '$cxx /nologo /showIncludes /FC '
1808                    '@$out.rsp /c $in /Fo$out /Fd$pdbname ')
1809     master_ninja.rule(
1810       'cc',
1811       description='CC $out',
1812       command=cc_command,
1813       rspfile='$out.rsp',
1814       rspfile_content='$defines $includes $cflags $cflags_c',
1815       deps=deps)
1816     master_ninja.rule(
1817       'cxx',
1818       description='CXX $out',
1819       command=cxx_command,
1820       rspfile='$out.rsp',
1821       rspfile_content='$defines $includes $cflags $cflags_cc',
1822       deps=deps)
1823     master_ninja.rule(
1824       'idl',
1825       description='IDL $in',
1826       command=('%s gyp-win-tool midl-wrapper $arch $outdir '
1827                '$tlb $h $dlldata $iid $proxy $in '
1828                '$idlflags' % sys.executable))
1829     master_ninja.rule(
1830       'rc',
1831       description='RC $in',
1832       # Note: $in must be last otherwise rc.exe complains.
1833       command=('%s gyp-win-tool rc-wrapper '
1834                '$arch $rc $defines $resource_includes $rcflags /fo$out $in' %
1835                sys.executable))
1836     master_ninja.rule(
1837       'asm',
1838       description='ASM $in',
1839       command=('%s gyp-win-tool asm-wrapper '
1840                '$arch $asm $defines $includes /c /Fo $out $in' %
1841                sys.executable))
1842
1843   if flavor != 'mac' and flavor != 'win':
1844     master_ninja.rule(
1845       'alink',
1846       description='AR $out',
1847       command='rm -f $out && $ar rcs $out $in')
1848     master_ninja.rule(
1849       'alink_thin',
1850       description='AR $out',
1851       command='rm -f $out && $ar rcsT $out $in')
1852
1853     # This allows targets that only need to depend on $lib's API to declare an
1854     # order-only dependency on $lib.TOC and avoid relinking such downstream
1855     # dependencies when $lib changes only in non-public ways.
1856     # The resulting string leaves an uninterpolated %{suffix} which
1857     # is used in the final substitution below.
1858     mtime_preserving_solink_base = (
1859         'if [ ! -e $lib -o ! -e ${lib}.TOC ]; then '
1860         '%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
1861         '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
1862         'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
1863         'fi; fi'
1864         % { 'solink':
1865               '$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
1866             'extract_toc':
1867               ('{ readelf -d ${lib} | grep SONAME ; '
1868                'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
1869
1870     master_ninja.rule(
1871       'solink',
1872       description='SOLINK $lib',
1873       restat=True,
1874       command=(mtime_preserving_solink_base % {
1875           'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
1876           '$libs'}),
1877       pool='link_pool')
1878     master_ninja.rule(
1879       'solink_module',
1880       description='SOLINK(module) $lib',
1881       restat=True,
1882       command=(mtime_preserving_solink_base % {
1883           'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
1884           '$libs'}),
1885       pool='link_pool')
1886     master_ninja.rule(
1887       'link',
1888       description='LINK $out',
1889       command=('$ld $ldflags -o $out '
1890                '-Wl,--start-group $in $solibs -Wl,--end-group $libs'),
1891       pool='link_pool')
1892   elif flavor == 'win':
1893     master_ninja.rule(
1894         'alink',
1895         description='LIB $out',
1896         command=('%s gyp-win-tool link-wrapper $arch '
1897                  '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
1898                  sys.executable),
1899         rspfile='$out.rsp',
1900         rspfile_content='$in_newline $libflags')
1901     _AddWinLinkRules(master_ninja, embed_manifest=True, link_incremental=True)
1902     _AddWinLinkRules(master_ninja, embed_manifest=True, link_incremental=False)
1903     _AddWinLinkRules(master_ninja, embed_manifest=False, link_incremental=False)
1904     # Do not generate rules for embed_manifest=False and link_incremental=True
1905     # because in that case rules for (False, False) should be used (see
1906     # implementation of _GetWinLinkRuleNameSuffix()).
1907   else:
1908     master_ninja.rule(
1909       'objc',
1910       description='OBJC $out',
1911       command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
1912                '$cflags_pch_objc -c $in -o $out'),
1913       depfile='$out.d',
1914       deps=deps)
1915     master_ninja.rule(
1916       'objcxx',
1917       description='OBJCXX $out',
1918       command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
1919                '$cflags_pch_objcc -c $in -o $out'),
1920       depfile='$out.d',
1921       deps=deps)
1922     master_ninja.rule(
1923       'alink',
1924       description='LIBTOOL-STATIC $out, POSTBUILDS',
1925       command='rm -f $out && '
1926               './gyp-mac-tool filter-libtool libtool $libtool_flags '
1927               '-static -o $out $in'
1928               '$postbuilds')
1929     master_ninja.rule(
1930       'lipo',
1931       description='LIPO $out, POSTBUILDS',
1932       command='rm -f $out && lipo -create $in -output $out$postbuilds')
1933
1934     # Record the public interface of $lib in $lib.TOC. See the corresponding
1935     # comment in the posix section above for details.
1936     solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
1937     mtime_preserving_solink_base = (
1938         'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
1939              # Always force dependent targets to relink if this library
1940              # reexports something. Handling this correctly would require
1941              # recursive TOC dumping but this is rare in practice, so punt.
1942              'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
1943           '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
1944         'else '
1945           '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
1946           'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
1947             'mv ${lib}.tmp ${lib}.TOC ; '
1948           'fi; '
1949         'fi'
1950         % { 'solink': solink_base,
1951             'extract_toc':
1952               '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
1953               'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
1954
1955     solink_suffix = '$in $solibs $libs$postbuilds'
1956     master_ninja.rule(
1957       'solink',
1958       description='SOLINK $lib, POSTBUILDS',
1959       restat=True,
1960       command=mtime_preserving_solink_base % {'suffix': solink_suffix,
1961                                               'type': '-shared'},
1962       pool='link_pool')
1963     master_ninja.rule(
1964       'solink_notoc',
1965       description='SOLINK $lib, POSTBUILDS',
1966       restat=True,
1967       command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
1968       pool='link_pool')
1969
1970     solink_module_suffix = '$in $solibs $libs$postbuilds'
1971     master_ninja.rule(
1972       'solink_module',
1973       description='SOLINK(module) $lib, POSTBUILDS',
1974       restat=True,
1975       command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
1976                                               'type': '-bundle'},
1977       pool='link_pool')
1978     master_ninja.rule(
1979       'solink_module_notoc',
1980       description='SOLINK(module) $lib, POSTBUILDS',
1981       restat=True,
1982       command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
1983       pool='link_pool')
1984
1985     master_ninja.rule(
1986       'link',
1987       description='LINK $out, POSTBUILDS',
1988       command=('$ld $ldflags -o $out '
1989                '$in $solibs $libs$postbuilds'),
1990       pool='link_pool')
1991     master_ninja.rule(
1992       'preprocess_infoplist',
1993       description='PREPROCESS INFOPLIST $out',
1994       command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
1995                'plutil -convert xml1 $out $out'))
1996     master_ninja.rule(
1997       'copy_infoplist',
1998       description='COPY INFOPLIST $in',
1999       command='$env ./gyp-mac-tool copy-info-plist $in $out $keys')
2000     master_ninja.rule(
2001       'mac_tool',
2002       description='MACTOOL $mactool_cmd $in',
2003       command='$env ./gyp-mac-tool $mactool_cmd $in $out')
2004     master_ninja.rule(
2005       'package_framework',
2006       description='PACKAGE FRAMEWORK $out, POSTBUILDS',
2007       command='./gyp-mac-tool package-framework $out $version$postbuilds '
2008               '&& touch $out')
2009   if flavor == 'win':
2010     master_ninja.rule(
2011       'stamp',
2012       description='STAMP $out',
2013       command='%s gyp-win-tool stamp $out' % sys.executable)
2014     master_ninja.rule(
2015       'copy',
2016       description='COPY $in $out',
2017       command='%s gyp-win-tool recursive-mirror $in $out' % sys.executable)
2018   else:
2019     master_ninja.rule(
2020       'stamp',
2021       description='STAMP $out',
2022       command='${postbuilds}touch $out')
2023     master_ninja.rule(
2024       'copy',
2025       description='COPY $in $out',
2026       command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
2027   master_ninja.newline()
2028
2029   all_targets = set()
2030   for build_file in params['build_files']:
2031     for target in gyp.common.AllTargets(target_list,
2032                                         target_dicts,
2033                                         os.path.normpath(build_file)):
2034       all_targets.add(target)
2035   all_outputs = set()
2036
2037   # target_outputs is a map from qualified target name to a Target object.
2038   target_outputs = {}
2039   # target_short_names is a map from target short name to a list of Target
2040   # objects.
2041   target_short_names = {}
2042
2043   for qualified_target in target_list:
2044     # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
2045     build_file, name, toolset = \
2046         gyp.common.ParseQualifiedTarget(qualified_target)
2047
2048     this_make_global_settings = data[build_file].get('make_global_settings', [])
2049     assert make_global_settings == this_make_global_settings, (
2050         "make_global_settings needs to be the same for all targets.")
2051
2052     spec = target_dicts[qualified_target]
2053     if flavor == 'mac':
2054       gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
2055
2056     build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
2057
2058     base_path = os.path.dirname(build_file)
2059     obj = 'obj'
2060     if toolset != 'target':
2061       obj += '.' + toolset
2062     output_file = os.path.join(obj, base_path, name + '.ninja')
2063
2064     ninja_output = StringIO()
2065     writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
2066                          ninja_output,
2067                          toplevel_build, output_file,
2068                          flavor, toplevel_dir=options.toplevel_dir)
2069
2070     target = writer.WriteSpec(spec, config_name, generator_flags)
2071
2072     if ninja_output.tell() > 0:
2073       # Only create files for ninja files that actually have contents.
2074       with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file:
2075         ninja_file.write(ninja_output.getvalue())
2076       ninja_output.close()
2077       master_ninja.subninja(output_file)
2078
2079     if target:
2080       if name != target.FinalOutput() and spec['toolset'] == 'target':
2081         target_short_names.setdefault(name, []).append(target)
2082       target_outputs[qualified_target] = target
2083       if qualified_target in all_targets:
2084         all_outputs.add(target.FinalOutput())
2085
2086   if target_short_names:
2087     # Write a short name to build this target.  This benefits both the
2088     # "build chrome" case as well as the gyp tests, which expect to be
2089     # able to run actions and build libraries by their short name.
2090     master_ninja.newline()
2091     master_ninja.comment('Short names for targets.')
2092     for short_name in target_short_names:
2093       master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
2094                                                target_short_names[short_name]])
2095
2096   if all_outputs:
2097     master_ninja.newline()
2098     master_ninja.build('all', 'phony', list(all_outputs))
2099     master_ninja.default(generator_flags.get('default_target', 'all'))
2100
2101
2102 def PerformBuild(data, configurations, params):
2103   options = params['options']
2104   for config in configurations:
2105     builddir = os.path.join(options.toplevel_dir, 'out', config)
2106     arguments = ['ninja', '-C', builddir]
2107     print 'Building [%s]: %s' % (config, arguments)
2108     subprocess.check_call(arguments)
2109
2110
2111 def CallGenerateOutputForConfig(arglist):
2112   # Ignore the interrupt signal so that the parent process catches it and
2113   # kills all multiprocessing children.
2114   signal.signal(signal.SIGINT, signal.SIG_IGN)
2115
2116   (target_list, target_dicts, data, params, config_name) = arglist
2117   GenerateOutputForConfig(target_list, target_dicts, data, params, config_name)
2118
2119
2120 def GenerateOutput(target_list, target_dicts, data, params):
2121   # Update target_dicts for iOS device builds.
2122   target_dicts = gyp.xcode_emulation.CloneConfigurationForDeviceAndEmulator(
2123       target_dicts)
2124
2125   user_config = params.get('generator_flags', {}).get('config', None)
2126   if gyp.common.GetFlavor(params) == 'win':
2127     target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
2128     target_list, target_dicts = MSVSUtil.InsertLargePdbShims(
2129         target_list, target_dicts, generator_default_variables)
2130
2131   if user_config:
2132     GenerateOutputForConfig(target_list, target_dicts, data, params,
2133                             user_config)
2134   else:
2135     config_names = target_dicts[target_list[0]]['configurations'].keys()
2136     if params['parallel']:
2137       try:
2138         pool = multiprocessing.Pool(len(config_names))
2139         arglists = []
2140         for config_name in config_names:
2141           arglists.append(
2142               (target_list, target_dicts, data, params, config_name))
2143         pool.map(CallGenerateOutputForConfig, arglists)
2144       except KeyboardInterrupt, e:
2145         pool.terminate()
2146         raise e
2147     else:
2148       for config_name in config_names:
2149         GenerateOutputForConfig(target_list, target_dicts, data, params,
2150                                 config_name)