Split Action objects into their own module.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 9 Jan 2002 01:15:41 +0000 (01:15 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 9 Jan 2002 01:15:41 +0000 (01:15 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@194 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Action.py [new file with mode: 0644]
src/engine/SCons/ActionTests.py [new file with mode: 0644]
src/engine/SCons/Builder.py
src/engine/SCons/Script/__init__.py

index bcf5898fcb0cb6159f7972fbe7f506b5b6cf2485..c19641b52095930ae34a1ef27c904dba2e32f182 100644 (file)
@@ -582,7 +582,7 @@ env.Depends('foo.c', 'foo.h')
 .PP
 .fi
 
-.SH Construction Variables
+.SS Construction Variables
 
 A construction environment has an associated dictionary of construction
 variables that are used by built-in or user-supplied build rules. A number
@@ -964,7 +964,8 @@ can be a relative or absolute path.
 is an optional directory that will be used as the parent directory. 
 
 
-.SH EXTENDING
+.SH EXTENDING SCONS
+.SS Builder Objects
 .B scons
 can be extended by adding new builders to a construction
 environment using the 
@@ -979,8 +980,12 @@ method used to create an instance of the builder.
 .IP action
 The command line string used to build the target from the source. 
 .B action
-can also be a dictionary mapping source file name suffixes to command line string,
-if the builder can accept multiple source file extensions.
+can also be a dictionary
+mapping source file name suffixes to command line string
+(if the builder should accept multiple source file extensions),
+a Python function,
+or an Action object
+(see the next section).
 
 .IP prefix 
 The prefix that will be prepended to the target file name.
@@ -996,8 +1001,41 @@ Specifies a builder to use when a source file name suffix does not match
 any of the suffixes of the builder. Using this argument produces a
 multi-stage builder.
 
-.LP
+.SS Action Objects
 
+The Builder function will turn its
+.B action
+keyword argument into an appropriate
+internal Action object.
+Occasionally, it may be more efficient
+to create an explicit Action object
+and use it to initialize multiple
+Builder objects,
+rather than let each separate Builder object
+create a separate Action.
+
+The Action function takes a single argument
+and returns an appropriate object for the action
+represented by the type of the argument:
+
+.IP Action
+If the argument is already an Action object,
+the object is simply returned.
+.IP String
+If the argument is a string,
+a command-line Action is returned.
+.IP Function
+If the argument is a Python function,
+a function Action is returned.
+.IP List
+If the argument is a list,
+then a list of Action objects is returned.
+An Action object is created as necessary
+for each element in the list.
+.PP
+If the action argument is not one of the above,
+None is returned.
+.SS Variable Substitution
 .B scons
 performs construction variable interpolation on the strings that make up
 the command line of builders before executing the command.
@@ -1006,57 +1044,44 @@ Variables are introduced by a
 prefix.
 Besides construction variables, scons provides the following
 variables for each command execution:
-
 .IP TARGET
 The file name of the target being built, or the file name of the first 
 target if multiple targets are being built.
-
 .IP TARGETS
-The file names of the targets being built.
-
+The file names of all targets being built.
 .IP SOURCES
 The file names of the sources of the build command.
-
 .LP 
-
 For example, given the construction variable CC='cc', targets=['foo'], and
 sources=['foo.c', 'bar.c']:
-
 .IP
 .nf
 action='$CC -c -o $TARGET $SOURCES'
 .PP
 .fi
-
 would produce the command line:
-
 .IP
 .nf
 cc -c -o foo foo.c bar.c
 .PP
 .fi
-
 Variable names may be surrounded by curly braces ({})
 to separate the name from the trailing characters.
 Within the curly braces, a variable name may have
 a Python slice subscript appended to select one
 or more items from a list.
 In the previous example, the string:
-
 .IP
 .nf
 ${SOURCES[1]}
 .PP
 .fi
-
 would produce:
-
 .IP
 .nf
 bar.c
 .PP
 .fi
-
 Additionally, a variable name may
 have the following special
 modifiers appended within the enclosing curly braces
@@ -1102,10 +1127,8 @@ ${TARGET.suffix}     => .x
 
 .SH EXAMPLES
 
-To help you get started using SCons
-(in lieu of a complete user guide),
-here is a
-brief overview of how to perform some common tasks:
+To help you get started using SCons,
+here is a brief overview of some common tasks:
 
 .SS Basic Compilation From a Single Source File
 
index 31763f5ef0e1411f6a8285484f03c6a6f08cfca2..2d2902821e036a68f53b6d0cb202f6e1040a1e15 100644 (file)
@@ -40,6 +40,8 @@ RELEASE 0.03 -
   - Collect String, Dict, and List type-checking in common utility
     routines so we can accept User{String,Dict,List}s all over.
 
+  - Put the Action factory and classes into their own module.
+
   From Anthony Roach:
 
   - Add a "duplicate" keyword argument to BuildDir() that can be set
index 5c6c0b0eeed73ae65ce178e30e4d88425ffe0ac5..100abcb12cec86a2c5270adf31071db44cd43549 100644 (file)
@@ -1,4 +1,5 @@
 SCons/__init__.py
+SCons/Action.py
 SCons/Builder.py
 SCons/Defaults.py
 SCons/Environment.py
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
new file mode 100644 (file)
index 0000000..7e53c2b
--- /dev/null
@@ -0,0 +1,281 @@
+"""engine.SCons.Action
+
+XXX
+
+"""
+
+#
+# Copyright (c) 2001 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import os.path
+import string
+import sys
+
+import SCons.Util
+
+print_actions = 1;
+execute_actions = 1;
+
+exitvalmap = {
+    2 : 127,
+    13 : 126,
+}
+
+if os.name == 'posix':
+
+    def spawn(cmd, args, env):
+        pid = os.fork()
+        if not pid:
+            # Child process.
+            exitval = 127
+            try:
+                os.execvpe(cmd, args, env)
+            except OSError, e:
+                exitval = exitvalmap[e[0]]
+                sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+            os._exit(exitval)
+        else:
+            # Parent process.
+            pid, stat = os.waitpid(pid, 0)
+            ret = stat >> 8
+            return ret
+
+elif os.name == 'nt':
+
+    def pathsearch(cmd, env):
+        # In order to deal with the fact that 1.5.2 doesn't have
+        # os.spawnvpe(), roll our own PATH search.
+        if os.path.isabs(cmd):
+            if not os.path.exists(cmd):
+                exts = env['PATHEXT']
+                if not SCons.Util.is_List(exts):
+                    exts = string.split(exts, os.pathsep)
+                for e in exts:
+                    f = cmd + e
+                    if os.path.exists(f):
+                        return f
+        else:
+            path = env['PATH']
+            if not SCons.Util.is_List(path):
+                path = string.split(path, os.pathsep)
+            exts = env['PATHEXT']
+            if not SCons.Util.is_List(exts):
+                exts = string.split(exts, os.pathsep)
+            pairs = []
+            for dir in path:
+                for e in exts:
+                    pairs.append((dir, e))
+            for dir, ext in pairs:
+                f = os.path.join(dir, cmd)
+                if not ext is None:
+                    f = f + ext
+                if os.path.exists(f):
+                    return f
+        return cmd
+
+    def spawn(cmd, args, env):
+        try:
+            try:
+                ret = os.spawnvpe(os.P_WAIT, cmd, args, env)
+            except AttributeError:
+                cmd = pathsearch(cmd, env)
+                ret = os.spawnve(os.P_WAIT, cmd, args, env)
+        except OSError, e:
+            ret = exitvalmap[e[0]]
+            sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+        return ret
+
+def Action(act):
+    """A factory for action objects."""
+    if isinstance(act, ActionBase):
+        return act
+    elif callable(act):
+        return FunctionAction(act)
+    elif SCons.Util.is_String(act):
+        return CommandAction(act)
+    elif SCons.Util.is_List(act):
+        return ListAction(act)
+    else:
+        return None
+    
+class ActionBase:
+    """Base class for actions that create output objects."""
+    def __cmp__(self, other):
+        return cmp(self.__dict__, other.__dict__)
+
+    def show(self, string):
+        print string
+
+    def subst_dict(self, **kw):
+        """Create a dictionary for substitution of construction
+        variables.
+
+        This translates the following special arguments:
+
+            env    - the construction environment itself,
+                     the values of which (CC, CCFLAGS, etc.)
+                     are copied straight into the dictionary
+
+            target - the target (object or array of objects),
+                     used to generate the TARGET and TARGETS
+                     construction variables
+
+            source - the source (object or array of objects),
+                     used to generate the SOURCES construction
+                     variable
+
+        Any other keyword arguments are copied into the
+        dictionary."""
+
+        dict = {}
+        if kw.has_key('env'):
+            dict.update(kw['env'])
+            del kw['env']
+
+        try:
+            cwd = kw['dir']
+        except:
+            cwd = None
+        else:
+            del kw['dir']
+
+        if kw.has_key('target'):
+            t = kw['target']
+            del kw['target']
+            if not SCons.Util.is_List(t):
+                t = [t]
+            try:
+                cwd = t[0].cwd
+            except AttributeError:
+                pass
+            dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
+            if dict['TARGETS']:
+                dict['TARGET'] = dict['TARGETS'][0]
+
+        if kw.has_key('source'):
+            s = kw['source']
+            del kw['source']
+            if not SCons.Util.is_List(s):
+                s = [s]
+            dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(str, s)))
+
+        dict.update(kw)
+
+        # Autogenerate necessary construction variables.
+        SCons.Util.autogenerate(dict, dir = cwd)
+
+        return dict
+
+class CommandAction(ActionBase):
+    """Class for command-execution actions."""
+    def __init__(self, string):
+        self.command = string
+
+    def execute(self, **kw):
+        import SCons.Util
+        dict = apply(self.subst_dict, (), kw)
+        cmd_list = SCons.Util.scons_subst_list(self.command, dict, {})
+        for cmd_line in cmd_list:
+            if len(cmd_line):
+                if print_actions:
+                    self.show(string.join(cmd_line))
+                if execute_actions:
+                    try:
+                        ENV = kw['env']['ENV']
+                    except:
+                        import SCons.Defaults
+                        ENV = SCons.Defaults.ConstructionEnvironment['ENV']
+                    ret = spawn(cmd_line[0], cmd_line, ENV)
+                    if ret:
+                        return ret
+        return 0
+
+    def get_contents(self, **kw):
+        """Return the signature contents of this action's command line.
+
+        For signature purposes, it doesn't matter what targets or
+        sources we use, so long as we use the same ones every time
+        so the signature stays the same.  We supply an array of two
+        of each to allow for distinction between TARGET and TARGETS.
+        """
+        kw['target'] = ['__t1__', '__t2__']
+        kw['source'] = ['__s1__', '__s2__']
+        dict = apply(self.subst_dict, (), kw)
+        return SCons.Util.scons_subst(self.command, dict, {})
+
+class FunctionAction(ActionBase):
+    """Class for Python function actions."""
+    def __init__(self, function):
+        self.function = function
+
+    def execute(self, **kw):
+        # if print_actions:
+        # XXX:  WHAT SHOULD WE PRINT HERE?
+        if execute_actions:
+            if kw.has_key('target'):
+                if SCons.Util.is_List(kw['target']):
+                    kw['target'] = map(str, kw['target'])
+                else:
+                    kw['target'] = str(kw['target'])
+            if kw.has_key('source'):
+                kw['source'] = map(str, kw['source'])
+            return apply(self.function, (), kw)
+
+    def get_contents(self, **kw):
+        """Return the signature contents of this callable action.
+
+        By providing direct access to the code object of the
+        function, Python makes this extremely easy.  Hooray!
+        """
+        #XXX DOES NOT ACCOUNT FOR CHANGES IN ENVIRONMENT VARIABLES
+        #THE FUNCTION MAY USE
+        try:
+            # "self.function" is a function.
+            code = self.function.func_code.co_code
+        except:
+            # "self.function" is a callable object.
+            code = self.function.__call__.im_func.func_code.co_code
+        return str(code)
+
+class ListAction(ActionBase):
+    """Class for lists of other actions."""
+    def __init__(self, list):
+        self.list = map(lambda x: Action(x), list)
+
+    def execute(self, **kw):
+        for l in self.list:
+            r = apply(l.execute, (), kw)
+            if r != 0:
+                return r
+        return 0
+
+    def get_contents(self, **kw):
+        """Return the signature contents of this action list.
+
+        Simple concatenation of the signatures of the elements.
+        """
+
+        return reduce(lambda x, y: x + str(y.get_contents()), self.list, "")
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
new file mode 100644 (file)
index 0000000..c01f47c
--- /dev/null
@@ -0,0 +1,190 @@
+#
+# Copyright (c) 2001 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/ActionTests.py __REVISION__ __DATE__ __DEVELOPER__"
+
+# Define a null function for use as a builder action.
+# Where this is defined in the file seems to affect its
+# byte-code contents, so try to minimize changes by
+# defining it here, before we even import anything.
+def Func():
+    pass
+
+import unittest
+
+import sys
+import unittest
+
+import SCons.Action
+import TestCmd
+
+class ActionTestCase(unittest.TestCase):
+
+    def runTest(self):
+        """Test the Action factory
+        """
+        def foo():
+            pass
+        a1 = SCons.Action.Action(foo)
+        assert isinstance(a1, SCons.Action.FunctionAction)
+
+        a2 = SCons.Action.Action("string")
+        assert isinstance(a2, SCons.Action.CommandAction)
+
+        a3 = SCons.Action.Action(["x", a2, "y"])
+        assert isinstance(a3, SCons.Action.ListAction)
+
+        a4 = SCons.Action.Action(1)
+        assert a4 is None, a4
+
+       a5 = SCons.Action.Action(a1)
+       assert a5 is a1
+
+class ActionBaseTestCase(unittest.TestCase):
+
+    def test_cmp(self):
+        """Test Action comparison
+        """
+        a1 = SCons.Action.Action("x")
+        a2 = SCons.Action.Action("x")
+        assert a1 == a2
+        a3 = SCons.Action.Action("y")
+        assert a1 != a3
+        assert a2 != a3
+
+    def test_subst_dict(self):
+        """Test substituting dictionary values in an Action
+        """
+        a = SCons.Action.Action("x")
+
+        d = a.subst_dict(env = {'a' : 'A', 'b' : 'B'})
+        assert d['a'] == 'A', d
+        assert d['b'] == 'B', d
+
+        d = a.subst_dict(target = 't', source = 's')
+        assert str(d['TARGETS']) == 't', d['TARGETS']
+        assert str(d['TARGET']) == 't', d['TARGET']
+        assert str(d['SOURCES']) == 's', d['SOURCES']
+
+        d = a.subst_dict(target = ['t1', 't2'], source = ['s1', 's2'])
+        TARGETS = map(lambda x: str(x), d['TARGETS'])
+        TARGETS.sort()
+        assert TARGETS == ['t1', 't2'], d['TARGETS']
+        assert str(d['TARGET']) == 't1', d['TARGET']
+        SOURCES = map(lambda x: str(x), d['SOURCES'])
+        SOURCES.sort()
+        assert SOURCES == ['s1', 's2'], d['SOURCES']
+
+class CommandActionTestCase(unittest.TestCase):
+
+    def test_init(self):
+        """Test creation of a command Action
+        """
+        a = SCons.Action.CommandAction("xyzzy")
+        assert a.command == "xyzzy"
+
+    def test_execute(self):
+        """Test executing a command Action
+        """
+        pass
+
+    def test_get_contents(self):
+        """Test fetching the contents of a command Action
+        """
+        a = SCons.Action.CommandAction("| $foo | $bar |")
+        c = a.get_contents(foo = 'FFF', bar = 'BBB')
+        assert c == "| FFF | BBB |"
+
+class FunctionActionTestCase(unittest.TestCase):
+
+    def test_init(self):
+        """Test creation of a function Action
+        """
+        def func():
+            pass
+        a = SCons.Action.FunctionAction(func)
+        assert a.function == func
+
+    def test_execute(self):
+        """Test executing a function Action
+        """
+        self.inc = 0
+        def f(s):
+            s.inc = s.inc + 1
+            return 0
+        a = SCons.Action.FunctionAction(f)
+        a.execute(s = self)
+        assert self.inc == 1, self.inc
+
+    def test_get_contents(self):
+        """Test fetching the contents of a function Action
+        """
+        a = SCons.Action.FunctionAction(Func)
+        c = a.get_contents()
+        assert c == "\177\036\000\177\037\000d\000\000S", repr(c)
+
+class ListActionTestCase(unittest.TestCase):
+
+    def test_init(self):
+        """Test creation of a list of subsidiary Actions
+        """
+        def func():
+            pass
+        a = SCons.Action.ListAction(["x", func, ["y", "z"]])
+        assert isinstance(a.list[0], SCons.Action.CommandAction)
+        assert isinstance(a.list[1], SCons.Action.FunctionAction)
+        assert isinstance(a.list[2], SCons.Action.ListAction)
+        assert isinstance(a.list[2].list[0], SCons.Action.CommandAction)
+        assert isinstance(a.list[2].list[1], SCons.Action.CommandAction)
+
+    def test_execute(self):
+        """Test executing a list of subsidiary Actions
+        """
+        self.inc = 0
+        def f(s):
+            s.inc = s.inc + 1
+            return 0
+        a = SCons.Action.ListAction([f, f, f])
+        a.execute(s = self)
+        assert self.inc == 3, self.inc
+
+    def test_get_contents(self):
+        """Test fetching the contents of a list of subsidiary Actions
+        """
+        a = SCons.Action.ListAction(["x", "y", "z"])
+        c = a.get_contents()
+        assert c == "xyz", c
+
+
+if __name__ == "__main__":
+    suite = unittest.TestSuite()
+    suite.addTest(ActionTestCase())
+    suite.addTest(ActionBaseTestCase("test_cmp"))
+    suite.addTest(ActionBaseTestCase("test_subst_dict"))
+    for tclass in [CommandActionTestCase,
+                   FunctionActionTestCase,
+                   ListActionTestCase]:
+        for func in ["test_init", "test_execute", "test_get_contents"]:
+            suite.addTest(tclass(func))
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
index 796af0ab91252bf91bccd726b9c7987087130f5f..59e8834fb28d677293b8ccf4a0181a62f0d1440a 100644 (file)
@@ -31,85 +31,14 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 
-import os
 import os.path
 import string
