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