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