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