| Index: site_scons/site_tools/defer.py
|
| ===================================================================
|
| --- site_scons/site_tools/defer.py (revision 12583)
|
| +++ site_scons/site_tools/defer.py (working copy)
|
| @@ -1,322 +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.
|
| -
|
| -"""Defer tool for SCons."""
|
| -
|
| -
|
| -import os
|
| -import sys
|
| -import types
|
| -import SCons.Errors
|
| -
|
| -
|
| -# Current group name being executed by ExecuteDefer(). Set to None outside
|
| -# of ExecuteDefer().
|
| -_execute_defer_context = None
|
| -
|
| -
|
| -class DeferGroup:
|
| - """Named list of functions to be deferred."""
|
| - # If we derive DeferGroup from object, instances of it return type
|
| - # <class 'defer.DeferGroup'>, which prevents SCons.Util.semi_deepcopy()
|
| - # from calling its __semi_deepcopy__ function.
|
| - # TODO(sgk): Make semi_deepcopy() capable of handling classes derived from
|
| - # object.
|
| -
|
| - def __init__(self):
|
| - """Initialize deferred function object."""
|
| - self.func_env_cwd = []
|
| - self.after = set()
|
| -
|
| - def __semi_deepcopy__(self):
|
| - """Makes a semi-deep-copy of this object.
|
| -
|
| - Returns:
|
| - A semi-deep-copy of this object.
|
| -
|
| - This means it copies the sets and lists contained by this object, but
|
| - doesn't make copies of the function pointers and environments pointed to by
|
| - those lists.
|
| -
|
| - Needed so env.Clone() makes a copy of the defer list, so that functions
|
| - and after-relationships subsequently added to the clone are not added to
|
| - the parent.
|
| - """
|
| - c = DeferGroup()
|
| - c.func_env_cwd = self.func_env_cwd[:]
|
| - c.after = self.after.copy()
|
| - return c
|
| -
|
| -
|
| -def SetDeferRoot(self):
|
| - """Sets the current environment as the root environment for defer.
|
| -
|
| - Args:
|
| - self: Current environment context.
|
| -
|
| - Functions deferred by environments cloned from the root environment (that is,
|
| - function deferred by children of the root environment) will be executed when
|
| - ExecuteDefer() is called from the root environment.
|
| -
|
| - Functions deferred by environments from which the root environment was cloned
|
| - (that is, functions deferred by parents of the root environment) will be
|
| - passed the root environment instead of the original parent environment.
|
| - (Otherwise, they would have no way to determine the root environment.)
|
| - """
|
| - # Set the current environment as the root for holding defer groups
|
| - self['_DEFER_ROOT_ENV'] = self
|
| -
|
| - # Deferred functions this environment got from its parents will be run in the
|
| - # new root context.
|
| - for group in GetDeferGroups(self).values():
|
| - new_list = [(func, self, cwd) for (func, env, cwd) in group.func_env_cwd]
|
| - group.func_env_cwd = new_list
|
| -
|
| -
|
| -def GetDeferRoot(self):
|
| - """Returns the root environment for defer.
|
| -
|
| - Args:
|
| - self: Current environment context.
|
| -
|
| - Returns:
|
| - The root environment for defer. If one of this environment's parents
|
| - called SetDeferRoot(), returns that environment. Otherwise returns the
|
| - current environment.
|
| - """
|
| - return self.get('_DEFER_ROOT_ENV', self)
|
| -
|
| -
|
| -def GetDeferGroups(env):
|
| - """Returns the dict of defer groups from the root defer environment.
|
| -
|
| - Args:
|
| - env: Environment context.
|
| -
|
| - Returns:
|
| - The dict of defer groups from the root defer environment.
|
| - """
|
| - return env.GetDeferRoot()['_DEFER_GROUPS']
|
| -
|
| -
|
| -def ExecuteDefer(self):
|
| - """Executes deferred functions.
|
| -
|
| - Args:
|
| - self: Current environment context.
|
| - """
|
| - # Check for re-entrancy
|
| - global _execute_defer_context
|
| - if _execute_defer_context:
|
| - raise SCons.Errors.UserError('Re-entrant call to ExecuteDefer().')
|
| -
|
| - # Save directory, so SConscript functions can occur in the right subdirs
|
| - oldcwd = os.getcwd()
|
| -
|
| - # If defer root is set and isn't this environment, we're being called from a
|
| - # sub-environment. That's not where we should be called.
|
| - if self.GetDeferRoot() != self:
|
| - print ('Warning: Ignoring call to ExecuteDefer() from child of the '
|
| - 'environment passed to SetDeferRoot().')
|
| - return
|
| -
|
| - # Get list of defer groups from ourselves.
|
| - defer_groups = GetDeferGroups(self)
|
| -
|
| - # Loop through deferred functions
|
| - try:
|
| - while defer_groups:
|
| - did_work = False
|
| - for name, group in defer_groups.items():
|
| - if group.after.intersection(defer_groups.keys()):
|
| - continue # Still have dependencies
|
| -
|
| - # Set defer context
|
| - _execute_defer_context = name
|
| -
|
| - # Remove this group from the list of defer groups now, in case one of
|
| - # the functions it calls adds back a function into that defer group.
|
| - del defer_groups[name]
|
| -
|
| - if group.func_env_cwd:
|
| - # Run all the functions in our named group
|
| - for func, env, cwd in group.func_env_cwd:
|
| - os.chdir(cwd)
|
| - func(env)
|
| -
|
| - # The defer groups have been altered, so restart the search for
|
| - # functions that can be executed.
|
| - did_work = True
|
| - break
|
| -
|
| - if not did_work:
|
| - errmsg = 'Error in ExecuteDefer: dependency cycle detected.\n'
|
| - for name, group in defer_groups.items():
|
| - errmsg += ' %s after: %s\n' % (name, group.after)
|
| - raise SCons.Errors.UserError(errmsg)
|
| - finally:
|
| - # No longer in a defer context
|
| - _execute_defer_context = None
|
| -
|
| - # Restore directory
|
| - os.chdir(oldcwd)
|
| -
|
| -
|
| -def PrintDefer(self, print_functions=True):
|
| - """Prints the current defer dependency graph.
|
| -
|
| - Args:
|
| - self: Environment in which PrintDefer() was called.
|
| - print_functions: Print individual functions in defer groups.
|
| - """
|
| - # Get the defer dict
|
| - # Get list of defer groups from ourselves.
|
| - defer_groups = GetDeferGroups(self)
|
| - dgkeys = defer_groups.keys()
|
| - dgkeys.sort()
|
| - for k in dgkeys:
|
| - print ' +- %s' % k
|
| - group = defer_groups[k]
|
| - after = list(group.after)
|
| - if after:
|
| - print ' | after'
|
| - after.sort()
|
| - for a in after:
|
| - print ' | +- %s' % a
|
| - if print_functions and group.func_env_cwd:
|
| - print ' functions'
|
| - for func, env, cwd in group.func_env_cwd:
|
| - print ' | +- %s %s' % (func.__name__, cwd)
|
| -
|
| -
|
| -def Defer(self, *args, **kwargs):
|
| - """Adds a deferred function or modifies defer dependencies.
|
| -
|
| - Args:
|
| - self: Environment in which Defer() was called
|
| - args: Positional arguments
|
| - kwargs: Named arguments
|
| -
|
| - The deferred function will be passed the environment used to call Defer(),
|
| - and will be executed in the same working directory as the calling SConscript.
|
| - (Exception: if this environment is cloned and the clone calls SetDeferRoot()
|
| - and then ExecuteDefer(), the function will be passed the root environment,
|
| - instead of the environment used to call Defer().)
|
| -
|
| - All deferred functions run after all SConscripts. Additional dependencies
|
| - may be specified with the after= keyword.
|
| -
|
| - Usage:
|
| -
|
| - env.Defer(func)
|
| - # Defer func() until after all SConscripts
|
| -
|
| - env.Defer(func, after=otherfunc)
|
| - # Defer func() until otherfunc() runs
|
| -
|
| - env.Defer(func, 'bob')
|
| - # Defer func() until after SConscripts, put in group 'bob'
|
| -
|
| - env.Defer(func2, after='bob')
|
| - # Defer func2() until after all funcs in 'bob' group have run
|
| -
|
| - env.Defer(func3, 'sam')
|
| - # Defer func3() until after SConscripts, put in group 'sam'
|
| -
|
| - env.Defer('bob', after='sam')
|
| - # Defer all functions in group 'bob' until after all functions in group
|
| - # 'sam' have run.
|
| -
|
| - env.Defer(func4, after=['bob', 'sam'])
|
| - # Defer func4() until after all functions in groups 'bob' and 'sam' have
|
| - # run.
|
| - """
|
| - # Get name of group to defer and/or the a function
|
| - name = None
|
| - func = None
|
| - for a in args:
|
| - if isinstance(a, str):
|
| - name = a
|
| - elif isinstance(a, types.FunctionType):
|
| - func = a
|
| - if func and not name:
|
| - name = func.__name__
|
| -
|
| - # TODO(rspangler): Why not allow multiple functions? Should be ok
|
| -
|
| - # Get list of names and/or functions this function should defer until after
|
| - after = []
|
| - for a in self.Flatten(kwargs.get('after')):
|
| - if isinstance(a, str):
|
| - # TODO(rspangler): Should check if '$' in a, and if so, subst() it and
|
| - # recurse into it.
|
| - after.append(a)
|
| - elif isinstance(a, types.FunctionType):
|
| - after.append(a.__name__)
|
| - elif a is not None:
|
| - # Deferring
|
| - raise ValueError('Defer after=%r is not a function or name' % a)
|
| -
|
| - # Find the deferred function
|
| - defer_groups = GetDeferGroups(self)
|
| - if name not in defer_groups:
|
| - defer_groups[name] = DeferGroup()
|
| - group = defer_groups[name]
|
| -
|
| - # If we were given a function, also save environment and current directory
|
| - if func:
|
| - group.func_env_cwd.append((func, self, os.getcwd()))
|
| -
|
| - # Add dependencies for the function
|
| - group.after.update(after)
|
| -
|
| - # If we are already inside a call to ExecuteDefer(), any functions which are
|
| - # deferring until after the current function must also be deferred until
|
| - # after this new function. In short, this means that if b() defers until
|
| - # after a() and a() calls Defer() to defer c(), then b() must also defer
|
| - # until after c().
|
| - if _execute_defer_context and name != _execute_defer_context:
|
| - for other_name, other_group in GetDeferGroups(self).items():
|
| - if other_name == name:
|
| - continue # Don't defer after ourselves
|
| - if _execute_defer_context in other_group.after:
|
| - other_group.after.add(name)
|
| -
|
| -
|
| -def generate(env):
|
| - # NOTE: SCons requires the use of this name, which fails gpylint.
|
| - """SCons entry point for this tool."""
|
| - env.Append(_DEFER_GROUPS={})
|
| -
|
| - env.AddMethod(Defer)
|
| - env.AddMethod(ExecuteDefer)
|
| - env.AddMethod(GetDeferRoot)
|
| - env.AddMethod(PrintDefer)
|
| - env.AddMethod(SetDeferRoot)
|
|
|