Add BuildDir(), Export(), and Install() functionality (courtesy Charles Crain).
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 15 Dec 2001 00:23:46 +0000 (00:23 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 15 Dec 2001 00:23:46 +0000 (00:23 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@157 fdb21ef1-2011-0410-befe-b5e4ea1792b1

15 files changed:
src/CHANGES.txt
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
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/Scanner/C.py
src/engine/SCons/Scanner/CTests.py
src/engine/SCons/Script.py
test/BuildDir.py [new file with mode: 0644]
test/CPPPATH.py
test/Depends.py
test/Install.py [new file with mode: 0644]
test/errors.py

index 3d47e8449028e3d9141a907eab0c887787c9b3ea..2aa15252f4e4665c7476689f5130cc14964ab177 100644 (file)
@@ -13,6 +13,12 @@ RELEASE 0.02 -
   - Fixed the version comment in the scons.bat (the UNIX geek used
     # instead of @rem).
 
+  - Added support for the Install method (courtesy Charles Crain).
+
+  - Added support for the BuildDir method (courtesy Charles Crain).
+
+  - Added the Export method (courtesy Charles Crain).
+
 
 
 RELEASE 0.01 - Thu Dec 13 19:25:23 CST 2001
index 9f78a655738bb07b14b8d67e18bef75067ef45cb..ad92dfd46be662ebff03f92e57fc2e1629b70175 100644 (file)
@@ -30,7 +30,7 @@ XXX
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
-
+import os
 import copy
 import os.path
 import re
@@ -40,12 +40,32 @@ import SCons.Builder
 import SCons.Defaults
 from SCons.Errors import UserError
 from UserList import UserList
-
-def Command():
-    pass       # XXX
-
-def Install():
-    pass       # XXX
+import SCons.Node.FS
+import sys
+import shutil
+
+def installFunc(env, target, source):
+    try:
+        os.unlink(target)
+    except OSError:
+        pass
+    
+    try:
+        SCons.Node.FS.file_link(source[0], target)
+        print 'Install file: "%s" as "%s"' % \
+              (source[0], target)
+        return 0
+    except IOError, e:
+        sys.stderr.write('Unable to install "%s" as "%s"\n%s\n' % \
+                         (source[0], target, str(e)))
+        return -1
+    except OSError, e:
+        sys.stderr.write('Unable to install "%s" as "%s"\n%s\n' % \
+                         (source[0], target, str(e)))
+        return -1
+
+InstallBuilder = SCons.Builder.Builder(name='Install',
+                                       action=installFunc)
 
 def InstallAs():
     pass       # XXX
@@ -171,6 +191,20 @@ class Environment:
         bld = SCons.Builder.Builder(name="Command", action=action)
         return bld(self, target, source)
 
+    def Install(self, dir, source):
+        """Install specified files in the given directory."""
+        sources = SCons.Util.scons_str2nodes(source)
+        dnodes = SCons.Util.scons_str2nodes(dir,
+                                            SCons.Node.FS.default_fs.Dir)
+        tgt = []
+        for dnode in dnodes:
+            for src in sources:
+                target = SCons.Node.FS.default_fs.File(src.name, dnode)
+                tgt.append(InstallBuilder(self, target, src))
+        if len(tgt) == 1:
+            tgt = tgt[0]
+        return tgt
+
     def subst(self, string):
        """Recursively interpolates construction variables from the
        Environment into the specified string, returning the expanded
index 65eb510295aca3dce0f8693356522c8da10c54ce..c2faa350779959c1293934a93a06ec3ab6258e62 100644 (file)
@@ -139,9 +139,6 @@ class EnvironmentTestCase(unittest.TestCase):
        s = env3.get_scanner(".cxx")
        assert s == None, s
 
-    def test_Command(self):
-       pass    # XXX
-
     def test_Copy(self):
        """Test construction Environment copying
 
@@ -218,7 +215,14 @@ class EnvironmentTestCase(unittest.TestCase):
        assert env1 == env2
 
     def test_Install(self):
-       pass    # XXX
+       """Test Install method"""
+        env=Environment()
+        tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ])
+        paths = map(str, tgt)
+        paths.sort()
+        assert paths == [ 'export/foo1', 'export/foo2' ], paths
+        for tnode in tgt:
+            assert tnode.builder == InstallBuilder
 
     def test_InstallAs(self):
        pass    # XXX
index 648e1f91ebf24111b2bb5333399a74e17007010b..303db61d51f7b1aef3247a0a7eb5a8f83d5a5f05 100644 (file)
@@ -39,6 +39,18 @@ import types
 import SCons.Node
 from UserDict import UserDict
 import sys
+from SCons.Errors import UserError
+
+try:
+    import os
+    file_link = os.link
+except AttributeError:
+    import shutil
+    import stat
+    def file_link(src, dest):
+        shutil.copyfile(src, dest)
+        st=os.stat(src)
+        os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]))
 
 class PathName:
     """This is a string like object with limited capabilities (i.e.,
@@ -119,6 +131,7 @@ class FS:
         self.Root = PathDict()
         self.Top = self.__doLookup(Dir, path)
         self.Top.path = '.'
+        self.Top.srcpath = '.'
         self.Top.path_ = os.path.join('.', '')
         self.cwd = self.Top
 
@@ -242,7 +255,17 @@ class FS:
         name, directory = self.__transformPath(name, directory)
         return self.__doLookup(Dir, name, directory)
 
-
+    def BuildDir(self, build_dir, src_dir):
+        """Link the supplied build directory to the source directory
+        for purposes of building files."""
+        dirSrc = self.Dir(src_dir)
+        dirBuild = self.Dir(build_dir)
+        if not dirSrc.is_under(self.Top) or not dirBuild.is_under(self.Top):
+            raise UserError, "Both source and build directories must be under top of build tree."
+        if dirSrc.is_under(dirBuild):
+            raise UserError, "Source directory cannot be under build directory."
+        dirBuild.link(dirSrc)
+        
 
 class Entry(SCons.Node.Node):
     """A generic class for file system entries.  This class if for
