diff options
Diffstat (limited to 'tools/gyp/pylib/gyp/generator/scons.py')
-rw-r--r-- | tools/gyp/pylib/gyp/generator/scons.py | 1072 |
1 files changed, 0 insertions, 1072 deletions
diff --git a/tools/gyp/pylib/gyp/generator/scons.py b/tools/gyp/pylib/gyp/generator/scons.py deleted file mode 100644 index fe7cb581b..000000000 --- a/tools/gyp/pylib/gyp/generator/scons.py +++ /dev/null @@ -1,1072 +0,0 @@ -# Copyright (c) 2012 Google Inc. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import gyp -import gyp.common -import gyp.SCons as SCons -import os.path -import pprint -import re -import subprocess - - -# TODO: remove when we delete the last WriteList() call in this module -WriteList = SCons.WriteList - - -generator_default_variables = { - 'EXECUTABLE_PREFIX': '', - 'EXECUTABLE_SUFFIX': '', - 'STATIC_LIB_PREFIX': '${LIBPREFIX}', - 'SHARED_LIB_PREFIX': '${SHLIBPREFIX}', - 'STATIC_LIB_SUFFIX': '${LIBSUFFIX}', - 'SHARED_LIB_SUFFIX': '${SHLIBSUFFIX}', - 'INTERMEDIATE_DIR': '${INTERMEDIATE_DIR}', - 'SHARED_INTERMEDIATE_DIR': '${SHARED_INTERMEDIATE_DIR}', - 'OS': 'linux', - 'PRODUCT_DIR': '$TOP_BUILDDIR', - 'SHARED_LIB_DIR': '$LIB_DIR', - 'LIB_DIR': '$LIB_DIR', - 'RULE_INPUT_ROOT': '${SOURCE.filebase}', - 'RULE_INPUT_DIRNAME': '${SOURCE.dir}', - 'RULE_INPUT_EXT': '${SOURCE.suffix}', - 'RULE_INPUT_NAME': '${SOURCE.file}', - 'RULE_INPUT_PATH': '${SOURCE.abspath}', - 'CONFIGURATION_NAME': '${CONFIG_NAME}', -} - -# Tell GYP how to process the input for us. -generator_handles_variants = True -generator_wants_absolute_build_file_paths = True - - -def FixPath(path, prefix): - if not os.path.isabs(path) and not path[0] == '$': - path = prefix + path - return path - - -header = """\ -# This file is generated; do not edit. -""" - - -_alias_template = """ -if GetOption('verbose'): - _action = Action([%(action)s]) -else: - _action = Action([%(action)s], %(message)s) -_outputs = env.Alias( - ['_%(target_name)s_action'], - %(inputs)s, - _action -) -env.AlwaysBuild(_outputs) -""" - -_run_as_template = """ -if GetOption('verbose'): - _action = Action([%(action)s]) -else: - _action = Action([%(action)s], %(message)s) -""" - -_run_as_template_suffix = """ -_run_as_target = env.Alias('run_%(target_name)s', target_files, _action) -env.Requires(_run_as_target, [ - Alias('%(target_name)s'), -]) -env.AlwaysBuild(_run_as_target) -""" - -_command_template = """ -if GetOption('verbose'): - _action = Action([%(action)s]) -else: - _action = Action([%(action)s], %(message)s) -_outputs = env.Command( - %(outputs)s, - %(inputs)s, - _action -) -""" - -# This is copied from the default SCons action, updated to handle symlinks. -_copy_action_template = """ -import shutil -import SCons.Action - -def _copy_files_or_dirs_or_symlinks(dest, src): - SCons.Node.FS.invalidate_node_memos(dest) - if SCons.Util.is_List(src) and os.path.isdir(dest): - for file in src: - shutil.copy2(file, dest) - return 0 - elif os.path.islink(src): - linkto = os.readlink(src) - os.symlink(linkto, dest) - return 0 - elif os.path.isfile(src): - return shutil.copy2(src, dest) - else: - return shutil.copytree(src, dest, 1) - -def _copy_files_or_dirs_or_symlinks_str(dest, src): - return 'Copying %s to %s ...' % (src, dest) - -GYPCopy = SCons.Action.ActionFactory(_copy_files_or_dirs_or_symlinks, - _copy_files_or_dirs_or_symlinks_str, - convert=str) -""" - -_rule_template = """ -%(name)s_additional_inputs = %(inputs)s -%(name)s_outputs = %(outputs)s -def %(name)s_emitter(target, source, env): - return (%(name)s_outputs, source + %(name)s_additional_inputs) -if GetOption('verbose'): - %(name)s_action = Action([%(action)s]) -else: - %(name)s_action = Action([%(action)s], %(message)s) -env['BUILDERS']['%(name)s'] = Builder(action=%(name)s_action, - emitter=%(name)s_emitter) - -_outputs = [] -_processed_input_files = [] -for infile in input_files: - if (type(infile) == type('') - and not os.path.isabs(infile) - and not infile[0] == '$'): - infile = %(src_dir)r + infile - if str(infile).endswith('.%(extension)s'): - _generated = env.%(name)s(infile) - env.Precious(_generated) - _outputs.append(_generated) - %(process_outputs_as_sources_line)s - else: - _processed_input_files.append(infile) -prerequisites.extend(_outputs) -input_files = _processed_input_files -""" - -_spawn_hack = """ -import re -import SCons.Platform.posix -needs_shell = re.compile('["\\'><!^&]') -def gyp_spawn(sh, escape, cmd, args, env): - def strip_scons_quotes(arg): - if arg[0] == '"' and arg[-1] == '"': - return arg[1:-1] - return arg - stripped_args = [strip_scons_quotes(a) for a in args] - if needs_shell.search(' '.join(stripped_args)): - return SCons.Platform.posix.exec_spawnvpe([sh, '-c', ' '.join(args)], env) - else: - return SCons.Platform.posix.exec_spawnvpe(stripped_args, env) -""" - - -def EscapeShellArgument(s): - """Quotes an argument so that it will be interpreted literally by a POSIX - shell. Taken from - http://stackoverflow.com/questions/35817/whats-the-best-way-to-escape-ossystem-calls-in-python - """ - return "'" + s.replace("'", "'\\''") + "'" - - -def InvertNaiveSConsQuoting(s): - """SCons tries to "help" with quoting by naively putting double-quotes around - command-line arguments containing space or tab, which is broken for all - but trivial cases, so we undo it. (See quote_spaces() in Subst.py)""" - if ' ' in s or '\t' in s: - # Then SCons will put double-quotes around this, so add our own quotes - # to close its quotes at the beginning and end. - s = '"' + s + '"' - return s - - -def EscapeSConsVariableExpansion(s): - """SCons has its own variable expansion syntax using $. We must escape it for - strings to be interpreted literally. For some reason this requires four - dollar signs, not two, even without the shell involved.""" - return s.replace('$', '$$$$') - - -def EscapeCppDefine(s): - """Escapes a CPP define so that it will reach the compiler unaltered.""" - s = EscapeShellArgument(s) - s = InvertNaiveSConsQuoting(s) - s = EscapeSConsVariableExpansion(s) - return s - - -def GenerateConfig(fp, config, indent='', src_dir=''): - """ - Generates SCons dictionary items for a gyp configuration. - - This provides the main translation between the (lower-case) gyp settings - keywords and the (upper-case) SCons construction variables. - """ - var_mapping = { - 'ASFLAGS' : 'asflags', - 'CCFLAGS' : 'cflags', - 'CFLAGS' : 'cflags_c', - 'CXXFLAGS' : 'cflags_cc', - 'CPPDEFINES' : 'defines', - 'CPPPATH' : 'include_dirs', - # Add the ldflags value to $LINKFLAGS, but not $SHLINKFLAGS. - # SCons defines $SHLINKFLAGS to incorporate $LINKFLAGS, so - # listing both here would case 'ldflags' to get appended to - # both, and then have it show up twice on the command line. - 'LINKFLAGS' : 'ldflags', - } - postamble='\n%s],\n' % indent - for scons_var in sorted(var_mapping.keys()): - gyp_var = var_mapping[scons_var] - value = config.get(gyp_var) - if value: - if gyp_var in ('defines',): - value = [EscapeCppDefine(v) for v in value] - if gyp_var in ('include_dirs',): - if src_dir and not src_dir.endswith('/'): - src_dir += '/' - result = [] - for v in value: - v = FixPath(v, src_dir) - # Force SCons to evaluate the CPPPATH directories at - # SConscript-read time, so delayed evaluation of $SRC_DIR - # doesn't point it to the --generator-output= directory. - result.append('env.Dir(%r)' % v) - value = result - else: - value = map(repr, value) - WriteList(fp, - value, - prefix=indent, - preamble='%s%s = [\n ' % (indent, scons_var), - postamble=postamble) - - -def GenerateSConscript(output_filename, spec, build_file, build_file_data): - """ - Generates a SConscript file for a specific target. - - This generates a SConscript file suitable for building any or all of - the target's configurations. - - A SConscript file may be called multiple times to generate targets for - multiple configurations. Consequently, it needs to be ready to build - the target for any requested configuration, and therefore contains - information about the settings for all configurations (generated into - the SConscript file at gyp configuration time) as well as logic for - selecting (at SCons build time) the specific configuration being built. - - The general outline of a generated SConscript file is: - - -- Header - - -- Import 'env'. This contains a $CONFIG_NAME construction - variable that specifies what configuration to build - (e.g. Debug, Release). - - -- Configurations. This is a dictionary with settings for - the different configurations (Debug, Release) under which this - target can be built. The values in the dictionary are themselves - dictionaries specifying what construction variables should added - to the local copy of the imported construction environment - (Append), should be removed (FilterOut), and should outright - replace the imported values (Replace). - - -- Clone the imported construction environment and update - with the proper configuration settings. - - -- Initialize the lists of the targets' input files and prerequisites. - - -- Target-specific actions and rules. These come after the - input file and prerequisite initializations because the - outputs of the actions and rules may affect the input file - list (process_outputs_as_sources) and get added to the list of - prerequisites (so that they're guaranteed to be executed before - building the target). - - -- Call the Builder for the target itself. - - -- Arrange for any copies to be made into installation directories. - - -- Set up the {name} Alias (phony Node) for the target as the - primary handle for building all of the target's pieces. - - -- Use env.Require() to make sure the prerequisites (explicitly - specified, but also including the actions and rules) are built - before the target itself. - - -- Return the {name} Alias to the calling SConstruct file - so it can be added to the list of default targets. - """ - scons_target = SCons.Target(spec) - - gyp_dir = os.path.dirname(output_filename) - if not gyp_dir: - gyp_dir = '.' - gyp_dir = os.path.abspath(gyp_dir) - - output_dir = os.path.dirname(output_filename) - src_dir = build_file_data['_DEPTH'] - src_dir_rel = gyp.common.RelativePath(src_dir, output_dir) - subdir = gyp.common.RelativePath(os.path.dirname(build_file), src_dir) - src_subdir = '$SRC_DIR/' + subdir - src_subdir_ = src_subdir + '/' - - component_name = os.path.splitext(os.path.basename(build_file))[0] - target_name = spec['target_name'] - - if not os.path.exists(gyp_dir): - os.makedirs(gyp_dir) - fp = open(output_filename, 'w') - fp.write(header) - - fp.write('\nimport os\n') - fp.write('\nImport("env")\n') - - # - fp.write('\n') - fp.write('env = env.Clone(COMPONENT_NAME=%s,\n' % repr(component_name)) - fp.write(' TARGET_NAME=%s)\n' % repr(target_name)) - - # - for config in spec['configurations'].itervalues(): - if config.get('scons_line_length'): - fp.write(_spawn_hack) - break - - # - indent = ' ' * 12 - fp.write('\n') - fp.write('configurations = {\n') - for config_name, config in spec['configurations'].iteritems(): - fp.write(' \'%s\' : {\n' % config_name) - - fp.write(' \'Append\' : dict(\n') - GenerateConfig(fp, config, indent, src_subdir) - libraries = spec.get('libraries') - if libraries: - WriteList(fp, - map(repr, libraries), - prefix=indent, - preamble='%sLIBS = [\n ' % indent, - postamble='\n%s],\n' % indent) - fp.write(' ),\n') - - fp.write(' \'FilterOut\' : dict(\n' ) - for key, var in config.get('scons_remove', {}).iteritems(): - fp.write(' %s = %s,\n' % (key, repr(var))) - fp.write(' ),\n') - - fp.write(' \'Replace\' : dict(\n' ) - scons_settings = config.get('scons_variable_settings', {}) - for key in sorted(scons_settings.keys()): - val = pprint.pformat(scons_settings[key]) - fp.write(' %s = %s,\n' % (key, val)) - if 'c++' in spec.get('link_languages', []): - fp.write(' %s = %s,\n' % ('LINK', repr('$CXX'))) - if config.get('scons_line_length'): - fp.write(' SPAWN = gyp_spawn,\n') - fp.write(' ),\n') - - fp.write(' \'ImportExternal\' : [\n' ) - for var in config.get('scons_import_variables', []): - fp.write(' %s,\n' % repr(var)) - fp.write(' ],\n') - - fp.write(' \'PropagateExternal\' : [\n' ) - for var in config.get('scons_propagate_variables', []): - fp.write(' %s,\n' % repr(var)) - fp.write(' ],\n') - - fp.write(' },\n') - fp.write('}\n') - - fp.write('\n' - 'config = configurations[env[\'CONFIG_NAME\']]\n' - 'env.Append(**config[\'Append\'])\n' - 'env.FilterOut(**config[\'FilterOut\'])\n' - 'env.Replace(**config[\'Replace\'])\n') - - fp.write('\n' - '# Scons forces -fPIC for SHCCFLAGS on some platforms.\n' - '# Disable that so we can control it from cflags in gyp.\n' - '# Note that Scons itself is inconsistent with its -fPIC\n' - '# setting. SHCCFLAGS forces -fPIC, and SHCFLAGS does not.\n' - '# This will make SHCCFLAGS consistent with SHCFLAGS.\n' - 'env[\'SHCCFLAGS\'] = [\'$CCFLAGS\']\n') - - fp.write('\n' - 'for _var in config[\'ImportExternal\']:\n' - ' if _var in ARGUMENTS:\n' - ' env[_var] = ARGUMENTS[_var]\n' - ' elif _var in os.environ:\n' - ' env[_var] = os.environ[_var]\n' - 'for _var in config[\'PropagateExternal\']:\n' - ' if _var in ARGUMENTS:\n' - ' env[_var] = ARGUMENTS[_var]\n' - ' elif _var in os.environ:\n' - ' env[\'ENV\'][_var] = os.environ[_var]\n') - - fp.write('\n' - "env['ENV']['LD_LIBRARY_PATH'] = env.subst('$LIB_DIR')\n") - - # - #fp.write("\nif env.has_key('CPPPATH'):\n") - #fp.write(" env['CPPPATH'] = map(env.Dir, env['CPPPATH'])\n") - - variants = spec.get('variants', {}) - for setting in sorted(variants.keys()): - if_fmt = 'if ARGUMENTS.get(%s) not in (None, \'0\'):\n' - fp.write('\n') - fp.write(if_fmt % repr(setting.upper())) - fp.write(' env.AppendUnique(\n') - GenerateConfig(fp, variants[setting], indent, src_subdir) - fp.write(' )\n') - - # - scons_target.write_input_files(fp) - - fp.write('\n') - fp.write('target_files = []\n') - prerequisites = spec.get('scons_prerequisites', []) - fp.write('prerequisites = %s\n' % pprint.pformat(prerequisites)) - - actions = spec.get('actions', []) - for action in actions: - a = ['cd', src_subdir, '&&'] + action['action'] - message = action.get('message') - if message: - message = repr(message) - inputs = [FixPath(f, src_subdir_) for f in action.get('inputs', [])] - outputs = [FixPath(f, src_subdir_) for f in action.get('outputs', [])] - if outputs: - template = _command_template - else: - template = _alias_template - fp.write(template % { - 'inputs' : pprint.pformat(inputs), - 'outputs' : pprint.pformat(outputs), - 'action' : pprint.pformat(a), - 'message' : message, - 'target_name': target_name, - }) - if int(action.get('process_outputs_as_sources', 0)): - fp.write('input_files.extend(_outputs)\n') - fp.write('prerequisites.extend(_outputs)\n') - fp.write('target_files.extend(_outputs)\n') - - rules = spec.get('rules', []) - for rule in rules: - name = re.sub('[^a-zA-Z0-9_]', '_', rule['rule_name']) - message = rule.get('message') - if message: - message = repr(message) - if int(rule.get('process_outputs_as_sources', 0)): - poas_line = '_processed_input_files.extend(_generated)' - else: - poas_line = '_processed_input_files.append(infile)' - inputs = [FixPath(f, src_subdir_) for f in rule.get('inputs', [])] - outputs = [FixPath(f, src_subdir_) for f in rule.get('outputs', [])] - # Skip a rule with no action and no inputs. - if 'action' not in rule and not rule.get('rule_sources', []): - continue - a = ['cd', src_subdir, '&&'] + rule['action'] - fp.write(_rule_template % { - 'inputs' : pprint.pformat(inputs), - 'outputs' : pprint.pformat(outputs), - 'action' : pprint.pformat(a), - 'extension' : rule['extension'], - 'name' : name, - 'message' : message, - 'process_outputs_as_sources_line' : poas_line, - 'src_dir' : src_subdir_, - }) - - scons_target.write_target(fp, src_subdir) - - copies = spec.get('copies', []) - if copies: - fp.write(_copy_action_template) - for copy in copies: - destdir = None - files = None - try: - destdir = copy['destination'] - except KeyError, e: - gyp.common.ExceptionAppend( - e, - "Required 'destination' key missing for 'copies' in %s." % build_file) - raise - try: - files = copy['files'] - except KeyError, e: - gyp.common.ExceptionAppend( - e, "Required 'files' key missing for 'copies' in %s." % build_file) - raise - if not files: - # TODO: should probably add a (suppressible) warning; - # a null file list may be unintentional. - continue - if not destdir: - raise Exception( - "Required 'destination' key is empty for 'copies' in %s." % build_file) - - fmt = ('\n' - '_outputs = env.Command(%s,\n' - ' %s,\n' - ' GYPCopy(\'$TARGET\', \'$SOURCE\'))\n') - for f in copy['files']: - # Remove trailing separators so basename() acts like Unix basename and - # always returns the last element, whether a file or dir. Without this, - # only the contents, not the directory itself, are copied (and nothing - # might be copied if dest already exists, since scons thinks nothing needs - # to be done). - dest = os.path.join(destdir, os.path.basename(f.rstrip(os.sep))) - f = FixPath(f, src_subdir_) - dest = FixPath(dest, src_subdir_) - fp.write(fmt % (repr(dest), repr(f))) - fp.write('target_files.extend(_outputs)\n') - - run_as = spec.get('run_as') - if run_as: - action = run_as.get('action', []) - working_directory = run_as.get('working_directory') - if not working_directory: - working_directory = gyp_dir - else: - if not os.path.isabs(working_directory): - working_directory = os.path.normpath(os.path.join(gyp_dir, - working_directory)) - if run_as.get('environment'): - for (key, val) in run_as.get('environment').iteritems(): - action = ['%s="%s"' % (key, val)] + action - action = ['cd', '"%s"' % working_directory, '&&'] + action - fp.write(_run_as_template % { - 'action' : pprint.pformat(action), - 'message' : run_as.get('message', ''), - }) - - fmt = "\ngyp_target = env.Alias('%s', target_files)\n" - fp.write(fmt % target_name) - - dependencies = spec.get('scons_dependencies', []) - if dependencies: - WriteList(fp, dependencies, preamble='dependencies = [\n ', - postamble='\n]\n') - fp.write('env.Requires(target_files, dependencies)\n') - fp.write('env.Requires(gyp_target, dependencies)\n') - fp.write('for prerequisite in prerequisites:\n') - fp.write(' env.Requires(prerequisite, dependencies)\n') - fp.write('env.Requires(gyp_target, prerequisites)\n') - - if run_as: - fp.write(_run_as_template_suffix % { - 'target_name': target_name, - }) - - fp.write('Return("gyp_target")\n') - - fp.close() - - -############################################################################# -# TEMPLATE BEGIN - -_wrapper_template = """\ - -__doc__ = ''' -Wrapper configuration for building this entire "solution," -including all the specific targets in various *.scons files. -''' - -import os -import sys - -import SCons.Environment -import SCons.Util - -def GetProcessorCount(): - ''' - Detects the number of CPUs on the system. Adapted form: - http://codeliberates.blogspot.com/2008/05/detecting-cpuscores-in-python.html - ''' - # Linux, Unix and Mac OS X: - if hasattr(os, 'sysconf'): - if os.sysconf_names.has_key('SC_NPROCESSORS_ONLN'): - # Linux and Unix or Mac OS X with python >= 2.5: - return os.sysconf('SC_NPROCESSORS_ONLN') - else: # Mac OS X with Python < 2.5: - return int(os.popen2("sysctl -n hw.ncpu")[1].read()) - # Windows: - if os.environ.has_key('NUMBER_OF_PROCESSORS'): - return max(int(os.environ.get('NUMBER_OF_PROCESSORS', '1')), 1) - return 1 # Default - -# Support PROGRESS= to show progress in different ways. -p = ARGUMENTS.get('PROGRESS') -if p == 'spinner': - Progress(['/\\r', '|\\r', '\\\\\\r', '-\\r'], - interval=5, - file=open('/dev/tty', 'w')) -elif p == 'name': - Progress('$TARGET\\r', overwrite=True, file=open('/dev/tty', 'w')) - -# Set the default -j value based on the number of processors. -SetOption('num_jobs', GetProcessorCount() + 1) - -# Have SCons use its cached dependency information. -SetOption('implicit_cache', 1) - -# Only re-calculate MD5 checksums if a timestamp has changed. -Decider('MD5-timestamp') - -# Since we set the -j value by default, suppress SCons warnings about being -# unable to support parallel build on versions of Python with no threading. -default_warnings = ['no-no-parallel-support'] -SetOption('warn', default_warnings + GetOption('warn')) - -AddOption('--mode', nargs=1, dest='conf_list', default=[], - action='append', help='Configuration to build.') - -AddOption('--verbose', dest='verbose', default=False, - action='store_true', help='Verbose command-line output.') - - -# -sconscript_file_map = %(sconscript_files)s - -class LoadTarget: - ''' - Class for deciding if a given target sconscript is to be included - based on a list of included target names, optionally prefixed with '-' - to exclude a target name. - ''' - def __init__(self, load): - ''' - Initialize a class with a list of names for possible loading. - - Arguments: - load: list of elements in the LOAD= specification - ''' - self.included = set([c for c in load if not c.startswith('-')]) - self.excluded = set([c[1:] for c in load if c.startswith('-')]) - - if not self.included: - self.included = set(['all']) - - def __call__(self, target): - ''' - Returns True if the specified target's sconscript file should be - loaded, based on the initialized included and excluded lists. - ''' - return (target in self.included or - ('all' in self.included and not target in self.excluded)) - -if 'LOAD' in ARGUMENTS: - load = ARGUMENTS['LOAD'].split(',') -else: - load = [] -load_target = LoadTarget(load) - -sconscript_files = [] -for target, sconscript in sconscript_file_map.iteritems(): - if load_target(target): - sconscript_files.append(sconscript) - - -target_alias_list= [] - -conf_list = GetOption('conf_list') -if conf_list: - # In case the same --mode= value was specified multiple times. - conf_list = list(set(conf_list)) -else: - conf_list = [%(default_configuration)r] - -sconsbuild_dir = Dir(%(sconsbuild_dir)s) - - -def FilterOut(self, **kw): - kw = SCons.Environment.copy_non_reserved_keywords(kw) - for key, val in kw.items(): - envval = self.get(key, None) - if envval is None: - # No existing variable in the environment, so nothing to delete. - continue - - for vremove in val: - # Use while not if, so we can handle duplicates. - while vremove in envval: - envval.remove(vremove) - - self[key] = envval - - # TODO(sgk): SCons.Environment.Append() has much more logic to deal - # with various types of values. We should handle all those cases in here - # too. (If variable is a dict, etc.) - - -non_compilable_suffixes = { - 'LINUX' : set([ - '.bdic', - '.css', - '.dat', - '.fragment', - '.gperf', - '.h', - '.hh', - '.hpp', - '.html', - '.hxx', - '.idl', - '.in', - '.in0', - '.in1', - '.js', - '.mk', - '.rc', - '.sigs', - '', - ]), - 'WINDOWS' : set([ - '.h', - '.hh', - '.hpp', - '.dat', - '.idl', - '.in', - '.in0', - '.in1', - ]), -} - -def compilable(env, file): - base, ext = os.path.splitext(str(file)) - if ext in non_compilable_suffixes[env['TARGET_PLATFORM']]: - return False - return True - -def compilable_files(env, sources): - return [x for x in sources if compilable(env, x)] - -def GypProgram(env, target, source, *args, **kw): - source = compilable_files(env, source) - result = env.Program(target, source, *args, **kw) - if env.get('INCREMENTAL'): - env.Precious(result) - return result - -def GypTestProgram(env, target, source, *args, **kw): - source = compilable_files(env, source) - result = env.Program(target, source, *args, **kw) - if env.get('INCREMENTAL'): - env.Precious(*result) - return result - -def GypLibrary(env, target, source, *args, **kw): - source = compilable_files(env, source) - result = env.Library(target, source, *args, **kw) - return result - -def GypLoadableModule(env, target, source, *args, **kw): - source = compilable_files(env, source) - result = env.LoadableModule(target, source, *args, **kw) - return result - -def GypStaticLibrary(env, target, source, *args, **kw): - source = compilable_files(env, source) - result = env.StaticLibrary(target, source, *args, **kw) - return result - -def GypSharedLibrary(env, target, source, *args, **kw): - source = compilable_files(env, source) - result = env.SharedLibrary(target, source, *args, **kw) - if env.get('INCREMENTAL'): - env.Precious(result) - return result - -def add_gyp_methods(env): - env.AddMethod(GypProgram) - env.AddMethod(GypTestProgram) - env.AddMethod(GypLibrary) - env.AddMethod(GypLoadableModule) - env.AddMethod(GypStaticLibrary) - env.AddMethod(GypSharedLibrary) - - env.AddMethod(FilterOut) - - env.AddMethod(compilable) - - -base_env = Environment( - tools = %(scons_tools)s, - INTERMEDIATE_DIR='$OBJ_DIR/${COMPONENT_NAME}/_${TARGET_NAME}_intermediate', - LIB_DIR='$TOP_BUILDDIR/lib', - OBJ_DIR='$TOP_BUILDDIR/obj', - SCONSBUILD_DIR=sconsbuild_dir.abspath, - SHARED_INTERMEDIATE_DIR='$OBJ_DIR/_global_intermediate', - SRC_DIR=Dir(%(src_dir)r), - TARGET_PLATFORM='LINUX', - TOP_BUILDDIR='$SCONSBUILD_DIR/$CONFIG_NAME', - LIBPATH=['$LIB_DIR'], -) - -if not GetOption('verbose'): - base_env.SetDefault( - ARCOMSTR='Creating library $TARGET', - ASCOMSTR='Assembling $TARGET', - CCCOMSTR='Compiling $TARGET', - CONCATSOURCECOMSTR='ConcatSource $TARGET', - CXXCOMSTR='Compiling $TARGET', - LDMODULECOMSTR='Building loadable module $TARGET', - LINKCOMSTR='Linking $TARGET', - MANIFESTCOMSTR='Updating manifest for $TARGET', - MIDLCOMSTR='Compiling IDL $TARGET', - PCHCOMSTR='Precompiling $TARGET', - RANLIBCOMSTR='Indexing $TARGET', - RCCOMSTR='Compiling resource $TARGET', - SHCCCOMSTR='Compiling $TARGET', - SHCXXCOMSTR='Compiling $TARGET', - SHLINKCOMSTR='Linking $TARGET', - SHMANIFESTCOMSTR='Updating manifest for $TARGET', - ) - -add_gyp_methods(base_env) - -for conf in conf_list: - env = base_env.Clone(CONFIG_NAME=conf) - SConsignFile(env.File('$TOP_BUILDDIR/.sconsign').abspath) - for sconscript in sconscript_files: - target_alias = env.SConscript(sconscript, exports=['env']) - if target_alias: - target_alias_list.extend(target_alias) - -Default(Alias('all', target_alias_list)) - -help_fmt = ''' -Usage: hammer [SCONS_OPTIONS] [VARIABLES] [TARGET] ... - -Local command-line build options: - --mode=CONFIG Configuration to build: - --mode=Debug [default] - --mode=Release - --verbose Print actual executed command lines. - -Supported command-line build variables: - LOAD=[module,...] Comma-separated list of components to load in the - dependency graph ('-' prefix excludes) - PROGRESS=type Display a progress indicator: - name: print each evaluated target name - spinner: print a spinner every 5 targets - -The following TARGET names can also be used as LOAD= module names: - -%%s -''' - -if GetOption('help'): - def columnar_text(items, width=78, indent=2, sep=2): - result = [] - colwidth = max(map(len, items)) + sep - cols = (width - indent) / colwidth - if cols < 1: - cols = 1 - rows = (len(items) + cols - 1) / cols - indent = '%%*s' %% (indent, '') - sep = indent - for row in xrange(0, rows): - result.append(sep) - for i in xrange(row, len(items), rows): - result.append('%%-*s' %% (colwidth, items[i])) - sep = '\\n' + indent - result.append('\\n') - return ''.join(result) - - load_list = set(sconscript_file_map.keys()) - target_aliases = set(map(str, target_alias_list)) - - common = load_list and target_aliases - load_only = load_list - common - target_only = target_aliases - common - help_text = [help_fmt %% columnar_text(sorted(list(common)))] - if target_only: - fmt = "The following are additional TARGET names:\\n\\n%%s\\n" - help_text.append(fmt %% columnar_text(sorted(list(target_only)))) - if load_only: - fmt = "The following are additional LOAD= module names:\\n\\n%%s\\n" - help_text.append(fmt %% columnar_text(sorted(list(load_only)))) - Help(''.join(help_text)) -""" - -# TEMPLATE END -############################################################################# - - -def GenerateSConscriptWrapper(build_file, build_file_data, name, - output_filename, sconscript_files, - default_configuration): - """ - Generates the "wrapper" SConscript file (analogous to the Visual Studio - solution) that calls all the individual target SConscript files. - """ - output_dir = os.path.dirname(output_filename) - src_dir = build_file_data['_DEPTH'] - src_dir_rel = gyp.common.RelativePath(src_dir, output_dir) - if not src_dir_rel: - src_dir_rel = '.' - scons_settings = build_file_data.get('scons_settings', {}) - sconsbuild_dir = scons_settings.get('sconsbuild_dir', '#') - scons_tools = scons_settings.get('tools', ['default']) - - sconscript_file_lines = ['dict('] - for target in sorted(sconscript_files.keys()): - sconscript = sconscript_files[target] - sconscript_file_lines.append(' %s = %r,' % (target, sconscript)) - sconscript_file_lines.append(')') - - fp = open(output_filename, 'w') - fp.write(header) - fp.write(_wrapper_template % { - 'default_configuration' : default_configuration, - 'name' : name, - 'scons_tools' : repr(scons_tools), - 'sconsbuild_dir' : repr(sconsbuild_dir), - 'sconscript_files' : '\n'.join(sconscript_file_lines), - 'src_dir' : src_dir_rel, - }) - fp.close() - - # Generate the SConstruct file that invokes the wrapper SConscript. - dir, fname = os.path.split(output_filename) - SConstruct = os.path.join(dir, 'SConstruct') - fp = open(SConstruct, 'w') - fp.write(header) - fp.write('SConscript(%s)\n' % repr(fname)) - fp.close() - - -def TargetFilename(target, build_file=None, output_suffix=''): - """Returns the .scons file name for the specified target. - """ - if build_file is None: - build_file, target = gyp.common.ParseQualifiedTarget(target)[:2] - output_file = os.path.join(os.path.dirname(build_file), - target + output_suffix + '.scons') - return output_file - - -def PerformBuild(data, configurations, params): - options = params['options'] - - # Due to the way we test gyp on the chromium typbots - # we need to look for 'scons.py' as well as the more common 'scons' - # TODO(sbc): update the trybots to have a more normal install - # of scons. - scons = 'scons' - paths = os.environ['PATH'].split(os.pathsep) - for scons_name in ['scons', 'scons.py']: - for path in paths: - test_scons = os.path.join(path, scons_name) - print 'looking for: %s' % test_scons - if os.path.exists(test_scons): - print "found scons: %s" % scons - scons = test_scons - break - - for config in configurations: - arguments = [scons, '-C', options.toplevel_dir, '--mode=%s' % config] - print "Building [%s]: %s" % (config, arguments) - subprocess.check_call(arguments) - - -def GenerateOutput(target_list, target_dicts, data, params): - """ - Generates all the output files for the specified targets. - """ - options = params['options'] - - if options.generator_output: - def output_path(filename): - return filename.replace(params['cwd'], options.generator_output) - else: - def output_path(filename): - return filename - - default_configuration = None - - for qualified_target in target_list: - spec = target_dicts[qualified_target] - if spec['toolset'] != 'target': - raise Exception( - 'Multiple toolsets not supported in scons build (target %s)' % - qualified_target) - scons_target = SCons.Target(spec) - if scons_target.is_ignored: - continue - - # TODO: assumes the default_configuration of the first target - # non-Default target is the correct default for all targets. - # Need a better model for handle variation between targets. - if (not default_configuration and - spec['default_configuration'] != 'Default'): - default_configuration = spec['default_configuration'] - - build_file, target = gyp.common.ParseQualifiedTarget(qualified_target)[:2] - output_file = TargetFilename(target, build_file, options.suffix) - if options.generator_output: - output_file = output_path(output_file) - - if not spec.has_key('libraries'): - spec['libraries'] = [] - - # Add dependent static library targets to the 'libraries' value. - deps = spec.get('dependencies', []) - spec['scons_dependencies'] = [] - for d in deps: - td = target_dicts[d] - target_name = td['target_name'] - spec['scons_dependencies'].append("Alias('%s')" % target_name) - if td['type'] in ('static_library', 'shared_library'): - libname = td.get('product_name', target_name) - spec['libraries'].append('lib' + libname) - if td['type'] == 'loadable_module': - prereqs = spec.get('scons_prerequisites', []) - # TODO: parameterize with <(SHARED_LIBRARY_*) variables? - td_target = SCons.Target(td) - td_target.target_prefix = '${SHLIBPREFIX}' - td_target.target_suffix = '${SHLIBSUFFIX}' - - GenerateSConscript(output_file, spec, build_file, data[build_file]) - - if not default_configuration: - default_configuration = 'Default' - - for build_file in sorted(data.keys()): - path, ext = os.path.splitext(build_file) - if ext != '.gyp': - continue - output_dir, basename = os.path.split(path) - output_filename = path + '_main' + options.suffix + '.scons' - - all_targets = gyp.common.AllTargets(target_list, target_dicts, build_file) - sconscript_files = {} - for t in all_targets: - scons_target = SCons.Target(target_dicts[t]) - if scons_target.is_ignored: - continue - bf, target = gyp.common.ParseQualifiedTarget(t)[:2] - target_filename = TargetFilename(target, bf, options.suffix) - tpath = gyp.common.RelativePath(target_filename, output_dir) - sconscript_files[target] = tpath - - output_filename = output_path(output_filename) - if sconscript_files: - GenerateSConscriptWrapper(build_file, data[build_file], basename, - output_filename, sconscript_files, - default_configuration) |