Document function Actions and fix their return values (None == success, don't ignore...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 15 Jan 2002 13:55:17 +0000 (13:55 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 15 Jan 2002 13:55:17 +0000 (13:55 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@207 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Errors.py
src/engine/SCons/ErrorsTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Script/__init__.py
test/exceptions.py [new file with mode: 0644]

index 6deab6ef3af5a2e024ea15c29ac591e1b6e6692c..28ad151cedcf7fbb927ff7f8fa5ffe6cb6acffb5 100644 (file)
@@ -723,6 +723,7 @@ to look-up a directory relative to the root of the source tree use #:
 env = Environment(CPPPATH='#/include')
 .EE
 
+.IP
 The directory look-up can also be forced using the 
 .BR Dir ()
 function:
@@ -1077,10 +1078,11 @@ method used to create an instance of the builder.
 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
+mapping source file name suffixes to
+any combination of command line strings
 (if the builder should accept multiple source file extensions),
-a Python function,
-or an Action object
+Python functions,
+or Action objects
 (see the next section).
 
 .IP prefix 
@@ -1110,7 +1112,7 @@ Builder objects,
 rather than let each separate Builder object
 create a separate Action.
 
-The Action function takes a single argument
+The Action method takes a single argument
 and returns an appropriate object for the action
 represented by the type of the argument:
 
@@ -1122,9 +1124,45 @@ the object is simply returned.
 If the argument is a string,
 a command-line Action is returned.
 
+.ES
+Action('$CC -c -o $TARGET $SOURCES')
+.EE
+
 .IP Function
 If the argument is a Python function,
 a function Action is returned.
+The Python function takes three keyword arguments,
+.B target
+(the name of the target file),
+.B source
+(the name of the source file)
+and
+.BR env
+(the construction environment
+used for building the target file).
+The
+.B target
+and
+.B source
+arguments may be lists of strings if there is
+more than one target file or source file.
+.IP
+The function should return
+.B 0
+or
+.B None
+to indicate a successful build of the target file(s).
+The function may raise an exception
+or return a non-zero exit status
+to indicate an unsuccessful build.
+
+.ES
+def build_it(target = None, source = None, env = None):
+    # build the target from the source
+    return 0
+a = Action(build_it)
+.EE
 
 .IP List
 If the argument is a list,
index 2fa528ed452eb1cef74573096d46df81279763bb..70e30be1d4e112391d399d6d019fe670684ef8d5 100644 (file)
@@ -20,6 +20,9 @@ RELEASE 0.04 -
     variables and other functions, defined begin and end macros for
     the example sections, regularized white space separation.
 
+  - Function action fixes:  None is now a successful return value.
+    Exceptions are now reported.  Document function actions.
+
 
 
 RELEASE 0.03 - Fri, 11 Jan 2002 01:09:30 -0600
index 2e8a455cc1481d6b1dd734d4c0938f7d26daa331..ae2bbb98bb1848bcac439e871f3ffbc16a8e9859 100644 (file)
@@ -33,9 +33,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 class BuildError(Exception):
-    def __init__(self, node=None, stat=0, *args):
+    def __init__(self, node=None, errstr="Unknown error", *args):
         self.node = node
-        self.stat = stat
+        self.errstr = errstr
         self.args = args
 
 class InternalError(Exception):
index 2612c95a0f2a6c6e8ef504ea9bfdc4fa4d890930..31ad119107eab7a225e5a98105904e7aa4f89298 100644 (file)
@@ -32,10 +32,10 @@ class ErrorsTestCase(unittest.TestCase):
     def test_BuildError(self):
         """Test the BuildError exception."""
         try:
-            raise SCons.Errors.BuildError(node = "n", stat = 7)
+            raise SCons.Errors.BuildError(node = "n", errstr = "foo")
         except SCons.Errors.BuildError, e:
             assert e.node == "n"
-            assert e.stat == 7
+            assert e.errstr == "foo"
 
     def test_InternalError(self):
        """Test the InternalError exception."""
index f55659e9552186cf6fb5977ce50034c1d5aa6cdd..37b9f37d70d701950603022400d2d5b885076f83 100644 (file)
@@ -47,10 +47,19 @@ class Builder:
     def get_contents(self, env, dir):
         return 7
 
+class NoneBuilder(Builder):
+    def execute(self, **kw):
+        apply(Builder.execute, (self,), kw)
+        return None
+
 class FailBuilder:
     def execute(self, **kw):
         return 1
 
+class ExceptBuilder:
+    def execute(self, **kw):
+        raise SCons.Errors.BuildError
+
 class Environment:
     def Dictionary(self, *args):
        pass
@@ -72,9 +81,21 @@ class NodeTestCase(unittest.TestCase):
        else:
            raise TestFailed, "did not catch expected BuildError"
 
+        node = SCons.Node.Node()
+        node.builder_set(ExceptBuilder())
+        node.env_set(Environment())
+        try:
+            node.build()
+        except SCons.Errors.BuildError:
+            pass
+        else:
+            raise TestFailed, "did not catch expected BuildError"
+
     def test_build(self):
        """Test building a node
        """
+        global built_it
+
         class MyNode(SCons.Node.Node):
             def __str__(self):
                 return self.path
@@ -94,6 +115,18 @@ class NodeTestCase(unittest.TestCase):
         assert str(built_target) == "xxx", str(built_target)
         assert built_source == ["yyy", "zzz"], built_source
 
+        built_it = None
+        node = MyNode()
+        node.builder_set(NoneBuilder())
+        node.env_set(Environment())
+        node.path = "qqq"
+        node.sources = ["rrr", "sss"]
+        node.build()
+        assert built_it
+        assert type(built_target) == type(MyNode()), type(built_target)
+        assert str(built_target) == "qqq", str(built_target)
+        assert built_source == ["rrr", "sss"], built_source
+
     def test_builder_set(self):
        """Test setting a Node's Builder
        """
index b81f911c29923fb44fa5e25db7664c10e104091b..102b532422e9a05c9c46d2eaec538900e4435d7b 100644 (file)
@@ -72,10 +72,13 @@ class Node:
         """Actually build the node.   Return the status from the build."""
        if not self.builder:
            return None
-        stat = self.builder.execute(env = self.env.Dictionary(),
-                                    target = self, source = self.sources)
-       if stat != 0:
-           raise BuildError(node = self, stat = stat)
+        try:
+            stat = self.builder.execute(env = self.env.Dictionary(),
+                                        target = self, source = self.sources)
+        except:
+            raise BuildError(node = self, errstr = "Exception")
+        if stat:
+            raise BuildError(node = self, errstr = "Error %d" % stat)
 
         # If we succesfully build a node, then we need to rescan for
         # implicit dependencies, since it might have changed on us.
index 101e5d66b3f403d0981350ed1027a32ad431abcb..1711f7a1cfb75b2e7458d87e8b2f255b48583311 100644 (file)
@@ -75,7 +75,7 @@ class BuildTask(SCons.Taskmaster.Task):
             try:
                 self.target.build()
             except BuildError, e:
-                sys.stderr.write("scons: *** [%s] Error %s\n" % (e.node, str(e.stat)))
+                sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
                 raise
 
     def executed(self):
diff --git a/test/exceptions.py b/test/exceptions.py
new file mode 100644 (file)
index 0000000..e67cec6
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# 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 sys
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+def func(source = None, target = None, env = None):
+    raise "func exception"
+B = Builder(name = 'B', action = func)
+env = Environment(BUILDERS = [B])
+env.B(target = 'foo.out', source = 'foo.in')
+""")
+
+test.write('foo.in', "foo.in\n")
+
+test.run(arguments = "foo.out", stderr = "scons: *** [foo.out] Exception\n")
+
+test.pass_test()