Make SConsignFile() behavior the default.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 7 May 2005 19:19:41 +0000 (19:19 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 7 May 2005 19:19:41 +0000 (19:19 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1287 fdb21ef1-2011-0410-befe-b5e4ea1792b1

18 files changed:
doc/man/scons.1
src/CHANGES.txt
src/RELEASE.txt
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/SConfTests.py
src/engine/SCons/SConsign.py
src/engine/SCons/SConsignTests.py
src/engine/SCons/dblite.py
test/Configure.py
test/Repository/SConsignFile.py [new file with mode: 0644]
test/option-n.py
test/sconsign/corrupt.py [new file with mode: 0644]
test/sconsign/nonwritable.py [moved from test/sconsign.py with 61% similarity]
test/sconsign/old.py [moved from test/old-sconsign.py with 99% similarity]
test/sconsign/script.py [moved from test/sconsign-script.py with 98% similarity]
test/subdivide.py

index cb1334be0bbce1b969f9fead8d79bdc44c958118..33c7a191296fbc39ffd4434207a1fb743b82b7a2 100644 (file)
@@ -3983,13 +3983,17 @@ SConscript('bar/SConscript')    # will chdir to bar
 This tells
 .B scons
 to store all file signatures
-in the specified
+in the specified database
 .IR file .
 If the
 .I file
-is omitted,
-.B .sconsign.dbm
+name is omitted,
+.B .sconsign
 is used by default.
+(The actual file name(s) stored on disk
+may have an appropriated suffix appended
+by the
+.IR  dbm_module .)
 If
 .I file
 is not an absolute path name,
@@ -3997,6 +4001,20 @@ the file is placed in the same directory as the top-level
 .B SConstruct
 file.
 
+If
+.I file
+is
+.BR None ,
+then
+.B scons
+will store file signatures
+in a separate
+.B .sconsign
+file in each directory,
+not in one global database file.
+(This was the default behavior
+prior to SCons 0.96.91 and 0.97.)
+
 The optional
 .I dbm_module
 argument can be used to specify
@@ -4010,8 +4028,9 @@ and which works on all Python versions from 1.5.2 on.
 Examples:
 
 .ES
-# Stores signatures in ".sconsign.dbm"
-# in the top-level SConstruct directory.
+# Explicitly stores signatures in ".sconsign.dblite"
+# in the top-level SConstruct directory (the
+# default behavior).
 SConsignFile()
 
 # Stores signatures in the file "etc/scons-signatures"
@@ -4020,6 +4039,10 @@ SConsignFile("etc/scons-signatures")
 
 # Stores signatures in the specified absolute file name.
 SConsignFile("/home/me/SCons/signatures")
+
+# Stores signatures in a separate .sconsign file
+# in each directory.
+SConsignFile(None)
 .EE
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
index cfd7097a291499a5cb2bf5d71f369f04d64a318f..5513057d6a682d6da58809716ab9d263b8ca1b94 100644 (file)
@@ -266,6 +266,11 @@ RELEASE 0.97 - XXX
     that accomodate parser generators that write header files to a
     different suffix than the hard-coded .hpp when the -d option is used.
 
+  - The default behavior is now to store signature information in a
+    single .sconsign.dblite file in the top-level SConstruct directory.
+    The old behavior of a separate .sconsign file in each directory can
+    be specified by calling SConsignFile(None).
+
   From Wayne Lee:
 
   - Avoid "maximum recursion limit" errors when removing $(-$) pairs
index 9c09e60517052d7e230b39a7e5d2d8da131e9061..47b0031aaca9fa96cfe7be9b1c8da27fa3a85dd0 100644 (file)
@@ -58,7 +58,33 @@ RELEASE 0.97 - XXX
         or /usr/local was passed as the source to a Builder or Command()
         call, in which case SCons would scan the entire directory tree.
 
-    --  SIGNATURE CHANGES WILL CAUSE LIKELY REBUILDS AFTER UPGRADE
+    --  SIGNATURES ARE NOW STORED IN AN SConsignFile() BY DEFAULT,
+        CAUSING LIKELY REBUILDS; SPECIAL NOTE CONCERNING INTERACTION
+        WITH REPOSITORIES
+
+        The default behavior has been changed to store signature
+        information in a single .sconsign.dblite file in the top-level
+        SConstruct file.  This will cause rebuilds on upgrade to 0.97,
+        unless you were already calling the SConsignFile() function in
+        your SConscript files.
+
+        The previous default behavior was to store signature information
+        in a .sconsign file in each directory that contained target
+        files that SCons knew about.  The old behavior may be preserved
+        by specifying:
+
+              SConsignFile(None)
+
+        in any SConscript file.
+
+        If you are using the Repository feature, are not already using
+        the SConsignFile() function in your build, you *must* add
+        SConsignFile(None) to your build to keep interoperating with an
+        existing Repository that uses the old behavior of a .sconsign
+        file in each directory.  Alternatively, you can rebuild the
+        Repository with the new default behavior.
+
+    --  OTHER SIGNATURE CHANGES WILL CAUSE LIKELY REBUILDS AFTER UPGRADE
 
         This release adds several changes to the signature mechanism that
         will cause SCons to rebuild most configurations after upgrading
index b813cf17eca16cdd8caaae5248f060e693fb7232..58440d6b8400a50d86b3d19fd293c55ad1311a60 100644 (file)
@@ -1367,9 +1367,10 @@ class Base(SubstitutionEnvironment):
         return apply(SCons.Scanner.Scanner, nargs, nkw)
 
     def SConsignFile(self, name=".sconsign", dbm_module=None):
-        name = self.subst(name)
-        if not os.path.isabs(name):
-            name = os.path.join(str(self.fs.SConstruct_dir), name)
+        if not name is None:
+            name = self.subst(name)
+            if not os.path.isabs(name):
+                name = os.path.join(str(self.fs.SConstruct_dir), name)
         SCons.SConsign.File(name, dbm_module)
 
     def SideEffect(self, side_effect, target):
index fcd0fee1ae77658171b7a6731011442888aafd10..495fab0e19d0e5fcd552d8b21b807f6ce67c3865 100644 (file)
@@ -2568,6 +2568,10 @@ def generate(env):
             env.SConsignFile()
             assert fnames[5] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
             assert dbms[5] == None, dbms
+
+            env.SConsignFile(None)
+            assert fnames[6] == None, fnames
+            assert dbms[6] == None, dbms
         finally:
             SCons.SConsign.File = save_SConsign_File
 
index 75efc15be0aed50d615fb89c2cb838171085c54b..9d7086ce661c9fff508833409e2334ec8b3ca2a7 100644 (file)
@@ -422,6 +422,10 @@ class Base(SCons.Node.Node):
             self.path = name
         else:
             self.path = directory.entry_path(name)
+        if directory.tpath == '.':
+            self.tpath = name
+        else:
+            self.tpath = directory.entry_tpath(name)
         self.path_elements = directory.path_elements + [self]
 
         self.dir = directory
@@ -732,6 +736,7 @@ class FS(LocalFS):
 
         self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
         self.Top.path = '.'
+        self.Top.tpath = '.'
         self._cwd = self.Top
 
     def clear_cache(self):
@@ -1102,6 +1107,7 @@ class Dir(Base):
     def addRepository(self, dir):
         if dir != self and not dir in self.repositories:
             self.repositories.append(dir)
+            dir.tpath = '.'
             self.__clearRepositoryCache()
 
     def up(self):
@@ -1284,6 +1290,9 @@ class Dir(Base):
     def entry_path(self, name):
         return self.path + os.sep + name
 
+    def entry_tpath(self, name):
+        return self.tpath + os.sep + name
+
     def must_be_a_Dir(self):
         """Called to make sure a Node is a Dir.  Since we're already
         one, this is a no-op for us."""
@@ -1389,6 +1398,7 @@ class RootDir(Dir):
         # won't gag won't it calls some of our methods.
         self.abspath = ''
         self.path = ''
+        self.tpath = ''
         self.path_elements = []
         self.duplicate = 0
         Base.__init__(self, name, self, fs)
@@ -1397,6 +1407,7 @@ class RootDir(Dir):
         # initial drive letter (the name) plus the directory separator.
         self.abspath = name + os.sep
         self.path = name + os.sep
+        self.tpath = name + os.sep
         self._morph()
 
     def __str__(self):
@@ -1408,6 +1419,9 @@ class RootDir(Dir):
     def entry_path(self, name):
         return self.path + name
 
+    def entry_tpath(self, name):
+        return self.tpath + name
+
     def is_under(self, dir):
         if self is dir:
             return 1
index a5e070060902a232d3cc787fd2e389e7f3049181..2685c2b34d16fad3c62405fe64cb744ab7aa2066 100644 (file)
@@ -44,10 +44,15 @@ class SConfTestCase(unittest.TestCase):
 
     def setUp(self):
         # we always want to start with a clean directory
+        self.save_cwd = os.getcwd()
         self.test = TestCmd.TestCmd(workdir = '') 
+        os.chdir(self.test.workpath(''))
 
     def tearDown(self):
         self.test.cleanup()
+        import SCons.SConsign
+        SCons.SConsign.Reset()
+        os.chdir(self.save_cwd)
 
     def _resetSConfState(self):
         # Ok, this is tricky, and i do not know, if everything is sane.
@@ -104,7 +109,7 @@ class SConfTestCase(unittest.TestCase):
                                  log_file=self.test.workpath('config.log'))
         try:
             res = checks( self, sconf, TryFunc )
-            assert res[0] and not res[1] 
+            assert res[0] and not res[1], res
         finally:
             sconf.Finish()
             
@@ -115,7 +120,7 @@ class SConfTestCase(unittest.TestCase):
                                  log_file=self.test.workpath('config.log'))
         try:
             res = checks( self, sconf, TryFunc )
