Refactor .sconsign management into its own module.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 6 May 2004 06:21:43 +0000 (06:21 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 6 May 2004 06:21:43 +0000 (06:21 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@971 fdb21ef1-2011-0410-befe-b5e4ea1792b1

12 files changed:
src/engine/MANIFEST.in
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/SConfTests.py
src/engine/SCons/SConsign.py [new file with mode: 0644]
src/engine/SCons/SConsignTests.py [new file with mode: 0644]
src/engine/SCons/Script/__init__.py
src/engine/SCons/Sig/SigTests.py
src/engine/SCons/Sig/__init__.py
src/script/sconsign.py

index 4942be8067ef31bbd1c04fb7546dce6bc26a2690..1b8a06fa3fcc9fc125831a655fb76ea7f461bc4e 100644 (file)
@@ -40,6 +40,7 @@ SCons/Scanner/Fortran.py
 SCons/Scanner/IDL.py
 SCons/Scanner/Prog.py
 SCons/SConf.py
+SCons/SConsign.py
 SCons/Script/SConscript.py
 SCons/Script/__init__.py
 SCons/Sig/__init__.py
index d4279d17c7c0671635faf9f976b921c2b2e85a80..b92064388dd9600d44e8b5e8defbd3719e193ad0 100644 (file)
@@ -53,6 +53,7 @@ import SCons.Node.Alias
 import SCons.Node.FS
 import SCons.Node.Python
 import SCons.Platform
+import SCons.SConsign
 import SCons.Sig
 import SCons.Sig.MD5
 import SCons.Sig.TimeStamp
@@ -1164,7 +1165,7 @@ class Base:
         name = self.subst(name)
         if not os.path.isabs(name):
             name = os.path.join(str(self.fs.SConstruct_dir), name)
-        SCons.Sig.SConsignFile(name, dbm_module)
+        SCons.SConsign.File(name, dbm_module)
 
     def SideEffect(self, side_effect, target):
         """Tell scons that side_effects are built as side 
index 8bab2468ec812bd51f21417c6021b6320ffc7720..8ac43cb9a6fc65147cd6862c474ea27c16a6551b 100644 (file)
@@ -2214,7 +2214,7 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_SConsignFile(self):
         """Test the SConsignFile() method"""
-        import SCons.Sig
+        import SCons.SConsign
 
         class MyFS:
             SConstruct_dir = os.sep + 'dir'
@@ -2230,8 +2230,8 @@ class EnvironmentTestCase(unittest.TestCase):
                 fnames.append(name)
                 dbms.append(dbm_module)
 
-            save_Sig_SConsignFile = SCons.Sig.SConsignFile
-            SCons.Sig.SConsignFile = capture
+            save_SConsign_File = SCons.SConsign.File
+            SCons.SConsign.File = capture
 
             env.SConsignFile('foo')
             assert fnames[0] == os.path.join(os.sep, 'dir', 'foo'), fnames
@@ -2257,7 +2257,7 @@ class EnvironmentTestCase(unittest.TestCase):
             assert fnames[5] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
             assert dbms[5] == None, dbms
         finally:
-            SCons.Sig.SConsignFile = save_Sig_SConsignFile
+            SCons.SConsign.File = save_SConsign_File
 
     def test_SideEffect(self):
         """Test the SideEffect() method"""
index 008976b3c2a9c6e4c014840bf5233f2883954752..0c6627c8a0f7444819c9641ad455cd67a36a9149 100644 (file)
@@ -1316,8 +1316,8 @@ class Dir(Base):
         """Return the .sconsign file info for this directory,
         creating it first if necessary."""
         if not self._sconsign:
-            import SCons.Sig
-            self._sconsign = SCons.Sig.SConsignForDirectory(self)
+            import SCons.SConsign
+            self._sconsign = SCons.SConsign.ForDirectory(self)
         return self._sconsign
 
     def srcnode(self):
index 9a4162c75037318cdac2d44df95f9a0bb5a40f95..6790cb5e0a3a8b311ac1dfa04ae6bdf1583b06e5 100644 (file)
@@ -49,7 +49,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 import copy
 
 from SCons.Debug import logInstanceCreation
-import SCons.Sig
+import SCons.SConsign
 import SCons.Util
 
 # Node states
@@ -565,7 +565,7 @@ class Node:
     def get_prevsiginfo(self):
         """Fetch the previous signature information from the
         .sconsign entry."""
-        return SCons.Sig._SConsign.null_siginfo
+        return SCons.SConsign.Base.null_siginfo
 
     def get_timestamp(self):
         return 0
index b8502b09707c3071b5795ed05673d258eb5ecd95..8bc11ad5a6e906288fa1a63ab6d6dd064d158417 100644 (file)
@@ -52,8 +52,8 @@ class SConfTestCase(unittest.TestCase):
     def _resetSConfState(self):
         # Ok, this is tricky, and i do not know, if everything is sane.
         # We try to reset scons' state (including all global variables)
-        import SCons.Sig
-        SCons.Sig.write() # simulate normal scons-finish
+        import SCons.SConsign
+        SCons.SConsign.write() # simulate normal scons-finish
         for n in sys.modules.keys():
             if string.split(n, '.')[0] == 'SCons':
                 m = sys.modules[n]
diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py
new file mode 100644 (file)
index 0000000..a91817b
--- /dev/null
@@ -0,0 +1,323 @@
+"""SCons.SConsign
+
+Writing and reading information to the .sconsign file or files.
+
+"""
+
+#
+# __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__"
+
+import cPickle
+import os
+import os.path
+import time
+
+import SCons.Sig
+import SCons.Node
+import SCons.Warnings
+
+#XXX Get rid of the global array so this becomes re-entrant.
+sig_files = []
+
+database = None
+
+def write():
+    global sig_files
+    for sig_file in sig_files:
+        sig_file.write()
+
+
+class Entry:
+
+    """Objects of this type are pickled to the .sconsign file, so it
+    should only contain simple builtin Python datatypes and no methods.
+
+    This class is used to store cache information about nodes between
+    scons runs for efficiency, and to store the build signature for
+    nodes so that scons can determine if they are out of date. """
+
+    # setup the default value for various attributes:
+    # (We make the class variables so the default values won't get pickled
+    # with the instances, which would waste a lot of space)
+    timestamp = None
+    bsig = None
+    csig = None
+    implicit = None
+    bkids = []
+    bkidsigs = []
+    bact = None
+    bactsig = None
+
+class Base:
+    """
+    This is the controlling class for the signatures for the collection of
+    entries associated with a specific directory.  The actual directory
+    association will be maintained by a subclass that is specific to
+    the underlying storage method.  This class provides a common set of
+    methods for fetching and storing the individual bits of information
+    that make up signature entry.
+    """
+    def __init__(self, module=None):
+        """
+        module - the signature module being used
+        """
+
+        self.module = module or SCons.Sig.default_calc.module
+        self.entries = {}
+        self.dirty = 0
+
+    # A null .sconsign entry.  We define this here so that it will
+    # be easy to keep this in sync if/whenever we change the type of
+    # information returned by the get() method, below.
+    null_siginfo = (None, None, None)
+
+    def get(self, filename):
+        """
+        Get the .sconsign entry for a file
+
+        filename - the filename whose signature will be returned
+        returns - (timestamp, bsig, csig)
+        """
+        entry = self.get_entry(filename)
+        return (entry.timestamp, entry.bsig, entry.csig)
+
+    def get_entry(self, filename):
+        """
+        Create an entry for the filename and return it, or if one already exists,
+        then return it.
+        """
+        try:
+            return self.entries[filename]
+        except (KeyError, AttributeError):
+            return Entry()
+
+    def set_entry(self, filename, entry):
+        """
+        Set the entry.
+        """
+        self.entries[filename] = entry
+        self.dirty = 1
+
+    def set_csig(self, filename, csig):
+        """
+        Set the csig .sconsign entry for a file
+
+        filename - the filename whose signature will be set
+        csig - the file's content signature
+        """
+
+        entry = self.get_entry(filename)
+        entry.csig = csig
+        self.set_entry(filename, entry)
+
+    def set_binfo(self, filename, bsig, bkids, bkidsigs, bact, bactsig):
+        """
+        Set the build info .sconsign entry for a file
+
+        filename - the filename whose signature will be set
+        bsig - the file's built signature
+        """
+
+        entry = self.get_entry(filename)
+        entry.bsig = bsig
+        entry.bkids = bkids
+        entry.bkidsigs = bkidsigs
+        entry.bact = bact
+        entry.bactsig = bactsig
+        self.set_entry(filename, entry)
+
+    def set_timestamp(self, filename, timestamp):
+        """
+        Set the timestamp .sconsign entry for a file
+
+        filename - the filename whose signature will be set
+        timestamp - the file's timestamp
+        """
+
+        entry = self.get_entry(filename)
+        entry.timestamp = timestamp
+        self.set_entry(filename, entry)
+
+    def get_implicit(self, filename):
+        """Fetch the cached implicit dependencies for 'filename'"""
+        entry = self.get_entry(filename)
+        return entry.implicit
+
+    def set_implicit(self, filename, implicit):
+        """Cache the implicit dependencies for 'filename'."""
+        entry = self.get_entry(filename)
+        if not SCons.Util.is_List(implicit):
+            implicit = [implicit]
+        implicit = map(str, implicit)
+        entry.implicit = implicit
+        self.set_entry(filename, entry)
+
+    def get_binfo(self, filename):
+        """Fetch the cached implicit dependencies for 'filename'"""
+        entry = self.get_entry(filename)
+        return entry.bsig, entry.bkids, entry.bkidsigs, entry.bact, entry.bactsig
+
+class DB(Base):
+    """
+    A Base subclass that reads and writes signature information
+    from a global .sconsign.dbm file.
+    """
+    def __init__(self, dir, module=None):
+        Base.__init__(self, module)
+
+        self.dir = dir
+
+        try:
+            global database
+            rawentries = database[self.dir.path]
+        except KeyError:
+            pass
+        else:
+            try:
+                self.entries = cPickle.loads(rawentries)
+                if type(self.entries) is not type({}):
+                    self.entries = {}
+                    raise TypeError
+            except KeyboardInterrupt:
+                raise
+            except:
+                SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+                                    "Ignoring corrupt sconsign entry : %s"%self.dir.path)
+
+        global sig_files
+        sig_files.append(self)
+
+    def write(self):
+        if self.dirty:
+            global database
+            database[self.dir.path] = cPickle.dumps(self.entries, 1)
+            try:
+                database.sync()
+            except AttributeError:
+                # Not all anydbm modules have sync() methods.
+                pass
+
+class Dir(Base):
+    def __init__(self, fp=None, module=None):
+        """
+        fp - file pointer to read entries from
+        module - the signature module being used
+        """
+        Base.__init__(self, module)
+
+        if fp:
+            self.entries = cPickle.load(fp)
+            if type(self.entries) is not type({}):
+                self.entries = {}
+                raise TypeError
+
+class DirFile(Dir):
+    """
+    Encapsulates reading and writing a per-directory .sconsign file.
+    """
+    def __init__(self, dir, module=None):
+        """
+        dir - the directory for the file
+        module - the signature module being used
+        """
+
+        self.dir = dir
+        self.sconsign = os.path.join(dir.path, '.sconsign')
+
+        try:
+            fp = open(self.sconsign, 'rb')
+        except IOError:
+            fp = None
+
+        try:
+            Dir.__init__(self, fp, module)
+        except KeyboardInterrupt:
+            raise
+        except:
+            SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+                                "Ignoring corrupt .sconsign file: %s"%self.sconsign)
+
+        global sig_files
+        sig_files.append(self)
+
+    def write(self):
+        """
+        Write the .sconsign file to disk.
+
+        Try to write to a temporary file first, and rename it if we
+        succeed.  If we can't write to the temporary file, it's
+        probably because the directory isn't writable (and if so,
+        how did we build anything in this directory, anyway?), so
+        try to write directly to the .sconsign file as a backup.
+        If we can't rename, try to copy the temporary contents back
+        to the .sconsign file.  Either way, always try to remove
+        the temporary file at the end.
+        """
+        if self.dirty:
+            temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
+            try:
+                file = open(temp, 'wb')
+                fname = temp
+            except IOError:
+                try:
+                    file = open(self.sconsign, 'wb')
+                    fname = self.sconsign
+                except IOError:
+                    return
+            cPickle.dump(self.entries, file, 1)
+            file.close()
+            if fname != self.sconsign:
+                try:
+                    mode = os.stat(self.sconsign)[0]
+                    os.chmod(self.sconsign, 0666)
+                    os.unlink(self.sconsign)
+                except OSError:
+                    pass
+                try:
+                    os.rename(fname, self.sconsign)
+                except OSError:
+                    open(self.sconsign, 'wb').write(open(fname, 'rb').read())
+                    os.chmod(self.sconsign, mode)
+            try:
+                os.unlink(temp)
+            except OSError:
+                pass
+
+ForDirectory = DirFile
+
+def File(name, dbm_module=None):
+    """
+    Arrange for all signatures to be stored in a global .sconsign.dbm
+    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
diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py
new file mode 100644 (file)
index 0000000..5f8e981
--- /dev/null
@@ -0,0 +1,180 @@
+#
+# __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__"
+
+import unittest
+import TestCmd
+import SCons.SConsign
+import sys
+
+class SConsignEntryTestCase(unittest.TestCase):
+
+    def runTest(self):
+        e = SCons.SConsign.Entry()
+        assert e.timestamp == None
+        assert e.csig == None
+        assert e.bsig == None
+        assert e.implicit == None
+
+class BaseTestCase(unittest.TestCase):
+
+    def runTest(self):
+        class DummyModule:
+            def to_string(self, sig):
+                return str(sig)
+
+            def from_string(self, sig):
+                return int(sig)
+            
+        class DummyNode:
+            path = 'not_a_valid_path'
+
+        f = SCons.SConsign.Base()
+        f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig')
+        assert f.get('foo') == (None, 1, None)
+        f.set_csig('foo', 2)
+        assert f.get('foo') == (None, 1, 2)
+        f.set_timestamp('foo', 3)
+        assert f.get('foo') == (3, 1, 2)
+        f.set_implicit('foo', ['bar'])
+        assert f.get('foo') == (3, 1, 2)
+        assert f.get_implicit('foo') == ['bar']
+
+        f = SCons.SConsign.Base(DummyModule())
+        f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig')
+        assert f.get('foo') == (None, 1, None)
+        f.set_csig('foo', 2)
+        assert f.get('foo') == (None, 1, 2)
+        f.set_timestamp('foo', 3)
+        assert f.get('foo') == (3, 1, 2)
+        f.set_implicit('foo', ['bar'])
+        assert f.get('foo') == (3, 1, 2)
+        assert f.get_implicit('foo') == ['bar']
+
+class SConsignDBTestCase(unittest.TestCase):
+
+    def runTest(self):
+        class DummyNode:
+            def __init__(self, path):
+                self.path = path
+        save_database = SCons.SConsign.database
+        SCons.SConsign.database = {}
+        try:
+            d1 = SCons.SConsign.DB(DummyNode('dir1'))
+            d1.set_timestamp('foo', 1)
+            d1.set_binfo('foo', 2, ['f1'], ['f2'], 'foo act', 'foo actsig')
+            d1.set_csig('foo', 3)
+            d1.set_timestamp('bar', 4)
+            d1.set_binfo('bar', 5, ['b1'], ['b2'], 'bar act', 'bar actsig')
+            d1.set_csig('bar', 6)
+            assert d1.get('foo') == (1, 2, 3)
+            assert d1.get('bar') == (4, 5, 6)
+
+            d2 = SCons.SConsign.DB(DummyNode('dir1'))
+            d2.set_timestamp('foo', 7)
+            d2.set_binfo('foo', 8, ['f3'], ['f4'], 'foo act', 'foo actsig')
+            d2.set_csig('foo', 9)
+            d2.set_timestamp('bar', 10)
+            d2.set_binfo('bar', 11, ['b3'], ['b4'], 'bar act', 'bar actsig')
+            d2.set_csig('bar', 12)
+            assert d2.get('foo') == (7, 8, 9)
+            assert d2.get('bar') == (10, 11, 12)
+        finally:
+            SCons.SConsign.database = save_database
+
+class SConsignDirFileTestCase(unittest.TestCase):
+
+    def runTest(self):
+        class DummyModule:
+            def to_string(self, sig):
+                return str(sig)
+
+            def from_string(self, sig):
+                return int(sig)
+            
+        class DummyNode:
+            path = 'not_a_valid_path'
+
+        f = SCons.SConsign.DirFile(DummyNode(), DummyModule())
+        f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig')
+        assert f.get('foo') == (None, 1, None)
+        f.set_csig('foo', 2)
+        assert f.get('foo') == (None, 1, 2)
+        f.set_timestamp('foo', 3)
+        assert f.get('foo') == (3, 1, 2)
+        f.set_implicit('foo', ['bar'])
+        assert f.get('foo') == (3, 1, 2)
+        assert f.get_implicit('foo') == ['bar']
+
+class SConsignFileTestCase(unittest.TestCase):
+
+    def runTest(self):
+        test = TestCmd.TestCmd(workdir = '')
+        file = test.workpath('sconsign_file')
+
+        assert SCons.SConsign.database is None, SCons.SConsign.database
+
+        SCons.SConsign.File(file)
+
+        assert not SCons.SConsign.database is SCons.dblite, SCons.SConsign.database
+
+        class Fake_DBM:
+            def open(self, name, mode):
+                self.name = name
+                self.mode = mode
+                return self
+
+        fake_dbm = Fake_DBM()
+
+        SCons.SConsign.File(file, fake_dbm)
+
+        assert not SCons.SConsign.database is None, SCons.SConsign.database
+        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)
+
+        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
+
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(SConsignEntryTestCase())
+    suite.addTest(BaseTestCase())
+    suite.addTest(SConsignDBTestCase())
+    suite.addTest(SConsignDirFileTestCase())
+    suite.addTest(SConsignFileTestCase())
+    return suite
+
+if __name__ == "__main__":
+    runner = unittest.TextTestRunner()
+    result = runner.run(suite())
+    if not result.wasSuccessful():
+        sys.exit(1)
+
index ee1ec81a954a7f284cd93424f88b54aa80b0f7ad..3a7d4c1cbe7263be6372cebc7e527fdd07449289 100644 (file)
@@ -1114,7 +1114,7 @@ def _main(args, parser):
         else:
             progress_display("scons: " + closing_message)
         if not options.noexec:
