Add support for target Aliases.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 25 Mar 2002 05:40:36 +0000 (05:40 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 25 Mar 2002 05:40:36 +0000 (05:40 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@305 fdb21ef1-2011-0410-befe-b5e4ea1792b1

16 files changed:
SConstruct
doc/man/scons.1
rpm/scons.spec
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Node/Alias.py [new file with mode: 0644]
src/engine/SCons/Node/AliasTests.py [new file with mode: 0644]
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Script/__init__.py
test/Alias.py [new file with mode: 0644]

index 5303c70aab54dddd19dab4cd9be37117118a99cd..1d97f10c7f103af771b7427c8d61ffc4a2082c50 100644 (file)
@@ -69,6 +69,7 @@ fakeroot = whereis('fakeroot')
 gzip = whereis('gzip')
 rpm = whereis('rpm')
 unzip = whereis('unzip')
+zip = whereis('zip')
 
 # My installation on Red Hat doesn't like any debhelper version
 # beyond 2, so let's use 2 as the default on any non-Debian build.
@@ -174,24 +175,33 @@ try:
     import zipfile
 
     def zipit(env, target, source):
-        print "Zipping %s:" % target
+        print "Zipping %s:" % str(target[0])
         def visit(arg, dirname, names):
             for name in names:
-                arg.write(os.path.join(dirname, name))
+                path = os.path.join(dirname, name)
+                if os.path.isfile(path):
+                    arg.write(path)
+        zf = zipfile.ZipFile(str(target[0]), 'w')
         os.chdir('build')
-        zf = zipfile.ZipFile(target, 'w')
         os.path.walk(env['PSV'], visit, zf)
         os.chdir('..')
+        zf.close()
 
     def unzipit(env, target, source):
-        print "Unzipping %s:" % source[0]
-        zf = zipfile.ZipFile(source[0], 'r')
+        print "Unzipping %s:" % str(source[0])
+        zf = zipfile.ZipFile(str(source[0]), 'r')
         for name in zf.namelist():
             dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
+            dir = os.path.dirname(dest)
+            try:
+                os.makedirs(dir)
+            except:
+                pass
+            print dest,name
             open(dest, 'w').write(zf.read(name))
 
 except:
-    if unzip:
+    if unzip and zip:
         zipit = "cd build && $ZIP $ZIPFLAGS dist/${TARGET.file} $PSV"
         unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
 
@@ -237,7 +247,7 @@ env = Environment(
 
                    TAR_HFLAG           = tar_hflag,
 
-                   ZIP                 = whereis('zip'),
+                   ZIP                 = zip,
                    ZIPFLAGS            = '-r',
                    UNZIP               = unzip,
                    UNZIPFLAGS          = '-o -d $UNPACK_ZIP_DIR',
@@ -441,6 +451,8 @@ for p in [ scons ]:
                     open(os.path.join(src, 'MANIFEST.in')).readlines())
     dst_files = src_files[:]
 
+    MANIFEST_in_list = []
+
     if p.has_key('subpkgs'):
         #
         # This package includes some sub-packages.  Read up their
@@ -451,8 +463,9 @@ for p in [ scons ]:
         for sp in p['subpkgs']:
             ssubdir = sp['src_subdir']
             isubdir = p['subinst_dirs'][sp['pkg']]
-            f = map(lambda x: x[:-1],
-                    open(os.path.join(src, ssubdir, 'MANIFEST.in')).readlines())
+            MANIFEST_in = os.path.join(src, ssubdir, 'MANIFEST.in')
+            MANIFEST_in_list.append(MANIFEST_in)
+            f = map(lambda x: x[:-1], open(MANIFEST_in).readlines())
             src_files.extend(map(lambda x, s=ssubdir: os.path.join(s, x), f))
             if isubdir:
                 f = map(lambda x, i=isubdir: os.path.join(i, x), f)
@@ -486,6 +499,7 @@ for p in [ scons ]:
     # MANIFEST itself to the array, of course.
     #
     src_files.append("MANIFEST")
+    MANIFEST_in_list.append(os.path.join(src, 'MANIFEST.in'))
 
     def copy(target, source, **kw):
         global src_files
@@ -495,9 +509,7 @@ for p in [ scons ]:
             f.write(file + "\n")
         f.close()
         return 0
-    env.Command(os.path.join(build, 'MANIFEST'),
-                os.path.join(src, 'MANIFEST.in'),
-                copy)
+    env.Command(os.path.join(build, 'MANIFEST'), MANIFEST_in_list, copy)
 
     #
     # Now go through and arrange to create whatever packages we can.
index cee4e6536a5ae738d380a0bb501117b431f38da4..e49008f5b63fdb8d543658bbf110d305c1959ae8 100644 (file)
@@ -766,6 +766,20 @@ deletes a target before building it.
 Multiple targets can be passed in to a single call to
 .BR Precious ().
 
+.TP
+.RI Alias( alias ", " targets )
+Creates a phony target that
+expands to one or more other targets.
+Returns the Node object representing the alias,
+which exists outside of any file system.
+This Node object, or the alias name,
+may be used as a dependency of any other target,
+including another alias.
+
+.ES
+env.Alias('install', ['/usr/local/bin', '/usr/local/lib'])
+.EE
+
 .TP
 .RI Update( key = val ", [...])"
 Updates the contents of an environment
@@ -795,7 +809,7 @@ The command line used to generate a static library from object files.
 
 .IP BUILDERS
 A list of the available builders.
-[CFile, CXXFile, Object, Program, Library] by default.
+[Alias, CFile, CXXFile, DVI, Library, Object, Program] by default.
 
 .IP CC 
 The C compiler.
index 59538425cb44f8e78d25e82124b9e9924852df09..77fe187ba48a737df7981b39c99547664f6b58b5 100644 (file)
@@ -62,6 +62,8 @@ rm -rf $RPM_BUILD_ROOT
 /usr/lib/scons/SCons/Errors.pyc
 /usr/lib/scons/SCons/Job.py
 /usr/lib/scons/SCons/Job.pyc
+/usr/lib/scons/SCons/Node/Alias.py
+/usr/lib/scons/SCons/Node/Alias.pyc
 /usr/lib/scons/SCons/Node/FS.py
 /usr/lib/scons/SCons/Node/FS.pyc
 /usr/lib/scons/SCons/Node/__init__.py
index b53da0f603c5e50d61e09ac443c499486d29c36a..1ffca34d92d2801b0da3fdcdde55b4e8d210b15a 100644 (file)
@@ -55,6 +55,8 @@ RELEASE 0.06 -
   - Modify the new DVI builder to create .dvi files from LaTeX (.ltx
     and .latex) files.
 
+  - Add support for Aliases (phony targets).
+
   From Steve Leblanc:
 
   - Add support for the -U option.
index 100abcb12cec86a2c5270adf31071db44cd43549..e86d6014ba0bbec1ca59dc79bdcf7ac421797531 100644 (file)
@@ -7,6 +7,7 @@ SCons/Errors.py
 SCons/Job.py
 SCons/exitfuncs.py
 SCons/Node/__init__.py
+SCons/Node/Alias.py
 SCons/Node/FS.py
 SCons/Scanner/__init__.py
 SCons/Scanner/C.py
index 46fe9cb0e6d32ba79d5f26bfdd4ad5ca5e5dad34..bc90e197d16140ac6dcfdf05802b549d8eb23409 100644 (file)
@@ -94,6 +94,8 @@ class BuilderBase:
                        suffix = '',
                        src_suffix = '',
                         node_factory = SCons.Node.FS.default_fs.File,
+                        target_factory = None,
+                        source_factory = None,
                         scanner = None):
         if name is None:
             raise UserError, "You must specify a name for the builder."
@@ -103,7 +105,8 @@ class BuilderBase:
        self.prefix = prefix
        self.suffix = suffix
        self.src_suffix = src_suffix
-       self.node_factory = node_factory
+        self.target_factory = target_factory or node_factory
+        self.source_factory = source_factory or node_factory
         self.scanner = scanner
         if self.suffix and self.suffix[0] not in '.$':
            self.suffix = '.' + self.suffix
@@ -136,12 +139,12 @@ class BuilderBase:
         tlist = SCons.Node.arg2nodes(adjustixes(target,
                                                 env.subst(self.prefix),
                                                 env.subst(self.suffix)),
-                                     self.node_factory)
+                                     self.target_factory)
 
         slist = SCons.Node.arg2nodes(adjustixes(source,
                                                 None,
                                                 env.subst(self.src_suffix)),
-                                     self.node_factory)
+                                     self.source_factory)
         return tlist, slist
 
     def __call__(self, env, target = None, source = None):