@@ -272,6 +295,19 @@ class Entry(SCons.Node.Node):
         self.abspath_ = self.abspath
         self.dir = directory
        self.use_signature = 1
+        self.__doSrcpath()
+
+    def adjust_srcpath(self):
+        self.__doSrcpath()
+        
+    def __doSrcpath(self):
+        if self.dir:
+            if str(self.dir.srcpath) == '.':
+                self.srcpath = self.name
+            else:
+                self.srcpath = os.path.join(self.dir.srcpath, self.name)
+        else:
+            self.srcpath = self.name
 
     def __str__(self):
        """A FS node's string representation is its path name."""
@@ -301,6 +337,13 @@ class Entry(SCons.Node.Node):
             return 0
         return None
 
+    def is_under(self, dir):
+        if self is dir:
+            return 1
+        if not self.dir:
+            return 0
+        return self.dir.is_under(dir)
+
 
 
 # XXX TODO?
@@ -343,6 +386,21 @@ class Dir(Entry):
         self.builder = 1
         self._sconsign = None
 
+    def __doReparent(self):
+        for ent in self.entries.values():
+            if not ent is self and not ent is self.dir:
+                ent.adjust_srcpath()
+
+    def adjust_srcpath(self):
+        Entry.adjust_srcpath(self)
+        self.__doReparent()
+                
+    def link(self, srcdir):
+        """Set this directory as the build directory for the
+        supplied source directory."""
+        self.srcpath = srcdir.path
+        self.__doReparent()
+
     def up(self):
         return self.entries['..']
 
@@ -425,17 +483,21 @@ class Dir(Entry):
 class File(Entry):
     """A class for files in a file system.
     """
+    def __init__(self, name, directory = None):
+        Entry.__init__(self, name, directory)
+        self._morph()
+        
     def _morph(self):
-       """Turn a file system node into a File object.  Nothing
-       to be done, actually, because all of the info we need
-       is handled by our base Entry class initialization."""
-        pass
+        """Turn a file system node into a File object."""
+        self.created = 0
 
     def root(self):
         return self.dir.root()
 
     def get_contents(self):
-        return open(self.path, "r").read()
+        if not self.exists():
+            return ''
+        return open(str(self), "r").read()
 
     def get_timestamp(self):
         if self.exists():
