summaryrefslogtreecommitdiffstats
path: root/o3d/site_scons
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/site_scons')
-rw-r--r--o3d/site_scons/http_download.py77
-rw-r--r--o3d/site_scons/site_init.py453
-rw-r--r--o3d/site_scons/site_tools/atlmfc_vc80.py64
-rw-r--r--o3d/site_scons/site_tools/code_coverage.py120
-rw-r--r--o3d/site_scons/site_tools/code_signing.py127
-rw-r--r--o3d/site_scons/site_tools/collada_dom.py44
-rw-r--r--o3d/site_scons/site_tools/command_output.py236
-rw-r--r--o3d/site_scons/site_tools/component_bits.py285
-rw-r--r--o3d/site_scons/site_tools/component_builders.py619
-rw-r--r--o3d/site_scons/site_tools/component_setup.py270
-rw-r--r--o3d/site_scons/site_tools/component_targets.py269
-rw-r--r--o3d/site_scons/site_tools/component_targets_msvs.py999
-rw-r--r--o3d/site_scons/site_tools/component_targets_xml.py125
-rw-r--r--o3d/site_scons/site_tools/concat_source.py130
-rw-r--r--o3d/site_scons/site_tools/defer.py322
-rw-r--r--o3d/site_scons/site_tools/directx_9_0_c.py40
-rw-r--r--o3d/site_scons/site_tools/directx_9_18_944_0_partial.py40
-rw-r--r--o3d/site_scons/site_tools/distcc.py91
-rw-r--r--o3d/site_scons/site_tools/environment_tools.py284
-rw-r--r--o3d/site_scons/site_tools/gather_inputs.py121
-rw-r--r--o3d/site_scons/site_tools/naclsdk.py233
-rw-r--r--o3d/site_scons/site_tools/publish.py253
-rw-r--r--o3d/site_scons/site_tools/replace_strings.py82
-rw-r--r--o3d/site_scons/site_tools/replicate.py125
-rw-r--r--o3d/site_scons/site_tools/sdl.py184
-rw-r--r--o3d/site_scons/site_tools/seven_zip.py145
-rw-r--r--o3d/site_scons/site_tools/target_debug.py52
-rw-r--r--o3d/site_scons/site_tools/target_optimized.py50
-rw-r--r--o3d/site_scons/site_tools/target_platform_linux.py109
-rw-r--r--o3d/site_scons/site_tools/target_platform_mac.py214
-rw-r--r--o3d/site_scons/site_tools/target_platform_windows.py408
-rw-r--r--o3d/site_scons/site_tools/visual_studio_solution.py131
-rw-r--r--o3d/site_scons/site_tools/windows_hard_link.py108
-rw-r--r--o3d/site_scons/sync_tgz.py72
-rw-r--r--o3d/site_scons/usage_log.py307
35 files changed, 7189 insertions, 0 deletions
diff --git a/o3d/site_scons/http_download.py b/o3d/site_scons/http_download.py
new file mode 100644
index 0000000..abc64e6
--- /dev/null
+++ b/o3d/site_scons/http_download.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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.
+
+"""Download a file from a URL to a file on disk.
+
+This module supports username and password with basic authentication.
+"""
+
+import base64
+import os
+import urllib2
+
+
+def _CreateDirectory(path):
+ """Create a directory tree, ignore if it's already there."""
+ try:
+ os.makedirs(path)
+ return True
+ except os.error:
+ return False
+
+
+def HttpDownload(url, target, username=None, password=None):
+ """Download a file from a remote server.
+
+ Args:
+ url: A URL to download from.
+ target: Filename to write download to.
+ username: Optional username for download.
+ password: Optional password for download (ignored if no username).
+ """
+
+ headers = [('Accept', '*/*')]
+ if username:
+ if password:
+ auth_code = base64.b64encode(username + ':' + password)
+ else:
+ auth_code = base64.b64encode(username)
+ headers.append(('Authorization', 'Basic ' + auth_code))
+ opener = urllib2.build_opener()
+ opener.addheaders = headers
+ urllib2.install_opener(opener)
+ src = urllib2.urlopen(url)
+ data = src.read()
+ src.close()
+
+ _CreateDirectory(os.path.split(target)[0])
+ fh = open(target, 'wb')
+ fh.write(data)
+ fh.close()
diff --git a/o3d/site_scons/site_init.py b/o3d/site_scons/site_init.py
new file mode 100644
index 0000000..09062e8
--- /dev/null
+++ b/o3d/site_scons/site_init.py
@@ -0,0 +1,453 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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
+import usage_log
+
+
+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
+ BuildEnvironments(), only environments with the current host platform will be
+ built. If for some reason you really need to examine the host platform,
+ check env.Bit('host_windows') / env.Bit('host_linux') / env.Bit('host_mac').
+
+ 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
+ help_text = '''
+Use --mode=type to specify the type of build to perform. The following types
+may be specified:
+'''
+
+ for build_type in all_build_types:
+ if build_type not in all_build_groups:
+ help_text += ' %-16s %s\n' % (
+ build_type, build_desc.get(build_type, ''))
+
+ 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 += ' %-16s %s\n' % (g, ','.join(all_build_groups[g]))
+
+ 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 BuildEnvironmentSConscripts(env):
+ """Evaluates SConscripts for the environment.
+
+ Called by BuildEnvironments().
+ """
+ # Read SConscript for each component
+ # TODO: Remove BUILD_COMPONENTS once all projects have transitioned to the
+ # BUILD_SCONSCRIPTS nomenclature.
+ for c in env.SubstList2('$BUILD_SCONSCRIPTS', '$BUILD_COMPONENTS'):
+ # Clone the environment so components can't interfere with each other
+ ec = env.Clone()
+
+ if ec.Entry(c).isdir():
+ # The component is a directory, so assume it contains a SConscript
+ # file.
+ c_dir = ec.Dir(c)
+
+ # Use 'build.scons' as the default filename, but if that doesn't
+ # exist, fall back to 'SConscript'.
+ c_script = c_dir.File('build.scons')
+ if not c_script.exists():
+ c_script = c_dir.File('SConscript')
+ else:
+ # The component is a SConscript file.
+ c_script = ec.File(c)
+ c_dir = c_script.dir
+
+ # Make c_dir a string.
+ c_dir = str(c_dir)
+
+ # Use build_dir differently depending on where the SConscript is.
+ if not ec.RelativePath('$TARGET_ROOT', c_dir).startswith('..'):
+ # The above expression means: if c_dir is $TARGET_ROOT or anything
+ # under it. Going from c_dir to $TARGET_ROOT and dropping the not fails
+ # to include $TARGET_ROOT.
+ # We want to be able to allow people to use addRepository to back things
+ # under $TARGET_ROOT/$OBJ_ROOT with things from above the current
+ # directory. When we are passed a SConscript that is already under
+ # $TARGET_ROOT, we should not use build_dir.
+ ec.SConscript(c_script, exports={'env': ec}, duplicate=0)
+ elif not ec.RelativePath('$MAIN_DIR', c_dir).startswith('..'):
+ # The above expression means: if c_dir is $MAIN_DIR or anything
+ # under it. Going from c_dir to $TARGET_ROOT and dropping the not fails
+ # to include $MAIN_DIR.
+ # Also, if we are passed a SConscript that
+ # is not under $MAIN_DIR, we should fail loudly, because it is unclear how
+ # this will correspond to things under $OBJ_ROOT.
+ ec.SConscript(c_script, build_dir='$OBJ_ROOT/' + c_dir,
+ exports={'env': ec}, duplicate=0)
+ else:
+ raise SCons.Error.UserError(
+ 'Bad location for a SConscript. "%s" is not under '
+ '\$TARGET_ROOT or \$MAIN_DIR' % c_script)
+
+def FilterEnvironments(environments):
+ """Filters out the environments to be actually build from the specified list
+
+ 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_SCONSCRIPTS (and for legacy reasons,
+ BUILD_COMPONENTS) in that environment will be matched.
+
+ Args:
+ environments: List of SCons environments.
+
+ Returns:
+ List of environments which were matched
+ """
+ # Get options
+ build_modes = SCons.Script.GetOption('build_mode')
+ # TODO: 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(',')
+
+ # Check build modes
+ _CheckBuildModes(build_modes, environments, HOST_PLATFORM)
+
+ environments_to_evaluate = []
+ 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):
+ environments_to_evaluate.append(e)
+ return environments_to_evaluate
+
+def BuildEnvironments(environments):
+ """Build a collection of SConscripts under a collection of environments.
+
+ The environments are subject to filtering (c.f. FilterEnvironments)
+
+ Args:
+ environments: List of SCons environments.
+
+ Returns:
+ List of environments which were actually evaluated (built).
+ """
+ usage_log.log.AddEntry('BuildEnvironments start')
+
+ environments_to_evaluate = FilterEnvironments(environments)
+
+ for e in environments_to_evaluate:
+ # Make this the root environment for deferred functions, so they don't
+ # execute until our call to ExecuteDefer().
+ e.SetDeferRoot()
+
+ # Defer building the SConscripts, so that other tools can do
+ # per-environment setup first.
+ e.Defer(BuildEnvironmentSConscripts)
+
+ # Execute deferred functions
+ e.ExecuteDefer()
+
+ # Add help on targets.
+ AddTargetHelp()
+
+ usage_log.log.AddEntry('BuildEnvironments done')
+
+ # Return list of environments actually evaluated
+ return environments_to_evaluate
+
+
+#------------------------------------------------------------------------------
+
+
+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)
+
+
+#------------------------------------------------------------------------------
+
+
+_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.
+ --usage-log=FILE Write XML usage log to FILE.
+'''
+
+def SiteInitMain():
+ """Main code executed in site_init."""
+
+ # Bail out if we've been here before. This is needed to handle the case where
+ # this site_init.py has been dropped into a project directory.
+ if hasattr(__builtin__, 'BuildEnvironments'):
+ return
+
+ usage_log.log.AddEntry('Software Construction Toolkit site init')
+
+ # Let people use new global methods directly.
+ __builtin__.AddSiteDir = AddSiteDir
+ __builtin__.FilterEnvironments = FilterEnvironments
+ __builtin__.BuildEnvironments = BuildEnvironments
+ # Legacy method names
+ # TODO: Remove these once they're no longer used anywhere.
+ __builtin__.BuildComponents = BuildEnvironments
+
+ # Set list of default tools for component_setup
+ __builtin__.component_setup_tools = [
+ # Defer must be first so other tools can register environment
+ # setup/cleanup functions.
+ 'defer',
+ # Component_targets must precede component_builders so builders can
+ # define target groups.
+ 'component_targets',
+ 'command_output',
+ 'component_bits',
+ 'component_builders',
+ 'concat_source',
+ 'environment_tools',
+ 'publish',
+ 'replicate',
+ ]
+
+ # Patch Tool._tool_module method to fill in an exists() method for the
+ # module if it isn't present.
+ # TODO: 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(
+ '--usage-log',
+ dest='usage_log',
+ nargs=1, type='string',
+ action='store',
+ metavar='PATH',
+ help='file to write XML usage log to')
+
+ SCons.Script.Help(_new_options_help)
+
+ # Set up usage log
+ usage_log_file = SCons.Script.GetOption('usage_log')
+ if usage_log_file:
+ usage_log.log.SetOutputFile(usage_log_file)
+
+ # Set current host platform
+ host_platform = SCons.Script.GetOption('host_platform')
+ if not host_platform:
+ host_platform = _HostPlatform()
+ __builtin__.HOST_PLATFORM = host_platform
+
+ # 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/o3d/site_scons/site_tools/atlmfc_vc80.py b/o3d/site_scons/site_tools/atlmfc_vc80.py
new file mode 100644
index 0000000..861b7e30
--- /dev/null
+++ b/o3d/site_scons/site_tools/atlmfc_vc80.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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: 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)
diff --git a/o3d/site_scons/site_tools/code_coverage.py b/o3d/site_scons/site_tools/code_coverage.py
new file mode 100644
index 0000000..75d9210
--- /dev/null
+++ b/o3d/site_scons/site_tools/code_coverage.py
@@ -0,0 +1,120 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 generating code coverage.
+
+This module enhances a debug environment to add code coverage.
+It is used as follows:
+ coverage_env = dbg_env.Clone(tools = ['code_coverage'])
+"""
+
+
+def AddCoverageSetup(env):
+ """Add coverage related build steps and dependency links.
+
+ Args:
+ env: a leaf environment ready to have coverage steps added.
+ """
+ # Add a step to start coverage (for instance windows needs this).
+ # This step should get run before and tests are run.
+ if env.get('COVERAGE_START_CMD', None):
+ start = env.Command('$COVERAGE_START_FILE', [], '$COVERAGE_START_CMD')
+ env.AlwaysBuild(start)
+ else:
+ start = []
+
+ # Add a step to end coverage (used on basically all platforms).
+ # This step should get after all the tests have run.
+ if env.get('COVERAGE_STOP_CMD', None):
+ stop = env.Command('$COVERAGE_OUTPUT_FILE', [], '$COVERAGE_STOP_CMD')
+ env.AlwaysBuild(stop)
+ else:
+ stop = []
+
+ # start must happen before tests run, stop must happen after.
+ for group in env.SubstList2('$COVERAGE_TARGETS'):
+ group_alias = env.Alias(group)
+ # Force each alias to happen after start but before stop.
+ env.Requires(group_alias, start)
+ env.Requires(stop, group_alias)
+ # Force each source of the aliases to happen after start but before stop.
+ # This is needed to work around non-standard aliases in some projects.
+ for test in group_alias:
+ for s in test.sources:
+ env.Requires(s, start)
+ env.Requires(stop, s)
+
+ # Add an alias for coverage.
+ env.Alias('coverage', [start, stop])
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ env['COVERAGE_ENABLED'] = True
+
+ env.SetDefault(
+ # Setup up coverage related tool paths.
+ # These can be overridden elsewhere, if needed, to relocate the tools.
+ COVERAGE_MCOV='mcov',
+ COVERAGE_GENHTML='genhtml',
+ COVERAGE_ANALYZER='coverage_analyzer.exe',
+ COVERAGE_VSPERFCMD='VSPerfCmd.exe',
+ COVERAGE_VSINSTR='vsinstr.exe',
+
+ # Setup coverage related locations.
+ COVERAGE_DIR='$TARGET_ROOT/coverage',
+ COVERAGE_HTML_DIR='$COVERAGE_DIR/html',
+ COVERAGE_START_FILE='$COVERAGE_DIR/start.junk',
+ COVERAGE_OUTPUT_FILE='$COVERAGE_DIR/coverage.lcov',
+
+ # The list of aliases containing test execution targets.
+ COVERAGE_TARGETS=['run_all_tests'],
+ )
+
+ # Add in coverage flags. These come from target_platform_xxx.
+ env.Append(
+ CCFLAGS='$COVERAGE_CCFLAGS',
+ LIBS='$COVERAGE_LIBS',
+ LINKFLAGS='$COVERAGE_LINKFLAGS',
+ SHLINKFLAGS='$COVERAGE_SHLINKFLAGS',
+ )
+
+ # Change the definition of Install if required by the platform.
+ if env.get('COVERAGE_INSTALL'):
+ env['PRECOVERAGE_INSTALL'] = env['INSTALL']
+ env['INSTALL'] = env['COVERAGE_INSTALL']
+
+ # Add any extra paths.
+ env.AppendENVPath('PATH', env.SubstList2('$COVERAGE_EXTRA_PATHS'))
+
+ # Add coverage start/stop and processing in deferred steps.
+ env.Defer(AddCoverageSetup)
diff --git a/o3d/site_scons/site_tools/code_signing.py b/o3d/site_scons/site_tools/code_signing.py
new file mode 100644
index 0000000..7534e92
--- /dev/null
+++ b/o3d/site_scons/site_tools/code_signing.py
@@ -0,0 +1,127 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 optparse
+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."""
+
+ try:
+ SCons.Script.AddOption('--certificate-name',
+ dest='certificate_name',
+ help='select which certificate to use')
+ SCons.Script.Help(
+ ' --certificate-name <NAME> select which signing certificate to use')
+ except (OptionConflictError, optparse.OptionConflictError):
+ # This gets catch to prevent duplicate help being added for this option
+ # for each build type.
+ pass
+
+ env.SetDefault(
+ # 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',
+ # The default certificate store.
+ CERTIFICATE_STORE='my',
+ # Set the certificate name from the command line.
+ CERTIFICATE_NAME=SCons.Script.GetOption('certificate_name'),
+ )
+
+ # 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.subst('$CERTIFICATE_PATH'):
+ source.append(env.subst('$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 file or certificate name.
+ if env.subst('$CERTIFICATE_PATH') or env.subst('$CERTIFICATE_NAME'):
+ # The command used to do signing (target added on below).
+ signing_cmd = '$SIGNTOOL sign '
+ # Add in certificate file if any.
+ if env.subst('$CERTIFICATE_PATH'):
+ signing_cmd += ' /f "$CERTIFICATE_PATH"'
+ # Add certificate password if any.
+ if env.subst('$CERTIFICATE_PASSWORD'):
+ signing_cmd += ' /p "$CERTIFICATE_PASSWORD"'
+ # Add certificate store if any.
+ if env.subst('$CERTIFICATE_NAME'):
+ # The command used to do signing (target added on below).
+ signing_cmd += ' /s "$CERTIFICATE_STORE" /n "$CERTIFICATE_NAME"'
+ # Add timestamp server if any.
+ if env.subst('$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/o3d/site_scons/site_tools/collada_dom.py b/o3d/site_scons/site_tools/collada_dom.py
new file mode 100644
index 0000000..1fc5c05
--- /dev/null
+++ b/o3d/site_scons/site_tools/collada_dom.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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."""
+
+ env.Append(CCFLAGS=[
+ '-I$COLLADA_DIR/include',
+ '-I$COLLADA_DIR/include/1.4',
+ ])
diff --git a/o3d/site_scons/site_tools/command_output.py b/o3d/site_scons/site_tools/command_output.py
new file mode 100644
index 0000000..88b8c39c
--- /dev/null
+++ b/o3d/site_scons/site_tools/command_output.py
@@ -0,0 +1,236 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 signal
+import subprocess
+import sys
+import threading
+import time
+import SCons.Script
+
+
+# TODO: Move KillProcessTree() and RunCommand() into their own module, since
+# they're used by other tools.
+
+
+def KillProcessTree(pid):
+ """Kills the process and all of its child processes.
+
+ Args:
+ pid: process to kill.
+
+ Raises:
+ OSError: Unsupported OS.
+ """
+
+ if sys.platform in ('win32', 'cygwin'):
+ # Use Windows' taskkill utility
+ killproc_path = '%s;%s\\system32;%s\\system32\\wbem' % (
+ (os.environ['SYSTEMROOT'],) * 3)
+ killproc_cmd = 'taskkill /F /T /PID %d' % pid
+ killproc_task = subprocess.Popen(killproc_cmd, shell=True,
+ stdout=subprocess.PIPE,
+ env={'PATH':killproc_path})
+ killproc_task.communicate()
+
+ elif sys.platform in ('linux', 'linux2', 'darwin'):
+ # Use ps to get a list of processes
+ ps_task = subprocess.Popen(['/bin/ps', 'x', '-o', 'pid,ppid'], stdout=subprocess.PIPE)
+ ps_out = ps_task.communicate()[0]
+
+ # Parse out a dict of pid->ppid
+ ppid = {}
+ for ps_line in ps_out.split('\n'):
+ w = ps_line.strip().split()
+ if len(w) < 2:
+ continue # Not enough words in this line to be a process list
+ try:
+ ppid[int(w[0])] = int(w[1])
+ except ValueError:
+ pass # Header or footer
+
+ # For each process, kill it if it or any of its parents is our child
+ for p in ppid:
+ p2 = p
+ while p2:
+ if p2 == pid:
+ os.kill(p, signal.SIGKILL)
+ break
+ p2 = ppid.get(p2)
+
+ else:
+ raise OSError('Unsupported OS for KillProcessTree()')
+
+
+def RunCommand(cmdargs, cwdir=None, env=None, echo_output=True, timeout=None,
+ timeout_errorlevel=14):
+ """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.
+ timeout: If not None, timeout for command in seconds. If command times
+ out, it will be killed and timeout_errorlevel will be returned.
+ timeout_errorlevel: The value to return if the command times out.
+
+ 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()])
+ start_time = time.time()
+ child = subprocess.Popen(cmdargs, cwd=cwdir, env=env, shell=True,
+ universal_newlines=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ child_out = []
+ child_retcode = None
+
+ def _ReadThread():
+ """Thread worker function to read output from child process.
+
+ Necessary since there is no cross-platform way of doing non-blocking
+ reads of the output pipe.
+ """
+ read_run = True
+ while read_run:
+ time.sleep(.1) # So we don't poll too frequently
+ # Need to have a delay of 1 cycle between child completing and
+ # thread exit, to pick up the final output from the child.
+ if child_retcode is not None:
+ read_run = False
+ new_out = child.stdout.read()
+ if new_out:
+ if echo_output:
+ print new_out,
+ child_out.append(new_out)
+
+ read_thread = threading.Thread(target=_ReadThread)
+ read_thread.setDaemon(True)
+ read_thread.start()
+
+ # Wait for child to exit or timeout
+ while child_retcode is None:
+ time.sleep(.1) # So we don't poll too frequently
+ child_retcode = child.poll()
+ if timeout and child_retcode is None:
+ elapsed = time.time() - start_time
+ if elapsed > timeout:
+ print '*** RunCommand() timeout:', cmdargs
+ KillProcessTree(child.pid)
+ child_retcode = timeout_errorlevel
+
+ # Wait a bit for worker thread to pick up final output and die. No need to
+ # worry if it's still alive at the end of this, since it's a daemon thread
+ # and won't block python from exiting. (And since it's blocked, it doesn't
+ # chew up CPU.)
+ read_thread.join(5)
+
+ 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
+ cmdecho = env.get('COMMAND_OUTPUT_ECHO', True)
+ timeout = env.get('COMMAND_OUTPUT_TIMEOUT')
+ timeout_errorlevel = env.get('COMMAND_OUTPUT_TIMEOUT_ERRORLEVEL')
+
+ retcode, output = RunCommand(cmdline, cwdir=cwdir, env=env['ENV'],
+ echo_output=cmdecho, timeout=timeout,
+ timeout_errorlevel=timeout_errorlevel)
+
+ # 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,
+ 'Output "$COMMAND_OUTPUT_CMDLINE" to $TARGET',
+ varlist=[
+ 'COMMAND_OUTPUT_CMDLINE',
+ 'COMMAND_OUTPUT_RUN_DIR',
+ 'COMMAND_OUTPUT_TIMEOUT',
+ 'COMMAND_OUTPUT_TIMEOUT_ERRORLEVEL',
+ # We use COMMAND_OUTPUT_ECHO also, but that doesn't change the
+ # command being run or its output.
+ ], )
+ 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: Add a pseudo-builder which takes an additional command line as an
+ # argument.
diff --git a/o3d/site_scons/site_tools/component_bits.py b/o3d/site_scons/site_tools/component_bits.py
new file mode 100644
index 0000000..a7b7055
--- /dev/null
+++ b/o3d/site_scons/site_tools/component_bits.py
@@ -0,0 +1,285 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 types
+import SCons
+
+
+_bit_descriptions = {}
+_bits_with_options = set()
+_bit_exclusive_groups = {}
+
+#------------------------------------------------------------------------------
+
+
+def _CheckDeclared(bits):
+ """Checks each of the bits to make sure it's been declared.
+
+ Args:
+ bits: List of bits to check.
+
+ Raises:
+ ValueError: A bit has not been declared.
+ """
+ for bit in bits:
+ if bit not in _bit_descriptions:
+ raise ValueError('Bit "%s" used before DeclareBit()' %
+ bit)
+
+
+def _CheckExclusive(already_set, proposed):
+ """Checks if setting proposed bits would violate any exclusive groups.
+
+ Args:
+ already_set: List of bits already set.
+ proposed: List of bits attempting to be set.
+
+ Raises:
+ ValueError: A proposed bit belongs to an exclusive group which already has
+ a bit set.
+ """
+ # Remove any already-set bits from proposed (note this makes a copy of
+ # proposed so we don't alter the passed list).
+ proposed = [bit for bit in proposed if bit not in already_set]
+
+ for group_name, group_bits in _bit_exclusive_groups.items():
+ set_match = group_bits.intersection(already_set)
+ proposed_match = group_bits.intersection(proposed)
+ if set_match and proposed_match:
+ raise ValueError('Unable to set bit "%s" because it belongs to the same '
+ 'exclusive group "%s" as already-set bit "%s"' % (
+ proposed_match.pop(), group_name, set_match.pop()))
+
+
+#------------------------------------------------------------------------------
+
+
+def DeclareBit(bit_name, desc, exclusive_groups=None):
+ """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. May be a string, list of string,
+ or None.
+
+ 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
+ if exclusive_groups:
+ if type(exclusive_groups) == types.StringType:
+ exclusive_groups = [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.
+ """
+ _CheckDeclared([bit_name])
+ 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.
+ """
+ _CheckDeclared(args)
+ 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.
+ """
+ _CheckDeclared(args)
+ 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.
+ """
+ _CheckDeclared(args)
+ _CheckExclusive(env['_BITS'], args)
+ env['_BITS'] = env['_BITS'].union(args)
+
+#------------------------------------------------------------------------------
+
+
+def ClearBits(env, *args):
+ """Clears the bits in the environment.
+
+ Args:
+ env: Environment to check.
+ args: List of bit names to clear (remove).
+ """
+ _CheckDeclared(args)
+ 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.
+ """
+ _CheckDeclared([bit_name])
+
+ # 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
+ __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()
+
+ # Declare bits for common target platforms
+ DeclareBit('linux', 'Target platform is linux.',
+ exclusive_groups=('target_platform'))
+ DeclareBit('mac', 'Target platform is mac.',
+ exclusive_groups=('target_platform'))
+ DeclareBit('windows', 'Target platform is windows.',
+ exclusive_groups=('target_platform'))
+
+ # Declare bits for common host platforms
+ DeclareBit('host_linux', 'Host platform is linux.',
+ exclusive_groups=('host_platform'))
+ DeclareBit('host_mac', 'Host platform is mac.',
+ exclusive_groups=('host_platform'))
+ DeclareBit('host_windows', 'Host platform is windows.',
+ exclusive_groups=('host_platform'))
+
+ # Declare other common bits from target_ tools
+ DeclareBit('debug', 'Build is debug, not optimized.')
+ DeclareBit('posix', 'Target platform is posix.')
+
+ # Set the appropriate host platform bit
+ host_platform_to_bit = {
+ 'MAC': 'host_mac',
+ 'LINUX': 'host_linux',
+ 'WINDOWS': 'host_windows',
+ }
+ if HOST_PLATFORM in host_platform_to_bit:
+ env.SetBits(host_platform_to_bit[HOST_PLATFORM])
diff --git a/o3d/site_scons/site_tools/component_builders.py b/o3d/site_scons/site_tools/component_builders.py
new file mode 100644
index 0000000..69b52d4
--- /dev/null
+++ b/o3d/site_scons/site_tools/component_builders.py
@@ -0,0 +1,619 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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(env):
+ """Re-initializes component builders module.
+
+ Args:
+ env: Environment context
+ """
+ env = env # 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
+
+ # Add compiler flags for included headers, if any
+ env['INCLUDES'] = env.Flatten(env.subst_list(['$INCLUDES']))
+ for h in env['INCLUDES']:
+ env.Append(CCFLAGS=['${CCFLAG_INCLUDE}%s' % h])
+
+ # 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: 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 = []
+ package_filter = env.Flatten(env.subst_list('$COMPONENT_PACKAGE_FILTER'))
+ components = _RetrieveComponents(package_name, 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.
+ 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)
+
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(package_name, TARGET_PATH=dest_dir)
+
+ # Set up deferred call to replicate resources
+ env.Defer(ComponentPackageDeferred)
+
+ # 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: 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'):
+ o = env.StaticObject(*args, **kwargs)
+ else:
+ o = env.SharedObject(*args, **kwargs)
+
+ # Add dependencies on includes
+ env.Depends(o, env['INCLUDES'])
+
+ return o
+
+#------------------------------------------------------------------------------
+
+
+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)
+
+ # Add dependencies on includes
+ env.Depends(lib_outputs, env['INCLUDES'])
+
+ # 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('$LIB_DIR', need_for_link)
+
+ # Publish output
+ env.Publish(lib_name, 'link', need_for_link)
+ 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)
+
+ # Let component_targets know this target is available in the current mode.
+ env.SetTargetProperty(lib_name, TARGET_PATH=lib_outputs[0])
+
+ # If library should publish itself, publish as if it was a program
+ if env.get('COMPONENT_LIBRARY_PUBLISH'):
+ env['PROGRAM_BASENAME'] = lib_name
+ 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 target properties
+ env.SetTargetProperty(
+ prog_name,
+ # The copy of the program we care about is the one in the tests dir
+ EXE='$TESTS_DIR/$PROGRAM_NAME',
+ RUN_CMDLINE='$COMPONENT_TEST_CMDLINE',
+ RUN_DIR='$TESTS_DIR',
+ TARGET_PATH='$TESTS_DIR/$PROGRAM_NAME',
+ )
+
+ # Add an alias for running the test in the test directory, if the test is
+ # runnable and has a test command line.
+ if env.get('COMPONENT_TEST_RUNNABLE') and env.get('COMPONENT_TEST_CMDLINE'):
+ env.Replace(
+ COMMAND_OUTPUT_CMDLINE=env['COMPONENT_TEST_CMDLINE'],
+ COMMAND_OUTPUT_RUN_DIR='$TESTS_DIR',
+ )
+ test_out_name = '$TEST_OUTPUT_DIR/${PROGRAM_BASENAME}.out.txt'
+ if (env.GetOption('component_test_retest')
+ and env.File(test_out_name).exists()):
+ # Delete old test results, so test will rerun.
+ env.Execute(SCons.Script.Delete(test_out_name))
+
+ # Set timeout based on test size
+ timeout = env.get('COMPONENT_TEST_TIMEOUT')
+ if type(timeout) is dict:
+ timeout = timeout.get(env.get('COMPONENT_TEST_SIZE'))
+ if timeout:
+ env['COMMAND_OUTPUT_TIMEOUT'] = timeout
+
+ # Test program is the first run resource we replicated. (Duplicate
+ # replicate is not harmful, and is a handy way to pick out the correct
+ # file from all those we replicated above.)
+ test_program = env.ReplicatePublished('$TESTS_DIR', prog_name, 'run')
+
+ # Run the test. Note that we need to refer to the file by name, so that
+ # SCons will recreate the file node after we've deleted it; if we used the
+ # env.File() we created in the if statement above, SCons would still think
+ # it exists and not rerun the test.
+ test_out = env.CommandOutput(test_out_name, 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)
+
+ # Add target properties
+ env.SetTargetProperty(prog_name, RUN_TARGET='run_' + prog_name)
+
+
+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().
+ """
+ # 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)
+
+ # Add dependencies on includes
+ env.Depends(out_nodes, env['INCLUDES'])
+
+ # 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)
+
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(prog_name, TARGET_PATH=out_nodes[0])
+
+ # Set up deferred call to replicate resources and run test
+ env.Defer(ComponentTestProgramDeferred)
+
+ # 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)
+
+ # Add dependencies on includes
+ env.Depends(out_nodes, env['INCLUDES'])
+
+ # 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)
+
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(prog_name, TARGET_PATH=out_nodes[0])
+
+ # Set up deferred call to replicate resources
+ env.Defer(ComponentProgramDeferred)
+
+ # Return the output nodes
+ return out_nodes
+
+#------------------------------------------------------------------------------
+
+
+def ComponentTestOutput(self, test_name, nodes, **kwargs):
+ """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.
+ kwargs: Keyword arguments.
+
+ Returns:
+ Passthrough return code from env.Alias().
+ """
+
+ # Clone and modify environment
+ env = _ComponentPlatformSetup(self, 'ComponentTestObject', **kwargs)
+
+ # Add an alias for the test output
+ a = env.Alias(test_name, nodes)
+
+ # Determine groups test belongs in
+ if env.get('COMPONENT_TEST_ENABLED'):
+ groups = env.SubstList2('$COMPONENT_TEST_OUTPUT_GROUPS')
+ if env.get('COMPONENT_TEST_SIZE'):
+ groups.append(env.subst('run_${COMPONENT_TEST_SIZE}_tests'))
+ else:
+ # Disabled tests only go in the explicit disabled tests group
+ groups = ['run_disabled_tests']
+
+ for group in groups:
+ SCons.Script.Alias(group, a)
+
+ # Let component_targets know this target is available in the current mode
+ env.SetTargetProperty(test_name, TARGET_PATH=nodes[0])
+
+ # 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(
+ LIB_DIR='$TARGET_ROOT/lib',
+ # TODO: Remove legacy COMPONENT_LIBRARY_DIR, once all users have
+ # transitioned to LIB_DIR
+ COMPONENT_LIBRARY_DIR='$LIB_DIR',
+ STAGING_DIR='$TARGET_ROOT/staging',
+ TESTS_DIR='$TARGET_ROOT/tests',
+ TEST_OUTPUT_DIR='$TARGET_ROOT/test_output',
+ # Default command line for a test is just the name of the file.
+ # TODO: Why doesn't the following work:
+ # COMPONENT_TEST_CMDLINE='${SOURCE.abspath}',
+ # (it generates a SCons error)
+ COMPONENT_TEST_CMDLINE='${PROGRAM_NAME}',
+ # Component tests are runnable by default.
+ COMPONENT_TEST_RUNNABLE=True,
+ # Default test size is large
+ COMPONENT_TEST_SIZE='large',
+ # Default timeouts for component tests
+ COMPONENT_TEST_TIMEOUT={'large': 900, 'medium': 450, 'small': 180},
+ # Tests are enabled by default
+ COMPONENT_TEST_ENABLED=True,
+ # Static linking is a sensible default
+ COMPONENT_STATIC=True,
+ # Don't publish libraries to the staging dir by themselves by default.
+ COMPONENT_LIBRARY_PUBLISH=False,
+ )
+ env.Append(
+ LIBPATH=['$LIB_DIR'],
+ RPATH=['$LIB_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 command line option for retest
+ SCons.Script.AddOption(
+ '--retest',
+ dest='component_test_retest',
+ action='store_true',
+ help='force all tests to rerun')
+ SCons.Script.Help(' --retest '
+ 'Rerun specified tests, ignoring cached results.\n')
+
+ # Defer per-environment initialization, but do before building SConscripts
+ env.Defer(_InitializeComponentBuilders)
+ env.Defer('BuildEnvironmentSConscripts', after=_InitializeComponentBuilders)
+
+ # Add our pseudo-builder methods
+ env.AddMethod(_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')
+ AddTargetGroup('run_disabled_tests', 'tests are disabled')
+ AddTargetGroup('run_small_tests', 'small tests can be run')
+ AddTargetGroup('run_medium_tests', 'medium tests can be run')
+ AddTargetGroup('run_large_tests', 'large tests can be run')
+
diff --git a/o3d/site_scons/site_tools/component_setup.py b/o3d/site_scons/site_tools/component_setup.py
new file mode 100644
index 0000000..a3be218
--- /dev/null
+++ b/o3d/site_scons/site_tools/component_setup.py
@@ -0,0 +1,270 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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
+import usage_log
+
+
+#------------------------------------------------------------------------------
+
+
+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 PreEvaluateVariables(env):
+ """Deferred function to pre-evaluate SCons varables for each build mode.
+
+ Args:
+ env: Environment for the current build mode.
+ """
+ # Convert directory variables to strings. Must use .abspath not str(), since
+ # otherwise $OBJ_ROOT is converted to a relative path, which evaluates
+ # improperly in SConscripts not in $MAIN_DIR.
+ for var in env.SubstList2('$PRE_EVALUATE_DIRS'):
+ env[var] = env.Dir('$' + var).abspath
+
+
+#------------------------------------------------------------------------------
+
+
+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')
+
+ # 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 command line options
+ SCons.Script.AddOption(
+ '--brief',
+ dest='brief_comstr',
+ default=True,
+ action='store_true',
+ help='brief command line output')
+ SCons.Script.AddOption(
+ '--verbose',
+ dest='brief_comstr',
+ default=True,
+ action='store_false',
+ help='verbose command line output')
+
+ # Add help for command line options
+ SCons.Script.Help("""\
+ --verbose Print verbose output while building, including
+ the full command lines for all commands.
+ --brief Print brief output while building (the default).
+ This and --verbose are opposites. Use --silent
+ to turn off all output.
+""")
+
+ # 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: 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,
+ )
+
+ # Specify defaults for variables where we don't need to force replacement
+ env.SetDefault(
+ # Environments are in the 'all' group by default
+ BUILD_GROUPS=['all'],
+
+ # Directories
+ DESTINATION_ROOT='$MAIN_DIR/scons-out$HOST_PLATFORM_SUFFIX',
+ TARGET_ROOT='$DESTINATION_ROOT/$BUILD_TYPE',
+ OBJ_ROOT='$TARGET_ROOT/obj',
+ ARTIFACTS_DIR='$TARGET_ROOT/artifacts',
+ )
+
+ # Add default list of variables we should pre-evaluate for each build mode
+ env.Append(PRE_EVALUATE_DIRS = [
+ 'ARTIFACTS_DIR',
+ 'DESTINATION_ROOT',
+ 'OBJ_ROOT',
+ 'SOURCE_ROOT',
+ 'TARGET_ROOT',
+ 'TOOL_ROOT',
+ ])
+
+ # If a host platform was specified on the command line, need to put the SCons
+ # output in its own destination directory.
+ force_host_platform = SCons.Script.GetOption('host_platform')
+ if force_host_platform:
+ env['HOST_PLATFORM_SUFFIX'] = '-' + force_host_platform
+
+ # 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 other's
+ # .sconsign databases and will allow two host platform builds to occur in the
+ # same # shared tree simulataneously.
+ #
+ # Note that we use sys.platform here rather than HOST_PLATFORM, since we need
+ # different sconsign databases for cygwin vs. win32.
+ sconsign_dir = env.Dir('$DESTINATION_ROOT').abspath
+ sconsign_filename = '$DESTINATION_ROOT/.sconsign_%s' % sys.platform
+ sconsign_file = env.File(sconsign_filename).abspath
+ # SConsignFile() doesn't seem to like it if the destination directory
+ # doesn't already exist, so make sure it exists.
+ # TODO: Remove once SCons has fixed this bug.
+ if not os.path.isdir(sconsign_dir):
+ os.makedirs(sconsign_dir)
+ SCons.Script.SConsignFile(sconsign_file)
+
+ # Build all by default
+ # TODO: 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 (reasonable, since component_setup should pretty
+ # much be the first thing in a SConstruct.
+ env.Default('$DESTINATION_ROOT')
+
+ # Use brief command line strings if necessary
+ if env.GetOption('brief_comstr'):
+ env.SetDefault(
+ ARCOMSTR='________Creating library $TARGET',
+ ASCOMSTR='________Assembling $TARGET',
+ CCCOMSTR='________Compiling $TARGET',
+ CONCAT_SOURCE_COMSTR='________ConcatSource $TARGET',
+ CXXCOMSTR='________Compiling $TARGET',
+ LDMODULECOMSTR='________Building loadable module $TARGET',
+ LINKCOMSTR='________Linking $TARGET',
+ MANIFEST_COMSTR='________Updating manifest for $TARGET',
+ MIDLCOMSTR='________Compiling IDL $TARGET',
+ PCHCOMSTR='________Precompiling $TARGET',
+ RANLIBCOMSTR='________Indexing $TARGET',
+ RCCOMSTR='________Compiling resource $TARGET',
+ SHCCCOMSTR='________Compiling $TARGET',
+ SHCXXCOMSTR='________Compiling $TARGET',
+ SHLINKCOMSTR='________Linking $TARGET',
+ SHMANIFEST_COMSTR='________Updating manifest for $TARGET',
+ )
+
+ # Add other default tools from our toolkit
+ # TODO: Currently this needs to be before SOURCE_ROOT in case a tool needs to
+ # redefine it. Need a better way to handle order-dependency in tool setup.
+ for t in component_setup_tools:
+ env.Tool(t)
+
+ # The following environment replacements use env.Dir() to force immediate
+ # evaluation/substitution of SCons variables. They can't be part of the
+ # preceding env.Replace() since they they may rely indirectly on variables
+ # defined there, and the env.Dir() calls would be evaluated before the
+ # env.Replace().
+
+ # Set default SOURCE_ROOT if there is none, assuming we're in a local
+ # site_scons directory for the project.
+ source_root_relative = os.path.normpath(
+ os.path.join(os.path.dirname(__file__), '../..'))
+ source_root = env.get('SOURCE_ROOT', source_root_relative)
+ env['SOURCE_ROOT'] = env.Dir(source_root).abspath
+
+ usage_log.log.SetParam('component_setup.project_path',
+ env.RelativePath('$SOURCE_ROOT', '$MAIN_DIR'))
+
+ # Make tool root separate from source root so it can be overridden when we
+ # have a common location for tools outside of the current clientspec. Need
+ # to check if it's defined already, so it can be set prior to this tool
+ # being included.
+ tool_root = env.get('TOOL_ROOT', '$SOURCE_ROOT')
+ env['TOOL_ROOT'] = env.Dir(tool_root).abspath
+
+ # Defer pre-evaluating some environment variables, but do before building
+ # SConscripts.
+ env.Defer(PreEvaluateVariables)
+ env.Defer('BuildEnvironmentSConscripts', after=PreEvaluateVariables)
diff --git a/o3d/site_scons/site_tools/component_targets.py b/o3d/site_scons/site_tools/component_targets.py
new file mode 100644
index 0000000..582f6b9
--- /dev/null
+++ b/o3d/site_scons/site_tools/component_targets.py
@@ -0,0 +1,269 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Software construction toolkit target management for SCons."""
+
+
+import __builtin__
+import SCons.Script
+
+
+# Dict of target groups (TargetGroup indexed by group name)
+__target_groups = {}
+
+# Dict of targets (Target indexed by target name)
+__targets = {}
+
+# Dict of target modes (TargetMode indexed by mode name)
+__target_modes = {}
+
+#------------------------------------------------------------------------------
+
+
+class TargetGroup(object):
+ """Target group, as used by AddTargetGroup() and GetTargetGroups()."""
+
+ def __init__(self, name, description):
+ """Initializes the target group.
+
+ Args:
+ name: Name of the target group.
+ description: Description of group.
+ """
+ self.name = name
+ self.description = description
+
+ def GetTargetNames(self):
+ """Returns a list of target name strings for the group."""
+ items = map(str, SCons.Script.Alias(self.name)[0].sources)
+ # Remove duplicates from multiple environments
+ return list(set(items))
+
+#------------------------------------------------------------------------------
+
+
+class TargetMode(object):
+ """Target mode, as used by GetTargetModes()."""
+
+ def __init__(self, name, description):
+ """Initializes the target mode.
+
+ Args:
+ name: Name of the target mode.
+ description: Description of mode.
+ """
+ self.name = name
+ self.description = description
+
+ def GetTargetNames(self):
+ """Returns a list of target name strings for the group."""
+ items = map(str, SCons.Script.Alias(self.name)[0].sources)
+ # Remove duplicates from multiple environments
+ return list(set(items))
+
+#------------------------------------------------------------------------------
+
+
+class Target(object):
+ """Target object."""
+
+ def __init__(self, name):
+ """Initializes the target.
+
+ Args:
+ name: Name of the target.
+ """
+ self.name = name
+ self.properties = {} # Global properties
+ self.mode_properties = {} # Dict of modes to mode-specific properties
+
+#------------------------------------------------------------------------------
+
+
+def AddTargetGroup(name, description):
+ """Adds a target group, used for printing help.
+
+ Args:
+ name: Name of target group. This should be the name of an alias which
+ points to other aliases for the specific targets.
+ description: Description of the target group. Should read properly when
+ appended to 'The following ' - for example, 'programs can be built'.
+ """
+
+ # Warn if the target group already exists with a different description
+ if (name in __target_groups
+ and __target_groups[name].description != description):
+ print ('Warning: Changing description of target group "%s" from "%s" to '
+ '"%s"' % (name, __target_groups[name].description, description))
+ __target_groups[name].description = description
+ else:
+ __target_groups[name] = TargetGroup(name, description)
+
+
+def GetTargetGroups():
+ """Gets the dict of target groups.
+
+ Returns:
+ The dict of target groups, indexed by group name.
+
+ This dict is not fully populated until after BuildEnvironments() has been
+ called.
+ """
+ return __target_groups
+
+
+def GetTargetModes():
+ """Gets the dict of target modes.
+
+ Returns:
+ The dict of target modes, indexed by mode name.
+
+ This dict is not fully populated until after BuildEnvironments() has been
+ called.
+ """
+ # TODO: Better to rename this to # GetTargetBuildEnvironments()? That's a
+ # more description name.
+ return __target_modes
+
+
+def GetTargets():
+ """Gets the dict of targets.
+
+ Returns:
+ The dict of targets, indexed by target name.
+
+ This dict is not fully populated until after BuildEnvironments() has been
+ called.
+ """
+ return __targets
+
+
+def SetTargetProperty(self, target_name, all_modes=False, **kwargs):
+ """Sets one or more properties for a target.
+
+ Args:
+ self: Environment context.
+ target_name: Name of the target.
+ all_modes: If True, property applies to all modes. If false, it applies
+ only to the current mode (determined by self['BUILD_TYPE']).
+ kwargs: Keyword args are used to set properties. Properties will be
+ converted to strings via env.subst().
+
+ For example:
+ foo_test = env.Program(...)[0]
+ env.SetTargetProperty('foo_test', global=True, DESCRIPTION='Foo test')
+ env.SetTargetProperty('foo_test', EXE=foo_test)
+ """
+ # Get the target
+ if target_name not in __targets:
+ __targets[target_name] = Target(target_name)
+ target = __targets[target_name]
+
+ if all_modes:
+ add_to_dict = target.properties
+ else:
+ mode = self.get('BUILD_TYPE')
+ if mode not in target.mode_properties:
+ target.mode_properties[mode] = {}
+ add_to_dict = target.mode_properties[mode]
+
+ # Add values
+ for k, v in kwargs.items():
+ add_to_dict[k] = self.subst(str(v))
+
+
+def AddTargetHelp():
+ """Adds SCons help for the targets, groups, and modes.
+
+ This is called automatically by BuildEnvironments()."""
+ help_text = ''
+
+ for group in GetTargetGroups().values():
+ items = group.GetTargetNames()
+ items.sort()
+ if items:
+ help_text += '\nThe following %s:' % group.description
+ colwidth = max(map(len, items)) + 2
+ cols = 77 / colwidth
+ if cols < 1:
+ cols = 1 # If target names are really long, one per line
+ rows = (len(items) + cols - 1) / cols
+ for row in range(0, rows):
+ help_text += '\n '
+ for i in range(row, len(items), rows):
+ help_text += '%-*s' % (colwidth, items[i])
+ help_text += '\n %s (do all of the above)\n' % group.name
+
+ SCons.Script.Help(help_text)
+
+
+def SetTargetDescription(self, target_name, description):
+ """Convenience function to set a target's global DESCRIPTION property.
+
+ Args:
+ self: Environment context.
+ target_name: Name of the target.
+ description: Description of the target.
+ """
+ self.SetTargetProperty(target_name, all_modes=True, DESCRIPTION=description)
+
+
+def AddTargetMode(env):
+ """Adds the environment as a target mode.
+
+ Args:
+ env: Environment context.
+
+ Called via env.Defer() for each build mode.
+ """
+ # Save the build mode and description
+ mode = env.get('BUILD_TYPE')
+ __target_modes[mode] = TargetMode(mode, env.get('BUILD_TYPE_DESCRIPTION'))
+
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ env = env # Silence gpylint
+
+ __builtin__.AddTargetGroup = AddTargetGroup
+ __builtin__.AddTargetHelp = AddTargetHelp
+ __builtin__.GetTargetGroups = GetTargetGroups
+ __builtin__.GetTargetModes = GetTargetModes
+ __builtin__.GetTargets = GetTargets
+
+ env.AddMethod(SetTargetDescription)
+ env.AddMethod(SetTargetProperty)
+
+ # Defer per-mode setup
+ env.Defer(AddTargetMode)
diff --git a/o3d/site_scons/site_tools/component_targets_msvs.py b/o3d/site_scons/site_tools/component_targets_msvs.py
new file mode 100644
index 0000000..78d5b85
--- /dev/null
+++ b/o3d/site_scons/site_tools/component_targets_msvs.py
@@ -0,0 +1,999 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Visual Studio solution output of component targets for SCons."""
+
+import copy
+import md5
+import os
+import sys
+import xml.dom
+import xml.dom.minidom
+import SCons
+
+
+#------------------------------------------------------------------------------
+
+
+def MakeGuid(name, seed='component_targets_msvs'):
+ """Returns a GUID for the specified target name.
+
+ Args:
+ name: Target name.
+ seed: Seed for MD5 hash.
+ Returns:
+ A GUID-line string calculated from the name and seed.
+
+ This generates something which looks like a GUID, but depends only on the
+ name and seed. This means the same name/seed will always generate the same
+ GUID, so that projects and solutions which refer to each other can explicitly
+ determine the GUID to refer to explicitly. It also means that the GUID will
+ not change when the project for a target is rebuilt.
+ """
+ # Calculate a MD5 signature for the seed and name.
+ d = md5.new(str(seed) + str(name)).hexdigest().upper()
+ # Convert most of the signature to GUID form (discard the rest)
+ guid = ('{' + d[:8] + '-' + d[8:12] + '-' + d[12:16] + '-' + d[16:20]
+ + '-' + d[20:32] + '}')
+ return guid
+
+#------------------------------------------------------------------------------
+
+
+def GetGuidAndNameFromVSProject(project_path):
+ """Reads the GUID from a Visual Studio project file.
+
+ Args:
+ project_path: Path to the Visual Studio project file.
+
+ Returns:
+ The GUID string from the file.
+ The project name from the file.
+ """
+ doc = xml.dom.minidom.parse(project_path)
+ try:
+ n_root = doc.documentElement
+ if n_root.nodeName != 'VisualStudioProject':
+ raise SCons.Errors.UserError('%s is not a Visual Studio project.' %
+ project_path)
+ return (
+ str(n_root.attributes['ProjectGUID'].nodeValue),
+ str(n_root.attributes['Name'].nodeValue),
+ )
+ finally:
+ # Clean up doc
+ doc.unlink()
+
+#------------------------------------------------------------------------------
+
+
+class VSProjectWriter(object):
+ """Visual Studio XML project writer."""
+
+ def __init__(self, project_path):
+ """Initializes the project.
+
+ Args:
+ project_path: Path to the project file.
+ """
+ self.project_path = project_path
+ self.doc = None
+
+ def Create(self, name):
+ """Creates the project document.
+
+ Args:
+ name: Name of the project.
+ """
+ self.name = name
+ self.guid = MakeGuid(name)
+
+ # Create XML doc
+ xml_impl = xml.dom.getDOMImplementation()
+ self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
+
+ # Add attributes to root element
+ self.n_root = self.doc.documentElement
+ self.n_root.setAttribute('ProjectType', 'Visual C++')
+ self.n_root.setAttribute('Version', '8.00')
+ self.n_root.setAttribute('Name', self.name)
+ self.n_root.setAttribute('ProjectGUID', self.guid)
+ self.n_root.setAttribute('RootNamespace', self.name)
+ self.n_root.setAttribute('Keyword', 'MakeFileProj')
+
+ # Add platform list
+ n_platform = self.doc.createElement('Platforms')
+ self.n_root.appendChild(n_platform)
+ n = self.doc.createElement('Platform')
+ n.setAttribute('Name', 'Win32')
+ n_platform.appendChild(n)
+
+ # Add empty ToolFiles section
+ self.n_root.appendChild(self.doc.createElement('ToolFiles'))
+
+ # Add configurations section
+ self.n_configs = self.doc.createElement('Configurations')
+ self.n_root.appendChild(self.n_configs)
+
+ # Add files section
+ self.n_files = self.doc.createElement('Files')
+ self.n_root.appendChild(self.n_files)
+
+ # Add empty Globals section
+ self.n_root.appendChild(self.doc.createElement('Globals'))
+
+ def AddConfig(self, name, attrs, tool_attrs):
+ """Adds a configuration to the project.
+
+ Args:
+ name: Configuration name.
+ attrs: Dict of configuration attributes.
+ tool_attrs: Dict of tool attributes.
+ """
+ # Add configuration node
+ n_config = self.doc.createElement('Configuration')
+ n_config.setAttribute('Name', '%s|Win32' % name)
+ n_config.setAttribute('ConfigurationType', '0')
+ for k, v in attrs.items():
+ n_config.setAttribute(k, v)
+ self.n_configs.appendChild(n_config)
+
+ # Add tool node
+ n_tool = self.doc.createElement('Tool')
+ n_tool.setAttribute('Name', 'VCNMakeTool')
+ n_tool.setAttribute('IncludeSearchPath', '')
+ n_tool.setAttribute('ForcedIncludes', '')
+ n_tool.setAttribute('AssemblySearchPath', '')
+ n_tool.setAttribute('ForcedUsingAssemblies', '')
+ n_tool.setAttribute('CompileAsManaged', '')
+ n_tool.setAttribute('PreprocessorDefinitions', '')
+ for k, v in tool_attrs.items():
+ n_tool.setAttribute(k, v)
+ n_config.appendChild(n_tool)
+
+ def _WalkFolders(self, folder_dict, parent):
+ """Recursively walks the folder tree.
+
+ Args:
+ folder_dict: Dict of folder entries. Entry is
+ either subdir_name:subdir_dict or relative_path_to_file:None.
+ parent: Parent node (folder node for that dict)
+ """
+ entries = folder_dict.keys()
+ entries.sort()
+ for e in entries:
+ if folder_dict[e]:
+ # Folder
+ n_subfolder = self.doc.createElement('Filter')
+ n_subfolder.setAttribute('Name', e)
+ parent.appendChild(n_subfolder)
+ self._WalkFolders(folder_dict[e], n_subfolder)
+ else:
+ # File
+ n_file = self.doc.createElement('File')
+ n_file.setAttribute('RelativePath', e)
+ parent.appendChild(n_file)
+
+ def AddFiles(self, name, files_dict):
+ """Adds files to the project.
+
+ Args:
+ name: Name of the folder. If None, files/folders will be added directly
+ to the files list.
+ files_dict: A dict of files / folders.
+
+ Within the files_dict:
+ * A file entry is relative_path:None
+ * A folder entry is folder_name:files_dict, where files_dict is another
+ dict of this form.
+ """
+ # Create subfolder if necessary
+ if name:
+ n_folder = self.doc.createElement('Filter')
+ n_folder.setAttribute('Name', name)
+ self.n_files.appendChild(n_folder)
+ else:
+ n_folder = self.n_files
+
+ # Recursively add files to the folder
+ self._WalkFolders(files_dict, n_folder)
+
+ def Write(self):
+ """Writes the project file."""
+ f = open(self.project_path, 'wt')
+ self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\n')
+ f.close()
+
+#------------------------------------------------------------------------------
+
+
+def ComponentVSProjectBuilder(target, source, env):
+ """Visual Studio project builder.
+
+ Args:
+ target: Destination file.
+ source: List of sources to be added to the target.
+ env: Environment context.
+
+ Returns:
+ Zero if successful.
+ """
+ source = source # Silence gpylint
+
+ target_name = env['TARGET_NAME']
+ project_file = target[0].path
+ project_to_main = env.RelativePath(target[0].dir, env.Dir('$MAIN_DIR'),
+ sep='/')
+
+ env_hammer_bat = env.Clone(VS_PROJECT_TO_MAIN_DIR=project_to_main)
+ hammer_bat = env_hammer_bat.subst('$COMPONENT_VS_PROJECT_SCRIPT_PATH', raw=1)
+
+ vsp = VSProjectWriter(project_file)
+ vsp.Create(target_name)
+
+ # Add configuration per build mode supported by this target
+ target_path = env['TARGET_PATH']
+ for mode, path in target_path.items():
+ attrs = {}
+ attrs['OutputDirectory'] = '$(ProjectDir)/%s/%s/out' % (mode, target_name)
+ attrs['IntermediateDirectory'] = ('$(ProjectDir)/%s/%s/tmp' %
+ (mode, target_name))
+
+ tool_attrs = {}
+ if path:
+ tool_attrs['Output'] = env.RelativePath(target[0].dir,
+ env.Entry(path), sep='/')
+ build_cmd = '%s --mode=%s %s' % (hammer_bat, mode, target_name)
+ clean_cmd = '%s --mode=%s -c %s' % (hammer_bat, mode, target_name)
+ tool_attrs['BuildCommandLine'] = build_cmd
+ tool_attrs['CleanCommandLine'] = clean_cmd
+ tool_attrs['ReBuildCommandLine'] = clean_cmd + ' && ' + build_cmd
+
+ vsp.AddConfig(mode, attrs, tool_attrs)
+
+ # TODO: Fill in files - at least, the .scons file invoking the target.
+
+ # Write project
+ vsp.Write()
+ return 0
+
+
+def ComponentVSProject(self, target_name, **kwargs):
+ """Visual Studio project pseudo-builder for the specified target.
+
+ Args:
+ self: Environment context.
+ target_name: Name of the target.
+ kwargs: Optional keyword arguments override environment variables in the
+ derived environment used to create the project.
+
+ Returns:
+ A list of output nodes.
+ """
+ # Builder only works on Windows
+ if sys.platform not in ('win32', 'cygwin'):
+ return []
+
+ # Clone environment and add keyword args
+ env = self.Clone()
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Save the target name
+ env['TARGET_NAME'] = target_name
+
+ # Extract the target properties and save in the environment for use by the
+ # real builder.
+ t = GetTargets().get(target_name)
+ env['TARGET_PATH'] = {}
+ if t:
+ for mode, mode_properties in t.mode_properties.items():
+ # Since the target path is what Visual Studio will run, use the EXE
+ # property in preference to TARGET_PATH.
+ target_path = mode_properties.get('EXE',
+ mode_properties.get('TARGET_PATH'))
+ env.Append(TARGET_PATH={mode: target_path})
+ else:
+ # No modes declared for this target. Could be a custom alias created by
+ # a SConscript, rather than a component builder. Assume it can be built in
+ # all modes, but produces no output.
+ for mode in GetTargetModes():
+ env.Append(TARGET_PATH={mode: None})
+
+ # Call the real builder
+ return env.ComponentVSProjectBuilder(
+ '$COMPONENT_VS_PROJECT_DIR/${TARGET_NAME}', [])
+
+#------------------------------------------------------------------------------
+
+
+class SourceWalker(object):
+ """Iterator for walking a node tree and collecting sources.
+
+ This is depth-first, children are visited before the parent. The object
+ can be initialized with any node, and returns the next node on the descent
+ with each Next() call.
+
+ This class does not get caught in node cycles caused, for example, by C
+ header file include loops.
+
+ Based on SCons.Node.Walker.
+ """
+
+ def __init__(self, node, seen, print_interval = 1000):
+ """Initializes the source walker.
+
+ Args:
+ node: Node to start walk from.
+ seen: Set to add seen nodes to, if not None.
+ print_interval: Interval in nodes examined at which to print a progress
+ indicator.
+ """
+ self.interval = print_interval
+ # Put node on stack
+ self.stack = [node]
+ # Scan for node's children, then copy them to node.wkids
+ node.wkids = copy.copy(node.children(scan=1))
+ # Keep track of nodes we've seen (returned)
+ if seen is None:
+ seen = set()
+ self.seen = seen
+ # Add node to history for cycle detection
+ self.history = set()
+ self.history.add(node)
+ # We've seen one node now
+ self.nodes_examined = 1
+ self.unique_nodes = 1
+
+
+ def Next(self):
+ """Get the next node for this walk of the tree.
+
+ Returns:
+ The next node, or None if no more nodes.
+
+ This function is intentionally iterative, not recursive, to sidestep any
+ issues of stack size limitations.
+ """
+ while self.stack:
+ if self.stack[-1].wkids:
+ # Node has children we haven't examined, so iterate into the first
+ # child
+ node = self.stack[-1].wkids.pop(0)
+ if not self.stack[-1].wkids:
+ # No more children of this node
+ self.stack[-1].wkids = None
+ self.nodes_examined += 1
+ if self.interval and not self.nodes_examined % self.interval:
+ self.PrintProgress()
+ if (node not in self.history) and (node not in self.seen):
+ # Haven't hit a cycle or a node we've already seen
+ node.wkids = copy.copy(node.children(scan=1))
+ self.stack.append(node)
+ self.history.add(node)
+ else:
+ # Coming back from iterating, so return the next node on the stack.
+ node = self.stack.pop()
+ self.history.remove(node)
+ self.seen.add(node)
+ self.unique_nodes += 1
+ return node
+ return None
+
+ def PrintProgress(self):
+ """Prints a progress indicator."""
+ print ' Examined %d nodes, found %d unique...' % (
+ self.nodes_examined, self.unique_nodes)
+
+ def WalkAll(self):
+ """Walks all nodes in the the tree."""
+ while self.Next():
+ pass
+ if self.interval:
+ self.PrintProgress()
+
+
+def ComponentVSSourceProjectBuilder(target, source, env):
+ """Visual Studio source project builder.
+
+ Args:
+ target: Destination file.
+ source: List of sources to be added to the target.
+ env: Environment context.
+
+ Returns:
+ Zero if successful.
+ """
+ source = source # Silence gpylint
+
+ target_name = env['PROJECT_NAME']
+ project_file = target[0].path
+ project_dir = target[0].dir
+
+ # Get list of suffixes to include
+ suffixes = env.SubstList2('$COMPONENT_VS_SOURCE_SUFFIXES')
+
+ # Convert source folders to absolute paths
+ folders = []
+ for f in env['COMPONENT_VS_SOURCE_FOLDERS']:
+ # (folder name, folder abspath, dict of contents)
+ folders.append((f[0], env.Dir(f[1]).abspath, {}))
+
+ # TODO: Additional enhancements:
+ # * Should be able to specify paths in folder name (i.e., foo/bar) and
+ # create the nested folder nodes ('foo' and 'bar')
+ # * Should be tolerant of a folder being specified more than once with
+ # the same name (probably necessary to support nested folder nodes anyway)
+ # Can probably accomplish both of those by creating a parent fodler dict and
+ # calling WalkFolders() only once.
+ # Create a temporary solution alias to point to all the targets, so we can
+ # make a single call to SourceWalker()
+ tmp_alias = env.Alias('vs_source_project_' + target_name,
+ map(env.Alias, env['COMPONENT_VS_SOURCE_TARGETS']))
+
+ # Scan all targets and add unique nodes to set of sources
+ print ' Scanning dependency tree ...'
+ all_srcs = set()
+ walker = SourceWalker(tmp_alias[0], all_srcs)
+ walker.WalkAll()
+
+ # Walk all sources and build directory trees
+ print ' Building source tree...'
+ for n in all_srcs:
+ if not hasattr(n, 'rfile'):
+ continue # Not a file
+ n = n.rfile()
+ if not hasattr(n, 'isfile') or not n.isfile():
+ continue # Not a file
+ if n.has_builder():
+ continue # Not a leaf node
+ if n.suffix not in suffixes:
+ continue # Not a file type we include
+
+ path = n.abspath
+ for f in folders:
+ if path.startswith(f[1]):
+ if f[0] is None:
+ # Folder name of None is a filter
+ break
+ relpath = path[len(f[1]) + 1:].split(os.sep)
+ folder_dict = f[2]
+ # Recursively add subdirs
+ for pathseg in relpath[:-1]:
+ if pathseg not in folder_dict:
+ folder_dict[pathseg] = {}
+ folder_dict = folder_dict[pathseg]
+ # Add file to last subdir. No dict, since this isn't a subdir
+ folder_dict[env.RelativePath(project_dir, path)] = None
+ break
+
+ print ' Writing project file...'
+
+ vsp = VSProjectWriter(project_file)
+ vsp.Create(target_name)
+
+ # One configuration for all build modes
+ vsp.AddConfig('all', {}, {})
+
+ # Add files
+ for f in folders:
+ if f[0] is None:
+ continue # Skip filters
+ vsp.AddFiles(f[0], f[2])
+
+ vsp.Write()
+ return 0
+
+
+def ComponentVSSourceProject(self, project_name, target_names, **kwargs):
+ """Visual Studio source project pseudo-builder.
+
+ Args:
+ self: Environment context.
+ project_name: Name of the project.
+ target_names: List of target names to include source for.
+ kwargs: Optional keyword arguments override environment variables in the
+ derived environment used to create the project.
+
+ Returns:
+ A list of output nodes.
+ """
+ # Builder only works on Windows
+ if sys.platform not in ('win32', 'cygwin'):
+ return []
+
+ # Clone environment and add keyword args
+ env = self.Clone()
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Save the project name and targets
+ env['PROJECT_NAME'] = project_name
+ env['COMPONENT_VS_SOURCE_TARGETS'] = target_names
+
+ # Call the real builder
+ return env.ComponentVSSourceProjectBuilder(
+ '$COMPONENT_VS_PROJECT_DIR/${PROJECT_NAME}', [])
+
+#------------------------------------------------------------------------------
+
+
+def FindSources(env, dest, source, suffixes=None):
+ """Recursively finds sources and adds them to the destination set.
+
+ Args:
+ env: Environment context.
+ dest: Set to add source nodes to.
+ source: Source file(s) to find. 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 added.
+ suffixes: List of suffixes to include. If not None, only files with these
+ suffixes will be added to dest.
+ """
+ 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:
+ if str(s.__class__) == 'SCons.Node.FS.Dir':
+ # Recursively search subdir. Since glob('*') doesn't match dot files,
+ # also glob('.*').
+ FindSources(env, dest, [s.abspath + '/*', s.abspath + '/.*'], suffixes)
+ elif suffixes and s.suffix in suffixes:
+ dest.add(s)
+
+
+def ComponentVSDirProjectBuilder(target, source, env):
+ """Visual Studio directory project builder.
+
+ Args:
+ target: Destination file.
+ source: List of sources to be added to the target.
+ env: Environment context.
+
+ Returns:
+ Zero if successful.
+ """
+ source = source # Silence gpylint
+
+ target_name = env['PROJECT_NAME']
+ project_file = target[0].path
+ project_dir = target[0].dir
+
+ # Convert source folders to absolute paths
+ folders = []
+ for f in env['COMPONENT_VS_SOURCE_FOLDERS']:
+ # (folder name, folder abspath, dict of contents)
+ folders.append((f[0], env.Dir(f[1]).abspath, {}))
+
+ # Recursively scan source directories
+ print ' Scanning directories for source...'
+ all_srcs = set()
+ FindSources(env, all_srcs, env['PROJECT_SOURCES'],
+ suffixes=env.SubstList2('$COMPONENT_VS_SOURCE_SUFFIXES'))
+
+ # Walk all sources and build directory trees
+ print ' Building source tree...'
+ for n in all_srcs:
+ # Map addRepository'd source to its real location.
+ path = n.rfile().abspath
+ for f in folders:
+ if path.startswith(f[1]):
+ if f[0] is None:
+ # Folder name of None is a filter
+ break
+ relpath = path[len(f[1]) + 1:].split(os.sep)
+ folder_dict = f[2]
+ # Recursively add subdirs
+ for pathseg in relpath[:-1]:
+ if pathseg not in folder_dict:
+ folder_dict[pathseg] = {}
+ folder_dict = folder_dict[pathseg]
+ # Add file to last subdir. No dict, since this isn't a subdir
+ folder_dict[env.RelativePath(project_dir, path)] = None
+ break
+
+ print ' Writing project file...'
+
+ vsp = VSProjectWriter(project_file)
+ vsp.Create(target_name)
+
+ # One configuration for all build modes
+ vsp.AddConfig('all', {}, {})
+
+ # Add files
+ for f in folders:
+ if f[0] is None:
+ continue # Skip filters
+ vsp.AddFiles(f[0], f[2])
+
+ vsp.Write()
+ return 0
+
+
+def ComponentVSDirProject(self, project_name, source, **kwargs):
+ """Visual Studio directory project pseudo-builder.
+
+ Args:
+ self: Environment context.
+ project_name: Name of the project.
+ source: List of source files and/or directories.
+ kwargs: Optional keyword arguments override environment variables in the
+ derived environment used to create the project.
+
+ Returns:
+ A list of output nodes.
+ """
+ # Builder only works on Windows
+ if sys.platform not in ('win32', 'cygwin'):
+ return []
+
+ # Clone environment and add keyword args
+ env = self.Clone()
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Save the project name and sources
+ env['PROJECT_NAME'] = project_name
+ env['PROJECT_SOURCES'] = source
+
+ # Call the real builder
+ return env.ComponentVSDirProjectBuilder(
+ '$COMPONENT_VS_PROJECT_DIR/${PROJECT_NAME}', [])
+
+#------------------------------------------------------------------------------
+
+
+def ComponentVSSolutionBuilder(target, source, env):
+ """Visual Studio solution builder.
+
+ Args:
+ target: Destination file.
+ source: List of sources to be added to the target.
+ env: Environment context.
+
+ Returns:
+ Zero if successful.
+ """
+ source = source # Silence gpylint
+
+ solution_file = target[0].path
+ projects = env['SOLUTION_PROJECTS']
+ folders = env['SOLUTION_FOLDERS']
+
+ # Scan externally-generated projects
+ external_projects = []
+ for p in source:
+ guid, name = GetGuidAndNameFromVSProject(p.abspath)
+ external_projects.append((p, name, guid))
+
+ f = open(solution_file, 'wt')
+ f.write('Microsoft Visual Studio Solution File, Format Version 9.00\n')
+ f.write('# Visual Studio 2005\n')
+
+ # Projects generated by ComponentVSSolution()
+ for p in projects:
+ project_file = env.File(
+ '$COMPONENT_VS_PROJECT_DIR/%s$COMPONENT_VS_PROJECT_SUFFIX' % p)
+ f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
+ '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID
+ p, # Project name
+ env.RelativePath(target[0].dir, project_file), # Path to project file
+ MakeGuid(p), # Project GUID
+ ))
+
+ # Projects generated elsewhere
+ for p, name, guid in external_projects:
+ f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
+ # TODO: What if this project isn't type external makefile?
+ # How to tell what type it is?
+ '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}', # External makefile GUID
+ name, # Project name
+ env.RelativePath(target[0].dir, p), # Path to project file
+ guid, # Project GUID
+ ))
+
+ # Folders from build groups
+ # TODO: Currently no way to add external project (specified in sources) to a
+ # solution folder.
+ for folder in folders:
+ f.write('Project("%s") = "%s", "%s", "%s"\nEndProject\n' % (
+ '{2150E333-8FDC-42A3-9474-1A3956D46DE8}', # Solution folder GUID
+ folder, # Folder name
+ folder, # Folder name (again)
+ # Use a different seed so the folder won't get the same GUID as a
+ # project.
+ MakeGuid(folder, seed='folder'), # Project GUID
+ ))
+
+ f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
+ for mode in GetTargetModes():
+ f.write('\t\t%s|Win32 = %s|Win32\n' % (mode, mode))
+ f.write('\tEndGlobalSection\n')
+
+ # Determine which projects should be enabled
+ # TODO: This is somewhat clunky. DEFAULT_TARGETS is global, and what we
+ # really need is something mode-specific. In theory we could make this a
+ # mode-specific dict rather than a list, but that'd also be a pain to
+ # populate.
+ # These variable names are also getting REALLY long. Perhaps we should
+ # define shorter ones (with the default value redirecting to the longer
+ # ones for legacy compatibility).
+ enable_projects = env.SubstList2('$COMPONENT_VS_ENABLED_PROJECTS')
+
+ f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
+
+ # Projects generated by ComponentVSSolution()
+ for p in projects:
+ for mode in GetTargetModes():
+ f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % (
+ MakeGuid(p), # Project GUID
+ mode, # Solution build configuration
+ mode, # Project build config for that solution config
+ ))
+
+ t = GetTargets().get(p)
+
+ # Determine if project should be enabled in this mode
+ enabled = t and mode in t.mode_properties
+ if enable_projects and p not in enable_projects:
+ # Enable list specified, but target isn't in it
+ # TODO: Since we env.Default(scons-out) elsewhere, this likely causes
+ # all projects to be disabled by default. But that's realistically
+ # better than enabling them all...
+ enabled = False
+
+ if enabled:
+ # Target can be built in this mode
+ f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % (
+ MakeGuid(p), # Project GUID
+ mode, # Solution build configuration
+ mode, # Project build config for that solution config
+ ))
+
+ # Projects generated elsewhere
+ for p, name, guid in external_projects:
+ for mode in GetTargetModes():
+ f.write('\t\t%s.%s|Win32.ActiveCfg = %s|Win32\n' % (
+ guid, # Project GUID
+ mode, # Solution build configuration
+ mode, # Project build config for that solution config
+ ))
+
+ if name in enable_projects or not enable_projects:
+ # Build target in this mode
+ f.write('\t\t%s.%s|Win32.Build.0 = %s|Win32\n' % (
+ guid, # Project GUID
+ mode, # Solution build configuration
+ mode, # Project build config for that solution config
+ ))
+
+ f.write('\tEndGlobalSection\n')
+
+ f.write('\tGlobalSection(SolutionProperties) = preSolution\n')
+ f.write('\t\tHideSolutionNode = FALSE\n')
+ f.write('\tEndGlobalSection\n')
+
+ if folders:
+ f.write('\tGlobalSection(NestedProjects) = preSolution\n')
+ for p, folder in projects.items():
+ f.write('\t\t%s = %s\n' % (MakeGuid(p), MakeGuid(folder, seed='folder')))
+ f.write('\tEndGlobalSection\n')
+
+ f.write('EndGlobal\n')
+ f.close()
+
+ return 0
+
+
+def ComponentVSSolution(self, solution_name, target_names, projects=None,
+ **kwargs):
+ """Visual Studio solution pseudo-builder.
+
+ Args:
+ self: Environment context.
+ solution_name: Name of the solution.
+ target_names: Names of targets or target groups to include in the solution.
+ This will automatically build projects for them.
+ projects: List of aditional projects not generated by this solution to
+ include in the solution.
+ kwargs: Optional keyword arguments override environment variables in the
+ derived environment used to create the solution.
+
+ Returns:
+ The list of output nodes.
+ """
+ # TODO: Should have option to build source project also. Perhaps select
+ # using a --source_project option, since it needs to use gather_inputs
+ # to scan the DAG and will blow up the null build time.
+ # TODO: Should also have autobuild_projects option. If false, don't build
+ # them.
+ # TODO: Should also be able to specify a target group directly
+ # (i.e. 'run_all_tests')
+
+ # Builder only works on Windows
+ if sys.platform not in ('win32', 'cygwin'):
+ return []
+
+ # Clone environment and add keyword args
+ env = self.Clone()
+ for k, v in kwargs.items():
+ env[k] = v
+
+ # Save the target name
+ env['SOLUTION_NAME'] = solution_name
+
+ # Get list of targets to make projects for. At this point we haven't
+ # determined whether they're groups or targets.
+ target_names = env.SubstList2(target_names)
+ env['SOLUTION_TARGETS'] = target_names
+
+ # Save the default targets list as an environment variable
+ env['COMPONENT_VS_SCONS_DEFAULT_TARGETS'] = SCons.Script.DEFAULT_TARGETS
+
+ # Expand target_names into project names, and create project-to-folder
+ # mappings
+ project_names = {}
+ folders = []
+ if target_names:
+ # Expand target_names into project names
+ for target in target_names:
+ if target in GetTargetGroups():
+ # Add target to folders
+ folders.append(target)
+ # Add all project_names in the group
+ for t in GetTargetGroups()[target].GetTargetNames():
+ project_names[t] = target
+ elif target in GetTargets():
+ # Just a target name
+ project_names[target] = None
+ else:
+ print 'Warning: ignoring unknown target "%s"' % target
+ else:
+ # No target names specified, so use all projects
+ for t in GetTargets():
+ project_names[t] = None
+
+ env['SOLUTION_FOLDERS'] = folders
+ env['SOLUTION_PROJECTS'] = project_names
+
+ # Call the real builder
+ out_nodes = env.ComponentVSSolutionBuilder(
+ '$COMPONENT_VS_SOLUTION_DIR/${SOLUTION_NAME}', projects or [])
+
+ # Call the real builder for the projects we generate
+ for p in project_names:
+ out_nodes += env.ComponentVSProject(p)
+
+ # Add the solution target
+ # TODO: Should really defer the rest of the work above, since otherwise we
+ # can't build a solution which has a target to rebuild itself.
+ env.Alias('all_solutions', env.Alias(solution_name, out_nodes))
+
+ # TODO: To rebuild the solution properly, need to override its project
+ # configuration so it only has '--mode=all' (or some other way of setting the
+ # subset of modes which it should use to rebuild itself). Rebuilding with
+ # the property below will strip it down to just the current build mode, which
+ # isn't what we want.
+ # Let component_targets know this target is available in the current mode
+ #self.SetTargetProperty(solution_name, TARGET_PATH=out_nodes[0])
+
+ return out_nodes
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Add pseudo-builders to set up the project and solution builders. These
+ # need to be available on all platforms so that SConscripts which reference
+ # them will work.
+ env.AddMethod(ComponentVSDirProject)
+ env.AddMethod(ComponentVSProject)
+ env.AddMethod(ComponentVSSolution)
+ env.AddMethod(ComponentVSSourceProject)
+
+ # If not on Windows, don't do anything else
+ if sys.platform not in ('win32', 'cygwin'):
+ return
+
+ # Include tools we need
+ env.Tool('gather_inputs')
+
+ env.SetDefault(
+ COMPONENT_VS_PROJECT_DIR='$COMPONENT_VS_SOLUTION_DIR/projects',
+ COMPONENT_VS_PROJECT_SCRIPT_NAME = 'hammer.bat',
+ COMPONENT_VS_PROJECT_SCRIPT_PATH = (
+ '$$(ProjectDir)/$VS_PROJECT_TO_MAIN_DIR/'
+ '$COMPONENT_VS_PROJECT_SCRIPT_NAME'),
+ COMPONENT_VS_PROJECT_SUFFIX='.vcproj',
+
+ COMPONENT_VS_SOLUTION_DIR='$DESTINATION_ROOT/solution',
+ COMPONENT_VS_SOLUTION_SUFFIX='.sln',
+ COMPONENT_VS_ENABLED_PROJECTS=['$COMPONENT_VS_SCONS_DEFAULT_TARGETS'],
+
+ COMPONENT_VS_SOURCE_SUFFIXES=['$CPPSUFFIXES', '.rc', '.scons'],
+ COMPONENT_VS_SOURCE_FOLDERS=[('source', '$MAIN_DIR')],
+ )
+
+ AddTargetGroup('all_solutions', 'solutions can be built')
+
+ # Add builders
+ vcprojaction = SCons.Script.Action(ComponentVSProjectBuilder, varlist=[
+ 'COMPONENT_VS_PROJECT_SCRIPT_PATH',
+ 'TARGET_NAME',
+ 'TARGET_PATH',
+ ])
+ vcprojbuilder = SCons.Script.Builder(
+ action=vcprojaction,
+ suffix='$COMPONENT_VS_PROJECT_SUFFIX')
+
+ source_vcproj_action = SCons.Script.Action(
+ ComponentVSSourceProjectBuilder, varlist=[
+ 'COMPONENT_VS_SOURCE_FOLDERS',
+ 'COMPONENT_VS_SOURCE_SUFFIXES',
+ 'COMPONENT_VS_SOURCE_TARGETS',
+ ])
+ source_vcproj_builder = SCons.Script.Builder(
+ action=source_vcproj_action,
+ suffix='$COMPONENT_VS_PROJECT_SUFFIX')
+
+ dir_vcproj_action = SCons.Script.Action(
+ ComponentVSDirProjectBuilder, varlist=[
+ 'COMPONENT_VS_SOURCE_FOLDERS',
+ 'COMPONENT_VS_SOURCE_SUFFIXES',
+ 'PROJECT_SOURCES',
+ ])
+ dir_vcproj_builder = SCons.Script.Builder(
+ action=dir_vcproj_action,
+ suffix='$COMPONENT_VS_PROJECT_SUFFIX')
+
+ slnaction = SCons.Script.Action(ComponentVSSolutionBuilder, varlist=[
+ 'COMPONENT_VS_ENABLED_PROJECTS',
+ 'SOLUTION_FOLDERS',
+ 'SOLUTION_PROJECTS',
+ 'SOLUTION_TARGETS',
+ ])
+ slnbuilder = SCons.Script.Builder(
+ action=slnaction,
+ suffix='$COMPONENT_VS_SOLUTION_SUFFIX')
+
+ env.Append(BUILDERS={
+ 'ComponentVSDirProjectBuilder': dir_vcproj_builder,
+ 'ComponentVSProjectBuilder': vcprojbuilder,
+ 'ComponentVSSolutionBuilder': slnbuilder,
+ 'ComponentVSSourceProjectBuilder': source_vcproj_builder,
+ })
diff --git a/o3d/site_scons/site_tools/component_targets_xml.py b/o3d/site_scons/site_tools/component_targets_xml.py
new file mode 100644
index 0000000..81f9f88
--- /dev/null
+++ b/o3d/site_scons/site_tools/component_targets_xml.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python2.4
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""XML output of component targets for SCons."""
+
+
+import xml.dom
+import SCons.Script
+
+
+def TargetXMLHelp(target, source, env):
+ """Generates target information in XML format.
+
+ Args:
+ target: Destination file.
+ source: List of sources. Should be empty, otherwise this will actually
+ require the sources to be built first.
+ env: Environment context.
+ """
+ env = env
+ source = source # Silence gpylint
+
+ target_path = target[0].abspath
+
+ xml_impl = xml.dom.getDOMImplementation()
+ doc = xml_impl.createDocument(None, 'help', None)
+
+ mode_list = doc.createElement('mode_list')
+ doc.documentElement.appendChild(mode_list)
+ for mode in GetTargetModes().values():
+ n = doc.createElement('build_mode')
+ n.setAttribute('name', mode.name)
+ n.setAttribute('description', mode.description)
+ mode_list.appendChild(n)
+
+ group_list = doc.createElement('target_groups')
+ doc.documentElement.appendChild(group_list)
+ for group in GetTargetGroups().values():
+ items = group.GetTargetNames()
+ if not items:
+ continue
+
+ ngroup = doc.createElement('target_group')
+ ngroup.setAttribute('name', group.name)
+ group_list.appendChild(ngroup)
+
+ for i in items:
+ ntarget = doc.createElement('build_target')
+ ntarget.setAttribute('name', i)
+ ngroup.appendChild(ntarget)
+
+ # Get properties for target, if any
+ target = GetTargets().get(i)
+ if target:
+ # All modes
+ for k, v in target.properties.items():
+ n = doc.createElement('target_property')
+ n.setAttribute('name', k)
+ n.setAttribute('value', v)
+ ntarget.appendChild(n)
+
+ # Mode-specific
+ for mode, mode_properties in target.mode_properties.items():
+ nmode = doc.createElement('target_mode')
+ nmode.setAttribute('name', mode)
+ ntarget.appendChild(nmode)
+
+ for k, v in mode_properties.items():
+ n = doc.createElement('target_property')
+ n.setAttribute('name', k)
+ n.setAttribute('value', v)
+ nmode.appendChild(n)
+
+ f = open(target_path, 'wt')
+ doc.writexml(f, encoding='UTF-8', addindent=' ', newl='\n')
+ f.close()
+
+#------------------------------------------------------------------------------
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ env = env # Silence gpylint
+
+ SCons.Script.Help('''\
+ targets_xml Write information on the build mode's targets to
+ targets.xml. Most useful with --mode=all.
+''')
+
+ # Add build target to generate help
+ p = env.Command('$DESTINATION_ROOT/targets.xml', [],
+ env.Action(TargetXMLHelp))
+
+ # Always build xml info if requested.
+ # TODO: Is there a better way to determine the xml info is up to date?
+ env.AlwaysBuild(p)
+ env.Alias('targets_xml', p)
diff --git a/o3d/site_scons/site_tools/concat_source.py b/o3d/site_scons/site_tools/concat_source.py
new file mode 100644
index 0000000..5b448c1
--- /dev/null
+++ b/o3d/site_scons/site_tools/concat_source.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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_path in map(str, source):
+ if env.get('CC') == 'cl':
+ # 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)
+
+ output_file = open(str(target[0]), 'w')
+ # Need an EOL at the end of the file for more finicky build tools
+ output_file.write('\n'.join(output_lines) + '\n')
+ 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 = []
+ suffixes = self.Flatten(self.subst_list('$CONCAT_SOURCE_SUFFIXES'))
+ for source_file in self.arg2nodes(source):
+ if source_file.suffix in suffixes:
+ cppsource.append(source_file)
+ else:
+ outputs.append(source_file)
+
+ 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, '$CONCAT_SOURCE_COMSTR',
+ varlist = ['CONCAT_SOURCE_SUFFIXES'])
+ builder = SCons.Script.Builder(action = action, suffix = '$CXXFILESUFFIX')
+ env.Append(BUILDERS={'ConcatSourceBuilder': builder})
+
+ env.SetDefault(
+ CONCAT_SOURCE_COMSTR = 'Creating ConcatSource $TARGET from $SOURCES',
+ # 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: Probably shouldn't mix C, C++ either...
+ 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/o3d/site_scons/site_tools/defer.py b/o3d/site_scons/site_tools/defer.py
new file mode 100644
index 0000000..878665a
--- /dev/null
+++ b/o3d/site_scons/site_tools/defer.py
@@ -0,0 +1,322 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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
+import SCons.Errors
+
+
+# Current group name being executed by ExecuteDefer(). Set to None outside
+# of ExecuteDefer().
+_execute_defer_context = None
+
+
+class DeferGroup:
+ """Named list of functions to be deferred."""
+ # If we derive DeferGroup from object, instances of it return type
+ # <class 'defer.DeferGroup'>, which prevents SCons.Util.semi_deepcopy()
+ # from calling its __semi_deepcopy__ function.
+ # TODO: Make semi_deepcopy() capable of handling classes derived from
+ # object.
+
+ def __init__(self):
+ """Initialize deferred function object."""
+ self.func_env_cwd = []
+ self.after = set()
+
+ def __semi_deepcopy__(self):
+ """Makes a semi-deep-copy of this object.
+
+ Returns:
+ A semi-deep-copy of this object.
+
+ This means it copies the sets and lists contained by this object, but
+ doesn't make copies of the function pointers and environments pointed to by
+ those lists.
+
+ Needed so env.Clone() makes a copy of the defer list, so that functions
+ and after-relationships subsequently added to the clone are not added to
+ the parent.
+ """
+ c = DeferGroup()
+ c.func_env_cwd = self.func_env_cwd[:]
+ c.after = self.after.copy()
+ return c
+
+
+def SetDeferRoot(self):
+ """Sets the current environment as the root environment for defer.
+
+ Args:
+ self: Current environment context.
+
+ Functions deferred by environments cloned from the root environment (that is,
+ function deferred by children of the root environment) will be executed when
+ ExecuteDefer() is called from the root environment.
+
+ Functions deferred by environments from which the root environment was cloned
+ (that is, functions deferred by parents of the root environment) will be
+ passed the root environment instead of the original parent environment.
+ (Otherwise, they would have no way to determine the root environment.)
+ """
+ # Set the current environment as the root for holding defer groups
+ self['_DEFER_ROOT_ENV'] = self
+
+ # Deferred functions this environment got from its parents will be run in the
+ # new root context.
+ for group in GetDeferGroups(self).values():
+ new_list = [(func, self, cwd) for (func, env, cwd) in group.func_env_cwd]
+ group.func_env_cwd = new_list
+
+
+def GetDeferRoot(self):
+ """Returns the root environment for defer.
+
+ Args:
+ self: Current environment context.
+
+ Returns:
+ The root environment for defer. If one of this environment's parents
+ called SetDeferRoot(), returns that environment. Otherwise returns the
+ current environment.
+ """
+ return self.get('_DEFER_ROOT_ENV', self)
+
+
+def GetDeferGroups(env):
+ """Returns the dict of defer groups from the root defer environment.
+
+ Args:
+ env: Environment context.
+
+ Returns:
+ The dict of defer groups from the root defer environment.
+ """
+ return env.GetDeferRoot()['_DEFER_GROUPS']
+
+
+def ExecuteDefer(self):
+ """Executes deferred functions.
+
+ Args:
+ self: Current environment context.
+ """
+ # Check for re-entrancy
+ global _execute_defer_context
+ if _execute_defer_context:
+ raise SCons.Errors.UserError('Re-entrant call to ExecuteDefer().')
+
+ # Save directory, so SConscript functions can occur in the right subdirs
+ oldcwd = os.getcwd()
+
+ # If defer root is set and isn't this environment, we're being called from a
+ # sub-environment. That's not where we should be called.
+ if self.GetDeferRoot() != self:
+ print ('Warning: Ignoring call to ExecuteDefer() from child of the '
+ 'environment passed to SetDeferRoot().')
+ return
+
+ # Get list of defer groups from ourselves.
+ defer_groups = GetDeferGroups(self)
+
+ # Loop through deferred functions
+ try:
+ while defer_groups:
+ did_work = False
+ for name, group in defer_groups.items():
+ if group.after.intersection(defer_groups.keys()):
+ continue # Still have dependencies
+
+ # Set defer context
+ _execute_defer_context = name
+
+ # Remove this group from the list of defer groups now, in case one of
+ # the functions it calls adds back a function into that defer group.
+ del defer_groups[name]
+
+ 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)
+
+ # The defer groups have been altered, so restart the search for
+ # functions that can be executed.
+ did_work = True
+ break
+
+ if not did_work:
+ errmsg = 'Error in ExecuteDefer: dependency cycle detected.\n'
+ for name, group in defer_groups.items():
+ errmsg += ' %s after: %s\n' % (name, group.after)
+ raise SCons.Errors.UserError(errmsg)
+ finally:
+ # No longer in a defer context
+ _execute_defer_context = None
+
+ # Restore directory
+ os.chdir(oldcwd)
+
+
+def PrintDefer(self, print_functions=True):
+ """Prints the current defer dependency graph.
+
+ Args:
+ self: Environment in which PrintDefer() was called.
+ print_functions: Print individual functions in defer groups.
+ """
+ # Get the defer dict
+ # Get list of defer groups from ourselves.
+ defer_groups = GetDeferGroups(self)
+ dgkeys = defer_groups.keys()
+ dgkeys.sort()
+ for k in dgkeys:
+ print ' +- %s' % k
+ group = defer_groups[k]
+ after = list(group.after)
+ if after:
+ print ' | after'
+ after.sort()
+ for a in after:
+ print ' | +- %s' % a
+ if print_functions and group.func_env_cwd:
+ print ' functions'
+ for func, env, cwd in group.func_env_cwd:
+ print ' | +- %s %s' % (func.__name__, cwd)
+
+
+def Defer(self, *args, **kwargs):
+ """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.
+ (Exception: if this environment is cloned and the clone calls SetDeferRoot()
+ and then ExecuteDefer(), the function will be passed the root environment,
+ instead of the environment used to call Defer().)
+
+ All deferred functions run after all SConscripts. Additional dependencies
+ may be specified with the after= keyword.
+
+ 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__
+
+ # TODO: Why not allow multiple functions? Should be ok
+
+ # Get list of names and/or functions this function should defer until after
+ after = []
+ for a in self.Flatten(kwargs.get('after')):
+ if isinstance(a, str):
+ # TODO: Should check if '$' in a, and if so, subst() it and recurse into
+ # it.
+ after.append(a)
+ elif isinstance(a, types.FunctionType):
+ after.append(a.__name__)
+ elif a is not None:
+ # Deferring
+ raise ValueError('Defer after=%r is not a function or name' % a)
+
+ # Find the deferred function
+ defer_groups = GetDeferGroups(self)
+ if name not in defer_groups:
+ defer_groups[name] = DeferGroup()
+ group = defer_groups[name]
+
+ # If we were given a function, also save environment and current directory
+ if func:
+ group.func_env_cwd.append((func, self, os.getcwd()))
+
+ # Add dependencies for the function
+ group.after.update(after)
+
+ # If we are already inside a call to ExecuteDefer(), any functions which are
+ # deferring until after the current function must also be deferred until
+ # after this new function. In short, this means that if b() defers until
+ # after a() and a() calls Defer() to defer c(), then b() must also defer
+ # until after c().
+ if _execute_defer_context and name != _execute_defer_context:
+ for other_name, other_group in GetDeferGroups(self).items():
+ if other_name == name:
+ continue # Don't defer after ourselves
+ if _execute_defer_context in other_group.after:
+ other_group.after.add(name)
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+ env.Append(_DEFER_GROUPS={})
+
+ env.AddMethod(Defer)
+ env.AddMethod(ExecuteDefer)
+ env.AddMethod(GetDeferRoot)
+ env.AddMethod(PrintDefer)
+ env.AddMethod(SetDeferRoot)
diff --git a/o3d/site_scons/site_tools/directx_9_0_c.py b/o3d/site_scons/site_tools/directx_9_0_c.py
new file mode 100644
index 0000000..3e883fe
--- /dev/null
+++ b/o3d/site_scons/site_tools/directx_9_0_c.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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)
diff --git a/o3d/site_scons/site_tools/directx_9_18_944_0_partial.py b/o3d/site_scons/site_tools/directx_9_18_944_0_partial.py
new file mode 100644
index 0000000..38ff986
--- /dev/null
+++ b/o3d/site_scons/site_tools/directx_9_18_944_0_partial.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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)
diff --git a/o3d/site_scons/site_tools/distcc.py b/o3d/site_scons/site_tools/distcc.py
new file mode 100644
index 0000000..e7a8743
--- /dev/null
+++ b/o3d/site_scons/site_tools/distcc.py
@@ -0,0 +1,91 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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
+import sys
+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:
+ if sys.platform == 'darwin':
+ # On Mac, distcc requires the full path to the compiler
+ compiler = env.WhereIs(compiler)
+ env[compiler_var] = '$DISTCC ' + compiler
diff --git a/o3d/site_scons/site_tools/environment_tools.py b/o3d/site_scons/site_tools/environment_tools.py
new file mode 100644
index 0000000..41ae0a1
--- /dev/null
+++ b/o3d/site_scons/site_tools/environment_tools.py
@@ -0,0 +1,284 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 os
+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:
+ # Use while not if, so we can handle duplicates.
+ while vremove in envval:
+ envval.remove(vremove)
+
+ self[key] = envval
+
+ # TODO: SCons.Environment.Append() has much more logic to deal with various
+ # types of values. We should handle all those cases in here too. (If
+ # variable is a dict, etc.)
+
+#------------------------------------------------------------------------------
+
+
+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 not overlap.
+
+ Converts the values to a set of plain strings via self.SubstList2() before
+ comparison, so SCons $ variables are evaluated.
+ """
+ set1 = set(self.SubstList2(values1))
+ set2 = set(self.SubstList2(values2))
+ 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 self.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 SubstList2(self, *args):
+ """Replacement subst_list designed for flags/parameters, not command lines.
+
+ Args:
+ self: Environment context.
+ args: One or more strings or lists of strings.
+
+ Returns:
+ A flattened, substituted list of strings.
+
+ SCons's built-in subst_list evaluates (substitutes) variables in its
+ arguments, and returns a list of lists (one per positional argument). Since
+ it is designed for use in command line expansion, the list items are
+ SCons.Subst.CmdStringHolder instances. These instances can't be passed into
+ env.File() (or subsequent calls to env.subst(), either). The returned
+ nested lists also need to be flattened via env.Flatten() before the caller
+ can iterate over the contents.
+
+ SubstList2() does a subst_list, flattens the result, then maps the flattened
+ list to strings.
+
+ It is better to do:
+ for x in env.SubstList2('$MYPARAMS'):
+ than to do:
+ for x in env.get('MYPARAMS', []):
+ and definitely better than:
+ for x in env['MYPARAMS']:
+ which will throw an exception if MYPARAMS isn't defined.
+ """
+ return map(str, self.Flatten(self.subst_list(args)))
+
+
+#------------------------------------------------------------------------------
+
+
+def RelativePath(self, source, target, sep=os.sep, source_is_file=False):
+ """Calculates the relative path from source to target.
+
+ Args:
+ self: Environment context.
+ source: Source path or node.
+ target: Target path or node.
+ sep: Path separator to use in returned relative path.
+ source_is_file: If true, calculates the relative path from the directory
+ containing the source, rather than the source itself. Note that if
+ source is a node, you can pass in source.dir instead, which is shorter.
+
+ Returns:
+ The relative path from source to target.
+ """
+ # Split source and target into list of directories
+ source = self.Entry(str(source))
+ if source_is_file:
+ source = source.dir
+ source = source.abspath.split(os.sep)
+ target = self.Entry(str(target)).abspath.split(os.sep)
+
+ # Handle source and target identical
+ if source == target:
+ if source_is_file:
+ return source[-1] # Bare filename
+ else:
+ return '.' # Directory pointing to itself
+
+ # TODO: Handle UNC paths and drive letters (fine if they're the same, but if
+ # they're different, there IS no relative path)
+
+ # Remove common elements
+ while source and target and source[0] == target[0]:
+ source.pop(0)
+ target.pop(0)
+ # Join the remaining elements
+ return sep.join(['..'] * len(source) + target)
+
+
+#------------------------------------------------------------------------------
+
+
+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)
+ env.AddMethod(RelativePath)
+ env.AddMethod(SubstList2)
diff --git a/o3d/site_scons/site_tools/gather_inputs.py b/o3d/site_scons/site_tools/gather_inputs.py
new file mode 100644
index 0000000..2e9ea7a
--- /dev/null
+++ b/o3d/site_scons/site_tools/gather_inputs.py
@@ -0,0 +1,121 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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:
+ # Get key to use for tracking whether we've seen this node
+ target_abspath = None
+ if hasattr(tgt, 'abspath'):
+ # Use target's absolute path as the key
+ target_abspath = tgt.abspath
+ target_key = target_abspath
+ else:
+ # Hope node's representation is unique enough (the default repr
+ # contains a pointer to the target as a string). This works for
+ # Alias() nodes.
+ target_key = repr(tgt)
+
+ # Skip if we have been here before
+ if target_key in all: return
+ # Note that we have been here
+ all[target_key] = True
+ # Skip ones that match an exclude pattern, if we have one.
+ if (exclude_pattern and target_abspath
+ and exclude_pattern.match(target_abspath)):
+ return
+
+ # Handle non-leaf nodes recursively
+ lst = tgt.children(scan=1)
+ if lst:
+ _FindSources(ptrns, lst, all)
+ return
+
+ # Get real file (backed by repositories).
+ rfile = tgt.rfile()
+ rfile_is_file = rfile.isfile()
+ # See who it matches
+ for pattern, lst in ptrns.items():
+ # Add files to the list for the first pattern that matches (implicitly,
+ # don't add directories).
+ if rfile_is_file and 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/o3d/site_scons/site_tools/naclsdk.py b/o3d/site_scons/site_tools/naclsdk.py
new file mode 100644
index 0000000..6295a8a
--- /dev/null
+++ b/o3d/site_scons/site_tools/naclsdk.py
@@ -0,0 +1,233 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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.
+
+"""Nacl SDK tool SCons."""
+
+import __builtin__
+import os
+import sys
+import SCons.Script
+import subprocess
+import sync_tgz
+
+
+NACL_PLATFORM_DIR_MAP = {
+ 'win32': 'windows',
+ 'cygwin': 'windows',
+ 'posix': 'linux',
+ 'linux': 'linux',
+ 'linux2': 'linux',
+ 'darwin': 'mac',
+}
+
+
+def _GetNaclSdkRoot(env, sdk_mode):
+ """Return the path to the sdk.
+
+ Args:
+ env: The SCons environment in question.
+ sdk_mode: A string indicating which location to select the tools from.
+ Returns:
+ The path to the sdk.
+ """
+ if sdk_mode == 'local':
+ if env['PLATFORM'] in ['win32', 'cygwin']:
+ # Try to use cygpath under the assumption we are running thru cygwin.
+ # If this is not the case, then 'local' doesn't really make any sense,
+ # so then we should complain.
+ try:
+ path = subprocess.Popen(
+ ['cygpath', '-m', '/usr/local/nacl-sdk'],
+ env={'PATH': os.environ['PRESCONS_PATH']}, shell=True,
+ stdout=subprocess.PIPE).communicate()[0].replace('\n', '')
+ except WindowsError:
+ raise NotImplementedError(
+ 'Not able to decide where /usr/local/nacl-sdk is on this platform,'
+ 'use naclsdk_mode=custom:...')
+ return path
+ else:
+ return '/usr/local/nacl-sdk'
+
+ elif sdk_mode == 'download':
+ return ('$MAIN_DIR/../third_party/nacl_sdk/' +
+ NACL_PLATFORM_DIR_MAP[env['PLATFORM']] + '/sdk/nacl-sdk')
+
+ elif sdk_mode.startswith('custom:'):
+ return os.path.abspath(sdk_mode[len('custom:'):])
+
+ else:
+ assert 0
+
+
+def _DownloadSdk(env):
+ """Download and untar the latest sdk.
+
+ Args:
+ env: SCons environment in question.
+ """
+
+ # Only try to download once.
+ try:
+ if __builtin__.nacl_sdk_downloaded: return
+ except AttributeError:
+ __builtin__.nacl_sdk_downloaded = True
+
+ # Get path to extract to.
+ target = env.subst('$MAIN_DIR/../third_party/nacl_sdk/' +
+ NACL_PLATFORM_DIR_MAP[env['PLATFORM']])
+
+ # Set NATIVE_CLIENT_SDK_PLATFORM before substitution.
+ env['NATIVE_CLIENT_SDK_PLATFORM'] = NACL_PLATFORM_DIR_MAP[env['PLATFORM']]
+
+ # Allow sdk selection function to be used instead.
+ if env.get('NATIVE_CLIENT_SDK_SOURCE'):
+ url = env['NATIVE_CLIENT_SDK_SOURCE'](env)
+ else:
+ # Pick download url.
+ url = [
+ env.subst(env.get(
+ 'NATIVE_CLIENT_SDK_URL',
+ 'http://nativeclient.googlecode.com/svn/data/sdk_tarballs/'
+ 'naclsdk_${NATIVE_CLIENT_SDK_PLATFORM}.tgz')),
+ env.get('NATIVE_CLIENT_SDK_USERNAME'),
+ env.get('NATIVE_CLIENT_SDK_PASSWORD'),
+ ]
+
+ sync_tgz.SyncTgz(url[0], target, url[1], url[2])
+
+
+def _ValidateSdk(env, sdk_mode):
+ """Check that the sdk is present.
+
+ Args:
+ env: SCons environment in question.
+ sdk_mode: mode string indicating where to get the sdk from.
+ """
+
+ # Try to download SDK if in download mode and using download version.
+ if env.GetOption('download') and sdk_mode == 'download':
+ _DownloadSdk(env)
+
+ # Check if stdio.h is present as a cheap check for the sdk.
+ if not os.path.exists(env.subst('$NACL_SDK_ROOT/nacl/include/stdio.h')):
+ sys.stderr.write('NativeClient SDK not present in %s\n'
+ 'Run again with the --download flag\n'
+ 'and the naclsdk_mode=download option,\n'
+ 'or build the SDK yourself.\n' %
+ env.subst('$NACL_SDK_ROOT'))
+ sys.exit(1)
+
+
+def generate(env):
+ """SCons entry point for this tool.
+
+ Args:
+ env: The SCons environment in question.
+
+ NOTE: SCons requires the use of this name, which fails lint.
+ """
+
+ # Add the download option.
+ try:
+ env.GetOption('download')
+ except AttributeError:
+ SCons.Script.AddOption('--download',
+ dest='download',
+ metavar='DOWNLOAD',
+ default=False,
+ action='store_true',
+ help='allow tools to download')
+
+ # Pick default sdk source.
+ default_mode = env.get('NATIVE_CLIENT_SDK_DEFAULT_MODE',
+ 'custom:../third_party/nacl_sdk/' +
+ NACL_PLATFORM_DIR_MAP[env['PLATFORM']] +
+ '/sdk/nacl-sdk')
+
+ # Get sdk mode (support sdk_mode for backward compatibility).
+ sdk_mode = SCons.Script.ARGUMENTS.get('sdk_mode', default_mode)
+ sdk_mode = SCons.Script.ARGUMENTS.get('naclsdk_mode', sdk_mode)
+
+ # Decide where to get the SDK.
+ env.Replace(NACL_SDK_ROOT=_GetNaclSdkRoot(env, sdk_mode))
+
+ # Validate the sdk unless disabled from the command line.
+ env.SetDefault(NACL_SDK_VALIDATE='1')
+ if int(SCons.Script.ARGUMENTS.get('naclsdk_validate',
+ env.subst('$NACL_SDK_VALIDATE'))):
+ _ValidateSdk(env, sdk_mode)
+
+ if env.subst('$NACL_SDK_ROOT_ONLY'): return
+
+ # Invoke the various unix tools that the NativeClient SDK resembles.
+ env.Tool('g++')
+ env.Tool('gcc')
+ env.Tool('gnulink')
+ env.Tool('ar')
+ env.Tool('as')
+
+ env.Replace(
+ HOST_PLATFORMS=['*'], # NaCl builds on all platforms.
+
+ COMPONENT_LINKFLAGS=['-Wl,-rpath-link,$COMPONENT_LIBRARY_DIR'],
+ COMPONENT_LIBRARY_LINK_SUFFIXES=['.so', '.a'],
+ COMPONENT_LIBRARY_DEBUG_SUFFIXES=[],
+
+ # TODO: This is needed for now to work around unc paths. Take this out
+ # when unc paths are fixed.
+ IMPLICIT_COMMAND_DEPENDENCIES=False,
+
+ # Setup path to NativeClient tools.
+ NACL_SDK_BIN='$NACL_SDK_ROOT/bin',
+ NACL_SDK_INCLUDE='$NACL_SDK_ROOT/nacl/include',
+ NACL_SDK_LIB='$NACL_SDK_ROOT/nacl/lib',
+
+ # Replace the normal unix tools with the NaCl ones.
+ CC='nacl-gcc',
+ CXX='nacl-g++',
+ AR='nacl-ar',
+ AS='nacl-as',
+ LINK='nacl-g++', # use g++ for linking so we can handle c AND c++
+ RANLIB='nacl-ranlib',
+
+ # TODO: this could be .nexe and then all the .nexe stuff goes away?
+ PROGSUFFIX='' # Force PROGSUFFIX to '' on all platforms.
+ )
+
+ env.PrependENVPath('PATH', [env.subst('$NACL_SDK_BIN')])
+ env.PrependENVPath('INCLUDE', [env.subst('$NACL_SDK_INCLUDE')])
+ env.Prepend(LINKFLAGS='-L' + env.subst('$NACL_SDK_LIB'))
+
+ env.Append(
+ CCFLAGS=[
+ '-fno-stack-protector',
+ '-fno-builtin',
+ ],
+ )
diff --git a/o3d/site_scons/site_tools/publish.py b/o3d/site_scons/site_tools/publish.py
new file mode 100644
index 0000000..08bf724
--- /dev/null
+++ b/o3d/site_scons/site_tools/publish.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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."""
+
+
+# List of published resources. This is a dict indexed by group name. Each
+# item in this dict is a dict indexed by resource type. Items in that dict
+# are lists of files for that resource.
+__published = {}
+
+#------------------------------------------------------------------------------
+
+
+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(env):
+ """Re-initializes published resources.
+
+ Args:
+ env: Parent environment
+ """
+ env=env # 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
+ #GOOGLE_CHANGE(pss) - FROM THIS:
+ #GOOGLE_CHANGE(pss) - TO THIS:
+ source_list = self.GetPublishedWithSubdirs(group_name, resource_type)
+ #GOOGLE_CHANGE(pss) - END CHANGES
+ dest_nodes = []
+ #GOOGLE_CHANGE(pss) - FROM THIS:
+ # for group in self.SubstList2(group_name):
+ # for resource in self.SubstList2(resource_type):
+ # # Get items for publish group and resource type
+ # items = __published.get(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)
+ #GOOGLE_CHANGE(pss) - TO THIS:
+ for source in source_list:
+ # Add the subdir if there is one in the source tuple.
+ if source[1]:
+ dest_nodes += self.Replicate(target_path + '/' + source[1], source[0])
+ else:
+ dest_nodes += self.Replicate(target_path, source[0])
+ #GOOGLE_CHANGE(pss) - END CHANGES
+ return dest_nodes
+
+
+#GOOGLE_CHANGE(pss) - FROM THIS:
+# 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.
+# """
+#GOOGLE_CHANGE(pss) - TO THIS:
+def GetPublishedWithSubdirs(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. Each source node is represented
+ by a pair consisting of (source_node, subdir). Returns an empty list
+ if there are no matching resources.
+ """
+#GOOGLE_CHANGE(pss) - END CHANGES
+ source_list = []
+ for group in self.SubstList2(group_name):
+ # Get items for publish group and resource type
+ for resource in self.SubstList2(resource_type):
+ items = __published.get(group, {}).get(resource, [])
+ for i in items:
+ #GOOGLE_CHANGE(pss) - FROM THIS:
+ # source_list.append(i.source)
+ #GOOGLE_CHANGE(pss) - TO THIS:
+ source_list.append((i.source, i.subdir))
+ #GOOGLE_CHANGE(pss) - END CHANGES
+
+ return source_list
+
+
+#GOOGLE_CHANGE(pss) - FROM THIS:
+#GOOGLE_CHANGE(pss) - TO THIS:
+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 = self.GetPublishedWithSubdirs(group_name, resource_type)
+ return [source[0] for source in source_list]
+
+
+#GOOGLE_CHANGE(pss) - END CHANGES
+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
+ # TODO: Should Publish() be able to take a list of group names and publish
+ # the resource to all of them?
+ group_name = self.subst(group_name)
+
+ # Get list of sources
+ items = []
+ for source_entry in self.Flatten(source):
+ if isinstance(source_entry, str):
+ # Search for matches for each source entry
+ # TODO: Should generate an error if there were no matches? But need to
+ # skip this warning if this is a recursive call to self.Publish() from
+ # below.
+ 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."""
+
+ # Defer initializing publish, but do before building SConscripts
+ env.Defer(_InitializePublish)
+ env.Defer('BuildEnvironmentSConscripts', after=_InitializePublish)
+
+ #GOOGLE_CHANGE(pss) - FROM THIS:
+ #GOOGLE_CHANGE(pss) - TO THIS:
+ env.AddMethod(GetPublishedWithSubdirs)
+ #GOOGLE_CHANGE(pss) - END CHANGES
+ env.AddMethod(GetPublished)
+ env.AddMethod(Publish)
+ env.AddMethod(ReplicatePublished)
diff --git a/o3d/site_scons/site_tools/replace_strings.py b/o3d/site_scons/site_tools/replace_strings.py
new file mode 100644
index 0000000..a0881f5
--- /dev/null
+++ b/o3d/site_scons/site_tools/replace_strings.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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. SCons variables in the
+ replacement strings will be evaluated.
+
+ 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], env.subst(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/o3d/site_scons/site_tools/replicate.py b/o3d/site_scons/site_tools/replicate.py
new file mode 100644
index 0000000..a434c72
--- /dev/null
+++ b/o3d/site_scons/site_tools/replicate.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 each target
+ directory.
+ 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_REPLACE: 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: 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 hasattr(target.get_builder(), 'name')
+ and target.get_builder().name == 'InstallBuilder'
+ and target.sources == [s]):
+ # Already installed that file, so pass through the destination node
+ # TODO: Is there a better way to determine if this is a duplicate
+ # call to install?
+ 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/o3d/site_scons/site_tools/sdl.py b/o3d/site_scons/site_tools/sdl.py
new file mode 100644
index 0000000..b23679a
--- /dev/null
+++ b/o3d/site_scons/site_tools/sdl.py
@@ -0,0 +1,184 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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.
+
+"""Simple DirectMedia Layer tool for SCons.
+
+This tool sets up an environment to use the SDL library.
+"""
+
+import os
+import sys
+import SCons.Script
+
+
+def _HermeticSDL(env):
+ """Set things up if sdl is hermetically setup somewhere."""
+
+ if sys.platform in ['win32', 'cygwin']:
+ env.SetDefault(
+ SDL_DIR='$SDL_HERMETIC_WINDOWS_DIR',
+ SDL_CPPPATH=['$SDL_DIR/include'],
+ SDL_LIBPATH=['$SDL_DIR/lib'],
+ SDL_LIBS=['SDL', 'SDLmain'],
+ SDL_FRAMEWORKPATH=[],
+ SDL_FRAMEWORKS=[],
+ )
+ elif sys.platform in ['darwin']:
+ env.SetDefault(
+ SDL_DIR='$SDL_HERMETIC_MAC_DIR',
+ SDL_CPPPATH=[
+ '$SDL_DIR/SDL.framework/Headers',
+ ],
+ SDL_LIBPATH=[],
+ SDL_LIBS=[],
+ SDL_FRAMEWORKPATH=['$SDL_DIR'],
+ SDL_FRAMEWORKS=['SDL', 'Cocoa'],
+ )
+ elif sys.platform in ['linux', 'linux2', 'posix']:
+ env.SetDefault(
+ SDL_DIR='$SDL_HERMETIC_LINUX_DIR',
+ SDL_CPPPATH='$SDL_DIR/include',
+ SDL_LIBPATH='$SDL_DIR/lib',
+ SDL_LIBS=['SDL', 'SDLmain'],
+ SDL_FRAMEWORKPATH=[],
+ SDL_FRAMEWORKS=[],
+ )
+ else:
+ env.SetDefault(
+ SDL_VALIDATE_PATHS=[
+ ('unsupported_platform',
+ ('Not supported on this platform.',)),
+ ],
+ SDL_IS_MISSING=True,
+ )
+
+ if not env.get('SDL_IS_MISSING', False):
+ env.SetDefault(
+ SDL_VALIDATE_PATHS=[
+ ('$SDL_DIR',
+ ('You are missing a hermetic copy of SDL...',)),
+ ],
+ )
+
+
+def _LocalSDL(env):
+ """Set things up if sdl is locally installed."""
+
+ if sys.platform in ['win32', 'cygwin']:
+ env.SetDefault(
+ SDL_DIR='c:/SDL-1.2.13',
+ SDL_CPPPATH='$SDL_DIR/include',
+ SDL_LIBPATH='$SDL_DIR/lib',
+ SDL_LIBS=['SDL', 'SDLmain'],
+ SDL_FRAMEWORKPATH=[],
+ SDL_FRAMEWORKS=[],
+ SDL_VALIDATE_PATHS=[
+ ('$SDL_DIR',
+ ('You are missing SDL-1.2.13 on your system.',
+ 'It was supposed to be in: ${SDL_DIR}',
+ 'You can download it from:',
+ ' http://www.libsdl.org/download-1.2.php')),
+ ],
+ )
+ elif sys.platform in ['darwin']:
+ env.SetDefault(
+ SDL_CPPPATH=['/Library/Frameworks/SDL.framework/Headers'],
+ SDL_LIBPATH=[],
+ SDL_LIBS=[],
+ SDL_FRAMEWORKPATH=[],
+ SDL_FRAMEWORKS=['SDL', 'Cocoa'],
+ SDL_VALIDATE_PATHS=[
+ ('/Library/Frameworks/SDL.framework/SDL',
+ ('You are missing the SDL framework on your system.',
+ 'You can download it from:',
+ 'http://www.libsdl.org/download-1.2.php')),
+ ],
+ )
+ elif sys.platform in ['linux', 'linux2', 'posix']:
+ env.SetDefault(
+ SDL_CPPPATH='/usr/include/SDL',
+ SDL_LIBPATH='/usr/lib',
+ SDL_LIBS=['SDL', 'SDLmain'],
+ SDL_FRAMEWORKPATH=[],
+ SDL_FRAMEWORKS=[],
+ SDL_VALIDATE_PATHS=[
+ ('/usr/lib/libSDL.so',
+ ('You are missing SDL on your system.',
+ 'Run sudo apt-get install libsdl1.2-dev.')),
+ ],
+ )
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # Default to hermetic mode
+ # TODO: Should default to local for open-source installs
+ env.SetDefault(SDL_MODE='hermetic')
+
+ # Allow the hermetic copy to be disabled on the command line.
+ sdl_mode = SCons.Script.ARGUMENTS.get('sdl', env.subst('$SDL_MODE'))
+ env['SDL_MODE'] = sdl_mode
+ if sdl_mode == 'local':
+ _LocalSDL(env)
+ elif sdl_mode == 'hermetic':
+ _HermeticSDL(env)
+ elif sdl_mode == 'none':
+ return
+ else:
+ assert False
+
+ validate_paths = env['SDL_VALIDATE_PATHS']
+
+ # TODO: Should just print a warning if SDL isn't installed. Perhaps check
+ # for an env['SDL_EXIT_IF_NOT_FOUND'] variable so that projects can decide
+ # whether to fail if SDL is missing.
+ if not validate_paths:
+ sys.stderr.write('*' * 77 + '\n')
+ sys.stderr.write('ERROR - SDL not supported on this platform.\n')
+ sys.stderr.write('*' * 77 + '\n')
+ sys.exit(-1)
+
+ for i in validate_paths:
+ if not os.path.exists(env.subst(i[0])):
+ sys.stderr.write('*' * 77 + '\n')
+ for j in i[1]:
+ sys.stderr.write(env.subst(j) + '\n')
+ sys.stderr.write('*' * 77 + '\n')
+ sys.exit(-1)
+
+ env.Append(
+ CPPPATH=['$SDL_CPPPATH'],
+ LIBPATH=['$SDL_LIBPATH'],
+ LIBS=['$SDL_LIBS'],
+ FRAMEWORKPATH=['$SDL_FRAMEWORKPATH'],
+ FRAMEWORKS=['$SDL_FRAMEWORKS'],
+ )
diff --git a/o3d/site_scons/site_tools/seven_zip.py b/o3d/site_scons/site_tools/seven_zip.py
new file mode 100644
index 0000000..d0d34e9
--- /dev/null
+++ b/o3d/site_scons/site_tools/seven_zip.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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/o3d/site_scons/site_tools/target_debug.py b/o3d/site_scons/site_tools/target_debug.py
new file mode 100644
index 0000000..e307fdc
--- /dev/null
+++ b/o3d/site_scons/site_tools/target_debug.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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."""
+
+ # Set target platform bits
+ 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/o3d/site_scons/site_tools/target_optimized.py b/o3d/site_scons/site_tools/target_optimized.py
new file mode 100644
index 0000000..603be5b
--- /dev/null
+++ b/o3d/site_scons/site_tools/target_optimized.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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/o3d/site_scons/site_tools/target_platform_linux.py b/o3d/site_scons/site_tools/target_platform_linux.py
new file mode 100644
index 0000000..ff15cc4
--- /dev/null
+++ b/o3d/site_scons/site_tools/target_platform_linux.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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."""
+
+ # Preserve some variables that get blown away by the tools.
+ saved = dict()
+ for k in ['CFLAGS', 'CCFLAGS', 'CXXFLAGS', 'LINKFLAGS', 'LIBS']:
+ saved[k] = env.get(k, [])
+ env[k] = []
+
+ # Use g++
+ env.Tool('g++')
+ env.Tool('gcc')
+ env.Tool('gnulink')
+ env.Tool('ar')
+ env.Tool('as')
+
+ # Set target platform bits
+ env.SetBits('linux', 'posix')
+
+ env.Replace(
+ TARGET_PLATFORM='LINUX',
+ COMPONENT_PLATFORM_SETUP=ComponentPlatformSetup,
+ CCFLAG_INCLUDE='-include', # Command line option to include a header
+
+ # Code coverage related.
+ COVERAGE_CCFLAGS=['-ftest-coverage', '-fprofile-arcs'],
+ COVERAGE_LIBS='gcov',
+ COVERAGE_STOP_CMD=[
+ '$COVERAGE_MCOV --directory "$TARGET_ROOT" --output "$TARGET"',
+ ('$COVERAGE_GENHTML --output-directory $COVERAGE_HTML_DIR '
+ '$COVERAGE_OUTPUT_FILE'),
+ ],
+ )
+
+ 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=[],
+ )
+
+ # Restore saved flags.
+ env.Append(**saved)
diff --git a/o3d/site_scons/site_tools/target_platform_mac.py b/o3d/site_scons/site_tools/target_platform_mac.py
new file mode 100644
index 0000000..51fd58f
--- /dev/null
+++ b/o3d/site_scons/site_tools/target_platform_mac.py
@@ -0,0 +1,214 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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'])
+
+#------------------------------------------------------------------------------
+# TODO: This bundle builder here needs refactoring to use ComponentPackage().
+# Until that refactoring, consider this code very experimental (i.e., don't use
+# it unless you're ok with it changing out from underneath you).
+
+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: 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.)
+ 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."""
+
+ # Preserve some variables that get blown away by the tools.
+ saved = dict()
+ for k in ['CFLAGS', 'CCFLAGS', 'CXXFLAGS', 'LINKFLAGS', 'LIBS']:
+ saved[k] = env.get(k, [])
+ env[k] = []
+
+ # Use g++
+ env.Tool('g++')
+ env.Tool('gcc')
+ env.Tool('gnulink')
+ env.Tool('ar')
+ env.Tool('as')
+ env.Tool('applelink')
+
+ # Set target platform bits
+ env.SetBits('mac', 'posix')
+
+ env.Replace(
+ TARGET_PLATFORM='MAC',
+ COMPONENT_PLATFORM_SETUP=ComponentPlatformSetup,
+ CCFLAG_INCLUDE='-include', # Command line option to include a header
+
+ # Code coverage related.
+ COVERAGE_CCFLAGS=['-ftest-coverage', '-fprofile-arcs'],
+ COVERAGE_LIBS='gcov',
+ COVERAGE_STOP_CMD=[
+ '$COVERAGE_MCOV --directory "$TARGET_ROOT" --output "$TARGET"',
+ ('$COVERAGE_GENHTML --output-directory $COVERAGE_HTML_DIR '
+ '$COVERAGE_OUTPUT_FILE'),
+ ],
+
+ # Libraries expect to be in the same directory as their executables.
+ # This is correct for unit tests, and for libraries which are published
+ # in Contents/MacOS next to their executables.
+ DYLIB_INSTALL_NAME_FLAGS=[
+ '-install_name',
+ '@loader_path/${TARGET.file}'
+ ],
+ )
+
+ env.Append(
+ HOST_PLATFORMS=['MAC'],
+ CPPDEFINES=['OS_MACOSX=OS_MACOSX'],
+
+ # Mac apps and dylibs have a more strict relationship about where they
+ # expect to find each other. When an app is linked, it stores the
+ # relative path from itself to any dylibs it links against. Override
+ # this so that it will store the relative path from $LIB_DIR instead.
+ # This is similar to RPATH on Linux.
+ LINKFLAGS = [
+ '-Xlinker', '-executable_path',
+ '-Xlinker', '$LIB_DIR',
+ ],
+ # Similarly, tell the library where it expects to find itself once it's
+ # installed.
+ SHLINKFLAGS = ['$DYLIB_INSTALL_NAME_FLAGS'],
+
+ # Settings for debug
+ CCFLAGS_DEBUG=['-g'],
+ LINKFLAGS_DEBUG=['-g'],
+
+ # Settings for optimized
+ # Optimized for space by default, which is what Xcode does
+ CCFLAGS_OPTIMIZED=['-Os'],
+
+ # 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')
+
+ # Restore saved flags.
+ env.Append(**saved)
diff --git a/o3d/site_scons/site_tools/target_platform_windows.py b/o3d/site_scons/site_tools/target_platform_windows.py
new file mode 100644
index 0000000..eabd7d6
--- /dev/null
+++ b/o3d/site_scons/site_tools/target_platform_windows.py
@@ -0,0 +1,408 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 SCons.Script
+import command_output
+
+
+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.
+ """
+ env = env # suppress lint
+ source = source # suppress lint
+ target_path = target[0].abspath
+ if not os.path.exists(target_path):
+ return 0 # Nothing to wait for
+
+ for unused_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, cmd):
+ """Run the Microsoft Visual Studio manifest tool (mt.exe).
+
+ Args:
+ target: List of target nodes.
+ source: List of source nodes.
+ env: Environment context.
+ cmd: Command to run.
+
+ 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(cmd, target=target, source=source)
+
+ 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, cmd='$MANIFEST_COM')
+
+
+def RunManifestDll(target, source, env):
+ """Calls RunManifest for updating a dll (resource_num=2)."""
+ return RunManifest(target, source, env, cmd='$SHMANIFEST_COM')
+
+
+def ComponentPlatformSetup(env, builder_name):
+ """Hook to allow platform to modify environment inside a component builder.
+
+ This is called on a clone of the environment passed into the component
+ builder, and is the last modification done to that environment before using
+ it to call the underlying SCons builder (env.Program(), env.Library(), etc.)
+
+ 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 (not env.get('COMPONENT_TEST_SUBSYSTEM_WINDOWS') and
+ builder_name == 'ComponentTestProgram'):
+ env.FilterOut(
+ CPPDEFINES=['_WINDOWS'],
+ LINKFLAGS=['/SUBSYSTEM:WINDOWS'],
+ )
+ env.Append(
+ CPPDEFINES=['_CONSOLE'],
+ LINKFLAGS=['/SUBSYSTEM:CONSOLE'],
+ )
+
+ # Make sure link methods are lists, so we can append to them below
+ env['LINKCOM'] = [env['LINKCOM']]
+ env['SHLINKCOM'] = [env['SHLINKCOM']]
+
+ # Support manifest file generation and consumption
+ if env.get('MANIFEST_FILE'):
+ env.Append(
+ LINKCOM=[SCons.Script.Action(RunManifestExe, '$MANIFEST_COMSTR')],
+ SHLINKCOM=[SCons.Script.Action(RunManifestDll, '$SHMANIFEST_COMSTR')],
+ )
+
+ # If manifest file should be autogenerated, add the -manifest link line and
+ # delete the generated manifest after running mt.exe.
+ if env.get('MANIFEST_FILE_GENERATED_BY_LINK'):
+ env.Append(
+ LINKFLAGS=['-manifest'],
+ LINKCOM=[SCons.Script.Delete('$MANIFEST_FILE_GENERATED_BY_LINK')],
+ SHLINKCOM=[SCons.Script.Delete('$MANIFEST_FILE_GENERATED_BY_LINK')],
+ )
+
+ # Wait for the output file to be writable before releasing control to
+ # SCons. Windows virus scanners temporarily lock modified executable files
+ # for scanning, which causes SCons's env.Install() to fail intermittently.
+ env.Append(
+ LINKCOM=[SCons.Script.Action(WaitForWritable, None)],
+ SHLINKCOM=[SCons.Script.Action(WaitForWritable, None)],
+ )
+
+#------------------------------------------------------------------------------
+
+
+def _CoverageInstall(dest, source, env):
+ """Version of Install that instruments EXEs and DLLs going to $TESTS_DIR.
+
+ When the destination is under a path in $COVERAGE_INSTRUMENTATION_PATHS and
+ an EXE/DLL is involved, instrument after copy. Other files are passed through
+ to the original Install method. PDBs are handled specially. They are ignored
+ when installed to $COVERAGE_INSTRUMENTATION_PATHS, and are instead copied
+ explicitly before the corresponding EXE/DLL. Only files from under
+ $DESTINATION_ROOT are considered.
+ Arguments:
+ dest: destination filename for the install
+ source: source filename for the install
+ env: the environment context in which this occurs
+ """
+ # Determine if this path is under $COVERAGE_INSTRUMENTATION_PATHS.
+ in_instrumentation_paths = False
+ for path in env.get('COVERAGE_INSTRUMENTATION_PATHS'):
+ if not env.RelativePath(path, dest).startswith('..'):
+ in_instrumentation_paths = True
+
+ # Determine if source is under $DESTINATION_ROOT.
+ source_under_dst_root = not env.RelativePath('$DESTINATION_ROOT',
+ source).startswith('..')
+
+ # get the source extension.
+ source_ext = os.path.splitext(source)[1]
+
+ if (source_ext == '.pdb' and
+ source_under_dst_root and in_instrumentation_paths):
+ # PDBs going into $TESTS_DIR will be copied as part of the EXE/DLL copy.
+ # TODO: Put in the proper env.Requires steps instead.
+ return
+ elif (source_ext in ['.exe', '.dll'] and
+ source_under_dst_root and in_instrumentation_paths):
+ # Copy PDBs (assumed for now to match the filenames of EXEs/DLLs) into
+ # place before instrumenting. The PDB is assumed to match the source PDB
+ # file name.
+ source_pdb = env.subst('$PDB', target=env.File(source))
+ dest_pdb = env.subst('$PDB', target=env.File(dest))
+ dest_pdb = os.path.join(os.path.split(dest_pdb)[0],
+ os.path.split(source_pdb)[1])
+ if os.path.exists(source_pdb):
+ env.Execute('copy "%s" "%s"' % (source_pdb, dest_pdb))
+ WaitForWritable([env.File(dest_pdb)], None, env)
+ # Copy EXEs/DLLs and then instrument.
+ env.Execute('copy "%s" "%s"' % (source, dest))
+ WaitForWritable([env.File(dest)], None, env)
+ env.Execute('$COVERAGE_VSINSTR /COVERAGE "%s"' % dest)
+ else:
+ env['PRECOVERAGE_INSTALL'](dest, source, env)
+
+
+def generate(env):
+ # NOTE: SCons requires the use of this name, which fails gpylint.
+ """SCons entry point for this tool."""
+
+ # TODO: Several sections here are gated out on the mac to prevent failure.
+ # This appears to be SCons issue 1720 manifesting itself on Mac when using
+ # windows tools like msvs or msvc.
+
+ # Preserve some variables that get blown away by the tools.
+ saved = dict()
+ for k in ['CFLAGS', 'CCFLAGS', 'CXXFLAGS', 'LINKFLAGS', 'LIBS']:
+ saved[k] = env.get(k, [])
+ env[k] = []
+
+ # Bring in the outside PATH, INCLUDE, and LIB if not blocked.
+ if not env.get('MSVC_BLOCK_ENVIRONMENT_CHANGES'):
+ env.AppendENVPath('PATH', os.environ.get('PATH', '[]'))
+ env.AppendENVPath('INCLUDE', os.environ.get('INCLUDE', '[]'))
+ env.AppendENVPath('LIB', os.environ.get('LIB', '[]'))
+
+ # Load various Visual Studio related tools.
+ if env['PLATFORM'] != 'darwin':
+ env.Tool('as')
+ env.Tool('msvs')
+ env.Tool('windows_hard_link')
+
+ pre_msvc_env = env['ENV'].copy()
+
+ if env['PLATFORM'] != 'darwin':
+ env.Tool('msvc')
+ env.Tool('mslib')
+ env.Tool('mslink')
+
+ # Find VC80_DIR if it isn't already set.
+ if not env.get('VC80_DIR'):
+ # Look in each directory in the path for cl.exe.
+ for p in env['ENV']['PATH'].split(os.pathsep):
+ # Use the directory two layers up if it exists.
+ if os.path.exists(os.path.join(p, 'cl.exe')):
+ env['VC80_DIR'] = os.path.dirname(os.path.dirname(p))
+
+ # 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
+
+ # Set target platform bits
+ 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.SetDefault(
+ # Command line option to include a header
+ CCFLAG_INCLUDE='/FI',
+
+ # Generate PDBs matching target name by default.
+ PDB='${TARGET.base}.pdb',
+
+ # Code coverage related.
+ COVERAGE_LINKFLAGS='/PROFILE', # Requires vc_80 or higher.
+ COVERAGE_SHLINKFLAGS='$COVERAGE_LINKFLAGS',
+ # Change install step for coverage to cause instrumentation.
+ COVERAGE_INSTALL=_CoverageInstall,
+ # NOTE: need to ignore error in return type here, the tool has issues.
+ # Thus a - is added.
+ COVERAGE_START_CMD=[
+ # If a previous build was cancelled or crashed, VSPerfCmd may still
+ # be running, which causes future coverage runs to fail. Make sure
+ # it's shut down before starting coverage up again.
+ '-$COVERAGE_VSPERFCMD -shutdown',
+ '$COVERAGE_VSPERFCMD -start:coverage '
+ '-output:${COVERAGE_OUTPUT_FILE}.pre'],
+ COVERAGE_STOP_CMD=[
+ '-$COVERAGE_VSPERFCMD -shutdown',
+ 'c:\\Windows\\System32\\regsvr32.exe /S '
+ '$COVERAGE_ANALYZER_DIR/msdia80.dll',
+ '$COVERAGE_ANALYZER -sym_path=. ${COVERAGE_OUTPUT_FILE}.pre.coverage',
+ 'c:\\Windows\\System32\\regsvr32.exe /S /U '
+ '$COVERAGE_ANALYZER_DIR/msdia80.dll',
+ SCons.Script.Copy('$COVERAGE_OUTPUT_FILE',
+ '${COVERAGE_OUTPUT_FILE}.pre.coverage.lcov'),
+ ],
+ COVERAGE_EXTRA_PATHS=['$COVERAGE_ANALYZER_DIR'],
+ # Directories for which EXEs and DLLs should by instrumented on install.
+ COVERAGE_INSTRUMENTATION_PATHS=['$TESTS_DIR', '$ARTIFACTS_DIR'],
+
+ # Manifest options
+ # When link.exe is run with '-manifest', it always generated a manifest
+ # with this name.
+ MANIFEST_FILE_GENERATED_BY_LINK='${TARGET}.manifest',
+ # Manifest file to use as input to mt.exe. Can be overridden to pass in
+ # a pregenerated manifest file.
+ MANIFEST_FILE='$MANIFEST_FILE_GENERATED_BY_LINK',
+ MANIFEST_COM=('mt.exe -nologo -manifest "$MANIFEST_FILE" '
+ '-outputresource:"$TARGET";1'),
+ MANIFEST_COMSTR='$MANIFEST_COM',
+ SHMANIFEST_COM=('mt.exe -nologo -manifest "$MANIFEST_FILE" '
+ '-outputresource:"$TARGET";2'),
+ SHMANIFEST_COMSTR='$SHMANIFEST_COM',
+ )
+
+ env.Append(
+ HOST_PLATFORMS=['WINDOWS'],
+ CPPDEFINES=['OS_WINDOWS=OS_WINDOWS'],
+
+ # Turn up the warning level
+ CCFLAGS=['/W3'],
+
+ # Force x86 platform, generate manifests
+ 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
+ ],
+ LINKFLAGS_OPTIMIZED=['/PDBPATH:none'],
+
+ # Settings for component_builders
+ COMPONENT_LIBRARY_LINK_SUFFIXES=['.lib'],
+ COMPONENT_LIBRARY_DEBUG_SUFFIXES=['.pdb'],
+ )
+
+ # TODO: mslink.py creates a shlibLinkAction which doesn't specify
+ # '$SHLINKCOMSTR' as its command string. This breaks --brief. For now,
+ # hack into the existing action and override its command string.
+ if env['PLATFORM'] != 'darwin':
+ env['SHLINKCOM'].list[0].cmdstr = '$SHLINKCOMSTR'
+
+ # Restore saved flags.
+ env.Append(**saved)
diff --git a/o3d/site_scons/site_tools/visual_studio_solution.py b/o3d/site_scons/site_tools/visual_studio_solution.py
new file mode 100644
index 0000000..36d0ad9
--- /dev/null
+++ b/o3d/site_scons/site_tools/visual_studio_solution.py
@@ -0,0 +1,131 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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(
+ [env.Dir('$DESTINATION_ROOT')],
+ ['.+\\.(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/o3d/site_scons/site_tools/windows_hard_link.py b/o3d/site_scons/site_tools/windows_hard_link.py
new file mode 100644
index 0000000..139f312
--- /dev/null
+++ b/o3d/site_scons/site_tools/windows_hard_link.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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 == 'win32':
+ # 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
diff --git a/o3d/site_scons/sync_tgz.py b/o3d/site_scons/sync_tgz.py
new file mode 100644
index 0000000..8af6a96
--- /dev/null
+++ b/o3d/site_scons/sync_tgz.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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.
+
+"""Keep a local directory in sync with a website tar file.
+
+This module downloads a tgz, and expands it as needed.
+It supports username and password with basic authentication.
+"""
+
+import os
+import shutil
+import tarfile
+import http_download
+
+
+def SyncTgz(url, target, username=None, password=None, verbose=True):
+ """Download a file from a remote server.
+
+ Args:
+ url: A URL to download from.
+ target: Directory to extract to and prefix to use for tgz file.
+ username: Optional username for download.
+ password: Optional password for download (ignored if no username).
+ verbose: Flag indicating if status shut be printed.
+ """
+ shutil.rmtree(target, True)
+ tgz_filename = target + '.tgz'
+
+ if verbose:
+ print 'Downloading %s to %s...' % (url, tgz_filename)
+ http_download.HttpDownload(url, tgz_filename,
+ username=username, password=password)
+
+ if verbose:
+ print 'Extracting from %s...' % tgz_filename
+ tgz = tarfile.open(tgz_filename, 'r')
+ for m in tgz:
+ if verbose:
+ print m.name
+ tgz.extract(m, target)
+ tgz.close()
+ os.remove(tgz_filename)
+
+ if verbose:
+ print 'Update complete.'
diff --git a/o3d/site_scons/usage_log.py b/o3d/site_scons/usage_log.py
new file mode 100644
index 0000000..cab04c8
--- /dev/null
+++ b/o3d/site_scons/usage_log.py
@@ -0,0 +1,307 @@
+#!/usr/bin/python2.4
+# Copyright 2009, 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.
+
+"""Optional usage logging for Software Construction Toolkit."""
+
+import atexit
+import os
+import platform
+import sys
+import time
+import xml.dom
+import SCons
+import SCons.Script
+
+
+chain_build_targets = None # Previous SCons _build_targets function
+
+#------------------------------------------------------------------------------
+# Wrappers and hooks into SCons
+
+
+class ProgressDisplayWrapper(object):
+ """Wrapper around SCons.Util.DisplayEngine.
+
+ Needs to be has-a not is-a, since DisplayEngine.set_mode() overrides the
+ __call__ member.
+ """
+
+ def __init__(self, old_display):
+ """Constructor.
+
+ Args:
+ old_display: Old display object to chain to.
+ """
+ self.old_display = old_display
+
+ def __call__(self, text, append_newline=1):
+ """Display progress.
+
+ Args:
+ text: Text to display.
+ append_newline: Append newline to text if non-zero.
+
+ Returns:
+ Passthru from old display object.
+ """
+ log.AddEntry('progress %s' % text)
+ return self.old_display(text, append_newline)
+
+ def set_mode(self, mode):
+ """Passthru to DisplayEngine.setmode().
+
+ Args:
+ mode: If non-zero, print progress.
+
+ Returns:
+ Passthru from old display object.
+ """
+ return self.old_display.set_mode(mode)
+
+
+def BuildTargetsWrapper(fs, options, targets, target_top):
+ """Wrapper around SCons.Script.Main._build_targets().
+
+ Args:
+ fs: Filesystem object.
+ options: SCons options (after modification by SConscripts.
+ targets: Targets to build.
+ target_top: Passed through to _build_targets().
+ """
+ log.AddEntry('build_targets start')
+ log.SetParam('build_targets.targets', map(str, targets))
+
+ # Get list of non-default options. SConscript settings override defaults.
+ build_opts = dict(options.__SConscript_settings__)
+ # Command line settings are direct attrs, and override SConscript settings.
+ for key in dir(options):
+ if key.startswith('__') or key == 'settable':
+ continue
+ value = getattr(options, key)
+ if callable(value):
+ continue
+ build_opts[key] = value
+
+ for key, value in build_opts.items():
+ log.SetParam('build_targets.option.%s' % key, value)
+
+ try:
+ returnval = None
+ if chain_build_targets:
+ returnval = chain_build_targets(fs, options, targets, target_top)
+ return returnval
+ finally:
+ log.AddEntry('build_targets done')
+
+
+def PrecmdWrapper(self, line):
+ """Pre-command handler for SCons.Script.Interactive() to support logging.
+
+ Args:
+ self: cmd object.
+ line: Command line which will be executed.
+
+ Returns:
+ Passthru value of line.
+ """
+ log.AddEntry('Interactive start')
+ log.SetParam('interactive.command', line or self.lastcmd)
+ return line
+
+
+def PostcmdWrapper(self, stop, line):
+ """Post-command handler for SCons.Script.Interactive() to support logging.
+
+ Args:
+ self: cmd object.
+ stop: Will execution stop after this function exits?
+ line: Command line which was executed.
+
+ Returns:
+ Passthru value of stop.
+ """
+ log.AddEntry('Interactive done')
+ log.Dump()
+ return stop
+
+
+#------------------------------------------------------------------------------
+# Usage log object
+
+
+class Log(object):
+ """Usage log object."""
+
+ def __init__(self):
+ """Constructor."""
+ self.params = {}
+ self.entries = []
+ self.dump_writer = None
+ self.time = time.time
+
+ def SetParam(self, key, value):
+ """Sets a parameter.
+
+ Args:
+ key: Parameter name (string).
+ value: Value for parameter.
+ """
+ self.params[key] = value
+
+ def AddEntry(self, text):
+ """Adds a timestamped log entry.
+
+ Args:
+ text: Text of log entry.
+ """
+ self.entries.append((self.time(), text))
+
+ def ConvertToXml(self):
+ """Converts the usage log to XML.
+
+ Returns:
+ An xml.dom.minidom.Document object with the usage log contents.
+ """
+ xml_impl = xml.dom.getDOMImplementation()
+ xml_doc = xml_impl.createDocument(None, 'usage_log', None)
+
+ # List build params
+ xml_param_list = xml_doc.createElement('param_list')
+ xml_doc.documentElement.appendChild(xml_param_list)
+ for key in sorted(self.params):
+ xml_param = xml_doc.createElement('param')
+ xml_param.setAttribute('name', str(key))
+ xml_param_list.appendChild(xml_param)
+
+ value = self.params[key]
+ if hasattr(value, '__iter__'):
+ # Iterable value, so list items
+ for v in value:
+ xml_item = xml_doc.createElement('item')
+ xml_item.setAttribute('value', str(v))
+ xml_param.appendChild(xml_item)
+ else:
+ # Non-iterable, so convert to string
+ xml_param.setAttribute('value', str(value))
+
+ # List log entries
+ xml_entry_list = xml_doc.createElement('entry_list')
+ xml_doc.documentElement.appendChild(xml_entry_list)
+ for entry_time, entry_text in self.entries:
+ xml_entry = xml_doc.createElement('entry')
+ xml_entry.setAttribute('time', str(entry_time))
+ xml_entry.setAttribute('text', str(entry_text))
+ xml_entry_list.appendChild(xml_entry)
+
+ return xml_doc
+
+ def Dump(self):
+ """Dumps the log by calling self.dump_writer(), then clears the log."""
+ if self.dump_writer:
+ self.dump_writer(self)
+
+ # Clear log entries (but not params, since they can be used again if SCons
+ # is in interactive mode).
+ self.entries = []
+
+
+ def SetOutputFile(self, filename):
+ """Sets the output filename for usage log dumps.
+
+ Args:
+ filename: Name of output file.
+ """
+ self.dump_to_file = filename
+ self.dump_writer = FileDumpWriter
+
+#------------------------------------------------------------------------------
+# Usage log methods
+
+def AddSystemParams():
+ """Prints system stats."""
+ log.SetParam('sys.argv', sys.argv)
+ log.SetParam('sys.executable', sys.executable)
+ log.SetParam('sys.version', sys.version)
+ log.SetParam('sys.version_info', sys.version_info)
+ log.SetParam('sys.path', sys.path)
+ log.SetParam('sys.platform', sys.platform)
+ log.SetParam('platform.uname', platform.uname())
+ log.SetParam('platform.platform', platform.platform())
+
+ for e in ['PATH', 'INCLUDE', 'LIB', 'HAMMER_OPTS', 'HAMMER_XGE']:
+ log.SetParam('shell.%s' % e, os.environ.get(e, ''))
+
+ log.SetParam('scons.version', SCons.__version__)
+
+
+def AtExit():
+ """Usage log cleanup at exit."""
+ log.AddEntry('usage_log exit')
+ log.Dump()
+
+
+def AtModuleLoad():
+ """Code executed at module load time."""
+ AddSystemParams()
+
+ # Wrap SCons' progress display wrapper
+ SCons.Script.Main.progress_display = ProgressDisplayWrapper(
+ SCons.Script.Main.progress_display)
+
+ # Wrap SCons' _build_targets()
+ global chain_build_targets
+ chain_build_targets = SCons.Script.Main._build_targets
+ SCons.Script.Main._build_targets = BuildTargetsWrapper
+
+ # Hook SCons interactive mode
+ SCons.Script.Interactive.SConsInteractiveCmd.precmd = PrecmdWrapper
+ SCons.Script.Interactive.SConsInteractiveCmd.postcmd = PostcmdWrapper
+
+ # Make sure we get called at exit
+ atexit.register(AtExit)
+
+
+def FileDumpWriter(log):
+ """Dumps the log to the specified file."""
+ print 'Writing usage log to %s...' % log.dump_to_file
+ f = open(log.dump_to_file, 'wt')
+ doc = log.ConvertToXml()
+ doc.writexml(f, encoding='UTF-8', addindent=' ', newl='\n')
+ doc.unlink()
+ f.close()
+ print 'Done writing log.'
+
+
+# Create the initial log (can't do this in AtModuleLoad() without 'global')
+log = Log()
+log.AddEntry('usage_log loaded')
+
+# Do other work at module load time
+AtModuleLoad()