@@ -243,13 +246,16 @@ class MultiStepBuilder(BuilderBase):
                        suffix = '',
                        src_suffix = '',
                         node_factory = SCons.Node.FS.default_fs.File,
+                        target_factory = None,
+                        source_factory = None,
                         scanner=None):
         BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
-                             node_factory, scanner)
+                             node_factory, target_factory, source_factory,
+                             scanner)
         self.src_builder = src_builder
 
     def __call__(self, env, target = None, source = None):
-        slist = SCons.Node.arg2nodes(source, self.node_factory)
+        slist = SCons.Node.arg2nodes(source, self.source_factory)
         final_sources = []
         src_suffix = env.subst(self.src_suffix)
         sdict = {}
index 93636314fed2943bee063862c67e9ceb8e71c3cd..16c87a4d2571148bb395ea220b61f344bd830357 100644 (file)
@@ -398,7 +398,32 @@ class BuilderTestCase(unittest.TestCase):
             global Foo
            return Foo(target)
        builder = SCons.Builder.Builder(name = "builder", node_factory = FooFactory)
-       assert builder.node_factory is FooFactory
+       assert builder.target_factory is FooFactory
+       assert builder.source_factory is FooFactory
+
+    def test_target_factory(self):
+       """Test a Builder that creates target nodes of a specified class
+       """
+       class Foo:
+           pass
+       def FooFactory(target):
+            global Foo
+           return Foo(target)
+       builder = SCons.Builder.Builder(name = "builder", target_factory = FooFactory)
+       assert builder.target_factory is FooFactory
+       assert not builder.source_factory is FooFactory
+
+    def test_source_factory(self):
+       """Test a Builder that creates source nodes of a specified class
+       """
+       class Foo:
+           pass
+       def FooFactory(source):
+            global Foo
+           return Foo(source)
+       builder = SCons.Builder.Builder(name = "builder", source_factory = FooFactory)
+       assert not builder.target_factory is FooFactory
+       assert builder.source_factory is FooFactory
 
     def test_prefix(self):
        """Test Builder creation with a specified target prefix
index 26c59a688c81815afb937839198d98e760e32685..36a6aa1db7f0859ec756edd5e7dd0fa8c0dbc8cd 100644 (file)
@@ -43,9 +43,11 @@ import sys
 
 import SCons.Action
 import SCons.Builder
+import SCons.Errors
+import SCons.Node.Alias
+import SCons.Node.FS
 import SCons.Scanner.C
 import SCons.Scanner.Prog
-import SCons.Errors
 import SCons.Util
 
 
@@ -121,6 +123,14 @@ DVI = SCons.Builder.Builder(name = 'DVI',
 
 CScan = SCons.Scanner.C.CScan()
 
+def alias_builder(env, target, source):
+    pass
+
+Alias = SCons.Builder.Builder(name = 'Alias',
+                              action = alias_builder,
+                              target_factory = SCons.Node.Alias.default_ans.Alias,
+                              source_factory = SCons.Node.FS.default_fs.Entry)
+
 def get_devstudio_versions ():
     """
     Get list of devstudio versions from the Windows registry.  Return a