-            SCons.Sig.write()
+            SCons.SConsign.write()
 
     if not memory_stats is None:
         memory_stats.append(SCons.Debug.memory())
index 76cd931fffa5dd33f1bb74a235a906291df6bc83..009addd1a0c4e76b5c6bbc28da2b18b751238761 100644 (file)
@@ -393,139 +393,6 @@ class CalcTestCase(unittest.TestCase):
         nn.always_build = 1
         assert not self.calc.current(nn, 33)
 
-class SConsignEntryTestCase(unittest.TestCase):
-
-    def runTest(self):
-        e = SCons.Sig.SConsignEntry()
-        assert e.timestamp == None
-        assert e.csig == None
-        assert e.bsig == None
-        assert e.implicit == None
-
-class _SConsignTestCase(unittest.TestCase):
-
-    def runTest(self):
-        class DummyModule:
-            def to_string(self, sig):
-                return str(sig)
-
-            def from_string(self, sig):
-                return int(sig)
-            
-        class DummyNode:
-            path = 'not_a_valid_path'
-
-        f = SCons.Sig._SConsign()
-        f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig')
-        assert f.get('foo') == (None, 1, None)
-        f.set_csig('foo', 2)
-        assert f.get('foo') == (None, 1, 2)
-        f.set_timestamp('foo', 3)
-        assert f.get('foo') == (3, 1, 2)
-        f.set_implicit('foo', ['bar'])
-        assert f.get('foo') == (3, 1, 2)
-        assert f.get_implicit('foo') == ['bar']
-
-        f = SCons.Sig._SConsign(DummyModule())
-        f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig')
-        assert f.get('foo') == (None, 1, None)
-        f.set_csig('foo', 2)
-        assert f.get('foo') == (None, 1, 2)
-        f.set_timestamp('foo', 3)
-        assert f.get('foo') == (3, 1, 2)
-        f.set_implicit('foo', ['bar'])
-        assert f.get('foo') == (3, 1, 2)
-        assert f.get_implicit('foo') == ['bar']
-
-class SConsignDBTestCase(unittest.TestCase):
-
-    def runTest(self):
-        class DummyNode:
-            def __init__(self, path):
-                self.path = path
-        save_SConsign_db = SCons.Sig.SConsign_db
-        SCons.Sig.SConsign_db = {}
-        try:
-            d1 = SCons.Sig.SConsignDB(DummyNode('dir1'))
-            d1.set_timestamp('foo', 1)
-            d1.set_binfo('foo', 2, ['f1'], ['f2'], 'foo act', 'foo actsig')
-            d1.set_csig('foo', 3)
-            d1.set_timestamp('bar', 4)
-            d1.set_binfo('bar', 5, ['b1'], ['b2'], 'bar act', 'bar actsig')
-            d1.set_csig('bar', 6)
-            assert d1.get('foo') == (1, 2, 3)
-            assert d1.get('bar') == (4, 5, 6)
-
-            d2 = SCons.Sig.SConsignDB(DummyNode('dir1'))
-            d2.set_timestamp('foo', 7)
-            d2.set_binfo('foo', 8, ['f3'], ['f4'], 'foo act', 'foo actsig')
-            d2.set_csig('foo', 9)
-            d2.set_timestamp('bar', 10)
-            d2.set_binfo('bar', 11, ['b3'], ['b4'], 'bar act', 'bar actsig')
-            d2.set_csig('bar', 12)
-            assert d2.get('foo') == (7, 8, 9)
-            assert d2.get('bar') == (10, 11, 12)
-        finally:
-            SCons.Sig.SConsign_db = save_SConsign_db
-
-class SConsignDirFileTestCase(unittest.TestCase):
-
-    def runTest(self):
-        class DummyModule:
-            def to_string(self, sig):
-                return str(sig)
-
-            def from_string(self, sig):
-                return int(sig)
-            
-        class DummyNode:
-            path = 'not_a_valid_path'
-
-        f = SCons.Sig.SConsignDirFile(DummyNode(), DummyModule())
-        f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig')
-        assert f.get('foo') == (None, 1, None)
-        f.set_csig('foo', 2)
-        assert f.get('foo') == (None, 1, 2)
-        f.set_timestamp('foo', 3)
-        assert f.get('foo') == (3, 1, 2)
-        f.set_implicit('foo', ['bar'])
-        assert f.get('foo') == (3, 1, 2)
-        assert f.get_implicit('foo') == ['bar']
-
-class SConsignFileTestCase(unittest.TestCase):
-
-    def runTest(self):
-        test = TestCmd.TestCmd(workdir = '')
-        file = test.workpath('sconsign_file')
-
-        assert SCons.Sig.SConsign_db is None, SCons.Sig.SConsign_db
-
-        SCons.Sig.SConsignFile(file)
-
-        assert not SCons.Sig.SConsign_db is SCons.dblite, SCons.Sig.SConsign_db
-
-        class Fake_DBM:
-            def open(self, name, mode):
-                self.name = name
-                self.mode = mode
-                return self
-
-        fake_dbm = Fake_DBM()
-
-        SCons.Sig.SConsignFile(file, fake_dbm)
-
-        assert not SCons.Sig.SConsign_db is None, SCons.Sig.SConsign_db
-        assert not hasattr(fake_dbm, 'name'), fake_dbm
-        assert not hasattr(fake_dbm, 'mode'), fake_dbm
-
-        SCons.Sig.SConsign_db = None
-
-        SCons.Sig.SConsignFile(file, fake_dbm)
-
-        assert not SCons.Sig.SConsign_db is None, SCons.Sig.SConsign_db
-        assert fake_dbm.name == file, fake_dbm.name
-        assert fake_dbm.mode == "c", fake_dbm.mode
-
 
 
 def suite():