@@ -472,6 +534,17 @@ class File(Entry):
                     self.add_implicit(scn.scan(self.path, self.env),
                                       scn)
                     self.scanned[scn] = 1
+                    
+    def exists(self):
+        if not self.created:
+            self.created = 1
+            if self.srcpath != self.path and \
+               os.path.exists(self.srcpath):
+                if os.path.exists(self.path):
+                    os.unlink(self.path)
+                self.__createDir()
+                file_link(self.srcpath, self.path)
+        return Entry.exists(self)
 
     def __createDir(self):
         # ensure that the directories for this node are
index 45f8e643c57abe9bad56c299a09192a8ee672f8b..0f6b8b72e4c68c37a0712d41ee59f1ddbc2fa854 100644 (file)
@@ -29,7 +29,8 @@ import string
 import sys
 import unittest
 import SCons.Node.FS
-
+from TestCmd import TestCmd
+from SCons.Errors import UserError
 
 built_it = None
 
@@ -59,6 +60,54 @@ class Environment:
     def get_scanner(self, skey):
         return self.scanner
 
+class BuildDirTestCase(unittest.TestCase):
+    def runTest(self):
+        """Test build dir functionality"""
+        fs = SCons.Node.FS.FS()
+        f1 = fs.File('build/test1')
+        fs.BuildDir('build', 'src')
+        f2 = fs.File('build/test2')
+        assert f1.srcpath == 'src/test1', f1.srcpath
+        assert f2.srcpath == 'src/test2', f2.srcpath
+
+        fs = SCons.Node.FS.FS()
+        f1 = fs.File('build/test1')
+        fs.BuildDir('build', '.')
+        f2 = fs.File('build/test2')
+        assert f1.srcpath == 'test1', f1.srcpath
+        assert f2.srcpath == 'test2', f2.srcpath
+
+        fs = SCons.Node.FS.FS()
+        fs.BuildDir('build/var1', 'src')
+        fs.BuildDir('build/var2', 'src')
+        f1 = fs.File('build/var1/test1')
+        f2 = fs.File('build/var2/test1')
+        assert f1.srcpath == 'src/test1', f1.srcpath
+        assert f2.srcpath == 'src/test1', f2.srcpath
+
+        exc_caught = 0
+        try:
+            fs = SCons.Node.FS.FS()
+            fs.BuildDir('/test/foo', '.')
+        except UserError:
+            exc_caught = 1
+        assert exc_caught, "Should have caught a UserError."
+
+        exc_caught = 0
+        try:
+            fs = SCons.Node.FS.FS()
+            fs.BuildDir('build', '/test/foo')
+        except UserError:
+            exc_caught = 1
+        assert exc_caught, "Should have caught a UserError."
+
+        exc_caught = 0
+        try:
+            fs = SCons.Node.FS.FS()
+            fs.BuildDir('build', 'build/src')
+        except UserError:
+            exc_caught = 1
+        assert exc_caught, "Should have caught a UserError."
 
 class FSTestCase(unittest.TestCase):
     def runTest(self):
