| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 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 gyp | |
| 6 import gyp.common | |
| 7 import gyp.SCons as SCons | |
| 8 import os.path | |
| 9 import pprint | |
| 10 import re | |
| 11 import subprocess | |
| 12 | |
| 13 | |
| 14 # TODO: remove when we delete the last WriteList() call in this module | |
| 15 WriteList = SCons.WriteList | |
| 16 | |
| 17 | |
| 18 generator_default_variables = { | |
| 19 'EXECUTABLE_PREFIX': '', | |
| 20 'EXECUTABLE_SUFFIX': '', | |
| 21 'STATIC_LIB_PREFIX': '${LIBPREFIX}', | |
| 22 'SHARED_LIB_PREFIX': '${SHLIBPREFIX}', | |
| 23 'STATIC_LIB_SUFFIX': '${LIBSUFFIX}', | |
| 24 'SHARED_LIB_SUFFIX': '${SHLIBSUFFIX}', | |
| 25 'INTERMEDIATE_DIR': '${INTERMEDIATE_DIR}', | |
| 26 'SHARED_INTERMEDIATE_DIR': '${SHARED_INTERMEDIATE_DIR}', | |
| 27 'OS': 'linux', | |
| 28 'PRODUCT_DIR': '$TOP_BUILDDIR', | |
| 29 'SHARED_LIB_DIR': '$LIB_DIR', | |
| 30 'LIB_DIR': '$LIB_DIR', | |
| 31 'RULE_INPUT_ROOT': '${SOURCE.filebase}', | |
| 32 'RULE_INPUT_DIRNAME': '${SOURCE.dir}', | |
| 33 'RULE_INPUT_EXT': '${SOURCE.suffix}', | |
| 34 'RULE_INPUT_NAME': '${SOURCE.file}', | |
| 35 'RULE_INPUT_PATH': '${SOURCE.abspath}', | |
| 36 'CONFIGURATION_NAME': '${CONFIG_NAME}', | |
| 37 } | |
| 38 | |
| 39 # Tell GYP how to process the input for us. | |
| 40 generator_handles_variants = True | |
| 41 generator_wants_absolute_build_file_paths = True | |
| 42 | |
| 43 | |
| 44 def FixPath(path, prefix): | |
| 45 if not os.path.isabs(path) and not path[0] == '$': | |
| 46 path = prefix + path | |
| 47 return path | |
| 48 | |
| 49 | |
| 50 header = """\ | |
| 51 # This file is generated; do not edit. | |
| 52 """ | |
| 53 | |
| 54 | |
| 55 _alias_template = """ | |
| 56 if GetOption('verbose'): | |
| 57 _action = Action([%(action)s]) | |
| 58 else: | |
| 59 _action = Action([%(action)s], %(message)s) | |
| 60 _outputs = env.Alias( | |
| 61 ['_%(target_name)s_action'], | |
| 62 %(inputs)s, | |
| 63 _action | |
| 64 ) | |
| 65 env.AlwaysBuild(_outputs) | |
| 66 """ | |
| 67 | |
| 68 _run_as_template = """ | |
| 69 if GetOption('verbose'): | |
| 70 _action = Action([%(action)s]) | |
| 71 else: | |
| 72 _action = Action([%(action)s], %(message)s) | |
| 73 """ | |
| 74 | |
| 75 _run_as_template_suffix = """ | |
| 76 _run_as_target = env.Alias('run_%(target_name)s', target_files, _action) | |
| 77 env.Requires(_run_as_target, [ | |
| 78 Alias('%(target_name)s'), | |
| 79 ]) | |
| 80 env.AlwaysBuild(_run_as_target) | |
| 81 """ | |
| 82 | |
| 83 _command_template = """ | |
| 84 if GetOption('verbose'): | |
| 85 _action = Action([%(action)s]) | |
| 86 else: | |
| 87 _action = Action([%(action)s], %(message)s) | |
| 88 _outputs = env.Command( | |
| 89 %(outputs)s, | |
| 90 %(inputs)s, | |
| 91 _action | |
| 92 ) | |
| 93 """ | |
| 94 | |
| 95 # This is copied from the default SCons action, updated to handle symlinks. | |
| 96 _copy_action_template = """ | |
| 97 import shutil | |
| 98 import SCons.Action | |
| 99 | |
| 100 def _copy_files_or_dirs_or_symlinks(dest, src): | |
| 101 SCons.Node.FS.invalidate_node_memos(dest) | |
| 102 if SCons.Util.is_List(src) and os.path.isdir(dest): | |
| 103 for file in src: | |
| 104 shutil.copy2(file, dest) | |
| 105 return 0 | |
| 106 elif os.path.islink(src): | |
| 107 linkto = os.readlink(src) | |
| 108 os.symlink(linkto, dest) | |
| 109 return 0 | |
| 110 elif os.path.isfile(src): | |
| 111 return shutil.copy2(src, dest) | |
| 112 else: | |
| 113 return shutil.copytree(src, dest, 1) | |
| 114 | |
| 115 def _copy_files_or_dirs_or_symlinks_str(dest, src): | |
| 116 return 'Copying %s to %s ...' % (src, dest) | |
| 117 | |
| 118 GYPCopy = SCons.Action.ActionFactory(_copy_files_or_dirs_or_symlinks, | |
| 119 _copy_files_or_dirs_or_symlinks_str, | |
| 120 convert=str) | |
| 121 """ | |
| 122 | |
| 123 _rule_template = """ | |
| 124 %(name)s_additional_inputs = %(inputs)s | |
| 125 %(name)s_outputs = %(outputs)s | |
| 126 def %(name)s_emitter(target, source, env): | |
| 127 return (%(name)s_outputs, source + %(name)s_additional_inputs) | |
| 128 if GetOption('verbose'): | |
| 129 %(name)s_action = Action([%(action)s]) | |
| 130 else: | |
| 131 %(name)s_action = Action([%(action)s], %(message)s) | |
| 132 env['BUILDERS']['%(name)s'] = Builder(action=%(name)s_action, | |
| 133 emitter=%(name)s_emitter) | |
| 134 | |
| 135 _outputs = [] | |
| 136 _processed_input_files = [] | |
| 137 for infile in input_files: | |
| 138 if (type(infile) == type('') | |
| 139 and not os.path.isabs(infile) | |
| 140 and not infile[0] == '$'): | |
| 141 infile = %(src_dir)r + infile | |
| 142 if str(infile).endswith('.%(extension)s'): | |
| 143 _generated = env.%(name)s(infile) | |
| 144 env.Precious(_generated) | |
| 145 _outputs.append(_generated) | |
| 146 %(process_outputs_as_sources_line)s | |
| 147 else: | |
| 148 _processed_input_files.append(infile) | |
| 149 prerequisites.extend(_outputs) | |
| 150 input_files = _processed_input_files | |
| 151 """ | |
| 152 | |
| 153 _spawn_hack = """ | |
| 154 import re | |
| 155 import SCons.Platform.posix | |
| 156 needs_shell = re.compile('["\\'><!^&]') | |
| 157 def gyp_spawn(sh, escape, cmd, args, env): | |
| 158 def strip_scons_quotes(arg): | |
| 159 if arg[0] == '"' and arg[-1] == '"': | |
| 160 return arg[1:-1] | |
| 161 return arg | |
| 162 stripped_args = [strip_scons_quotes(a) for a in args] | |
| 163 if needs_shell.search(' '.join(stripped_args)): | |
| 164 return SCons.Platform.posix.exec_spawnvpe([sh, '-c', ' '.join(args)], env) | |
| 165 else: | |
| 166 return SCons.Platform.posix.exec_spawnvpe(stripped_args, env) | |
| 167 """ | |
| 168 | |
| 169 | |
| 170 def EscapeShellArgument(s): | |
| 171 """Quotes an argument so that it will be interpreted literally by a POSIX | |
| 172 shell. Taken from | |
| 173 http://stackoverflow.com/questions/35817/whats-the-best-way-to-escape-ossys
tem-calls-in-python | |
| 174 """ | |
| 175 return "'" + s.replace("'", "'\\''") + "'" | |
| 176 | |
| 177 | |
| 178 def InvertNaiveSConsQuoting(s): | |
| 179 """SCons tries to "help" with quoting by naively putting double-quotes around | |
| 180 command-line arguments containing space or tab, which is broken for all | |
| 181 but trivial cases, so we undo it. (See quote_spaces() in Subst.py)""" | |
| 182 if ' ' in s or '\t' in s: | |
| 183 # Then SCons will put double-quotes around this, so add our own quotes | |
| 184 # to close its quotes at the beginning and end. | |
| 185 s = '"' + s + '"' | |
| 186 return s | |
| 187 | |
| 188 | |
| 189 def EscapeSConsVariableExpansion(s): | |
| 190 """SCons has its own variable expansion syntax using $. We must escape it for | |
| 191 strings to be interpreted literally. For some reason this requires four | |
| 192 dollar signs, not two, even without the shell involved.""" | |
| 193 return s.replace('$', '$$$$') | |
| 194 | |
| 195 | |
| 196 def EscapeCppDefine(s): | |
| 197 """Escapes a CPP define so that it will reach the compiler unaltered.""" | |
| 198 s = EscapeShellArgument(s) | |
| 199 s = InvertNaiveSConsQuoting(s) | |
| 200 s = EscapeSConsVariableExpansion(s) | |
| 201 return s | |
| 202 | |
| 203 | |
| 204 def GenerateConfig(fp, config, indent='', src_dir=''): | |
| 205 """ | |
| 206 Generates SCons dictionary items for a gyp configuration. | |
| 207 | |
| 208 This provides the main translation between the (lower-case) gyp settings | |
| 209 keywords and the (upper-case) SCons construction variables. | |
| 210 """ | |
| 211 var_mapping = { | |
| 212 'ASFLAGS' : 'asflags', | |
| 213 'CCFLAGS' : 'cflags', | |
| 214 'CFLAGS' : 'cflags_c', | |
| 215 'CXXFLAGS' : 'cflags_cc', | |
| 216 'CPPDEFINES' : 'defines', | |
| 217 'CPPPATH' : 'include_dirs', | |
| 218 # Add the ldflags value to $LINKFLAGS, but not $SHLINKFLAGS. | |
| 219 # SCons defines $SHLINKFLAGS to incorporate $LINKFLAGS, so | |
| 220 # listing both here would case 'ldflags' to get appended to | |
| 221 # both, and then have it show up twice on the command line. | |
| 222 'LINKFLAGS' : 'ldflags', | |
| 223 } | |
| 224 postamble='\n%s],\n' % indent | |
| 225 for scons_var in sorted(var_mapping.keys()): | |
| 226 gyp_var = var_mapping[scons_var] | |
| 227 value = config.get(gyp_var) | |
| 228 if value: | |
| 229 if gyp_var in ('defines',): | |
| 230 value = [EscapeCppDefine(v) for v in value] | |
| 231 if gyp_var in ('include_dirs',): | |
| 232 if src_dir and not src_dir.endswith('/'): | |
| 233 src_dir += '/' | |
| 234 result = [] | |
| 235 for v in value: | |
| 236 v = FixPath(v, src_dir) | |
| 237 # Force SCons to evaluate the CPPPATH directories at | |
| 238 # SConscript-read time, so delayed evaluation of $SRC_DIR | |
| 239 # doesn't point it to the --generator-output= directory. | |
| 240 result.append('env.Dir(%r)' % v) | |
| 241 value = result | |
| 242 else: | |
| 243 value = map(repr, value) | |
| 244 WriteList(fp, | |
| 245 value, | |
| 246 prefix=indent, | |
| 247 preamble='%s%s = [\n ' % (indent, scons_var), | |
| 248 postamble=postamble) | |
| 249 | |
| 250 | |
| 251 def GenerateSConscript(output_filename, spec, build_file, build_file_data): | |
| 252 """ | |
| 253 Generates a SConscript file for a specific target. | |
| 254 | |
| 255 This generates a SConscript file suitable for building any or all of | |
| 256 the target's configurations. | |
| 257 | |
| 258 A SConscript file may be called multiple times to generate targets for | |
| 259 multiple configurations. Consequently, it needs to be ready to build | |
| 260 the target for any requested configuration, and therefore contains | |
| 261 information about the settings for all configurations (generated into | |
| 262 the SConscript file at gyp configuration time) as well as logic for | |
| 263 selecting (at SCons build time) the specific configuration being built. | |
| 264 | |
| 265 The general outline of a generated SConscript file is: | |
| 266 | |
| 267 -- Header | |
| 268 | |
| 269 -- Import 'env'. This contains a $CONFIG_NAME construction | |
| 270 variable that specifies what configuration to build | |
| 271 (e.g. Debug, Release). | |
| 272 | |
| 273 -- Configurations. This is a dictionary with settings for | |
| 274 the different configurations (Debug, Release) under which this | |
| 275 target can be built. The values in the dictionary are themselves | |
| 276 dictionaries specifying what construction variables should added | |
| 277 to the local copy of the imported construction environment | |
| 278 (Append), should be removed (FilterOut), and should outright | |
| 279 replace the imported values (Replace). | |
| 280 | |
| 281 -- Clone the imported construction environment and update | |
| 282 with the proper configuration settings. | |
| 283 | |
| 284 -- Initialize the lists of the targets' input files and prerequisites. | |
| 285 | |
| 286 -- Target-specific actions and rules. These come after the | |
| 287 input file and prerequisite initializations because the | |
| 288 outputs of the actions and rules may affect the input file | |
| 289 list (process_outputs_as_sources) and get added to the list of | |
| 290 prerequisites (so that they're guaranteed to be executed before | |
| 291 building the target). | |
| 292 | |
| 293 -- Call the Builder for the target itself. | |
| 294 | |
| 295 -- Arrange for any copies to be made into installation directories. | |
| 296 | |
| 297 -- Set up the {name} Alias (phony Node) for the target as the | |
| 298 primary handle for building all of the target's pieces. | |
| 299 | |
| 300 -- Use env.Require() to make sure the prerequisites (explicitly | |
| 301 specified, but also including the actions and rules) are built | |
| 302 before the target itself. | |
| 303 | |
| 304 -- Return the {name} Alias to the calling SConstruct file | |
| 305 so it can be added to the list of default targets. | |
| 306 """ | |
| 307 scons_target = SCons.Target(spec) | |
| 308 | |
| 309 gyp_dir = os.path.dirname(output_filename) | |
| 310 if not gyp_dir: | |
| 311 gyp_dir = '.' | |
| 312 gyp_dir = os.path.abspath(gyp_dir) | |
| 313 | |
| 314 output_dir = os.path.dirname(output_filename) | |
| 315 src_dir = build_file_data['_DEPTH'] | |
| 316 src_dir_rel = gyp.common.RelativePath(src_dir, output_dir) | |
| 317 subdir = gyp.common.RelativePath(os.path.dirname(build_file), src_dir) | |
| 318 src_subdir = '$SRC_DIR/' + subdir | |
| 319 src_subdir_ = src_subdir + '/' | |
| 320 | |
| 321 component_name = os.path.splitext(os.path.basename(build_file))[0] | |
| 322 target_name = spec['target_name'] | |
| 323 | |
| 324 if not os.path.exists(gyp_dir): | |
| 325 os.makedirs(gyp_dir) | |
| 326 fp = open(output_filename, 'w') | |
| 327 fp.write(header) | |
| 328 | |
| 329 fp.write('\nimport os\n') | |
| 330 fp.write('\nImport("env")\n') | |
| 331 | |
| 332 # | |
| 333 fp.write('\n') | |
| 334 fp.write('env = env.Clone(COMPONENT_NAME=%s,\n' % repr(component_name)) | |
| 335 fp.write(' TARGET_NAME=%s)\n' % repr(target_name)) | |
| 336 | |
| 337 # | |
| 338 for config in spec['configurations'].itervalues(): | |
| 339 if config.get('scons_line_length'): | |
| 340 fp.write(_spawn_hack) | |
| 341 break | |
| 342 | |
| 343 # | |
| 344 indent = ' ' * 12 | |
| 345 fp.write('\n') | |
| 346 fp.write('configurations = {\n') | |
| 347 for config_name, config in spec['configurations'].iteritems(): | |
| 348 fp.write(' \'%s\' : {\n' % config_name) | |
| 349 | |
| 350 fp.write(' \'Append\' : dict(\n') | |
| 351 GenerateConfig(fp, config, indent, src_subdir) | |
| 352 libraries = spec.get('libraries') | |
| 353 if libraries: | |
| 354 WriteList(fp, | |
| 355 map(repr, libraries), | |
| 356 prefix=indent, | |
| 357 preamble='%sLIBS = [\n ' % indent, | |
| 358 postamble='\n%s],\n' % indent) | |
| 359 fp.write(' ),\n') | |
| 360 | |
| 361 fp.write(' \'FilterOut\' : dict(\n' ) | |
| 362 for key, var in config.get('scons_remove', {}).iteritems(): | |
| 363 fp.write(' %s = %s,\n' % (key, repr(var))) | |
| 364 fp.write(' ),\n') | |
| 365 | |
| 366 fp.write(' \'Replace\' : dict(\n' ) | |
| 367 scons_settings = config.get('scons_variable_settings', {}) | |
| 368 for key in sorted(scons_settings.keys()): | |
| 369 val = pprint.pformat(scons_settings[key]) | |
| 370 fp.write(' %s = %s,\n' % (key, val)) | |
| 371 if 'c++' in spec.get('link_languages', []): | |
| 372 fp.write(' %s = %s,\n' % ('LINK', repr('$CXX'))) | |
| 373 if config.get('scons_line_length'): | |
| 374 fp.write(' SPAWN = gyp_spawn,\n') | |
| 375 fp.write(' ),\n') | |
| 376 | |
| 377 fp.write(' \'ImportExternal\' : [\n' ) | |
| 378 for var in config.get('scons_import_variables', []): | |
| 379 fp.write(' %s,\n' % repr(var)) | |
| 380 fp.write(' ],\n') | |
| 381 | |
| 382 fp.write(' \'PropagateExternal\' : [\n' ) | |
| 383 for var in config.get('scons_propagate_variables', []): | |
| 384 fp.write(' %s,\n' % repr(var)) | |
| 385 fp.write(' ],\n') | |
| 386 | |
| 387 fp.write(' },\n') | |
| 388 fp.write('}\n') | |
| 389 | |
| 390 fp.write('\n' | |
| 391 'config = configurations[env[\'CONFIG_NAME\']]\n' | |
| 392 'env.Append(**config[\'Append\'])\n' | |
| 393 'env.FilterOut(**config[\'FilterOut\'])\n' | |
| 394 'env.Replace(**config[\'Replace\'])\n') | |
| 395 | |
| 396 fp.write('\n' | |
| 397 '# Scons forces -fPIC for SHCCFLAGS on some platforms.\n' | |
| 398 '# Disable that so we can control it from cflags in gyp.\n' | |
| 399 '# Note that Scons itself is inconsistent with its -fPIC\n' | |
| 400 '# setting. SHCCFLAGS forces -fPIC, and SHCFLAGS does not.\n' | |
| 401 '# This will make SHCCFLAGS consistent with SHCFLAGS.\n' | |
| 402 'env[\'SHCCFLAGS\'] = [\'$CCFLAGS\']\n') | |
| 403 | |
| 404 fp.write('\n' | |
| 405 'for _var in config[\'ImportExternal\']:\n' | |
| 406 ' if _var in ARGUMENTS:\n' | |
| 407 ' env[_var] = ARGUMENTS[_var]\n' | |
| 408 ' elif _var in os.environ:\n' | |
| 409 ' env[_var] = os.environ[_var]\n' | |
| 410 'for _var in config[\'PropagateExternal\']:\n' | |
| 411 ' if _var in ARGUMENTS:\n' | |
| 412 ' env[_var] = ARGUMENTS[_var]\n' | |
| 413 ' elif _var in os.environ:\n' | |
| 414 ' env[\'ENV\'][_var] = os.environ[_var]\n') | |
| 415 | |
| 416 fp.write('\n' | |
| 417 "env['ENV']['LD_LIBRARY_PATH'] = env.subst('$LIB_DIR')\n") | |
| 418 | |
| 419 # | |
| 420 #fp.write("\nif env.has_key('CPPPATH'):\n") | |
| 421 #fp.write(" env['CPPPATH'] = map(env.Dir, env['CPPPATH'])\n") | |
| 422 | |
| 423 variants = spec.get('variants', {}) | |
| 424 for setting in sorted(variants.keys()): | |
| 425 if_fmt = 'if ARGUMENTS.get(%s) not in (None, \'0\'):\n' | |
| 426 fp.write('\n') | |
| 427 fp.write(if_fmt % repr(setting.upper())) | |
| 428 fp.write(' env.AppendUnique(\n') | |
| 429 GenerateConfig(fp, variants[setting], indent, src_subdir) | |
| 430 fp.write(' )\n') | |
| 431 | |
| 432 # | |
| 433 scons_target.write_input_files(fp) | |
| 434 | |
| 435 fp.write('\n') | |
| 436 fp.write('target_files = []\n') | |
| 437 prerequisites = spec.get('scons_prerequisites', []) | |
| 438 fp.write('prerequisites = %s\n' % pprint.pformat(prerequisites)) | |
| 439 | |
| 440 actions = spec.get('actions', []) | |
| 441 for action in actions: | |
| 442 a = ['cd', src_subdir, '&&'] + action['action'] | |
| 443 message = action.get('message') | |
| 444 if message: | |
| 445 message = repr(message) | |
| 446 inputs = [FixPath(f, src_subdir_) for f in action.get('inputs', [])] | |
| 447 outputs = [FixPath(f, src_subdir_) for f in action.get('outputs', [])] | |
| 448 if outputs: | |
| 449 template = _command_template | |
| 450 else: | |
| 451 template = _alias_template | |
| 452 fp.write(template % { | |
| 453 'inputs' : pprint.pformat(inputs), | |
| 454 'outputs' : pprint.pformat(outputs), | |
| 455 'action' : pprint.pformat(a), | |
| 456 'message' : message, | |
| 457 'target_name': target_name, | |
| 458 }) | |
| 459 if int(action.get('process_outputs_as_sources', 0)): | |
| 460 fp.write('input_files.extend(_outputs)\n') | |
| 461 fp.write('prerequisites.extend(_outputs)\n') | |
| 462 fp.write('target_files.extend(_outputs)\n') | |
| 463 | |
| 464 rules = spec.get('rules', []) | |
| 465 for rule in rules: | |
| 466 name = re.sub('[^a-zA-Z0-9_]', '_', rule['rule_name']) | |
| 467 message = rule.get('message') | |
| 468 if message: | |
| 469 message = repr(message) | |
| 470 if int(rule.get('process_outputs_as_sources', 0)): | |
| 471 poas_line = '_processed_input_files.extend(_generated)' | |
| 472 else: | |
| 473 poas_line = '_processed_input_files.append(infile)' | |
| 474 inputs = [FixPath(f, src_subdir_) for f in rule.get('inputs', [])] | |
| 475 outputs = [FixPath(f, src_subdir_) for f in rule.get('outputs', [])] | |
| 476 # Skip a rule with no action and no inputs. | |
| 477 if 'action' not in rule and not rule.get('rule_sources', []): | |
| 478 continue | |
| 479 a = ['cd', src_subdir, '&&'] + rule['action'] | |
| 480 fp.write(_rule_template % { | |
| 481 'inputs' : pprint.pformat(inputs), | |
| 482 'outputs' : pprint.pformat(outputs), | |
| 483 'action' : pprint.pformat(a), | |
| 484 'extension' : rule['extension'], | |
| 485 'name' : name, | |
| 486 'message' : message, | |
| 487 'process_outputs_as_sources_line' : poas_line, | |
| 488 'src_dir' : src_subdir_, | |
| 489 }) | |
| 490 | |
| 491 scons_target.write_target(fp, src_subdir) | |
| 492 | |
| 493 copies = spec.get('copies', []) | |
| 494 if copies: | |
| 495 fp.write(_copy_action_template) | |
| 496 for copy in copies: | |
| 497 destdir = None | |
| 498 files = None | |
| 499 try: | |
| 500 destdir = copy['destination'] | |
| 501 except KeyError, e: | |
| 502 gyp.common.ExceptionAppend( | |
| 503 e, | |
| 504 "Required 'destination' key missing for 'copies' in %s." % build_file) | |
| 505 raise | |
| 506 try: | |
| 507 files = copy['files'] | |
| 508 except KeyError, e: | |
| 509 gyp.common.ExceptionAppend( | |
| 510 e, "Required 'files' key missing for 'copies' in %s." % build_file) | |
| 511 raise | |
| 512 if not files: | |
| 513 # TODO: should probably add a (suppressible) warning; | |
| 514 # a null file list may be unintentional. | |
| 515 continue | |
| 516 if not destdir: | |
| 517 raise Exception( | |
| 518 "Required 'destination' key is empty for 'copies' in %s." % build_file) | |
| 519 | |
| 520 fmt = ('\n' | |
| 521 '_outputs = env.Command(%s,\n' | |
| 522 ' %s,\n' | |
| 523 ' GYPCopy(\'$TARGET\', \'$SOURCE\'))\n') | |
| 524 for f in copy['files']: | |
| 525 # Remove trailing separators so basename() acts like Unix basename and | |
| 526 # always returns the last element, whether a file or dir. Without this, | |
| 527 # only the contents, not the directory itself, are copied (and nothing | |
| 528 # might be copied if dest already exists, since scons thinks nothing needs | |
| 529 # to be done). | |
| 530 dest = os.path.join(destdir, os.path.basename(f.rstrip(os.sep))) | |
| 531 f = FixPath(f, src_subdir_) | |
| 532 dest = FixPath(dest, src_subdir_) | |
| 533 fp.write(fmt % (repr(dest), repr(f))) | |
| 534 fp.write('target_files.extend(_outputs)\n') | |
| 535 | |
| 536 run_as = spec.get('run_as') | |
| 537 if run_as: | |
| 538 action = run_as.get('action', []) | |
| 539 working_directory = run_as.get('working_directory') | |
| 540 if not working_directory: | |
| 541 working_directory = gyp_dir | |
| 542 else: | |
| 543 if not os.path.isabs(working_directory): | |
| 544 working_directory = os.path.normpath(os.path.join(gyp_dir, | |
| 545 working_directory)) | |
| 546 if run_as.get('environment'): | |
| 547 for (key, val) in run_as.get('environment').iteritems(): | |
| 548 action = ['%s="%s"' % (key, val)] + action | |
| 549 action = ['cd', '"%s"' % working_directory, '&&'] + action | |
| 550 fp.write(_run_as_template % { | |
| 551 'action' : pprint.pformat(action), | |
| 552 'message' : run_as.get('message', ''), | |
| 553 }) | |
| 554 | |
| 555 fmt = "\ngyp_target = env.Alias('%s', target_files)\n" | |
| 556 fp.write(fmt % target_name) | |
| 557 | |
| 558 dependencies = spec.get('scons_dependencies', []) | |
| 559 if dependencies: | |
| 560 WriteList(fp, dependencies, preamble='dependencies = [\n ', | |
| 561 postamble='\n]\n') | |
| 562 fp.write('env.Requires(target_files, dependencies)\n') | |
| 563 fp.write('env.Requires(gyp_target, dependencies)\n') | |
| 564 fp.write('for prerequisite in prerequisites:\n') | |
| 565 fp.write(' env.Requires(prerequisite, dependencies)\n') | |
| 566 fp.write('env.Requires(gyp_target, prerequisites)\n') | |
| 567 | |
| 568 if run_as: | |
| 569 fp.write(_run_as_template_suffix % { | |
| 570 'target_name': target_name, | |
| 571 }) | |
| 572 | |
| 573 fp.write('Return("gyp_target")\n') | |
| 574 | |
| 575 fp.close() | |
| 576 | |
| 577 | |
| 578 ############################################################################# | |
| 579 # TEMPLATE BEGIN | |
| 580 | |
| 581 _wrapper_template = """\ | |
| 582 | |
| 583 __doc__ = ''' | |
| 584 Wrapper configuration for building this entire "solution," | |
| 585 including all the specific targets in various *.scons files. | |
| 586 ''' | |
| 587 | |
| 588 import os | |
| 589 import sys | |
| 590 | |
| 591 import SCons.Environment | |
| 592 import SCons.Util | |
| 593 | |
| 594 def GetProcessorCount(): | |
| 595 ''' | |
| 596 Detects the number of CPUs on the system. Adapted form: | |
| 597 http://codeliberates.blogspot.com/2008/05/detecting-cpuscores-in-python.html | |
| 598 ''' | |
| 599 # Linux, Unix and Mac OS X: | |
| 600 if hasattr(os, 'sysconf'): | |
| 601 if os.sysconf_names.has_key('SC_NPROCESSORS_ONLN'): | |
| 602 # Linux and Unix or Mac OS X with python >= 2.5: | |
| 603 return os.sysconf('SC_NPROCESSORS_ONLN') | |
| 604 else: # Mac OS X with Python < 2.5: | |
| 605 return int(os.popen2("sysctl -n hw.ncpu")[1].read()) | |
| 606 # Windows: | |
| 607 if os.environ.has_key('NUMBER_OF_PROCESSORS'): | |
| 608 return max(int(os.environ.get('NUMBER_OF_PROCESSORS', '1')), 1) | |
| 609 return 1 # Default | |
| 610 | |
| 611 # Support PROGRESS= to show progress in different ways. | |
| 612 p = ARGUMENTS.get('PROGRESS') | |
| 613 if p == 'spinner': | |
| 614 Progress(['/\\r', '|\\r', '\\\\\\r', '-\\r'], | |
| 615 interval=5, | |
| 616 file=open('/dev/tty', 'w')) | |
| 617 elif p == 'name': | |
| 618 Progress('$TARGET\\r', overwrite=True, file=open('/dev/tty', 'w')) | |
| 619 | |
| 620 # Set the default -j value based on the number of processors. | |
| 621 SetOption('num_jobs', GetProcessorCount() + 1) | |
| 622 | |
| 623 # Have SCons use its cached dependency information. | |
| 624 SetOption('implicit_cache', 1) | |
| 625 | |
| 626 # Only re-calculate MD5 checksums if a timestamp has changed. | |
| 627 Decider('MD5-timestamp') | |
| 628 | |
| 629 # Since we set the -j value by default, suppress SCons warnings about being | |
| 630 # unable to support parallel build on versions of Python with no threading. | |
| 631 default_warnings = ['no-no-parallel-support'] | |
| 632 SetOption('warn', default_warnings + GetOption('warn')) | |
| 633 | |
| 634 AddOption('--mode', nargs=1, dest='conf_list', default=[], | |
| 635 action='append', help='Configuration to build.') | |
| 636 | |
| 637 AddOption('--verbose', dest='verbose', default=False, | |
| 638 action='store_true', help='Verbose command-line output.') | |
| 639 | |
| 640 | |
| 641 # | |
| 642 sconscript_file_map = %(sconscript_files)s | |
| 643 | |
| 644 class LoadTarget: | |
| 645 ''' | |
| 646 Class for deciding if a given target sconscript is to be included | |
| 647 based on a list of included target names, optionally prefixed with '-' | |
| 648 to exclude a target name. | |
| 649 ''' | |
| 650 def __init__(self, load): | |
| 651 ''' | |
| 652 Initialize a class with a list of names for possible loading. | |
| 653 | |
| 654 Arguments: | |
| 655 load: list of elements in the LOAD= specification | |
| 656 ''' | |
| 657 self.included = set([c for c in load if not c.startswith('-')]) | |
| 658 self.excluded = set([c[1:] for c in load if c.startswith('-')]) | |
| 659 | |
| 660 if not self.included: | |
| 661 self.included = set(['all']) | |
| 662 | |
| 663 def __call__(self, target): | |
| 664 ''' | |
| 665 Returns True if the specified target's sconscript file should be | |
| 666 loaded, based on the initialized included and excluded lists. | |
| 667 ''' | |
| 668 return (target in self.included or | |
| 669 ('all' in self.included and not target in self.excluded)) | |
| 670 | |
| 671 if 'LOAD' in ARGUMENTS: | |
| 672 load = ARGUMENTS['LOAD'].split(',') | |
| 673 else: | |
| 674 load = [] | |
| 675 load_target = LoadTarget(load) | |
| 676 | |
| 677 sconscript_files = [] | |
| 678 for target, sconscript in sconscript_file_map.iteritems(): | |
| 679 if load_target(target): | |
| 680 sconscript_files.append(sconscript) | |
| 681 | |
| 682 | |
| 683 target_alias_list= [] | |
| 684 | |
| 685 conf_list = GetOption('conf_list') | |
| 686 if conf_list: | |
| 687 # In case the same --mode= value was specified multiple times. | |
| 688 conf_list = list(set(conf_list)) | |
| 689 else: | |
| 690 conf_list = [%(default_configuration)r] | |
| 691 | |
| 692 sconsbuild_dir = Dir(%(sconsbuild_dir)s) | |
| 693 | |
| 694 | |
| 695 def FilterOut(self, **kw): | |
| 696 kw = SCons.Environment.copy_non_reserved_keywords(kw) | |
| 697 for key, val in kw.items(): | |
| 698 envval = self.get(key, None) | |
| 699 if envval is None: | |
| 700 # No existing variable in the environment, so nothing to delete. | |
| 701 continue | |
| 702 | |
| 703 for vremove in val: | |
| 704 # Use while not if, so we can handle duplicates. | |
| 705 while vremove in envval: | |
| 706 envval.remove(vremove) | |
| 707 | |
| 708 self[key] = envval | |
| 709 | |
| 710 # TODO(sgk): SCons.Environment.Append() has much more logic to deal | |
| 711 # with various types of values. We should handle all those cases in here | |
| 712 # too. (If variable is a dict, etc.) | |
| 713 | |
| 714 | |
| 715 non_compilable_suffixes = { | |
| 716 'LINUX' : set([ | |
| 717 '.bdic', | |
| 718 '.css', | |
| 719 '.dat', | |
| 720 '.fragment', | |
| 721 '.gperf', | |
| 722 '.h', | |
| 723 '.hh', | |
| 724 '.hpp', | |
| 725 '.html', | |
| 726 '.hxx', | |
| 727 '.idl', | |
| 728 '.in', | |
| 729 '.in0', | |
| 730 '.in1', | |
| 731 '.js', | |
| 732 '.mk', | |
| 733 '.rc', | |
| 734 '.sigs', | |
| 735 '', | |
| 736 ]), | |
| 737 'WINDOWS' : set([ | |
| 738 '.h', | |
| 739 '.hh', | |
| 740 '.hpp', | |
| 741 '.dat', | |
| 742 '.idl', | |
| 743 '.in', | |
| 744 '.in0', | |
| 745 '.in1', | |
| 746 ]), | |
| 747 } | |
| 748 | |
| 749 def compilable(env, file): | |
| 750 base, ext = os.path.splitext(str(file)) | |
| 751 if ext in non_compilable_suffixes[env['TARGET_PLATFORM']]: | |
| 752 return False | |
| 753 return True | |
| 754 | |
| 755 def compilable_files(env, sources): | |
| 756 return [x for x in sources if compilable(env, x)] | |
| 757 | |
| 758 def GypProgram(env, target, source, *args, **kw): | |
| 759 source = compilable_files(env, source) | |
| 760 result = env.Program(target, source, *args, **kw) | |
| 761 if env.get('INCREMENTAL'): | |
| 762 env.Precious(result) | |
| 763 return result | |
| 764 | |
| 765 def GypTestProgram(env, target, source, *args, **kw): | |
| 766 source = compilable_files(env, source) | |
| 767 result = env.Program(target, source, *args, **kw) | |
| 768 if env.get('INCREMENTAL'): | |
| 769 env.Precious(*result) | |
| 770 return result | |
| 771 | |
| 772 def GypLibrary(env, target, source, *args, **kw): | |
| 773 source = compilable_files(env, source) | |
| 774 result = env.Library(target, source, *args, **kw) | |
| 775 return result | |
| 776 | |
| 777 def GypLoadableModule(env, target, source, *args, **kw): | |
| 778 source = compilable_files(env, source) | |
| 779 result = env.LoadableModule(target, source, *args, **kw) | |
| 780 return result | |
| 781 | |
| 782 def GypStaticLibrary(env, target, source, *args, **kw): | |
| 783 source = compilable_files(env, source) | |
| 784 result = env.StaticLibrary(target, source, *args, **kw) | |
| 785 return result | |
| 786 | |
| 787 def GypSharedLibrary(env, target, source, *args, **kw): | |
| 788 source = compilable_files(env, source) | |
| 789 result = env.SharedLibrary(target, source, *args, **kw) | |
| 790 if env.get('INCREMENTAL'): | |
| 791 env.Precious(result) | |
| 792 return result | |
| 793 | |
| 794 def add_gyp_methods(env): | |
| 795 env.AddMethod(GypProgram) | |
| 796 env.AddMethod(GypTestProgram) | |
| 797 env.AddMethod(GypLibrary) | |
| 798 env.AddMethod(GypLoadableModule) | |
| 799 env.AddMethod(GypStaticLibrary) | |
| 800 env.AddMethod(GypSharedLibrary) | |
| 801 | |
| 802 env.AddMethod(FilterOut) | |
| 803 | |
| 804 env.AddMethod(compilable) | |
| 805 | |
| 806 | |
| 807 base_env = Environment( | |
| 808 tools = %(scons_tools)s, | |
| 809 INTERMEDIATE_DIR='$OBJ_DIR/${COMPONENT_NAME}/_${TARGET_NAME}_intermediate', | |
| 810 LIB_DIR='$TOP_BUILDDIR/lib', | |
| 811 OBJ_DIR='$TOP_BUILDDIR/obj', | |
| 812 SCONSBUILD_DIR=sconsbuild_dir.abspath, | |
| 813 SHARED_INTERMEDIATE_DIR='$OBJ_DIR/_global_intermediate', | |
| 814 SRC_DIR=Dir(%(src_dir)r), | |
| 815 TARGET_PLATFORM='LINUX', | |
| 816 TOP_BUILDDIR='$SCONSBUILD_DIR/$CONFIG_NAME', | |
| 817 LIBPATH=['$LIB_DIR'], | |
| 818 ) | |
| 819 | |
| 820 if not GetOption('verbose'): | |
| 821 base_env.SetDefault( | |
| 822 ARCOMSTR='Creating library $TARGET', | |
| 823 ASCOMSTR='Assembling $TARGET', | |
| 824 CCCOMSTR='Compiling $TARGET', | |
| 825 CONCATSOURCECOMSTR='ConcatSource $TARGET', | |
| 826 CXXCOMSTR='Compiling $TARGET', | |
| 827 LDMODULECOMSTR='Building loadable module $TARGET', | |
| 828 LINKCOMSTR='Linking $TARGET', | |
| 829 MANIFESTCOMSTR='Updating manifest for $TARGET', | |
| 830 MIDLCOMSTR='Compiling IDL $TARGET', | |
| 831 PCHCOMSTR='Precompiling $TARGET', | |
| 832 RANLIBCOMSTR='Indexing $TARGET', | |
| 833 RCCOMSTR='Compiling resource $TARGET', | |
| 834 SHCCCOMSTR='Compiling $TARGET', | |
| 835 SHCXXCOMSTR='Compiling $TARGET', | |
| 836 SHLINKCOMSTR='Linking $TARGET', | |
| 837 SHMANIFESTCOMSTR='Updating manifest for $TARGET', | |
| 838 ) | |
| 839 | |
| 840 add_gyp_methods(base_env) | |
| 841 | |
| 842 for conf in conf_list: | |
| 843 env = base_env.Clone(CONFIG_NAME=conf) | |
| 844 SConsignFile(env.File('$TOP_BUILDDIR/.sconsign').abspath) | |
| 845 for sconscript in sconscript_files: | |
| 846 target_alias = env.SConscript(sconscript, exports=['env']) | |
| 847 if target_alias: | |
| 848 target_alias_list.extend(target_alias) | |
| 849 | |
| 850 Default(Alias('all', target_alias_list)) | |
| 851 | |
| 852 help_fmt = ''' | |
| 853 Usage: hammer [SCONS_OPTIONS] [VARIABLES] [TARGET] ... | |
| 854 | |
| 855 Local command-line build options: | |
| 856 --mode=CONFIG Configuration to build: | |
| 857 --mode=Debug [default] | |
| 858 --mode=Release | |
| 859 --verbose Print actual executed command lines. | |
| 860 | |
| 861 Supported command-line build variables: | |
| 862 LOAD=[module,...] Comma-separated list of components to load in the | |
| 863 dependency graph ('-' prefix excludes) | |
| 864 PROGRESS=type Display a progress indicator: | |
| 865 name: print each evaluated target name | |
| 866 spinner: print a spinner every 5 targets | |
| 867 | |
| 868 The following TARGET names can also be used as LOAD= module names: | |
| 869 | |
| 870 %%s | |
| 871 ''' | |
| 872 | |
| 873 if GetOption('help'): | |
| 874 def columnar_text(items, width=78, indent=2, sep=2): | |
| 875 result = [] | |
| 876 colwidth = max(map(len, items)) + sep | |
| 877 cols = (width - indent) / colwidth | |
| 878 if cols < 1: | |
| 879 cols = 1 | |
| 880 rows = (len(items) + cols - 1) / cols | |
| 881 indent = '%%*s' %% (indent, '') | |
| 882 sep = indent | |
| 883 for row in xrange(0, rows): | |
| 884 result.append(sep) | |
| 885 for i in xrange(row, len(items), rows): | |
| 886 result.append('%%-*s' %% (colwidth, items[i])) | |
| 887 sep = '\\n' + indent | |
| 888 result.append('\\n') | |
| 889 return ''.join(result) | |
| 890 | |
| 891 load_list = set(sconscript_file_map.keys()) | |
| 892 target_aliases = set(map(str, target_alias_list)) | |
| 893 | |
| 894 common = load_list and target_aliases | |
| 895 load_only = load_list - common | |
| 896 target_only = target_aliases - common | |
| 897 help_text = [help_fmt %% columnar_text(sorted(list(common)))] | |
| 898 if target_only: | |
| 899 fmt = "The following are additional TARGET names:\\n\\n%%s\\n" | |
| 900 help_text.append(fmt %% columnar_text(sorted(list(target_only)))) | |
| 901 if load_only: | |
| 902 fmt = "The following are additional LOAD= module names:\\n\\n%%s\\n" | |
| 903 help_text.append(fmt %% columnar_text(sorted(list(load_only)))) | |
| 904 Help(''.join(help_text)) | |
| 905 """ | |
| 906 | |
| 907 # TEMPLATE END | |
| 908 ############################################################################# | |
| 909 | |
| 910 | |
| 911 def GenerateSConscriptWrapper(build_file, build_file_data, name, | |
| 912 output_filename, sconscript_files, | |
| 913 default_configuration): | |
| 914 """ | |
| 915 Generates the "wrapper" SConscript file (analogous to the Visual Studio | |
| 916 solution) that calls all the individual target SConscript files. | |
| 917 """ | |
| 918 output_dir = os.path.dirname(output_filename) | |
| 919 src_dir = build_file_data['_DEPTH'] | |
| 920 src_dir_rel = gyp.common.RelativePath(src_dir, output_dir) | |
| 921 if not src_dir_rel: | |
| 922 src_dir_rel = '.' | |
| 923 scons_settings = build_file_data.get('scons_settings', {}) | |
| 924 sconsbuild_dir = scons_settings.get('sconsbuild_dir', '#') | |
| 925 scons_tools = scons_settings.get('tools', ['default']) | |
| 926 | |
| 927 sconscript_file_lines = ['dict('] | |
| 928 for target in sorted(sconscript_files.keys()): | |
| 929 sconscript = sconscript_files[target] | |
| 930 sconscript_file_lines.append(' %s = %r,' % (target, sconscript)) | |
| 931 sconscript_file_lines.append(')') | |
| 932 | |
| 933 fp = open(output_filename, 'w') | |
| 934 fp.write(header) | |
| 935 fp.write(_wrapper_template % { | |
| 936 'default_configuration' : default_configuration, | |
| 937 'name' : name, | |
| 938 'scons_tools' : repr(scons_tools), | |
| 939 'sconsbuild_dir' : repr(sconsbuild_dir), | |
| 940 'sconscript_files' : '\n'.join(sconscript_file_lines), | |
| 941 'src_dir' : src_dir_rel, | |
| 942 }) | |
| 943 fp.close() | |
| 944 | |
| 945 # Generate the SConstruct file that invokes the wrapper SConscript. | |
| 946 dir, fname = os.path.split(output_filename) | |
| 947 SConstruct = os.path.join(dir, 'SConstruct') | |
| 948 fp = open(SConstruct, 'w') | |
| 949 fp.write(header) | |
| 950 fp.write('SConscript(%s)\n' % repr(fname)) | |
| 951 fp.close() | |
| 952 | |
| 953 | |
| 954 def TargetFilename(target, build_file=None, output_suffix=''): | |
| 955 """Returns the .scons file name for the specified target. | |
| 956 """ | |
| 957 if build_file is None: | |
| 958 build_file, target = gyp.common.ParseQualifiedTarget(target)[:2] | |
| 959 output_file = os.path.join(os.path.dirname(build_file), | |
| 960 target + output_suffix + '.scons') | |
| 961 return output_file | |
| 962 | |
| 963 | |
| 964 def PerformBuild(data, configurations, params): | |
| 965 options = params['options'] | |
| 966 | |
| 967 # Due to the way we test gyp on the chromium typbots | |
| 968 # we need to look for 'scons.py' as well as the more common 'scons' | |
| 969 # TODO(sbc): update the trybots to have a more normal install | |
| 970 # of scons. | |
| 971 scons = 'scons' | |
| 972 paths = os.environ['PATH'].split(os.pathsep) | |
| 973 for scons_name in ['scons', 'scons.py']: | |
| 974 for path in paths: | |
| 975 test_scons = os.path.join(path, scons_name) | |
| 976 print 'looking for: %s' % test_scons | |
| 977 if os.path.exists(test_scons): | |
| 978 print "found scons: %s" % scons | |
| 979 scons = test_scons | |
| 980 break | |
| 981 | |
| 982 for config in configurations: | |
| 983 arguments = [scons, '-C', options.toplevel_dir, '--mode=%s' % config] | |
| 984 print "Building [%s]: %s" % (config, arguments) | |
| 985 subprocess.check_call(arguments) | |
| 986 | |
| 987 | |
| 988 def GenerateOutput(target_list, target_dicts, data, params): | |
| 989 """ | |
| 990 Generates all the output files for the specified targets. | |
| 991 """ | |
| 992 options = params['options'] | |
| 993 | |
| 994 if options.generator_output: | |
| 995 def output_path(filename): | |
| 996 return filename.replace(params['cwd'], options.generator_output) | |
| 997 else: | |
| 998 def output_path(filename): | |
| 999 return filename | |
| 1000 | |
| 1001 default_configuration = None | |
| 1002 | |
| 1003 for qualified_target in target_list: | |
| 1004 spec = target_dicts[qualified_target] | |
| 1005 if spec['toolset'] != 'target': | |
| 1006 raise Exception( | |
| 1007 'Multiple toolsets not supported in scons build (target %s)' % | |
| 1008 qualified_target) | |
| 1009 scons_target = SCons.Target(spec) | |
| 1010 if scons_target.is_ignored: | |
| 1011 continue | |
| 1012 | |
| 1013 # TODO: assumes the default_configuration of the first target | |
| 1014 # non-Default target is the correct default for all targets. | |
| 1015 # Need a better model for handle variation between targets. | |
| 1016 if (not default_configuration and | |
| 1017 spec['default_configuration'] != 'Default'): | |
| 1018 default_configuration = spec['default_configuration'] | |
| 1019 | |
| 1020 build_file, target = gyp.common.ParseQualifiedTarget(qualified_target)[:2] | |
| 1021 output_file = TargetFilename(target, build_file, options.suffix) | |
| 1022 if options.generator_output: | |
| 1023 output_file = output_path(output_file) | |
| 1024 | |
| 1025 if not spec.has_key('libraries'): | |
| 1026 spec['libraries'] = [] | |
| 1027 | |
| 1028 # Add dependent static library targets to the 'libraries' value. | |
| 1029 deps = spec.get('dependencies', []) | |
| 1030 spec['scons_dependencies'] = [] | |
| 1031 for d in deps: | |
| 1032 td = target_dicts[d] | |
| 1033 target_name = td['target_name'] | |
| 1034 spec['scons_dependencies'].append("Alias('%s')" % target_name) | |
| 1035 if td['type'] in ('static_library', 'shared_library'): | |
| 1036 libname = td.get('product_name', target_name) | |
| 1037 spec['libraries'].append('lib' + libname) | |
| 1038 if td['type'] == 'loadable_module': | |
| 1039 prereqs = spec.get('scons_prerequisites', []) | |
| 1040 # TODO: parameterize with <(SHARED_LIBRARY_*) variables? | |
| 1041 td_target = SCons.Target(td) | |
| 1042 td_target.target_prefix = '${SHLIBPREFIX}' | |
| 1043 td_target.target_suffix = '${SHLIBSUFFIX}' | |
| 1044 | |
| 1045 GenerateSConscript(output_file, spec, build_file, data[build_file]) | |
| 1046 | |
| 1047 if not default_configuration: | |
| 1048 default_configuration = 'Default' | |
| 1049 | |
| 1050 for build_file in sorted(data.keys()): | |
| 1051 path, ext = os.path.splitext(build_file) | |
| 1052 if ext != '.gyp': | |
| 1053 continue | |
| 1054 output_dir, basename = os.path.split(path) | |
| 1055 output_filename = path + '_main' + options.suffix + '.scons' | |
| 1056 | |
| 1057 all_targets = gyp.common.AllTargets(target_list, target_dicts, build_file) | |
| 1058 sconscript_files = {} | |
| 1059 for t in all_targets: | |
| 1060 scons_target = SCons.Target(target_dicts[t]) | |
| 1061 if scons_target.is_ignored: | |
| 1062 continue | |
| 1063 bf, target = gyp.common.ParseQualifiedTarget(t)[:2] | |
| 1064 target_filename = TargetFilename(target, bf, options.suffix) | |
| 1065 tpath = gyp.common.RelativePath(target_filename, output_dir) | |
| 1066 sconscript_files[target] = tpath | |
| 1067 | |
| 1068 output_filename = output_path(output_filename) | |
| 1069 if sconscript_files: | |
| 1070 GenerateSConscriptWrapper(build_file, data[build_file], basename, | |
| 1071 output_filename, sconscript_files, | |
| 1072 default_configuration) | |
| OLD | NEW |