-import sys
-import types
 from Errors import UserError
 
+import SCons.Action
 import SCons.Node.FS
 import SCons.Util
 
-exitvalmap = {
-    2 : 127,
-    13 : 126,
-}
-
-if os.name == 'posix':
-
-    def spawn(cmd, args, env):
-        pid = os.fork()
-        if not pid:
-            # Child process.
-            exitval = 127
-            try:
-                os.execvpe(cmd, args, env)
-            except OSError, e:
-                exitval = exitvalmap[e[0]]
-                sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
-            os._exit(exitval)
-        else:
-            # Parent process.
-            pid, stat = os.waitpid(pid, 0)
-            ret = stat >> 8
-           return ret
-
-elif os.name == 'nt':
-
-    def pathsearch(cmd, env):
-       # In order to deal with the fact that 1.5.2 doesn't have
-       # os.spawnvpe(), roll our own PATH search.
-       if os.path.isabs(cmd):
-           if not os.path.exists(cmd):
-               exts = env['PATHEXT']
-               if not SCons.Util.is_List(exts):
-                   exts = string.split(exts, os.pathsep)
-               for e in exts:
-                   f = cmd + e
-                   if os.path.exists(f):
-                       return f
-       else:
-           path = env['PATH']
-           if not SCons.Util.is_List(path):
-               path = string.split(path, os.pathsep)
-           exts = env['PATHEXT']
-           if not SCons.Util.is_List(exts):
-               exts = string.split(exts, os.pathsep)
-           pairs = []
-           for dir in path:
-               for e in exts:
-                   pairs.append((dir, e))
-           for dir, ext in pairs:
-               f = os.path.join(dir, cmd)
-               if not ext is None:
-                   f = f + ext
-               if os.path.exists(f):
-                   return f
-       return cmd
-
-    def spawn(cmd, args, env):
-        try:
-            try:
-                ret = os.spawnvpe(os.P_WAIT, cmd, args, env)
-            except AttributeError:
-                cmd = pathsearch(cmd, env)
-                ret = os.spawnve(os.P_WAIT, cmd, args, env)
-        except OSError, e:
-            ret = exitvalmap[e[0]]
-            sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
-        return ret
-
 
 
 def Builder(**kw):