@@ -68,8 +117,6 @@ class FSTestCase(unittest.TestCase):
         tests in one environment, so we don't have to set up a
         complicated directory structure for each test individually.
         """
-        from TestCmd import TestCmd
-
         test = TestCmd(workdir = '')
         test.subdir('sub', ['sub', 'dir'])
 
@@ -393,5 +440,6 @@ class FSTestCase(unittest.TestCase):
 if __name__ == "__main__":
     suite = unittest.TestSuite()
     suite.addTest(FSTestCase())
+    suite.addTest(BuildDirTestCase())
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
index 673ab2573ff27dd35dd374f4c35a5b6beff5c664..f55659e9552186cf6fb5977ce50034c1d5aa6cdd 100644 (file)
@@ -415,8 +415,32 @@ class NodeTestCase(unittest.TestCase):
         assert n2.children_are_executed()
         assert n1.children_are_executed()
 
-
-
+    def test_rescan(self):
+        """Test that built nodes are rescanned."""
+        class DummyScanner:
+            pass
+        
+        class TestNode(SCons.Node.Node):
+            def scan(self):
+                for scn in self.scanners:
+                    if not self.scanned.has_key(scn):
+                        n=SCons.Node.Node()
+                        n.scanner_set(scn)
+                        self.add_implicit([ n ], scn)
+                    self.scanned[scn] = 1
+        tn=TestNode()
+        tn.builder_set(Builder())
+        tn.env_set(Environment())
+        ds = DummyScanner()
+        tn.scanner_set(ds)
+        tn.scan()
+        map(lambda x: x.scan(), tn.depends)
+        assert tn.scanned[ds]
+        assert len(tn.implicit[ds]) == 1, tn.implicit
+        tn.build()
+        assert len(tn.implicit[ds]) == 2, tn.implicit
+        for dep in tn.implicit[ds]:
+            assert dep.scanned[ds] == 1
 
 if __name__ == "__main__":
     suite = unittest.makeSuite(NodeTestCase, 'test_')
index ddcddfb9a13492d6cb74eb9c67c2a19e6f862263..0682c7f10fbaaa25138277a3e62b9cd689225953 100644 (file)
@@ -76,6 +76,28 @@ class Node:
                                     target = self, source = self.sources)
        if stat != 0:
            raise BuildError(node = self, stat = stat)
+
+        # If we succesfully build a node, then we need to rescan for
+        # implicit dependencies, since it might have changed on us.
+
+        # XXX Modify this so we only rescan using the scanner(s) relevant
+        # to this build.
+        for scn in self.scanners:
+            try:
+                del self.scanned[scn]
+            except KeyError:
+                pass
+        
+        self.scan()
+
+        for scn in self.scanners:
+            try:
+                for dep in self.implicit[scn]:
+                    w=Walker(dep)
+                    while not w.is_done():
+                        w.next().scan()
+            except KeyError:
+                pass
        return stat
 
     def builder_set(self, builder):
index 8ad7e002b5e366fbd0cab661390756ff369854ed..a5aa02d66c98706b0012fb0e2fda2a4705fdc6c5 100644 (file)
@@ -96,7 +96,7 @@ def scan(filename, env, args = [SCons.Node.FS.default_fs, ()]):
 
     fs, cpppath = args
 
-    try:
+    if fs.File(filename, fs.Top).exists():
         file = open(filename)
         contents = file.read()
         file.close()
@@ -109,9 +109,9 @@ def scan(filename, env, args = [SCons.Node.FS.default_fs, ()]):
             source_dir = (fs.Dir(dir, fs.Top),)
         else:
             source_dir = ( fs.Top, )
+
         return (SCons.Util.find_files(angle_includes, cpppath + source_dir,
                                       fs.File)
                 + SCons.Util.find_files(quote_includes, source_dir + cpppath,
                                         fs.File))
-    except (IOError, OSError):
-        return []
+    return []
index bc30fa4805febc7918682004fde51cdb8cef0549..c9cc5ea18656b3a98918fea78476f8d97f6989ce 100644 (file)
@@ -29,6 +29,7 @@ import unittest
 import sys
 import os
 import os.path
+import SCons.Node.FS
 
 test = TestCmd.TestCmd(workdir = '')
 
@@ -176,6 +177,11 @@ class CScannerTestCase5(unittest.TestCase):
         env = DummyEnvironment([])
         s = SCons.Scanner.C.CScan()
         deps = s.instance(env).scan(test.workpath('f3.cpp'), env)
+        
+        # Make sure exists() gets called on the file node being
+        # scanned, essential for cooperation with BuildDir functionality.
+        assert SCons.Node.FS.default_fs.File(test.workpath('f3.cpp')).created
+        
         headers =  ['f1.h', 'f2.h', 'f3.h', 'fi.h', 'fj.h',
                     'd1/f1.h', 'd1/f2.h', 'd1/f3.h']
         deps_match(self, deps, map(test.workpath, headers))
index 836fb1aeeb69507388f427b89c208ae60928dd6e..086e470cf10a83dee4512d58faa0c0df7b238dbf 100644 (file)
@@ -42,6 +42,7 @@ import os.path
 import string
 import sys
 import traceback
+import copy
 
 # Strip the script directory from sys.path() so on case-insensitive
 # (WIN32) systems Python doesn't think that the "scons" script is the
@@ -159,9 +160,9 @@ def _scons_other_errors():
 
 
 
-def SConscript(filename):
+def SConscript(sconscript, export={}):
     global scripts
-    scripts.append(SCons.Node.FS.default_fs.File(filename))
+    scripts.append( (SCons.Node.FS.default_fs.File(sconscript), export) )
 
 def Default(*targets):
     for t in targets:
@@ -175,7 +176,12 @@ def Help(text):
        print "Use scons -H for help about command-line options."
        sys.exit(0)
 
+def BuildDir(build_dir, src_dir):
+    SCons.Node.FS.default_fs.BuildDir(build_dir, src_dir)
 
+def Export(**kw):
+    # A convenient shorthand to pass exports to the SConscript function.
+    return kw
 
 #
 # After options are initialized, the following variables are
@@ -374,9 +380,9 @@ def options_init():
     def opt_f(opt, arg):
        global scripts
        if arg == '-':
-            scripts.append(arg)
+            scripts.append( ( arg, {} ) )
        else:
-           scripts.append(SCons.Node.FS.default_fs.File(arg))
+            scripts.append( (SCons.Node.FS.default_fs.File(arg), {}) )
 
     Option(func = opt_f,
        short = 'f', long = ['file', 'makefile', 'sconstruct'], arg = 'FILE',
@@ -599,7 +605,7 @@ def _main():
     if not scripts:
         for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
             if os.path.isfile(file):
-                scripts.append(SCons.Node.FS.default_fs.File(file))
+                scripts.append( (SCons.Node.FS.default_fs.File(file), {}) )
                 break
 
     if help_option == 'H':
@@ -619,17 +625,19 @@ def _main():
     sys.path = include_dirs + sys.path
 
     while scripts:
-        f, scripts = scripts[0], scripts[1:]
+        f, exports = scripts.pop(0)
+        script_env = copy.copy(globals())
+        script_env.update(exports)
         if f == "-":
-           exec sys.stdin in globals()
+            exec sys.stdin in script_env
        else:
-            try:
-                file = open(f.path, "r")
-           except IOError, s:
-                sys.stderr.write("Ignoring missing SConscript '%s'\n" % f.path)
-           else:
+            if f.exists():
+                file = open(str(f), "r")
                 SCons.Node.FS.default_fs.chdir(f.dir)
-                exec file in globals()
+                exec file in script_env
+            else:
+                sys.stderr.write("Ignoring missing SConscript '%s'\n" % f.path)
+
     SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Top)
 
     if help_option == 'h':
diff --git a/test/BuildDir.py b/test/BuildDir.py
new file mode 100644 (file)
index 0000000..78d2afc
--- /dev/null
@@ -0,0 +1,111 @@
+#!/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.path
+import sys
+import time
+import TestSCons
+
+if sys.platform == 'win32':
+    _exe = '.exe'
+else:
+    _exe = ''
+
+test = TestSCons.TestSCons()
+
+foo1 = test.workpath('build/var1/foo1' + _exe)
+foo2 = test.workpath('build/var1/foo2' + _exe)
+foo3 = test.workpath('build/var2/foo1' + _exe)
+foo4 = test.workpath('build/var2/foo2' + _exe)
+
+test.write('SConstruct', """
+BuildDir('build/var1', 'src')
+BuildDir('build/var2', 'src')
+SConscript('build/var1/SConscript')
+SConscript('build/var2/SConscript')
+""")
+
+test.subdir('src')
+test.write('src/SConscript', """
+import os
+import os.path
+
+def buildIt(target, source, env):
+    if not os.path.exists('build'):
+        os.mkdir('build')
+    f1=open(source[0], 'r')
+    f2=open(target, 'w')
+    f2.write(f1.read())
+    f2.close()
+    f1.close()
+    return 0
+
+env = Environment()
+env.Command(target='f2.c', source='f2.in', action=buildIt)
+env.Program(target='foo2', source='f2.c')
+env.Program(target='foo1', source='f1.c')
+""")
+
+test.write('src/f1.c', r"""
+#include "f1.h"
+
+int
+main(int argc, char *argv[])
+{
+       argv[argc++] = "--";
+       printf(F1_STR);
+       exit (0);
+}
+""")
+
+test.write('src/f2.in', r"""
+#include "f2.h"
+
+int
+main(int argc, char *argv[])
+{
+       argv[argc++] = "--";
+       printf(F2_STR);
+       exit (0);
+}
+""")
+
+test.write('src/f1.h', """
+#define F1_STR "f1.c\n"
+""")
+
+test.write('src/f2.h', """
+#define F2_STR "f2.c\n"
+""")
+
+test.run(arguments = '.')
+
+test.run(program = foo1, stdout = "f1.c\n")
+test.run(program = foo2, stdout = "f2.c\n")
+test.run(program = foo3, stdout = "f1.c\n")
+test.run(program = foo4, stdout = "f2.c\n")
+
+test.pass_test()
index 52fbbfb8159888e5a5359ea06e3d68adac261361..aa750194b831119baad00db69ae52f2880840a9b 100644 (file)
@@ -46,7 +46,7 @@ test.write('SConstruct', """
 env = Environment(CPPPATH = ['include'])
 obj = env.Object(target='prog', source='subdir/prog.c')
 env.Program(target='prog', source=obj)
