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