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_base_name = self.GypPathToUniqueOutput(
1041 self.ComputeOutputFileName(spec))
1042 ldflags, intermediate_manifest, manifest_files = \
1043 self.msvs_settings.GetLdflags(config_name, self.GypPathToNinja,
1044 self.ExpandSpecial, manifest_base_name,
1045 output, is_executable,
1046 self.toplevel_build)
1047 ldflags = env_ldflags + ldflags
1048 self.WriteVariableList(ninja_file, 'manifests', manifest_files)
1049 implicit_deps = implicit_deps.union(manifest_files)
1050 if intermediate_manifest:
1051 self.WriteVariableList(
1052 ninja_file, 'intermediatemanifest', [intermediate_manifest])
1053 command_suffix = _GetWinLinkRuleNameSuffix(
1054 self.msvs_settings.IsEmbedManifest(config_name))
1055 def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja)
1057 implicit_deps.add(def_file)
1059 # Respect environment variables related to build, but target-specific
1060 # flags can still override them.
1061 ldflags = env_ldflags + config.get('ldflags', [])
1062 if is_executable and len(solibs):
1064 if self.toolset != 'target':
1065 rpath += self.toolset
1066 ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
1067 ldflags.append('-Wl,-rpath-link=%s' % rpath)
1068 self.WriteVariableList(ninja_file, 'ldflags',
1069 gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
1071 library_dirs = config.get('library_dirs', [])
1072 if self.flavor == 'win':
1073 library_dirs = [self.msvs_settings.ConvertVSMacros(l, config_name)
1074 for l in library_dirs]
1075 library_dirs = ['/LIBPATH:' + QuoteShellArgument(self.GypPathToNinja(l),
1077 for l in library_dirs]
1079 library_dirs = [QuoteShellArgument('-L' + self.GypPathToNinja(l),
1081 for l in library_dirs]
1083 libraries = gyp.common.uniquer(map(self.ExpandSpecial,
1084 spec.get('libraries', [])))
1085 if self.flavor == 'mac':
1086 libraries = self.xcode_settings.AdjustLibraries(libraries, config_name)
1087 elif self.flavor == 'win':
1088 libraries = self.msvs_settings.AdjustLibraries(libraries)
1090 self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries)
1092 linked_binary = output
1094 if command in ('solink', 'solink_module'):
1095 extra_bindings.append(('soname', os.path.split(output)[1]))
1096 extra_bindings.append(('lib',
1097 gyp.common.EncodePOSIXShellArgument(output)))
1098 if self.flavor == 'win':
1099 extra_bindings.append(('binary', output))
1100 if '/NOENTRY' not in ldflags:
1101 self.target.import_lib = output + '.lib'
1102 extra_bindings.append(('implibflag',
1103 '/IMPLIB:%s' % self.target.import_lib))
1104 pdbname = self.msvs_settings.GetPDBName(
1105 config_name, self.ExpandSpecial, output + '.pdb')
1106 output = [output, self.target.import_lib]
1108 output.append(pdbname)
1109 elif not self.is_mac_bundle:
1110 output = [output, output + '.TOC']
1112 command = command + '_notoc'
1113 elif self.flavor == 'win':
1114 extra_bindings.append(('binary', output))
1115 pdbname = self.msvs_settings.GetPDBName(
1116 config_name, self.ExpandSpecial, output + '.pdb')
1118 output = [output, pdbname]
1122 extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs)))
1124 ninja_file.build(output, command + command_suffix, link_deps,
1125 implicit=list(implicit_deps),
1126 variables=extra_bindings)
1127 return linked_binary
1129 def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
1130 extra_link_deps = any(self.target_outputs.get(dep).Linkable()
1131 for dep in spec.get('dependencies', [])
1132 if dep in self.target_outputs)
1133 if spec['type'] == 'none' or (not link_deps and not extra_link_deps):
1134 # TODO(evan): don't call this function for 'none' target types, as
1135 # it doesn't do anything, and we fake out a 'binary' with a stamp file.
1136 self.target.binary = compile_deps
1137 self.target.type = 'none'
1138 elif spec['type'] == 'static_library':
1139 self.target.binary = self.ComputeOutput(spec)
1140 if (self.flavor not in ('mac', 'openbsd', 'win') and not
1141 self.is_standalone_static_library):
1142 self.ninja.build(self.target.binary, 'alink_thin', link_deps,
1143 order_only=compile_deps)
1146 if self.xcode_settings:
1147 libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
1149 variables.append(('libtool_flags', libtool_flags))
1150 if self.msvs_settings:
1151 libflags = self.msvs_settings.GetLibFlags(config_name,
1152 self.GypPathToNinja)
1153 variables.append(('libflags', libflags))
1155 if self.flavor != 'mac' or len(self.archs) == 1:
1156 self.AppendPostbuildVariable(variables, spec,
1157 self.target.binary, self.target.binary)
1158 self.ninja.build(self.target.binary, 'alink', link_deps,
1159 order_only=compile_deps, variables=variables)
1162 for arch in self.archs:
1163 output = self.ComputeOutput(spec, arch)
1164 self.arch_subninjas[arch].build(output, 'alink', link_deps[arch],
1165 order_only=compile_deps,
1166 variables=variables)
1167 inputs.append(output)
1168 # TODO: It's not clear if libtool_flags should be passed to the alink
1169 # call that combines single-arch .a files into a fat .a file.
1170 self.AppendPostbuildVariable(variables, spec,
1171 self.target.binary, self.target.binary)
1172 self.ninja.build(self.target.binary, 'alink', inputs,
1173 # FIXME: test proving order_only=compile_deps isn't
1175 variables=variables)
1177 self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
1178 return self.target.binary
1180 def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
1181 assert self.is_mac_bundle
1182 package_framework = spec['type'] in ('shared_library', 'loadable_module')
1183 output = self.ComputeMacBundleOutput()
1187 self.AppendPostbuildVariable(variables, spec, output, self.target.binary,
1188 is_command_start=not package_framework)
1189 if package_framework and not is_empty:
1190 variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
1191 self.ninja.build(output, 'package_framework', mac_bundle_depends,
1192 variables=variables)
1194 self.ninja.build(output, 'stamp', mac_bundle_depends,
1195 variables=variables)
1196 self.target.bundle = output
1199 def GetSortedXcodeEnv(self, additional_settings=None):
1200 """Returns the variables Xcode would set for build steps."""
1201 assert self.abs_build_dir
1202 abs_build_dir = self.abs_build_dir
1203 return gyp.xcode_emulation.GetSortedXcodeEnv(
1204 self.xcode_settings, abs_build_dir,
1205 os.path.join(abs_build_dir, self.build_to_base), self.config_name,
1206 additional_settings)
1208 def GetSortedXcodePostbuildEnv(self):
1209 """Returns the variables Xcode would set for postbuild steps."""
1210 postbuild_settings = {}
1211 # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
1212 # TODO(thakis): It would be nice to have some general mechanism instead.
1213 strip_save_file = self.xcode_settings.GetPerTargetSetting(
1214 'CHROMIUM_STRIP_SAVE_FILE')
1216 postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
1217 return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
1219 def AppendPostbuildVariable(self, variables, spec, output, binary,
1220 is_command_start=False):
1221 """Adds a 'postbuild' variable if there is a postbuild for |output|."""
1222 postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
1224 variables.append(('postbuilds', postbuild))
1226 def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
1227 """Returns a shell command that runs all the postbuilds, and removes
1228 |output| if any of them fails. If |is_command_start| is False, then the
1229 returned string will start with ' && '."""
1230 if not self.xcode_settings or spec['type'] == 'none' or not output:
1232 output = QuoteShellArgument(output, self.flavor)
1233 postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True)
1234 if output_binary is not None:
1235 postbuilds = self.xcode_settings.AddImplicitPostbuilds(
1237 os.path.normpath(os.path.join(self.base_to_build, output)),
1239 os.path.normpath(os.path.join(self.base_to_build, output_binary)),
1241 postbuilds, quiet=True)
1245 # Postbuilds expect to be run in the gyp file's directory, so insert an
1246 # implicit postbuild to cd to there.
1247 postbuilds.insert(0, gyp.common.EncodePOSIXShellList(
1248 ['cd', self.build_to_base]))
1249 env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
1250 # G will be non-null if any postbuild fails. Run all postbuilds in a
1252 commands = env + ' (' + \
1253 ' && '.join([ninja_syntax.escape(command) for command in postbuilds])
1254 command_string = (commands + '); G=$$?; '
1255 # Remove the final output if any postbuild failed.
1256 '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
1257 if is_command_start:
1258 return '(' + command_string + ' && '
1260 return '$ && (' + command_string
1262 def ComputeExportEnvString(self, env):
1263 """Given an environment, returns a string looking like
1264 'export FOO=foo; export BAR="${FOO} bar;'
1265 that exports |env| to the shell."""
1268 export_str.append('export %s=%s;' %
1269 (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v))))
1270 return ' '.join(export_str)
1272 def ComputeMacBundleOutput(self):
1273 """Return the 'output' (full output path) to a bundle output directory."""
1274 assert self.is_mac_bundle
1275 path = generator_default_variables['PRODUCT_DIR']
1276 return self.ExpandSpecial(
1277 os.path.join(path, self.xcode_settings.GetWrapperName()))
1279 def ComputeOutputFileName(self, spec, type=None):
1280 """Compute the filename of the final output for the current target."""
1284 default_variables = copy.copy(generator_default_variables)
1285 CalculateVariables(default_variables, {'flavor': self.flavor})
1287 # Compute filename prefix: the product prefix, or a default for
1290 'loadable_module': default_variables['SHARED_LIB_PREFIX'],
1291 'shared_library': default_variables['SHARED_LIB_PREFIX'],
1292 'static_library': default_variables['STATIC_LIB_PREFIX'],
1293 'executable': default_variables['EXECUTABLE_PREFIX'],
1295 prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
1297 # Compute filename extension: the product extension, or a default
1298 # for the product type.
1299 DEFAULT_EXTENSION = {
1300 'loadable_module': default_variables['SHARED_LIB_SUFFIX'],
1301 'shared_library': default_variables['SHARED_LIB_SUFFIX'],
1302 'static_library': default_variables['STATIC_LIB_SUFFIX'],
1303 'executable': default_variables['EXECUTABLE_SUFFIX'],
1305 extension = spec.get('product_extension')
1307 extension = '.' + extension
1309 extension = DEFAULT_EXTENSION.get(type, '')
1311 if 'product_name' in spec:
1312 # If we were given an explicit name, use that.
1313 target = spec['product_name']
1315 # Otherwise, derive a name from the target name.
1316 target = spec['target_name']
1318 # Snip out an extra 'lib' from libs if appropriate.
1319 target = StripPrefix(target, 'lib')
1321 if type in ('static_library', 'loadable_module', 'shared_library',
1323 return '%s%s%s' % (prefix, target, extension)
1324 elif type == 'none':
1325 return '%s.stamp' % target
1327 raise Exception('Unhandled output type %s' % type)
1329 def ComputeOutput(self, spec, arch=None):
1330 """Compute the path for the final output of the spec."""
1333 if self.flavor == 'win':
1334 override = self.msvs_settings.GetOutputName(self.config_name,
1339 if arch is None and self.flavor == 'mac' and type in (
1340 'static_library', 'executable', 'shared_library', 'loadable_module'):
1341 filename = self.xcode_settings.GetExecutablePath()
1343 filename = self.ComputeOutputFileName(spec, type)
1345 if arch is None and 'product_dir' in spec:
1346 path = os.path.join(spec['product_dir'], filename)
1347 return self.ExpandSpecial(path)
1349 # Some products go into the output root, libraries go into shared library
1350 # dir, and everything else goes into the normal place.
1351 type_in_output_root = ['executable', 'loadable_module']
1352 if self.flavor == 'mac' and self.toolset == 'target':
1353 type_in_output_root += ['shared_library', 'static_library']
1354 elif self.flavor == 'win' and self.toolset == 'target':
1355 type_in_output_root += ['shared_library']
1357 if arch is not None:
1358 # Make sure partial executables don't end up in a bundle or the regular
1361 if self.toolset != 'target':
1362 archdir = os.path.join('arch', '%s' % self.toolset)
1363 return os.path.join(archdir, AddArch(filename, arch))
1364 elif type in type_in_output_root or self.is_standalone_static_library:
1366 elif type == 'shared_library':
1368 if self.toolset != 'target':
1369 libdir = os.path.join('lib', '%s' % self.toolset)
1370 return os.path.join(libdir, filename)
1372 return self.GypPathToUniqueOutput(filename, qualified=False)
1374 def WriteVariableList(self, ninja_file, var, values):
1375 assert not isinstance(values, str)
1378 ninja_file.variable(var, ' '.join(values))
1380 def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
1381 """Write out a new ninja "rule" statement for a given command.
1383 Returns the name of the new rule, and a copy of |args| with variables
1386 if self.flavor == 'win':
1387 args = [self.msvs_settings.ConvertVSMacros(
1388 arg, self.base_to_build, config=self.config_name)
1390 description = self.msvs_settings.ConvertVSMacros(
1391 description, config=self.config_name)
1392 elif self.flavor == 'mac':
1393 # |env| is an empty list on non-mac.
1394 args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args]
1395 description = gyp.xcode_emulation.ExpandEnvVars(description, env)
1397 # TODO: we shouldn't need to qualify names; we do it because
1398 # currently the ninja rule namespace is global, but it really
1399 # should be scoped to the subninja.
1400 rule_name = self.name
1401 if self.toolset == 'target':
1402 rule_name += '.' + self.toolset
1403 rule_name += '.' + name
1404 rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name)
1406 # Remove variable references, but not if they refer to the magic rule
1407 # variables. This is not quite right, as it also protects these for
1408 # actions, not just for rules where they are valid. Good enough.
1409 protect = [ '${root}', '${dirname}', '${source}', '${ext}', '${name}' ]
1410 protect = '(?!' + '|'.join(map(re.escape, protect)) + ')'
1411 description = re.sub(protect + r'\$', '_', description)
1413 # gyp dictates that commands are run from the base directory.
1414 # cd into the directory before running, and adjust paths in
1415 # the arguments to point to the proper locations.
1417 rspfile_content = None
1418 args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
1419 if self.flavor == 'win':
1420 rspfile = rule_name + '.$unique_name.rsp'
1421 # The cygwin case handles this inside the bash sub-shell.
1422 run_in = '' if is_cygwin else ' ' + self.build_to_base
1424 rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine(
1425 args, self.build_to_base)
1427 rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args)
1428 command = ('%s gyp-win-tool action-wrapper $arch ' % sys.executable +
1431 env = self.ComputeExportEnvString(env)
1432 command = gyp.common.EncodePOSIXShellList(args)
1433 command = 'cd %s; ' % self.build_to_base + env + command
1435 # GYP rules/actions express being no-ops by not touching their outputs.
1436 # Avoid executing downstream dependencies in this case by specifying
1437 # restat=1 to ninja.
1438 self.ninja.rule(rule_name, command, description, restat=True,
1439 rspfile=rspfile, rspfile_content=rspfile_content)
1440 self.ninja.newline()
1442 return rule_name, args
1445 def CalculateVariables(default_variables, params):
1446 """Calculate additional variables for use in the build (called by gyp)."""
1447 global generator_additional_non_configuration_keys
1448 global generator_additional_path_sections
1449 flavor = gyp.common.GetFlavor(params)
1451 default_variables.setdefault('OS', 'mac')
1452 default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
1453 default_variables.setdefault('SHARED_LIB_DIR',
1454 generator_default_variables['PRODUCT_DIR'])
1455 default_variables.setdefault('LIB_DIR',
1456 generator_default_variables['PRODUCT_DIR'])
1458 # Copy additional generator configuration data from Xcode, which is shared
1459 # by the Mac Ninja generator.
1460 import gyp.generator.xcode as xcode_generator
1461 generator_additional_non_configuration_keys = getattr(xcode_generator,
1462 'generator_additional_non_configuration_keys', [])
1463 generator_additional_path_sections = getattr(xcode_generator,
1464 'generator_additional_path_sections', [])
1465 global generator_extra_sources_for_rules
1466 generator_extra_sources_for_rules = getattr(xcode_generator,
1467 'generator_extra_sources_for_rules', [])
1468 elif flavor == 'win':
1469 default_variables.setdefault('OS', 'win')
1470 default_variables['EXECUTABLE_SUFFIX'] = '.exe'
1471 default_variables['STATIC_LIB_PREFIX'] = ''
1472 default_variables['STATIC_LIB_SUFFIX'] = '.lib'
1473 default_variables['SHARED_LIB_PREFIX'] = ''
1474 default_variables['SHARED_LIB_SUFFIX'] = '.dll'
1476 # Copy additional generator configuration data from VS, which is shared
1477 # by the Windows Ninja generator.
1478 import gyp.generator.msvs as msvs_generator
1479 generator_additional_non_configuration_keys = getattr(msvs_generator,
1480 'generator_additional_non_configuration_keys', [])
1481 generator_additional_path_sections = getattr(msvs_generator,
1482 'generator_additional_path_sections', [])
1484 gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
1486 operating_system = flavor
1487 if flavor == 'android':
1488 operating_system = 'linux' # Keep this legacy behavior for now.
1489 default_variables.setdefault('OS', operating_system)
1490 default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
1491 default_variables.setdefault('SHARED_LIB_DIR',
1492 os.path.join('$!PRODUCT_DIR', 'lib'))
1493 default_variables.setdefault('LIB_DIR',
1494 os.path.join('$!PRODUCT_DIR', 'obj'))
1496 def ComputeOutputDir(params):
1497 """Returns the path from the toplevel_dir to the build output directory."""
1498 # generator_dir: relative path from pwd to where make puts build files.
1499 # Makes migrating from make to ninja easier, ninja doesn't put anything here.
1500 generator_dir = os.path.relpath(params['options'].generator_output or '.')
1502 # output_dir: relative path from generator_dir to the build directory.
1503 output_dir = params.get('generator_flags', {}).get('output_dir', 'out')
1505 # Relative path from source root to our output files. e.g. "out"
1506 return os.path.normpath(os.path.join(generator_dir, output_dir))
1509 def CalculateGeneratorInputInfo(params):
1510 """Called by __init__ to initialize generator values based on params."""
1511 # E.g. "out/gypfiles"
1512 toplevel = params['options'].toplevel_dir
1513 qualified_out_dir = os.path.normpath(os.path.join(
1514 toplevel, ComputeOutputDir(params), 'gypfiles'))
1516 global generator_filelist_paths
1517 generator_filelist_paths = {
1518 'toplevel': toplevel,
1519 'qualified_out_dir': qualified_out_dir,
1523 def OpenOutput(path, mode='w'):
1524 """Open |path| for writing, creating directories if necessary."""
1525 gyp.common.EnsureDirExists(path)
1526 return open(path, mode)
1529 def CommandWithWrapper(cmd, wrappers, prog):
1530 wrapper = wrappers.get(cmd, '')
1532 return wrapper + ' ' + prog
1536 def GetDefaultConcurrentLinks():
1537 """Returns a best-guess for a number of concurrent links."""
1538 if sys.platform in ('win32', 'cygwin'):
1541 class MEMORYSTATUSEX(ctypes.Structure):
1543 ("dwLength", ctypes.c_ulong),
1544 ("dwMemoryLoad", ctypes.c_ulong),
1545 ("ullTotalPhys", ctypes.c_ulonglong),
1546 ("ullAvailPhys", ctypes.c_ulonglong),
1547 ("ullTotalPageFile", ctypes.c_ulonglong),
1548 ("ullAvailPageFile", ctypes.c_ulonglong),
1549 ("ullTotalVirtual", ctypes.c_ulonglong),
1550 ("ullAvailVirtual", ctypes.c_ulonglong),
1551 ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
1554 stat = MEMORYSTATUSEX()
1555 stat.dwLength = ctypes.sizeof(stat)
1556 ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
1558 mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30))) # total / 4GB
1559 hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
1560 # return min(mem_limit, hard_cap)
1561 # TODO(scottmg): Temporary speculative fix for OOM on builders
1562 # See http://crbug.com/333000.
1564 elif sys.platform.startswith('linux'):
1565 with open("/proc/meminfo") as meminfo:
1566 memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
1567 for line in meminfo:
1568 match = memtotal_re.match(line)
1571 # Allow 8Gb per link on Linux because Gold is quite memory hungry
1572 return max(1, int(match.group(1)) / (8 * (2 ** 20)))
1574 elif sys.platform == 'darwin':
1576 avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
1577 # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
1578 # 4GB per ld process allows for some more bloat.
1579 return max(1, avail_bytes / (4 * (2 ** 30))) # total / 4GB
1583 # TODO(scottmg): Implement this for other platforms.
1587 def _GetWinLinkRuleNameSuffix(embed_manifest):
1588 """Returns the suffix used to select an appropriate linking rule depending on
1589 whether the manifest embedding is enabled."""
1590 return '_embed' if embed_manifest else ''
1593 def _AddWinLinkRules(master_ninja, embed_manifest):
1594 """Adds link rules for Windows platform to |master_ninja|."""
1595 def FullLinkCommand(ldcmd, out, binary_type):
1600 return '%(python)s gyp-win-tool link-with-manifests $arch %(embed)s ' \
1601 '%(out)s "%(ldcmd)s" %(resname)s $mt $rc "$intermediatemanifest" ' \
1603 'python': sys.executable,
1606 'resname': resource_name,
1607 'embed': embed_manifest }
1608 rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest)
1609 use_separate_mspdbsrv = (
1610 int(os.environ.get('GYP_USE_SEPARATE_MSPDBSRV', '0')) != 0)
1611 dlldesc = 'LINK%s(DLL) $binary' % rule_name_suffix.upper()
1612 dllcmd = ('%s gyp-win-tool link-wrapper $arch %s '
1613 '$ld /nologo $implibflag /DLL /OUT:$binary '
1614 '@$binary.rsp' % (sys.executable, use_separate_mspdbsrv))
1615 dllcmd = FullLinkCommand(dllcmd, '$binary', 'dll')
1616 master_ninja.rule('solink' + rule_name_suffix,
1617 description=dlldesc, command=dllcmd,
1618 rspfile='$binary.rsp',
1619 rspfile_content='$libs $in_newline $ldflags',
1622 master_ninja.rule('solink_module' + rule_name_suffix,
1623 description=dlldesc, command=dllcmd,
1624 rspfile='$binary.rsp',
1625 rspfile_content='$libs $in_newline $ldflags',
1628 # Note that ldflags goes at the end so that it has the option of
1629 # overriding default settings earlier in the command line.
1630 exe_cmd = ('%s gyp-win-tool link-wrapper $arch %s '
1631 '$ld /nologo /OUT:$binary @$binary.rsp' %
1632 (sys.executable, use_separate_mspdbsrv))
1633 exe_cmd = FullLinkCommand(exe_cmd, '$binary', 'exe')
1634 master_ninja.rule('link' + rule_name_suffix,
1635 description='LINK%s $binary' % rule_name_suffix.upper(),
1637 rspfile='$binary.rsp',
1638 rspfile_content='$in_newline $libs $ldflags',
1642 def GenerateOutputForConfig(target_list, target_dicts, data, params,
1644 options = params['options']
1645 flavor = gyp.common.GetFlavor(params)
1646 generator_flags = params.get('generator_flags', {})
1648 # build_dir: relative path from source root to our output files.
1650 build_dir = os.path.normpath(
1651 os.path.join(ComputeOutputDir(params), config_name))
1653 toplevel_build = os.path.join(options.toplevel_dir, build_dir)
1655 master_ninja_file = OpenOutput(os.path.join(toplevel_build, 'build.ninja'))
1656 master_ninja = ninja_syntax.Writer(master_ninja_file, width=120)
1658 # Put build-time support tools in out/{config_name}.
1659 gyp.common.CopyTool(flavor, toplevel_build)
1661 # Grab make settings for CC/CXX.
1663 # - The priority from low to high is gcc/g++, the 'make_global_settings' in
1664 # gyp, the environment variable.
1665 # - If there is no 'make_global_settings' for CC.host/CXX.host or
1666 # 'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
1669 # Overridden by local arch choice in the use_deps case.
1670 # Chromium's ffmpeg c99conv.py currently looks for a 'cc =' line in
1671 # build.ninja so needs something valid here. http://crbug.com/233985
1681 ld_host = '$cc_host'
1682 ldxx_host = '$cxx_host'
1686 cc_host_global_setting = None
1687 cxx_host_global_setting = None
1689 build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
1690 make_global_settings = data[build_file].get('make_global_settings', [])
1691 build_to_root = gyp.common.InvertRelativePath(build_dir,
1692 options.toplevel_dir)
1694 for key, value in make_global_settings:
1696 cc = os.path.join(build_to_root, value)
1698 cxx = os.path.join(build_to_root, value)
1699 if key == 'CC.host':
1700 cc_host = os.path.join(build_to_root, value)
1701 cc_host_global_setting = value
1702 if key == 'CXX.host':
1703 cxx_host = os.path.join(build_to_root, value)
1704 cxx_host_global_setting = value
1705 if key.endswith('_wrapper'):
1706 wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
1708 # Support wrappers from environment variables too.
1709 for key, value in os.environ.iteritems():
1710 if key.lower().endswith('_wrapper'):
1711 key_prefix = key[:-len('_wrapper')]
1712 key_prefix = re.sub(r'\.HOST$', '.host', key_prefix)
1713 wrappers[key_prefix] = os.path.join(build_to_root, value)
1716 cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
1717 toplevel_build, generator_flags, OpenOutput)
1718 for arch, path in cl_paths.iteritems():
1719 master_ninja.variable(
1720 'cl_' + arch, CommandWithWrapper('CC', wrappers,
1721 QuoteShellArgument(path, flavor)))
1723 cc = GetEnvironFallback(['CC_target', 'CC'], cc)
1724 master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
1725 cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
1726 master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx))
1729 master_ninja.variable('ld', ld)
1730 master_ninja.variable('idl', 'midl.exe')
1731 master_ninja.variable('ar', 'lib.exe')
1732 master_ninja.variable('rc', 'rc.exe')
1733 master_ninja.variable('asm', 'ml.exe')
1734 master_ninja.variable('mt', 'mt.exe')
1736 master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
1737 master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
1738 master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
1740 if generator_supports_multiple_toolsets:
1746 master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
1747 cc_host = GetEnvironFallback(['CC_host'], cc_host)
1748 cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
1750 # The environment variable could be used in 'make_global_settings', like
1751 # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
1752 if '$(CC)' in cc_host and cc_host_global_setting:
1753 cc_host = cc_host_global_setting.replace('$(CC)', cc)
1754 if '$(CXX)' in cxx_host and cxx_host_global_setting:
1755 cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
1756 master_ninja.variable('cc_host',
1757 CommandWithWrapper('CC.host', wrappers, cc_host))
1758 master_ninja.variable('cxx_host',
1759 CommandWithWrapper('CXX.host', wrappers, cxx_host))
1761 master_ninja.variable('ld_host', ld_host)
1763 master_ninja.variable('ld_host', CommandWithWrapper(
1764 'LINK', wrappers, ld_host))
1765 master_ninja.variable('ldxx_host', CommandWithWrapper(
1766 'LINK', wrappers, ldxx_host))
1768 master_ninja.newline()
1770 master_ninja.pool('link_pool', depth=GetDefaultConcurrentLinks())
1771 master_ninja.newline()
1773 deps = 'msvc' if flavor == 'win' else 'gcc'
1778 description='CC $out',
1779 command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
1780 '$cflags_pch_c -c $in -o $out'),
1785 description='CC $out',
1786 command=('$cc $defines $includes $cflags $cflags_c '
1787 '$cflags_pch_c -c $in -o $out'))
1790 description='CXX $out',
1791 command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
1792 '$cflags_pch_cc -c $in -o $out'),
1796 # TODO(scottmg) Separate pdb names is a test to see if it works around
1797 # http://crbug.com/142362. It seems there's a race between the creation of
1798 # the .pdb by the precompiled header step for .cc and the compilation of
1799 # .c files. This should be handled by mspdbsrv, but rarely errors out with
1800 # c1xx : fatal error C1033: cannot open program database
1801 # By making the rules target separate pdb files this might be avoided.
1802 cc_command = ('ninja -t msvc -e $arch ' +
1804 '$cc /nologo /showIncludes /FC '
1805 '@$out.rsp /c $in /Fo$out /Fd$pdbname_c ')
1806 cxx_command = ('ninja -t msvc -e $arch ' +
1808 '$cxx /nologo /showIncludes /FC '
1809 '@$out.rsp /c $in /Fo$out /Fd$pdbname_cc ')
1812 description='CC $out',
1815 rspfile_content='$defines $includes $cflags $cflags_c',
1819 description='CXX $out',
1820 command=cxx_command,
1822 rspfile_content='$defines $includes $cflags $cflags_cc',
1826 description='IDL $in',
1827 command=('%s gyp-win-tool midl-wrapper $arch $outdir '
1828 '$tlb $h $dlldata $iid $proxy $in '
1829 '$idlflags' % sys.executable))
1832 description='RC $in',
1833 # Note: $in must be last otherwise rc.exe complains.
1834 command=('%s gyp-win-tool rc-wrapper '
1835 '$arch $rc $defines $resource_includes $rcflags /fo$out $in' %
1839 description='ASM $in',
1840 command=('%s gyp-win-tool asm-wrapper '
1841 '$arch $asm $defines $includes /c /Fo $out $in' %
1844 if flavor != 'mac' and flavor != 'win':
1847 description='AR $out',
1848 command='rm -f $out && $ar rcs $out $in')
1851 description='AR $out',
1852 command='rm -f $out && $ar rcsT $out $in')
1854 # This allows targets that only need to depend on $lib's API to declare an
1855 # order-only dependency on $lib.TOC and avoid relinking such downstream
1856 # dependencies when $lib changes only in non-public ways.
1857 # The resulting string leaves an uninterpolated %{suffix} which
1858 # is used in the final substitution below.
1859 mtime_preserving_solink_base = (
1860 'if [ ! -e $lib -o ! -e ${lib}.TOC ]; then '
1861 '%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
1862 '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
1863 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
1866 '$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
1868 ('{ readelf -d ${lib} | grep SONAME ; '
1869 'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
1873 description='SOLINK $lib',
1875 command=(mtime_preserving_solink_base % {
1876 'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
1881 description='SOLINK(module) $lib',
1883 command=(mtime_preserving_solink_base % {
1884 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
1889 description='LINK $out',
1890 command=('$ld $ldflags -o $out '
1891 '-Wl,--start-group $in $solibs -Wl,--end-group $libs'),
1893 elif flavor == 'win':
1896 description='LIB $out',
1897 command=('%s gyp-win-tool link-wrapper $arch False '
1898 '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
1901 rspfile_content='$in_newline $libflags')
1902 _AddWinLinkRules(master_ninja, embed_manifest=True)
1903 _AddWinLinkRules(master_ninja, embed_manifest=False)
1907 description='OBJC $out',
1908 command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
1909 '$cflags_pch_objc -c $in -o $out'),
1914 description='OBJCXX $out',
1915 command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
1916 '$cflags_pch_objcc -c $in -o $out'),
1921 description='LIBTOOL-STATIC $out, POSTBUILDS',
1922 command='rm -f $out && '
1923 './gyp-mac-tool filter-libtool libtool $libtool_flags '
1924 '-static -o $out $in'
1928 description='LIPO $out, POSTBUILDS',
1929 command='rm -f $out && lipo -create $in -output $out$postbuilds')
1931 # Record the public interface of $lib in $lib.TOC. See the corresponding
1932 # comment in the posix section above for details.
1933 solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
1934 mtime_preserving_solink_base = (
1935 'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
1936 # Always force dependent targets to relink if this library
1937 # reexports something. Handling this correctly would require
1938 # recursive TOC dumping but this is rare in practice, so punt.
1939 'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
1940 '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
1942 '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
1943 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
1944 'mv ${lib}.tmp ${lib}.TOC ; '
1947 % { 'solink': solink_base,
1949 '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
1950 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
1952 solink_suffix = '$in $solibs $libs$postbuilds'
1955 description='SOLINK $lib, POSTBUILDS',
1957 command=mtime_preserving_solink_base % {'suffix': solink_suffix,
1962 description='SOLINK $lib, POSTBUILDS',
1964 command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
1967 solink_module_suffix = '$in $solibs $libs$postbuilds'
1970 description='SOLINK(module) $lib, POSTBUILDS',
1972 command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
1976 'solink_module_notoc',
1977 description='SOLINK(module) $lib, POSTBUILDS',
1979 command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
1984 description='LINK $out, POSTBUILDS',
1985 command=('$ld $ldflags -o $out '
1986 '$in $solibs $libs$postbuilds'),
1989 'preprocess_infoplist',
1990 description='PREPROCESS INFOPLIST $out',
1991 command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
1992 'plutil -convert xml1 $out $out'))
1995 description='COPY INFOPLIST $in',
1996 command='$env ./gyp-mac-tool copy-info-plist $in $out $keys')
1999 description='MACTOOL $mactool_cmd $in',
2000 command='$env ./gyp-mac-tool $mactool_cmd $in $out')
2002 'package_framework',
2003 description='PACKAGE FRAMEWORK $out, POSTBUILDS',
2004 command='./gyp-mac-tool package-framework $out $version$postbuilds '
2009 description='STAMP $out',
2010 command='%s gyp-win-tool stamp $out' % sys.executable)
2013 description='COPY $in $out',
2014 command='%s gyp-win-tool recursive-mirror $in $out' % sys.executable)
2018 description='STAMP $out',
2019 command='${postbuilds}touch $out')
2022 description='COPY $in $out',
2023 command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
2024 master_ninja.newline()
2027 for build_file in params['build_files']:
2028 for target in gyp.common.AllTargets(target_list,
2030 os.path.normpath(build_file)):
2031 all_targets.add(target)
2034 # target_outputs is a map from qualified target name to a Target object.
2036 # target_short_names is a map from target short name to a list of Target
2038 target_short_names = {}
2040 for qualified_target in target_list:
2041 # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
2042 build_file, name, toolset = \
2043 gyp.common.ParseQualifiedTarget(qualified_target)
2045 this_make_global_settings = data[build_file].get('make_global_settings', [])
2046 assert make_global_settings == this_make_global_settings, (
2047 "make_global_settings needs to be the same for all targets. %s vs. %s" %
2048 (this_make_global_settings, make_global_settings))
2050 spec = target_dicts[qualified_target]
2052 gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
2054 build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
2056 base_path = os.path.dirname(build_file)
2058 if toolset != 'target':
2059 obj += '.' + toolset
2060 output_file = os.path.join(obj, base_path, name + '.ninja')
2062 ninja_output = StringIO()
2063 writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
2065 toplevel_build, output_file,
2066 flavor, toplevel_dir=options.toplevel_dir)
2068 target = writer.WriteSpec(spec, config_name, generator_flags)
2070 if ninja_output.tell() > 0:
2071 # Only create files for ninja files that actually have contents.
2072 with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file:
2073 ninja_file.write(ninja_output.getvalue())
2074 ninja_output.close()
2075 master_ninja.subninja(output_file)
2078 if name != target.FinalOutput() and spec['toolset'] == 'target':
2079 target_short_names.setdefault(name, []).append(target)
2080 target_outputs[qualified_target] = target
2081 if qualified_target in all_targets:
2082 all_outputs.add(target.FinalOutput())
2084 if target_short_names:
2085 # Write a short name to build this target. This benefits both the
2086 # "build chrome" case as well as the gyp tests, which expect to be
2087 # able to run actions and build libraries by their short name.
2088 master_ninja.newline()
2089 master_ninja.comment('Short names for targets.')
2090 for short_name in target_short_names:
2091 master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
2092 target_short_names[short_name]])
2095 master_ninja.newline()
2096 master_ninja.build('all', 'phony', list(all_outputs))
2097 master_ninja.default(generator_flags.get('default_target', 'all'))
2099 master_ninja_file.close()
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,