Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 | |
| 3 # Copyright (c) 2011 Google Inc. All rights reserved. | |
| 4 # Use of this source code is governed by a BSD-style license that can be | |
| 5 # found in the LICENSE file. | |
| 6 | |
| 7 import gyp | |
| 8 import gyp.common | |
| 9 import gyp.system_test | |
| 10 import os.path | |
| 11 import pprint | |
| 12 import subprocess | |
| 13 import sys | |
| 14 | |
| 15 import gyp.ninja_syntax as ninja_syntax | |
| 16 | |
| 17 generator_default_variables = { | |
| 18 'OS': 'linux', | |
| 19 | |
| 20 'EXECUTABLE_PREFIX': '', | |
| 21 'EXECUTABLE_SUFFIX': '', | |
| 22 'STATIC_LIB_PREFIX': '', | |
| 23 'STATIC_LIB_SUFFIX': '.a', | |
| 24 'SHARED_LIB_PREFIX': 'lib', | |
| 25 'SHARED_LIB_SUFFIX': '.so', | |
| 26 # TODO: intermediate dir should *not* be shared between different targets. | |
| 27 # Unfortunately, whatever we provide here gets written into many different | |
| 28 # places within the gyp spec so it's difficult to make it target-specific. | |
| 29 # Apparently we've made it this far with one global path for the make build | |
| 30 # we're safe for now. | |
| 31 'INTERMEDIATE_DIR': '$b/geni', | |
| 32 'SHARED_INTERMEDIATE_DIR': '$b/gen', | |
| 33 'PRODUCT_DIR': '$b', | |
| 34 'SHARED_LIB_DIR': '$b/lib', | |
| 35 'LIB_DIR': '$b', | |
| 36 | |
| 37 # Special variables that may be used by gyp 'rule' targets. | |
| 38 # We generate definitions for these variables on the fly when processing a | |
| 39 # rule. | |
| 40 'RULE_INPUT_ROOT': '$root', | |
| 41 'RULE_INPUT_PATH': '$source', | |
| 42 'RULE_INPUT_EXT': '$ext', | |
| 43 'RULE_INPUT_NAME': '$name', | |
| 44 } | |
| 45 | |
| 46 NINJA_BASE = """\ | |
| 47 builddir = %(builddir)s | |
| 48 # Short alias for builddir. | |
| 49 b = %(builddir)s | |
| 50 | |
| 51 cc = %(cc)s | |
| 52 cxx = %(cxx)s | |
| 53 | |
| 54 rule cc | |
| 55 depfile = $out.d | |
| 56 description = CC $out | |
| 57 command = $cc -MMD -MF $out.d $defines $includes $cflags $cflags_c $ | |
| 58 -c $in -o $out | |
| 59 | |
| 60 rule cxx | |
| 61 depfile = $out.d | |
| 62 description = CXX $out | |
| 63 command = $cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc $ | |
| 64 -c $in -o $out | |
| 65 | |
| 66 rule alink | |
| 67 description = AR $out | |
| 68 command = rm -f $out && ar rcsT $out $in | |
|
Nico
2011/08/19 22:45:10
Should you `assert system_test.ArSupportsThinArchi
| |
| 69 | |
| 70 rule solink | |
| 71 description = SOLINK $out | |
| 72 command = g++ -Wl,--threads -Wl,--thread-count=4 $ | |
| 73 -shared $ldflags -o $out -Wl,-soname=$soname $ | |
| 74 -Wl,--whole-archive $in -Wl,--no-whole-archive $libs | |
| 75 | |
| 76 rule link | |
| 77 description = LINK $out | |
| 78 command = g++ -Wl,--threads -Wl,--thread-count=4 $ | |
| 79 $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib $ | |
| 80 -Wl,--start-group $in -Wl,--end-group $libs | |
| 81 | |
| 82 rule stamp | |
| 83 description = STAMP $out | |
| 84 command = touch $out | |
| 85 | |
| 86 rule copy | |
| 87 description = COPY $in $out | |
| 88 command = ln -f $in $out 2>/dev/null || cp -af $in $out | |
| 89 | |
| 90 """ | |
| 91 | |
| 92 | |
| 93 def StripPrefix(arg, prefix): | |
| 94 if arg.startswith(prefix): | |
| 95 return arg[len(prefix):] | |
| 96 return arg | |
| 97 | |
| 98 | |
| 99 def QuoteShellArgument(arg): | |
| 100 return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'" | |
| 101 | |
| 102 | |
| 103 def MaybeQuoteShellArgument(arg): | |
| 104 if '"' in arg or ' ' in arg: | |
| 105 return QuoteShellArgument(arg) | |
| 106 return arg | |
| 107 | |
| 108 | |
| 109 # A small discourse on paths as used within the Ninja build: | |
| 110 # | |
| 111 # Paths within a given .gyp file are always relative to the directory | |
| 112 # containing the .gyp file. Call these "gyp paths". This includes | |
| 113 # sources as well as the starting directory a given gyp rule/action | |
| 114 # expects to be run from. We call this directory "base_dir" within | |
| 115 # the per-.gyp-file NinjaWriter code. | |
| 116 # | |
| 117 # All paths as written into the .ninja files are relative to the root | |
| 118 # of the tree. Call these paths "ninja paths". We set up the ninja | |
| 119 # variable "$b" to be the path to the root of the build output, | |
| 120 # e.g. out/Debug/. All files we produce (both at gyp and at build | |
| 121 # time) appear in that output directory. | |
| 122 # | |
| 123 # We translate between these two notions of paths with two helper | |
| 124 # functions: | |
| 125 # | |
| 126 # - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file) | |
| 127 # into the equivalent ninja path. | |
| 128 # | |
| 129 # - GypPathToUniqueOutput translates a gyp path into a ninja path to write | |
| 130 # an output file; the result can be namespaced such that is unique | |
| 131 # to the input file name as well as the output target name. | |
| 132 | |
| 133 class NinjaWriter: | |
| 134 def __init__(self, target_outputs, base_dir, output_file): | |
| 135 self.target_outputs = target_outputs | |
| 136 # The root-relative path to the source .gyp file; by gyp | |
| 137 # semantics, all input paths are relative to this. | |
| 138 self.base_dir = base_dir | |
| 139 self.ninja = ninja_syntax.Writer(output_file) | |
| 140 | |
| 141 def GypPathToNinja(self, path): | |
| 142 """Translate a gyp path to a ninja path. | |
| 143 | |
| 144 See the above discourse on path conversions.""" | |
| 145 if path.startswith('$'): | |
| 146 # If the path contains a reference to a ninja variable, we know | |
| 147 # it's already relative to the source root. | |
| 148 return path | |
| 149 return os.path.normpath(os.path.join(self.base_dir, path)) | |
| 150 | |
| 151 def GypPathToUniqueOutput(self, path, qualified=False): | |
| 152 """Translate a gyp path to a ninja path for writing output. | |
| 153 | |
| 154 If qualified is True, qualify the resulting filename with the name | |
| 155 of the target. This is necessary when e.g. compiling the same | |
| 156 path twice for two separate output targets. | |
| 157 | |
| 158 See the above discourse on path conversions.""" | |
| 159 | |
| 160 # It may seem strange to discard components of the path, but we are just | |
| 161 # attempting to produce a known-unique output filename; we don't want to | |
| 162 # reuse any global directory. | |
| 163 assert not generator_default_variables['SHARED_INTERMEDIATE_DIR'].startswith ( | |
|
Nico
2011/08/19 22:45:10
80 cols
| |
| 164 generator_default_variables['INTERMEDIATE_DIR']) | |
| 165 path = StripPrefix(path, | |
| 166 generator_default_variables['INTERMEDIATE_DIR']) | |
| 167 path = StripPrefix(path, | |
| 168 generator_default_variables['SHARED_INTERMEDIATE_DIR']) | |
| 169 path = StripPrefix(path, '/') | |
| 170 assert not path.startswith('$') | |
| 171 | |
| 172 # Translate the path following this scheme: | |
| 173 # Input: foo/bar.gyp, target targ, references baz/out.o | |
| 174 # Output: $b/obj/foo/baz/targ.out.o (if qualified) | |
| 175 # $b/obj/foo/baz/out.o (otherwise) | |
| 176 # | |
| 177 # Why this scheme and not some other one? | |
| 178 # 1) for a given input, you can compute all derived outputs by matching | |
| 179 # its path, even if the input is brought via a gyp file with '..'. | |
| 180 # 2) simple files like libraries and stamps have a simple filename. | |
| 181 path_dir, path_basename = os.path.split(path) | |
| 182 if qualified: | |
| 183 path_basename = self.name + '.' + path_basename | |
| 184 return os.path.normpath(os.path.join('$b/obj', self.base_dir, path_dir, | |
| 185 path_basename)) | |
| 186 | |
| 187 def StampPath(self, name): | |
| 188 """Return a path for a stamp file with a particular name. | |
| 189 | |
| 190 Stamp files are used to collapse a dependency on a bunch of files | |
| 191 into a single file.""" | |
| 192 return self.GypPathToUniqueOutput(name + '.stamp', qualified=True) | |
| 193 | |
| 194 def WriteSpec(self, spec, config): | |
| 195 """The main entry point for NinjaWriter: write the build rules for a spec. | |
| 196 | |
| 197 Returns the path to the build output, or None.""" | |
| 198 | |
| 199 if spec['type'] == 'settings': | |
| 200 # TODO: 'settings' is not actually part of gyp; it was | |
| 201 # accidentally introduced somehow into just the Linux build files. | |
| 202 return None | |
| 203 | |
| 204 self.name = spec['target_name'] | |
| 205 | |
| 206 # Compute predepends for all rules. | |
| 207 # prebuild is the dependencies this target depends on before | |
| 208 # running any of its internal steps. | |
| 209 prebuild = [] | |
| 210 if 'dependencies' in spec: | |
| 211 prebuild_deps = [] | |
| 212 for dep in spec['dependencies']: | |
| 213 if dep in self.target_outputs: | |
| 214 prebuild_deps.append(self.target_outputs[dep][0]) | |
| 215 if prebuild_deps: | |
| 216 stamp = self.StampPath('predepends') | |
| 217 prebuild = self.ninja.build(stamp, 'stamp', prebuild_deps) | |
| 218 self.ninja.newline() | |
| 219 | |
| 220 # Write out actions, rules, and copies. These must happen before we | |
| 221 # compile any sources, so compute a list of predependencies for sources | |
| 222 # while we do it. | |
| 223 extra_sources = [] | |
| 224 sources_predepends = self.WriteActionsRulesCopies(spec, extra_sources, | |
| 225 prebuild) | |
| 226 | |
| 227 # Write out the compilation steps, if any. | |
| 228 link_deps = [] | |
| 229 sources = spec.get('sources', []) + extra_sources | |
| 230 if sources: | |
| 231 link_deps = self.WriteSources(config, sources, | |
| 232 sources_predepends or prebuild) | |
| 233 # Some actions/rules output 'sources' that are already object files. | |
| 234 link_deps += [f for f in sources if f.endswith('.o')] | |
| 235 | |
| 236 # The final output of our target depends on the last output of the | |
| 237 # above steps. | |
| 238 final_deps = link_deps or sources_predepends or prebuild | |
| 239 if final_deps: | |
| 240 return self.WriteTarget(spec, config, final_deps) | |
| 241 | |
| 242 def WriteActionsRulesCopies(self, spec, extra_sources, prebuild): | |
| 243 """Write out the Actions, Rules, and Copies steps. Return any outputs | |
| 244 of these steps (or a stamp file if there are lots of outputs).""" | |
| 245 outputs = [] | |
| 246 | |
| 247 if 'actions' in spec: | |
| 248 outputs += self.WriteActions(spec['actions'], extra_sources, prebuild) | |
| 249 if 'rules' in spec: | |
| 250 outputs += self.WriteRules(spec['rules'], extra_sources, prebuild) | |
| 251 if 'copies' in spec: | |
| 252 outputs += self.WriteCopies(spec['copies'], prebuild) | |
| 253 | |
| 254 # To simplify downstream build edges, ensure we generate a single | |
| 255 # stamp file that represents the results of all of the above. | |
| 256 if len(outputs) > 1: | |
| 257 stamp = self.StampPath('actions_rules_copies') | |
| 258 outputs = self.ninja.build(stamp, 'stamp', outputs) | |
| 259 | |
| 260 return outputs | |
| 261 | |
| 262 def WriteActions(self, actions, extra_sources, prebuild): | |
| 263 all_outputs = [] | |
| 264 for action in actions: | |
| 265 # First write out a rule for the action. | |
| 266 name = action['action_name'] | |
| 267 if 'message' in action: | |
| 268 description = 'ACTION ' + action['message'] | |
| 269 else: | |
| 270 description = 'ACTION %s: %s' % (self.name, action['action_name']) | |
| 271 rule_name = self.WriteNewNinjaRule(name, action['action'], description) | |
| 272 | |
| 273 inputs = [self.GypPathToNinja(i) for i in action['inputs']] | |
| 274 if int(action.get('process_outputs_as_sources', False)): | |
| 275 extra_sources += action['outputs'] | |
| 276 outputs = [self.GypPathToNinja(o) for o in action['outputs']] | |
| 277 | |
| 278 # Then write out an edge using the rule. | |
| 279 self.ninja.build(outputs, rule_name, inputs, | |
| 280 order_only=prebuild) | |
| 281 all_outputs += outputs | |
| 282 | |
| 283 self.ninja.newline() | |
| 284 | |
| 285 return all_outputs | |
| 286 | |
| 287 def WriteRules(self, rules, extra_sources, prebuild): | |
| 288 all_outputs = [] | |
| 289 for rule in rules: | |
| 290 # First write out a rule for the rule action. | |
| 291 name = rule['rule_name'] | |
| 292 args = rule['action'] | |
| 293 if 'message' in rule: | |
| 294 description = 'RULE ' + rule['message'] | |
| 295 else: | |
| 296 description = 'RULE %s: %s $source' % (self.name, name) | |
| 297 rule_name = self.WriteNewNinjaRule(name, args, description) | |
| 298 | |
| 299 # TODO: if the command references the outputs directly, we should | |
| 300 # simplify it to just use $out. | |
| 301 | |
| 302 # Rules can potentially make use of some special variables which | |
| 303 # must vary per source file. | |
| 304 # Compute the list of variables we'll need to provide. | |
| 305 special_locals = ('source', 'root', 'ext', 'name') | |
| 306 needed_variables = set(['source']) | |
| 307 for argument in args: | |
| 308 for var in special_locals: | |
| 309 if '$' + var in argument: | |
| 310 needed_variables.add(var) | |
| 311 | |
| 312 # For each source file, write an edge that generates all the outputs. | |
| 313 for source in rule.get('rule_sources', []): | |
| 314 basename = os.path.basename(source) | |
| 315 root, ext = os.path.splitext(basename) | |
| 316 source = self.GypPathToNinja(source) | |
| 317 | |
| 318 outputs = [] | |
| 319 for output in rule['outputs']: | |
| 320 outputs.append(output.replace('$root', root)) | |
| 321 | |
| 322 extra_bindings = [] | |
| 323 for var in needed_variables: | |
| 324 if var == 'root': | |
| 325 extra_bindings.append(('root', root)) | |
| 326 elif var == 'source': | |
| 327 extra_bindings.append(('source', source)) | |
| 328 elif var == 'ext': | |
| 329 extra_bindings.append(('ext', ext)) | |
| 330 elif var == 'name': | |
| 331 extra_bindings.append(('name', basename)) | |
| 332 else: | |
| 333 assert var == None, repr(var) | |
| 334 | |
| 335 inputs = map(self.GypPathToNinja, rule.get('inputs', [])) | |
| 336 self.ninja.build(outputs, rule_name, source, | |
| 337 implicit=inputs, | |
| 338 order_only=prebuild, | |
| 339 variables=extra_bindings) | |
| 340 | |
| 341 if int(rule.get('process_outputs_as_sources', False)): | |
| 342 extra_sources += outputs | |
| 343 | |
| 344 all_outputs.extend(outputs) | |
| 345 | |
| 346 return all_outputs | |
| 347 | |
| 348 def WriteCopies(self, copies, prebuild): | |
| 349 outputs = [] | |
| 350 for copy in copies: | |
| 351 for path in copy['files']: | |
| 352 # Normalize the path so trailing slashes don't confuse us. | |
| 353 path = os.path.normpath(path) | |
| 354 basename = os.path.split(path)[1] | |
| 355 src = self.GypPathToNinja(path) | |
| 356 dst = self.GypPathToNinja(os.path.join(copy['destination'], basename)) | |
| 357 outputs += self.ninja.build(dst, 'copy', src, | |
| 358 order_only=prebuild) | |
| 359 | |
| 360 return outputs | |
| 361 | |
| 362 def WriteSources(self, config, sources, predepends): | |
| 363 """Write build rules to compile all of |sources|.""" | |
| 364 self.WriteVariableList('defines', | |
| 365 ['-D' + MaybeQuoteShellArgument(ninja_syntax.escape(d)) | |
| 366 for d in config.get('defines', [])]) | |
| 367 self.WriteVariableList('includes', | |
| 368 ['-I' + self.GypPathToNinja(i) | |
| 369 for i in config.get('include_dirs', [])]) | |
| 370 self.WriteVariableList('cflags', config.get('cflags')) | |
| 371 self.WriteVariableList('cflags_c', config.get('cflags_c')) | |
| 372 self.WriteVariableList('cflags_cc', config.get('cflags_cc')) | |
| 373 self.ninja.newline() | |
| 374 outputs = [] | |
| 375 for source in sources: | |
| 376 filename, ext = os.path.splitext(source) | |
| 377 ext = ext[1:] | |
| 378 if ext in ('cc', 'cpp', 'cxx'): | |
| 379 command = 'cxx' | |
| 380 elif ext in ('c', 's', 'S'): | |
| 381 command = 'cc' | |
| 382 else: | |
| 383 # TODO: should we assert here on unexpected extensions? | |
| 384 continue | |
| 385 input = self.GypPathToNinja(source) | |
| 386 output = self.GypPathToUniqueOutput(filename + '.o', qualified=True) | |
| 387 self.ninja.build(output, command, input, | |
| 388 order_only=predepends) | |
| 389 outputs.append(output) | |
| 390 self.ninja.newline() | |
| 391 return outputs | |
| 392 | |
| 393 def WriteTarget(self, spec, config, final_deps): | |
| 394 output = self.ComputeOutput(spec) | |
| 395 | |
| 396 output_uses_linker = spec['type'] in ('executable', 'loadable_module', | |
| 397 'shared_library') | |
| 398 | |
| 399 implicit_deps = set() | |
| 400 if 'dependencies' in spec: | |
| 401 # Two kinds of dependencies: | |
| 402 # - Linkable dependencies (like a .a or a .so): add them to the link line. | |
| 403 # - Non-linkable dependencies (like a rule that generates a file | |
| 404 # and writes a stamp file): add them to implicit_deps | |
| 405 if output_uses_linker: | |
| 406 extra_deps = set() | |
| 407 for dep in spec['dependencies']: | |
| 408 input, linkable = self.target_outputs.get(dep, (None, False)) | |
| 409 if not input: | |
| 410 continue | |
| 411 if linkable: | |
| 412 extra_deps.add(input) | |
|
Nico
2011/08/19 22:45:10
dpranke will not appreciate this
| |
| 413 else: | |
| 414 # XXX Chrome-specific HACK. Chrome runs this lastchange rule on | |
|
Nico
2011/08/19 22:45:10
s/XXX/TODO/
| |
| 415 # every build, but we don't want to rebuild when it runs. | |
| 416 if 'lastchange.stamp' not in input: | |
| 417 implicit_deps.add(input) | |
| 418 final_deps.extend(list(extra_deps)) | |
| 419 command_map = { | |
| 420 'executable': 'link', | |
| 421 'static_library': 'alink', | |
| 422 'loadable_module': 'solink', | |
| 423 'shared_library': 'solink', | |
| 424 'none': 'stamp', | |
| 425 } | |
| 426 command = command_map[spec['type']] | |
| 427 | |
| 428 if output_uses_linker: | |
| 429 self.WriteVariableList('ldflags', | |
| 430 gyp.common.uniquer(config.get('ldflags', []))) | |
| 431 self.WriteVariableList('libs', | |
| 432 gyp.common.uniquer(spec.get('libraries', []))) | |
| 433 | |
| 434 extra_bindings = [] | |
| 435 if command == 'solink': | |
| 436 extra_bindings.append(('soname', os.path.split(output)[1])) | |
| 437 | |
| 438 self.ninja.build(output, command, final_deps, | |
| 439 implicit=list(implicit_deps), | |
| 440 variables=extra_bindings) | |
| 441 | |
| 442 # Write a short name to build this target. This benefits both the | |
| 443 # "build chrome" case as well as the gyp tests, which expect to be | |
| 444 # able to run actions and build libraries by their short name. | |
| 445 self.ninja.build(self.name, 'phony', output) | |
| 446 | |
| 447 return output | |
| 448 | |
| 449 def ComputeOutputFileName(self, spec): | |
| 450 """Compute the filename of the final output for the current target.""" | |
| 451 | |
| 452 # Compute filename prefix: the product prefix, or a default for | |
| 453 # the product type. | |
| 454 DEFAULT_PREFIX = { | |
| 455 'loadable_module': 'lib', | |
| 456 'shared_library': 'lib', | |
| 457 } | |
| 458 prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(spec['type'], '')) | |
| 459 | |
| 460 # Compute filename extension: the product extension, or a default | |
| 461 # for the product type. | |
| 462 DEFAULT_EXTENSION = { | |
| 463 'static_library': 'a', | |
| 464 'loadable_module': 'so', | |
| 465 'shared_library': 'so', | |
| 466 } | |
| 467 extension = spec.get('product_extension', | |
| 468 DEFAULT_EXTENSION.get(spec['type'], '')) | |
| 469 if extension: | |
| 470 extension = '.' + extension | |
| 471 | |
| 472 if 'product_name' in spec: | |
| 473 # If we were given an explicit name, use that. | |
| 474 target = spec['product_name'] | |
| 475 else: | |
| 476 # Otherwise, derive a name from the target name. | |
| 477 target = spec['target_name'] | |
| 478 if prefix == 'lib': | |
| 479 # Snip out an extra 'lib' from libs if appropriate. | |
| 480 target = StripPrefix(target, 'lib') | |
|
Nico
2011/08/19 22:45:10
Remark: In this file there are several "if spec['t
| |
| 481 | |
| 482 if spec['type'] in ('static_library', 'loadable_module', 'shared_library', | |
| 483 'executable'): | |
| 484 return '%s%s%s' % (prefix, target, extension) | |
| 485 elif spec['type'] == 'none': | |
| 486 return '%s.stamp' % target | |
| 487 elif spec['type'] == 'settings': | |
| 488 return None | |
| 489 else: | |
| 490 raise 'Unhandled output type', spec['type'] | |
| 491 | |
| 492 def ComputeOutput(self, spec): | |
| 493 """Compute the path for the final output of the spec.""" | |
| 494 | |
| 495 filename = self.ComputeOutputFileName(spec) | |
| 496 | |
| 497 if 'product_dir' in spec: | |
| 498 path = os.path.join(spec['product_dir'], filename) | |
| 499 print 'pdir', path | |
|
Nico
2011/08/19 22:45:10
Are you intentionally writing to stdout here? (pro
| |
| 500 return path | |
| 501 | |
| 502 # Executables and loadable modules go into the output root, | |
| 503 # libraries go into shared library dir, and everything else | |
| 504 # goes into the normal place. | |
| 505 if spec['type'] in ('executable', 'loadable_module'): | |
| 506 return os.path.join('$b', filename) | |
| 507 elif spec['type'] == 'shared_library': | |
| 508 return os.path.join('$b/lib', filename) | |
| 509 else: | |
| 510 return self.GypPathToUniqueOutput(filename) | |
| 511 | |
| 512 def WriteVariableList(self, var, values): | |
| 513 if values is None: | |
| 514 values = [] | |
| 515 self.ninja.variable(var, ' '.join(values)) | |
| 516 | |
| 517 def WriteNewNinjaRule(self, name, args, description): | |
| 518 """Write out a new ninja "rule" statement for a given command. | |
| 519 | |
| 520 Returns the name of the new rule.""" | |
| 521 | |
| 522 # TODO: we shouldn't need to qualify names; we do it because | |
| 523 # currently the ninja rule namespace is global, but it really | |
| 524 # should be scoped to the subninja. | |
|
Nico
2011/08/19 22:45:10
I like the word "subninja"
| |
| 525 rule_name = ('%s.%s' % (self.name, name)).replace(' ', '_') | |
| 526 | |
| 527 cd = '' | |
| 528 args = args[:] | |
| 529 if self.base_dir: | |
| 530 # gyp dictates that commands are run from the base directory. | |
| 531 # cd into the directory before running, and adjust all paths in | |
| 532 # the arguments point to the proper locations. | |
| 533 cd = 'cd %s; ' % self.base_dir | |
| 534 cdup = '../' * len(self.base_dir.split('/')) | |
| 535 for i, arg in enumerate(args): | |
| 536 arg = arg.replace('$b', cdup + '$b') | |
| 537 arg = arg.replace('$source', cdup + '$source') | |
| 538 args[i] = arg | |
|
Nico
2011/08/19 22:45:10
This could be in a separate function maybe
| |
| 539 | |
| 540 command = cd + gyp.common.EncodePOSIXShellList(args) | |
| 541 self.ninja.rule(rule_name, command, description) | |
| 542 self.ninja.newline() | |
| 543 | |
| 544 return rule_name | |
| 545 | |
| 546 | |
| 547 def CalculateVariables(default_variables, params): | |
| 548 """Calculate additional variables for use in the build (called by gyp).""" | |
| 549 cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc')) | |
| 550 default_variables['LINKER_SUPPORTS_ICF'] = \ | |
| 551 gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target) | |
| 552 | |
| 553 | |
| 554 def OpenOutput(path): | |
| 555 """Open |path| for writing, creating directories if necessary.""" | |
| 556 try: | |
| 557 os.makedirs(os.path.dirname(path)) | |
| 558 except OSError: | |
| 559 pass | |
| 560 return open(path, 'w') | |
| 561 | |
| 562 | |
| 563 def GenerateOutput(target_list, target_dicts, data, params): | |
| 564 options = params['options'] | |
| 565 generator_flags = params.get('generator_flags', {}) | |
| 566 | |
| 567 if options.generator_output: | |
| 568 raise NotImplementedError, "--generator_output not implemented for ninja" | |
| 569 | |
| 570 config_name = generator_flags.get('config', None) | |
| 571 if config_name is None: | |
|
Nico
2011/08/19 22:45:10
and len(target_list) > 0? :-P (maybe that's handle
| |
| 572 # Guess which config we want to use: pick the first one from the | |
| 573 # first target. | |
| 574 config_name = target_dicts[target_list[0]]['default_configuration'] | |
| 575 | |
| 576 # builddir: relative path from source root to our output files. | |
| 577 # e.g. "out/Debug" | |
| 578 builddir = os.path.join(generator_flags.get('output_dir', 'out'), config_name) | |
| 579 | |
| 580 master_ninja = OpenOutput(os.path.join(options.toplevel_dir, builddir, | |
| 581 'build.ninja')) | |
| 582 master_ninja.write(NINJA_BASE % { | |
| 583 'builddir': builddir, | |
| 584 'cc': os.environ.get('CC', 'gcc'), | |
| 585 'cxx': os.environ.get('CXX', 'g++'), | |
| 586 }) | |
| 587 | |
| 588 all_targets = set() | |
| 589 for build_file in params['build_files']: | |
| 590 for target in gyp.common.AllTargets(target_list, target_dicts, build_file): | |
| 591 all_targets.add(target) | |
| 592 all_outputs = set() | |
| 593 | |
| 594 subninjas = set() | |
| 595 target_outputs = {} | |
| 596 for qualified_target in target_list: | |
| 597 # qualified_target is like: third_party/icu/icu.gyp:icui18n#target | |
| 598 build_file, target, _ = gyp.common.ParseQualifiedTarget(qualified_target) | |
| 599 | |
| 600 # TODO: what is options.depth and how is it different than | |
| 601 # options.toplevel_dir? | |
| 602 build_file = gyp.common.RelativePath(build_file, options.depth) | |
| 603 | |
| 604 base_path = os.path.dirname(build_file) | |
| 605 output_file = os.path.join(builddir, 'obj', base_path, target + '.ninja') | |
| 606 spec = target_dicts[qualified_target] | |
| 607 config = spec['configurations'][config_name] | |
| 608 | |
| 609 writer = NinjaWriter(target_outputs, base_path, | |
| 610 OpenOutput(os.path.join(options.toplevel_dir, | |
| 611 output_file))) | |
| 612 subninjas.add(output_file) | |
| 613 | |
| 614 output = writer.WriteSpec(spec, config) | |
| 615 if output: | |
| 616 linkable = spec['type'] in ('static_library', 'shared_library') | |
| 617 target_outputs[qualified_target] = (output, linkable) | |
| 618 | |
| 619 if qualified_target in all_targets: | |
| 620 all_outputs.add(output) | |
| 621 | |
| 622 for ninja in subninjas: | |
| 623 print >>master_ninja, 'subninja', ninja | |
| 624 | |
| 625 if all_outputs: | |
| 626 print >>master_ninja, 'build all: phony ||' + ' '.join(all_outputs) | |
| 627 | |
| 628 master_ninja.close() | |
| OLD | NEW |