@@ -533,11 +400,6 @@ def suite():
     suite.addTest(MD5TestCase())
     suite.addTest(TimeStampTestCase())
     suite.addTest(CalcTestCase())
-    suite.addTest(SConsignEntryTestCase())
-    suite.addTest(_SConsignTestCase())
-    suite.addTest(SConsignDBTestCase())
-    suite.addTest(SConsignDirFileTestCase())
-    suite.addTest(SConsignFileTestCase())
     return suite
 
 if __name__ == "__main__":
index dac3f6c11d9d9c07a8c573f12a9c747a366caaef..271f252826cb30314ab9f69861a8933d08de326d 100644 (file)
@@ -46,293 +46,6 @@ except ImportError:
 
 default_max_drift = 2*24*60*60
 
-#XXX Get rid of the global array so this becomes re-entrant.
-sig_files = []
-
-SConsign_db = None
-
-def write():
-    global sig_files
-    for sig_file in sig_files:
-        sig_file.write()
-
-
-class SConsignEntry:
-
-    """Objects of this type are pickled to the .sconsign file, so it
-    should only contain simple builtin Python datatypes and no methods.
-
-    This class is used to store cache information about nodes between
-    scons runs for efficiency, and to store the build signature for
-    nodes so that scons can determine if they are out of date. """
-
-    # setup the default value for various attributes:
-    # (We make the class variables so the default values won't get pickled
-    # with the instances, which would waste a lot of space)
-    timestamp = None
-    bsig = None
-    csig = None
-    implicit = None
-    bkids = []
-    bkidsigs = []
-    bact = None
-    bactsig = None
-
-class _SConsign:
-    """
-    This is the controlling class for the signatures for the collection of
-    entries associated with a specific directory.  The actual directory
-    association will be maintained by a subclass that is specific to
-    the underlying storage method.  This class provides a common set of
-    methods for fetching and storing the individual bits of information
-    that make up signature entry.
-    """
-    def __init__(self, module=None):
-        """
-        module - the signature module being used
-        """
-
-        if module is None:
-            self.module = default_calc.module
-        else:
-            self.module = module
-        self.entries = {}
-        self.dirty = 0
-
-    # A null .sconsign entry.  We define this here so that it will
-    # be easy to keep this in sync if/whenever we change the type of
-    # information returned by the get() method, below.
-    null_siginfo = (None, None, None)
-
-    def get(self, filename):
-        """
-        Get the .sconsign entry for a file
-
-        filename - the filename whose signature will be returned
-        returns - (timestamp, bsig, csig)
-        """
-        entry = self.get_entry(filename)
-        return (entry.timestamp, entry.bsig, entry.csig)
-
-    def get_entry(self, filename):
-        """
-        Create an entry for the filename and return it, or if one already exists,
-        then return it.
-        """
-        try:
-            return self.entries[filename]
-        except (KeyError, AttributeError):
-            return SConsignEntry()
-
-    def set_entry(self, filename, entry):
-        """
-        Set the entry.
-        """
-        self.entries[filename] = entry
-        self.dirty = 1
-
-    def set_csig(self, filename, csig):
-        """
-        Set the csig .sconsign entry for a file
-
-        filename - the filename whose signature will be set
-        csig - the file's content signature
-        """
-
-        entry = self.get_entry(filename)
-        entry.csig = csig
-        self.set_entry(filename, entry)
-
-    def set_binfo(self, filename, bsig, bkids, bkidsigs, bact, bactsig):
-        """
-        Set the build info .sconsign entry for a file
-
-        filename - the filename whose signature will be set
-        bsig - the file's built signature
-        """
-
-        entry = self.get_entry(filename)
-        entry.bsig = bsig
-        entry.bkids = bkids
-        entry.bkidsigs = bkidsigs
-        entry.bact = bact
-        entry.bactsig = bactsig
-        self.set_entry(filename, entry)
-
-    def set_timestamp(self, filename, timestamp):
-        """
-        Set the timestamp .sconsign entry for a file
-
-        filename - the filename whose signature will be set
-        timestamp - the file's timestamp
-        """
-
-        entry = self.get_entry(filename)
-        entry.timestamp = timestamp
-        self.set_entry(filename, entry)
-
-    def get_implicit(self, filename):
-        """Fetch the cached implicit dependencies for 'filename'"""
-        entry = self.get_entry(filename)
-        return entry.implicit
-
-    def set_implicit(self, filename, implicit):
-        """Cache the implicit dependencies for 'filename'."""
-        entry = self.get_entry(filename)
-        if not SCons.Util.is_List(implicit):
-            implicit = [implicit]
-        implicit = map(str, implicit)
-        entry.implicit = implicit
-        self.set_entry(filename, entry)
-
-    def get_binfo(self, filename):
-        """Fetch the cached implicit dependencies for 'filename'"""
-        entry = self.get_entry(filename)
-        return entry.bsig, entry.bkids, entry.bkidsigs, entry.bact, entry.bactsig
-
-class SConsignDB(_SConsign):
-    """
-    A _SConsign subclass that reads and writes signature information
-    from a global .sconsign.dbm file.
-    """
-    def __init__(self, dir, module=None):
-        _SConsign.__init__(self, module)
-
-        self.dir = dir
-
-        try:
-            global SConsign_db
-            rawentries = SConsign_db[self.dir.path]
-        except KeyError:
-            pass
-        else:
-            try:
-                self.entries = cPickle.loads(rawentries)
-                if type(self.entries) is not type({}):
-                    self.entries = {}
-                    raise TypeError
-            except KeyboardInterrupt:
-                raise
-            except:
-                SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
-                                    "Ignoring corrupt sconsign entry : %s"%self.dir.path)
-
-        global sig_files
-        sig_files.append(self)
-
-    def write(self):
-        if self.dirty:
-            global SConsign_db
-            SConsign_db[self.dir.path] = cPickle.dumps(self.entries, 1)
-            try:
-                SConsign_db.sync()
-            except AttributeError:
-                # Not all anydbm modules have sync() methods.
-                pass
-
-class SConsignDir(_SConsign):
-    def __init__(self, fp=None, module=None):
-        """
-        fp - file pointer to read entries from
-        module - the signature module being used
-        """
-        _SConsign.__init__(self, module)
-
-        if fp:
-            self.entries = cPickle.load(fp)
-            if type(self.entries) is not type({}):
-                self.entries = {}
-                raise TypeError
-
-class SConsignDirFile(SConsignDir):
-    """
-    Encapsulates reading and writing a per-directory .sconsign file.
-    """
-    def __init__(self, dir, module=None):
-        """
-        dir - the directory for the file
-        module - the signature module being used
-        """
-
-        self.dir = dir
-        self.sconsign = os.path.join(dir.path, '.sconsign')
-
-        try:
-            fp = open(self.sconsign, 'rb')
-        except IOError:
-            fp = None
-
-        try:
-            SConsignDir.__init__(self, fp, module)
-        except KeyboardInterrupt:
-            raise
-        except:
-            SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
-                                "Ignoring corrupt .sconsign file: %s"%self.sconsign)
-
-        global sig_files
-        sig_files.append(self)
-
-    def write(self):
-        """
-        Write the .sconsign file to disk.
-
-        Try to write to a temporary file first, and rename it if we
-        succeed.  If we can't write to the temporary file, it's
-        probably because the directory isn't writable (and if so,
-        how did we build anything in this directory, anyway?), so
-        try to write directly to the .sconsign file as a backup.
-        If we can't rename, try to copy the temporary contents back
-        to the .sconsign file.  Either way, always try to remove
-        the temporary file at the end.
-        """
-        if self.dirty:
-            temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
-            try:
-                file = open(temp, 'wb')
-                fname = temp
-            except IOError:
-                try:
-                    file = open(self.sconsign, 'wb')
-                    fname = self.sconsign
-                except IOError:
-                    return
-            cPickle.dump(self.entries, file, 1)
-            file.close()
-            if fname != self.sconsign:
-                try:
-                    mode = os.stat(self.sconsign)[0]
-                    os.chmod(self.sconsign, 0666)
-                    os.unlink(self.sconsign)
-                except OSError:
-                    pass
-                try:
-                    os.rename(fname, self.sconsign)
-                except OSError:
-                    open(self.sconsign, 'wb').write(open(fname, 'rb').read())
-                    os.chmod(self.sconsign, mode)
-            try:
-                os.unlink(temp)
-            except OSError:
-                pass
-
-SConsignForDirectory = SConsignDirFile
-
-def SConsignFile(name, dbm_module=None):
-    """
-    Arrange for all signatures to be stored in a global .sconsign.dbm
-    file.
-    """
-    global SConsign_db
-    if SConsign_db is None:
-        if dbm_module is None:
-            import SCons.dblite
-            dbm_module = SCons.dblite
-        SConsign_db = dbm_module.open(name, "c")
-
-    global SConsignForDirectory
-    SConsignForDirectory = SConsignDB
-
 class Calculator:
     """
     Encapsulates signature calculations and .sconsign file generating
@@ -420,7 +133,8 @@ class Calculator:
         if self.max_drift >= 0:
             oldtime, oldbsig, oldcsig = node.get_prevsiginfo()
         else:
-            oldtime, oldbsig, oldcsig = _SConsign.null_siginfo
+            import SCons.SConsign
+            oldtime, oldbsig, oldcsig = SCons.SConsign.Base.null_siginfo
 
         mtime = node.get_timestamp()
 
index e3905ce08e359514ff85573652fc8f401f43ed86..22049271151bd1ba4eae86234d90df1d14b797fd 100644 (file)
@@ -146,7 +146,7 @@ import imp
 import string
 import whichdb
 
-import SCons.Sig
+import SCons.SConsign
 
 def my_whichdb(filename):
     try:
@@ -286,7 +286,7 @@ def Do_SConsignDir(name):
         sys.stderr.write("sconsign: %s\n" % (e))
         return
     try:
-        sconsign = SCons.Sig.SConsignDir(fp)
+        sconsign = SCons.SConsign.Dir(fp)
     except:
         sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % name)
         return