-            assert res[0] and not res[1] 
+            assert res[0] and not res[1], res
         finally:
             sconf.Finish()
         # we should have exactly one one error cached 
@@ -136,7 +141,7 @@ class SConfTestCase(unittest.TestCase):
         try:
             res = checks( self, sconf, TryFunc )
             log = self.test.read( self.test.workpath('config.log') )
-            assert res[0] and res[1] 
+            assert res[0] and res[1], res
         finally:
             sconf.Finish()
 
@@ -231,8 +236,8 @@ int main() {
                                  log_file=self.test.workpath('config.log'))
         try:
             res = checks(sconf)
-            assert res[0][0] and res[0][1] == "Hello" 
-            assert not res[1][0] and res[1][1] == ""
+            assert res[0][0] and res[0][1] == "Hello", res
+            assert not res[1][0] and res[1][1] == "", res
         finally:
             sconf.Finish()
         log = self.test.read( self.test.workpath('config.log') )
@@ -244,8 +249,8 @@ int main() {
                                  log_file=self.test.workpath('config.log'))
         try:
             res = checks(sconf)
-            assert res[0][0] and res[0][1] == "Hello" 
-            assert not res[1][0] and res[1][1] == ""
+            assert res[0][0] and res[0][1] == "Hello", res
+            assert not res[1][0] and res[1][1] == "", res
         finally:
             sconf.Finish()
         # we should have exactly one error cached 