@@ -136,7 +65,7 @@ class BuilderBase:
                         node_factory = SCons.Node.FS.default_fs.File,
                         scanner = None):
        self.name = name
-       self.action = Action(action)
+       self.action = SCons.Action.Action(action)
 
        self.prefix = prefix
        self.suffix = suffix
@@ -336,179 +265,3 @@ class CompositeBuilder(BuilderBase):
         return reduce(lambda x, y: x + y,
                       map(lambda b: b.src_suffixes(),
                           self.builder_dict.values()))
-
-print_actions = 1;
-execute_actions = 1;
-
-def Action(act):
-    """A factory for action objects."""
-    if callable(act):
-       return FunctionAction(act)
-    elif SCons.Util.is_String(act):
-       return CommandAction(act)
-    elif SCons.Util.is_List(act):
-       return ListAction(act)
-    else:
-       return None
-    
-class ActionBase:
-    """Base class for actions that create output objects.
-    
-    We currently expect Actions will only be accessible through
-    Builder objects, so they don't yet merit their own module."""
-    def __cmp__(self, other):
-       return cmp(self.__dict__, other.__dict__)
-
-    def show(self, string):
-       print string
-
-    def subst_dict(self, **kw):
-        """Create a dictionary for substitution of construction
-        variables.
-
-        This translates the following special arguments:
-
-            env    - the construction environment itself,
-                     the values of which (CC, CCFLAGS, etc.)
-                     are copied straight into the dictionary
-
-            target - the target (object or array of objects),
-                     used to generate the TARGET and TARGETS
-                     construction variables
-
-            source - the source (object or array of objects),
-                     used to generate the SOURCES construction
-                     variable
-
-        Any other keyword arguments are copied into the
-        dictionary."""
-
-        dict = {}
-        if kw.has_key('env'):
-            dict.update(kw['env'])
-            del kw['env']
-
-        try:
-            cwd = kw['dir']
-        except:
-            cwd = None
-        else:
-            del kw['dir']
-
-        if kw.has_key('target'):
-            t = kw['target']
-            del kw['target']
-            if not SCons.Util.is_List(t):
-                t = [t]
-            try:
-                cwd = t[0].cwd
-            except AttributeError:
-                pass
-            dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
-            if dict['TARGETS']:
-                dict['TARGET'] = dict['TARGETS'][0]
-
-        if kw.has_key('source'):
-            s = kw['source']
-            del kw['source']
-            if not SCons.Util.is_List(s):
-                s = [s]
-            dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(str, s)))
-
-        dict.update(kw)
-
-        # Autogenerate necessary construction variables.
-        SCons.Util.autogenerate(dict, dir = cwd)
-
-        return dict
-
-class CommandAction(ActionBase):
-    """Class for command-execution actions."""
-    def __init__(self, string):
-        self.command = string
-
-    def execute(self, **kw):
-        import SCons.Util
-        dict = apply(self.subst_dict, (), kw)
-        cmd_list = SCons.Util.scons_subst_list(self.command, dict, {})
-        for cmd_line in cmd_list:
-            if len(cmd_line):
-                if print_actions:
-                    self.show(string.join(cmd_line))
-                if execute_actions:
-                    try:
-                        ENV = kw['env']['ENV']
-                    except:
-                        import SCons.Defaults
-                        ENV = SCons.Defaults.ConstructionEnvironment['ENV']
-                    ret = spawn(cmd_line[0], cmd_line, ENV)
-                    if ret:
-                        return ret
-        return 0
-
-    def get_contents(self, **kw):
-        """Return the signature contents of this action's command line.
-
-        For signature purposes, it doesn't matter what targets or
-        sources we use, so long as we use the same ones every time
-        so the signature stays the same.  We supply an array of two
-        of each to allow for distinction between TARGET and TARGETS.
-        """
-        kw['target'] = ['__t1__', '__t2__']
-        kw['source'] = ['__s1__', '__s2__']
-        dict = apply(self.subst_dict, (), kw)
-        return SCons.Util.scons_subst(self.command, dict, {})
-
-class FunctionAction(ActionBase):
-    """Class for Python function actions."""
-    def __init__(self, function):
-       self.function = function
-
-    def execute(self, **kw):
-       # if print_actions:
-       # XXX:  WHAT SHOULD WE PRINT HERE?
-       if execute_actions:
-            if kw.has_key('target'):
-                if SCons.Util.is_List(kw['target']):
-                    kw['target'] = map(str, kw['target'])
-                else:
-                    kw['target'] = str(kw['target'])
-            if kw.has_key('source'):
-                kw['source'] = map(str, kw['source'])
-            return apply(self.function, (), kw)
-
-    def get_contents(self, **kw):
-        """Return the signature contents of this callable action.
-
-        By providing direct access to the code object of the
-        function, Python makes this extremely easy.  Hooray!
-        """
-        #XXX DOES NOT ACCOUNT FOR CHANGES IN ENVIRONMENT VARIABLES
-        #THE FUNCTION MAY USE
-        try:
-            # "self.function" is a function.
-            code = self.function.func_code.co_code
-        except:
-            # "self.function" is a callable object.
-            code = self.function.__call__.im_func.func_code.co_code
-        return str(code)
-
-class ListAction(ActionBase):
-    """Class for lists of other actions."""
-    def __init__(self, list):
-       self.list = map(lambda x: Action(x), list)
-
-    def execute(self, **kw):
-       for l in self.list:
-           r = apply(l.execute, (), kw)
-           if r != 0:
-               return r
-       return 0
-
-    def get_contents(self, **kw):
-        """Return the signature contents of this action list.
-
-        Simple concatenation of the signatures of the elements.
-        """
-
-        return reduce(lambda x, y: x + str(y.get_contents()), self.list, "")
index 9b5430fad841d36034bd36e2dd25ce0edc9605ca..101e5d66b3f403d0981350ed1027a32ad431abcb 100644 (file)
@@ -450,7 +450,7 @@ def options_init():
        help = "Don't build; list files and where defined.")
 
     def opt_n(opt, arg):
-       SCons.Builder.execute_actions = None
+       SCons.Action.execute_actions = None
 
     Option(func = opt_n,
        short = 'n', long = ['no-exec', 'just-print', 'dry-run', 'recon'],
@@ -481,7 +481,7 @@ def options_init():
        help = "Build dependencies in random order.")
 
     def opt_s(opt, arg):
-       SCons.Builder.print_actions = None
+       SCons.Action.print_actions = None
 
     Option(func = opt_s,
        short = 's', long = ['silent', 'quiet'],