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