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