summaryrefslogtreecommitdiffstats
path: root/site_scons
diff options
context:
space:
mode:
authorbradnelson@chromium.org <bradnelson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-09 22:38:44 +0000
committerbradnelson@chromium.org <bradnelson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-09 22:38:44 +0000
commitfb9a9fa42b71367b1cda5ca444cf93a3ef74cd3b (patch)
treef610f8a9b7c2b4f96d28905c77d9c830ea50f800 /site_scons
parent8b4b861f8ef2456c2c643032d78a17f6261fd234 (diff)
downloadchromium_src-fb9a9fa42b71367b1cda5ca444cf93a3ef74cd3b.zip
chromium_src-fb9a9fa42b71367b1cda5ca444cf93a3ef74cd3b.tar.gz
chromium_src-fb9a9fa42b71367b1cda5ca444cf93a3ef74cd3b.tar.bz2
Dropping in software construction toolkit.
Review URL: http://codereview.chromium.org/6329 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3145 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'site_scons')
-rw-r--r--site_scons/site_init.py441
-rw-r--r--site_scons/site_tools/atlmfc_vc80.py77
-rw-r--r--site_scons/site_tools/code_signing.py103
-rw-r--r--site_scons/site_tools/collada_dom.py69
-rw-r--r--site_scons/site_tools/command_output.py130
-rw-r--r--site_scons/site_tools/component_bits.py212
-rw-r--r--site_scons/site_tools/component_builders.py511
-rw-r--r--site_scons/site_tools/component_setup.py195
-rw-r--r--site_scons/site_tools/concat_source.py135
-rw-r--r--site_scons/site_tools/defer.py178
-rw-r--r--site_scons/site_tools/directx_9_0_c.py52
-rw-r--r--site_scons/site_tools/directx_9_18_944_0_partial.py52
-rw-r--r--site_scons/site_tools/distcc.py93
-rw-r--r--site_scons/site_tools/environment_tools.py210
-rw-r--r--site_scons/site_tools/gather_inputs.py105
-rw-r--r--site_scons/site_tools/publish.py185
-rw-r--r--site_scons/site_tools/replace_strings.py81
-rw-r--r--site_scons/site_tools/replicate.py123
-rw-r--r--site_scons/site_tools/seven_zip.py145
-rw-r--r--site_scons/site_tools/target_debug.py53
-rw-r--r--site_scons/site_tools/target_optimized.py50
-rw-r--r--site_scons/site_tools/target_platform_linux.py93
-rw-r--r--site_scons/site_tools/target_platform_mac.py173
-rw-r--r--site_scons/site_tools/target_platform_windows.py262
-rw-r--r--site_scons/site_tools/visual_studio_solution.py131
-rw-r--r--site_scons/site_tools/windows_hard_link.py108
26 files changed, 3967 insertions, 0 deletions
diff --git a/site_scons/site_init.py b/site_scons/site_init.py
new file mode 100644
index 0000000..38a52e2
--- /dev/null
+++ b/site_scons/site_init.py
@@ -0,0 +1,441 @@
+#!/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 site_scons configuration.
+
+This module sets up SCons for use with this toolkit. This should contain setup
+which occurs outside of environments. If a method operates within the context
+of an environment, it should instead go in a tool in site_tools and be invoked
+for the target environment.
+"""
+
+import __builtin__
+import sys
+import SCons
+
+
+# List of target groups for printing help; modified by AddTargetGroup(); used
+# by BuildComponents().
+__target_groups = {}
+
+
+def _HostPlatform():
+ """Returns the current host platform.
+
+ That is, the platform we're actually running SCons on. You shouldn't use
+ this inside your SConscript files; instead, include the appropriate
+ target_platform tool for your environments. When you call BuildComponents(),
+ only environments with the current host platform will be built.
+
+ Returns:
+ The host platform name - one of ('WINDOWS', 'LINUX', 'MAC').
+ """
+
+ platform_map = {
+ 'win32': 'WINDOWS',
+ 'cygwin': 'WINDOWS',
+ 'linux': 'LINUX',
+ 'linux2': 'LINUX',
+ 'darwin': 'MAC',
+ }
+
+ if sys.platform not in platform_map:
+ print ('site_init.py warning: platform "%s" is not in platfom map.' %
+ sys.platform)
+
+ return platform_map.get(sys.platform, sys.platform)
+
+
+#------------------------------------------------------------------------------
+
+
+def _CheckBuildModes(build_modes, environments, host_platform):
+ """Checks the build modes for the environments.
+
+ Args:
+ build_modes: List of build mode strings.
+ environments: List of SCons environments.
+ host_platform: Host platform string.
+
+ Raises:
+ ValueError: build groups and/or types invalid.
+ """
+ # Make sure the list of environments for the current host platform have
+ # unique BUILD_TYPE. This ensures they won't overwrite each others' build
+ # output. (It is ok for build types in different host platforms to overlap;
+ # that is, WINDOWS and MAC can both have a 'dbg' build type.)
+ all_build_types = []
+ all_build_groups = {}
+ build_desc = {}
+ for e in environments:
+ if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']):
+ continue
+ if e['BUILD_TYPE'] in all_build_types:
+ raise ValueError('Multiple environments have the same BUILD_TYPE=%s' %
+ e['BUILD_TYPE'])
+ else:
+ all_build_types.append(e['BUILD_TYPE'])
+ build_desc[e['BUILD_TYPE']] = e.get('BUILD_TYPE_DESCRIPTION')
+
+ # Keep track of build groups and the build types which belong to them
+ for g in e['BUILD_GROUPS']:
+ if g not in all_build_groups:
+ all_build_groups[g] = []
+ # Don't allow build types and groups to share names
+ if g in all_build_types:
+ raise ValueError('Build group %s also specified as BUILD_TYPE.' % g)
+ else:
+ all_build_types.append(g)
+ 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 = '''
+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 % (
+ 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 += '''
+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:
+'''
+
+ 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]))
+
+ if xml_help:
+ help_text += '</group_list>\n'
+ else:
+ 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.
+ '''
+ SCons.Script.Help(help_text)
+
+ # Make sure all build modes specified by the user are ones which apply to
+ # the current environment.
+ for mode in build_modes:
+ if mode not in all_build_types and mode not in all_build_groups:
+ print ('Warning: Ignoring build mode "%s", which is not defined on this '
+ '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
+ 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 BuildComponents(environments):
+ """Build a collection of components under a collection of environments.
+
+ Only environments with HOST_PLATFORMS containing the platform specified by
+ --host-platform (or the native host platform, if --host-platform was not
+ specified) will be matched.
+
+ Each matching environment is checked against the modes passed to the --mode
+ command line argument (or 'default', if no mode(s) were specified). If any
+ of the modes match the environment's BUILD_TYPE or any of the environment's
+ BUILD_GROUPS, all the BUILD_COMPONENTS and BUILD_SCONSCRIPTS in that
+ environment will be built.
+
+ Args:
+ environments: List of SCons environments.
+ """
+ # 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.
+ legacy_mode_option = SCons.Script.ARGUMENTS.get('MODE')
+ if legacy_mode_option:
+ build_modes = legacy_mode_option
+ build_modes = build_modes.split(',')
+
+ host_platform = SCons.Script.GetOption('host_platform')
+ if not host_platform:
+ host_platform = _HostPlatform()
+
+ # Check build modes
+ _CheckBuildModes(build_modes, environments, host_platform)
+
+ if xml_help:
+ SCons.Script.Help('<help_from_sconscripts>\n<![CDATA[\n')
+
+ for e in environments:
+ if not e.Overlap(e['HOST_PLATFORMS'], [host_platform, '*']):
+ continue # Environment requires a host platform which isn't us
+
+ if e.Overlap([e['BUILD_TYPE'], e['BUILD_GROUPS']], build_modes):
+ # 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
+
+ ec.SConscript(c_script,
+ build_dir='$OBJ_ROOT/' + str(c_dir),
+ exports={'env': ec},
+ duplicate=0)
+
+ # Execute deferred functions
+ e._ExecuteDefer()
+
+ if xml_help:
+ SCons.Script.Help(']]>\n</help_from_sconscripts>\n')
+
+ _AddTargetHelp()
+
+ # End final help tag
+ if xml_help:
+ SCons.Script.Help('</help>\n')
+
+
+#------------------------------------------------------------------------------
+
+
+def _ToolExists():
+ """Replacement for SCons tool module exists() function, if one isn't present.
+
+ Returns:
+ True. This enables modules which always exist not to need to include a
+ dummy exists() function.
+ """
+ return True
+
+
+def _ToolModule(self):
+ """Thunk for SCons.Tool.Tool._tool_module to patch in exists() function.
+
+ Returns:
+ The module from the original SCons.Tool.Tool._tool_module call, with an
+ exists() method added if it wasn't present.
+ """
+ module = self._tool_module_orig()
+ if not hasattr(module, 'exists'):
+ module.exists = _ToolExists
+
+ return module
+
+#------------------------------------------------------------------------------
+
+
+def AddSiteDir(site_dir):
+ """Adds a site directory, as if passed to the --site-dir option.
+
+ Args:
+ site_dir: Site directory path to add, relative to the location of the
+ SConstruct file.
+
+ This may be called from the SConscript file to add a local site scons
+ directory for a project. This does the following:
+ * Adds site_dir/site_scons to sys.path.
+ * Imports site_dir/site_init.py.
+ * Adds site_dir/site_scons to the SCons tools path.
+ """
+ # Call the same function that SCons does for the --site-dir option.
+ SCons.Script.Main._load_site_scons_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
+
+#------------------------------------------------------------------------------
+
+
+_new_options_help = '''
+Additional options for SCons:
+
+ --mode=MODE Specify build mode (see below).
+ --host-platform=PLATFORM Force SCons to use PLATFORM as the host platform,
+ instead of the actual platform on which SCons is
+ run. Useful for examining the dependency tree
+ which would be created, but not useful for
+ actually running the build because it'll attempt
+ to use the wrong tools for your actual platform.
+ --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():
+ """Main code executed in site_init."""
+
+ # Let people use new global methods directly.
+ __builtin__.AddSiteDir = AddSiteDir
+ __builtin__.BuildComponents = BuildComponents
+ __builtin__.AddTargetGroup = AddTargetGroup
+
+ # Set list of default tools for component_setup
+ __builtin__.component_setup_tools = [
+ 'command_output',
+ 'component_bits',
+ 'component_builders',
+ 'concat_source',
+ 'defer',
+ 'environment_tools',
+ 'publish',
+ 'replicate',
+ ]
+
+ # Patch Tool._tool_module method to fill in an exists() method for the
+ # module if it isn't present.
+ # TODO(sgk): This functionality should be patched into SCons itself by
+ # changing Tool.__init__().
+ SCons.Tool.Tool._tool_module_orig = SCons.Tool.Tool._tool_module
+ SCons.Tool.Tool._tool_module = _ToolModule
+
+ # Add our options
+ SCons.Script.AddOption(
+ '--mode', '--build-mode',
+ dest='build_mode',
+ nargs=1, type='string',
+ action='store',
+ metavar='MODE',
+ default='default',
+ help='build mode(s)')
+ SCons.Script.AddOption(
+ '--host-platform',
+ dest='host_platform',
+ nargs=1, type='string',
+ action='store',
+ metavar='PLATFORM',
+ help='build mode(s)')
+ SCons.Script.AddOption(
+ '--site-path',
+ dest='site_path',
+ nargs=1, type='string',
+ 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)
+
+ # Check for site path. This is a list of site directories which each are
+ # processed as if they were passed to --site-dir.
+ site_path = SCons.Script.GetOption('site_path')
+ if site_path:
+ for site_dir in site_path.split(','):
+ AddSiteDir(site_dir)
+
+ # Since our site dir was specified on the SCons command line, SCons will
+ # normally only look at our site dir. Add back checking for project-local
+ # site_scons directories.
+ if not SCons.Script.GetOption('no_site_dir'):
+ SCons.Script.Main._load_site_scons_dir(
+ SCons.Node.FS.get_default_fs().SConstruct_dir, None)
+
+# Run main code
+SiteInitMain()
diff --git a/site_scons/site_tools/atlmfc_vc80.py b/site_scons/site_tools/atlmfc_vc80.py
new file mode 100644
index 0000000..d6e31df
--- /dev/null
+++ b/site_scons/site_tools/atlmfc_vc80.py
@@ -0,0 +1,77 @@
+#!/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.
+
+"""Windows ATL MFC for VC80 (Visual Studio 2005) tool for SCons.
+
+Note that ATL MFC requires the commercial (non-free) version of Visual Studio
+2005. Using this in an open-source project thus limits the size of the
+developer community to those with the commercial version.
+"""
+
+import os
+
+
+def _FindLocalInstall():
+ """Returns the directory containing the local install of the tool.
+
+ Returns:
+ Path to tool (as a string), or None if not found.
+ """
+ # TODO(rspangler): Should use a better search. Probably needs to be based on
+ # msvs tool, as msvc detection is.
+ default_dir = 'C:/Program Files/Microsoft Visual Studio 8/VC/atlmfc'
+ if os.path.exists(default_dir):
+ return default_dir
+ else:
+ return None
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ if not env.get('ATLMFC_VC80_DIR'):
+ env['ATLMFC_VC80_DIR'] = _FindLocalInstall()
+
+ env.AppendENVPath('INCLUDE', env.Dir('$ATLMFC_VC80_DIR/include').abspath)
+ env.AppendENVPath('LIB', env.Dir('$ATLMFC_VC80_DIR/lib').abspath)
+
+
+def exists(env):
+ """Returns true if tool exists."""
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+
+ # If directory explicitly specified, we exist
+ if env.get('ATLMFC_VC80_DIR'):
+ return 1
+ elif _FindLocalInstall():
+ return 1
+ else:
+ return 0
diff --git a/site_scons/site_tools/code_signing.py b/site_scons/site_tools/code_signing.py
new file mode 100644
index 0000000..bf647c9
--- /dev/null
+++ b/site_scons/site_tools/code_signing.py
@@ -0,0 +1,103 @@
+#!/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.
+
+"""Code signing build tool.
+
+This module sets up code signing.
+It is used as follows:
+ env = Environment(tools = ["code_signing"])
+To sign an EXE/DLL do:
+ env.SignedBinary('hello_signed.exe', 'hello.exe',
+ CERTIFICATE_FILE='bob.pfx',
+ CERTIFICATE_PASSWORD='123',
+ TIMESTAMP_SERVER='')
+If no certificate file is specified, copying instead of signing will occur.
+If an empty timestamp server string is specified, there will be no timestamp.
+"""
+
+import SCons.Script
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env.Replace(
+ # Path to Microsoft signtool.exe
+ SIGNTOOL='$VC80_DIR/common7/tools/bin/signtool.exe',
+ # No certificate by default.
+ CERTIFICATE_PATH='',
+ # No certificate password by default.
+ CERTIFICATE_PASSWORD='',
+ # The default timestamp server.
+ TIMESTAMP_SERVER='http://timestamp.verisign.com/scripts/timestamp.dll',
+ )
+
+ # Setup Builder for Signing
+ env['BUILDERS']['SignedBinary'] = SCons.Script.Builder(
+ generator=SignedBinaryGenerator,
+ emitter=SignedBinaryEmitter)
+
+
+def SignedBinaryEmitter(target, source, env):
+ """Add the signing certificate (if any) to the source dependencies."""
+ if env['CERTIFICATE_PATH']:
+ source.append(env['CERTIFICATE_PATH'])
+ return target, source
+
+
+def SignedBinaryGenerator(source, target, env, for_signature):
+ """A builder generator for code signing."""
+ source = source # Silence gpylint.
+ target = target # Silence gpylint.
+ for_signature = for_signature # Silence gpylint.
+
+ # Alway copy and make writable.
+ commands = [
+ SCons.Script.Copy('$TARGET', '$SOURCE'),
+ SCons.Script.Chmod('$TARGET', 0755),
+ ]
+
+ # Only do signing if there is a certificate path.
+ if env['CERTIFICATE_PATH']:
+ # The command used to do signing (target added on below).
+ signing_cmd = '$SIGNTOOL sign /f "$CERTIFICATE_PATH"'
+ # Add certificate password if any.
+ if env['CERTIFICATE_PASSWORD']:
+ signing_cmd += ' /p "$CERTIFICATE_PASSWORD"'
+ # Add timestamp server if any.
+ if env['TIMESTAMP_SERVER']:
+ signing_cmd += ' /t "$TIMESTAMP_SERVER"'
+ # Add in target name
+ signing_cmd += ' $TARGET'
+ # Add the signing to the list of commands to perform.
+ commands.append(signing_cmd)
+
+ return commands
diff --git a/site_scons/site_tools/collada_dom.py b/site_scons/site_tools/collada_dom.py
new file mode 100644
index 0000000..bafccd9
--- /dev/null
+++ b/site_scons/site_tools/collada_dom.py
@@ -0,0 +1,69 @@
+#!/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.
+
+"""Collada DOM 1.3.0 tool for SCons."""
+
+
+import sys
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # TODO(rspangler): Should this really be a bit, or should we have a
+ # COLLADA_DOM_VERSION variable
+ env['COLLADA_DOM_VERSION'] = '1.3.0'
+ env.Append(CPPDEFINES=['COLLADA_DOM_130'])
+
+ if sys.platform in ('win32', 'cygwin'):
+ # Use /I for collada includes, so SCons won't scan for their
+ # dependencies (as it would if they were added to CPPPATH).
+ env.Append(CCFLAGS=[
+ '/I$COLLADA_DIR/include',
+ '/I$COLLADA_DIR/include/1.4',
+ ])
+ else:
+ env.Append(CCFLAGS=[
+ '-I$COLLADA_DIR/include',
+ '-I$COLLADA_DIR/include/1.4',
+ ])
+
+
+def exists(env):
+ """Returns true if tool exists."""
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+
+ # If directory explicitly specified, we exist
+ if env.get('COLLADA_DIR'):
+ return 1
+ else:
+ # TODO(rspangler): Don't know how to find it otherwise!
+ return 0
diff --git a/site_scons/site_tools/command_output.py b/site_scons/site_tools/command_output.py
new file mode 100644
index 0000000..ce702b7
--- /dev/null
+++ b/site_scons/site_tools/command_output.py
@@ -0,0 +1,130 @@
+#!/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.
+
+"""Command output builder for SCons."""
+
+
+import os
+import SCons.Script
+import subprocess
+
+
+def RunCommand(cmdargs, cwdir=None, env=None, echo_output=True):
+ """Runs an external command.
+
+ Args:
+ cmdargs: A command string, or a tuple containing the command and its
+ arguments.
+ cwdir: Working directory for the command, if not None.
+ env: Environment variables dict, if not None.
+ echo_output: If True, output will be echoed to stdout.
+
+ Returns:
+ The integer errorlevel from the command.
+ The combined stdout and stderr as a string.
+ """
+
+ # Force unicode string in the environment to strings.
+ if env:
+ env = dict([(k, str(v)) for k, v in env.items()])
+ child = subprocess.Popen(cmdargs, cwd=cwdir, env=env, shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ child_out = []
+ child_retcode = None
+
+ # Need to poll the child process, since the stdout pipe can fill.
+ while child_retcode is None:
+ child_retcode = child.poll()
+ new_out = child.stdout.read()
+ if echo_output:
+ print new_out,
+ child_out.append(new_out)
+
+ if echo_output:
+ print # end last line of output
+ return child_retcode, ''.join(child_out)
+
+
+def CommandOutputBuilder(target, source, env):
+ """Command output builder.
+
+ Args:
+ self: Environment in which to build
+ target: List of target nodes
+ source: List of source nodes
+
+ Returns:
+ None or 0 if successful; nonzero to indicate failure.
+
+ Runs the command specified in the COMMAND_OUTPUT_CMDLINE environment variable
+ and stores its output in the first target file. Additional target files
+ should be specified if the command creates additional output files.
+
+ Runs the command in the COMMAND_OUTPUT_RUN_DIR subdirectory.
+ """
+ env = env.Clone()
+
+ cmdline = env.subst('$COMMAND_OUTPUT_CMDLINE', target=target, source=source)
+ cwdir = env.subst('$COMMAND_OUTPUT_RUN_DIR', target=target, source=source)
+ if cwdir:
+ cwdir = os.path.normpath(cwdir)
+ env.AppendENVPath('PATH', cwdir)
+ env.AppendENVPath('LD_LIBRARY_PATH', cwdir)
+ else:
+ cwdir = None
+
+ retcode, output = RunCommand(cmdline, cwdir=cwdir, env=env['ENV'])
+
+ # Save command line output
+ output_file = open(str(target[0]), 'w')
+ output_file.write(output)
+ output_file.close()
+
+ return retcode
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add the builder and tell it which build environment variables we use.
+ action = SCons.Script.Action(CommandOutputBuilder, varlist=[
+ 'COMMAND_OUTPUT_CMDLINE',
+ 'COMMAND_OUTPUT_RUN_DIR',
+ ])
+ builder = SCons.Script.Builder(action = action)
+ env.Append(BUILDERS={'CommandOutput': builder})
+
+ # Default command line is to run the first input
+ env['COMMAND_OUTPUT_CMDLINE'] = '$SOURCE'
+
+ # TODO(rspangler): add a pseudo-builder which takes an additional command
+ # line as an argument.
diff --git a/site_scons/site_tools/component_bits.py b/site_scons/site_tools/component_bits.py
new file mode 100644
index 0000000..94d8ab9
--- /dev/null
+++ b/site_scons/site_tools/component_bits.py
@@ -0,0 +1,212 @@
+#!/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.
+
+"""Environment bit support for software construction toolkit.
+
+This module is automatically included by the component_setup tool.
+"""
+
+
+import __builtin__
+import SCons
+
+
+_bit_descriptions = {}
+_bits_with_options = set()
+_bit_exclusive_groups = {}
+
+#------------------------------------------------------------------------------
+
+
+def DeclareBit(bit_name, desc, exclusive_groups=tuple()):
+ """Declares and describes the bit.
+
+ Args:
+ bit_name: Name of the bit being described.
+ desc: Description of bit.
+ exclusive_groups: Bit groups which this bit belongs to. At most one bit
+ may be set in each exclusive group.
+
+ Raises:
+ ValueError: The bit has already been defined with a different description,
+ or the description is empty.
+
+ Adds a description for the bit in the global dictionary of bit names. All
+ bits should be described before being used in Bit()/AllBits()/AnyBits().
+ """
+
+ if not desc:
+ raise ValueError('Must supply a description for bit "%s"' % bit_name)
+
+ existing_desc = _bit_descriptions.get(bit_name)
+ if existing_desc and desc != existing_desc:
+ raise ValueError('Cannot describe bit "%s" as "%s" because it has already'
+ 'been described as "%s".' %
+ (bit_name, desc, existing_desc))
+
+ _bit_descriptions[bit_name] = desc
+
+ # Add bit to its exclusive groups
+ for g in exclusive_groups:
+ if g not in _bit_exclusive_groups:
+ _bit_exclusive_groups[g] = set()
+ _bit_exclusive_groups[g].add(bit_name)
+
+#------------------------------------------------------------------------------
+
+
+def Bit(env, bit_name):
+ """Checks if the environment has the bit.
+
+ Args:
+ env: Environment to check.
+ bit_name: Name of the bit to check.
+
+ Returns:
+ True if the bit is present in the environment.
+ """
+ # TODO(rspangler): Add bit sanity checking (description exists, exclusive
+ # groups not violated).
+ return bit_name in env['_BITS']
+
+#------------------------------------------------------------------------------
+
+
+def AllBits(env, *args):
+ """Checks if the environment has all the bits.
+
+ Args:
+ env: Environment to check.
+ args: List of bit names to check.
+
+ Returns:
+ True if every bit listed is present in the environment.
+ """
+ # TODO(rspangler): Add bit sanity checking
+ return set(args).issubset(env['_BITS'])
+
+#------------------------------------------------------------------------------
+
+
+def AnyBits(env, *args):
+ """Checks if the environment has at least one of the bits.
+
+ Args:
+ env: Environment to check.
+ args: List of bit names to check.
+
+ Returns:
+ True if at least one bit listed is present in the environment.
+ """
+ # TODO(rspangler): Add bit sanity checking
+ return set(args).intersection(env['_BITS'])
+
+#------------------------------------------------------------------------------
+
+
+def SetBits(env, *args):
+ """Sets the bits in the environment.
+
+ Args:
+ env: Environment to check.
+ args: List of bit names to set.
+ """
+ # TODO(rspangler): Add bit sanity checking
+ env['_BITS'] = env['_BITS'].union(args)
+
+#------------------------------------------------------------------------------
+
+
+def ClearBits(env, *args):
+ """Sets the bits in the environment.
+
+ Args:
+ env: Environment to check.
+ args: List of bit names to set.
+ """
+ # TODO(rspangler): Add bit sanity checking
+ env['_BITS'] = env['_BITS'].difference(args)
+
+#------------------------------------------------------------------------------
+
+
+def SetBitFromOption(env, bit_name, default):
+ """Sets the bit in the environment from a command line option.
+
+ Args:
+ env: Environment to check.
+ bit_name: Name of the bit to set from a command line option.
+ default: Default value for bit if command line option is not present.
+ """
+ # TODO(rspangler): Add bit sanity checking
+
+ # Add the command line option, if not already present
+ if bit_name not in _bits_with_options:
+ _bits_with_options.add(bit_name)
+ SCons.Script.AddOption('--' + bit_name,
+ dest=bit_name,
+ action='store_true',
+ help='set bit:' + _bit_descriptions[bit_name])
+ SCons.Script.AddOption('--no-' + bit_name,
+ dest=bit_name,
+ action='store_false',
+ help='clear bit:' + _bit_descriptions[bit_name])
+
+ bit_set = env.GetOption(bit_name)
+ if bit_set is None:
+ # Not specified on command line, so use default
+ bit_set = default
+
+ if bit_set:
+ env['_BITS'].add(bit_name)
+ elif bit_name in env['_BITS']:
+ env['_BITS'].remove(bit_name)
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add methods to builtin
+ # TODO(rspangler): These really belong in site_init.py - but if we do that,
+ # what's the right way to access the bit global variables?
+ __builtin__.DeclareBit = DeclareBit
+
+ # Add methods to environment
+ env.AddMethod(AllBits)
+ env.AddMethod(AnyBits)
+ env.AddMethod(Bit)
+ env.AddMethod(ClearBits)
+ env.AddMethod(SetBitFromOption)
+ env.AddMethod(SetBits)
+
+ env['_BITS'] = set()
diff --git a/site_scons/site_tools/component_builders.py b/site_scons/site_tools/component_builders.py
new file mode 100644
index 0000000..ae3e336
--- /dev/null
+++ b/site_scons/site_tools/component_builders.py
@@ -0,0 +1,511 @@
+#!/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 builders for SCons."""
+
+
+import SCons
+
+
+__component_list = {}
+
+
+def _InitializeComponentBuilders(self):
+ """Re-initializes component builders module.
+
+ Args:
+ self: Parent environment.
+ """
+ self = self # Silence gpylint
+
+ __component_list.clear()
+
+
+def _RetrieveComponents(component_name, filter_components=None):
+ """Get the list of all components required by the specified component.
+
+ Args:
+ component_name: Name of the base component.
+ filter_components: List of components NOT to include.
+
+ Returns:
+ A list of the transitive closure of all components required by the base
+ component. That is, if A requires B and B requires C, this returns [B, C].
+
+ """
+ if filter_components:
+ filter_components = set(filter_components)
+ else:
+ filter_components = set()
+
+ components = set([component_name]) # Components always require themselves
+ new_components = set(components)
+ while new_components:
+ # Take next new component and add it to the list we've already scanned.
+ c = new_components.pop()
+ components.add(c)
+ # Add to the list of new components any of c's components that we haven't
+ # seen before.
+ new_components.update(__component_list.get(c, set())
+ - components - filter_components)
+
+ return list(components)
+
+
+def _StoreComponents(self, component_name):
+ """Stores the list of child components for the specified component.
+
+ Args:
+ self: Environment containing component.
+ component_name: Name of the component.
+
+ Adds component references based on the LIBS and COMPONENTS variables in the
+ current environment. Should be called at primary SConscript execution time;
+ use _RetrieveComponents() to get the final components lists in a Defer()'d
+ function.
+ """
+
+ components = set()
+ for clist in ('LIBS', 'COMPONENTS'):
+ components.update(map(self.subst, self.Flatten(self[clist])))
+
+ if component_name not in __component_list:
+ __component_list[component_name] = set()
+ __component_list[component_name].update(components)
+
+
+def _ComponentPlatformSetup(env, builder_name, **kwargs):
+ """Modify an environment to work with a component builder.
+
+ Args:
+ env: Environment to clone.
+ builder_name: Name of the builder.
+ kwargs: Keyword arguments.
+
+ Returns:
+ A modified clone of the environment.
+ """
+ # Clone environment so we can modify it
+ env = env.Clone()
+
+ # Add all keyword arguments to the environment
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Call platform-specific component setup function, if any
+ if env.get('COMPONENT_PLATFORM_SETUP'):
+ env['COMPONENT_PLATFORM_SETUP'](env, builder_name)
+
+ # Return the modified environment
+ return env
+
+#------------------------------------------------------------------------------
+
+# TODO(rspangler): Should be possible to refactor programs, test programs,
+# libs to all publish as packages, for simplicity and code reuse.
+
+
+def ComponentPackageDeferred(env):
+ """Deferred build steps for component package.
+
+ Args:
+ env: Environment from ComponentPackage().
+
+ Sets up the aliases to build the package.
+ """
+ package_name = env['PACKAGE_NAME']
+
+ # Install program and resources
+ all_outputs = []
+ components = _RetrieveComponents(package_name,
+ env.get('COMPONENT_PACKAGE_FILTER'))
+ for resource, dest_dir in env.get('COMPONENT_PACKAGE_RESOURCES').items():
+ all_outputs += env.ReplicatePublished(dest_dir, components, resource)
+
+ # Add installed program and resources to the alias
+ env.Alias(package_name, all_outputs)
+
+
+def ComponentPackage(self, package_name, dest_dir, **kwargs):
+ """Pseudo-builder for package containing other components.
+
+ Args:
+ self: Environment in which we were called.
+ package_name: Name of package.
+ dest_dir: Destination directory for package.
+ args: Positional arguments.
+ kwargs: Keyword arguments.
+
+ Returns:
+ The alias node for the package.
+ """
+ # Clone and modify environment
+ env = _ComponentPlatformSetup(self, 'ComponentPackage', **kwargs)
+
+ env.Replace(
+ PACKAGE_NAME=package_name,
+ PACKAGE_DIR=dest_dir,
+ )
+
+ # Add an empty alias for the package and add it to the right groups
+ a = env.Alias(package_name, [])
+ for group in env['COMPONENT_PACKAGE_GROUPS']:
+ SCons.Script.Alias(group, a)
+
+ # Store list of components for this program
+ env._StoreComponents(package_name)
+
+ # Set up deferred call to replicate resources
+ env.Defer(ComponentPackageDeferred)
+
+ # Return the alias, since it's the only node we have
+ return a
+
+#------------------------------------------------------------------------------
+
+
+def ComponentObject(self, *args, **kwargs):
+ """Pseudo-builder for object to handle platform-dependent type.
+
+ Args:
+ self: Environment in which we were called.
+ args: Positional arguments.
+ kwargs: Keyword arguments.
+
+ Returns:
+ Passthrough return code from env.StaticLibrary() or env.SharedLibrary().
+
+ TODO(rspangler): Perhaps this should be a generator builder, so it can take
+ a list of inputs and return a list of outputs?
+ """
+ # Clone and modify environment
+ env = _ComponentPlatformSetup(self, 'ComponentObject', **kwargs)
+
+ # Make appropriate object type
+ if env.get('COMPONENT_STATIC'):
+ return env.StaticObject(*args, **kwargs)
+ else:
+ return env.SharedObject(*args, **kwargs)
+
+#------------------------------------------------------------------------------
+
+
+def ComponentLibrary(self, lib_name, *args, **kwargs):
+ """Pseudo-builder for library to handle platform-dependent type.
+
+ Args:
+ self: Environment in which we were called.
+ lib_name: Library name.
+ args: Positional arguments.
+ kwargs: Keyword arguments.
+
+ Returns:
+ Passthrough return code from env.StaticLibrary() or env.SharedLibrary().
+ """
+ # Clone and modify environment
+ env = _ComponentPlatformSetup(self, 'ComponentLibrary', **kwargs)
+
+ # Make appropriate library type
+ if env.get('COMPONENT_STATIC'):
+ lib_outputs = env.StaticLibrary(lib_name, *args, **kwargs)
+ else:
+ lib_outputs = env.SharedLibrary(lib_name, *args, **kwargs)
+
+ # Scan library outputs for files we need to link against this library, and
+ # files we need to run executables linked against this library.
+ need_for_link = []
+ need_for_debug = []
+ need_for_run = []
+ for o in lib_outputs:
+ if o.suffix in env['COMPONENT_LIBRARY_LINK_SUFFIXES']:
+ need_for_link.append(o)
+ if o.suffix in env['COMPONENT_LIBRARY_DEBUG_SUFFIXES']:
+ need_for_debug.append(o)
+ if o.suffix == env['SHLIBSUFFIX']:
+ need_for_run.append(o)
+ all_outputs = lib_outputs
+
+ # Install library in intermediate directory, so other libs and programs can
+ # link against it
+ all_outputs += env.Replicate('$COMPONENT_LIBRARY_DIR', need_for_link)
+
+ # Publish output
+ env.Publish(lib_name, 'run', need_for_run)
+ env.Publish(lib_name, 'debug', need_for_debug)
+
+ # Add an alias to build and copy the library, and add it to the right groups
+ a = self.Alias(lib_name, all_outputs)
+ for group in env['COMPONENT_LIBRARY_GROUPS']:
+ SCons.Script.Alias(group, a)
+
+ # Store list of components for this library
+ env._StoreComponents(lib_name)
+
+ # If library should publish itself, publish as if it was a program
+ if env.get('COMPONENT_LIBRARY_PUBLISH'):
+ env['PROGRAM_BASENAME'] = lib_name
+ env.Defer(ComponentProgramDeferred)
+
+ # Return the library outputs
+ return lib_outputs
+
+#------------------------------------------------------------------------------
+
+
+def ComponentTestProgramDeferred(env):
+ """Deferred build steps for test program.
+
+ Args:
+ env: Environment from ComponentTestProgram().
+
+ Sets up the aliases to compile and run the test program.
+ """
+ prog_name = env['PROGRAM_BASENAME']
+
+ # Install program and resources
+ all_outputs = []
+ components = _RetrieveComponents(prog_name)
+ for resource, dest_dir in env.get('COMPONENT_TEST_RESOURCES').items():
+ all_outputs += env.ReplicatePublished(dest_dir, components, resource)
+
+ # Add installed program and resources to the alias
+ env.Alias(prog_name, all_outputs)
+
+ # Add an alias for running the test in the test directory, if there's a test
+ # command line.
+ if env.get('COMPONENT_TEST_CMDLINE'):
+ # Test program is the first run resource we replicated.
+ test_program = env.ReplicatePublished('$TESTS_DIR', prog_name, 'run')
+ env.Replace(
+ COMMAND_OUTPUT_CMDLINE=env['COMPONENT_TEST_CMDLINE'],
+ COMMAND_OUTPUT_RUN_DIR='$TESTS_DIR',
+ )
+ test_out = env.CommandOutput(
+ '$TEST_OUTPUT_DIR/${PROGRAM_BASENAME}.out.txt', test_program)
+ # Running the test requires the test and its libs copied to the tests dir
+ env.Depends(test_out, all_outputs)
+ env.ComponentTestOutput('run_' + prog_name, test_out)
+
+
+def ComponentTestProgram(self, prog_name, *args, **kwargs):
+ """Pseudo-builder for test program to handle platform-dependent type.
+
+ Args:
+ self: Environment in which we were called.
+ prog_name: Test program name.
+ args: Positional arguments.
+ kwargs: Keyword arguments.
+
+ Returns:
+ Output node list from env.Program().
+
+ TODO(rspangler): Should have some sort of support for S/M/L categorization
+ """
+ # Clone and modify environment
+ env = _ComponentPlatformSetup(self, 'ComponentTestProgram', **kwargs)
+
+ env['PROGRAM_BASENAME'] = prog_name
+ env['PROGRAM_NAME'] = '$PROGPREFIX$PROGRAM_BASENAME$PROGSUFFIX'
+
+ # Call env.Program()
+ out_nodes = env.Program(prog_name, *args, **kwargs)
+
+ # Publish output
+ env.Publish(prog_name, 'run', out_nodes[0])
+ env.Publish(prog_name, 'debug', out_nodes[1:])
+
+ # Add an alias to build the program to the right groups
+ a = env.Alias(prog_name, out_nodes)
+ for group in env['COMPONENT_TEST_PROGRAM_GROUPS']:
+ SCons.Script.Alias(group, a)
+
+ # Store list of components for this program
+ env._StoreComponents(prog_name)
+
+ # Set up deferred call to replicate resources and run test
+ env.Defer(ComponentTestProgramDeferred)
+
+ # Return the output node
+ return out_nodes
+
+#------------------------------------------------------------------------------
+
+
+def ComponentProgramDeferred(env):
+ """Deferred build steps for program.
+
+ Args:
+ env: Environment from ComponentProgram().
+
+ Sets up the aliases to compile the program.
+ """
+ prog_name = env['PROGRAM_BASENAME']
+
+ # Install program and resources
+ all_outputs = []
+ components = _RetrieveComponents(prog_name)
+ for resource, dest_dir in env.get('COMPONENT_PROGRAM_RESOURCES').items():
+ all_outputs += env.ReplicatePublished(dest_dir, components, resource)
+
+ # Add installed program and resources to the alias
+ env.Alias(prog_name, all_outputs)
+
+
+def ComponentProgram(self, prog_name, *args, **kwargs):
+ """Pseudo-builder for program to handle platform-dependent type.
+
+ Args:
+ self: Environment in which we were called.
+ prog_name: Test program name.
+ args: Positional arguments.
+ kwargs: Keyword arguments.
+
+ Returns:
+ Output node list from env.Program().
+ """
+ # Clone and modify environment
+ env = _ComponentPlatformSetup(self, 'ComponentProgram', **kwargs)
+
+ env['PROGRAM_BASENAME'] = prog_name
+
+ # Call env.Program()
+ out_nodes = env.Program(prog_name, *args, **kwargs)
+
+ # Publish output
+ env.Publish(prog_name, 'run', out_nodes[0])
+ env.Publish(prog_name, 'debug', out_nodes[1:])
+
+ # Add an alias to build the program to the right groups
+ a = env.Alias(prog_name, out_nodes)
+ for group in env['COMPONENT_PROGRAM_GROUPS']:
+ SCons.Script.Alias(group, a)
+
+ # Store list of components for this program
+ env._StoreComponents(prog_name)
+
+ # Set up deferred call to replicate resources
+ env.Defer(ComponentProgramDeferred)
+
+ # Return the output nodes
+ return out_nodes
+
+#------------------------------------------------------------------------------
+
+
+def ComponentTestOutput(self, test_name, nodes):
+ """Pseudo-builder for test output.
+
+ Args:
+ self: Environment in which we were called.
+ test_name: Test name.
+ nodes: List of files/Nodes output by the test.
+
+ Returns:
+ Passthrough return code from env.Alias().
+ """
+
+ # Add an alias for the test outputs, and add it to the right groups
+ a = self.Alias(test_name, nodes)
+ for group in self['COMPONENT_TEST_OUTPUT_GROUPS']:
+ SCons.Script.Alias(group, a)
+
+ # Return the output node
+ return a
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env.Replace(
+ COMPONENT_LIBRARY_DIR='$TARGET_ROOT/lib',
+ STAGING_DIR='$TARGET_ROOT/staging',
+ TESTS_DIR='$TARGET_ROOT/tests',
+ TEST_OUTPUT_DIR='$TARGET_ROOT/test_output',
+ # Default command line for a test is just the name of the file.
+ # TODO(rspangler): Why doesn't the following work:
+ # COMPONENT_TEST_CMDLINE='${SOURCE.abspath}',
+ # (it generates a SCons error)
+ COMPONENT_TEST_CMDLINE='${PROGRAM_NAME}',
+ COMPONENT_STATIC=True, # Static linking is a sensible default.
+ # Don't publish libraries to the staging dir by themselves by default.
+ COMPONENT_LIBRARY_PUBLISH=False,
+ )
+ env.Append(
+ LIBPATH=['$COMPONENT_LIBRARY_DIR'],
+ RPATH=['$COMPONENT_LIBRARY_DIR'],
+
+ # Default alias groups for component builders
+ COMPONENT_PACKAGE_GROUPS=['all_packages'],
+ COMPONENT_LIBRARY_GROUPS=['all_libraries'],
+ COMPONENT_PROGRAM_GROUPS=['all_programs'],
+ COMPONENT_TEST_PROGRAM_GROUPS=['all_test_programs'],
+ COMPONENT_TEST_OUTPUT_GROUPS=['run_all_tests'],
+
+ # Additional components whose resources should be copied into program
+ # directories, in addition to those from LIBS and the program itself.
+ LIBS=[],
+ COMPONENTS=[],
+
+ # Dicts of what resources should go in each destination directory for
+ # programs and test programs.
+ COMPONENT_PACKAGE_RESOURCES={
+ 'run': '$PACKAGE_DIR',
+ 'debug': '$PACKAGE_DIR',
+ },
+ COMPONENT_PROGRAM_RESOURCES={
+ 'run': '$STAGING_DIR',
+ 'debug': '$STAGING_DIR',
+ },
+ COMPONENT_TEST_RESOURCES={
+ 'run': '$TESTS_DIR',
+ 'debug': '$TESTS_DIR',
+ 'test_input': '$TESTS_DIR',
+ },
+ )
+
+ # Add our pseudo-builder methods
+ env.AddMethod(_InitializeComponentBuilders)
+ env.AddMethod(_StoreComponents)
+ env.AddMethod(ComponentPackage)
+ env.AddMethod(ComponentObject)
+ env.AddMethod(ComponentLibrary)
+ env.AddMethod(ComponentProgram)
+ env.AddMethod(ComponentTestProgram)
+ env.AddMethod(ComponentTestOutput)
+
+ # Add our target groups
+ AddTargetGroup('all_libraries', 'libraries can be built')
+ AddTargetGroup('all_programs', 'programs can be built')
+ AddTargetGroup('all_test_programs', 'tests can be built')
+ AddTargetGroup('all_packages', 'packages can be built')
+ AddTargetGroup('run_all_tests', 'tests can be run')
diff --git a/site_scons/site_tools/component_setup.py b/site_scons/site_tools/component_setup.py
new file mode 100644
index 0000000..947a2be
--- /dev/null
+++ b/site_scons/site_tools/component_setup.py
@@ -0,0 +1,195 @@
+#!/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.
+
+"""Main setup for software construction toolkit.
+
+This module is a SCons tool which should be include in all environments.
+It is used as follows:
+ env = Environment(tools = ['component_setup'])
+and should be the first tool from this toolkit referenced by any environment.
+"""
+
+import os
+import sys
+import SCons
+
+
+#------------------------------------------------------------------------------
+
+
+def InstallUsingLink(target, source, env):
+ """Install function for environment which uses link in preference to copy.
+
+ Args:
+ target: Destintion filename
+ source: Source filename
+ env: Environment
+
+ Returns:
+ Return code from SCons Node link function.
+ """
+
+ # Use link function for Install() and InstallAs(), since it's much much
+ # faster than copying. This is ok for the way we build clients, where we're
+ # installing to a build output directory and not to a permanent location such
+ # as /usr/bin.
+ # 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 generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Use MD5 to tell when files differ, if the timestamps differ. This is
+ # better than pure MD5 (since if the timestamps are the same, we don't need
+ # to rescan the file), and also better than pure timestamp (since if a file
+ # is rebuilt to the same contents, we don't need to trigger the build steps
+ # which depend on it).
+ env.Decider('MD5-timestamp')
+
+ # Use implicit-cache by default. This means that SCons doesn't scan all the
+ # directories looking for include files found in an earlier directory in the
+ # include path. For continuous builds, this is not an issue because they're
+ # usually clean builds (so there wouldn't be a cache to load anyway).
+ #
+ # If you're doing a continuous incremental build, just use the
+ # --implicit-deps-changed option to force SCons to ignore its implicit cache.
+ #
+ # Use SCons.Script.SetOption() rather than env.SetOption() to make it clear
+ # this is a global setting, not just a setting for the current environment.
+ SCons.Script.SetOption('implicit_cache', 1)
+
+ # For duplication order, use hard links then fall back to copying. Don't use
+ # soft links, since those won't do the right thing if the output directory
+ # is tar'd up and moved elsewhere.
+ SCons.Script.SetOption('duplicate', 'hard-copy')
+
+ # Remove the alias namespace lookup function from the list which SCons uses
+ # when coercing strings into nodes. This prevents SCons from looking up
+ # aliases in input/output lists if they're not explicitly coerced via
+ # Alias(), and removes a conflict where a program has the same shorthand
+ # alias as the program name itself. This conflict manifests itself as a
+ # python exception if you try to build a program in multiple modes on linux,
+ # for example:
+ # hammer --mode=dbg,opt port_test
+ new_lookup_list = []
+ for func in env.lookup_list:
+ if func.im_class != SCons.Node.Alias.AliasNameSpace:
+ 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
+ # invoke python.
+ PYTHON = env.File(sys.executable),
+
+ # Get the absolute path to the directory containing main.scons (or
+ # SConstruct). This should be used in place of the SCons variable '#',
+ # since '#' is not always replaced (for example, when being used to set
+ # an environment variable).
+ MAIN_DIR = env.Dir('#').abspath,
+ # Supply deprecated SCONSTRUCT_DIR for legacy suport
+ # TODO(rspangler): remove legacy support once everyone has switched over.
+ SCONSTRUCT_DIR = env.Dir('#').abspath,
+
+ # Use install function above, which uses links in preference to copying.
+ INSTALL = InstallUsingLink,
+
+ # Environments are in the 'all' group by default
+ BUILD_GROUPS=['all'],
+
+ 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=[],
+ )
+
+ # 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.
+ force_host_platform = SCons.Script.GetOption('host_platform')
+ 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
+ # case where the --host_platform option is not used (for instance when the
+ # project has platform suffixes on all the build types).
+ # This will prevent host platforms from mistakenly using each others .sconsign
+ # databases and will allow two host platform builds to occur in the same
+ # shared tree simulataneously.
+ sconsign_dir = env.Dir('$DESTINATION_ROOT').abspath
+ sconsign_filename = '$DESTINATION_ROOT/.sconsign_%s' % sys.platform
+ sconsign_file = env.File(sconsign_filename).abspath
+ # TODO(sgk): SConsignFile() doesn't seem to like it if the destination
+ # directory doesn't already exists, so make sure it exists.
+ if not os.path.isdir(sconsign_dir):
+ os.makedirs(sconsign_dir)
+ SCons.Script.SConsignFile(sconsign_file)
+
+ # Build all by default
+ # TODO(rspangler): This would be more nicely done by creating an 'all'
+ # alias and mapping that to $DESTINATION_ROOT (or the accumulation of all
+ # $TARGET_ROOT's for the environments which apply to the current host
+ # platform). Ideally, that would be done in site_init.py and not here. But
+ # since we can't do that, just set the default to be DESTINATION_ROOT here.
+ # 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')
diff --git a/site_scons/site_tools/concat_source.py b/site_scons/site_tools/concat_source.py
new file mode 100644
index 0000000..9a34f50
--- /dev/null
+++ b/site_scons/site_tools/concat_source.py
@@ -0,0 +1,135 @@
+#!/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.
+
+"""Source concatenation builder for SCons."""
+
+
+import SCons.Script
+
+
+def ConcatSourceBuilder(target, source, env):
+ """ConcatSource builder.
+
+ Args:
+ target: List of target nodes
+ source: List of source nodes
+ env: Environment in which to build
+
+ Returns:
+ None if successful; 1 if error.
+ """
+ if len(target) != 1:
+ print 'ERROR: multiple ConcatSource targets when 1 expected'
+ return 1
+
+ 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)
+
+ if 'msvc' in env['TOOLS']:
+ # Add message pragma for nicer progress indication when building with
+ # MSVC.
+ output_lines.append('#pragma message("--%s")' % (
+ source_path.replace("\\", "/")))
+
+ output_lines.append('#include "%s"' % source_path)
+
+ # Need an EOL at the end of the file for more finicky build tools
+ output_lines.append('\n')
+
+ output_file = open(str(target[0]), 'w')
+ output_file.write('\n'.join(output_lines))
+ output_file.close()
+
+
+def ConcatSourcePseudoBuilder(self, target, source):
+ """ConcatSource pseudo-builder; calls builder or passes through source nodes.
+
+ Args:
+ self: Environment in which to build
+ target: List of target nodes
+ source: List of source nodes
+
+ Returns:
+ If self['CONCAT_SOURCE_ENABLE'], calls self.ConcatSource and returns
+ the list of target nodes. Otherwise, returns the list of source nodes.
+ Source nodes which are not CPP files are passed through unchanged to the
+ list of output nodes.
+ """
+ if self.get('CONCAT_SOURCE_ENABLE', True):
+ # Scan down source list and separate CPP sources (which we concatenate)
+ # 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']:
+ cppsource.append(source_file)
+ else:
+ outputs.append(source_file)
+
+ if len(cppsource) > 1:
+ # More than one file, so concatenate them together
+ outputs += self.ConcatSourceBuilder(target, cppsource)
+ else:
+ # <2 files, so pass them through; no need for a ConcatSource target
+ outputs += cppsource
+ return outputs
+ else:
+ # ConcatSource is disabled, so pass through the list of source nodes.
+ return source
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add the builder
+ action = SCons.Script.Action(ConcatSourceBuilder,
+ varlist = ['CONCAT_SOURCE_SUFFIXES'])
+ builder = SCons.Script.Builder(action = action, suffix = '$CXXFILESUFFIX')
+ env.Append(BUILDERS={'ConcatSourceBuilder': builder})
+
+ # Suffixes of sources we can concatenate. Files not in this list will be
+ # passed through untouched. (Note that on Mac, Objective C/C++ files
+ # cannot be concatenated with regular C/C++ files.)
+ # TODO(rspangler): Probably shouldn't mix C, C++ either...
+ env['CONCAT_SOURCE_SUFFIXES'] = ['.c', '.C', '.cxx', '.cpp', '.c++', '.cc',
+ '.h', '.H', '.hxx', '.hpp', '.hh']
+
+ # Add a psuedo-builder method which can look at the environment to determine
+ # whether to call the ConcatSource builder or not
+ env.AddMethod(ConcatSourcePseudoBuilder, 'ConcatSource')
diff --git a/site_scons/site_tools/defer.py b/site_scons/site_tools/defer.py
new file mode 100644
index 0000000..166f314
--- /dev/null
+++ b/site_scons/site_tools/defer.py
@@ -0,0 +1,178 @@
+#!/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.
+
+"""Defer tool for SCons."""
+
+
+import os
+import sys
+import types
+
+
+__defer_groups = {}
+
+
+def _InitializeDefer(self):
+ """Re-initializes deferred function handling.
+
+ Args:
+ self: Parent environment
+ """
+ # Clear the list of deferred groups
+ __defer_groups.clear()
+
+
+def _ExecuteDefer(self):
+ """Executes deferred functions.
+
+ Args:
+ self: Parent environment
+ """
+ # Save directory, so SConscript functions can occur in the right subdirs
+ oldcwd = os.getcwd()
+
+ # Loop through deferred functions
+ while __defer_groups:
+ did_work = False
+ 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
+ for func, env, cwd in group.func_env_cwd:
+ os.chdir(cwd)
+ func(env)
+ did_work = True
+ del __defer_groups[name]
+ break
+ if not did_work:
+ print 'Error in _ExecuteDefer: dependency cycle detected.'
+ for name, group in __defer_groups.items():
+ print ' %s after: %s' % (name, group.after)
+ # TODO(rspangler): should throw exception?
+ sys.exit(1)
+
+ # Restore directory
+ os.chdir(oldcwd)
+
+
+class DeferFunc(object):
+ """Named list of functions to be deferred."""
+
+ def __init__(self):
+ """Initialize deferred function object."""
+ object.__init__(self)
+ self.func_env_cwd = []
+ self.after = set()
+
+
+def Defer(self, *args, **kwargs):
+ """Adds a deferred function or modifies defer dependencies.
+
+ Args:
+ self: Environment in which Defer() was called
+ args: Positional arguments
+ kwargs: Named arguments
+
+ 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.
+
+ All deferred functions run after all SConscripts. Additional dependencies
+ may be specified with the after= keyword.
+
+ Usage:
+
+ env.Defer(func)
+ # Defer func() until after all SConscripts
+
+ env.Defer(func, after=otherfunc)
+ # Defer func() until otherfunc() runs
+
+ env.Defer(func, 'bob')
+ # Defer func() until after SConscripts, put in group 'bob'
+
+ env.Defer(func2, after='bob')
+ # Defer func2() until after all funcs in 'bob' group have run
+
+ env.Defer(func3, 'sam')
+ # Defer func3() until after SConscripts, put in group 'sam'
+
+ env.Defer('bob', after='sam')
+ # Defer all functions in group 'bob' until after all functions in group
+ # 'sam' have run.
+
+ env.Defer(func4, after=['bob', 'sam'])
+ # Defer func4() until after all functions in groups 'bob' and 'sam' have
+ # run.
+ """
+ # Get name of group to defer and/or the a function
+ name = None
+ func = None
+ for a in args:
+ if isinstance(a, str):
+ name = a
+ elif isinstance(a, types.FunctionType):
+ func = a
+ if func and not name:
+ name = func.__name__
+
+ # 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):
+ after.append(a)
+ elif isinstance(a, types.FunctionType):
+ after.append(a.__name__)
+ elif a is not None:
+ # Deferring
+ # TODO(rspangler): should throw an exception
+ print ('Warning: Defer can only wait for functions or names; ignoring'
+ 'after = ', a)
+
+ # Find the deferred function
+ if name not in __defer_groups:
+ __defer_groups[name] = DeferFunc()
+ group = __defer_groups[name]
+
+ # If we were given a function, also save environment and current directory
+ if func:
+ group.func_env_cwd.append((func, self, os.getcwd()))
+
+ # Add dependencies for the function
+ group.after.update(after)
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env.AddMethod(_InitializeDefer)
+ env.AddMethod(_ExecuteDefer)
+ env.AddMethod(Defer)
diff --git a/site_scons/site_tools/directx_9_0_c.py b/site_scons/site_tools/directx_9_0_c.py
new file mode 100644
index 0000000..535198a
--- /dev/null
+++ b/site_scons/site_tools/directx_9_0_c.py
@@ -0,0 +1,52 @@
+#!/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.
+
+"""Windows DirectX 9.0c tool for SCons."""
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env['DIRECTX9_DIR'] = '$DIRECTX9_0_C_DIR'
+ env.AppendENVPath('INCLUDE', env.Dir('$DIRECTX9_DIR/include').abspath)
+ env.AppendENVPath('LIB', env.Dir('$DIRECTX9_DIR/lib').abspath)
+
+
+def exists(env):
+ """Returns true if tool exists."""
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+
+ # If directory explicitly specified, we exist
+ if env.get('DIRECTX9_0_C_DIR'):
+ return 1
+ else:
+ # TODO(rspangler): Don't know how to find it otherwise!
+ return 0
diff --git a/site_scons/site_tools/directx_9_18_944_0_partial.py b/site_scons/site_tools/directx_9_18_944_0_partial.py
new file mode 100644
index 0000000..f520a71
--- /dev/null
+++ b/site_scons/site_tools/directx_9_18_944_0_partial.py
@@ -0,0 +1,52 @@
+#!/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.
+
+"""Windows DirectX 9.18.944.0 tool for SCons."""
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env['DIRECTX9_DIR'] = '$DIRECTX9_18_944_0_PARTIAL_DIR'
+ env.AppendENVPath('INCLUDE', env.Dir('$DIRECTX9_DIR/include').abspath)
+ env.AppendENVPath('LIB', env.Dir('$DIRECTX9_DIR/lib/x86').abspath)
+
+
+def exists(env):
+ """Returns true if tool exists."""
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+
+ # If directory explicitly specified, we exist
+ if env.get('DIRECTX9_18_944_0_PARTIAL_DIR'):
+ return 1
+ else:
+ # TODO(rspangler): Don't know how to find it otherwise!
+ return 0
diff --git a/site_scons/site_tools/distcc.py b/site_scons/site_tools/distcc.py
new file mode 100644
index 0000000..8f79042
--- /dev/null
+++ b/site_scons/site_tools/distcc.py
@@ -0,0 +1,93 @@
+#!/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.
+
+"""Distcc support for SCons.
+
+Since this modifies the C compiler strings, it must be specified after the
+compiler tool in the tool list.
+
+Distcc support can be enabled by specifying --distcc on the SCons command
+line.
+"""
+
+
+import optparse
+import os
+from SCons.compat._scons_optparse import OptionConflictError
+import SCons.Script
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ if not env.Detect('distcc'):
+ return
+
+ try:
+ SCons.Script.AddOption(
+ '--distcc',
+ dest='distcc',
+ action='store_true',
+ help='enable distcc support')
+ SCons.Script.Help(' --distcc Enable distcc suport.\n')
+
+ except (OptionConflictError, optparse.OptionConflictError):
+ # The distcc tool can be specified for multiple platforms, but the
+ # --distcc option can only be added once. Ignore the error which
+ # results from trying to add it a second time.
+ pass
+
+ # If distcc isn't enabled, stop now
+ if not env.GetOption('distcc'):
+ return
+
+ # Copy DISTCC_HOSTS and HOME environment variables from system environment
+ for envvar in ('DISTCC_HOSTS', 'HOME'):
+ value = env.get(envvar, os.environ.get(envvar))
+ if not value:
+ print 'Warning: %s not set in environment; disabling distcc.' % envvar
+ return
+ env['ENV'][envvar] = value
+
+ # Set name of distcc tool
+ env['DISTCC'] = 'distcc'
+
+ # Modify compilers we support
+ distcc_compilers = env.get('DISTCC_COMPILERS', ['cc', 'gcc', 'c++', 'g++'])
+ for compiler_var in ('CC', 'CXX'):
+ compiler = env.get(compiler_var)
+ if compiler in distcc_compilers:
+ env[compiler_var] = '$DISTCC ' + compiler
+
+
+def exists(env):
+ """Returns true if tool exists."""
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ return env.Detect('distcc')
diff --git a/site_scons/site_tools/environment_tools.py b/site_scons/site_tools/environment_tools.py
new file mode 100644
index 0000000..969a923
--- /dev/null
+++ b/site_scons/site_tools/environment_tools.py
@@ -0,0 +1,210 @@
+#!/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.
+
+"""Set up tools for environments for for software construction toolkit.
+
+This module is a SCons tool which should be include in all environments. It
+will automatically be included by the component_setup tool.
+"""
+
+
+import SCons
+
+
+#------------------------------------------------------------------------------
+
+
+def FilterOut(self, **kw):
+ """Removes values from existing construction variables in an Environment.
+
+ The values to remove should be a list. For example:
+
+ self.FilterOut(CPPDEFINES=['REMOVE_ME', 'ME_TOO'])
+
+ Args:
+ self: Environment to alter.
+ kw: (Any other named arguments are values to remove).
+ """
+
+ 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:
+ if 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.
+
+#------------------------------------------------------------------------------
+
+
+def Overlap(self, values1, values2):
+ """Checks for overlap between the values.
+
+ Args:
+ self: Environment to use for variable substitution.
+ values1: First value(s) to compare. May be a string or list of strings.
+ values2: Second value(s) to compare. May be a string or list of strings.
+
+ Returns:
+ The list of values in common after substitution, or an empty list if
+ the values do no overlap.
+
+ Converts the values to a set of plain strings via self.subst() 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))
+
+ return list(set1.intersection(set2))
+
+
+#------------------------------------------------------------------------------
+
+
+def ApplySConscript(self, sconscript_file):
+ """Applies a SConscript to the current environment.
+
+ Args:
+ self: Environment to modify.
+ sconscript_file: Name of SConscript file to apply.
+
+ Returns:
+ The return value from the call to SConscript().
+
+ ApplySConscript() should be used when an existing SConscript which sets up an
+ environment gets too large, or when there is common setup between multiple
+ environments which can't be reduced into a parent environment which the
+ multiple child environments Clone() from. The latter case is necessary
+ because env.Clone() only enables single inheritance for environments.
+
+ ApplySConscript() is NOT intended to replace the Tool() method. If you need
+ to add methods or builders to one or more environments, do that as a tool
+ (and write unit tests for them).
+
+ ApplySConscript() is equivalent to the following SCons call:
+ SConscript(sconscript_file, exports={'env':self})
+
+ The called SConscript should import the 'env' variable to get access to the
+ calling environment:
+ Import('env')
+
+ Changes made to env in the called SConscript will be applied to the
+ environment calling ApplySConscript() - that is, env in the called SConscript
+ is a reference to the calling environment.
+
+ 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})
+
+#------------------------------------------------------------------------------
+
+
+def BuildSConscript(self, sconscript_file):
+ """Builds a SConscript based on the current environment.
+
+ Args:
+ self: Environment to clone and pass to the called SConscript.
+ sconscript_file: Name of SConscript file to build. If this is a directory,
+ this method will look for sconscript_file+'/build.scons', and if that
+ is not found, sconscript_file+'/SConscript'.
+
+ Returns:
+ The return value from the call to SConscript().
+
+ BuildSConscript() should be used when an existing SConscript which builds a
+ project gets too large, or when a group of SConscripts are logically related
+ but should not directly affect each others' environments (for example, a
+ library might want to build a number of unit tests which exist in
+ subdirectories, but not allow those tests' SConscripts to affect/pollute the
+ library's environment.
+
+ BuildSConscript() is NOT intended to replace the Tool() method. If you need
+ to add methods or builders to one or more environments, do that as a tool
+ (and write unit tests for them).
+
+ BuildSConscript() is equivalent to the following SCons call:
+ SConscript(sconscript_file, exports={'env':self.Clone()})
+ or if sconscript_file is a directory:
+ SConscript(sconscript_file+'/build.scons', exports={'env':self.Clone()})
+
+ The called SConscript should import the 'env' variable to get access to the
+ calling environment:
+ Import('env')
+
+ Changes made to env in the called SConscript will NOT be applied to the
+ environment calling BuildSConscript() - that is, env in the called SConscript
+ is a clone/copy of the calling environment, not a reference to that
+ environment.
+
+ If you need to export multiple variables to the called SConscript, or return
+ variables from it, use the existing SConscript() function.
+ """
+ # Need to look for the source node, since by default SCons will look for the
+ # entry in the variant_dir, which won't exist (and thus won't be a directory
+ # or a file). This isn't a problem in BuildComponents(), since the variant
+ # dir is only set inside its call to SConscript().
+ if self.Entry(sconscript_file).srcnode().isdir():
+ # Building a subdirectory, so look for build.scons or SConscript
+ script_file = sconscript_file + '/build.scons'
+ if not self.File(script_file).srcnode().exists():
+ script_file = sconscript_file + '/SConscript'
+ else:
+ script_file = sconscript_file
+
+ self.SConscript(script_file, exports={'env':self.Clone()})
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add methods to environment
+ env.AddMethod(ApplySConscript)
+ env.AddMethod(BuildSConscript)
+ env.AddMethod(FilterOut)
+ env.AddMethod(Overlap)
diff --git a/site_scons/site_tools/gather_inputs.py b/site_scons/site_tools/gather_inputs.py
new file mode 100644
index 0000000..cae733a
--- /dev/null
+++ b/site_scons/site_tools/gather_inputs.py
@@ -0,0 +1,105 @@
+#!/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.
+
+"""Input gathering tool for SCons."""
+
+
+import re
+import SCons.Script
+
+
+def GatherInputs(env, target, groups=['.*'], exclude_pattern=None):
+ """Find all (non-generated) input files used for a target.
+
+ Args:
+ target: a target node to find source files for
+ For example: File('bob.exe')
+ groups: a list of patterns to use as categories
+ For example: ['.*\\.c$', '.*\\.h$']
+ exclude_pattern: a pattern to exclude from the search
+ For example: '.*third_party.*'
+ Returns:
+ A list of lists of files for each category.
+ Each file will be placed in the first category which matches,
+ even if categories overlap.
+ For example:
+ [['bob.c', 'jim.c'], ['bob.h', 'jim.h']]
+ """
+
+ # Compile exclude pattern if any
+ if exclude_pattern:
+ exclude_pattern = re.compile(exclude_pattern)
+
+ def _FindSources(ptrns, tgt, all):
+ """Internal Recursive function to find all pattern matches."""
+ # Recursively process lists
+ if SCons.Util.is_List(tgt):
+ for t in tgt:
+ _FindSources(ptrns, t, all)
+ else:
+ # Skip if we have been here before
+ if tgt.abspath in all: return
+ # Note that we have been here
+ all[tgt.abspath] = True
+ # Skip ones that match an exclude pattern, if we have one.
+ if exclude_pattern and exclude_pattern.match(tgt.abspath): return
+
+ # Handle non-leaf nodes recursively
+ lst = tgt.children(scan=1)
+ if lst:
+ _FindSources(ptrns, lst, all)
+ return
+
+ # See who it matches
+ for pattern, lst in ptrns.items():
+ # Get real file (backed by repositories).
+ rfile = tgt.rfile()
+ # Add to the list for the first pattern that matches.
+ if pattern.match(rfile.path):
+ lst.append(rfile.abspath)
+ break
+
+ # Prepare a group for each pattern.
+ patterns = {}
+ for g in groups:
+ patterns[re.compile(g, re.IGNORECASE)] = []
+
+ # Do the search.
+ _FindSources(patterns, target, {})
+
+ return patterns.values()
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add a method to gather all inputs needed by a target.
+ env.AddMethod(GatherInputs, 'GatherInputs')
diff --git a/site_scons/site_tools/publish.py b/site_scons/site_tools/publish.py
new file mode 100644
index 0000000..ebd6acf
--- /dev/null
+++ b/site_scons/site_tools/publish.py
@@ -0,0 +1,185 @@
+#!/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.
+
+"""Publish tool for SCons."""
+
+
+__published = {} # List of published resources
+
+#------------------------------------------------------------------------------
+
+
+class PublishItem(object):
+ """Item to be published."""
+
+ def __init__(self, source, subdir):
+ """Initialize object.
+
+ Args:
+ source: Source node.
+ subdir: If not None, subdirectory to copy node into in
+ ReplicatePublished().
+ """
+ object.__init__(self)
+ self.source = source
+ self.subdir = subdir
+
+#------------------------------------------------------------------------------
+
+
+def _InitializePublish(self):
+ """Re-initializes published resources.
+
+ Args:
+ self: Parent environment
+ """
+ self=self # Silence gpylint
+
+ # Clear the dict of published resources
+ __published.clear()
+
+
+def ReplicatePublished(self, target, group_name, resource_type):
+ """Replicate published resources for the group to the target directory.
+
+ Args:
+ self: Environment in which this function was called.
+ target: Target directory for resources.
+ group_name: Name of resource group, or a list of names of resource groups.
+ resource_type: Type of resources (string), or a list of resource types.
+
+ Uses the subdir parameter passed to Publish() when replicating source nodes
+ to the target.
+
+ Returns:
+ The list of target nodes from the calls to Replicate().
+
+ Since this is based on Replicate(), it will also use the REPLICATE_REPLACE
+ variable, if it's set in the calling environment.
+ """
+ target_path = self.Dir(target).abspath
+
+ dest_nodes = []
+ for group in self.Flatten(group_name):
+ for resource in self.Flatten(resource_type):
+ # Get items for publish group and resource type
+ items = __published.get(self.subst(group), {}).get(resource, [])
+ for i in items:
+ if i.subdir:
+ dest_nodes += self.Replicate(target_path + '/' + i.subdir, i.source)
+ else:
+ dest_nodes += self.Replicate(target_path, i.source)
+ return dest_nodes
+
+
+def GetPublished(self, group_name, resource_type):
+ """Returns a list of the published resources of the specified type.
+
+ Args:
+ self: Environment in which this function was called.
+ group_name: Name of resource group, or a list of names of resource groups.
+ resource_type: Type of resources (string), or a list of resource types.
+
+ Returns:
+ A flattened list of the source nodes from calls to Publish() for the
+ specified group and resource type. Returns an empty list if there are
+ no matching resources.
+ """
+ source_list = []
+ for group in self.Flatten(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 i in items:
+ source_list.append(i.source)
+
+ return source_list
+
+
+def Publish(self, group_name, resource_type, source, subdir=None):
+ """Publishes resources for use by other scripts.
+
+ Args:
+ self: Environment in which this function was called.
+ group_name: Name of resource group.
+ resource_type: Type of resources (string).
+ source: Source file(s) to copy. May be a string, Node, or a list of
+ mixed strings or Nodes. Strings will be passed through env.Glob() to
+ evaluate wildcards. If a source evaluates to a directory, the entire
+ directory will be recursively copied.
+ subdir: Subdirectory to which the resources should be copied, relative to
+ the primary directory for that resource type, if not None.
+ """
+ if subdir is None:
+ subdir = '' # Make string so we can append to it
+
+ # Evaluate SCons variables in group name
+ group_name = self.subst(group_name)
+
+ # Get list of sources
+ items = []
+ for source_entry in self.Flatten(source):
+ if type(source_entry) == str:
+ # Search for matches for each source entry
+ source_nodes = self.Glob(source_entry)
+ else:
+ # Source entry is already a file or directory node; no need to glob it
+ source_nodes = [source_entry]
+ for s in source_nodes:
+ if str(s.__class__) == 'SCons.Node.FS.Dir':
+ # Recursively publish all files in subdirectory. Since glob('*')
+ # doesn't match dot files, also glob('.*').
+ self.Publish(group_name, resource_type,
+ [s.abspath + '/*', s.abspath + '/.*'],
+ subdir=subdir + '/' + s.name)
+ else:
+ items.append(PublishItem(s, subdir))
+
+ # Publish items, if any
+ if items:
+ # Get publish group
+ if group_name not in __published:
+ __published[group_name] = {}
+ group = __published[group_name]
+ if resource_type not in group:
+ group[resource_type] = []
+
+ # Publish items into group
+ group[resource_type] += items
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env.AddMethod(_InitializePublish)
+ env.AddMethod(GetPublished)
+ env.AddMethod(Publish)
+ env.AddMethod(ReplicatePublished)
diff --git a/site_scons/site_tools/replace_strings.py b/site_scons/site_tools/replace_strings.py
new file mode 100644
index 0000000..c31f685
--- /dev/null
+++ b/site_scons/site_tools/replace_strings.py
@@ -0,0 +1,81 @@
+#!/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.
+
+"""Search and replace builder for SCons."""
+
+
+import re
+import SCons.Script
+
+
+def ReplaceStrings(target, source, env):
+ """Replace Strings builder, does regex substitution on files.
+
+ Args:
+ target: A single target file node.
+ source: A single input file node.
+ env: Environment in which to build.
+
+ From env:
+ REPLACE_STRINGS: A list of pairs of regex search and replacement strings.
+ The body of the source file has substitution performed on each
+ pair (search_regex, replacement) in order.
+
+ Returns:
+ The target node, a file with contents from source, with the substitutions
+ from REPLACE_STRINGS performed on it.
+
+ For example:
+ env.ReplaceStrings('out', 'in',
+ REPLACE_STRINGS = [('a*', 'b'), ('b', 'CCC')])
+ With 'in' having contents: Haaapy.
+ Outputs: HCCCpy.
+ """
+ # Load text.
+ fh = open(source[0].abspath, 'rb')
+ text = fh.read()
+ fh.close()
+ # Do replacements.
+ for r in env['REPLACE_STRINGS']:
+ text = re.sub(r[0], r[1], text)
+ # Write it out.
+ fh = open(target[0].abspath, 'wb')
+ fh.write(text)
+ fh.close()
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add the builder
+ act = SCons.Script.Action(ReplaceStrings, varlist=['REPLACE_STRINGS'])
+ bld = SCons.Script.Builder(action=act, single_source=True)
+ env.Append(BUILDERS={'ReplaceStrings': bld})
diff --git a/site_scons/site_tools/replicate.py b/site_scons/site_tools/replicate.py
new file mode 100644
index 0000000..6547167b
--- /dev/null
+++ b/site_scons/site_tools/replicate.py
@@ -0,0 +1,123 @@
+#!/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.
+
+"""Replicate tool for SCons."""
+
+
+import re
+
+
+def Replicate(env, target, source, **kw):
+ """Replicates (copies) source files/directories to the target directory.
+
+ Much like env.Install(), with the following differences:
+ * If the source is a directory, recurses through it and calls
+ env.Install() on each source file, rather than copying the entire
+ directory at once. This provides more opportunity for hard linking, and
+ also makes the destination files/directories all writable.
+ * Can take sources which contain env.Glob()-style wildcards.
+ * Can take multiple target directories; will copy to all of them.
+ * Handles duplicate requests.
+
+ Args:
+ env: Environment in which to operate.
+ target: Destination(s) for copy. Must evaluate to a directory via
+ env.Dir(), or a list of directories. If more than one directory is
+ passed, the entire source list will be copied to all target
+ directories.
+ source: Source file(s) to copy. May be a string, Node, or a list of
+ mixed strings or Nodes. Strings will be passed through env.Glob() to
+ evaluate wildcards. If a source evaluates to a directory, the entire
+ directory will be recursively copied.
+
+ From env:
+ REPLICATE_RENAME: A list of pairs of regex search and replacement strings.
+ Each full destination path has substitution performed on each pair
+ (search_regex, replacement) in order.
+
+ env.Replicate('destdir', ['footxt.txt'], REPLICATE_REPLACE = [
+ ('\\.txt', '.bar'), ('est', 'ist')])
+ will copy to 'distdir/footxt.bar'
+
+ In the example above, note the use of \\ to escape the '.' character,
+ so that it doesn't act like the regexp '.' and match any character.
+
+ Returns:
+ A list of the destination nodes from the calls to env.Install().
+ """
+ replace_list = kw.get('REPLICATE_REPLACE', env.get('REPLICATE_REPLACE', []))
+
+ dest_nodes = []
+ for target_entry in env.Flatten(target):
+ for source_entry in env.Flatten(source):
+ if type(source_entry) == str:
+ # Search for matches for each source entry
+ source_nodes = env.Glob(source_entry)
+ else:
+ # Source entry is already a file or directory node; no need to glob it
+ source_nodes = [source_entry]
+ for s in source_nodes:
+ target_name = env.Dir(target_entry).abspath + '/' + s.name
+ # We need to use the following incantation rather than s.isdir() in
+ # order to handle chained replicates (A -> B -> C). The isdir()
+ # function is not properly defined in all of the Node type classes in
+ # SCons. This change is particularly crucial if hardlinks are present,
+ # in which case using isdir() can cause files to be unintentionally
+ # deleted.
+ # TODO(bradnelson): Look into fixing the innards of SCons so this isn't
+ # needed.
+ if str(s.__class__) == 'SCons.Node.FS.Dir':
+ # Recursively copy all files in subdir. Since glob('*') doesn't
+ # match dot files, also glob('.*').
+ dest_nodes += env.Replicate(
+ target_name, [s.abspath + '/*', s.abspath + '/.*'],
+ REPLICATE_REPLACE=replace_list)
+ else:
+ # Apply replacement strings, if any
+ for r in replace_list:
+ target_name = re.sub(r[0], r[1], target_name)
+ target = env.File(target_name)
+ if (target.has_builder()
+ and target.get_builder().name == 'InstallBuilder'
+ and target.sources == [s]):
+ # Already installed that file, so pass through the destination node
+ dest_nodes += [target]
+ else:
+ dest_nodes += env.InstallAs(target_name, s)
+
+ # Return list of destination nodes
+ return dest_nodes
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env.AddMethod(Replicate)
diff --git a/site_scons/site_tools/seven_zip.py b/site_scons/site_tools/seven_zip.py
new file mode 100644
index 0000000..94d807f
--- /dev/null
+++ b/site_scons/site_tools/seven_zip.py
@@ -0,0 +1,145 @@
+#!/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.
+
+
+"""SCons tool for 7zip."""
+
+
+import os
+import shutil
+import subprocess
+import tempfile
+import SCons.Script
+
+
+def SevenZipGetFiles(env, source):
+ """SCons emitter for 7zip extract.
+
+ Examines the source 7z archive to determine the list of files which will be
+ created by extract/unzip operation.
+ Args:
+ env: The SCons environment to get the 7zip command line from.
+ source: The 7zip archive to examine.
+ Returns:
+ The list of filenames in the archive.
+ """
+ # Expand the command to list archive contents.
+ cmd = env.subst('$SEVEN_ZIP l "%s"' % source)
+ # Run it and capture output.
+ output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
+ # Strip off 7-line header and 3-line trailer from 7zip output.
+ lines = output.split('\r\n')[7:-3]
+ # Trim out just the files and their names.
+ files = [i[53:] for i in lines if i[20] != 'D']
+ return files
+
+
+def SevenZipEmitter(target, source, env):
+ """An emitter that decides what nodes are vented from a 7zip archive.
+
+ Args:
+ target: The target directory node.
+ source: The source archive node.
+ env: The environment in which the emit takes place.
+ Returns:
+ The pair (target, source) which lists the emitted targets and sources.
+ """
+ # Remember out dir for later.
+ env['SEVEN_ZIP_OUT_DIR'] = target[0].dir
+ # Get out contents
+ files = SevenZipGetFiles(env, env.subst('$SOURCE', source=source))
+ # Extract a layer deeper if there is only one, and it extension is 7z.
+ if env.get('SEVEN_ZIP_PEEL_LAYERS', False):
+ assert len(files) == 1 and os.path.splitext(files[0])[1] == '.7z'
+ # Create a temporary directory.
+ tmp_dir = tempfile.mkdtemp()
+ # Expand the command to extract the archive to a temporary location.
+ cmd = env.subst('$SEVEN_ZIP x $SOURCE -o"%s"' % tmp_dir, source=source[0])
+ # Run it and swallow output.
+ subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
+ # Get contents.
+ inner_files = SevenZipGetFiles(env, os.path.join(tmp_dir, files[0]))
+ # Make into file nodes.
+ inner_files = [target[0].dir.File(i) for i in inner_files]
+ # Delete temp_dir.
+ shutil.rmtree(tmp_dir)
+ # Decide where to extra working file to.
+ working_file = env.Dir(target[0].dir.abspath +
+ '.7zip_extract').File(files[0])
+ # Combine everything.
+ files = [working_file] + inner_files
+ else:
+ # Make into file nodes.
+ files = [target[0].dir.File(i) for i in files]
+ # Return files as actual target.
+ return (files, source)
+
+
+def SevenZipGenerator(source, target, env, for_signature):
+ """The generator function which decides how to extract a file."""
+
+ # Silence lint.
+ source = source
+ target = target
+ for_signature = for_signature
+
+ if env.get('SEVEN_ZIP_PEEL_LAYERS', False):
+ return [SCons.Script.Delete('$SEVEN_ZIP_OUT_DIR'),
+ '$SEVEN_ZIP x $SOURCE -o"${TARGET.dir}"',
+ '$SEVEN_ZIP x $TARGET -o"$SEVEN_ZIP_OUT_DIR"']
+ else:
+ return [SCons.Script.Delete('$SEVEN_ZIP_OUT_DIR'),
+ '$SEVEN_ZIP x $SOURCE -o"$SEVEN_ZIP_OUT_DIR"']
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env.Replace(
+ SEVEN_ZIP='$SEVEN_ZIP_DIR/7za.exe',
+ SEVEN_ZIP_ARCHIVE_OPTIONS = ['-t7z', '-mx0'],
+ SEVEN_ZIP_COMPRESS_OPTIONS = ['-t7z', '-mx9'],
+ )
+
+ b = SCons.Script.Builder(generator=SevenZipGenerator,
+ emitter=SevenZipEmitter)
+ env['BUILDERS']['Extract7zip'] = b
+
+ b = SCons.Script.Builder(
+ action=('cd $SOURCE && '
+ '$SEVEN_ZIP a $SEVEN_ZIP_ARCHIVE_OPTIONS ${TARGET.abspath} ./'))
+ env['BUILDERS']['Archive7zip'] = b
+
+ b = SCons.Script.Builder(
+ action=('cd ${SOURCE.dir} && '
+ '$SEVEN_ZIP a $SEVEN_ZIP_COMPRESS_OPTIONS '
+ '${TARGET.abspath} ${SOURCE.file}'))
+ env['BUILDERS']['Compress7zip'] = b
diff --git a/site_scons/site_tools/target_debug.py b/site_scons/site_tools/target_debug.py
new file mode 100644
index 0000000..4c7c0f1
--- /dev/null
+++ b/site_scons/site_tools/target_debug.py
@@ -0,0 +1,53 @@
+#!/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.
+
+"""Build tool setup for debug environments.
+
+This module is a SCons tool which setups environments for debugging.
+It is used as follows:
+ debug_env = env.Clone(tools = ['target_debug'])
+"""
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add in general settings.
+ DeclareBit('debug', 'Build is debug, not optimized.')
+ env.SetBits('debug')
+
+ env['TARGET_DEBUG'] = True
+
+ env.Append(
+ CPPDEFINES=['_DEBUG'] + env.get('CPPDEFINES_DEBUG', []),
+ CCFLAGS=env.get('CCFLAGS_DEBUG', []),
+ LINKFLAGS=env.get('LINKFLAGS_DEBUG', []),
+ )
diff --git a/site_scons/site_tools/target_optimized.py b/site_scons/site_tools/target_optimized.py
new file mode 100644
index 0000000..10b819d
--- /dev/null
+++ b/site_scons/site_tools/target_optimized.py
@@ -0,0 +1,50 @@
+#!/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.
+
+"""Build tool setup for optimized environments.
+
+This module is a SCons tool which setups environments for optimized builds.
+It is used as follows:
+ optimized_env = env.Clone(tools = ['target_optimized'])
+"""
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add in general options.
+ env['TARGET_DEBUG'] = False
+
+ env.Append(
+ CPPDEFINES=['NDEBUG'] + env.get('CPPDEFINES_OPTIMIZED', []),
+ CCFLAGS=env.get('CCFLAGS_OPTIMIZED', []),
+ LINKFLAGS=env.get('LINKFLAGS_OPTIMIZED', []),
+ )
diff --git a/site_scons/site_tools/target_platform_linux.py b/site_scons/site_tools/target_platform_linux.py
new file mode 100644
index 0000000..af66b5d
--- /dev/null
+++ b/site_scons/site_tools/target_platform_linux.py
@@ -0,0 +1,93 @@
+#!/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.
+
+"""Build tool setup for Linux.
+
+This module is a SCons tool which should be include in the topmost windows
+environment.
+It is used as follows:
+ env = base_env.Clone(tools = ['component_setup'])
+ linux_env = base_env.Clone(tools = ['target_platform_linux'])
+"""
+
+
+def ComponentPlatformSetup(env, builder_name):
+ """Hook to allow platform to modify environment inside a component builder.
+
+ Args:
+ env: Environment to modify
+ builder_name: Name of the builder
+ """
+ if env.get('ENABLE_EXCEPTIONS'):
+ env.FilterOut(CCFLAGS=['-fno-exceptions'])
+ env.Append(CCFLAGS=['-fexceptions'])
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Use g++
+ env.Tool('g++')
+ env.Tool('gcc')
+ env.Tool('gnulink')
+ env.Tool('ar')
+ env.Tool('as')
+
+ # Declare bits
+ DeclareBit('linux', 'Target platform is linux.',
+ exclusive_groups=('target_platform'))
+ DeclareBit('posix', 'Target platform is posix.')
+ env.SetBits('linux', 'posix')
+
+ env.Replace(
+ TARGET_PLATFORM='LINUX',
+ COMPONENT_PLATFORM_SETUP=ComponentPlatformSetup,
+ )
+
+ env.Append(
+ HOST_PLATFORMS=['LINUX'],
+ CPPDEFINES=['OS_LINUX=OS_LINUX'],
+
+ # Settings for debug
+ CCFLAGS_DEBUG=[
+ '-O0', # turn off optimizations
+ '-g', # turn on debugging info
+ ],
+
+ # Settings for optimized
+ CCFLAGS_OPTIMIZED=['-O2'],
+
+ # Settings for component_builders
+ COMPONENT_LIBRARY_LINK_SUFFIXES=['.so', '.a'],
+ COMPONENT_LIBRARY_DEBUG_SUFFIXES=[],
+ )
diff --git a/site_scons/site_tools/target_platform_mac.py b/site_scons/site_tools/target_platform_mac.py
new file mode 100644
index 0000000..c1a82a9
--- /dev/null
+++ b/site_scons/site_tools/target_platform_mac.py
@@ -0,0 +1,173 @@
+#!/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.
+
+"""Build tool setup for MacOS.
+
+This module is a SCons tool which should be include in the topmost mac
+environment.
+It is used as follows:
+ env = base_env.Clone(tools = ['component_setup'])
+ mac_env = base_env.Clone(tools = ['target_platform_mac'])
+"""
+
+
+import SCons.Script
+
+
+def ComponentPlatformSetup(env, builder_name):
+ """Hook to allow platform to modify environment inside a component builder.
+
+ Args:
+ env: Environment to modify
+ builder_name: Name of the builder
+ """
+ if env.get('ENABLE_EXCEPTIONS'):
+ env.FilterOut(CCFLAGS=['-fno-exceptions'])
+ env.Append(CCFLAGS=['-fexceptions'])
+
+#------------------------------------------------------------------------------
+
+
+def BundlePseudoBuilder(env, target, **kwargs):
+ """MacOS Bundle PseudoBuilder.
+
+ Args:
+ env: Environment in which to build
+ target: Name of the bundle to build
+ kwargs: Additional parameters to set in the environment
+
+ Returns:
+ The target is returned.
+ """
+ # Don't change the environment passed into the pseudo-builder
+ env = env.Clone()
+
+ # Bring keywords args into the environment.
+ for k, v in kwargs.items():
+ env[k] = v
+ # Make sure BUNDLE_RESOURCES is set and not empty; force it to be a list
+ bundle_resources = env.Flatten(env.get('BUNDLE_RESOURCES', []))
+ if not bundle_resources:
+ raise ValueError('BUNDLE_RESOURCES must be set and non-empty')
+
+ # Make each resource into a directory node.
+ # TODO(jrg): this seems a little too restrictive.
+ # bundle_resources = [env.Dir(i) for i in bundle_resources]
+ bundle_resources = [i for i in bundle_resources]
+
+ # Create a PkgInfo file only if BUNDLE_PKGINFO_FILENAME is useful.
+ # (NPAPI bundles are unhappy with PkgInfo files.)
+ # TODO(jrg): discuss method with Bradley
+ if env.get('BUNDLE_PKGINFO_FILENAME'):
+ pkginfo_create_command = ('$BUNDLE_GENERATE_PKGINFO '
+ '>$TARGET/$BUNDLE_PKGINFO_FILENAME')
+ else:
+ pkginfo_create_command = '/bin/echo no PkgInfo will be created' # noop
+
+ # Add the build step for the bundle.
+ p = env.Command(env.Dir(target),
+ [env.File('$BUNDLE_EXE'),
+ env.File('$BUNDLE_INFO_PLIST')] +
+ bundle_resources,
+ [SCons.Script.Delete('$TARGET'),
+ SCons.Script.Mkdir('$TARGET/Contents'),
+ SCons.Script.Mkdir('$TARGET/Contents/MacOS'),
+ SCons.Script.Mkdir('$TARGET/Contents/Resources'),
+ 'cp -f $SOURCE $TARGET/Contents/MacOS',
+ 'cp -f ${SOURCES[1]} $TARGET/Contents',
+ pkginfo_create_command,
+ 'cp -rf ${SOURCES[2:]} $TARGET/Contents/Resources'])
+
+ # Add an alias for this target.
+ # This also allows the 'all_bundles' target to build me.
+ a = env.Alias(target, p)
+ for group in env['COMPONENT_BUNDLE_GROUPS']:
+ SCons.Script.Alias(group, a)
+
+ return env.Dir(target)
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Use g++
+ env.Tool('g++')
+ env.Tool('gcc')
+ env.Tool('gnulink')
+ env.Tool('ar')
+ env.Tool('as')
+ env.Tool('applelink')
+
+ # Declare bits
+ DeclareBit('mac', 'Target platform is mac.',
+ exclusive_groups=('target_platform'))
+ DeclareBit('posix', 'Target platform is posix.')
+ env.SetBits('mac', 'posix')
+
+ env.Replace(
+ TARGET_PLATFORM='MAC',
+ COMPONENT_PLATFORM_SETUP=ComponentPlatformSetup,
+ )
+
+ env.Append(
+ HOST_PLATFORMS=['MAC'],
+ CPPDEFINES=['OS_MACOSX=OS_MACOSX'],
+ BITS=['mac', 'posix'],
+
+ # Settings for debug
+ CCFLAGS_DEBUG=['-g'],
+ LINKFLAGS_DEBUG=['-g'],
+
+ # Settings for optimized
+ CCFLAGS_OPTIMIZED=['-O2'],
+
+ # Settings for component_builders
+ COMPONENT_LIBRARY_LINK_SUFFIXES=['.dylib', '.a'],
+ COMPONENT_LIBRARY_DEBUG_SUFFIXES=[],
+
+ # New 'all' target. Helpful: "hammer -h" now lists it!
+ COMPONENT_BUNDLE_GROUPS=['all_bundles'],
+ )
+
+
+ # Set default values used by the Bundle pseudobuilder.
+ env.Replace(
+ BUNDLE_TYPE='APPL',
+ BUNDLE_STRING='${BUNDLE_TYPE}????',
+ BUNDLE_GENERATE_PKGINFO='echo "${BUNDLE_STRING}"',
+ BUNDLE_PKGINFO_FILENAME='PkgInfo',
+ BUNDLE_INFO_PLIST='Info.plist',
+ )
+
+ # Add the Bundle pseudobuilder.
+ env.AddMethod(BundlePseudoBuilder, 'Bundle')
+
+ # Add our target groups
+ AddTargetGroup('all_bundles', 'bundles can be built')
diff --git a/site_scons/site_tools/target_platform_windows.py b/site_scons/site_tools/target_platform_windows.py
new file mode 100644
index 0000000..8162812
--- /dev/null
+++ b/site_scons/site_tools/target_platform_windows.py
@@ -0,0 +1,262 @@
+#!/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.
+
+"""Build tool setup for Windows.
+
+This module is a SCons tool which should be include in the topmost windows
+environment.
+It is used as follows:
+ env = base_env.Clone(tools = ['component_setup'])
+ win_env = base_env.Clone(tools = ['target_platform_windows'])
+"""
+
+
+import os
+import time
+import command_output
+import SCons.Script
+
+
+def WaitForWritable(target, source, env):
+ """Waits for the target to become writable.
+
+ Args:
+ target: List of target nodes.
+ source: List of source nodes.
+ env: Environment context.
+
+ Returns:
+ Zero if success, nonzero if error.
+
+ This is a necessary hack on Windows, where antivirus software can lock exe
+ files briefly after they're written. This can cause subsequent reads of the
+ file by env.Install() to fail. To prevent these failures, wait for the file
+ to be writable.
+ """
+ target_path = target[0].abspath
+ if not os.path.exists(target_path):
+ return 0 # Nothing to wait for
+
+ for retries in range(10):
+ try:
+ f = open(target_path, 'a+b')
+ f.close()
+ return 0 # Successfully opened file for write, so we're done
+ except (IOError, OSError):
+ print 'Waiting for access to %s...' % target_path
+ time.sleep(1)
+
+ # If we're still here, fail
+ print 'Timeout waiting for access to %s.' % target_path
+ return 1
+
+
+def RunManifest(target, source, env, resource_num):
+ """Run the Microsoft Visual Studio manifest tool (mt.exe).
+
+ Args:
+ target: List of target nodes.
+ source: List of source nodes.
+ env: Environment context.
+ resource_num: Resource number to modify in target (1=exe, 2=dll).
+
+ Returns:
+ Zero if success, nonzero if error.
+
+ The mt.exe tool seems to experience intermittent failures trying to write to
+ .exe or .dll files. Antivirus software makes this worse, but the problem
+ can still occur even if antivirus software is disabled. The failures look
+ like:
+
+ mt.exe : general error c101008d: Failed to write the updated manifest to
+ the resource of file "(name of exe)". Access is denied.
+
+ with mt.exe returning an errorlevel (return code) of 31. The workaround is
+ to retry running mt.exe after a short delay.
+ """
+
+ cmdline = env.subst(
+ 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;%d'
+ % resource_num,
+ target=target, source=source)
+ print cmdline
+
+ for retry in range(5):
+ # If this is a retry, print a message and delay first
+ if retry:
+ # mt.exe failed to write to the target file. Print a warning message,
+ # delay 3 seconds, and retry.
+ print 'Warning: mt.exe failed to write to %s; retrying.' % target[0]
+ time.sleep(3)
+
+ return_code, output = command_output.RunCommand(
+ cmdline, env=env['ENV'], echo_output=False)
+ if return_code != 31: # Something other than the intermittent error
+ break
+
+ # Pass through output (if any) and return code from manifest
+ if output:
+ print output
+ return return_code
+
+
+def RunManifestExe(target, source, env):
+ """Calls RunManifest for updating an executable (resource_num=1)."""
+ return RunManifest(target, source, env, resource_num=1)
+
+
+def RunManifestDll(target, source, env):
+ """Calls RunManifest for updating a dll (resource_num=2)."""
+ return RunManifest(target, source, env, resource_num=2)
+
+
+def ComponentPlatformSetup(env, builder_name):
+ """Hook to allow platform to modify environment inside a component builder.
+
+ Args:
+ env: Environment to modify
+ builder_name: Name of the builder
+ """
+ if env.get('ENABLE_EXCEPTIONS'):
+ env.FilterOut(
+ CPPDEFINES=['_HAS_EXCEPTIONS=0'],
+ # There are problems with LTCG when some files are compiled with
+ # exceptions and some aren't (the v-tables for STL and BOOST classes
+ # don't match). Therefore, turn off LTCG when exceptions are enabled.
+ CCFLAGS=['/GL'],
+ LINKFLAGS=['/LTCG'],
+ ARFLAGS=['/LTCG'],
+ )
+ env.Append(CCFLAGS=['/EHsc'])
+
+ if builder_name in ('ComponentObject', 'ComponentLibrary'):
+ if env.get('COMPONENT_STATIC'):
+ env.Append(CPPDEFINES=['_LIB'])
+ else:
+ env.Append(CPPDEFINES=['_USRDLL', '_WINDLL'])
+
+ if builder_name == 'ComponentTestProgram':
+ env.FilterOut(
+ CPPDEFINES=['_WINDOWS'],
+ LINKFLAGS=['/SUBSYSTEM:WINDOWS'],
+ )
+ env.Append(
+ CPPDEFINES=['_CONSOLE'],
+ LINKFLAGS=['/SUBSYSTEM:CONSOLE'],
+ )
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Set up environment paths first
+
+ # Load various Visual Studio related tools.
+ env.Tool('as')
+ env.Tool('msvs')
+ env.Tool('windows_hard_link')
+
+ pre_msvc_env = env['ENV'].copy()
+
+ env.Tool('msvc')
+ env.Tool('mslib')
+ env.Tool('mslink')
+
+ # The msvc, mslink, and mslib tools search the registry for installed copies
+ # of Visual Studio and prepends them to the PATH, INCLUDE, and LIB
+ # environment variables. Block these changes if necessary.
+ if env.get('MSVC_BLOCK_ENVIRONMENT_CHANGES'):
+ env['ENV'] = pre_msvc_env
+
+ # Declare bits
+ DeclareBit('windows', 'Target platform is windows.',
+ exclusive_groups=('target_platform'))
+ env.SetBits('windows')
+
+ env.Replace(
+ TARGET_PLATFORM='WINDOWS',
+ COMPONENT_PLATFORM_SETUP=ComponentPlatformSetup,
+
+ # A better rebuild command (actually cleans, then rebuild)
+ MSVSREBUILDCOM=''.join(['$MSVSSCONSCOM -c "$MSVSBUILDTARGET" && ',
+ '$MSVSSCONSCOM "$MSVSBUILDTARGET"']),
+ )
+
+ env.Append(
+ HOST_PLATFORMS=['WINDOWS'],
+ CPPDEFINES=['OS_WINDOWS=OS_WINDOWS'],
+
+ # Turn up the warning level
+ CCFLAGS=['/W3'],
+
+ # Force x86 platform for now
+ LINKFLAGS=['/MACHINE:X86'],
+ ARFLAGS=['/MACHINE:X86'],
+
+ # Settings for debug
+ CCFLAGS_DEBUG=[
+ '/Od', # disable optimizations
+ '/RTC1', # enable fast checks
+ '/MTd', # link with LIBCMTD.LIB debug lib
+ ],
+ LINKFLAGS_DEBUG=['/DEBUG'],
+
+ # Settings for optimized
+ CCFLAGS_OPTIMIZED=[
+ '/O1', # optimize for size
+ '/MT', # link with LIBCMT.LIB (multi-threaded, static linked crt)
+ '/GS', # enable security checks
+ ],
+
+ # Settings for component_builders
+ COMPONENT_LIBRARY_LINK_SUFFIXES=['.lib'],
+ COMPONENT_LIBRARY_DEBUG_SUFFIXES=['.pdb'],
+ )
+
+ # Add manifests to EXEs and DLLs
+ wait_action = SCons.Script.Action(WaitForWritable,
+ lambda target, source, env: ''),
+ env['LINKCOM'] = [
+ env['LINKCOM'],
+ SCons.Script.Action(RunManifestExe, lambda target, source, env: ''),
+ SCons.Script.Delete('${TARGET}.manifest'),
+ wait_action,
+ ]
+ env['SHLINKCOM'] = [
+ env['SHLINKCOM'],
+ SCons.Script.Action(RunManifestDll, lambda target, source, env: ''),
+ SCons.Script.Delete('${TARGET}.manifest'),
+ wait_action,
+ ]
+ env['WINDOWS_INSERT_MANIFESTS'] = True
+ env.Append(LINKFLAGS=['-manifest'])
diff --git a/site_scons/site_tools/visual_studio_solution.py b/site_scons/site_tools/visual_studio_solution.py
new file mode 100644
index 0000000..9cb9a4a
--- /dev/null
+++ b/site_scons/site_tools/visual_studio_solution.py
@@ -0,0 +1,131 @@
+#!/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 file generation tool for SCons."""
+
+
+import sys
+import SCons.Script
+
+
+def Solution(env, solution_name,
+ environments,
+ exclude_pattern=None,
+ extra_build_targets=None):
+ """Builds an MSVS solution containing all projects underneath build/win.
+
+ Args:
+ solution_name: Name of the solution.
+ environments: List of environments for variants. Only the first one
+ will be used to build the solutions/projects.
+ exclude_pattern: Files matching this pattern will not be added to the
+ projects and solution.
+ extra_build_targets: Dict of extra build targets, indexed by target
+ name. Each extra build target will be given
+ its own empty project.
+ """
+
+ # Provide an empty dict for the set of extra targets, by default.
+ if extra_build_targets is None:
+ extra_build_targets = dict()
+
+ # Fail if not on windows for now.
+ if sys.platform not in ['win32', 'cygwin']:
+ print ('*** Solution file generation skipped '
+ '(not supported on this platform).')
+ return
+
+ # Add in the msvs tool.
+ env.Tool('msvs')
+
+ # Pick out variants
+ variants = [e['BUILD_TYPE'] for e in environments]
+ # Pick out build targets
+ build_targets = [e.subst('$TARGET_ROOT') for e in environments]
+ # pick out sources, headers, and resources
+ sources, headers, resources, others = env.GatherInputs(
+ [SCons.Script.Dir('.')],
+ ['.+\\.(c|cc|m|mm|cpp)$', # source files
+ '.+\\.(h|hh|hpp)$', # header files
+ '.+\\.(rc)$', # resource files
+ '.*'], # all other files
+ 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)
+ # Collect other projects
+ for e in extra_build_targets:
+ # Explicitly create a node for target, so SCons will expand env variables.
+ build_target = env.File(extra_build_targets[e])
+ # Create an empty project that only has a build target.
+ project_list += env.MSVSProject(target='projects/' + e + '/' + e +
+ env['MSVSPROJECTSUFFIX'],
+ srcs=[],
+ incs=[],
+ resources=[],
+ misc=[],
+ auto_build_solution=0,
+ MSVSCLEANCOM='rem',
+ MSVSBUILDCOM='rem',
+ MSVSREBUILD='rem',
+ buildtarget=build_target,
+ variant=variants[0])
+
+ # Build Visual Studio Solution file.
+ solution =env.MSVSSolution(target=solution_name + env['MSVSSOLUTIONSUFFIX'],
+ projects=project_list,
+ variant=variants)
+ # Explicitly add dependencies.
+ env.Depends(solution, project_list)
+
+ return solution
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add in the gather_inputs tool.
+ env.Tool('gather_inputs')
+
+ # Add a method to generate a combined solution file.
+ env.AddMethod(Solution, 'Solution')
diff --git a/site_scons/site_tools/windows_hard_link.py b/site_scons/site_tools/windows_hard_link.py
new file mode 100644
index 0000000..36c768b
--- /dev/null
+++ b/site_scons/site_tools/windows_hard_link.py
@@ -0,0 +1,108 @@
+#!/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.
+
+"""Hard link support for Windows.
+
+This module is a SCons tool which should be include in the topmost windows
+environment. It is usually included by the target_platform_windows tool.
+"""
+
+
+import os
+import stat
+import sys
+import SCons
+
+if sys.platform in ('win32', 'cygwin'):
+ # Only attempt to load pywin32 on Windows systems
+ try:
+ import win32file
+ except ImportError:
+ print ('Warning: Unable to load win32file module; using copy instead of'
+ ' hard linking for env.Install(). Is pywin32 present?')
+
+#------------------------------------------------------------------------------
+# Python 2.4 and 2.5's os module doesn't support os.link on Windows, even
+# though Windows does have hard-link capability on NTFS filesystems. So by
+# default, SCons will insist on copying files instead of linking them as it
+# does on other (linux,mac) OS's.
+#
+# Use the CreateHardLink() functionality from pywin32 to provide hard link
+# capability on Windows also.
+
+
+def _HardLink(fs, src, dst):
+ """Hard link function for hooking into SCons.Node.FS.
+
+ Args:
+ fs: Filesystem class to use.
+ src: Source filename to link to.
+ dst: Destination link name to create.
+
+ Raises:
+ OSError: The link could not be created.
+ """
+ # A hard link shares file permissions from the source. On Windows, the write
+ # access of the file itself determines whether the file can be deleted
+ # (unlike Linux/Mac, where it's the write access of the containing
+ # directory). So if we made a link from a read-only file, the only way to
+ # delete it would be to make the link writable, which would have the
+ # unintended effect of making the source writable too.
+ #
+ # So if the source is read-only, we can't hard link from it.
+ if not stat.S_IMODE(fs.stat(src)[stat.ST_MODE]) & stat.S_IWRITE:
+ raise OSError('Unsafe to hard-link read-only file: %s' % src)
+
+ # If the file is writable, only hard-link from it if it was build by SCons.
+ # Those files shouldn't later become read-only. We don't hard-link from
+ # writable files which SCons didn't create, because those could become
+ # read-only (for example, following a 'p4 submit'), which as indicated above
+ # would make our link read-only too.
+ if not fs.File(src).has_builder():
+ raise OSError('Unsafe to hard-link file not built by SCons: %s' % src)
+
+ try:
+ win32file.CreateHardLink(dst, src)
+ except win32file.error, msg:
+ # Translate errors into standard OSError which SCons expects.
+ raise OSError(msg)
+
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ env = env # Silence gpylint
+
+ # Patch in our hard link function, if we were able to load pywin32
+ if 'win32file' in globals():
+ SCons.Node.FS._hardlink_func = _HardLink