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. 4 5import collections 6import copy 7import hashlib 8import json 9import multiprocessing 10import os.path 11import re 12import signal 13import subprocess 14import sys 15import gyp 16import gyp.common 17from gyp.common import OrderedSet 18import gyp.msvs_emulation 19import gyp.MSVSUtil as MSVSUtil 20import gyp.xcode_emulation 21from cStringIO import StringIO 22 23from gyp.common import GetEnvironFallback 24import gyp.ninja_syntax as ninja_syntax 25 26generator_default_variables = { 27 'EXECUTABLE_PREFIX': '', 28 'EXECUTABLE_SUFFIX': '', 29 'STATIC_LIB_PREFIX': 'lib', 30 'STATIC_LIB_SUFFIX': '.a', 31 'SHARED_LIB_PREFIX': 'lib', 32 33 # Gyp expects the following variables to be expandable by the build 34 # system to the appropriate locations. Ninja prefers paths to be 35 # known at gyp time. To resolve this, introduce special 36 # variables starting with $! and $| (which begin with a $ so gyp knows it 37 # should be treated specially, but is otherwise an invalid 38 # ninja/shell variable) that are passed to gyp here but expanded 39 # before writing out into the target .ninja files; see 40 # ExpandSpecial. 41 # $! is used for variables that represent a path and that can only appear at 42 # the start of a string, while $| is used for variables that can appear 43 # anywhere in a string. 44 'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR', 45 'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen', 46 'PRODUCT_DIR': '$!PRODUCT_DIR', 47 'CONFIGURATION_NAME': '$|CONFIGURATION_NAME', 48 49 # Special variables that may be used by gyp 'rule' targets. 50 # We generate definitions for these variables on the fly when processing a 51 # rule. 52 'RULE_INPUT_ROOT': '${root}', 53 'RULE_INPUT_DIRNAME': '${dirname}', 54 'RULE_INPUT_PATH': '${source}', 55 'RULE_INPUT_EXT': '${ext}', 56 'RULE_INPUT_NAME': '${name}', 57} 58 59# Placates pylint. 60generator_additional_non_configuration_keys = [] 61generator_additional_path_sections = [] 62generator_extra_sources_for_rules = [] 63generator_filelist_paths = None 64 65# TODO: figure out how to not build extra host objects in the non-cross-compile 66# case when this is enabled, and enable unconditionally. 67generator_supports_multiple_toolsets = ( 68 os.environ.get('GYP_CROSSCOMPILE') or 69 os.environ.get('AR_host') or 70 os.environ.get('CC_host') or 71 os.environ.get('CXX_host') or 72 os.environ.get('AR_target') or 73 os.environ.get('CC_target') or 74 os.environ.get('CXX_target')) 75 76 77def StripPrefix(arg, prefix): 78 if arg.startswith(prefix): 79 return arg[len(prefix):] 80 return arg 81 82 83def QuoteShellArgument(arg, flavor): 84 """Quote a string such that it will be interpreted as a single argument 85 by the shell.""" 86 # Rather than attempting to enumerate the bad shell characters, just 87 # whitelist common OK ones and quote anything else. 88 if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg): 89 return arg # No quoting necessary. 90 if flavor == 'win': 91 return gyp.msvs_emulation.QuoteForRspFile(arg) 92 return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'" 93 94 95def Define(d, flavor): 96 """Takes a preprocessor define and returns a -D parameter that's ninja- and 97 shell-escaped.""" 98 if flavor == 'win': 99 # cl.exe replaces literal # characters with = in preprocesor definitions for 100 # some reason. Octal-encode to work around that. 101 d = d.replace('#', '\\%03o' % ord('#')) 102 return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor) 103 104 105def AddArch(output, arch): 106 """Adds an arch string to an output path.""" 107 output, extension = os.path.splitext(output) 108 return '%s.%s%s' % (output, arch, extension) 109 110 111class Target: 112 """Target represents the paths used within a single gyp target. 113 114 Conceptually, building a single target A is a series of steps: 115 116 1) actions/rules/copies generates source/resources/etc. 117 2) compiles generates .o files 118 3) link generates a binary (library/executable) 119 4) bundle merges the above in a mac bundle 120 121 (Any of these steps can be optional.) 122 123 From a build ordering perspective, a dependent target B could just 124 depend on the last output of this series of steps. 125 126 But some dependent commands sometimes need to reach inside the box. 127 For example, when linking B it needs to get the path to the static 128 library generated by A. 129 130 This object stores those paths. To keep things simple, member 131 variables only store concrete paths to single files, while methods 132 compute derived values like "the last output of the target". 133 """ 134 def __init__(self, type): 135 # Gyp type ("static_library", etc.) of this target. 136 self.type = type 137 # File representing whether any input dependencies necessary for 138 # dependent actions have completed. 139 self.preaction_stamp = None 140 # File representing whether any input dependencies necessary for 141 # dependent compiles have completed. 142 self.precompile_stamp = None 143 # File representing the completion of actions/rules/copies, if any. 144 self.actions_stamp = None 145 # Path to the output of the link step, if any. 146 self.binary = None 147 # Path to the file representing the completion of building the bundle, 148 # if any. 149 self.bundle = None 150 # On Windows, incremental linking requires linking against all the .objs 151 # that compose a .lib (rather than the .lib itself). That list is stored 152 # here. 153 self.component_objs = None 154 # Windows only. The import .lib is the output of a build step, but 155 # because dependents only link against the lib (not both the lib and the 156 # dll) we keep track of the import library here. 157 self.import_lib = None 158 159 def Linkable(self): 160 """Return true if this is a target that can be linked against.""" 161 return self.type in ('static_library', 'shared_library') 162 163 def UsesToc(self, flavor): 164 """Return true if the target should produce a restat rule based on a TOC 165 file.""" 166 # For bundles, the .TOC should be produced for the binary, not for 167 # FinalOutput(). But the naive approach would put the TOC file into the 168 # bundle, so don't do this for bundles for now. 169 if flavor == 'win' or self.bundle: 170 return False 171 return self.type in ('shared_library', 'loadable_module') 172 173 def PreActionInput(self, flavor): 174 """Return the path, if any, that should be used as a dependency of 175 any dependent action step.""" 176 if self.UsesToc(flavor): 177 return self.FinalOutput() + '.TOC' 178 return self.FinalOutput() or self.preaction_stamp 179 180 def PreCompileInput(self): 181 """Return the path, if any, that should be used as a dependency of 182 any dependent compile step.""" 183 return self.actions_stamp or self.precompile_stamp 184 185 def FinalOutput(self): 186 """Return the last output of the target, which depends on all prior 187 steps.""" 188 return self.bundle or self.binary or self.actions_stamp 189 190 191# A small discourse on paths as used within the Ninja build: 192# All files we produce (both at gyp and at build time) appear in the 193# build directory (e.g. out/Debug). 194# 195# Paths within a given .gyp file are always relative to the directory 196# containing the .gyp file. Call these "gyp paths". This includes 197# sources as well as the starting directory a given gyp rule/action 198# expects to be run from. We call the path from the source root to 199# the gyp file the "base directory" within the per-.gyp-file 200# NinjaWriter code. 201# 202# All paths as written into the .ninja files are relative to the build 203# directory. Call these paths "ninja paths". 204# 205# We translate between these two notions of paths with two helper 206# functions: 207# 208# - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file) 209# into the equivalent ninja path. 210# 211# - GypPathToUniqueOutput translates a gyp path into a ninja path to write 212# an output file; the result can be namespaced such that it is unique 213# to the input file name as well as the output target name. 214 215class NinjaWriter: 216 def __init__(self, qualified_target, target_outputs, base_dir, build_dir, 217 output_file, toplevel_build, output_file_name, flavor, 218 toplevel_dir=None): 219 """ 220 base_dir: path from source root to directory containing this gyp file, 221 by gyp semantics, all input paths are relative to this 222 build_dir: path from source root to build output 223 toplevel_dir: path to the toplevel directory 224 """ 225 226 self.qualified_target = qualified_target 227 self.target_outputs = target_outputs 228 self.base_dir = base_dir 229 self.build_dir = build_dir 230 self.ninja = ninja_syntax.Writer(output_file) 231 self.toplevel_build = toplevel_build 232 self.output_file_name = output_file_name 233 234 self.flavor = flavor 235 self.abs_build_dir = None 236 if toplevel_dir is not None: 237 self.abs_build_dir = os.path.abspath(os.path.join(toplevel_dir, 238 build_dir)) 239 self.obj_ext = '.obj' if flavor == 'win' else '.o' 240 if flavor == 'win': 241 # See docstring of msvs_emulation.GenerateEnvironmentFiles(). 242 self.win_env = {} 243 for arch in ('x86', 'x64'): 244 self.win_env[arch] = 'environment.' + arch 245 246 # Relative path from build output dir to base dir. 247 build_to_top = gyp.common.InvertRelativePath(build_dir, toplevel_dir) 248 self.build_to_base = os.path.join(build_to_top, base_dir) 249 # Relative path from base dir to build dir. 250 base_to_top = gyp.common.InvertRelativePath(base_dir, toplevel_dir) 251 self.base_to_build = os.path.join(base_to_top, build_dir) 252 253 def ExpandSpecial(self, path, product_dir=None): 254 """Expand specials like $!PRODUCT_DIR in |path|. 255 256 If |product_dir| is None, assumes the cwd is already the product 257 dir. Otherwise, |product_dir| is the relative path to the product 258 dir. 259 """ 260 261 PRODUCT_DIR = '$!PRODUCT_DIR' 262 if PRODUCT_DIR in path: 263 if product_dir: 264 path = path.replace(PRODUCT_DIR, product_dir) 265 else: 266 path = path.replace(PRODUCT_DIR + '/', '') 267 path = path.replace(PRODUCT_DIR + '\\', '') 268 path = path.replace(PRODUCT_DIR, '.') 269 270 INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR' 271 if INTERMEDIATE_DIR in path: 272 int_dir = self.GypPathToUniqueOutput('gen') 273 # GypPathToUniqueOutput generates a path relative to the product dir, 274 # so insert product_dir in front if it is provided. 275 path = path.replace(INTERMEDIATE_DIR, 276 os.path.join(product_dir or '', int_dir)) 277 278 CONFIGURATION_NAME = '$|CONFIGURATION_NAME' 279 path = path.replace(CONFIGURATION_NAME, self.config_name) 280 281 return path 282 283 def ExpandRuleVariables(self, path, root, dirname, source, ext, name): 284 if self.flavor == 'win': 285 path = self.msvs_settings.ConvertVSMacros( 286 path, config=self.config_name) 287 path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root) 288 path = path.replace(generator_default_variables['RULE_INPUT_DIRNAME'], 289 dirname) 290 path = path.replace(generator_default_variables['RULE_INPUT_PATH'], source) 291 path = path.replace(generator_default_variables['RULE_INPUT_EXT'], ext) 292 path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name) 293 return path 294 295 def GypPathToNinja(self, path, env=None): 296 """Translate a gyp path to a ninja path, optionally expanding environment 297 variable references in |path| with |env|. 298 299 See the above discourse on path conversions.""" 300 if env: 301 if self.flavor == 'mac': 302 path = gyp.xcode_emulation.ExpandEnvVars(path, env) 303 elif self.flavor == 'win': 304 path = gyp.msvs_emulation.ExpandMacros(path, env) 305 if path.startswith('$!'): 306 expanded = self.ExpandSpecial(path) 307 if self.flavor == 'win': 308 expanded = os.path.normpath(expanded) 309 return expanded 310 if '$|' in path: 311 path = self.ExpandSpecial(path) 312 assert '$' not in path, path 313 return os.path.normpath(os.path.join(self.build_to_base, path)) 314 315 def GypPathToUniqueOutput(self, path, qualified=True): 316 """Translate a gyp path to a ninja path for writing output. 317 318 If qualified is True, qualify the resulting filename with the name 319 of the target. This is necessary when e.g. compiling the same 320 path twice for two separate output targets. 321 322 See the above discourse on path conversions.""" 323 324 path = self.ExpandSpecial(path) 325 assert not path.startswith('$'), path 326 327 # Translate the path following this scheme: 328 # Input: foo/bar.gyp, target targ, references baz/out.o 329 # Output: obj/foo/baz/targ.out.o (if qualified) 330 # obj/foo/baz/out.o (otherwise) 331 # (and obj.host instead of obj for cross-compiles) 332 # 333 # Why this scheme and not some other one? 334 # 1) for a given input, you can compute all derived outputs by matching 335 # its path, even if the input is brought via a gyp file with '..'. 336 # 2) simple files like libraries and stamps have a simple filename. 337 338 obj = 'obj' 339 if self.toolset != 'target': 340 obj += '.' + self.toolset 341 342 path_dir, path_basename = os.path.split(path) 343 if qualified: 344 path_basename = self.name + '.' + path_basename 345 return os.path.normpath(os.path.join(obj, self.base_dir, path_dir, 346 path_basename)) 347 348 def WriteCollapsedDependencies(self, name, targets, order_only=None): 349 """Given a list of targets, return a path for a single file 350 representing the result of building all the targets or None. 351 352 Uses a stamp file if necessary.""" 353 354 assert targets == filter(None, targets), targets 355 if len(targets) == 0: 356 assert not order_only 357 return None 358 if len(targets) > 1 or order_only: 359 stamp = self.GypPathToUniqueOutput(name + '.stamp') 360 targets = self.ninja.build(stamp, 'stamp', targets, order_only=order_only) 361 self.ninja.newline() 362 return targets[0] 363 364 def _SubninjaNameForArch(self, arch): 365 output_file_base = os.path.splitext(self.output_file_name)[0] 366 return '%s.%s.ninja' % (output_file_base, arch) 367 368 def WriteSpec(self, spec, config_name, generator_flags): 369 """The main entry point for NinjaWriter: write the build rules for a spec. 370 371 Returns a Target object, which represents the output paths for this spec. 372 Returns None if there are no outputs (e.g. a settings-only 'none' type 373 target).""" 374 375 self.config_name = config_name 376 self.name = spec['target_name'] 377 self.toolset = spec['toolset'] 378 config = spec['configurations'][config_name] 379 self.target = Target(spec['type']) 380 self.is_standalone_static_library = bool( 381 spec.get('standalone_static_library', 0)) 382 # Track if this target contains any C++ files, to decide if gcc or g++ 383 # should be used for linking. 384 self.uses_cpp = False 385 386 self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec) 387 self.xcode_settings = self.msvs_settings = None 388 if self.flavor == 'mac': 389 self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec) 390 if self.flavor == 'win': 391 self.msvs_settings = gyp.msvs_emulation.MsvsSettings(spec, 392 generator_flags) 393 arch = self.msvs_settings.GetArch(config_name) 394 self.ninja.variable('arch', self.win_env[arch]) 395 self.ninja.variable('cc', '$cl_' + arch) 396 self.ninja.variable('cxx', '$cl_' + arch) 397 self.ninja.variable('cc_host', '$cl_' + arch) 398 self.ninja.variable('cxx_host', '$cl_' + arch) 399 400 if self.flavor == 'mac': 401 self.archs = self.xcode_settings.GetActiveArchs(config_name) 402 if len(self.archs) > 1: 403 self.arch_subninjas = dict( 404 (arch, ninja_syntax.Writer( 405 OpenOutput(os.path.join(self.toplevel_build, 406 self._SubninjaNameForArch(arch)), 407 'w'))) 408 for arch in self.archs) 409 410 # Compute predepends for all rules. 411 # actions_depends is the dependencies this target depends on before running 412 # any of its action/rule/copy steps. 413 # compile_depends is the dependencies this target depends on before running 414 # any of its compile steps. 415 actions_depends = [] 416 compile_depends = [] 417 # TODO(evan): it is rather confusing which things are lists and which 418 # are strings. Fix these. 419 if 'dependencies' in spec: 420 for dep in spec['dependencies']: 421 if dep in self.target_outputs: 422 target = self.target_outputs[dep] 423 actions_depends.append(target.PreActionInput(self.flavor)) 424 compile_depends.append(target.PreCompileInput()) 425 actions_depends = filter(None, actions_depends) 426 compile_depends = filter(None, compile_depends) 427 actions_depends = self.WriteCollapsedDependencies('actions_depends', 428 actions_depends) 429 compile_depends = self.WriteCollapsedDependencies('compile_depends', 430 compile_depends) 431 self.target.preaction_stamp = actions_depends 432 self.target.precompile_stamp = compile_depends 433 434 # Write out actions, rules, and copies. These must happen before we 435 # compile any sources, so compute a list of predependencies for sources 436 # while we do it. 437 extra_sources = [] 438 mac_bundle_depends = [] 439 self.target.actions_stamp = self.WriteActionsRulesCopies( 440 spec, extra_sources, actions_depends, mac_bundle_depends) 441 442 # If we have actions/rules/copies, we depend directly on those, but 443 # otherwise we depend on dependent target's actions/rules/copies etc. 444 # We never need to explicitly depend on previous target's link steps, 445 # because no compile ever depends on them. 446 compile_depends_stamp = (self.target.actions_stamp or compile_depends) 447 448 # Write out the compilation steps, if any. 449 link_deps = [] 450 sources = extra_sources + spec.get('sources', []) 451 if sources: 452 if self.flavor == 'mac' and len(self.archs) > 1: 453 # Write subninja file containing compile and link commands scoped to 454 # a single arch if a fat binary is being built. 455 for arch in self.archs: 456 self.ninja.subninja(self._SubninjaNameForArch(arch)) 457 458 pch = None 459 if self.flavor == 'win': 460 gyp.msvs_emulation.VerifyMissingSources( 461 sources, self.abs_build_dir, generator_flags, self.GypPathToNinja) 462 pch = gyp.msvs_emulation.PrecompiledHeader( 463 self.msvs_settings, config_name, self.GypPathToNinja, 464 self.GypPathToUniqueOutput, self.obj_ext) 465 else: 466 pch = gyp.xcode_emulation.MacPrefixHeader( 467 self.xcode_settings, self.GypPathToNinja, 468 lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang)) 469 link_deps = self.WriteSources( 470 self.ninja, config_name, config, sources, compile_depends_stamp, pch, 471 spec) 472 # Some actions/rules output 'sources' that are already object files. 473 obj_outputs = [f for f in sources if f.endswith(self.obj_ext)] 474 if obj_outputs: 475 if self.flavor != 'mac' or len(self.archs) == 1: 476 link_deps += [self.GypPathToNinja(o) for o in obj_outputs] 477 else: 478 print "Warning: Actions/rules writing object files don't work with " \ 479 "multiarch targets, dropping. (target %s)" % spec['target_name'] 480 elif self.flavor == 'mac' and len(self.archs) > 1: 481 link_deps = collections.defaultdict(list) 482 483 484 if self.flavor == 'win' and self.target.type == 'static_library': 485 self.target.component_objs = link_deps 486 487 # Write out a link step, if needed. 488 output = None 489 is_empty_bundle = not link_deps and not mac_bundle_depends 490 if link_deps or self.target.actions_stamp or actions_depends: 491 output = self.WriteTarget(spec, config_name, config, link_deps, 492 self.target.actions_stamp or actions_depends) 493 if self.is_mac_bundle: 494 mac_bundle_depends.append(output) 495 496 # Bundle all of the above together, if needed. 497 if self.is_mac_bundle: 498 output = self.WriteMacBundle(spec, mac_bundle_depends, is_empty_bundle) 499 500 if not output: 501 return None 502 503 assert self.target.FinalOutput(), output 504 return self.target 505 506 def _WinIdlRule(self, source, prebuild, outputs): 507 """Handle the implicit VS .idl rule for one source file. Fills |outputs| 508 with files that are generated.""" 509 outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData( 510 source, self.config_name) 511 outdir = self.GypPathToNinja(outdir) 512 def fix_path(path, rel=None): 513 path = os.path.join(outdir, path) 514 dirname, basename = os.path.split(source) 515 root, ext = os.path.splitext(basename) 516 path = self.ExpandRuleVariables( 517 path, root, dirname, source, ext, basename) 518 if rel: 519 path = os.path.relpath(path, rel) 520 return path 521 vars = [(name, fix_path(value, outdir)) for name, value in vars] 522 output = [fix_path(p) for p in output] 523 vars.append(('outdir', outdir)) 524 vars.append(('idlflags', flags)) 525 input = self.GypPathToNinja(source) 526 self.ninja.build(output, 'idl', input, 527 variables=vars, order_only=prebuild) 528 outputs.extend(output) 529 530 def WriteWinIdlFiles(self, spec, prebuild): 531 """Writes rules to match MSVS's implicit idl handling.""" 532 assert self.flavor == 'win' 533 if self.msvs_settings.HasExplicitIdlRulesOrActions(spec): 534 return [] 535 outputs = [] 536 for source in filter(lambda x: x.endswith('.idl'), spec['sources']): 537 self._WinIdlRule(source, prebuild, outputs) 538 return outputs 539 540 def WriteActionsRulesCopies(self, spec, extra_sources, prebuild, 541 mac_bundle_depends): 542 """Write out the Actions, Rules, and Copies steps. Return a path 543 representing the outputs of these steps.""" 544 outputs = [] 545 if self.is_mac_bundle: 546 mac_bundle_resources = spec.get('mac_bundle_resources', [])[:] 547 else: 548 mac_bundle_resources = [] 549 extra_mac_bundle_resources = [] 550 551 if 'actions' in spec: 552 outputs += self.WriteActions(spec['actions'], extra_sources, prebuild, 553 extra_mac_bundle_resources) 554 if 'rules' in spec: 555 outputs += self.WriteRules(spec['rules'], extra_sources, prebuild, 556 mac_bundle_resources, 557 extra_mac_bundle_resources) 558 if 'copies' in spec: 559 outputs += self.WriteCopies(spec['copies'], prebuild, mac_bundle_depends) 560 561 if 'sources' in spec and self.flavor == 'win': 562 outputs += self.WriteWinIdlFiles(spec, prebuild) 563 564 stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs) 565 566 if self.is_mac_bundle: 567 self.WriteMacBundleResources( 568 extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends) 569 self.WriteMacInfoPlist(mac_bundle_depends) 570 571 return stamp 572 573 def GenerateDescription(self, verb, message, fallback): 574 """Generate and return a description of a build step. 575 576 |verb| is the short summary, e.g. ACTION or RULE. 577 |message| is a hand-written description, or None if not available. 578 |fallback| is the gyp-level name of the step, usable as a fallback. 579 """ 580 if self.toolset != 'target': 581 verb += '(%s)' % self.toolset 582 if message: 583 return '%s %s' % (verb, self.ExpandSpecial(message)) 584 else: 585 return '%s %s: %s' % (verb, self.name, fallback) 586 587 def WriteActions(self, actions, extra_sources, prebuild, 588 extra_mac_bundle_resources): 589 # Actions cd into the base directory. 590 env = self.GetToolchainEnv() 591 all_outputs = [] 592 for action in actions: 593 # First write out a rule for the action. 594 name = '%s_%s' % (action['action_name'], 595 hashlib.md5(self.qualified_target).hexdigest()) 596 description = self.GenerateDescription('ACTION', 597 action.get('message', None), 598 name) 599 is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action) 600 if self.flavor == 'win' else False) 601 args = action['action'] 602 pool = 'console' if int(action.get('ninja_use_console', 0)) else None 603 rule_name, _ = self.WriteNewNinjaRule(name, args, description, 604 is_cygwin, env, pool) 605 606 inputs = [self.GypPathToNinja(i, env) for i in action['inputs']] 607 if int(action.get('process_outputs_as_sources', False)): 608 extra_sources += action['outputs'] 609 if int(action.get('process_outputs_as_mac_bundle_resources', False)): 610 extra_mac_bundle_resources += action['outputs'] 611 outputs = [self.GypPathToNinja(o, env) for o in action['outputs']] 612 613 # Then write out an edge using the rule. 614 self.ninja.build(outputs, rule_name, inputs, 615 order_only=prebuild) 616 all_outputs += outputs 617 618 self.ninja.newline() 619 620 return all_outputs 621 622 def WriteRules(self, rules, extra_sources, prebuild, 623 mac_bundle_resources, extra_mac_bundle_resources): 624 env = self.GetToolchainEnv() 625 all_outputs = [] 626 for rule in rules: 627 # Skip a rule with no action and no inputs. 628 if 'action' not in rule and not rule.get('rule_sources', []): 629 continue 630 631 # First write out a rule for the rule action. 632 name = '%s_%s' % (rule['rule_name'], 633 hashlib.md5(self.qualified_target).hexdigest()) 634 635 args = rule['action'] 636 description = self.GenerateDescription( 637 'RULE', 638 rule.get('message', None), 639 ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name) 640 is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule) 641 if self.flavor == 'win' else False) 642 pool = 'console' if int(rule.get('ninja_use_console', 0)) else None 643 rule_name, args = self.WriteNewNinjaRule( 644 name, args, description, is_cygwin, env, pool) 645 646 # TODO: if the command references the outputs directly, we should 647 # simplify it to just use $out. 648 649 # Rules can potentially make use of some special variables which 650 # must vary per source file. 651 # Compute the list of variables we'll need to provide. 652 special_locals = ('source', 'root', 'dirname', 'ext', 'name') 653 needed_variables = set(['source']) 654 for argument in args: 655 for var in special_locals: 656 if ('${%s}' % var) in argument: 657 needed_variables.add(var) 658 659 def cygwin_munge(path): 660 if is_cygwin: 661 return path.replace('\\', '/') 662 return path 663 664 inputs = [self.GypPathToNinja(i, env) for i in rule.get('inputs', [])] 665 666 # If there are n source files matching the rule, and m additional rule 667 # inputs, then adding 'inputs' to each build edge written below will 668 # write m * n inputs. Collapsing reduces this to m + n. 669 sources = rule.get('rule_sources', []) 670 num_inputs = len(inputs) 671 if prebuild: 672 num_inputs += 1 673 if num_inputs > 2 and len(sources) > 2: 674 inputs = [self.WriteCollapsedDependencies( 675 rule['rule_name'], inputs, order_only=prebuild)] 676 prebuild = [] 677 678 # For each source file, write an edge that generates all the outputs. 679 for source in sources: 680 source = os.path.normpath(source) 681 dirname, basename = os.path.split(source) 682 root, ext = os.path.splitext(basename) 683 684 # Gather the list of inputs and outputs, expanding $vars if possible. 685 outputs = [self.ExpandRuleVariables(o, root, dirname, 686 source, ext, basename) 687 for o in rule['outputs']] 688 689 if int(rule.get('process_outputs_as_sources', False)): 690 extra_sources += outputs 691 692 was_mac_bundle_resource = source in mac_bundle_resources 693 if was_mac_bundle_resource or \ 694 int(rule.get('process_outputs_as_mac_bundle_resources', False)): 695 extra_mac_bundle_resources += outputs 696 # Note: This is n_resources * n_outputs_in_rule. Put to-be-removed 697 # items in a set and remove them all in a single pass if this becomes 698 # a performance issue. 699 if was_mac_bundle_resource: 700 mac_bundle_resources.remove(source) 701 702 extra_bindings = [] 703 for var in needed_variables: 704 if var == 'root': 705 extra_bindings.append(('root', cygwin_munge(root))) 706 elif var == 'dirname': 707 # '$dirname' is a parameter to the rule action, which means 708 # it shouldn't be converted to a Ninja path. But we don't 709 # want $!PRODUCT_DIR in there either. 710 dirname_expanded = self.ExpandSpecial(dirname, self.base_to_build) 711 extra_bindings.append(('dirname', cygwin_munge(dirname_expanded))) 712 elif var == 'source': 713 # '$source' is a parameter to the rule action, which means 714 # it shouldn't be converted to a Ninja path. But we don't 715 # want $!PRODUCT_DIR in there either. 716 source_expanded = self.ExpandSpecial(source, self.base_to_build) 717 extra_bindings.append(('source', cygwin_munge(source_expanded))) 718 elif var == 'ext': 719 extra_bindings.append(('ext', ext)) 720 elif var == 'name': 721 extra_bindings.append(('name', cygwin_munge(basename))) 722 else: 723 assert var == None, repr(var) 724 725 outputs = [self.GypPathToNinja(o, env) for o in outputs] 726 if self.flavor == 'win': 727 # WriteNewNinjaRule uses unique_name for creating an rsp file on win. 728 extra_bindings.append(('unique_name', 729 hashlib.md5(outputs[0]).hexdigest())) 730 self.ninja.build(outputs, rule_name, self.GypPathToNinja(source), 731 implicit=inputs, 732 order_only=prebuild, 733 variables=extra_bindings) 734 735 all_outputs.extend(outputs) 736 737 return all_outputs 738 739 def WriteCopies(self, copies, prebuild, mac_bundle_depends): 740 outputs = [] 741 env = self.GetToolchainEnv() 742 for copy in copies: 743 for path in copy['files']: 744 # Normalize the path so trailing slashes don't confuse us. 745 path = os.path.normpath(path) 746 basename = os.path.split(path)[1] 747 src = self.GypPathToNinja(path, env) 748 dst = self.GypPathToNinja(os.path.join(copy['destination'], basename), 749 env) 750 outputs += self.ninja.build(dst, 'copy', src, order_only=prebuild) 751 if self.is_mac_bundle: 752 # gyp has mac_bundle_resources to copy things into a bundle's 753 # Resources folder, but there's no built-in way to copy files to other 754 # places in the bundle. Hence, some targets use copies for this. Check 755 # if this file is copied into the current bundle, and if so add it to 756 # the bundle depends so that dependent targets get rebuilt if the copy 757 # input changes. 758 if dst.startswith(self.xcode_settings.GetBundleContentsFolderPath()): 759 mac_bundle_depends.append(dst) 760 761 return outputs 762 763 def WriteMacBundleResources(self, resources, bundle_depends): 764 """Writes ninja edges for 'mac_bundle_resources'.""" 765 for output, res in gyp.xcode_emulation.GetMacBundleResources( 766 generator_default_variables['PRODUCT_DIR'], 767 self.xcode_settings, map(self.GypPathToNinja, resources)): 768 output = self.ExpandSpecial(output) 769 self.ninja.build(output, 'mac_tool', res, 770 variables=[('mactool_cmd', 'copy-bundle-resource')]) 771 bundle_depends.append(output) 772 773 def WriteMacInfoPlist(self, bundle_depends): 774 """Write build rules for bundle Info.plist files.""" 775 info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist( 776 generator_default_variables['PRODUCT_DIR'], 777 self.xcode_settings, self.GypPathToNinja) 778 if not info_plist: 779 return 780 out = self.ExpandSpecial(out) 781 if defines: 782 # Create an intermediate file to store preprocessed results. 783 intermediate_plist = self.GypPathToUniqueOutput( 784 os.path.basename(info_plist)) 785 defines = ' '.join([Define(d, self.flavor) for d in defines]) 786 info_plist = self.ninja.build( 787 intermediate_plist, 'preprocess_infoplist', info_plist, 788 variables=[('defines',defines)]) 789 790 env = self.GetSortedXcodeEnv(additional_settings=extra_env) 791 env = self.ComputeExportEnvString(env) 792 793 keys = self.xcode_settings.GetExtraPlistItems(self.config_name) 794 keys = QuoteShellArgument(json.dumps(keys), self.flavor) 795 self.ninja.build(out, 'copy_infoplist', info_plist, 796 variables=[('env', env), ('keys', keys)]) 797 bundle_depends.append(out) 798 799 def WriteSources(self, ninja_file, config_name, config, sources, predepends, 800 precompiled_header, spec): 801 """Write build rules to compile all of |sources|.""" 802 if self.toolset == 'host': 803 self.ninja.variable('ar', '$ar_host') 804 self.ninja.variable('cc', '$cc_host') 805 self.ninja.variable('cxx', '$cxx_host') 806 self.ninja.variable('ld', '$ld_host') 807 self.ninja.variable('ldxx', '$ldxx_host') 808 self.ninja.variable('nm', '$nm_host') 809 self.ninja.variable('readelf', '$readelf_host') 810 811 if self.flavor != 'mac' or len(self.archs) == 1: 812 return self.WriteSourcesForArch( 813 self.ninja, config_name, config, sources, predepends, 814 precompiled_header, spec) 815 else: 816 return dict((arch, self.WriteSourcesForArch( 817 self.arch_subninjas[arch], config_name, config, sources, predepends, 818 precompiled_header, spec, arch=arch)) 819 for arch in self.archs) 820 821 def WriteSourcesForArch(self, ninja_file, config_name, config, sources, 822 predepends, precompiled_header, spec, arch=None): 823 """Write build rules to compile all of |sources|.""" 824 825 extra_defines = [] 826 if self.flavor == 'mac': 827 cflags = self.xcode_settings.GetCflags(config_name, arch=arch) 828 cflags_c = self.xcode_settings.GetCflagsC(config_name) 829 cflags_cc = self.xcode_settings.GetCflagsCC(config_name) 830 cflags_objc = ['$cflags_c'] + \ 831 self.xcode_settings.GetCflagsObjC(config_name) 832 cflags_objcc = ['$cflags_cc'] + \ 833 self.xcode_settings.GetCflagsObjCC(config_name) 834 elif self.flavor == 'win': 835 asmflags = self.msvs_settings.GetAsmflags(config_name) 836 cflags = self.msvs_settings.GetCflags(config_name) 837 cflags_c = self.msvs_settings.GetCflagsC(config_name) 838 cflags_cc = self.msvs_settings.GetCflagsCC(config_name) 839 extra_defines = self.msvs_settings.GetComputedDefines(config_name) 840 # See comment at cc_command for why there's two .pdb files. 841 pdbpath_c = pdbpath_cc = self.msvs_settings.GetCompilerPdbName( 842 config_name, self.ExpandSpecial) 843 if not pdbpath_c: 844 obj = 'obj' 845 if self.toolset != 'target': 846 obj += '.' + self.toolset 847 pdbpath = os.path.normpath(os.path.join(obj, self.base_dir, self.name)) 848 pdbpath_c = pdbpath + '.c.pdb' 849 pdbpath_cc = pdbpath + '.cc.pdb' 850 self.WriteVariableList(ninja_file, 'pdbname_c', [pdbpath_c]) 851 self.WriteVariableList(ninja_file, 'pdbname_cc', [pdbpath_cc]) 852 self.WriteVariableList(ninja_file, 'pchprefix', [self.name]) 853 else: 854 cflags = config.get('cflags', []) 855 cflags_c = config.get('cflags_c', []) 856 cflags_cc = config.get('cflags_cc', []) 857 858 # Respect environment variables related to build, but target-specific 859 # flags can still override them. 860 if self.toolset == 'target': 861 cflags_c = (os.environ.get('CPPFLAGS', '').split() + 862 os.environ.get('CFLAGS', '').split() + cflags_c) 863 cflags_cc = (os.environ.get('CPPFLAGS', '').split() + 864 os.environ.get('CXXFLAGS', '').split() + cflags_cc) 865 866 defines = config.get('defines', []) + extra_defines 867 self.WriteVariableList(ninja_file, 'defines', 868 [Define(d, self.flavor) for d in defines]) 869 if self.flavor == 'win': 870 self.WriteVariableList(ninja_file, 'asmflags', 871 map(self.ExpandSpecial, asmflags)) 872 self.WriteVariableList(ninja_file, 'rcflags', 873 [QuoteShellArgument(self.ExpandSpecial(f), self.flavor) 874 for f in self.msvs_settings.GetRcflags(config_name, 875 self.GypPathToNinja)]) 876 877 include_dirs = config.get('include_dirs', []) 878 879 env = self.GetToolchainEnv() 880 if self.flavor == 'win': 881 include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs, 882 config_name) 883 self.WriteVariableList(ninja_file, 'includes', 884 [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor) 885 for i in include_dirs]) 886 887 pch_commands = precompiled_header.GetPchBuildCommands(arch) 888 if self.flavor == 'mac': 889 # Most targets use no precompiled headers, so only write these if needed. 890 for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'), 891 ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]: 892 include = precompiled_header.GetInclude(ext, arch) 893 if include: ninja_file.variable(var, include) 894 895 self.WriteVariableList(ninja_file, 'cflags', 896 map(self.ExpandSpecial, cflags)) 897 self.WriteVariableList(ninja_file, 'cflags_c', 898 map(self.ExpandSpecial, cflags_c)) 899 self.WriteVariableList(ninja_file, 'cflags_cc', 900 map(self.ExpandSpecial, cflags_cc)) 901 if self.flavor == 'mac': 902 self.WriteVariableList(ninja_file, 'cflags_objc', 903 map(self.ExpandSpecial, cflags_objc)) 904 self.WriteVariableList(ninja_file, 'cflags_objcc', 905 map(self.ExpandSpecial, cflags_objcc)) 906 ninja_file.newline() 907 outputs = [] 908 has_rc_source = False 909 for source in sources: 910 filename, ext = os.path.splitext(source) 911 ext = ext[1:] 912 obj_ext = self.obj_ext 913 if ext in ('cc', 'cpp', 'cxx'): 914 command = 'cxx' 915 self.uses_cpp = True 916 elif ext == 'c' or (ext == 'S' and self.flavor != 'win'): 917 command = 'cc' 918 elif ext == 's' and self.flavor != 'win': # Doesn't generate .o.d files. 919 command = 'cc_s' 920 elif (self.flavor == 'win' and ext == 'asm' and 921 self.msvs_settings.GetArch(config_name) == 'x86' and 922 not self.msvs_settings.HasExplicitAsmRules(spec)): 923 # Asm files only get auto assembled for x86 (not x64). 924 command = 'asm' 925 # Add the _asm suffix as msvs is capable of handling .cc and 926 # .asm files of the same name without collision. 927 obj_ext = '_asm.obj' 928 elif self.flavor == 'mac' and ext == 'm': 929 command = 'objc' 930 elif self.flavor == 'mac' and ext == 'mm': 931 command = 'objcxx' 932 self.uses_cpp = True 933 elif self.flavor == 'win' and ext == 'rc': 934 command = 'rc' 935 obj_ext = '.res' 936 has_rc_source = True 937 else: 938 # Ignore unhandled extensions. 939 continue 940 input = self.GypPathToNinja(source) 941 output = self.GypPathToUniqueOutput(filename + obj_ext) 942 if arch is not None: 943 output = AddArch(output, arch) 944 implicit = precompiled_header.GetObjDependencies([input], [output], arch) 945 variables = [] 946 if self.flavor == 'win': 947 variables, output, implicit = precompiled_header.GetFlagsModifications( 948 input, output, implicit, command, cflags_c, cflags_cc, 949 self.ExpandSpecial) 950 ninja_file.build(output, command, input, 951 implicit=[gch for _, _, gch in implicit], 952 order_only=predepends, variables=variables) 953 outputs.append(output) 954 955 if has_rc_source: 956 resource_include_dirs = config.get('resource_include_dirs', include_dirs) 957 self.WriteVariableList(ninja_file, 'resource_includes', 958 [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor) 959 for i in resource_include_dirs]) 960 961 self.WritePchTargets(ninja_file, pch_commands) 962 963 ninja_file.newline() 964 return outputs 965 966 def WritePchTargets(self, ninja_file, pch_commands): 967 """Writes ninja rules to compile prefix headers.""" 968 if not pch_commands: 969 return 970 971 for gch, lang_flag, lang, input in pch_commands: 972 var_name = { 973 'c': 'cflags_pch_c', 974 'cc': 'cflags_pch_cc', 975 'm': 'cflags_pch_objc', 976 'mm': 'cflags_pch_objcc', 977 }[lang] 978 979 map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', } 980 cmd = map.get(lang) 981 ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)]) 982 983 def WriteLink(self, spec, config_name, config, link_deps): 984 """Write out a link step. Fills out target.binary. """ 985 if self.flavor != 'mac' or len(self.archs) == 1: 986 return self.WriteLinkForArch( 987 self.ninja, spec, config_name, config, link_deps) 988 else: 989 output = self.ComputeOutput(spec) 990 inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec, 991 config_name, config, link_deps[arch], 992 arch=arch) 993 for arch in self.archs] 994 extra_bindings = [] 995 if not self.is_mac_bundle: 996 self.AppendPostbuildVariable(extra_bindings, spec, output, output) 997 self.ninja.build(output, 'lipo', inputs, variables=extra_bindings) 998 return output 999 1000 def WriteLinkForArch(self, ninja_file, spec, config_name, config, 1001 link_deps, arch=None): 1002 """Write out a link step. Fills out target.binary. """ 1003 command = { 1004 'executable': 'link', 1005 'loadable_module': 'solink_module', 1006 'shared_library': 'solink', 1007 }[spec['type']] 1008 command_suffix = '' 1009 1010 implicit_deps = set() 1011 solibs = set() 1012 1013 if 'dependencies' in spec: 1014 # Two kinds of dependencies: 1015 # - Linkable dependencies (like a .a or a .so): add them to the link line. 1016 # - Non-linkable dependencies (like a rule that generates a file 1017 # and writes a stamp file): add them to implicit_deps 1018 extra_link_deps = set() 1019 for dep in spec['dependencies']: 1020 target = self.target_outputs.get(dep) 1021 if not target: 1022 continue 1023 linkable = target.Linkable() 1024 if linkable: 1025 new_deps = [] 1026 if (self.flavor == 'win' and 1027 target.component_objs and 1028 self.msvs_settings.IsUseLibraryDependencyInputs(config_name)): 1029 new_deps = target.component_objs 1030 elif self.flavor == 'win' and target.import_lib: 1031 new_deps = [target.import_lib] 1032 elif target.UsesToc(self.flavor): 1033 solibs.add(target.binary) 1034 implicit_deps.add(target.binary + '.TOC') 1035 else: 1036 new_deps = [target.binary] 1037 for new_dep in new_deps: 1038 if new_dep not in extra_link_deps: 1039 extra_link_deps.add(new_dep) 1040 link_deps.append(new_dep) 1041 1042 final_output = target.FinalOutput() 1043 if not linkable or final_output != target.binary: 1044 implicit_deps.add(final_output) 1045 1046 extra_bindings = [] 1047 if self.uses_cpp and self.flavor != 'win': 1048 extra_bindings.append(('ld', '$ldxx')) 1049 1050 output = self.ComputeOutput(spec, arch) 1051 if arch is None and not self.is_mac_bundle: 1052 self.AppendPostbuildVariable(extra_bindings, spec, output, output) 1053 1054 is_executable = spec['type'] == 'executable' 1055 # The ldflags config key is not used on mac or win. On those platforms 1056 # linker flags are set via xcode_settings and msvs_settings, respectively. 1057 env_ldflags = os.environ.get('LDFLAGS', '').split() 1058 if self.flavor == 'mac': 1059 ldflags = self.xcode_settings.GetLdflags(config_name, 1060 self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']), 1061 self.GypPathToNinja, arch) 1062 ldflags = env_ldflags + ldflags 1063 elif self.flavor == 'win': 1064 manifest_base_name = self.GypPathToUniqueOutput( 1065 self.ComputeOutputFileName(spec)) 1066 ldflags, intermediate_manifest, manifest_files = \ 1067 self.msvs_settings.GetLdflags(config_name, self.GypPathToNinja, 1068 self.ExpandSpecial, manifest_base_name, 1069 output, is_executable, 1070 self.toplevel_build) 1071 ldflags = env_ldflags + ldflags 1072 self.WriteVariableList(ninja_file, 'manifests', manifest_files) 1073 implicit_deps = implicit_deps.union(manifest_files) 1074 if intermediate_manifest: 1075 self.WriteVariableList( 1076 ninja_file, 'intermediatemanifest', [intermediate_manifest]) 1077 command_suffix = _GetWinLinkRuleNameSuffix( 1078 self.msvs_settings.IsEmbedManifest(config_name)) 1079 def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja) 1080 if def_file: 1081 implicit_deps.add(def_file) 1082 else: 1083 # Respect environment variables related to build, but target-specific 1084 # flags can still override them. 1085 ldflags = env_ldflags + config.get('ldflags', []) 1086 if is_executable and len(solibs): 1087 rpath = 'lib/' 1088 if self.toolset != 'target': 1089 rpath += self.toolset 1090 ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath) 1091 ldflags.append('-Wl,-rpath-link=%s' % rpath) 1092 self.WriteVariableList(ninja_file, 'ldflags', 1093 gyp.common.uniquer(map(self.ExpandSpecial, ldflags))) 1094 1095 library_dirs = config.get('library_dirs', []) 1096 if self.flavor == 'win': 1097 library_dirs = [self.msvs_settings.ConvertVSMacros(l, config_name) 1098 for l in library_dirs] 1099 library_dirs = ['/LIBPATH:' + QuoteShellArgument(self.GypPathToNinja(l), 1100 self.flavor) 1101 for l in library_dirs] 1102 else: 1103 library_dirs = [QuoteShellArgument('-L' + self.GypPathToNinja(l), 1104 self.flavor) 1105 for l in library_dirs] 1106 1107 libraries = gyp.common.uniquer(map(self.ExpandSpecial, 1108 spec.get('libraries', []))) 1109 if self.flavor == 'mac': 1110 libraries = self.xcode_settings.AdjustLibraries(libraries, config_name) 1111 elif self.flavor == 'win': 1112 libraries = self.msvs_settings.AdjustLibraries(libraries) 1113 1114 self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries) 1115 1116 linked_binary = output 1117 1118 if command in ('solink', 'solink_module'): 1119 extra_bindings.append(('soname', os.path.split(output)[1])) 1120 extra_bindings.append(('lib', 1121 gyp.common.EncodePOSIXShellArgument(output))) 1122 if self.flavor != 'win': 1123 link_file_list = output 1124 if self.is_mac_bundle: 1125 # 'Dependency Framework.framework/Versions/A/Dependency Framework' -> 1126 # 'Dependency Framework.framework.rsp' 1127 link_file_list = self.xcode_settings.GetWrapperName() 1128 if arch: 1129 link_file_list += '.' + arch 1130 link_file_list += '.rsp' 1131 # If an rspfile contains spaces, ninja surrounds the filename with 1132 # quotes around it and then passes it to open(), creating a file with 1133 # quotes in its name (and when looking for the rsp file, the name 1134 # makes it through bash which strips the quotes) :-/ 1135 link_file_list = link_file_list.replace(' ', '_') 1136 extra_bindings.append( 1137 ('link_file_list', 1138 gyp.common.EncodePOSIXShellArgument(link_file_list))) 1139 if self.flavor == 'win': 1140 extra_bindings.append(('binary', output)) 1141 if '/NOENTRY' not in ldflags: 1142 self.target.import_lib = output + '.lib' 1143 extra_bindings.append(('implibflag', 1144 '/IMPLIB:%s' % self.target.import_lib)) 1145 pdbname = self.msvs_settings.GetPDBName( 1146 config_name, self.ExpandSpecial, output + '.pdb') 1147 output = [output, self.target.import_lib] 1148 if pdbname: 1149 output.append(pdbname) 1150 elif not self.is_mac_bundle: 1151 output = [output, output + '.TOC'] 1152 else: 1153 command = command + '_notoc' 1154 elif self.flavor == 'win': 1155 extra_bindings.append(('binary', output)) 1156 pdbname = self.msvs_settings.GetPDBName( 1157 config_name, self.ExpandSpecial, output + '.pdb') 1158 if pdbname: 1159 output = [output, pdbname] 1160 1161 1162 if len(solibs): 1163 extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs))) 1164 1165 ninja_file.build(output, command + command_suffix, link_deps, 1166 implicit=list(implicit_deps), 1167 variables=extra_bindings) 1168 return linked_binary 1169 1170 def WriteTarget(self, spec, config_name, config, link_deps, compile_deps): 1171 extra_link_deps = any(self.target_outputs.get(dep).Linkable() 1172 for dep in spec.get('dependencies', []) 1173 if dep in self.target_outputs) 1174 if spec['type'] == 'none' or (not link_deps and not extra_link_deps): 1175 # TODO(evan): don't call this function for 'none' target types, as 1176 # it doesn't do anything, and we fake out a 'binary' with a stamp file. 1177 self.target.binary = compile_deps 1178 self.target.type = 'none' 1179 elif spec['type'] == 'static_library': 1180 self.target.binary = self.ComputeOutput(spec) 1181 if (self.flavor not in ('mac', 'openbsd', 'win') and not 1182 self.is_standalone_static_library): 1183 self.ninja.build(self.target.binary, 'alink_thin', link_deps, 1184 order_only=compile_deps) 1185 else: 1186 variables = [] 1187 if self.xcode_settings: 1188 libtool_flags = self.xcode_settings.GetLibtoolflags(config_name) 1189 if libtool_flags: 1190 variables.append(('libtool_flags', libtool_flags)) 1191 if self.msvs_settings: 1192 libflags = self.msvs_settings.GetLibFlags(config_name, 1193 self.GypPathToNinja) 1194 variables.append(('libflags', libflags)) 1195 1196 if self.flavor != 'mac' or len(self.archs) == 1: 1197 self.AppendPostbuildVariable(variables, spec, 1198 self.target.binary, self.target.binary) 1199 self.ninja.build(self.target.binary, 'alink', link_deps, 1200 order_only=compile_deps, variables=variables) 1201 else: 1202 inputs = [] 1203 for arch in self.archs: 1204 output = self.ComputeOutput(spec, arch) 1205 self.arch_subninjas[arch].build(output, 'alink', link_deps[arch], 1206 order_only=compile_deps, 1207 variables=variables) 1208 inputs.append(output) 1209 # TODO: It's not clear if libtool_flags should be passed to the alink 1210 # call that combines single-arch .a files into a fat .a file. 1211 self.AppendPostbuildVariable(variables, spec, 1212 self.target.binary, self.target.binary) 1213 self.ninja.build(self.target.binary, 'alink', inputs, 1214 # FIXME: test proving order_only=compile_deps isn't 1215 # needed. 1216 variables=variables) 1217 else: 1218 self.target.binary = self.WriteLink(spec, config_name, config, link_deps) 1219 return self.target.binary 1220 1221 def WriteMacBundle(self, spec, mac_bundle_depends, is_empty): 1222 assert self.is_mac_bundle 1223 package_framework = spec['type'] in ('shared_library', 'loadable_module') 1224 output = self.ComputeMacBundleOutput() 1225 if is_empty: 1226 output += '.stamp' 1227 variables = [] 1228 self.AppendPostbuildVariable(variables, spec, output, self.target.binary, 1229 is_command_start=not package_framework) 1230 if package_framework and not is_empty: 1231 variables.append(('version', self.xcode_settings.GetFrameworkVersion())) 1232 self.ninja.build(output, 'package_framework', mac_bundle_depends, 1233 variables=variables) 1234 else: 1235 self.ninja.build(output, 'stamp', mac_bundle_depends, 1236 variables=variables) 1237 self.target.bundle = output 1238 return output 1239 1240 def GetToolchainEnv(self, additional_settings=None): 1241 """Returns the variables toolchain would set for build steps.""" 1242 env = self.GetSortedXcodeEnv(additional_settings=additional_settings) 1243 if self.flavor == 'win': 1244 env = self.GetMsvsToolchainEnv( 1245 additional_settings=additional_settings) 1246 return env 1247 1248 def GetMsvsToolchainEnv(self, additional_settings=None): 1249 """Returns the variables Visual Studio would set for build steps.""" 1250 return self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR', 1251 config=self.config_name) 1252 1253 def GetSortedXcodeEnv(self, additional_settings=None): 1254 """Returns the variables Xcode would set for build steps.""" 1255 assert self.abs_build_dir 1256 abs_build_dir = self.abs_build_dir 1257 return gyp.xcode_emulation.GetSortedXcodeEnv( 1258 self.xcode_settings, abs_build_dir, 1259 os.path.join(abs_build_dir, self.build_to_base), self.config_name, 1260 additional_settings) 1261 1262 def GetSortedXcodePostbuildEnv(self): 1263 """Returns the variables Xcode would set for postbuild steps.""" 1264 postbuild_settings = {} 1265 # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack. 1266 # TODO(thakis): It would be nice to have some general mechanism instead. 1267 strip_save_file = self.xcode_settings.GetPerTargetSetting( 1268 'CHROMIUM_STRIP_SAVE_FILE') 1269 if strip_save_file: 1270 postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file 1271 return self.GetSortedXcodeEnv(additional_settings=postbuild_settings) 1272 1273 def AppendPostbuildVariable(self, variables, spec, output, binary, 1274 is_command_start=False): 1275 """Adds a 'postbuild' variable if there is a postbuild for |output|.""" 1276 postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start) 1277 if postbuild: 1278 variables.append(('postbuilds', postbuild)) 1279 1280 def GetPostbuildCommand(self, spec, output, output_binary, is_command_start): 1281 """Returns a shell command that runs all the postbuilds, and removes 1282 |output| if any of them fails. If |is_command_start| is False, then the 1283 returned string will start with ' && '.""" 1284 if not self.xcode_settings or spec['type'] == 'none' or not output: 1285 return '' 1286 output = QuoteShellArgument(output, self.flavor) 1287 postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True) 1288 if output_binary is not None: 1289 postbuilds = self.xcode_settings.AddImplicitPostbuilds( 1290 self.config_name, 1291 os.path.normpath(os.path.join(self.base_to_build, output)), 1292 QuoteShellArgument( 1293 os.path.normpath(os.path.join(self.base_to_build, output_binary)), 1294 self.flavor), 1295 postbuilds, quiet=True) 1296 1297 if not postbuilds: 1298 return '' 1299 # Postbuilds expect to be run in the gyp file's directory, so insert an 1300 # implicit postbuild to cd to there. 1301 postbuilds.insert(0, gyp.common.EncodePOSIXShellList( 1302 ['cd', self.build_to_base])) 1303 env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv()) 1304 # G will be non-null if any postbuild fails. Run all postbuilds in a 1305 # subshell. 1306 commands = env + ' (' + \ 1307 ' && '.join([ninja_syntax.escape(command) for command in postbuilds]) 1308 command_string = (commands + '); G=$$?; ' 1309 # Remove the final output if any postbuild failed. 1310 '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)') 1311 if is_command_start: 1312 return '(' + command_string + ' && ' 1313 else: 1314 return '$ && (' + command_string 1315 1316 def ComputeExportEnvString(self, env): 1317 """Given an environment, returns a string looking like 1318 'export FOO=foo; export BAR="${FOO} bar;' 1319 that exports |env| to the shell.""" 1320 export_str = [] 1321 for k, v in env: 1322 export_str.append('export %s=%s;' % 1323 (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v)))) 1324 return ' '.join(export_str) 1325 1326 def ComputeMacBundleOutput(self): 1327 """Return the 'output' (full output path) to a bundle output directory.""" 1328 assert self.is_mac_bundle 1329 path = generator_default_variables['PRODUCT_DIR'] 1330 return self.ExpandSpecial( 1331 os.path.join(path, self.xcode_settings.GetWrapperName())) 1332 1333 def ComputeOutputFileName(self, spec, type=None): 1334 """Compute the filename of the final output for the current target.""" 1335 if not type: 1336 type = spec['type'] 1337 1338 default_variables = copy.copy(generator_default_variables) 1339 CalculateVariables(default_variables, {'flavor': self.flavor}) 1340 1341 # Compute filename prefix: the product prefix, or a default for 1342 # the product type. 1343 DEFAULT_PREFIX = { 1344 'loadable_module': default_variables['SHARED_LIB_PREFIX'], 1345 'shared_library': default_variables['SHARED_LIB_PREFIX'], 1346 'static_library': default_variables['STATIC_LIB_PREFIX'], 1347 'executable': default_variables['EXECUTABLE_PREFIX'], 1348 } 1349 prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, '')) 1350 1351 # Compute filename extension: the product extension, or a default 1352 # for the product type. 1353 DEFAULT_EXTENSION = { 1354 'loadable_module': default_variables['SHARED_LIB_SUFFIX'], 1355 'shared_library': default_variables['SHARED_LIB_SUFFIX'], 1356 'static_library': default_variables['STATIC_LIB_SUFFIX'], 1357 'executable': default_variables['EXECUTABLE_SUFFIX'], 1358 } 1359 extension = spec.get('product_extension') 1360 if extension: 1361 extension = '.' + extension 1362 else: 1363 extension = DEFAULT_EXTENSION.get(type, '') 1364 1365 if 'product_name' in spec: 1366 # If we were given an explicit name, use that. 1367 target = spec['product_name'] 1368 else: 1369 # Otherwise, derive a name from the target name. 1370 target = spec['target_name'] 1371 if prefix == 'lib': 1372 # Snip out an extra 'lib' from libs if appropriate. 1373 target = StripPrefix(target, 'lib') 1374 1375 if type in ('static_library', 'loadable_module', 'shared_library', 1376 'executable'): 1377 return '%s%s%s' % (prefix, target, extension) 1378 elif type == 'none': 1379 return '%s.stamp' % target 1380 else: 1381 raise Exception('Unhandled output type %s' % type) 1382 1383 def ComputeOutput(self, spec, arch=None): 1384 """Compute the path for the final output of the spec.""" 1385 type = spec['type'] 1386 1387 if self.flavor == 'win': 1388 override = self.msvs_settings.GetOutputName(self.config_name, 1389 self.ExpandSpecial) 1390 if override: 1391 return override 1392 1393 if arch is None and self.flavor == 'mac' and type in ( 1394 'static_library', 'executable', 'shared_library', 'loadable_module'): 1395 filename = self.xcode_settings.GetExecutablePath() 1396 else: 1397 filename = self.ComputeOutputFileName(spec, type) 1398 1399 if arch is None and 'product_dir' in spec: 1400 path = os.path.join(spec['product_dir'], filename) 1401 return self.ExpandSpecial(path) 1402 1403 # Some products go into the output root, libraries go into shared library 1404 # dir, and everything else goes into the normal place. 1405 type_in_output_root = ['executable', 'loadable_module'] 1406 if self.flavor == 'mac' and self.toolset == 'target': 1407 type_in_output_root += ['shared_library', 'static_library'] 1408 elif self.flavor == 'win' and self.toolset == 'target': 1409 type_in_output_root += ['shared_library'] 1410 1411 if arch is not None: 1412 # Make sure partial executables don't end up in a bundle or the regular 1413 # output directory. 1414 archdir = 'arch' 1415 if self.toolset != 'target': 1416 archdir = os.path.join('arch', '%s' % self.toolset) 1417 return os.path.join(archdir, AddArch(filename, arch)) 1418 elif type in type_in_output_root or self.is_standalone_static_library: 1419 return filename 1420 elif type == 'shared_library': 1421 libdir = 'lib' 1422 if self.toolset != 'target': 1423 libdir = os.path.join('lib', '%s' % self.toolset) 1424 return os.path.join(libdir, filename) 1425 else: 1426 return self.GypPathToUniqueOutput(filename, qualified=False) 1427 1428 def WriteVariableList(self, ninja_file, var, values): 1429 assert not isinstance(values, str) 1430 if values is None: 1431 values = [] 1432 ninja_file.variable(var, ' '.join(values)) 1433 1434 def WriteNewNinjaRule(self, name, args, description, is_cygwin, env, pool): 1435 """Write out a new ninja "rule" statement for a given command. 1436 1437 Returns the name of the new rule, and a copy of |args| with variables 1438 expanded.""" 1439 1440 if self.flavor == 'win': 1441 args = [self.msvs_settings.ConvertVSMacros( 1442 arg, self.base_to_build, config=self.config_name) 1443 for arg in args] 1444 description = self.msvs_settings.ConvertVSMacros( 1445 description, config=self.config_name) 1446 elif self.flavor == 'mac': 1447 # |env| is an empty list on non-mac. 1448 args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args] 1449 description = gyp.xcode_emulation.ExpandEnvVars(description, env) 1450 1451 # TODO: we shouldn't need to qualify names; we do it because 1452 # currently the ninja rule namespace is global, but it really 1453 # should be scoped to the subninja. 1454 rule_name = self.name 1455 if self.toolset == 'target': 1456 rule_name += '.' + self.toolset 1457 rule_name += '.' + name 1458 rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name) 1459 1460 # Remove variable references, but not if they refer to the magic rule 1461 # variables. This is not quite right, as it also protects these for 1462 # actions, not just for rules where they are valid. Good enough. 1463 protect = [ '${root}', '${dirname}', '${source}', '${ext}', '${name}' ] 1464 protect = '(?!' + '|'.join(map(re.escape, protect)) + ')' 1465 description = re.sub(protect + r'\$', '_', description) 1466 1467 # gyp dictates that commands are run from the base directory. 1468 # cd into the directory before running, and adjust paths in 1469 # the arguments to point to the proper locations. 1470 rspfile = None 1471 rspfile_content = None 1472 args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args] 1473 if self.flavor == 'win': 1474 rspfile = rule_name + '.$unique_name.rsp' 1475 # The cygwin case handles this inside the bash sub-shell. 1476 run_in = '' if is_cygwin else ' ' + self.build_to_base 1477 if is_cygwin: 1478 rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine( 1479 args, self.build_to_base) 1480 else: 1481 rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args) 1482 command = ('%s gyp-win-tool action-wrapper $arch ' % sys.executable + 1483 rspfile + run_in) 1484 else: 1485 env = self.ComputeExportEnvString(env) 1486 command = gyp.common.EncodePOSIXShellList(args) 1487 command = 'cd %s; ' % self.build_to_base + env + command 1488 1489 # GYP rules/actions express being no-ops by not touching their outputs. 1490 # Avoid executing downstream dependencies in this case by specifying 1491 # restat=1 to ninja. 1492 self.ninja.rule(rule_name, command, description, restat=True, pool=pool, 1493 rspfile=rspfile, rspfile_content=rspfile_content) 1494 self.ninja.newline() 1495 1496 return rule_name, args 1497 1498 1499def CalculateVariables(default_variables, params): 1500 """Calculate additional variables for use in the build (called by gyp).""" 1501 global generator_additional_non_configuration_keys 1502 global generator_additional_path_sections 1503 flavor = gyp.common.GetFlavor(params) 1504 if flavor == 'mac': 1505 default_variables.setdefault('OS', 'mac') 1506 default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') 1507 default_variables.setdefault('SHARED_LIB_DIR', 1508 generator_default_variables['PRODUCT_DIR']) 1509 default_variables.setdefault('LIB_DIR', 1510 generator_default_variables['PRODUCT_DIR']) 1511 1512 # Copy additional generator configuration data from Xcode, which is shared 1513 # by the Mac Ninja generator. 1514 import gyp.generator.xcode as xcode_generator 1515 generator_additional_non_configuration_keys = getattr(xcode_generator, 1516 'generator_additional_non_configuration_keys', []) 1517 generator_additional_path_sections = getattr(xcode_generator, 1518 'generator_additional_path_sections', []) 1519 global generator_extra_sources_for_rules 1520 generator_extra_sources_for_rules = getattr(xcode_generator, 1521 'generator_extra_sources_for_rules', []) 1522 elif flavor == 'win': 1523 default_variables.setdefault('OS', 'win') 1524 default_variables['EXECUTABLE_SUFFIX'] = '.exe' 1525 default_variables['STATIC_LIB_PREFIX'] = '' 1526 default_variables['STATIC_LIB_SUFFIX'] = '.lib' 1527 default_variables['SHARED_LIB_PREFIX'] = '' 1528 default_variables['SHARED_LIB_SUFFIX'] = '.dll' 1529 1530 # Copy additional generator configuration data from VS, which is shared 1531 # by the Windows Ninja generator. 1532 import gyp.generator.msvs as msvs_generator 1533 generator_additional_non_configuration_keys = getattr(msvs_generator, 1534 'generator_additional_non_configuration_keys', []) 1535 generator_additional_path_sections = getattr(msvs_generator, 1536 'generator_additional_path_sections', []) 1537 1538 gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) 1539 else: 1540 operating_system = flavor 1541 if flavor == 'android': 1542 operating_system = 'linux' # Keep this legacy behavior for now. 1543 default_variables.setdefault('OS', operating_system) 1544 default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') 1545 default_variables.setdefault('SHARED_LIB_DIR', 1546 os.path.join('$!PRODUCT_DIR', 'lib')) 1547 default_variables.setdefault('LIB_DIR', 1548 os.path.join('$!PRODUCT_DIR', 'obj')) 1549 1550def ComputeOutputDir(params): 1551 """Returns the path from the toplevel_dir to the build output directory.""" 1552 # generator_dir: relative path from pwd to where make puts build files. 1553 # Makes migrating from make to ninja easier, ninja doesn't put anything here. 1554 generator_dir = os.path.relpath(params['options'].generator_output or '.') 1555 1556 # output_dir: relative path from generator_dir to the build directory. 1557 output_dir = params.get('generator_flags', {}).get('output_dir', 'out') 1558 1559 # Relative path from source root to our output files. e.g. "out" 1560 return os.path.normpath(os.path.join(generator_dir, output_dir)) 1561 1562 1563def CalculateGeneratorInputInfo(params): 1564 """Called by __init__ to initialize generator values based on params.""" 1565 # E.g. "out/gypfiles" 1566 toplevel = params['options'].toplevel_dir 1567 qualified_out_dir = os.path.normpath(os.path.join( 1568 toplevel, ComputeOutputDir(params), 'gypfiles')) 1569 1570 global generator_filelist_paths 1571 generator_filelist_paths = { 1572 'toplevel': toplevel, 1573 'qualified_out_dir': qualified_out_dir, 1574 } 1575 1576 1577def OpenOutput(path, mode='w'): 1578 """Open |path| for writing, creating directories if necessary.""" 1579 gyp.common.EnsureDirExists(path) 1580 return open(path, mode) 1581 1582 1583def CommandWithWrapper(cmd, wrappers, prog): 1584 wrapper = wrappers.get(cmd, '') 1585 if wrapper: 1586 return wrapper + ' ' + prog 1587 return prog 1588 1589 1590def GetDefaultConcurrentLinks(): 1591 """Returns a best-guess for a number of concurrent links.""" 1592 pool_size = int(os.getenv('GYP_LINK_CONCURRENCY', 0)) 1593 if pool_size: 1594 return pool_size 1595 1596 if sys.platform in ('win32', 'cygwin'): 1597 import ctypes 1598 1599 class MEMORYSTATUSEX(ctypes.Structure): 1600 _fields_ = [ 1601 ("dwLength", ctypes.c_ulong), 1602 ("dwMemoryLoad", ctypes.c_ulong), 1603 ("ullTotalPhys", ctypes.c_ulonglong), 1604 ("ullAvailPhys", ctypes.c_ulonglong), 1605 ("ullTotalPageFile", ctypes.c_ulonglong), 1606 ("ullAvailPageFile", ctypes.c_ulonglong), 1607 ("ullTotalVirtual", ctypes.c_ulonglong), 1608 ("ullAvailVirtual", ctypes.c_ulonglong), 1609 ("sullAvailExtendedVirtual", ctypes.c_ulonglong), 1610 ] 1611 1612 stat = MEMORYSTATUSEX() 1613 stat.dwLength = ctypes.sizeof(stat) 1614 ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)) 1615 1616 mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30))) # total / 4GB 1617 hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32))) 1618 return min(mem_limit, hard_cap) 1619 elif sys.platform.startswith('linux'): 1620 if os.path.exists("/proc/meminfo"): 1621 with open("/proc/meminfo") as meminfo: 1622 memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB') 1623 for line in meminfo: 1624 match = memtotal_re.match(line) 1625 if not match: 1626 continue 1627 # Allow 8Gb per link on Linux because Gold is quite memory hungry 1628 return max(1, int(match.group(1)) / (8 * (2 ** 20))) 1629 return 1 1630 elif sys.platform == 'darwin': 1631 try: 1632 avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize'])) 1633 # A static library debug build of Chromium's unit_tests takes ~2.7GB, so 1634 # 4GB per ld process allows for some more bloat. 1635 return max(1, avail_bytes / (4 * (2 ** 30))) # total / 4GB 1636 except: 1637 return 1 1638 else: 1639 # TODO(scottmg): Implement this for other platforms. 1640 return 1 1641 1642 1643def _GetWinLinkRuleNameSuffix(embed_manifest): 1644 """Returns the suffix used to select an appropriate linking rule depending on 1645 whether the manifest embedding is enabled.""" 1646 return '_embed' if embed_manifest else '' 1647 1648 1649def _AddWinLinkRules(master_ninja, embed_manifest): 1650 """Adds link rules for Windows platform to |master_ninja|.""" 1651 def FullLinkCommand(ldcmd, out, binary_type): 1652 resource_name = { 1653 'exe': '1', 1654 'dll': '2', 1655 }[binary_type] 1656 return '%(python)s gyp-win-tool link-with-manifests $arch %(embed)s ' \ 1657 '%(out)s "%(ldcmd)s" %(resname)s $mt $rc "$intermediatemanifest" ' \ 1658 '$manifests' % { 1659 'python': sys.executable, 1660 'out': out, 1661 'ldcmd': ldcmd, 1662 'resname': resource_name, 1663 'embed': embed_manifest } 1664 rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest) 1665 use_separate_mspdbsrv = ( 1666 int(os.environ.get('GYP_USE_SEPARATE_MSPDBSRV', '0')) != 0) 1667 dlldesc = 'LINK%s(DLL) $binary' % rule_name_suffix.upper() 1668 dllcmd = ('%s gyp-win-tool link-wrapper $arch %s ' 1669 '$ld /nologo $implibflag /DLL /OUT:$binary ' 1670 '@$binary.rsp' % (sys.executable, use_separate_mspdbsrv)) 1671 dllcmd = FullLinkCommand(dllcmd, '$binary', 'dll') 1672 master_ninja.rule('solink' + rule_name_suffix, 1673 description=dlldesc, command=dllcmd, 1674 rspfile='$binary.rsp', 1675 rspfile_content='$libs $in_newline $ldflags', 1676 restat=True, 1677 pool='link_pool') 1678 master_ninja.rule('solink_module' + rule_name_suffix, 1679 description=dlldesc, command=dllcmd, 1680 rspfile='$binary.rsp', 1681 rspfile_content='$libs $in_newline $ldflags', 1682 restat=True, 1683 pool='link_pool') 1684 # Note that ldflags goes at the end so that it has the option of 1685 # overriding default settings earlier in the command line. 1686 exe_cmd = ('%s gyp-win-tool link-wrapper $arch %s ' 1687 '$ld /nologo /OUT:$binary @$binary.rsp' % 1688 (sys.executable, use_separate_mspdbsrv)) 1689 exe_cmd = FullLinkCommand(exe_cmd, '$binary', 'exe') 1690 master_ninja.rule('link' + rule_name_suffix, 1691 description='LINK%s $binary' % rule_name_suffix.upper(), 1692 command=exe_cmd, 1693 rspfile='$binary.rsp', 1694 rspfile_content='$in_newline $libs $ldflags', 1695 pool='link_pool') 1696 1697 1698def GenerateOutputForConfig(target_list, target_dicts, data, params, 1699 config_name): 1700 options = params['options'] 1701 flavor = gyp.common.GetFlavor(params) 1702 generator_flags = params.get('generator_flags', {}) 1703 1704 # build_dir: relative path from source root to our output files. 1705 # e.g. "out/Debug" 1706 build_dir = os.path.normpath( 1707 os.path.join(ComputeOutputDir(params), config_name)) 1708 1709 toplevel_build = os.path.join(options.toplevel_dir, build_dir) 1710 1711 master_ninja_file = OpenOutput(os.path.join(toplevel_build, 'build.ninja')) 1712 master_ninja = ninja_syntax.Writer(master_ninja_file, width=120) 1713 1714 # Put build-time support tools in out/{config_name}. 1715 gyp.common.CopyTool(flavor, toplevel_build) 1716 1717 # Grab make settings for CC/CXX. 1718 # The rules are 1719 # - The priority from low to high is gcc/g++, the 'make_global_settings' in 1720 # gyp, the environment variable. 1721 # - If there is no 'make_global_settings' for CC.host/CXX.host or 1722 # 'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set 1723 # to cc/cxx. 1724 if flavor == 'win': 1725 ar = 'lib.exe' 1726 # cc and cxx must be set to the correct architecture by overriding with one 1727 # of cl_x86 or cl_x64 below. 1728 cc = 'UNSET' 1729 cxx = 'UNSET' 1730 ld = 'link.exe' 1731 ld_host = '$ld' 1732 else: 1733 ar = 'ar' 1734 cc = 'cc' 1735 cxx = 'c++' 1736 ld = '$cc' 1737 ldxx = '$cxx' 1738 ld_host = '$cc_host' 1739 ldxx_host = '$cxx_host' 1740 1741 ar_host = 'ar' 1742 cc_host = None 1743 cxx_host = None 1744 cc_host_global_setting = None 1745 cxx_host_global_setting = None 1746 clang_cl = None 1747 nm = 'nm' 1748 nm_host = 'nm' 1749 readelf = 'readelf' 1750 readelf_host = 'readelf' 1751 1752 build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0]) 1753 make_global_settings = data[build_file].get('make_global_settings', []) 1754 build_to_root = gyp.common.InvertRelativePath(build_dir, 1755 options.toplevel_dir) 1756 wrappers = {} 1757 for key, value in make_global_settings: 1758 if key == 'AR': 1759 ar = os.path.join(build_to_root, value) 1760 if key == 'AR.host': 1761 ar_host = os.path.join(build_to_root, value) 1762 if key == 'CC': 1763 cc = os.path.join(build_to_root, value) 1764 if cc.endswith('clang-cl'): 1765 clang_cl = cc 1766 if key == 'CXX': 1767 cxx = os.path.join(build_to_root, value) 1768 if key == 'CC.host': 1769 cc_host = os.path.join(build_to_root, value) 1770 cc_host_global_setting = value 1771 if key == 'CXX.host': 1772 cxx_host = os.path.join(build_to_root, value) 1773 cxx_host_global_setting = value 1774 if key == 'LD': 1775 ld = os.path.join(build_to_root, value) 1776 if key == 'LD.host': 1777 ld_host = os.path.join(build_to_root, value) 1778 if key == 'NM': 1779 nm = os.path.join(build_to_root, value) 1780 if key == 'NM.host': 1781 nm_host = os.path.join(build_to_root, value) 1782 if key == 'READELF': 1783 readelf = os.path.join(build_to_root, value) 1784 if key == 'READELF.host': 1785 readelf_host = os.path.join(build_to_root, value) 1786 if key.endswith('_wrapper'): 1787 wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value) 1788 1789 # Support wrappers from environment variables too. 1790 for key, value in os.environ.iteritems(): 1791 if key.lower().endswith('_wrapper'): 1792 key_prefix = key[:-len('_wrapper')] 1793 key_prefix = re.sub(r'\.HOST$', '.host', key_prefix) 1794 wrappers[key_prefix] = os.path.join(build_to_root, value) 1795 1796 if flavor == 'win': 1797 configs = [target_dicts[qualified_target]['configurations'][config_name] 1798 for qualified_target in target_list] 1799 shared_system_includes = None 1800 if not generator_flags.get('ninja_use_custom_environment_files', 0): 1801 shared_system_includes = \ 1802 gyp.msvs_emulation.ExtractSharedMSVSSystemIncludes( 1803 configs, generator_flags) 1804 cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles( 1805 toplevel_build, generator_flags, shared_system_includes, OpenOutput) 1806 for arch, path in cl_paths.iteritems(): 1807 if clang_cl: 1808 # If we have selected clang-cl, use that instead. 1809 path = clang_cl 1810 command = CommandWithWrapper('CC', wrappers, 1811 QuoteShellArgument(path, 'win')) 1812 if clang_cl: 1813 # Use clang-cl to cross-compile for x86 or x86_64. 1814 command += (' -m32' if arch == 'x86' else ' -m64') 1815 master_ninja.variable('cl_' + arch, command) 1816 1817 cc = GetEnvironFallback(['CC_target', 'CC'], cc) 1818 master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc)) 1819 cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx) 1820 master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx)) 1821 1822 if flavor == 'win': 1823 master_ninja.variable('ld', ld) 1824 master_ninja.variable('idl', 'midl.exe') 1825 master_ninja.variable('ar', ar) 1826 master_ninja.variable('rc', 'rc.exe') 1827 master_ninja.variable('asm', 'ml.exe') 1828 master_ninja.variable('mt', 'mt.exe') 1829 else: 1830 master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld)) 1831 master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx)) 1832 master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], ar)) 1833 if flavor != 'mac': 1834 # Mac does not use readelf/nm for .TOC generation, so avoiding polluting 1835 # the master ninja with extra unused variables. 1836 master_ninja.variable( 1837 'nm', GetEnvironFallback(['NM_target', 'NM'], nm)) 1838 master_ninja.variable( 1839 'readelf', GetEnvironFallback(['READELF_target', 'READELF'], readelf)) 1840 1841 if generator_supports_multiple_toolsets: 1842 if not cc_host: 1843 cc_host = cc 1844 if not cxx_host: 1845 cxx_host = cxx 1846 1847 master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], ar_host)) 1848 master_ninja.variable('nm_host', GetEnvironFallback(['NM_host'], nm_host)) 1849 master_ninja.variable('readelf_host', 1850 GetEnvironFallback(['READELF_host'], readelf_host)) 1851 cc_host = GetEnvironFallback(['CC_host'], cc_host) 1852 cxx_host = GetEnvironFallback(['CXX_host'], cxx_host) 1853 1854 # The environment variable could be used in 'make_global_settings', like 1855 # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here. 1856 if '$(CC)' in cc_host and cc_host_global_setting: 1857 cc_host = cc_host_global_setting.replace('$(CC)', cc) 1858 if '$(CXX)' in cxx_host and cxx_host_global_setting: 1859 cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx) 1860 master_ninja.variable('cc_host', 1861 CommandWithWrapper('CC.host', wrappers, cc_host)) 1862 master_ninja.variable('cxx_host', 1863 CommandWithWrapper('CXX.host', wrappers, cxx_host)) 1864 if flavor == 'win': 1865 master_ninja.variable('ld_host', ld_host) 1866 else: 1867 master_ninja.variable('ld_host', CommandWithWrapper( 1868 'LINK', wrappers, ld_host)) 1869 master_ninja.variable('ldxx_host', CommandWithWrapper( 1870 'LINK', wrappers, ldxx_host)) 1871 1872 master_ninja.newline() 1873 1874 master_ninja.pool('link_pool', depth=GetDefaultConcurrentLinks()) 1875 master_ninja.newline() 1876 1877 deps = 'msvc' if flavor == 'win' else 'gcc' 1878 1879 if flavor != 'win': 1880 master_ninja.rule( 1881 'cc', 1882 description='CC $out', 1883 command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c ' 1884 '$cflags_pch_c -c $in -o $out'), 1885 depfile='$out.d', 1886 deps=deps) 1887 master_ninja.rule( 1888 'cc_s', 1889 description='CC $out', 1890 command=('$cc $defines $includes $cflags $cflags_c ' 1891 '$cflags_pch_c -c $in -o $out')) 1892 master_ninja.rule( 1893 'cxx', 1894 description='CXX $out', 1895 command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc ' 1896 '$cflags_pch_cc -c $in -o $out'), 1897 depfile='$out.d', 1898 deps=deps) 1899 else: 1900 # TODO(scottmg) Separate pdb names is a test to see if it works around 1901 # http://crbug.com/142362. It seems there's a race between the creation of 1902 # the .pdb by the precompiled header step for .cc and the compilation of 1903 # .c files. This should be handled by mspdbsrv, but rarely errors out with 1904 # c1xx : fatal error C1033: cannot open program database 1905 # By making the rules target separate pdb files this might be avoided. 1906 cc_command = ('ninja -t msvc -e $arch ' + 1907 '-- ' 1908 '$cc /nologo /showIncludes /FC ' 1909 '@$out.rsp /c $in /Fo$out /Fd$pdbname_c ') 1910 cxx_command = ('ninja -t msvc -e $arch ' + 1911 '-- ' 1912 '$cxx /nologo /showIncludes /FC ' 1913 '@$out.rsp /c $in /Fo$out /Fd$pdbname_cc ') 1914 master_ninja.rule( 1915 'cc', 1916 description='CC $out', 1917 command=cc_command, 1918 rspfile='$out.rsp', 1919 rspfile_content='$defines $includes $cflags $cflags_c', 1920 deps=deps) 1921 master_ninja.rule( 1922 'cxx', 1923 description='CXX $out', 1924 command=cxx_command, 1925 rspfile='$out.rsp', 1926 rspfile_content='$defines $includes $cflags $cflags_cc', 1927 deps=deps) 1928 master_ninja.rule( 1929 'idl', 1930 description='IDL $in', 1931 command=('%s gyp-win-tool midl-wrapper $arch $outdir ' 1932 '$tlb $h $dlldata $iid $proxy $in ' 1933 '$idlflags' % sys.executable)) 1934 master_ninja.rule( 1935 'rc', 1936 description='RC $in', 1937 # Note: $in must be last otherwise rc.exe complains. 1938 command=('%s gyp-win-tool rc-wrapper ' 1939 '$arch $rc $defines $resource_includes $rcflags /fo$out $in' % 1940 sys.executable)) 1941 master_ninja.rule( 1942 'asm', 1943 description='ASM $out', 1944 command=('%s gyp-win-tool asm-wrapper ' 1945 '$arch $asm $defines $includes $asmflags /c /Fo $out $in' % 1946 sys.executable)) 1947 1948 if flavor != 'mac' and flavor != 'win': 1949 master_ninja.rule( 1950 'alink', 1951 description='AR $out', 1952 command='rm -f $out && $ar rcs $out $in') 1953 master_ninja.rule( 1954 'alink_thin', 1955 description='AR $out', 1956 command='rm -f $out && $ar rcsT $out $in') 1957 1958 # This allows targets that only need to depend on $lib's API to declare an 1959 # order-only dependency on $lib.TOC and avoid relinking such downstream 1960 # dependencies when $lib changes only in non-public ways. 1961 # The resulting string leaves an uninterpolated %{suffix} which 1962 # is used in the final substitution below. 1963 mtime_preserving_solink_base = ( 1964 'if [ ! -e $lib -o ! -e $lib.TOC ]; then ' 1965 '%(solink)s && %(extract_toc)s > $lib.TOC; else ' 1966 '%(solink)s && %(extract_toc)s > $lib.tmp && ' 1967 'if ! cmp -s $lib.tmp $lib.TOC; then mv $lib.tmp $lib.TOC ; ' 1968 'fi; fi' 1969 % { 'solink': 1970 '$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s', 1971 'extract_toc': 1972 ('{ $readelf -d $lib | grep SONAME ; ' 1973 '$nm -gD -f p $lib | cut -f1-2 -d\' \'; }')}) 1974 1975 master_ninja.rule( 1976 'solink', 1977 description='SOLINK $lib', 1978 restat=True, 1979 command=mtime_preserving_solink_base % {'suffix': '@$link_file_list'}, 1980 rspfile='$link_file_list', 1981 rspfile_content= 1982 '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive $libs', 1983 pool='link_pool') 1984 master_ninja.rule( 1985 'solink_module', 1986 description='SOLINK(module) $lib', 1987 restat=True, 1988 command=mtime_preserving_solink_base % {'suffix': '@$link_file_list'}, 1989 rspfile='$link_file_list', 1990 rspfile_content='-Wl,--start-group $in -Wl,--end-group $solibs $libs', 1991 pool='link_pool') 1992 master_ninja.rule( 1993 'link', 1994 description='LINK $out', 1995 command=('$ld $ldflags -o $out ' 1996 '-Wl,--start-group $in -Wl,--end-group $solibs $libs'), 1997 pool='link_pool') 1998 elif flavor == 'win': 1999 master_ninja.rule( 2000 'alink', 2001 description='LIB $out', 2002 command=('%s gyp-win-tool link-wrapper $arch False ' 2003 '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' % 2004 sys.executable), 2005 rspfile='$out.rsp', 2006 rspfile_content='$in_newline $libflags') 2007 _AddWinLinkRules(master_ninja, embed_manifest=True) 2008 _AddWinLinkRules(master_ninja, embed_manifest=False) 2009 else: 2010 master_ninja.rule( 2011 'objc', 2012 description='OBJC $out', 2013 command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc ' 2014 '$cflags_pch_objc -c $in -o $out'), 2015 depfile='$out.d', 2016 deps=deps) 2017 master_ninja.rule( 2018 'objcxx', 2019 description='OBJCXX $out', 2020 command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc ' 2021 '$cflags_pch_objcc -c $in -o $out'), 2022 depfile='$out.d', 2023 deps=deps) 2024 master_ninja.rule( 2025 'alink', 2026 description='LIBTOOL-STATIC $out, POSTBUILDS', 2027 command='rm -f $out && ' 2028 './gyp-mac-tool filter-libtool libtool $libtool_flags ' 2029 '-static -o $out $in' 2030 '$postbuilds') 2031 master_ninja.rule( 2032 'lipo', 2033 description='LIPO $out, POSTBUILDS', 2034 command='rm -f $out && lipo -create $in -output $out$postbuilds') 2035 2036 # Record the public interface of $lib in $lib.TOC. See the corresponding 2037 # comment in the posix section above for details. 2038 solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s' 2039 mtime_preserving_solink_base = ( 2040 'if [ ! -e $lib -o ! -e $lib.TOC ] || ' 2041 # Always force dependent targets to relink if this library 2042 # reexports something. Handling this correctly would require 2043 # recursive TOC dumping but this is rare in practice, so punt. 2044 'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then ' 2045 '%(solink)s && %(extract_toc)s > $lib.TOC; ' 2046 'else ' 2047 '%(solink)s && %(extract_toc)s > $lib.tmp && ' 2048 'if ! cmp -s $lib.tmp $lib.TOC; then ' 2049 'mv $lib.tmp $lib.TOC ; ' 2050 'fi; ' 2051 'fi' 2052 % { 'solink': solink_base, 2053 'extract_toc': 2054 '{ otool -l $lib | grep LC_ID_DYLIB -A 5; ' 2055 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'}) 2056 2057 2058 solink_suffix = '@$link_file_list$postbuilds' 2059 master_ninja.rule( 2060 'solink', 2061 description='SOLINK $lib, POSTBUILDS', 2062 restat=True, 2063 command=mtime_preserving_solink_base % {'suffix': solink_suffix, 2064 'type': '-shared'}, 2065 rspfile='$link_file_list', 2066 rspfile_content='$in $solibs $libs', 2067 pool='link_pool') 2068 master_ninja.rule( 2069 'solink_notoc', 2070 description='SOLINK $lib, POSTBUILDS', 2071 restat=True, 2072 command=solink_base % {'suffix':solink_suffix, 'type': '-shared'}, 2073 rspfile='$link_file_list', 2074 rspfile_content='$in $solibs $libs', 2075 pool='link_pool') 2076 2077 master_ninja.rule( 2078 'solink_module', 2079 description='SOLINK(module) $lib, POSTBUILDS', 2080 restat=True, 2081 command=mtime_preserving_solink_base % {'suffix': solink_suffix, 2082 'type': '-bundle'}, 2083 rspfile='$link_file_list', 2084 rspfile_content='$in $solibs $libs', 2085 pool='link_pool') 2086 master_ninja.rule( 2087 'solink_module_notoc', 2088 description='SOLINK(module) $lib, POSTBUILDS', 2089 restat=True, 2090 command=solink_base % {'suffix': solink_suffix, 'type': '-bundle'}, 2091 rspfile='$link_file_list', 2092 rspfile_content='$in $solibs $libs', 2093 pool='link_pool') 2094 2095 master_ninja.rule( 2096 'link', 2097 description='LINK $out, POSTBUILDS', 2098 command=('$ld $ldflags -o $out ' 2099 '$in $solibs $libs$postbuilds'), 2100 pool='link_pool') 2101 master_ninja.rule( 2102 'preprocess_infoplist', 2103 description='PREPROCESS INFOPLIST $out', 2104 command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && ' 2105 'plutil -convert xml1 $out $out')) 2106 master_ninja.rule( 2107 'copy_infoplist', 2108 description='COPY INFOPLIST $in', 2109 command='$env ./gyp-mac-tool copy-info-plist $in $out $keys') 2110 master_ninja.rule( 2111 'mac_tool', 2112 description='MACTOOL $mactool_cmd $in', 2113 command='$env ./gyp-mac-tool $mactool_cmd $in $out') 2114 master_ninja.rule( 2115 'package_framework', 2116 description='PACKAGE FRAMEWORK $out, POSTBUILDS', 2117 command='./gyp-mac-tool package-framework $out $version$postbuilds ' 2118 '&& touch $out') 2119 if flavor == 'win': 2120 master_ninja.rule( 2121 'stamp', 2122 description='STAMP $out', 2123 command='%s gyp-win-tool stamp $out' % sys.executable) 2124 master_ninja.rule( 2125 'copy', 2126 description='COPY $in $out', 2127 command='%s gyp-win-tool recursive-mirror $in $out' % sys.executable) 2128 else: 2129 master_ninja.rule( 2130 'stamp', 2131 description='STAMP $out', 2132 command='${postbuilds}touch $out') 2133 master_ninja.rule( 2134 'copy', 2135 description='COPY $in $out', 2136 command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)') 2137 master_ninja.newline() 2138 2139 all_targets = set() 2140 for build_file in params['build_files']: 2141 for target in gyp.common.AllTargets(target_list, 2142 target_dicts, 2143 os.path.normpath(build_file)): 2144 all_targets.add(target) 2145 all_outputs = set() 2146 2147 # target_outputs is a map from qualified target name to a Target object. 2148 target_outputs = {} 2149 # target_short_names is a map from target short name to a list of Target 2150 # objects. 2151 target_short_names = {} 2152 2153 # short name of targets that were skipped because they didn't contain anything 2154 # interesting. 2155 # NOTE: there may be overlap between this an non_empty_target_names. 2156 empty_target_names = set() 2157 2158 # Set of non-empty short target names. 2159 # NOTE: there may be overlap between this an empty_target_names. 2160 non_empty_target_names = set() 2161 2162 for qualified_target in target_list: 2163 # qualified_target is like: third_party/icu/icu.gyp:icui18n#target 2164 build_file, name, toolset = \ 2165 gyp.common.ParseQualifiedTarget(qualified_target) 2166 2167 this_make_global_settings = data[build_file].get('make_global_settings', []) 2168 assert make_global_settings == this_make_global_settings, ( 2169 "make_global_settings needs to be the same for all targets. %s vs. %s" % 2170 (this_make_global_settings, make_global_settings)) 2171 2172 spec = target_dicts[qualified_target] 2173 if flavor == 'mac': 2174 gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec) 2175 2176 build_file = gyp.common.RelativePath(build_file, options.toplevel_dir) 2177 2178 base_path = os.path.dirname(build_file) 2179 obj = 'obj' 2180 if toolset != 'target': 2181 obj += '.' + toolset 2182 output_file = os.path.join(obj, base_path, name + '.ninja') 2183 2184 ninja_output = StringIO() 2185 writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir, 2186 ninja_output, 2187 toplevel_build, output_file, 2188 flavor, toplevel_dir=options.toplevel_dir) 2189 2190 target = writer.WriteSpec(spec, config_name, generator_flags) 2191 2192 if ninja_output.tell() > 0: 2193 # Only create files for ninja files that actually have contents. 2194 with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file: 2195 ninja_file.write(ninja_output.getvalue()) 2196 ninja_output.close() 2197 master_ninja.subninja(output_file) 2198 2199 if target: 2200 if name != target.FinalOutput() and spec['toolset'] == 'target': 2201 target_short_names.setdefault(name, []).append(target) 2202 target_outputs[qualified_target] = target 2203 if qualified_target in all_targets: 2204 all_outputs.add(target.FinalOutput()) 2205 non_empty_target_names.add(name) 2206 else: 2207 empty_target_names.add(name) 2208 2209 if target_short_names: 2210 # Write a short name to build this target. This benefits both the 2211 # "build chrome" case as well as the gyp tests, which expect to be 2212 # able to run actions and build libraries by their short name. 2213 master_ninja.newline() 2214 master_ninja.comment('Short names for targets.') 2215 for short_name in target_short_names: 2216 master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in 2217 target_short_names[short_name]]) 2218 2219 # Write phony targets for any empty targets that weren't written yet. As 2220 # short names are not necessarily unique only do this for short names that 2221 # haven't already been output for another target. 2222 empty_target_names = empty_target_names - non_empty_target_names 2223 if empty_target_names: 2224 master_ninja.newline() 2225 master_ninja.comment('Empty targets (output for completeness).') 2226 for name in sorted(empty_target_names): 2227 master_ninja.build(name, 'phony') 2228 2229 if all_outputs: 2230 master_ninja.newline() 2231 master_ninja.build('all', 'phony', list(all_outputs)) 2232 master_ninja.default(generator_flags.get('default_target', 'all')) 2233 2234 master_ninja_file.close() 2235 2236 2237def PerformBuild(data, configurations, params): 2238 options = params['options'] 2239 for config in configurations: 2240 builddir = os.path.join(options.toplevel_dir, 'out', config) 2241 arguments = ['ninja', '-C', builddir] 2242 print 'Building [%s]: %s' % (config, arguments) 2243 subprocess.check_call(arguments) 2244 2245 2246def CallGenerateOutputForConfig(arglist): 2247 # Ignore the interrupt signal so that the parent process catches it and 2248 # kills all multiprocessing children. 2249 signal.signal(signal.SIGINT, signal.SIG_IGN) 2250 2251 (target_list, target_dicts, data, params, config_name) = arglist 2252 GenerateOutputForConfig(target_list, target_dicts, data, params, config_name) 2253 2254 2255def GenerateOutput(target_list, target_dicts, data, params): 2256 # Update target_dicts for iOS device builds. 2257 target_dicts = gyp.xcode_emulation.CloneConfigurationForDeviceAndEmulator( 2258 target_dicts) 2259 2260 user_config = params.get('generator_flags', {}).get('config', None) 2261 if gyp.common.GetFlavor(params) == 'win': 2262 target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts) 2263 target_list, target_dicts = MSVSUtil.InsertLargePdbShims( 2264 target_list, target_dicts, generator_default_variables) 2265 2266 if user_config: 2267 GenerateOutputForConfig(target_list, target_dicts, data, params, 2268 user_config) 2269 else: 2270 config_names = target_dicts[target_list[0]]['configurations'].keys() 2271 if params['parallel']: 2272 try: 2273 pool = multiprocessing.Pool(len(config_names)) 2274 arglists = [] 2275 for config_name in config_names: 2276 arglists.append( 2277 (target_list, target_dicts, data, params, config_name)) 2278 pool.map(CallGenerateOutputForConfig, arglists) 2279 except KeyboardInterrupt, e: 2280 pool.terminate() 2281 raise e 2282 else: 2283 for config_name in config_names: 2284 GenerateOutputForConfig(target_list, target_dicts, data, params, 2285 config_name) 2286