@@ -248,7 +258,7 @@ def make_win32_env_from_paths(include, lib, path):
         'LATEXFLAGS' : '',
         'LATEXCOM'   : '$LATEX $LATEXFLAGS $SOURCES',
         'DVISUFFIX'  : '.dvi',
-        'BUILDERS'   : [CFile, CXXFile, Object, Program, Library, DVI],
+        'BUILDERS'   : [Alias, CFile, CXXFile, DVI, Object, Program, Library],
         'SCANNERS'   : [CScan],
         'OBJPREFIX'  : '',
         'OBJSUFFIX'  : '.obj',
@@ -318,7 +328,7 @@ if os.name == 'posix':
         'LATEXFLAGS' : '',
         'LATEXCOM'   : '$LATEX $LATEXFLAGS $SOURCES',
         'DVISUFFIX'  : '.dvi',
-        'BUILDERS'   : [CFile, CXXFile, Object, Program, Library, DVI],
+        'BUILDERS'   : [Alias, CFile, CXXFile, DVI, Object, Program, Library],
         'SCANNERS'   : [CScan],
         'OBJPREFIX'  : '',
         'OBJSUFFIX'  : '.o',
diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py
new file mode 100644 (file)
index 0000000..31f0a9b
--- /dev/null
@@ -0,0 +1,93 @@
+
+"""scons.Node.Alias
+
+Alias nodes.
+
+This creates a hash of global Aliases (dummy targets).
+
+"""
+
+#
+# Copyright (c) 2001, 2002 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__ __USER__"
+
+import UserDict
+
+import SCons.Errors
+import SCons.Node
+import SCons.Util
+
+class AliasNameSpace(UserDict.UserDict):
+    def Alias(self, name):
+        if self.has_key(name):
+            raise SCons.Errors.UserError
+        self[name] = SCons.Node.Alias.Alias(name)
+        return self[name]
+
+    def lookup(self, name):
+        try:
+            return self[name]
+        except KeyError:
+            return None
+
+class Alias(SCons.Node.Node):
+    def __init__(self, name):
+        SCons.Node.Node.__init__(self)
+        self.name = name
+
+    def __str__(self):
+        return self.name
+
+    def build(self):
+        """A "builder" for aliases."""
+        pass
+
+    def set_bsig(self, bsig):
+        """An alias has no signature."""
+        pass
+
+    def set_csig(self, csig):
+        """An alias has no signature."""
+        pass
+
+    def current(self):
+        """If all of our children were up-to-date, then this
+        Alias was up-to-date, too."""
+        state = 0
+        for kid in self.children(None):
+            s = kid.get_state()
+            if s and (not state or s > state):
+                state = s
+        if state == 0 or state == SCons.Node.up_to_date:
+            return 1
+        else:
+            return 0
+
+    def sconsign(self):
+        """An Alias is not recorded in .sconsign files"""
+        pass
+        
+default_ans = AliasNameSpace()
+
+SCons.Node.arg2nodes_lookups.append(default_ans.lookup)
diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py
new file mode 100644 (file)
index 0000000..a14bc57
--- /dev/null
@@ -0,0 +1,87 @@
+#
+# Copyright (c) 2001, 2002 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__ __USER__"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Node.Alias
+
+class AliasTestCase(unittest.TestCase):
+
+    def test_AliasNameSpace(self):
+        """Test creating an Alias name space
+        """
+        ans = SCons.Node.Alias.AliasNameSpace()
+        assert not ans is None, ans
+
+    def test_ANS_Alias(self):
+        """Test the Alias() factory
+        """
+        ans = SCons.Node.Alias.AliasNameSpace()
+
+        a = ans.Alias('a1')
+       assert a.name == 'a1', a.name
+
+        try:
+            ans.Alias('a1')
+        except SCons.Errors.UserError:
+            pass
+        else:
+            raise TestFailed, "did not catch expected UserError"
+
+    def test_lookup(self):
+        """Test the lookup() method
+        """
+        ans = SCons.Node.Alias.AliasNameSpace()
+
+        ans.Alias('a1')
+        a = ans.lookup('a1')
+        assert a.name == 'a1', a.name
+
+        a1 = ans.lookup('a1')
+        assert a is a1, a1
+
+        a = ans.lookup('a2')
+        assert a == None, a
+
+    def test_Alias(self):
+        """Test creating an Alias() object
+        """
+        a1 = SCons.Node.Alias.Alias('a')
+        assert a1.name == 'a', a1.name
+
+        a2 = SCons.Node.Alias.Alias('a')
+        assert a2.name == 'a', a2.name
+
+        assert not a1 is a2
+        assert a1.name == a2.name
+
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(AliasTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
index 362559c444f92f737098baf4ef35cc62975c4e8e..1be5e8135bc8fd4ed3a1c42599b6b468f061041d 100644 (file)
@@ -336,6 +336,12 @@ class Entry(SCons.Node.Node):
             self.exists_flag = self.exists()
             return self.exists_flag
 
+    def get_parents(self):
+        parents = SCons.Node.Node.get_parents(self)
+        if self.dir and not isinstance(self.dir, ParentOfRoot):
+            parents.append(self.dir)
+        return parents
+
     def current(self):
         """If the underlying path doesn't exist, we know the node is
         not current without even checking the signature, so return 0.
index 6cf8f1afa95da5137c0aac6c38d13663446647d7..bea14990120601ed8424befc8bf2658bbfab9a4b 100644 (file)
@@ -290,8 +290,6 @@ class FSTestCase(unittest.TestCase):
         d1.build()
         assert not built_it
 
-        assert d1.get_parents() == [] 
-
         built_it = None
         assert not built_it
         f1.add_source([SCons.Node.Node()])    # XXX FAKE SUBCLASS ATTRIBUTE
@@ -480,6 +478,12 @@ class FSTestCase(unittest.TestCase):
         assert fs.File('foo.x').scanner_key() == '.x'
         assert fs.File('foo.xyz').scanner_key() == '.xyz'
 
+        d1 = fs.Dir('dir')
+        f1 = fs.File('dir/file')
+        assert f1.dir == d1, f1.dir
+        parents = f1.get_parents()
+        assert parents == [ d1 ], parents
+
 
 class find_fileTestCase(unittest.TestCase):
     def runTest(self):
index 1c9247655bd89acba6e97541a51aa7d0715d834e..16c55485634005ebcc0f38ccc349262960508d8f 100644 (file)
@@ -740,6 +740,40 @@ class NodeTestCase(unittest.TestCase):
         assert len(nodes) == 1, nodes
         assert isinstance(nodes[0], OtherNode), node
 
+        def lookup_a(str, F=Factory):
+            if str[0] == 'a':
+                n = F(str)
+                n.a = 1
+                return n
+            else:
+                return None
+
+        def lookup_b(str, F=Factory):
+            if str[0] == 'b':
+                n = F(str)
+                n.b = 1
+                return n
+            else:
+                return None
+
+        SCons.Node.arg2nodes_lookups.append(lookup_a)
+        SCons.Node.arg2nodes_lookups.append(lookup_b)
+
+        nodes = SCons.Node.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
+        assert len(nodes) == 3, nodes
+
+        assert nodes[0].name == 'aaa', nodes[0]
+        assert nodes[0].a == 1, nodes[0]
+        assert not hasattr(nodes[0], 'b'), nodes[0]
+
+        assert nodes[1].name == 'bbb'
+        assert not hasattr(nodes[1], 'a'), nodes[1]
+        assert nodes[1].b == 1, nodes[1]
+
+        assert nodes[2].name == 'ccc'
+        assert not hasattr(nodes[2], 'a'), nodes[1]
+        assert not hasattr(nodes[2], 'b'), nodes[1]
+
 
 
 if __name__ == "__main__":
index a7c8521a4d0e6c760b34cd2340d3ad9dff5faa80..98b2d500f113787a0ba309d682459513dd3fc081 100644 (file)
@@ -322,6 +322,9 @@ class Walker:
         return not self.stack
 
 
+arg2nodes_lookups = []
+
+
 def arg2nodes(arg, node_factory=None):
     """This function converts a string or list into a list of Node instances.
     It follows the rules outlined in the SCons design document by accepting
@@ -342,7 +345,14 @@ def arg2nodes(arg, node_factory=None):
     nodes = []
     for v in narg:
         if SCons.Util.is_String(v):
-            if node_factory:
+            n = None
+            for l in arg2nodes_lookups:
+                n = l(v)
+                if not n is None:
+                    break
+            if not n is None:
+                nodes.append(n)
+            elif node_factory:
                 nodes.append(node_factory(v))
         # Do we enforce the following restriction?  Maybe, but it
         # would also restrict what we can do to allow people to
index 055dbbc53a1e4642f40f5ee615d9e3f441363e70..6866e35f5281fb579863b1f866425bbf20264049 100644 (file)
@@ -753,9 +753,11 @@ def _main():
             node = x
         else:
             try:
-                node = SCons.Node.FS.default_fs.Entry(x,
-                                                      directory = top,
-                                                      create = 0)
+                node = SCons.Node.Alias.default_ans.lookup(x)
+                if node is None:
+                    node = SCons.Node.FS.default_fs.Entry(x,
+                                                          directory = top,
+                                                          create = 0)
             except UserError:
                 string = "scons: *** Do not know how to make target `%s'." % x
                 if not keep_going_on_error:
diff --git a/test/Alias.py b/test/Alias.py
new file mode 100644 (file)
index 0000000..e6de2c0
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002 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
+
+python = sys.executable
+
+test = TestSCons.TestSCons()
+
+test.subdir('sub1', 'sub2')
+
+test.write('build.py', r"""
+import sys
+open(sys.argv[1], 'wb').write(open(sys.argv[2], 'rb').read())
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+B = Builder(name = "B", action = r"%s build.py $TARGET $SOURCES")
+builders = Environment().Dictionary('BUILDERS')
+env = Environment(BUILDERS = builders + [ B ])
+env.B(target = 'f1.out', source = 'f1.in')
+env.B(target = 'f2.out', source = 'f2.in')
+env.B(target = 'f3.out', source = 'f3.in')
+SConscript('sub1/SConscript', "env")
+SConscript('sub2/SConscript', "env")
+env.Alias('foo', ['f2.out', 'sub1'])
+env.Alias('bar', ['sub2', 'f3.out'])
+env.Depends('f1.out', 'bar')
+""" % python)
+
+test.write(['sub1', 'SConscript'], """
+Import("env")
+env.B(target = 'f4.out', source = 'f4.in')
+env.B(target = 'f5.out', source = 'f5.in')
+env.B(target = 'f6.out', source = 'f6.in')
+""")
+
+test.write(['sub2', 'SConscript'], """
+Import("env")
+env.B(target = 'f7.out', source = 'f7.in')
+env.B(target = 'f8.out', source = 'f8.in')
+env.B(target = 'f9.out', source = 'f9.in')
+""")
+
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+test.write('f3.in', "f3.in\n")
+
+test.write(['sub1', 'f4.in'], "sub1/f4.in\n")
+test.write(['sub1', 'f5.in'], "sub1/f5.in\n")
+test.write(['sub1', 'f6.in'], "sub1/f6.in\n")
+
+test.write(['sub2', 'f7.in'], "sub2/f7.in\n")
+test.write(['sub2', 'f8.in'], "sub2/f8.in\n")
+test.write(['sub2', 'f9.in'], "sub2/f9.in\n")
+
+test.run(arguments = 'foo')
+
+test.fail_test(os.path.exists(test.workpath('f1.out')))
+test.fail_test(not os.path.exists(test.workpath('f2.out')))
+test.fail_test(os.path.exists(test.workpath('f3.out')))
+
+test.fail_test(not os.path.exists(test.workpath('sub1', 'f4.out')))
+test.fail_test(not os.path.exists(test.workpath('sub1', 'f5.out')))
+test.fail_test(not os.path.exists(test.workpath('sub1', 'f6.out')))
+
+test.fail_test(os.path.exists(test.workpath('sub2', 'f7.out')))
+test.fail_test(os.path.exists(test.workpath('sub2', 'f8.out')))
+test.fail_test(os.path.exists(test.workpath('sub2', 'f9.out')))
+
+test.up_to_date(arguments = 'foo')
+
+test.run(arguments = 'f1.out')
+
+test.fail_test(not os.path.exists(test.workpath('f1.out')))
+test.fail_test(not os.path.exists(test.workpath('f3.out')))
+
+test.fail_test(not os.path.exists(test.workpath('sub2', 'f7.out')))
+test.fail_test(not os.path.exists(test.workpath('sub2', 'f8.out')))
+test.fail_test(not os.path.exists(test.workpath('sub2', 'f9.out')))
+
+test.up_to_date(arguments = 'f1.out')
+
+test.pass_test()