@@ -271,9 +276,9 @@ int main() {
                                   log_file=self.test.workpath('config.log'))
         try:
             (ret, output) = sconf.TryAction(action=actionOK)
-            assert ret and output == "RUN OK"
+            assert ret and output == "RUN OK", (ret, output)
             (ret, output) = sconf.TryAction(action=actionFAIL)
-            assert not ret and output == ""
+            assert not ret and output == "", (ret, output)
         finally:
             sconf.Finish()
 
@@ -514,7 +519,7 @@ int main() {
 """
             (ret, output) = test.TryRun( prog, ".c" )
             test.Result( ret )
-            assert ret and output == "Hello" 
+            assert ret and output == "Hello", (ret, output)
             return ret
         
 
@@ -525,7 +530,7 @@ int main() {
                                  log_file=self.test.workpath('config.log'))
         try:
             ret = sconf.CheckCustom()
-            assert ret 
+            assert ret, ret
         finally:
             sconf.Finish()
             
index 212ec8def5f7cc24e1979f14fb29df5a64ee75d7..9b5c420958480b7b67a11d75e3f980aaca3ac95d 100644 (file)
@@ -35,15 +35,68 @@ import os.path
 import string
 import time
 
+import SCons.dblite
 import SCons.Node
 import SCons.Sig
 import SCons.Warnings
 
+from SCons.Debug import Trace
+
+def corrupt_dblite_warning(filename):
+    SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+                        "Ignoring corrupt .sconsign file: %s"%filename)
+
+SCons.dblite.ignore_corrupt_dbfiles = 1
+SCons.dblite.corruption_warning = corrupt_dblite_warning
+
 #XXX Get rid of the global array so this becomes re-entrant.
 sig_files = []
 
-# Handle to open database object if using the DB SConsign implementation.
-database = None
+# Info for the database SConsign implementation (now the default):
+# "DataBase" is a dictionary that maps top-level SConstruct directories
+# to open database handles.
+# "DB_Module" is the Python database module to create the handles.
+# "DB_Name" is the base name of the database file (minus any
+# extension the underlying DB module will add).
+DataBase = {}
+DB_Module = SCons.dblite
+DB_Name = ".sconsign"
+DB_sync_list = []
+
+def Get_DataBase(dir):
+    global DataBase, DB_Module, DB_Name
+    top = dir.fs.Top
+    if not os.path.isabs(DB_Name) and top.repositories:
+        mode = "c"
+        for d in [top] + top.repositories:
+            if dir.is_under(d):
+                try:
+                    return DataBase[d], mode
+                except KeyError:
+                    path = d.entry_abspath(DB_Name)
+                    try: db = DataBase[d] = DB_Module.open(path, mode)
+                    except (IOError, OSError): pass
+                    else:
+                        if mode != "r":
+                            DB_sync_list.append(db)
+                        return db, mode
+            mode = "r"
+    try:
+        return DataBase[top], "c"
+    except KeyError:
+        db = DataBase[top] = DB_Module.open(DB_Name, "c")
+        DB_sync_list.append(db)
+        return db, "c"
+    except TypeError:
+        print "DataBase =", DataBase
+        raise
+
+def Reset():
+    """Reset global state.  Used by unit tests that end up using
+    SConsign multiple times to get a clean slate for each test."""
+    global sig_files, DB_sync_list
+    sig_files = []
+    DB_sync_list = []
 
 if os.sep == '/':
     norm_entry = lambda s: s
@@ -55,9 +108,9 @@ def write():
     global sig_files
     for sig_file in sig_files:
         sig_file.write(sync=0)
-    if database:
+    for db in DB_sync_list:
         try:
-            syncmethod = database.sync
+            syncmethod = db.sync
         except AttributeError:
             pass # Not all anydbm modules have sync() methods.
         else:
@@ -94,19 +147,25 @@ class Base:
         self.entries[filename] = obj
         self.dirty = 1
 
+    def do_not_set_entry(self, filename, obj):
+        pass
+
 class DB(Base):
     """
     A Base subclass that reads and writes signature information
-    from a global .sconsign.dbm file.
+    from a global .sconsign.db* file--the actual file suffix is
+    determined by the specified database module.
     """
     def __init__(self, dir, module=None):
         Base.__init__(self, module)
 
         self.dir = dir
 
+        db, mode = Get_DataBase(dir)
+
+        tpath = norm_entry(dir.tpath)
         try:
-            global database
-            rawentries = database[norm_entry(self.dir.path)]
+            rawentries = db[tpath]
         except KeyError:
             pass
         else:
@@ -119,18 +178,25 @@ class DB(Base):
                 raise
             except:
                 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
-                                    "Ignoring corrupt sconsign entry : %s"%self.dir.path)
+                                    "Ignoring corrupt sconsign entry : %s"%self.dir.tpath)
+
+        if mode == "r":
+            # This directory is actually under a repository, which means
+            # likely they're reaching in directly for a dependency on
+            # a file there.  Don't actually set any entry info, so we
+            # won't try to write to that .sconsign.dblite file.
+            self.set_entry = self.do_not_set_entry
 
         global sig_files
         sig_files.append(self)
 
     def write(self, sync=1):
         if self.dirty:
-            global database
-            database[norm_entry(self.dir.path)] = cPickle.dumps(self.entries, 1)
+            db, mode = Get_DataBase(self.dir)
+            db[norm_entry(self.dir.tpath)] = cPickle.dumps(self.entries, 1)
             if sync:
                 try:
-                    syncmethod = database.sync
+                    syncmethod = db.sync
                 except AttributeError:
                     # Not all anydbm modules have sync() methods.
                     pass
@@ -223,19 +289,19 @@ class DirFile(Dir):
             except OSError:
                 pass
 
-ForDirectory = DirFile
+ForDirectory = DB
 
 def File(name, dbm_module=None):
     """
-    Arrange for all signatures to be stored in a global .sconsign.dbm
+    Arrange for all signatures to be stored in a global .sconsign.db*
     file.
     """
-    global database
-    if database is None:
-        if dbm_module is None:
-            import SCons.dblite
-            dbm_module = SCons.dblite
-        database = dbm_module.open(name, "c")
-
-    global ForDirectory
-    ForDirectory = DB
+    global ForDirectory, DB_Name, DB_Module
+    if name is None:
+        ForDirectory = DirFile
+        DB_Module = None
+    else:
+        ForDirectory = DB
+        DB_Name = name
+        if not dbm_module is None:
+            DB_Module = dbm_module
index c2f40bd623399e28c23d46c23c3db8e2c5c274fe..025ad8eea8c94ca08701854a91271c9cb6139d73 100644 (file)
@@ -28,6 +28,8 @@ import sys
 import TestCmd
 import unittest
 
+import SCons.dblite
+
 import SCons.SConsign
 
 class BuildInfo:
@@ -41,12 +43,30 @@ class DummyModule:
     def from_string(self, sig):
         return int(sig)
 
-class BaseTestCase(unittest.TestCase):
+class FS:
+    def __init__(self, top):
+        self.Top = top
+        self.Top.repositories = []
+
+class DummyNode:
+    def __init__(self, path='not_a_valid_path'):
+        self.path = path
+        self.tpath = path
+        self.fs = FS(self)
+
+class SConsignTestCase(unittest.TestCase):
+    def setUp(self):
+        self.save_cwd = os.getcwd()
+        self.test = TestCmd.TestCmd(workdir = '')
+        os.chdir(self.test.workpath(''))
+    def tearDown(self):
+        self.test.cleanup()
+        SCons.SConsign.Reset()
+        os.chdir(self.save_cwd)
+
+class BaseTestCase(SConsignTestCase):
 
     def runTest(self):
-        class DummyNode:
-            path = 'not_a_valid_path'
-
         aaa = BuildInfo('aaa')
         bbb = BuildInfo('bbb')
         bbb.arg1 = 'bbb arg1'
@@ -96,14 +116,11 @@ class BaseTestCase(unittest.TestCase):
         assert e.name == 'fff', e.name
         assert e.arg == 'fff arg', e.arg
 
-class SConsignDBTestCase(unittest.TestCase):
+class SConsignDBTestCase(SConsignTestCase):
 
     def runTest(self):
-        class DummyNode:
-            def __init__(self, path):
-                self.path = path
-        save_database = SCons.SConsign.database
-        SCons.SConsign.database = {}
+        save_DataBase = SCons.SConsign.DataBase
+        SCons.SConsign.DataBase = {}
         try:
             d1 = SCons.SConsign.DB(DummyNode('dir1'))
             d1.set_entry('aaa', BuildInfo('aaa name'))
@@ -137,14 +154,11 @@ class SConsignDBTestCase(unittest.TestCase):
             hhh = d32.get_entry('hhh')
             assert hhh.name == 'hhh name'
         finally:
-            SCons.SConsign.database = save_database
+            SCons.SConsign.DataBase = save_DataBase
 
-class SConsignDirFileTestCase(unittest.TestCase):
+class SConsignDirFileTestCase(SConsignTestCase):
 
     def runTest(self):
-        class DummyNode:
-            path = 'not_a_valid_path'
-
         foo = BuildInfo('foo')
         bar = BuildInfo('bar')
 
@@ -169,52 +183,65 @@ class SConsignDirFileTestCase(unittest.TestCase):
         assert e.arg == 'bbb arg', e.arg
 
 
-class SConsignFileTestCase(unittest.TestCase):
+class SConsignFileTestCase(SConsignTestCase):
 
     def runTest(self):
-        test = TestCmd.TestCmd(workdir = '')
+        test = self.test
         file = test.workpath('sconsign_file')
 
-        assert SCons.SConsign.database is None, SCons.SConsign.database
+        assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+        assert SCons.SConsign.DB_Name == ".sconsign", SCons.SConsign.DB_Name
+        assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
 
         SCons.SConsign.File(file)
 
-        assert not SCons.SConsign.database is SCons.dblite, SCons.SConsign.database
+        assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+        assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+        assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
+
+        SCons.SConsign.File(None)
+
+        assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+        assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+        assert SCons.SConsign.DB_Module is None, SCons.SConsign.DB_Module
 
         class Fake_DBM:
             def open(self, name, mode):
                 self.name = name
                 self.mode = mode
                 return self
+            def __getitem__(self, key):
+                pass
+            def __setitem__(self, key, value):
+                pass
 
         fake_dbm = Fake_DBM()
 
         SCons.SConsign.File(file, fake_dbm)
 
-        assert not SCons.SConsign.database is None, SCons.SConsign.database
+        assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+        assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+        assert SCons.SConsign.DB_Module is fake_dbm, SCons.SConsign.DB_Module
         assert not hasattr(fake_dbm, 'name'), fake_dbm
         assert not hasattr(fake_dbm, 'mode'), fake_dbm
 
-        SCons.SConsign.database = None
-
-        SCons.SConsign.File(file, fake_dbm)
+        SCons.SConsign.ForDirectory(DummyNode(test.workpath('dir')))
 
-        assert not SCons.SConsign.database is None, SCons.SConsign.database
+        assert not SCons.SConsign.DataBase is None, SCons.SConsign.DataBase
         assert fake_dbm.name == file, fake_dbm.name
         assert fake_dbm.mode == "c", fake_dbm.mode
 
 
-class writeTestCase(unittest.TestCase):
+class writeTestCase(SConsignTestCase):
 
     def runTest(self):
 
-        class DummyNode:
-            path = 'not_a_valid_path'
-
-        test = TestCmd.TestCmd(workdir = '')
+        test = self.test
         file = test.workpath('sconsign_file')
 
         class Fake_DBM:
+            def __getitem__(self, key):
+                return None
             def __setitem__(self, key, value):
                 pass
             def open(self, name, mode):
@@ -225,10 +252,10 @@ class writeTestCase(unittest.TestCase):
 
         fake_dbm = Fake_DBM()
 
-        SCons.SConsign.database = None
+        SCons.SConsign.DataBase = {}
         SCons.SConsign.File(file, fake_dbm)
 
-        f = SCons.SConsign.DirFile(DummyNode(), DummyModule())
+        f = SCons.SConsign.DB(DummyNode(), DummyModule())
 
         f.set_entry('foo', BuildInfo('foo'))
         f.set_entry('bar', BuildInfo('bar'))
index 00f82745a41618904fdd353d7adc00d216466c2b..637e50371fac1dd157bf59c5dff9ddba2328a819 100644 (file)
@@ -14,6 +14,9 @@ _open = __builtin__.open # avoid name clash
 keep_all_files = 00000
 ignore_corrupt_dbfiles = 0
 
+def corruption_warning(filename):
+    print "Warning: Discarding corrupt database:", filename
+
 if hasattr(types, 'UnicodeType'):
     def is_string(s):
         t = type(s)
@@ -64,7 +67,7 @@ class dblite:
           except cPickle.UnpicklingError:
             if (ignore_corrupt_dbfiles == 0): raise
             if (ignore_corrupt_dbfiles == 1):
-              print "Warning: Discarding corrupt database:", self._file_name
+              corruption_warning(self._file_name)
 
   def __del__(self):
     if (self._needs_sync):
index 515ae70b148dfaf14acb7eaf65ea7d589014157e..83155bbdcdc1a06713319aeccd460bb0efd6b3f2 100644 (file)
@@ -509,6 +509,7 @@ int main() {
                         os.path.join("build", "sub", "SConscript"))
 
     shutil.rmtree(test.workpath(work_dir, ".sconf_temp"))
+    os.unlink(test.workpath(work_dir, ".sconsign.dblite"))
 
     # now with SConscriptChdir(1)
     test.run(chdir=work_dir, arguments='chdir=yes')
diff --git a/test/Repository/SConsignFile.py b/test/Repository/SConsignFile.py
new file mode 100644 (file)
index 0000000..9643cb8
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__"
+
+"""
+Verify that the Repository option works when recording signature
+information in an SConsignFile().
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('repository', 'work')
+
+SConstruct_contents = """\
+SConsignFile('sconsignfile')
+env = Environment()
+result = env.Command('file.out', 'file.in', Copy("$TARGET", "$SOURCE"))
+Default(result)
+"""
+
+test.write(['repository', 'SConstruct'], SConstruct_contents)
+
+test.write(['repository', 'file.in'], "repository/file.in\n")
+
+test.run(chdir='repository')
+
+test.must_match(['repository', 'file.out'], "repository/file.in\n")
+
+test.write(['work', 'SConstruct'], SConstruct_contents)
+
+test.run(chdir='work',
+         arguments='-Y ../repository -n --debug=explain',
+         stdout=test.wrap_stdout("scons: `file.out' is up to date.\n"))
+
+test.must_not_exist(['work', 'file.out'])
+
+test.pass_test()
index aa0b8f2ef3a620737a60b9c2f7aab204178903c0..769c4dd114a74ab1c4ab2f39adf06716f1af4f99 100644 (file)
@@ -120,7 +120,7 @@ test.fail_test(not os.path.exists(test.workpath('f1.out')))
 expect = test.wrap_stdout("""\
 %s build.py f1.out
 """ % python)
-test.unlink('.sconsign')
+test.unlink('.sconsign.dblite')
 test.write('f1.out', "X1.out\n")
 test.run(arguments = '-n f1.out', stdout = expect)
 test.run(arguments = '-n f1.out', stdout = expect)
diff --git a/test/sconsign/corrupt.py b/test/sconsign/corrupt.py
new file mode 100644 (file)
index 0000000..fd46de4
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__"
+
+"""
+Test that we get proper warnings when .sconsign* files are corrupt.
+"""
+
+import os
+import TestSCons
+import TestCmd
+import cPickle
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+test.subdir('work1', ['work1', 'sub'],
+            'work2', ['work2', 'sub'])
+
+work1__sconsign_dblite = test.workpath('work1', '.sconsign.dblite')
+work2_sub__sconsign = test.workpath('work2', 'sub', '.sconsign')
+
+SConstruct_contents = """\
+def build1(target, source, env):
+    open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
+    return None
+
+B1 = Builder(action = build1)
+env = Environment(BUILDERS = { 'B1' : B1})
+env.B1(target = 'sub/foo.out', source = 'foo.in')
+"""
+
+
+
+test.write(['work1', 'SConstruct'], SConstruct_contents)
+
+test.write(['work1', 'foo.in'], "work1/foo.in\n")
+
+stderr = '''
+scons: warning: Ignoring corrupt .sconsign file: \.sconsign\.dblite
+.*
+'''
+
+stdout = test.wrap_stdout('build1\(\["sub.foo\.out"\], \["foo\.in"\]\)\n')
+
+test.write(work1__sconsign_dblite, 'not:a:sconsign:file')
+test.run(chdir='work1', arguments='.', stderr=stderr, stdout=stdout)
+
+test.write(work1__sconsign_dblite, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0')
+test.run(chdir='work1', arguments='.', stderr=stderr, stdout=stdout)
+
+
+
+# Test explicitly using a .sconsign file in each directory.
+
+SConstruct_contents = """\
+SConsignFile(None)
+""" + SConstruct_contents
+
+test.write(['work2', 'SConstruct'], SConstruct_contents)
+
+test.write(['work2', 'foo.in'], "work2/foo.in\n")
+
+stderr = '''
+scons: warning: Ignoring corrupt .sconsign file: sub.\.sconsign
+.*
+'''
+
+stdout = test.wrap_stdout('build1\(\["sub.foo\.out"\], \["foo\.in"\]\)\n')
+
+test.write(work2_sub__sconsign, 'not:a:sconsign:file')
+test.run(chdir='work2', arguments='.', stderr=stderr, stdout=stdout)
+
+test.write(work2_sub__sconsign, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0')
+test.run(chdir='work2', arguments='.', stderr=stderr, stdout=stdout)
+
+
+
+test.pass_test()
similarity index 61%
rename from test/sconsign.py
rename to test/sconsign/nonwritable.py
index e9a6609f4ca7356671aea10a237833f8a1f23c5f..9ad679cfa48956c739fc2fd59ffe3c568e4eacce 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+"""
+Test that things still work when a .sconsign* file is not writable.
+"""
+
 import os
 import TestSCons
 import TestCmd
@@ -31,9 +35,21 @@ import cPickle
 
 test = TestSCons.TestSCons(match = TestCmd.match_re)
 
-test.subdir('sub1', 'sub2', 'sub3')
-
-test.write('SConstruct', """
+test.subdir('work1',
+            ['work1', 'sub1'],
+            ['work1', 'sub2'],
+            ['work1', 'sub3'],
+            'work2',
+            ['work2', 'sub1'],
+            ['work2', 'sub2'],
+            ['work2', 'sub3'])
+
+work1__sconsign_dblite = test.workpath('work1', '.sconsign.dblite')
+work2_sub1__sconsign = test.workpath('work2', 'sub1', '.sconsign')
+work2_sub2__sconsign = test.workpath('work2', 'sub2', '.sconsign')
+work2_sub3__sconsign = test.workpath('work2', 'sub3', '.sconsign')
+
+SConstruct_contents = """\
 def build1(target, source, env):
     open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
     return None
@@ -52,48 +68,37 @@ env = Environment(BUILDERS = { 'B1' : B1, 'B2' : B2 })
 env.B1(target = 'sub1/foo.out', source = 'foo.in')
 env.B2(target = 'sub2/foo.out', source = 'foo.in')
 env.B2(target = 'sub3/foo.out', source = 'foo.in')
-""")
+"""
 
-test.write('foo.in', "foo.in\n")
 
-sub1__sconsign = test.workpath('sub1', '.sconsign')
-sub2__sconsign = test.workpath('sub2', '.sconsign')
-sub3__sconsign = test.workpath('sub3', '.sconsign')
 
-cPickle.dump({}, open(sub1__sconsign, 'wb'), 1)
-cPickle.dump({}, open(sub2__sconsign, 'wb'), 1)
+test.write(['work1', 'SConstruct'], SConstruct_contents)
 
-os.chmod(sub1__sconsign, 0444)
+test.write(['work1', 'foo.in'], "work1/foo.in\n")
 
-test.run(arguments = '.')
+test.write(work1__sconsign_dblite, "")
 
-test.fail_test(test.read(sub1__sconsign) == "")
-test.fail_test(test.read(sub2__sconsign) == "")
+os.chmod(work1__sconsign_dblite, 0444)
 
-os.chmod(sub1__sconsign, 0666)
+test.run(chdir='work1', arguments='.')
 
-test.write('SConstruct', """
-def build1(target, source, env):
-    open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
-    return None
 
-B1 = Builder(action = build1)
-env = Environment(BUILDERS = { 'B1' : B1})
-env.B1(target = 'sub1/foo.out', source = 'foo.in')
-""")
 
-stderr = '''
-scons: warning: Ignoring corrupt .sconsign file: sub1.\.sconsign
-.*
-'''
+SConstruct_contents = """\
+SConsignFile(None)
+""" + SConstruct_contents
+
+test.write(['work2', 'SConstruct'], SConstruct_contents)
+
+test.write(['work2', 'foo.in'], "work2/foo.in\n")
+
+cPickle.dump({}, open(work2_sub1__sconsign, 'wb'), 1)
+cPickle.dump({}, open(work2_sub2__sconsign, 'wb'), 1)
 
-stdout = test.wrap_stdout('build1\(\["sub1.foo\.out"\], \["foo\.in"\]\)\n')
+os.chmod(work2_sub1__sconsign, 0444)
 
-test.write(sub1__sconsign, 'not:a:sconsign:file')
-test.run(arguments = '.', stderr=stderr, stdout=stdout)
+test.run(chdir='work2', arguments='.')
 
-test.write(sub1__sconsign, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0')
-test.run(arguments = '.', stderr=stderr, stdout=stdout)
 
 
 test.pass_test()
similarity index 99%
rename from test/old-sconsign.py
rename to test/sconsign/old.py
index 6c057ddc68815e9bd1c435f1ae01762915968ef7..703a924ccc6fc69b8c12a255908cb2f7d316a7ec 100644 (file)
@@ -113,8 +113,11 @@ for key, val in old_db.items():
 db.sync()
 """)
 
+
+
 # Now generate a simple .sconsign file for a simple build.
 test.write(['src1', 'SConstruct'], """\
+SConsignFile(None)
 import os
 def cat(env, source, target):
     target = str(target[0])
@@ -154,10 +157,10 @@ for sconsign in sconsign_list:
 
 test.up_to_date(chdir='src1', arguments='.')
 
+
+
 # Now do the same with SConsignFile().
 test.write(['src2', 'SConstruct'], """\
-SConsignFile()
-
 import os
 def cat(env, source, target):
     target = str(target[0])
@@ -189,4 +192,6 @@ for sconsign in sconsign_list:
 
 test.up_to_date(chdir='src2', arguments='.')
 
+
+
 test.pass_test()
similarity index 98%
rename from test/sconsign-script.py
rename to test/sconsign/script.py
index 7a24aef3c35c68c5a0d4501199993ddf56b45b8b..9758d5a32a99b5c57853678041f42272151210c5 100644 (file)
@@ -74,6 +74,7 @@ test.subdir('work1', ['work1', 'sub1'], ['work1', 'sub2'],
             'work2', ['work2', 'sub1'], ['work2', 'sub2'])
 
 test.write(['work1', 'SConstruct'], """
+SConsignFile(None)
 env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
 env1.Program('sub1/hello.c')
 env2 = env1.Copy(CPPPATH = ['sub2'])
@@ -221,18 +222,18 @@ test.run(chdir = 'work1', arguments = '. --max-drift=1 --debug=stacktrace')
 
 test.run_sconsign(arguments = "-e hello.exe -e hello.obj work1/sub1/.sconsign",
          stdout = """\
-hello.exe: None \d+ None
-        hello.obj: \d+
-hello.obj: None \d+ None
-        hello.c: \d+
+hello.exe: None \S+ None
+        hello.obj: \S+
+hello.obj: None \S+ None
+        hello.c: \S+
 """)
 
 test.run_sconsign(arguments = "-e hello.exe -e hello.obj -r work1/sub1/.sconsign",
          stdout = """\
-hello.exe: None \d+ None
-        hello.obj: \d+
-hello.obj: None \d+ None
-        hello.c: \d+
+hello.exe: None \S+ None
+        hello.obj: \S+
+hello.obj: None \S+ None
+        hello.c: \S+
 """)
 
 
index f6aa4f11d1b842b5862b4651ae84fcff1efb54a7..18219bf3013375064b0758a6828676d220ddfbe6 100644 (file)
@@ -25,8 +25,9 @@
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 """
-Verify that rebuilds do not occur when TargetSignatures()
-content is used to subdivide a dependency tree.
+Verify that rebuilds do not occur when SConsignFile(None) is used to
+put a .sconsign file in each directory, and TargetSignatures('content')
+is used to subdivide a dependency tree.
 """
 
 import os.path
@@ -46,6 +47,7 @@ test = TestSCons.TestSCons()
 test.subdir('src', ['src', 'sub'])
 
 test.write('SConstruct', """\
+SConsignFile(None)
 TargetSignatures('content')
 env = Environment()
 env.SConscript('src/SConstruct', exports=['env'])
@@ -53,6 +55,7 @@ env.Object('foo.c')
 """)
 
 test.write(['src', 'SConstruct'], """\
+SConsignFile(None)
 TargetSignatures('content')
 env = Environment()
 p = env.Program('prog', ['main.c', '../foo%s', 'sub/bar.c'])