-SConscript('subdir/SConscript')
+SConscript('subdir/SConscript', Export(env=env))
 """)
 
 test.write(['subdir', 'SConscript'], """
index 822573a5f795e5ce81784c2487a12453f6ed4cce..ab045aeb8607526471923d89cf6321846441d1bf 100644 (file)
@@ -52,7 +52,7 @@ env.Depends(target = 'f3.out', dependency = 'subdir/bar.dep')
 env.Foo(target = 'f1.out', source = 'f1.in')
 env.Foo(target = 'f2.out', source = 'f2.in')
 env.Bar(target = 'f3.out', source = 'f3.in')
-SConscript('subdir/SConscript')
+SConscript('subdir/SConscript', Export(env=env))
 """ % (python, python))
 
 test.write(['subdir', 'SConscript'], """
diff --git a/test/Install.py b/test/Install.py
new file mode 100644 (file)
index 0000000..dce9990
--- /dev/null
@@ -0,0 +1,96 @@
+#!/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.path
+import sys
+import time
+import TestSCons
+
+if sys.platform == 'win32':
+    _exe = '.exe'
+else:
+    _exe = ''
+
+test = TestSCons.TestSCons()
+
+foo1 = test.workpath('export/foo1' + _exe)
+foo2 = test.workpath('export/foo2' + _exe)
+
+test.write('SConstruct', """
+env=Environment()
+t=env.Program(target='foo1', source='f1.c')
+env.Install(dir='export', source=t)
+t=env.Program(target='foo2', source='f2.c')
+env.Install(dir='export', source=t)
+""")
+
+test.write('f1.c', """
+#include <stdio.h>
+
+int main(void)
+{
+   printf("f1.c\n");
+   return 0;
+}
+""")
+
+test.write('f2.c', """
+#include <stdio.h>
+
+int main(void)
+{
+   printf("f2.c\n");
+   return 0;
+}
+""")
+
+test.run(arguments = '.')
+
+test.run(program = foo1, stdout = "f1.c\n")
+test.run(program = foo2, stdout = "f2.c\n")
+
+# make sure the programs didn't get rebuilt, because nothing changed:
+oldtime1 = os.path.getmtime(foo1)
+oldtime2 = os.path.getmtime(foo2)
+
+test.write('f1.c', """
+#include <stdio.h>
+
+int main(void)
+{
+   printf("f1.c again\n");
+   return 0;
+}
+""")
+
+time.sleep(2) # introduce a small delay, to make the test valid
+
+test.run(arguments = '.')
+
+test.fail_test(oldtime1 == os.path.getmtime(foo1))
+test.fail_test(oldtime2 != os.path.getmtime(foo2))
+
+test.pass_test()
index abf2a2833a10f3de04e90e5ed8f31eb82c799faa..5edd8f92c828a3b9e2fc92e6fc2a6fe6144a1ec8 100644 (file)
@@ -67,7 +67,7 @@ test.run(arguments='-f SConstruct3',
   File ".*Script.py", line \d+, in main
     _main\(\)
   File ".*Script.py", line \d+, in _main
-    exec file in globals\(\)
+    exec file in script_env
   File "SConstruct3", line \d+, in \?
     raise InternalError, 'error inside'
 InternalError: error inside