summaryrefslogtreecommitdiffstats
path: root/site_scons
diff options
context:
space:
mode:
authorbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-04 06:10:23 +0000
committerbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-04 06:10:23 +0000
commit1283ba4fe600d3c8cb32b494cbba96fb1eb106e4 (patch)
tree984323cbc7d5db84ecb9317f1c5e10dd07874026 /site_scons
parent7199517ad5241497f9571f93b1f71b95aea95e0b (diff)
downloadchromium_src-1283ba4fe600d3c8cb32b494cbba96fb1eb106e4.zip
chromium_src-1283ba4fe600d3c8cb32b494cbba96fb1eb106e4.tar.gz
chromium_src-1283ba4fe600d3c8cb32b494cbba96fb1eb106e4.tar.bz2
Adding in new software construction toolkit version.
Review URL: http://codereview.chromium.org/9094 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4566 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'site_scons')
-rw-r--r--site_scons/site_init.py209
-rw-r--r--site_scons/site_tools/component_builders.py50
-rw-r--r--site_scons/site_tools/component_setup.py83
-rwxr-xr-xsite_scons/site_tools/component_targets.py264
-rwxr-xr-xsite_scons/site_tools/component_targets_msvs.py461
-rwxr-xr-xsite_scons/site_tools/component_targets_xml.py126
-rw-r--r--site_scons/site_tools/concat_source.py14
-rw-r--r--site_scons/site_tools/defer.py162
-rw-r--r--site_scons/site_tools/environment_tools.py106
-rw-r--r--site_scons/site_tools/publish.py34
-rw-r--r--site_scons/site_tools/replicate.py3
-rw-r--r--site_scons/site_tools/target_platform_mac.py4
-rw-r--r--site_scons/site_tools/visual_studio_solution.py27
13 files changed, 1299 insertions, 244 deletions
diff --git a/site_scons/site_init.py b/site_scons/site_init.py
index 490a1db..369b886 100644
--- a/site_scons/site_init.py
+++ b/site_scons/site_init.py
@@ -37,16 +37,10 @@ for the target environment.
"""
import __builtin__
-import os
import sys
import SCons
-# List of target groups for printing help; modified by AddTargetGroup(); used
-# by BuildEnvironments().
-__target_groups = {}
-
-
def _HostPlatform():
"""Returns the current host platform.
@@ -118,29 +112,17 @@ def _CheckBuildModes(build_modes, environments, host_platform):
all_build_groups[g].append(e['BUILD_TYPE'])
# Add help for build types
- xml_help = SCons.Script.GetOption('xml_help')
- if xml_help:
- help_mode_format = ' <build_mode name="%s"><![CDATA[%s]]></build_mode>\n'
- help_text = '<mode_list>\n'
- else:
- help_text = '''
+ help_text = '''
Use --mode=type to specify the type of build to perform. The following types
may be specified:
'''
- help_mode_format = ' %-16s %s\n'
for build_type in all_build_types:
if build_type not in all_build_groups:
- help_text += help_mode_format % (
+ help_text += ' %-16s %s\n' % (
build_type, build_desc.get(build_type, ''))
- if xml_help:
- help_group_format = (' <build_group name="%s"><![CDATA[%s]]>'
- '</build_group>\n')
- help_text += '</mode_list>\n<group_list>\n'
- else:
- help_group_format = ' %-16s %s\n'
- help_text += '''
+ help_text += '''
The following build groups may also be specified via --mode. Build groups
build one or more of the other build types. The available build groups are:
'''
@@ -148,12 +130,9 @@ build one or more of the other build types. The available build groups are:
groups_sorted = all_build_groups.keys()
groups_sorted.sort()
for g in groups_sorted:
- help_text += help_group_format % (g, ','.join(all_build_groups[g]))
+ help_text += ' %-16s %s\n' % (g, ','.join(all_build_groups[g]))
- if xml_help:
- help_text += '</group_list>\n'
- else:
- help_text += '''
+ help_text += '''
Multiple modes may be specified, separated by commas: --mode=mode1,mode2. If
no mode is specified, the default group will be built. This is equivalent to
specifying --mode=default.
@@ -168,39 +147,62 @@ specifying --mode=default.
'platform.' % mode)
-def _AddTargetHelp():
- """Adds help for the target groups from the global __target_groups."""
- xml_help = SCons.Script.GetOption('xml_help')
- help_text = ''
-
- for alias, description in __target_groups.items():
- items = map(str, SCons.Script.Alias(alias)[0].sources)
- # Remove duplicates from multiple environments
- items = list(set(items))
-
- if items:
- colwidth = max(map(len, items)) + 2
- cols = 77 / colwidth
- if cols < 1:
- cols = 1 # If target names are really long, one per line
- rows = (len(items) + cols - 1) / cols
- items.sort()
- if xml_help:
- help_text += '<target_group name="%s">\n' % alias
- for i in items:
- help_text += ' <build_target name="%s"/>\n' % i
- help_text += '</target_group>\n'
- else:
- help_text += '\nThe following %s:' % description
- for row in range(0, rows):
- help_text += '\n '
- for i in range(row, len(items), rows):
- help_text += '%-*s' % (colwidth, items[i])
- help_text += '\n %s (do all of the above)\n' % alias
+#------------------------------------------------------------------------------
- SCons.Script.Help(help_text)
-#------------------------------------------------------------------------------
+def BuildEnvironmentSConscripts(env):
+ """Evaluates SConscripts for the environment.
+
+ Called by BuildEnvironments().
+ """
+ # Read SConscript for each component
+ # TODO(rspangler): Remove BUILD_COMPONENTS once all projects have
+ # transitioned to the BUILD_SCONSCRIPTS nomenclature.
+ for c in env.SubstList2('$BUILD_SCONSCRIPTS', '$BUILD_COMPONENTS'):
+ # Clone the environment so components can't interfere with each other
+ ec = env.Clone()
+
+ if ec.Entry(c).isdir():
+ # The component is a directory, so assume it contains a SConscript
+ # file.
+ c_dir = ec.Dir(c)
+
+ # Use 'build.scons' as the default filename, but if that doesn't
+ # exist, fall back to 'SConscript'.
+ c_script = c_dir.File('build.scons')
+ if not c_script.exists():
+ c_script = c_dir.File('SConscript')
+ else:
+ # The component is a SConscript file.
+ c_script = ec.File(c)
+ c_dir = c_script.dir
+
+ # Make c_dir a string.
+ c_dir = str(c_dir)
+
+ # Use build_dir differently depending on where the SConscript is.
+ if not ec.RelativePath('$TARGET_ROOT', c_dir).startswith('..'):
+ # The above expression means: if c_dir is $TARGET_ROOT or anything
+ # under it. Going from c_dir to $TARGET_ROOT and dropping the not fails
+ # to include $TARGET_ROOT.
+ # We want to be able to allow people to use addRepository to back things
+ # under $TARGET_ROOT/$OBJ_ROOT with things from above the current
+ # directory. When we are passed a SConscript that is already under
+ # $TARGET_ROOT, we should not use build_dir.
+ ec.SConscript(c_script, exports={'env': ec}, duplicate=0)
+ elif not ec.RelativePath('$MAIN_DIR', c_dir).startswith('..'):
+ # The above expression means: if c_dir is $MAIN_DIR or anything
+ # under it. Going from c_dir to $TARGET_ROOT and dropping the not fails
+ # to include $MAIN_DIR.
+ # Also, if we are passed a SConscript that
+ # is not under $MAIN_DIR, we should fail loudly, because it is unclear how
+ # this will correspond to things under $OBJ_ROOT.
+ ec.SConscript(c_script, build_dir='$OBJ_ROOT/' + c_dir,
+ exports={'env': ec}, duplicate=0)
+ else:
+ raise SCons.Error.UserError(
+ 'Bad location for a SConscript. "%s" is not under '
+ '\$TARGET_ROOT or \$MAIN_DIR' % c_script)
def BuildEnvironments(environments):
@@ -223,7 +225,6 @@ def BuildEnvironments(environments):
List of environments which were actually evaluated (built).
"""
# Get options
- xml_help = SCons.Script.GetOption('xml_help')
build_modes = SCons.Script.GetOption('build_mode')
# TODO(rspangler): Remove support legacy MODE= argument, once everyone has
# transitioned to --mode.
@@ -239,9 +240,6 @@ def BuildEnvironments(environments):
# Check build modes
_CheckBuildModes(build_modes, environments, host_platform)
- if xml_help:
- SCons.Script.Help('<help_from_sconscripts>\n<![CDATA[\n')
-
environments_to_evaluate = []
for e in environments:
if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']):
@@ -251,56 +249,19 @@ def BuildEnvironments(environments):
environments_to_evaluate.append(e)
for e in environments_to_evaluate:
- # Set up for deferred functions and published resources
- e._InitializeComponentBuilders()
- e._InitializeDefer()
- e._InitializePublish()
-
- # Read SConscript for each component
- # TODO(rspangler): Remove BUILD_COMPONENTS once all projects have
- # transitioned to the BUILD_SCONSCRIPTS nomenclature.
- for c in e.get('BUILD_COMPONENTS', []) + e.get('BUILD_SCONSCRIPTS', []):
- # Clone the environment so components can't interfere with each other
- ec = e.Clone()
-
- if ec.Entry(c).isdir():
- # The component is a directory, so assume it contains a SConscript
- # file.
- c_dir = ec.Dir(c)
-
- # Use 'build.scons' as the default filename, but if that doesn't
- # exist, fall back to 'SConscript'.
- c_script = c_dir.File('build.scons')
- if not c_script.exists():
- c_script = c_dir.File('SConscript')
- else:
- # The component is a SConscript file.
- c_script = ec.File(c)
- c_dir = c_script.dir
-
- # TODO(bradnelson): this hack is not in mainline.
- # Need to unify how to do this sort of thing.
- c_dir = str(c_dir)
- if os.path.isabs(c_dir):
- build_dir = None
- else:
- build_dir = '$OBJ_ROOT/' + c_dir
- ec.SConscript(c_script,
- build_dir=build_dir,
- exports={'env': ec},
- duplicate=0)
-
- # Execute deferred functions
- e._ExecuteDefer()
+ # Make this the root environment for deferred functions, so they don't
+ # execute until our call to ExecuteDefer().
+ e.SetDeferRoot()
- if xml_help:
- SCons.Script.Help(']]>\n</help_from_sconscripts>\n')
+ # Defer building the SConscripts, so that other tools can do
+ # per-environment setup first.
+ e.Defer(BuildEnvironmentSConscripts)
- _AddTargetHelp()
+ # Execute deferred functions
+ e.ExecuteDefer()
- # End final help tag
- if xml_help:
- SCons.Script.Help('</help>\n')
+ # Add help on targets.
+ AddTargetHelp()
# Return list of environments actually evaluated
return environments_to_evaluate
@@ -353,17 +314,6 @@ def AddSiteDir(site_dir):
SCons.Node.FS.get_default_fs().SConstruct_dir, site_dir)
-def AddTargetGroup(target_group, description):
- """Adds a target group, used for printing help.
-
- Args:
- target_group: Name of target group. This should be the name of an alias
- which points to other aliases for the specific targets.
- description: Description of the target group.
- """
-
- __target_groups[target_group] = description
-
#------------------------------------------------------------------------------
@@ -380,7 +330,6 @@ Additional options for SCons:
--site-path=DIRLIST Comma-separated list of additional site
directory paths; each is processed as if passed
to --site-dir.
- --xml-help Print help in XML format.
'''
def SiteInitMain():
@@ -394,7 +343,6 @@ def SiteInitMain():
# Let people use new global methods directly.
__builtin__.AddSiteDir = AddSiteDir
__builtin__.BuildEnvironments = BuildEnvironments
- __builtin__.AddTargetGroup = AddTargetGroup
# Legacy method names
# TODO(rspangler): Remove these once they're no longer used anywhere.
__builtin__.BuildComponents = BuildEnvironments
@@ -402,11 +350,16 @@ def SiteInitMain():
# Set list of default tools for component_setup
__builtin__.component_setup_tools = [
+ # Defer must be first so other tools can register environment
+ # setup/cleanup functions.
+ 'defer',
+ # Component_targets must precede component_builders so builders can
+ # define target groups.
+ 'component_targets',
'command_output',
'component_bits',
'component_builders',
'concat_source',
- 'defer',
'environment_tools',
'publish',
'replicate',
@@ -442,16 +395,8 @@ def SiteInitMain():
action='store',
metavar='PATH',
help='comma-separated list of site directories')
- SCons.Script.AddOption(
- '--xml-help',
- dest='xml_help',
- action='store_true',
- help='print help in XML format')
-
- if SCons.Script.GetOption('xml_help'):
- SCons.Script.Help('<?xml version="1.0" encoding="UTF-8" ?>\n<help>\n')
- else:
- SCons.Script.Help(_new_options_help)
+
+ SCons.Script.Help(_new_options_help)
# Check for site path. This is a list of site directories which each are
# processed as if they were passed to --site-dir.
diff --git a/site_scons/site_tools/component_builders.py b/site_scons/site_tools/component_builders.py
index 00de6a7..9580d7e 100644
--- a/site_scons/site_tools/component_builders.py
+++ b/site_scons/site_tools/component_builders.py
@@ -37,13 +37,13 @@ import SCons
__component_list = {}
-def _InitializeComponentBuilders(self):
+def _InitializeComponentBuilders(env):
"""Re-initializes component builders module.
Args:
- self: Parent environment.
+ env: Environment context
"""
- self = self # Silence gpylint
+ env = env # Silence gpylint
__component_list.clear()
@@ -187,6 +187,9 @@ def ComponentPackage(self, package_name, dest_dir, **kwargs):
# Store list of components for this program
env._StoreComponents(package_name)
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(package_name, TARGET_PATH=dest_dir)
+
# Set up deferred call to replicate resources
env.Defer(ComponentPackageDeferred)
@@ -267,7 +270,7 @@ def ComponentLibrary(self, lib_name, *args, **kwargs):
# Install library in intermediate directory, so other libs and programs can
# link against it
- all_outputs += env.Replicate('$COMPONENT_LIBRARY_DIR', need_for_link)
+ all_outputs += env.Replicate('$LIB_DIR', need_for_link)
# Publish output
env.Publish(lib_name, 'run', need_for_run)
@@ -281,6 +284,9 @@ def ComponentLibrary(self, lib_name, *args, **kwargs):
# Store list of components for this library
env._StoreComponents(lib_name)
+ # Let component_targets know this target is available in the current mode.
+ env.SetTargetProperty(lib_name, TARGET_PATH=lib_outputs[0])
+
# If library should publish itself, publish as if it was a program
if env.get('COMPONENT_LIBRARY_PUBLISH'):
env['PROGRAM_BASENAME'] = lib_name
@@ -343,6 +349,17 @@ def ComponentTestProgramDeferred(env):
env.Depends(test_out, all_outputs)
env.ComponentTestOutput('run_' + prog_name, test_out)
+ # Add target properties
+ env.SetTargetProperty(
+ prog_name,
+ # The copy of the program we care about is the one in the tests dir
+ EXE='$TESTS_DIR/$PROGRAM_NAME',
+ RUN_TARGET='run_' + prog_name,
+ RUN_CMDLINE='$COMPONENT_TEST_CMDLINE',
+ RUN_DIR='$TESTS_DIR',
+ TARGET_PATH='$TESTS_DIR/$PROGRAM_NAME',
+ )
+
def ComponentTestProgram(self, prog_name, *args, **kwargs):
"""Pseudo-builder for test program to handle platform-dependent type.
@@ -382,6 +399,9 @@ def ComponentTestProgram(self, prog_name, *args, **kwargs):
# Store list of components for this program
env._StoreComponents(prog_name)
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(prog_name, TARGET_PATH=out_nodes[0])
+
# Set up deferred call to replicate resources and run test
env.Defer(ComponentTestProgramDeferred)
@@ -446,6 +466,9 @@ def ComponentProgram(self, prog_name, *args, **kwargs):
# Store list of components for this program
env._StoreComponents(prog_name)
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(prog_name)
+
# Set up deferred call to replicate resources
env.Defer(ComponentProgramDeferred)
@@ -485,6 +508,9 @@ def ComponentTestOutput(self, test_name, nodes):
for group in groups:
SCons.Script.Alias(group, a)
+ # Let component_targets know this target is available in the current mode
+ self.SetTargetProperty(test_name, TARGET_PATH=nodes[0])
+
# Return the output node
return a
@@ -496,7 +522,10 @@ def generate(env):
"""SCons entry point for this tool."""
env.Replace(
- COMPONENT_LIBRARY_DIR='$TARGET_ROOT/lib',
+ LIB_DIR='$TARGET_ROOT/lib',
+ # TODO(rspangler): Remove legacy COMPONENT_LIBRARY_DIR, once all users
+ # have transitioned to LIB_DIR
+ COMPONENT_LIBRARY_DIR='$LIB_DIR',
STAGING_DIR='$TARGET_ROOT/staging',
TESTS_DIR='$TARGET_ROOT/tests',
TEST_OUTPUT_DIR='$TARGET_ROOT/test_output',
@@ -508,7 +537,7 @@ def generate(env):
# Default test size is large
COMPONENT_TEST_SIZE='large',
# Default timeouts for component tests
- COMPONENT_TEST_TIMEOUT={'large':900, 'medium':450, 'small':180},
+ COMPONENT_TEST_TIMEOUT={'large': 900, 'medium': 450, 'small': 180},
# Tests are enabled by default
COMPONENT_TEST_ENABLED=True,
# Static linking is a sensible default
@@ -517,8 +546,8 @@ def generate(env):
COMPONENT_LIBRARY_PUBLISH=False,
)
env.Append(
- LIBPATH=['$COMPONENT_LIBRARY_DIR'],
- RPATH=['$COMPONENT_LIBRARY_DIR'],
+ LIBPATH=['$LIB_DIR'],
+ RPATH=['$LIB_DIR'],
# Default alias groups for component builders
COMPONENT_PACKAGE_GROUPS=['all_packages'],
@@ -557,8 +586,11 @@ def generate(env):
SCons.Script.Help(' --retest '
'Rerun specified tests, ignoring cached results.\n')
+ # Defer per-environment initialization, but do before building SConscripts
+ env.Defer(_InitializeComponentBuilders)
+ env.Defer('BuildEnvironmentSConscripts', after=_InitializeComponentBuilders)
+
# Add our pseudo-builder methods
- env.AddMethod(_InitializeComponentBuilders)
env.AddMethod(_StoreComponents)
env.AddMethod(ComponentPackage)
env.AddMethod(ComponentObject)
diff --git a/site_scons/site_tools/component_setup.py b/site_scons/site_tools/component_setup.py
index 947a2be..e3c4574 100644
--- a/site_scons/site_tools/component_setup.py
+++ b/site_scons/site_tools/component_setup.py
@@ -63,6 +63,18 @@ def InstallUsingLink(target, source, env):
# Need to force the target and source to be lists of nodes
return SCons.Node.FS.LinkFunc([env.Entry(target)], [env.Entry(source)], env)
+
+def PreEvaluateVariables(env):
+ """Deferred function to pre-evaluate SCons varables for each build mode.
+
+ Args:
+ env: Environment for the current build mode.
+ """
+ # Convert directory variables to strings
+ for var in env.SubstList2('PRE_EVALUATE_DIRS'):
+ env[var] = str(env.Dir('$' + var))
+
+
#------------------------------------------------------------------------------
@@ -108,10 +120,6 @@ def generate(env):
new_lookup_list.append(func)
env.lookup_list = new_lookup_list
- # Add other default tools from our toolkit
- for t in component_setup_tools:
- env.Tool(t)
-
# Cover part of the environment
env.Replace(
# Add a reference to our python executable, so subprocesses can find and
@@ -129,17 +137,30 @@ def generate(env):
# Use install function above, which uses links in preference to copying.
INSTALL = InstallUsingLink,
+ )
+ # Specify defaults for variables where we don't need to force replacement
+ env.SetDefault(
# Environments are in the 'all' group by default
BUILD_GROUPS=['all'],
+ # Directories
DESTINATION_ROOT='$MAIN_DIR/scons-out$HOST_PLATFORM_SUFFIX',
TARGET_ROOT='$DESTINATION_ROOT/$BUILD_TYPE',
OBJ_ROOT='$TARGET_ROOT/obj',
ARTIFACTS_DIR='$TARGET_ROOT/artifacts',
- CPPDEFINES=[],
)
+ # Add default list of variables we should pre-evaluate for each build mode
+ env.Append(PRE_EVALUATE_DIRS = [
+ 'ARTIFACTS_DIR',
+ 'DESTINATION_ROOT',
+ 'OBJ_ROOT',
+ 'SOURCE_ROOT',
+ 'TARGET_ROOT',
+ 'TOOL_ROOT',
+ ])
+
# If a host platform was specified, need to put the SCons output in its own
# destination directory. Different host platforms compile the same files
# different ways, so need their own .sconsign files.
@@ -147,26 +168,6 @@ def generate(env):
if force_host_platform:
env['HOST_PLATFORM_SUFFIX'] = '-' + force_host_platform
- # The following environment replacements use env.Dir() to force immediate
- # evaluation/substitution of SCons variables. They can't be part of the
- # preceding env.Replace() since they they may rely indirectly on variables
- # defined there, and the env.Dir() calls would be evaluated before the
- # env.Replace().
-
- # Set default SOURCE_ROOT if there is none, assuming we're in a local
- # site_scons directory for the project.
- source_root_relative = os.path.normpath(
- os.path.join(os.path.dirname(__file__), '../..'))
- source_root = env.get('SOURCE_ROOT', source_root_relative)
- env['SOURCE_ROOT'] = env.Dir(source_root)
-
- # Make tool root separate from source root so it can be overridden when we
- # have a common location for tools outside of the current clientspec. Need
- # to check if it's defined already, so it can be set prior to this tool
- # being included.
- tool_root = env.get('TOOL_ROOT', '$SOURCE_ROOT')
- env['TOOL_ROOT'] = env.Dir(tool_root)
-
# Put the .sconsign.dblite file in our destination root directory, so that we
# don't pollute the source tree. Use the '_' + sys.platform suffix to prevent
# the .sconsign.dblite from being shared between host platforms, even in the
@@ -193,3 +194,35 @@ def generate(env):
# Note that this currently forces projects which want to override the
# default to do so after including the component_setup tool.
env.Default('$DESTINATION_ROOT')
+
+ # Add other default tools from our toolkit
+ # TODO(rspangler): Currently this needs to be before SOURCE_ROOT in case a
+ # tool needs to redefine it. Need a better way to handle order-dependency
+ # in tool setup.
+ for t in component_setup_tools:
+ env.Tool(t)
+
+ # The following environment replacements use env.Dir() to force immediate
+ # evaluation/substitution of SCons variables. They can't be part of the
+ # preceding env.Replace() since they they may rely indirectly on variables
+ # defined there, and the env.Dir() calls would be evaluated before the
+ # env.Replace().
+
+ # Set default SOURCE_ROOT if there is none, assuming we're in a local
+ # site_scons directory for the project.
+ source_root_relative = os.path.normpath(
+ os.path.join(os.path.dirname(__file__), '../..'))
+ source_root = env.get('SOURCE_ROOT', source_root_relative)
+ env['SOURCE_ROOT'] = env.Dir(source_root).abspath
+
+ # Make tool root separate from source root so it can be overridden when we
+ # have a common location for tools outside of the current clientspec. Need
+ # to check if it's defined already, so it can be set prior to this tool
+ # being included.
+ tool_root = env.get('TOOL_ROOT', '$SOURCE_ROOT')
+ env['TOOL_ROOT'] = env.Dir(tool_root).abspath
+
+ # Defer pre-evaluating some environment variables, but do before building
+ # SConscripts.
+ env.Defer(PreEvaluateVariables)
+ env.Defer('BuildEnvironmentSConscripts', after=PreEvaluateVariables)
diff --git a/site_scons/site_tools/component_targets.py b/site_scons/site_tools/component_targets.py
new file mode 100755
index 0000000..493e0e2
--- /dev/null
+++ b/site_scons/site_tools/component_targets.py
@@ -0,0 +1,264 @@
+#!/usr/bin/python2.4
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Software construction toolkit target management for SCons."""
+
+
+import __builtin__
+import SCons.Script
+
+
+# Dict of target groups (TargetGroup indexed by group name)
+__target_groups = {}
+
+# Dict of targets (Target indexed by target name)
+__targets = {}
+
+# Dict of target modes (TargetMode indexed by mode name)
+__target_modes = {}
+
+#------------------------------------------------------------------------------
+
+
+class TargetGroup(object):
+ """Target group, as used by AddTargetGroup() and GetTargetGroups()."""
+
+ def __init__(self, name, description):
+ """Initializes the target group.
+
+ Args:
+ name: Name of the target group.
+ description: Description of group.
+ """
+ self.name = name
+ self.description = description
+
+ def GetTargetNames(self):
+ """Returns a list of target name strings for the group."""
+ items = map(str, SCons.Script.Alias(self.name)[0].sources)
+ # Remove duplicates from multiple environments
+ return list(set(items))
+
+#------------------------------------------------------------------------------
+
+
+class TargetMode(object):
+ """Target mode, as used by GetTargetModes()."""
+
+ def __init__(self, name, description):
+ """Initializes the target mode.
+
+ Args:
+ name: Name of the target mode.
+ description: Description of mode.
+ """
+ self.name = name
+ self.description = description
+
+ def GetTargetNames(self):
+ """Returns a list of target name strings for the group."""
+ items = map(str, SCons.Script.Alias(self.name)[0].sources)
+ # Remove duplicates from multiple environments
+ return list(set(items))
+
+#------------------------------------------------------------------------------
+
+
+class Target(object):
+ """Target object."""
+
+ def __init__(self, name):
+ """Initializes the target.
+
+ Args:
+ name: Name of the target.
+ """
+ self.name = name
+ self.properties = {} # Global properties
+ self.mode_properties = {} # Dict of modes to mode-specific properties
+
+#------------------------------------------------------------------------------
+
+
+def AddTargetGroup(name, description):
+ """Adds a target group, used for printing help.
+
+ Args:
+ name: Name of target group. This should be the name of an alias which
+ points to other aliases for the specific targets.
+ description: Description of the target group.
+ """
+
+ # Warn if the target group already exists with a different description
+ if (name in __target_groups
+ and __target_groups[name].description != description):
+ print ('Warning: Changing description of target group "%s" from "%s" to '
+ '"%s"' % (name, __target_groups[name].description, description))
+ __target_groups[name].description = description
+ else:
+ __target_groups[name] = TargetGroup(name, description)
+
+
+def GetTargetGroups():
+ """Gets the dict of target groups.
+
+ Returns:
+ The dict of target groups, indexed by group name.
+
+ This dict is not fully populated until after BuildEnvironments() has been
+ called.
+ """
+ return __target_groups
+
+
+def GetTargetModes():
+ """Gets the dict of target modes.
+
+ Returns:
+ The dict of target modes, indexed by mode name.
+
+ This dict is not fully populated until after BuildEnvironments() has been
+ called.
+ """
+ return __target_modes
+
+
+def GetTargets():
+ """Gets the dict of targets.
+
+ Returns:
+ The dict of targets, indexed by target name.
+
+ This dict is not fully populated until after BuildEnvironments() has been
+ called.
+ """
+ return __targets
+
+
+def SetTargetProperty(self, target_name, all_modes=False, **kwargs):
+ """Sets one or more properties for a target.
+
+ Args:
+ self: Environment context.
+ target_name: Name of the target.
+ all_modes: If True, property applies to all modes. If false, it applies
+ only to the current mode (determined by self['BUILD_TYPE']).
+ kwargs: Keyword args are used to set properties. Properties will be
+ converted to strings via env.subst().
+
+ For example:
+ foo_test = env.Program(...)[0]
+ env.SetTargetProperty('foo_test', global=True, DESCRIPTION='Foo test')
+ env.SetTargetProperty('foo_test', EXE=foo_test)
+ """
+ # Get the target
+ if target_name not in __targets:
+ __targets[target_name] = Target(target_name)
+ target = __targets[target_name]
+
+ if all_modes:
+ add_to_dict = target.properties
+ else:
+ mode = self.get('BUILD_TYPE')
+ if mode not in target.mode_properties:
+ target.mode_properties[mode] = {}
+ add_to_dict = target.mode_properties[mode]
+
+ # Add values
+ for k, v in kwargs.items():
+ add_to_dict[k] = self.subst(str(v))
+
+
+def AddTargetHelp():
+ """Adds help for the targets, groups, and modes."""
+ help_text = ''
+
+ for group in GetTargetGroups().values():
+ items = group.GetTargetNames()
+ items.sort()
+ if items:
+ help_text += '\nThe following %s:' % group.description
+ colwidth = max(map(len, items)) + 2
+ cols = 77 / colwidth
+ if cols < 1:
+ cols = 1 # If target names are really long, one per line
+ rows = (len(items) + cols - 1) / cols
+ for row in range(0, rows):
+ help_text += '\n '
+ for i in range(row, len(items), rows):
+ help_text += '%-*s' % (colwidth, items[i])
+ help_text += '\n %s (do all of the above)\n' % group.name
+
+ SCons.Script.Help(help_text)
+
+
+def SetTargetDescription(self, target_name, description):
+ """Convenience function to set a target's global DESCRIPTION property.
+
+ Args:
+ self: Environment context.
+ target_name: Name of the target.
+ description: Description of the target.
+ """
+ self.SetTargetProperty(target_name, all_modes=True, DESCRIPTION=description)
+
+
+def AddTargetMode(env):
+ """Adds the environment as a target mode.
+
+ Args:
+ env: Environment context.
+
+ Called via env.Defer() for each build mode.
+ """
+ # Save the build mode and description
+ mode = env.get('BUILD_TYPE')
+ __target_modes[mode] = TargetMode(mode, env.get('BUILD_TYPE_DESCRIPTION'))
+
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ env = env # Silence gpylint
+
+ __builtin__.AddTargetGroup = AddTargetGroup
+ __builtin__.AddTargetHelp = AddTargetHelp
+ __builtin__.GetTargetGroups = GetTargetGroups
+ __builtin__.GetTargetModes = GetTargetModes
+ __builtin__.GetTargets = GetTargets
+
+ env.AddMethod(SetTargetDescription)
+ env.AddMethod(SetTargetProperty)
+
+ # Defer per-mode setup
+ env.Defer(AddTargetMode)
diff --git a/site_scons/site_tools/component_targets_msvs.py b/site_scons/site_tools/component_targets_msvs.py
new file mode 100755
index 0000000..57be5b3
--- /dev/null
+++ b/site_scons/site_tools/component_targets_msvs.py
@@ -0,0 +1,461 @@
+#!/usr/bin/python2.4
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Visual Studio solution output of component targets for SCons."""
+
+import md5
+import sys
+import xml.dom
+import xml.dom.minidom
+import SCons
+
+
+#------------------------------------------------------------------------------
+
+
+def MakeGuid(name, seed='component_targets_msvs'):
+ """Returns a GUID for the specified target name.
+
+ Args:
+ name: Target name.
+ seed: Seed for MD5 hash.
+ Returns:
+ A GUID-line string calculated from the name and seed.
+
+ This generates something which looks like a GUID, but depends only on the
+ name and seed. This means the same name/seed will always generate the same
+ GUID, so that projects and solutions which refer to each other can explicitly
+ determine the GUID to refer to explicitly. It also means that the GUID will
+ not change when the project for a target is rebuilt.
+ """
+ # Calculate a MD5 signature for the seed and name.
+ d = md5.new(str(seed) + str(name)).hexdigest().upper()
+ # Convert most of the signature to GUID form (discard the rest)
+ guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20]
+ + '-' + d[20:32] + '}')
+ return guid
+
+#------------------------------------------------------------------------------
+
+
+def GetGuidFromVSProject(project_path):
+ """Reads the GUID from a Visual Studio project file.
+
+ Args:
+ project_path: Path to the Visual Studio project file.
+
+ Returns:
+ The GUID string from the file.
+ """
+ doc = xml.dom.minidom.parse(project_path)
+ try:
+ n_root = doc.documentElement
+ if n_root.nodeName != 'VisualStudioProject':
+ raise SCons.Errors.UserError('%s is not a Visual Studio project.' %
+ project_path)
+ return str(n_root.attributes['ProjectGUID'].nodeValue)
+ finally:
+ # Clean up doc
+ doc.unlink()
+
+#------------------------------------------------------------------------------
+
+
+def ComponentVSProjectBuilder(target, source, env):
+ """Visual Studio project builder.
+
+ Args:
+ target: Destination file.
+ source: List of sources to be added to the target.
+ env: Environment context.
+
+ Returns:
+ Zero if successful.
+ """
+ source = source # Silence gpylint
+
+ target_name = env['TARGET_NAME']
+ project_file = target[0].path
+ project_to_main = env.RelativePath(target[0].dir, env.Dir('$MAIN_DIR'),
+ sep='/')
+ hammer_bat = '$(ProjectDir)/%s/hammer.bat' % project_to_main
+
+ # Project header
+ xml_impl = xml.dom.getDOMImplementation()
+ doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
+
+ n_root = doc.documentElement
+ n_root.setAttribute('ProjectType', 'Visual C++')
+ n_root.setAttribute('Version', '8.00')
+ n_root.setAttribute('Name', target_name)
+ n_root.setAttribute('ProjectGUID', MakeGuid(target_name))
+ n_root.setAttribute('RootNamespace', target_name)
+ n_root.setAttribute('Keyword', 'MakeFileProj')
+
+ n_platform = doc.createElement('Platforms')
+ n_root.appendChild(n_platform)
+ n = doc.createElement('Platform')
+ n.setAttribute('Name', 'Win32')
+ n_platform.appendChild(n)
+
+ n_root.appendChild(doc.createElement('ToolFiles'))
+
+ # One configuration per build mode supported by this target
+ n_configs = doc.createElement('Configurations')
+ n_root.appendChild(n_configs)
+
+ target_path = env['TARGET_PATH']
+ for mode, path in target_path.items():
+ n_config = doc.createElement('Configuration')
+ n_config.setAttribute('Name', '%s|Win32' % mode)
+ n_config.setAttribute('OutputDirectory',
+ '$(ProjectDir)/%s/%s/out' % (mode, target_name))
+ n_config.setAttribute('IntermediateDirectory',
+ '$(ProjectDir)/%s/%s/tmp' % (mode, target_name))
+ n_config.setAttribute('ConfigurationType', '0')
+ n_configs.appendChild(n_config)
+
+ n_tool = doc.createElement('Tool')
+ n_tool.setAttribute('Name', 'VCNMakeTool')
+ n_tool.setAttribute('IncludeSearchPath', '')
+ n_tool.setAttribute('ForcedIncludes', '')
+ n_tool.setAttribute('AssemblySearchPath', '')
+ n_tool.setAttribute('ForcedUsingAssemblies', '')
+ n_tool.setAttribute('CompileAsManaged', '')
+ n_tool.setAttribute('PreprocessorDefinitions', '')
+ if path:
+ n_tool.setAttribute(
+ 'Output', env.RelativePath(target[0].dir, env.Entry(path), sep='/'))
+ build_cmd = '%s --mode=%s %s' % (hammer_bat, mode, target_name)
+ clean_cmd = '%s --mode=%s -c %s' % (hammer_bat, mode, target_name)
+ n_tool.setAttribute('BuildCommandLine', build_cmd)
+ n_tool.setAttribute('CleanCommandLine', clean_cmd)
+ n_tool.setAttribute('ReBuildCommandLine', clean_cmd + ' && ' + build_cmd)
+ n_config.appendChild(n_tool)
+
+ n_files = doc.createElement('Files')
+ n_root.appendChild(n_files)
+ # TODO(rspangler): Fill in files - at least, the .scons file invoking the
+ # target.
+
+ n_root.appendChild(doc.createElement('Globals'))
+
+ f = open(project_file, 'wt')
+ doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\n')
+ f.close()
+
+ return 0
+
+
+def ComponentVSProject(self, target_name, **kwargs):
+ """Visual Studio project pseudo-builder for the specified target.
+
+ Args:
+ self: Environment context.
+ target_name: Name of the target.
+ kwargs: Optional keyword arguments override environment variables in the
+ derived environment used to create the project.
+
+ Returns:
+ A list of output nodes.
+ """
+ # Builder only works on Windows
+ if sys.platform not in ('win32', 'cygwin'):
+ return []
+
+ # Clone environment and add keyword args
+ env = self.Clone()
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Save the target name
+ env['TARGET_NAME'] = target_name
+
+ # Extract the target properties and save in the environment for use by the
+ # real builder.
+ t = GetTargets().get(target_name)
+ env['TARGET_PATH'] = {}
+ if t:
+ for mode, mode_properties in t.mode_properties.items():
+ # Since the target path is what Visual Studio will run, use the EXE
+ # property in preference to TARGET_PATH.
+ target_path = mode_properties.get('EXE',
+ mode_properties.get('TARGET_PATH'))
+ env.Append(TARGET_PATH={mode: target_path})
+ else:
+ # No modes declared for this target. Could be a custom alias created by
+ # a SConscript, rather than a component builder. Assume it can be built in
+ # all modes, but produces no output.
+ for mode in GetTargetModes():
+ env.Append(TARGET_PATH={mode: None})
+
+ # Call the real builder
+ return env.ComponentVSProjectBuilder(
+ '$COMPONENT_VS_PROJECT_DIR/${TARGET_NAME}', [])
+
+#------------------------------------------------------------------------------
+
+
+def ComponentVSSolutionBuilder(target, source, env):
+ """Visual Studio solution builder.
+
+ Args:
+ target: Destination file.
+ source: List of sources to be added to the target.
+ env: Environment context.
+
+ Returns:
+ Zero if successful.
+ """
+ source = source # Silence gpylint
+
+ solution_file = target[0].path
+ projects = env['SOLUTION_PROJECTS']
+ folders = env['SOLUTION_FOLDERS']
+
+ f = open(solution_file, 'wt')
+
+ f.write('Microsoft Visual Studio Solution File, Format Version 9.00\n')
+ f.write('# Visual Studio 2005\n')
+
+ # Projects generated by ComponentVSSolution()
+ for p in projects:
+ project_file = env.File(
+ '$COMPONENT_VS_PROJECT_DIR/%s$COMPONENT_VS_PROJECT_SUFFIX' % p)
+ f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
+ '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID
+ p, # Project name
+ env.RelativePath(target[0].dir, project_file), # Path to project file
+ MakeGuid(p), # Project GUID
+ ))
+
+ # Projects generated elsewhere
+ for p in source:
+ f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
+ # TODO(rspangler): What if this project isn't type external makefile?
+ # How to tell what type it is?
+ '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID
+ p, # Project name
+ env.RelativePath(target[0].dir, p), # Path to project file
+ GetGuidFromVSProject(p.abspath), # Project GUID
+ ))
+
+ # Folders from build groups
+ for folder in folders:
+ f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
+ '{2150E333-8FDC-42A3-9474-1A3956D46DE8}', # Solution folder GUID
+ folder, # Folder name
+ folder, # Folder name (again)
+ # Use a different seed so the folder won't get the same GUID as a
+ # project.
+ MakeGuid(folder, seed='folder'), # Project GUID
+ ))
+
+ f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
+ for mode in GetTargetModes():
+ f.write('\t\t%s|Win32 = %s|Win32\n' % (mode, mode))
+ f.write('\tEndGlobalSection\n')
+
+ f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
+ for p in projects:
+ for mode in GetTargetModes():
+ f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % (
+ MakeGuid(p), # Project GUID
+ mode, # Solution build configuration
+ mode, # Project build config for that solution config
+ ))
+ t = GetTargets().get(p)
+ if t and mode in t.mode_properties:
+ # Target can be built in this mode
+ f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % (
+ MakeGuid(p), # Project GUID
+ mode, # Solution build configuration
+ mode, # Project build config for that solution config
+ ))
+ f.write('\tEndGlobalSection\n')
+
+ f.write('\tGlobalSection(SolutionProperties) = preSolution\n')
+ f.write('\t\tHideSolutionNode = FALSE\n')
+ f.write('\tEndGlobalSection\n')
+
+ if folders:
+ f.write('\tGlobalSection(NestedProjects) = preSolution\n')
+ for p, folder in projects.items():
+ f.write('\t\t%s = %s\n' % (MakeGuid(p), MakeGuid(folder, seed='folder')))
+ f.write('\tEndGlobalSection\n')
+
+ f.write('EndGlobal\n')
+ f.close()
+
+ return 0
+
+
+def ComponentVSSolution(self, solution_name, target_names, projects=None,
+ **kwargs):
+ """Visual Studio solution pseudo-builder.
+
+ Args:
+ self: Environment context.
+ solution_name: Name of the target.
+ target_names: Names of targets or target groups to include in the solution.
+ This will automatically build projects for them.
+ projects: List of aditional projects not generated by this solution to
+ include in the solution.
+ kwargs: Optional keyword arguments override environment variables in the
+ derived environment used to create the solution.
+
+ Returns:
+ The list of output nodes.
+ """
+ # TODO(rspangler): Should have option to build source project also. Perhaps
+ # select using a --source_project option, since it needs to use gather_inputs
+ # to scan the DAG and will blow up the null build time.
+ # TODO(rspangler): Should also have autobuild_projects option. If false,
+ # don't build them.
+ # TODO(rspangler): Should also be able to specify a target group directly
+ # (i.e. 'run_all_tests')
+
+ # Builder only works on Windows
+ if sys.platform not in ('win32', 'cygwin'):
+ return []
+
+ # Clone environment and add keyword args
+ env = self.Clone()
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Save the target name
+ env['SOLUTION_NAME'] = solution_name
+
+ # Get list of targets to make projects for. At this point we haven't
+ # determined whether they're groups or targets.
+ target_names = env.SubstList2(target_names)
+ env['SOLUTION_TARGETS'] = target_names
+
+ project_names = {}
+ folders = []
+ # Expand target_names into project names, and create project-to-folder
+ # mappings
+ if target_names:
+ # Expand target_names into project names
+ for target in target_names:
+ if target in GetTargetGroups():
+ # Add target to folders
+ folders.append(target)
+ # Add all project_names in the group
+ for t in GetTargetGroups()[target].GetTargetNames():
+ project_names[t] = target
+ elif target in GetTargets():
+ # Just a target name
+ project_names[target] = None
+ else:
+ print 'Warning: ignoring unknown target "%s"' % target
+ else:
+ # No target names specified, so use all projects
+ for t in GetTargets():
+ project_names[t] = None
+
+ env['SOLUTION_FOLDERS'] = folders
+ env['SOLUTION_PROJECTS'] = project_names
+
+ # Call the real builder
+ out_nodes = env.ComponentVSSolutionBuilder(
+ '$COMPONENT_VS_SOLUTION_DIR/${SOLUTION_NAME}', projects or [])
+
+ # Call the real builder for the projects we generate
+ for p in project_names:
+ out_nodes += env.ComponentVSProject(p)
+
+ # Add the solution target
+ # TODO(rspangler): Should really defer the rest of the work above, since
+ # otherwise we can't build a solution which has a target to rebuild itself.
+ env.Alias('all_solutions', env.Alias(solution_name, out_nodes))
+
+ # TODO(rspangler): To rebuild the solution properly, need to override its
+ # project configuration so it only has '--mode=all' (or some other way of
+ # setting the subset of modes which it should use to rebuild itself).
+ # Rebuilding with the property below will strip it down to just the current
+ # build mode, which isn't what we want.
+ # Let component_targets know this target is available in the current mode
+ #self.SetTargetProperty(solution_name, TARGET_PATH=out_nodes[0])
+
+ return out_nodes
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add pseudo-builders to set up the project and solution builders. These
+ # need to be available on all platforms so that SConscripts which reference
+ # them will work.
+ env.AddMethod(ComponentVSProject)
+ env.AddMethod(ComponentVSSolution)
+
+ # If not on Windows, don't do anything else
+ if sys.platform not in ('win32', 'cygwin'):
+ return
+
+ # Include tools we need
+ env.Tool('gather_inputs')
+
+ env.SetDefault(
+ COMPONENT_VS_SOLUTION_DIR='$DESTINATION_ROOT/solution',
+ COMPONENT_VS_PROJECT_DIR='$COMPONENT_VS_SOLUTION_DIR/projects',
+ COMPONENT_VS_SOLUTION_SUFFIX='.sln',
+ COMPONENT_VS_PROJECT_SUFFIX='.vcproj',
+ )
+
+ AddTargetGroup('all_solutions', 'solutions can be built')
+
+ # Add builders
+ vcprojaction = SCons.Script.Action(ComponentVSProjectBuilder, varlist=[
+ 'TARGET_NAME',
+ 'TARGET_PATH',
+ ])
+ vcprojbuilder = SCons.Script.Builder(
+ action=vcprojaction,
+ suffix='$COMPONENT_VS_PROJECT_SUFFIX')
+
+ slnaction = SCons.Script.Action(ComponentVSSolutionBuilder, varlist=[
+ 'SOLUTION_TARGETS',
+ 'SOLUTION_FOLDERS',
+ 'SOLUTION_PROJECTS',
+ ])
+ slnbuilder = SCons.Script.Builder(
+ action=slnaction,
+ suffix='$COMPONENT_VS_SOLUTION_SUFFIX')
+
+ env.Append(BUILDERS={
+ 'ComponentVSProjectBuilder': vcprojbuilder,
+ 'ComponentVSSolutionBuilder': slnbuilder,
+ })
diff --git a/site_scons/site_tools/component_targets_xml.py b/site_scons/site_tools/component_targets_xml.py
new file mode 100755
index 0000000..9162d97
--- /dev/null
+++ b/site_scons/site_tools/component_targets_xml.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python2.4
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""XML output of component targets for SCons."""
+
+
+import xml.dom
+import SCons.Script
+
+
+def TargetXMLHelp(target, source, env):
+ """Generates target information in XML format.
+
+ Args:
+ target: Destination file.
+ source: List of sources. Should be empty, otherwise this will actually
+ require the sources to be built first.
+ env: Environment context.
+ """
+ env = env
+ source = source # Silence gpylint
+
+ target_path = target[0].abspath
+
+ xml_impl = xml.dom.getDOMImplementation()
+ doc = xml_impl.createDocument(None, 'help', None)
+
+ mode_list = doc.createElement('mode_list')
+ doc.documentElement.appendChild(mode_list)
+ for mode in GetTargetModes().values():
+ n = doc.createElement('build_mode')
+ n.setAttribute('name', mode.name)
+ n.setAttribute('description', mode.description)
+ mode_list.appendChild(n)
+
+ group_list = doc.createElement('target_groups')
+ doc.documentElement.appendChild(group_list)
+ for group in GetTargetGroups().values():
+ items = group.GetTargetNames()
+ if not items:
+ continue
+
+ ngroup = doc.createElement('target_group')
+ ngroup.setAttribute('name', group.name)
+ group_list.appendChild(ngroup)
+
+ for i in items:
+ ntarget = doc.createElement('build_target')
+ ntarget.setAttribute('name', i)
+ ngroup.appendChild(ntarget)
+
+ # Get properties for target, if any
+ target = GetTargets().get(i)
+ if target:
+ # All modes
+ for k, v in target.properties.items():
+ n = doc.createElement('target_property')
+ n.setAttribute('name', k)
+ n.setAttribute('value', v)
+ ntarget.appendChild(n)
+
+ # Mode-specific
+ for mode, mode_properties in target.mode_properties.items():
+ nmode = doc.createElement('target_mode')
+ nmode.setAttribute('name', mode)
+ ntarget.appendChild(nmode)
+
+ for k, v in mode_properties.items():
+ n = doc.createElement('target_property')
+ n.setAttribute('name', k)
+ n.setAttribute('value', v)
+ nmode.appendChild(n)
+
+ f = open(target_path, 'wt')
+ doc.writexml(f, encoding='UTF-8', addindent=' ', newl='\n')
+ f.close()
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ env = env # Silence gpylint
+
+ SCons.Script.Help('''\
+ targets_xml Write information on the build mode's targets to
+ targets.xml. Most useful with --mode=all.
+''')
+
+ # Add build target to generate help
+ p = env.Command('$DESTINATION_ROOT/targets.xml', [],
+ env.Action(TargetXMLHelp))
+
+ # Always build xml info if requested.
+ # TODO(rspangler): Is there a better way to determine the xml info is up to
+ # date?
+ env.AlwaysBuild(p)
+ env.Alias('targets_xml', p)
diff --git a/site_scons/site_tools/concat_source.py b/site_scons/site_tools/concat_source.py
index c67463b..0598f97 100644
--- a/site_scons/site_tools/concat_source.py
+++ b/site_scons/site_tools/concat_source.py
@@ -52,14 +52,7 @@ def ConcatSourceBuilder(target, source, env):
output_lines = [
'// This file is auto-generated by the ConcatSource builder.']
- for source_file in source:
- # Skip source files which are not CPP files. These will be passed through
- # to the output list by the pseudo-builder.
- if source_file.suffix not in env['CONCAT_SOURCE_SUFFIXES']:
- continue
-
- source_path = str(source_file)
-
+ for source_path in map(str, source):
if env.get('CC') == 'cl':
# Add message pragma for nicer progress indication when building with
# MSVC.
@@ -93,8 +86,9 @@ def ConcatSourcePseudoBuilder(self, target, source):
# from other files (which we pass through).
cppsource = []
outputs = []
- for source_file in SCons.Script.Flatten(source):
- if self.File(source_file).suffix in self['CONCAT_SOURCE_SUFFIXES']:
+ suffixes = self.Flatten(self.subst_list('$CONCAT_SOURCE_SUFFIXES'))
+ for source_file in self.arg2nodes(source):
+ if source_file.suffix in suffixes:
cppsource.append(source_file)
else:
outputs.append(source_file)
diff --git a/site_scons/site_tools/defer.py b/site_scons/site_tools/defer.py
index ffd30da..8b6de89 100644
--- a/site_scons/site_tools/defer.py
+++ b/site_scons/site_tools/defer.py
@@ -36,33 +36,115 @@ import sys
import types
import SCons.Errors
-__defer_groups = {}
+class DeferGroup:
+ """Named list of functions to be deferred."""
+ # If we derive DeferGroup from object, instances of it return type
+ # <class 'defer.DeferGroup'>, which prevents SCons.Util.semi_deepcopy()
+ # from calling its __semi_deepcopy__ function.
+ # TODO(sgk): Make semi_deepcopy() capable of handling classes derived from
+ # object.
+
+ def __init__(self):
+ """Initialize deferred function object."""
+ self.func_env_cwd = []
+ self.after = set()
+
+ def __semi_deepcopy__(self):
+ """Makes a semi-deep-copy of this object.
+
+ Returns:
+ A semi-deep-copy of this object.
+
+ This means it copies the sets and lists contained by this object, but
+ doesn't make copies of the function pointers and environments pointed to by
+ those lists.
+
+ Needed so env.Clone() makes a copy of the defer list, so that functions
+ and after-relationships subsequently added to the clone are not added to
+ the parent.
+ """
+ c = DeferGroup()
+ c.func_env_cwd = self.func_env_cwd[:]
+ c.after = self.after.copy()
+ return c
+
+
+def SetDeferRoot(self):
+ """Sets the current environment as the root environment for defer.
+
+ Args:
+ self: Current environment context.
+
+ Functions deferred by environments cloned from the root environment (that is,
+ function deferred by children of the root environment) will be executed when
+ ExecuteDefer() is called from the root environment.
+
+ Functions deferred by environments from which the root environment was cloned
+ (that is, functions deferred by parents of the root environment) will be
+ passed the root environment instead of the original parent environment.
+ (Otherwise, they would have no way to determine the root environment.)
+ """
+ # Set the current environment as the root for holding defer groups
+ self['_DEFER_ROOT_ENV'] = self
+
+ # Deferred functions this environment got from its parents will be run in the
+ # new root context.
+ for group in GetDeferGroups(self).values():
+ new_list = [(func, self, cwd) for (func, env, cwd) in group.func_env_cwd]
+ group.func_env_cwd = new_list
-def _InitializeDefer(self):
- """Re-initializes deferred function handling.
+
+def GetDeferRoot(self):
+ """Returns the root environment for defer.
Args:
- self: Parent environment
+ self: Current environment context.
+
+ Returns:
+ The root environment for defer. If one of this environment's parents
+ called SetDeferRoot(), returns that environment. Otherwise returns the
+ current environment.
"""
- # Clear the list of deferred groups
- __defer_groups.clear()
+ return self.get('_DEFER_ROOT_ENV', self)
+
+def GetDeferGroups(env):
+ """Returns the dict of defer groups from the root defer environment.
+
+ Args:
+ env: Environment context.
-def _ExecuteDefer(self):
+ Returns:
+ The dict of defer groups from the root defer environment.
+ """
+ return env.GetDeferRoot()['_DEFER_GROUPS']
+
+
+def ExecuteDefer(self):
"""Executes deferred functions.
Args:
- self: Parent environment
+ self: Current environment context.
"""
# Save directory, so SConscript functions can occur in the right subdirs
oldcwd = os.getcwd()
+ # If defer root is set and isn't this environment, we're being called from a
+ # sub-environment. That's not where we should be called.
+ if self.GetDeferRoot() != self:
+ print ('Warning: Ignoring call to ExecuteDefer() from child of the '
+ 'environment passed to SetDeferRoot().')
+ return
+
+ # Get list of defer groups from ourselves.
+ defer_groups = GetDeferGroups(self)
+
# Loop through deferred functions
- while __defer_groups:
+ while defer_groups:
did_work = False
- for name, group in __defer_groups.items():
- if group.after.intersection(__defer_groups.keys()):
+ for name, group in defer_groups.items():
+ if group.after.intersection(defer_groups.keys()):
continue # Still have dependencies
if group.func_env_cwd:
# Run all the functions in our named group
@@ -70,11 +152,11 @@ def _ExecuteDefer(self):
os.chdir(cwd)
func(env)
did_work = True
- del __defer_groups[name]
+ del defer_groups[name]
break
if not did_work:
- errmsg = 'Error in _ExecuteDefer: dependency cycle detected.\n'
- for name, group in __defer_groups.items():
+ errmsg = 'Error in ExecuteDefer: dependency cycle detected.\n'
+ for name, group in defer_groups.items():
errmsg += ' %s after: %s\n' % (name, group.after)
raise SCons.Errors.UserError(errmsg)
@@ -82,14 +164,31 @@ def _ExecuteDefer(self):
os.chdir(oldcwd)
-class DeferFunc(object):
- """Named list of functions to be deferred."""
+def PrintDefer(self, print_functions=True):
+ """Prints the current defer dependency graph.
- def __init__(self):
- """Initialize deferred function object."""
- object.__init__(self)
- self.func_env_cwd = []
- self.after = set()
+ Args:
+ self: Environment in which PrintDefer() was called.
+ print_functions: Print individual functions in defer groups.
+ """
+ # Get the defer dict
+ # Get list of defer groups from ourselves.
+ defer_groups = GetDeferGroups(self)
+ dgkeys = defer_groups.keys()
+ dgkeys.sort()
+ for k in dgkeys:
+ print ' +- %s' % k
+ group = defer_groups[k]
+ after = list(group.after)
+ if after:
+ print ' | after'
+ after.sort()
+ for a in after:
+ print ' | +- %s' % a
+ if print_functions and group.func_env_cwd:
+ print ' functions'
+ for func, env, cwd in group.func_env_cwd:
+ print ' | +- %s %s' % (func.__name__, cwd)
def Defer(self, *args, **kwargs):
@@ -102,6 +201,9 @@ def Defer(self, *args, **kwargs):
The deferred function will be passed the environment used to call Defer(),
and will be executed in the same working directory as the calling SConscript.
+ (Exception: if this environment is cloned and the clone calls SetDeferRoot()
+ and then ExecuteDefer(), the function will be passed the root environment,
+ instead of the environment used to call Defer().)
All deferred functions run after all SConscripts. Additional dependencies
may be specified with the after= keyword.
@@ -142,10 +244,14 @@ def Defer(self, *args, **kwargs):
if func and not name:
name = func.__name__
+ # TODO(rspangler): Why not allow multiple functions? Should be ok
+
# Get list of names and/or functions this function should defer until after
after = []
for a in self.Flatten(kwargs.get('after')):
if isinstance(a, str):
+ # TODO(rspangler): Should check if '$' in a, and if so, subst() it and
+ # recurse into it.
after.append(a)
elif isinstance(a, types.FunctionType):
after.append(a.__name__)
@@ -154,9 +260,10 @@ def Defer(self, *args, **kwargs):
raise ValueError('Defer after=%r is not a function or name' % a)
# Find the deferred function
- if name not in __defer_groups:
- __defer_groups[name] = DeferFunc()
- group = __defer_groups[name]
+ defer_groups = GetDeferGroups(self)
+ if name not in defer_groups:
+ defer_groups[name] = DeferGroup()
+ group = defer_groups[name]
# If we were given a function, also save environment and current directory
if func:
@@ -169,7 +276,10 @@ def Defer(self, *args, **kwargs):
def generate(env):
# NOTE: SCons requires the use of this name, which fails gpylint.
"""SCons entry point for this tool."""
+ env.Append(_DEFER_GROUPS={})
- env.AddMethod(_InitializeDefer)
- env.AddMethod(_ExecuteDefer)
env.AddMethod(Defer)
+ env.AddMethod(ExecuteDefer)
+ env.AddMethod(GetDeferRoot)
+ env.AddMethod(PrintDefer)
+ env.AddMethod(SetDeferRoot)
diff --git a/site_scons/site_tools/environment_tools.py b/site_scons/site_tools/environment_tools.py
index 969a923..0e4f938 100644
--- a/site_scons/site_tools/environment_tools.py
+++ b/site_scons/site_tools/environment_tools.py
@@ -35,6 +35,7 @@ will automatically be included by the component_setup tool.
"""
+import os
import SCons
@@ -61,14 +62,15 @@ def FilterOut(self, **kw):
continue
for vremove in val:
- if vremove in envval:
+ # 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.
+ # too. (If variable is a dict, etc.)
#------------------------------------------------------------------------------
@@ -83,23 +85,15 @@ def Overlap(self, values1, values2):
Returns:
The list of values in common after substitution, or an empty list if
- the values do no overlap.
+ the values do not overlap.
- Converts the values to a set of plain strings via self.subst() before
+ Converts the values to a set of plain strings via self.SubstList2() before
comparison, so SCons $ variables are evaluated.
"""
-
- set1 = set()
- for v in self.Flatten(values1):
- set1.add(self.subst(v))
-
- set2 = set()
- for v in self.Flatten(values2):
- set2.add(self.subst(v))
-
+ set1 = set(self.SubstList2(values1))
+ set2 = set(self.SubstList2(values2))
return list(set1.intersection(set2))
-
#------------------------------------------------------------------------------
@@ -137,7 +131,7 @@ def ApplySConscript(self, sconscript_file):
If you need to export multiple variables to the called SConscript, or return
variables from it, use the existing SConscript() function.
"""
- return SCons.Script.SConscript(sconscript_file, exports={'env':self})
+ return SCons.Script.SConscript(sconscript_file, exports={'env': self})
#------------------------------------------------------------------------------
@@ -194,7 +188,85 @@ def BuildSConscript(self, sconscript_file):
else:
script_file = sconscript_file
- self.SConscript(script_file, exports={'env':self.Clone()})
+ self.SConscript(script_file, exports={'env': self.Clone()})
+
+#------------------------------------------------------------------------------
+
+
+def SubstList2(self, *args):
+ """Replacement subst_list designed for flags/parameters, not command lines.
+
+ Args:
+ self: Environment context.
+ args: One or more strings or lists of strings.
+
+ Returns:
+ A flattened, substituted list of strings.
+
+ SCons's built-in subst_list evaluates (substitutes) variables in its
+ arguments, and returns a list of lists (one per positional argument). Since
+ it is designed for use in command line expansion, the list items are
+ SCons.Subst.CmdStringHolder instances. These instances can't be passed into
+ env.File() (or subsequent calls to env.subst(), either). The returned
+ nested lists also need to be flattened via env.Flatten() before the caller
+ can iterate over the contents.
+
+ SubstList2() does a subst_list, flattens the result, then maps the flattened
+ list to strings.
+
+ It is better to do:
+ for x in env.SubstList2('$MYPARAMS'):
+ than to do:
+ for x in env.get('MYPARAMS', []):
+ and definitely better than:
+ for x in env['MYPARAMS']:
+ which will throw an exception if MYPARAMS isn't defined.
+ """
+ return map(str, self.Flatten(self.subst_list(args)))
+
+
+#------------------------------------------------------------------------------
+
+
+def RelativePath(self, source, target, sep=os.sep, source_is_file=False):
+ """Calculates the relative path from source to target.
+
+ Args:
+ self: Environment context.
+ source: Source path or node.
+ target: Target path or node.
+ sep: Path separator to use in returned relative path.
+ source_is_file: If true, calculates the relative path from the directory
+ containing the source, rather than the source itself. Note that if
+ source is a node, you can pass in source.dir instead, which is shorter.
+
+ Returns:
+ The relative path from source to target.
+ """
+ # Split source and target into list of directories
+ source = self.Entry(str(source))
+ if source_is_file:
+ source = source.dir
+ source = source.abspath.split(os.sep)
+ target = self.Entry(str(target)).abspath.split(os.sep)
+
+ # Handle source and target identical
+ if source == target:
+ if source_is_file:
+ return source[-1] # Bare filename
+ else:
+ return '.' # Directory pointing to itself
+
+ # TODO(rspangler): Handle UNC paths and drive letters (fine if they're the
+ # same, but if they're different, there IS no relative path)
+
+ # Remove common elements
+ while source and target and source[0] == target[0]:
+ source.pop(0)
+ target.pop(0)
+ # Join the remaining elements
+ return sep.join(['..'] * len(source) + target)
+
#------------------------------------------------------------------------------
@@ -208,3 +280,5 @@ def generate(env):
env.AddMethod(BuildSConscript)
env.AddMethod(FilterOut)
env.AddMethod(Overlap)
+ env.AddMethod(RelativePath)
+ env.AddMethod(SubstList2)
diff --git a/site_scons/site_tools/publish.py b/site_scons/site_tools/publish.py
index ebd6acf..bf6a737 100644
--- a/site_scons/site_tools/publish.py
+++ b/site_scons/site_tools/publish.py
@@ -31,7 +31,10 @@
"""Publish tool for SCons."""
-__published = {} # List of published resources
+# List of published resources. This is a dict indexed by group name. Each
+# item in this dict is a dict indexed by resource type. Items in that dict
+# are lists of files for that resource.
+__published = {}
#------------------------------------------------------------------------------
@@ -54,13 +57,13 @@ class PublishItem(object):
#------------------------------------------------------------------------------
-def _InitializePublish(self):
+def _InitializePublish(env):
"""Re-initializes published resources.
Args:
- self: Parent environment
+ env: Parent environment
"""
- self=self # Silence gpylint
+ env=env # Silence gpylint
# Clear the dict of published resources
__published.clear()
@@ -87,10 +90,10 @@ def ReplicatePublished(self, target, group_name, resource_type):
target_path = self.Dir(target).abspath
dest_nodes = []
- for group in self.Flatten(group_name):
- for resource in self.Flatten(resource_type):
+ for group in self.SubstList2(group_name):
+ for resource in self.SubstList2(resource_type):
# Get items for publish group and resource type
- items = __published.get(self.subst(group), {}).get(resource, [])
+ items = __published.get(group, {}).get(resource, [])
for i in items:
if i.subdir:
dest_nodes += self.Replicate(target_path + '/' + i.subdir, i.source)
@@ -113,10 +116,10 @@ def GetPublished(self, group_name, resource_type):
no matching resources.
"""
source_list = []
- for group in self.Flatten(group_name):
+ for group in self.SubstList2(group_name):
# Get items for publish group and resource type
- for resource in self.Flatten(resource_type):
- items = __published.get(self.subst(group), {}).get(resource, [])
+ for resource in self.SubstList2(resource_type):
+ items = __published.get(group, {}).get(resource, [])
for i in items:
source_list.append(i.source)
@@ -141,13 +144,17 @@ def Publish(self, group_name, resource_type, source, subdir=None):
subdir = '' # Make string so we can append to it
# Evaluate SCons variables in group name
+ # TODO(rspangler): Should Publish() be able to take a list of group names
+ # and publish the resource to all of them?
group_name = self.subst(group_name)
# Get list of sources
items = []
for source_entry in self.Flatten(source):
- if type(source_entry) == str:
+ if isinstance(source_entry, str):
# Search for matches for each source entry
+ # TODO(rspangler): If there are no wildcard chars in the source entry,
+ # should generate an error if there were no matches?
source_nodes = self.Glob(source_entry)
else:
# Source entry is already a file or directory node; no need to glob it
@@ -179,7 +186,10 @@ def generate(env):
# NOTE: SCons requires the use of this name, which fails gpylint.
"""SCons entry point for this tool."""
- env.AddMethod(_InitializePublish)
+ # Defer initializing publish, but do before building SConscripts
+ env.Defer(_InitializePublish)
+ env.Defer('BuildEnvironmentSConscripts', after=_InitializePublish)
+
env.AddMethod(GetPublished)
env.AddMethod(Publish)
env.AddMethod(ReplicatePublished)
diff --git a/site_scons/site_tools/replicate.py b/site_scons/site_tools/replicate.py
index 6547167b..b11ce14 100644
--- a/site_scons/site_tools/replicate.py
+++ b/site_scons/site_tools/replicate.py
@@ -105,9 +105,12 @@ def Replicate(env, target, source, **kw):
target_name = re.sub(r[0], r[1], target_name)
target = env.File(target_name)
if (target.has_builder()
+ and hasattr(target.get_builder(), 'name')
and target.get_builder().name == 'InstallBuilder'
and target.sources == [s]):
# Already installed that file, so pass through the destination node
+ # TODO(rspangler): Is there a better way to determine if this is a
+ # duplicate install?
dest_nodes += [target]
else:
dest_nodes += env.InstallAs(target_name, s)
diff --git a/site_scons/site_tools/target_platform_mac.py b/site_scons/site_tools/target_platform_mac.py
index 3034f36..4c35334 100644
--- a/site_scons/site_tools/target_platform_mac.py
+++ b/site_scons/site_tools/target_platform_mac.py
@@ -113,6 +113,9 @@ def BundlePseudoBuilder(env, target, **kwargs):
return env.Dir(target)
+#------------------------------------------------------------------------------
+
+
def generate(env):
# NOTE: SCons requires the use of this name, which fails gpylint.
"""SCons entry point for this tool."""
@@ -140,7 +143,6 @@ def generate(env):
env.Append(
HOST_PLATFORMS=['MAC'],
CPPDEFINES=['OS_MACOSX=OS_MACOSX'],
- BITS=['mac', 'posix'],
# Settings for debug
CCFLAGS_DEBUG=['-g'],
diff --git a/site_scons/site_tools/visual_studio_solution.py b/site_scons/site_tools/visual_studio_solution.py
index eae83fd..4c18d23 100644
--- a/site_scons/site_tools/visual_studio_solution.py
+++ b/site_scons/site_tools/visual_studio_solution.py
@@ -71,7 +71,7 @@ def Solution(env, solution_name,
build_targets = [e.subst('$TARGET_ROOT') for e in environments]
# pick out sources, headers, and resources
sources, headers, resources, others = env.GatherInputs(
- env.Dir('$DESTINATION_ROOT'),
+ [SCons.Script.Dir('$DESTINATION_ROOT')],
['.+\\.(c|cc|m|mm|cpp)$', # source files
'.+\\.(h|hh|hpp)$', # header files
'.+\\.(rc)$', # resource files
@@ -79,18 +79,19 @@ def Solution(env, solution_name,
exclude_pattern=exclude_pattern,
)
# Build main Visual Studio Project file
- project_list = env.MSVSProject(
- target=solution_name + env['MSVSPROJECTSUFFIX'],
- srcs=sources + headers + others + resources,
- incs=[],
- misc=[],
- resources=[],
- auto_build_solution=0,
- MSVSCLEANCOM='hammer.bat -c MODE=all',
- MSVSBUILDCOM='hammer.bat MODE=all',
- MSVSREBUILD='hammer.bat -c MODE=all; hammer.bat MODE=all',
- buildtarget=build_targets,
- variant=variants)
+ project_list = env.MSVSProject(target=solution_name +
+ env['MSVSPROJECTSUFFIX'],
+ srcs=sources + headers + others + resources,
+ incs=[],
+ misc=[],
+ resources=[],
+ auto_build_solution=0,
+ MSVSCLEANCOM='hammer.bat -c MODE=all',
+ MSVSBUILDCOM='hammer.bat MODE=all',
+ MSVSREBUILD='hammer.bat -c MODE=all;'
+ 'hammer.bat MODE=all',
+ buildtarget=build_targets,
+ variant=variants)
# Collect other projects
for e in extra_build_targets:
# Explicitly create a node for target, so SCons will expand env variables.