diff options
Diffstat (limited to 'site_scons/site_tools/component_builders.py')
-rw-r--r-- | site_scons/site_tools/component_builders.py | 618 |
1 files changed, 0 insertions, 618 deletions
diff --git a/site_scons/site_tools/component_builders.py b/site_scons/site_tools/component_builders.py deleted file mode 100644 index 1fe638c..0000000 --- a/site_scons/site_tools/component_builders.py +++ /dev/null @@ -1,618 +0,0 @@ -#!/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(env): - """Re-initializes component builders module. - - Args: - env: Environment context - """ - env = env # Silence gpylint - - __component_list.clear() - - -def _RetrieveComponents(component_name, filter_components=None): - """Get the list of all components required by the specified component. - - Args: - component_name: Name of the base component. - filter_components: List of components NOT to include. - - Returns: - A list of the transitive closure of all components required by the base - component. That is, if A requires B and B requires C, this returns [B, C]. - - """ - if filter_components: - filter_components = set(filter_components) - else: - filter_components = set() - - components = set([component_name]) # Components always require themselves - new_components = set(components) - while new_components: - # Take next new component and add it to the list we've already scanned. - c = new_components.pop() - components.add(c) - # Add to the list of new components any of c's components that we haven't - # seen before. - new_components.update(__component_list.get(c, set()) - - components - filter_components) - - return list(components) - - -def _StoreComponents(self, component_name): - """Stores the list of child components for the specified component. - - Args: - self: Environment containing component. - component_name: Name of the component. - - Adds component references based on the LIBS and COMPONENTS variables in the - current environment. Should be called at primary SConscript execution time; - use _RetrieveComponents() to get the final components lists in a Defer()'d - function. - """ - - components = set() - for clist in ('LIBS', 'COMPONENTS'): - components.update(map(self.subst, self.Flatten(self[clist]))) - - if component_name not in __component_list: - __component_list[component_name] = set() - __component_list[component_name].update(components) - - -def _ComponentPlatformSetup(env, builder_name, **kwargs): - """Modify an environment to work with a component builder. - - Args: - env: Environment to clone. - builder_name: Name of the builder. - kwargs: Keyword arguments. - - Returns: - A modified clone of the environment. - """ - # Clone environment so we can modify it - env = env.Clone() - - # Add all keyword arguments to the environment - for k, v in kwargs.items(): - env[k] = v - - # Add compiler flags for included headers, if any - env['INCLUDES'] = env.Flatten(env.subst_list(['$INCLUDES'])) - for h in env['INCLUDES']: - env.Append(CCFLAGS = ['${CCFLAG_INCLUDE}%s' % h]) - - # Call platform-specific component setup function, if any - if env.get('COMPONENT_PLATFORM_SETUP'): - env['COMPONENT_PLATFORM_SETUP'](env, builder_name) - - # Return the modified environment - return env - -#------------------------------------------------------------------------------ - -# TODO(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 = [] - filter = env.Flatten(env.subst_list('$COMPONENT_PACKAGE_FILTER')) - components = _RetrieveComponents(package_name, 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) - - # Let component_targets know this target is available in the current mode - env.SetTargetProperty(package_name, TARGET_PATH=dest_dir) - - # Set up deferred call to replicate resources - env.Defer(ComponentPackageDeferred) - - # Return the alias, since it's the only node we have - return a - -#------------------------------------------------------------------------------ - - -def ComponentObject(self, *args, **kwargs): - """Pseudo-builder for object to handle platform-dependent type. - - Args: - self: Environment in which we were called. - args: Positional arguments. - kwargs: Keyword arguments. - - Returns: - Passthrough return code from env.StaticLibrary() or env.SharedLibrary(). - - TODO(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'): - o = env.StaticObject(*args, **kwargs) - else: - o = env.SharedObject(*args, **kwargs) - - # Add dependencies on includes - env.Depends(o, env['INCLUDES']) - - return o - -#------------------------------------------------------------------------------ - - -def ComponentLibrary(self, lib_name, *args, **kwargs): - """Pseudo-builder for library to handle platform-dependent type. - - Args: - self: Environment in which we were called. - lib_name: Library name. - args: Positional arguments. - kwargs: Keyword arguments. - - Returns: - Passthrough return code from env.StaticLibrary() or env.SharedLibrary(). - """ - # Clone and modify environment - env = _ComponentPlatformSetup(self, 'ComponentLibrary', **kwargs) - - # Make appropriate library type - if env.get('COMPONENT_STATIC'): - lib_outputs = env.StaticLibrary(lib_name, *args, **kwargs) - else: - lib_outputs = env.SharedLibrary(lib_name, *args, **kwargs) - - # Add dependencies on includes - env.Depends(lib_outputs, env['INCLUDES']) - - # Scan library outputs for files we need to link against this library, and - # files we need to run executables linked against this library. - need_for_link = [] - need_for_debug = [] - need_for_run = [] - for o in lib_outputs: - if o.suffix in env['COMPONENT_LIBRARY_LINK_SUFFIXES']: - need_for_link.append(o) - if o.suffix in env['COMPONENT_LIBRARY_DEBUG_SUFFIXES']: - need_for_debug.append(o) - if o.suffix == env['SHLIBSUFFIX']: - need_for_run.append(o) - all_outputs = lib_outputs - - # Install library in intermediate directory, so other libs and programs can - # link against it - all_outputs += env.Replicate('$LIB_DIR', need_for_link) - - # Publish output - env.Publish(lib_name, 'run', need_for_run) - env.Publish(lib_name, 'debug', need_for_debug) - - # Add an alias to build and copy the library, and add it to the right groups - a = self.Alias(lib_name, all_outputs) - for group in env['COMPONENT_LIBRARY_GROUPS']: - SCons.Script.Alias(group, a) - - # Store list of components for this library - env._StoreComponents(lib_name) - - # Let component_targets know this target is available in the current mode. - env.SetTargetProperty(lib_name, TARGET_PATH=lib_outputs[0]) - - # If library should publish itself, publish as if it was a program - if env.get('COMPONENT_LIBRARY_PUBLISH'): - env['PROGRAM_BASENAME'] = lib_name - env.Defer(ComponentProgramDeferred) - - # Return the library outputs - return lib_outputs - -#------------------------------------------------------------------------------ - - -def ComponentTestProgramDeferred(env): - """Deferred build steps for test program. - - Args: - env: Environment from ComponentTestProgram(). - - Sets up the aliases to compile and run the test program. - """ - prog_name = env['PROGRAM_BASENAME'] - - # Install program and resources - all_outputs = [] - components = _RetrieveComponents(prog_name) - for resource, dest_dir in env.get('COMPONENT_TEST_RESOURCES').items(): - all_outputs += env.ReplicatePublished(dest_dir, components, resource) - - # Add installed program and resources to the alias - env.Alias(prog_name, all_outputs) - - # Add target properties - env.SetTargetProperty( - prog_name, - # The copy of the program we care about is the one in the tests dir - EXE='$TESTS_DIR/$PROGRAM_NAME', - RUN_CMDLINE='$COMPONENT_TEST_CMDLINE', - RUN_DIR='$TESTS_DIR', - TARGET_PATH='$TESTS_DIR/$PROGRAM_NAME', - ) - - # Add an alias for running the test in the test directory, if the test is - # runnable and has a test command line. - if env.get('COMPONENT_TEST_RUNNABLE') and env.get('COMPONENT_TEST_CMDLINE'): - env.Replace( - COMMAND_OUTPUT_CMDLINE=env['COMPONENT_TEST_CMDLINE'], - COMMAND_OUTPUT_RUN_DIR='$TESTS_DIR', - ) - test_out_name = '$TEST_OUTPUT_DIR/${PROGRAM_BASENAME}.out.txt' - if (env.GetOption('component_test_retest') - and env.File(test_out_name).exists()): - # Delete old test results, so test will rerun. - env.Execute(SCons.Script.Delete(test_out_name)) - - # Set timeout based on test size - timeout = env.get('COMPONENT_TEST_TIMEOUT') - if type(timeout) is dict: - timeout = timeout.get(env.get('COMPONENT_TEST_SIZE')) - if timeout: - env['COMMAND_OUTPUT_TIMEOUT'] = timeout - - # Test program is the first run resource we replicated. (Duplicate - # replicate is not harmful, and is a handy way to pick out the correct - # file from all those we replicated above.) - test_program = env.ReplicatePublished('$TESTS_DIR', prog_name, 'run') - - # Run the test. Note that we need to refer to the file by name, so that - # SCons will recreate the file node after we've deleted it; if we used the - # env.File() we created in the if statement above, SCons would still think - # it exists and not rerun the test. - test_out = env.CommandOutput(test_out_name, test_program) - - # Running the test requires the test and its libs copied to the tests dir - env.Depends(test_out, all_outputs) - env.ComponentTestOutput('run_' + prog_name, test_out) - - # Add target properties - env.SetTargetProperty(prog_name, RUN_TARGET='run_' + prog_name) - -def ComponentTestProgram(self, prog_name, *args, **kwargs): - """Pseudo-builder for test program to handle platform-dependent type. - - Args: - self: Environment in which we were called. - prog_name: Test program name. - args: Positional arguments. - kwargs: Keyword arguments. - - Returns: - Output node list from env.Program(). - - 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) - - # Add dependencies on includes - env.Depends(out_nodes, env['INCLUDES']) - - # Publish output - env.Publish(prog_name, 'run', out_nodes[0]) - env.Publish(prog_name, 'debug', out_nodes[1:]) - - # Add an alias to build the program to the right groups - a = env.Alias(prog_name, out_nodes) - for group in env['COMPONENT_TEST_PROGRAM_GROUPS']: - SCons.Script.Alias(group, a) - - # Store list of components for this program - env._StoreComponents(prog_name) - - # Let component_targets know this target is available in the current mode - env.SetTargetProperty(prog_name, TARGET_PATH=out_nodes[0]) - - # Set up deferred call to replicate resources and run test - env.Defer(ComponentTestProgramDeferred) - - # Return the output node - return out_nodes - -#------------------------------------------------------------------------------ - - -def ComponentProgramDeferred(env): - """Deferred build steps for program. - - Args: - env: Environment from ComponentProgram(). - - Sets up the aliases to compile the program. - """ - prog_name = env['PROGRAM_BASENAME'] - - # Install program and resources - all_outputs = [] - components = _RetrieveComponents(prog_name) - for resource, dest_dir in env.get('COMPONENT_PROGRAM_RESOURCES').items(): - all_outputs += env.ReplicatePublished(dest_dir, components, resource) - - # Add installed program and resources to the alias - env.Alias(prog_name, all_outputs) - - -def ComponentProgram(self, prog_name, *args, **kwargs): - """Pseudo-builder for program to handle platform-dependent type. - - Args: - self: Environment in which we were called. - prog_name: Test program name. - args: Positional arguments. - kwargs: Keyword arguments. - - Returns: - Output node list from env.Program(). - """ - # Clone and modify environment - env = _ComponentPlatformSetup(self, 'ComponentProgram', **kwargs) - - env['PROGRAM_BASENAME'] = prog_name - - # Call env.Program() - out_nodes = env.Program(prog_name, *args, **kwargs) - - # Add dependencies on includes - env.Depends(out_nodes, env['INCLUDES']) - - # Publish output - env.Publish(prog_name, 'run', out_nodes[0]) - env.Publish(prog_name, 'debug', out_nodes[1:]) - - # Add an alias to build the program to the right groups - a = env.Alias(prog_name, out_nodes) - for group in env['COMPONENT_PROGRAM_GROUPS']: - SCons.Script.Alias(group, a) - - # Store list of components for this program - env._StoreComponents(prog_name) - - # Let component_targets know this target is available in the current mode - env.SetTargetProperty(prog_name) - - # 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 output - a = self.Alias(test_name, nodes) - - groups = self.get('COMPONENT_TEST_OUTPUT_GROUPS') - if not groups: - # Output group not explicitly specified, so automatically add to groups - if self.get('COMPONENT_TEST_ENABLED'): - # Enabled tests go in all tests, and their size category - groups = ['run_all_tests'] - if self.get('COMPONENT_TEST_SIZE'): - groups.append(self.subst('run_${COMPONENT_TEST_SIZE}_tests')) - else: - # Disabled tests only go in their group - groups = ['run_disabled_tests'] - - for group in groups: - SCons.Script.Alias(group, a) - - # Let component_targets know this target is available in the current mode - self.SetTargetProperty(test_name, TARGET_PATH=nodes[0]) - - # Return the output node - return a - -#------------------------------------------------------------------------------ - - -def generate(env): - # NOTE: SCons requires the use of this name, which fails gpylint. - """SCons entry point for this tool.""" - - env.Replace( - LIB_DIR='$TARGET_ROOT/lib', - # TODO(rspangler): Remove legacy COMPONENT_LIBRARY_DIR, once all users - # have transitioned to LIB_DIR - COMPONENT_LIBRARY_DIR='$LIB_DIR', - STAGING_DIR='$TARGET_ROOT/staging', - TESTS_DIR='$TARGET_ROOT/tests', - TEST_OUTPUT_DIR='$TARGET_ROOT/test_output', - # 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 tests are runnable by default. - COMPONENT_TEST_RUNNABLE=True, - # Default test size is large - COMPONENT_TEST_SIZE='large', - # Default timeouts for component tests - COMPONENT_TEST_TIMEOUT={'large': 900, 'medium': 450, 'small': 180}, - # Tests are enabled by default - COMPONENT_TEST_ENABLED=True, - # Static linking is a sensible default - COMPONENT_STATIC=True, - # Don't publish libraries to the staging dir by themselves by default. - COMPONENT_LIBRARY_PUBLISH=False, - ) - env.Append( - LIBPATH=['$LIB_DIR'], - RPATH=['$LIB_DIR'], - - # Default alias groups for component builders - COMPONENT_PACKAGE_GROUPS=['all_packages'], - COMPONENT_LIBRARY_GROUPS=['all_libraries'], - COMPONENT_PROGRAM_GROUPS=['all_programs'], - COMPONENT_TEST_PROGRAM_GROUPS=['all_test_programs'], - - # Additional components whose resources should be copied into program - # directories, in addition to those from LIBS and the program itself. - LIBS=[], - COMPONENTS=[], - - # Dicts of what resources should go in each destination directory for - # programs and test programs. - COMPONENT_PACKAGE_RESOURCES={ - 'run': '$PACKAGE_DIR', - 'debug': '$PACKAGE_DIR', - }, - COMPONENT_PROGRAM_RESOURCES={ - 'run': '$STAGING_DIR', - 'debug': '$STAGING_DIR', - }, - COMPONENT_TEST_RESOURCES={ - 'run': '$TESTS_DIR', - 'debug': '$TESTS_DIR', - 'test_input': '$TESTS_DIR', - }, - ) - - # Add command line option for retest - SCons.Script.AddOption( - '--retest', - dest='component_test_retest', - action='store_true', - help='force all tests to rerun') - SCons.Script.Help(' --retest ' - 'Rerun specified tests, ignoring cached results.\n') - - # Defer per-environment initialization, but do before building SConscripts - env.Defer(_InitializeComponentBuilders) - env.Defer('BuildEnvironmentSConscripts', after=_InitializeComponentBuilders) - - # Add our pseudo-builder methods - env.AddMethod(_StoreComponents) - env.AddMethod(ComponentPackage) - env.AddMethod(ComponentObject) - env.AddMethod(ComponentLibrary) - env.AddMethod(ComponentProgram) - env.AddMethod(ComponentTestProgram) - env.AddMethod(ComponentTestOutput) - - # Add our target groups - AddTargetGroup('all_libraries', 'libraries can be built') - AddTargetGroup('all_programs', 'programs can be built') - AddTargetGroup('all_test_programs', 'tests can be built') - AddTargetGroup('all_packages', 'packages can be built') - AddTargetGroup('run_all_tests', 'tests can be run') - AddTargetGroup('run_disabled_tests', 'tests are disabled') - AddTargetGroup('run_small_tests', 'small tests can be run') - AddTargetGroup('run_medium_tests', 'medium tests can be run') - AddTargetGroup('run_large_tests', 'large tests can be run') - |