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