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