Support using a single .sconsign file. (Stephen Kennedy)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 2 Sep 2003 03:11:08 +0000 (03:11 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 2 Sep 2003 03:11:08 +0000 (03:11 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@787 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
doc/man/sconsign.1
src/CHANGES.txt
src/engine/SCons/Node/FS.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Sig/SigTests.py
src/engine/SCons/Sig/__init__.py
src/script/sconsign.py
test/SConsignFile.py [new file with mode: 0644]
test/sconsign-script.py

index 22cedab34cff1d575c29a6e3db69f5ef12383d16..490975d585a1237c9256a805dae75c5aade611a9 100644 (file)
@@ -31,7 +31,7 @@
 .RE
 .fi
 ..
-.TH SCONS 1 "June 2003"
+.TH SCONS 1 "September 2003"
 .SH NAME
 scons \- a software construction tool
 .SH SYNOPSIS
@@ -4729,6 +4729,39 @@ Return("foo")
 Return(["foo", "bar"])
 .EE
 
+.TP
+.RI SConsignFile([ file ])
+This tells
+.B scons
+to store all file signatures
+in the specified
+.IR file .
+If the
+.I file
+is omitted,
+.B .sconsign.dbm
+is used by default.
+If
+.I file
+is not an absolute path name,
+the file is placed in the same directory as the top-level
+.B SConstruct
+file.
+Examples:
+
+.ES
+# Stores signatures in ".sconsign.dbm"
+# in the top-level SConstruct directory.
+SConsignFile()
+
+# Stores signatures in the file "etc/scons-signatures"
+# relative to the top-level SConstruct directory.
+SConsignFile("etc/scons-signatures")
+
+# Stores signatures in the specified absolute file name.
+SConsignFile("/home/me/SCons/signatures")
+.EE
+
 .TP
 .RI SetOption( name ", " value )
 This function provides a way to set a select subset of the scons command
index d5f71407d174a0d39218dfb8d40b03be1a9b077d..079e7ff1e92b3e235ef8cbf2aaef3344b0735170 100644 (file)
@@ -31,7 +31,7 @@
 .RE
 .fi
 ..
-.TH SCONSIGN 1 "July 2003"
+.TH SCONSIGN 1 "September 2003"
 .SH NAME
 sconsign \- print SCons .sconsign file information
 .SH SYNOPSIS
@@ -66,16 +66,39 @@ is printed.
 If the entry has no implicit dependencies,
 the lines are simply omitted.
 
+By default,
+.B sconsign
+assumes that any
+.I file
+arguments that end with a
+.B .dbm
+suffix contains
+signature entries for
+more than one directory
+(that is,
+was specified by the
+.B SConsignFile ()
+function).
+Any
+.I file
+argument that does not end in
+.B .dbm
+is assumed to be a traditional
+.B .sconsign
+file containing the signature entries
+for a single directory.
+An explicit format
+may be specified using the
+.B -f
+or
+.B --file=
+options.
+
 .SH OPTIONS
 
 Various options control what information is printed
 and the format:
 
-.TP
--b
-Ignored for compatibility with non-GNU versions of
-.BR make.
-
 .TP
 -b, --bsig
 Prints the build signature (bsig) information
@@ -87,13 +110,48 @@ Prints the content signature (csig) information
 for all entries or the specified entries.
 
 .TP
--e entry, --entry=entry
-Prints information about only the specified entry.
+-d DIRECTORY, --dir=DIRECTORY
+When the signatures are being
+read from a
+.B .dbm
+file, or the
+.B -f dbm
+or
+.B --format=dbm
+options are used,
+prints information about
+only the signatures
+for entries in the specified
+.IR DIRECTORY .
+
+.TP
+-e ENTRY, --entry=ENTRY
+Prints information about only the specified
+.IR ENTRY .
 Multiple -e options may be used,
 in which case information about each
-entry is printed in the order in which the
+.I ENTRY
+is printed in the order in which the
 options are specified on the command line.
 
+.TP
+-f FORMAT, --format=FORMAT
+The file(s) to be printed
+are in the specified
+.IR FORMAT .
+Legal values are
+.B dbm
+(the DBM format used
+when the
+.BR SConsignFile ()
+function is used)
+or
+.B sconsign
+(the default format
+used for an individual
+.B .sconsign
+file in each directory).
+
 .TP
 -h, --help
 Prints a help message and exits.
index 7dcdaf947998f11f9997b23de969a5629e2daada..1eeb7a973b3ce482dc8389602810288e75d9e273 100644 (file)
@@ -20,6 +20,12 @@ RELEASE X.XX - XXX
   - Add support for CCVERSION and CXXVERSION variables for a number
     of C and C++ compilers.
 
+  From Stephen Kennedy:
+
+  - Add support for a configurable global .sconsign.dbm file which
+    can be used to avoid cluttering each directory with an individual
+    .sconsign file.
+
   From Steven Knight:
 
   - The -Q option suppressed too many messages; fix it so that it only
index 249c8c6ae3ed33729e6fd132e931c21c1667b29d..a78a2c905e7be82f671566c99849f9c9f97c8e3c 100644 (file)
@@ -1100,7 +1100,7 @@ class Dir(Base):
         creating it first if necessary."""
         if not self._sconsign:
             import SCons.Sig
-            self._sconsign = SCons.Sig.SConsignFile(self)
+            self._sconsign = SCons.Sig.SConsignForDirectory(self)
         return self._sconsign
 
     def srcnode(self):
index 0f9daa7799ce430536f192d29051fbee8807b6a9..1c17ee203ae3f93a286807533f69558bcb740973 100644 (file)
@@ -41,7 +41,6 @@ import SCons.Node.Python
 import SCons.Platform
 import SCons.SConf
 import SCons.Script
-import SCons.Tool
 import SCons.Util
 import SCons.Options
 import SCons
@@ -539,6 +538,13 @@ def SetOption(name, value):
 def GetOption(name):
     return SCons.Script.ssoptions.get(name)
 
+def SConsignFile(name=".sconsign.dbm"):
+    import SCons.Sig
+    if not os.path.isabs(name):
+        sd = str(SCons.Node.FS.default_fs.SConstruct_dir)
+        name = os.path.join(sd, name)
+    SCons.Sig.SConsignFile(name)
+
 def BuildDefaultGlobals():
     """
     Create a dictionary containing all the default globals for 
@@ -583,6 +589,7 @@ def BuildDefaultGlobals():
     globals['Return']            = Return
     globals['SConscript']        = SConscript
     globals['SConscriptChdir']   = SConscriptChdir
+    globals['SConsignFile']      = SConsignFile
     globals['Scanner']           = SCons.Scanner.Base
     globals['SetBuildSignatureType'] = SetBuildSignatureType
     globals['SetCommandHandler'] = SCons.Action.SetCommandHandler
index af2ebab19c0c26104e8d46e524acd98367a77359..c82d2c5fe59dad087c8871c8701fb6c4eee06e44 100644 (file)
@@ -424,7 +424,7 @@ class _SConsignTestCase(unittest.TestCase):
         assert f.get('foo') == (3, 1, 2)
         assert f.get_implicit('foo') == ['bar']
 
-        f = SCons.Sig._SConsign(None, DummyModule())
+        f = SCons.Sig._SConsign(DummyModule())
         f.set_bsig('foo', 1)
         assert f.get('foo') == (None, 1, None)
         f.set_csig('foo', 2)
@@ -435,7 +435,38 @@ class _SConsignTestCase(unittest.TestCase):
         assert f.get('foo') == (3, 1, 2)
         assert f.get_implicit('foo') == ['bar']
 
-class SConsignFileTestCase(unittest.TestCase):
+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_bsig('foo', 2)
+            d1.set_csig('foo', 3)
+            d1.set_timestamp('bar', 4)
+            d1.set_bsig('bar', 5)
+            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_bsig('foo', 8)
+            d2.set_csig('foo', 9)
+            d2.set_timestamp('bar', 10)
+            d2.set_bsig('bar', 11)
+            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:
@@ -448,7 +479,7 @@ class SConsignFileTestCase(unittest.TestCase):
         class DummyNode:
             path = 'not_a_valid_path'
 
-        f = SCons.Sig.SConsignFile(DummyNode(), DummyModule())
+        f = SCons.Sig.SConsignDirFile(DummyNode(), DummyModule())
         f.set_bsig('foo', 1)
         assert f.get('foo') == (None, 1, None)
         f.set_csig('foo', 2)
@@ -467,7 +498,8 @@ def suite():
     suite.addTest(CalcTestCase())
     suite.addTest(SConsignEntryTestCase())
     suite.addTest(_SConsignTestCase())
-    suite.addTest(SConsignFileTestCase())
+    suite.addTest(SConsignDBTestCase())
+    suite.addTest(SConsignDirFileTestCase())
     return suite
 
 if __name__ == "__main__":
index b4b70b4cfbb1cae9cb8c2fa13ab6f3bd3d6d6454..298db66cac88e11fa91f8d6471f2aa026f62f478 100644 (file)
@@ -49,6 +49,8 @@ default_max_drift = 2*24*60*60
 #XXX Get rid of the global array so this becomes re-entrant.
 sig_files = []
 
+SConsign_db = None
+
 # 1 means use build signature for derived source files
 # 0 means use content signature for derived source files
 build_signature = 1
@@ -77,10 +79,16 @@ class SConsignEntry:
     implicit = None
 
 class _SConsign:
-
-    def __init__(self, fp=None, module=None):
+    """
+    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):
         """
-        fp - file pointer to read entries from
         module - the signature module being used
         """
 
@@ -91,18 +99,6 @@ class _SConsign:
         self.entries = {}
         self.dirty = 0
 
-        if fp:
-            try:
-                self.entries = cPickle.load(fp)
-                if type(self.entries) is not type({}):
-                    self.entries = {}
-                    raise TypeError
-            except:
-                SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
-                                    "Ignoring corrupt .sconsign file: %s"%self.sconsign)
-        global sig_files
-        sig_files.append(self)
-
     # 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.
@@ -185,11 +181,58 @@ class _SConsign:
         entry.implicit = implicit
         self.set_entry(filename, entry)
 
-class SConsignFile(_SConsign):
+class SConsignDB(_SConsign):
     """
-    Encapsulates reading and writing a .sconsign file.
+    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:
+                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)
+            SConsign_db.sync()
 
+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
@@ -204,7 +247,14 @@ class SConsignFile(_SConsign):
         except:
             fp = None
 
-        _SConsign.__init__(self, fp, module)
+        try:
+            SConsignDir.__init__(self, fp, module)
+        except:
+            SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+                                "Ignoring corrupt .sconsign file: %s"%self.sconsign)
+
+        global sig_files
+        sig_files.append(self)
 
     def write(self):
         """
@@ -249,6 +299,21 @@ class SConsignFile(_SConsign):
             except:
                 pass
 
+SConsignForDirectory = SConsignDirFile
+
+def SConsignFile(name):
+    """
+    Arrange for all signatures to be stored in a global .sconsign.dbm
+    file.
+    """
+    global SConsign_db
+    if SConsign_db is None:
+        import anydbm
+        SConsign_db = anydbm.open(name, "c")
+
+    global SConsignForDirectory
+    SConsignForDirectory = SConsignDB
+
 class Calculator:
     """
     Encapsulates signature calculations and .sconsign file generating
index d70f02b7953a2c5faa8ed5585ffbbb1d24237a5c..015f1dbe4b2081bb457b2d440d01b1448e2e5bcd 100644 (file)
@@ -134,6 +134,108 @@ sys.path = libs + sys.path
 # END STANDARD SCons SCRIPT HEADER
 ##############################################################################
 
+PF_bsig      = 0x1
+PF_csig      = 0x2
+PF_timestamp = 0x4
+PF_implicit  = 0x8
+PF_all       = PF_bsig | PF_csig | PF_timestamp | PF_implicit
+
+Do_Func = None
+Print_Directories = []
+Print_Entries = []
+Print_Flags = 0
+Verbose = 0
+Readable = 0
+
+def field(name, pf, val):
+    if Print_Flags & pf:
+        if Verbose:
+            sep = "\n    " + name + ": "
+        else:
+            sep = " "
+        return sep + str(val)
+    else:
+        return ""
+
+def printfield(name, entry):
+    if Readable and entry.timestamp:
+        ts = "'" + time.ctime(entry.timestamp) + "'"
+    else:
+        ts = entry.timestamp
+    timestamp = field("timestamp", PF_timestamp, ts)
+    bsig = field("bsig", PF_bsig, entry.bsig)
+    csig = field("csig", PF_csig, entry.csig)
+    print name + ":" + timestamp + bsig + csig
+    if Print_Flags & PF_implicit and entry.implicit:
+        if Verbose:
+            print "    implicit:"
+        for i in entry.implicit:
+            print "        %s" % i
+
+def printentries(entries):
+    if Print_Entries:
+        for name in Print_Entries:
+            try:
+                entry = entries[name]
+            except KeyError:
+                sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, args[0]))
+            else:
+                printfield(name, entry)
+    else:
+        for name, e in entries.items():
+            printfield(name, e)
+
+import SCons.Sig
+
+def Do_SConsignDB(name):
+    import anydbm
+    import cPickle
+    try:
+        open(name, 'rb')
+    except (IOError, OSError), e:
+        sys.stderr.write("sconsign: %s\n" % (e))
+        return
+    try:
+        db = anydbm.open(name, "r")
+    except anydbm.error, e:
+        sys.stderr.write("sconsign: ignoring invalid .sconsign.dbm file `%s': %s\n" % (name, e))
+        return
+    if Print_Directories:
+        for dir in Print_Directories:
+            try:
+                val = db[dir]
+            except KeyError:
+                sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0]))
+            else:
+                entries = cPickle.loads(val)
+                print '=== ' + dir + ':'
+                printentries(entries)
+    else:
+        keys = db.keys()
+        keys.sort()
+        for dir in keys:
+            entries = cPickle.loads(db[dir])
+            print '=== ' + dir + ':'
+            printentries(entries)
+
+def Do_SConsignDir(name):
+    try:
+        fp = open(name, 'rb')
+    except (IOError, OSError), e:
+        sys.stderr.write("sconsign: %s\n" % (e))
+        return
+    try:
+        sconsign = SCons.Sig.SConsignDir(fp)
+    except:
+        sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % name)
+        return
+    printentries(sconsign.entries)
+
+Function_Map = {'dbm'      : Do_SConsignDB,
+                'sconsign' : Do_SConsignDir}
+
+##############################################################################
+
 import getopt
 
 helpstr = """\
@@ -141,7 +243,9 @@ Usage: sconsign [OPTIONS] FILE [...]
 Options:
   -b, --bsig                  Print build signature information.
   -c, --csig                  Print content signature information.
-  -e, --entry ENTRY           Print only info about ENTRY.
+  -d DIR, --dir=DIR           Print only info about DIR.
+  -e ENTRY, --entry=ENTRY     Print only info about ENTRY.
+  -f FORMAT, --format=FORMAT  FILE is in the specified FORMAT.
   -h, --help                  Print this message and exit.
   -i, --implicit              Print implicit dependency information.
   -r, --readable              Print timestamps in human-readable form.
@@ -149,84 +253,48 @@ Options:
   -v, --verbose               Verbose, describe each field.
 """
 
-opts, args = getopt.getopt(sys.argv[1:], "bce:hirtv",
-                            ['bsig', 'csig', 'entry=', 'help', 'implicit',
+opts, args = getopt.getopt(sys.argv[1:], "bcd:e:f:hirtv",
+                            ['bsig', 'csig', 'dir=', 'entry=',
+                             'format=', 'help', 'implicit',
                              'readable', 'timestamp', 'verbose'])
 
-pf_bsig      = 0x1
-pf_csig      = 0x2
-pf_timestamp = 0x4
-pf_implicit  = 0x8
-pf_all       = pf_bsig | pf_csig | pf_timestamp | pf_implicit
-
-entries = []
-printflags = 0
-verbose = 0
-readable = 0
-
 for o, a in opts:
     if o in ('-b', '--bsig'):
-        printflags = printflags | pf_bsig
+        Print_Flags = Print_Flags | PF_bsig
     elif o in ('-c', '--csig'):
-        printflags = printflags | pf_csig
+        Print_Flags = Print_Flags | PF_csig
+    elif o in ('-d', '--dir'):
+        Print_Directories.append(a)
     elif o in ('-e', '--entry'):
-        entries.append(a)
+        Print_Entries.append(a)
+    elif o in ('-f', '--format'):
+        try:
+            Do_Func = Function_Map[a]
+        except KeyError:
+            sys.stderr.write("sconsign: illegal file format `%s'\n" % a)
+            print helpstr
+            sys.exit(2)
     elif o in ('-h', '--help'):
         print helpstr
         sys.exit(0)
     elif o in ('-i', '--implicit'):
-        printflags = printflags | pf_implicit
+        Print_Flags = Print_Flags | PF_implicit
     elif o in ('-r', '--readable'):
-        readable = 1
+        Readable = 1
     elif o in ('-t', '--timestamp'):
-        printflags = printflags | pf_timestamp
+        Print_Flags = Print_Flags | PF_timestamp
     elif o in ('-v', '--verbose'):
-        verbose = 1
+        Verbose = 1
 
-if printflags == 0:
-    printflags = pf_all
-
-def field(name, pf, val):
-    if printflags & pf:
-        if verbose:
-            sep = "\n    " + name + ": "
-        else:
-            sep = " "
-        return sep + str(val)
-    else:
-        return ""
-
-def printfield(name, entry):
-    if readable and entry.timestamp:
-        ts = "'" + time.ctime(entry.timestamp) + "'"
-    else:
-        ts = entry.timestamp
-    timestamp = field("timestamp", pf_timestamp, ts)
-    bsig = field("bsig", pf_bsig, entry.bsig)
-    csig = field("csig", pf_csig, entry.csig)
-    print name + ":" + timestamp + bsig + csig
-    if printflags & pf_implicit and entry.implicit:
-        if verbose:
-            print "    implicit:"
-        for i in entry.implicit:
-            print "        %s" % i
-
-import SCons.Sig
-
-def do_sconsign(fp):
-    sconsign = SCons.Sig._SConsign(fp)
-    if entries:
-        for name in entries:
-            try:
-                entry = sconsign.entries[name]
-            except KeyError:
-                sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, args[0]))
-            else:
-                printfield(name, entry)
-    else:
-        for name, e in sconsign.entries.items():
-            printfield(name, e)
-    
+if Print_Flags == 0:
+    Print_Flags = PF_all
     
 for a in args:
-    do_sconsign(open(a, 'rb'))
+    if Do_Func:
+        Do_Func(a)
+    elif a[-4:] == '.dbm':
+        Do_SConsignDB(a)
+    else:
+        Do_SConsignDir(a)
+
+sys.exit(0)
diff --git a/test/SConsignFile.py b/test/SConsignFile.py
new file mode 100644 (file)
index 0000000..29f2af2
--- /dev/null
@@ -0,0 +1,113 @@
+#!/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__"
+
+import TestSCons
+import os.path
+
+python = TestSCons.python
+
+test = TestSCons.TestSCons()
+
+test.subdir('work1', ['work1', 'subdir'])
+test.subdir('work2', ['work2', 'subdir'])
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'rb').read()
+file = open(sys.argv[1], 'wb')
+file.write(contents)
+file.close()
+""")
+
+#
+test.write(['work1', 'SConstruct'], """
+SConsignFile()
+B = Builder(action = "%s ../build.py $TARGETS $SOURCES")
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'f1.out', source = 'f1.in')
+env.B(target = 'f2.out', source = 'f2.in')
+env.B(target = 'subdir/f3.out', source = 'subdir/f3.in')
+env.B(target = 'subdir/f4.out', source = 'subdir/f4.in')
+""" % python)
+
+test.write(['work1', 'f1.in'], "work1/f1.in\n")
+test.write(['work1', 'f2.in'], "work1/f2.in\n")
+test.write(['work1', 'subdir', 'f3.in'], "work1/subdir/f3.in\n")
+test.write(['work1', 'subdir', 'f4.in'], "work1/subdir/f4.in\n")
+
+test.run(chdir = 'work1')
+
+test.fail_test(not os.path.exists(test.workpath('work1', '.sconsign.dbm')))
+test.fail_test(os.path.exists(test.workpath('work1', '.sconsign')))
+test.fail_test(os.path.exists(test.workpath('work1', 'subdir', '.sconsign')))
+
+test.fail_test(test.read(['work1', 'f1.out']) != "work1/f1.in\n")
+test.fail_test(test.read(['work1', 'f2.out']) != "work1/f2.in\n")
+test.fail_test(test.read(['work1', 'subdir', 'f3.out']) != "work1/subdir/f3.in\n")
+test.fail_test(test.read(['work1', 'subdir', 'f4.out']) != "work1/subdir/f4.in\n")
+
+test.up_to_date(chdir = 'work1', arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath('work1', '.sconsign.dbm')))
+test.fail_test(os.path.exists(test.workpath('work1', '.sconsign')))
+test.fail_test(os.path.exists(test.workpath('work1', 'subdir', '.sconsign')))
+
+#
+test.write(['work2', 'SConstruct'], """
+SConsignFile('my_sconsign')
+B = Builder(action = "%s ../build.py $TARGETS $SOURCES")
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'f5.out', source = 'f5.in')
+env.B(target = 'f6.out', source = 'f6.in')
+env.B(target = 'subdir/f7.out', source = 'subdir/f7.in')
+env.B(target = 'subdir/f8.out', source = 'subdir/f8.in')
+""" % python)
+
+test.write(['work2', 'f5.in'], "work2/f5.in\n")
+test.write(['work2', 'f6.in'], "work2/f6.in\n")
+test.write(['work2', 'subdir', 'f7.in'], "work2/subdir/f7.in\n")
+test.write(['work2', 'subdir', 'f8.in'], "work2/subdir/f8.in\n")
+
+test.run(chdir = 'work2')
+
+test.fail_test(not os.path.exists(test.workpath('work2', 'my_sconsign')))
+test.fail_test(os.path.exists(test.workpath('work2', '.sconsign.dbm')))
+test.fail_test(os.path.exists(test.workpath('work2', '.sconsign')))
+test.fail_test(os.path.exists(test.workpath('work2', 'subdir', '.sconsign')))
+
+test.fail_test(test.read(['work2', 'f5.out']) != "work2/f5.in\n")
+test.fail_test(test.read(['work2', 'f6.out']) != "work2/f6.in\n")
+test.fail_test(test.read(['work2', 'subdir', 'f7.out']) != "work2/subdir/f7.in\n")
+test.fail_test(test.read(['work2', 'subdir', 'f8.out']) != "work2/subdir/f8.in\n")
+
+test.up_to_date(chdir = 'work2', arguments = '.')
+
+test.fail_test(not os.path.exists(test.workpath('work2', 'my_sconsign')))
+test.fail_test(os.path.exists(test.workpath('work2', '.sconsign.dbm')))
+test.fail_test(os.path.exists(test.workpath('work2', '.sconsign')))
+test.fail_test(os.path.exists(test.workpath('work2', 'subdir', '.sconsign')))
+
+test.pass_test()
index eac6ab3247c4310383f23913beedab0ea1641d0c..a5c0aa788ecb6f583851ac8aa27a9de1030ecc2d 100644 (file)
@@ -26,6 +26,7 @@ __revision__ = "/home/scons/scons/branch.0/baseline/test/sconsign.py 0.90.D001 2
 
 import os.path
 import string
+import time
 
 import TestCmd
 import TestSCons
@@ -49,16 +50,20 @@ def sort_match(test, lines, expect):
 
 test = TestSCons.TestSCons(match = TestCmd.match_re)
 
-test.subdir('sub1', 'sub2')
 
-test.write('SConstruct', """
+
+
+test.subdir('work1', ['work1', 'sub1'], ['work1', 'sub2'],
+            'work2', ['work2', 'sub1'], ['work2', 'sub2'])
+
+test.write(['work1', 'SConstruct'], """
 env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
 env1.Program('sub1/hello.c')
 env2 = env1.Copy(CPPPATH = ['sub2'])
 env2.Program('sub2/hello.c')
 """)
 
-test.write(['sub1', 'hello.c'], r"""\
+test.write(['work1', 'sub1', 'hello.c'], r"""\
 int
 main(int argc, char *argv[])
 {
@@ -68,7 +73,7 @@ main(int argc, char *argv[])
 }
 """)
 
-test.write(['sub2', 'hello.c'], r"""\
+test.write(['work1', 'sub2', 'hello.c'], r"""\
 #include <inc1.h>
 #include <inc2.h>
 int
@@ -80,19 +85,19 @@ main(int argc, char *argv[])
 }
 """)
 
-test.write(['sub2', 'inc1.h'], r"""\
+test.write(['work1', 'sub2', 'inc1.h'], r"""\
 #define STRING1 "inc1.h"
 """)
 
-test.write(['sub2', 'inc2.h'], r"""\
+test.write(['work1', 'sub2', 'inc2.h'], r"""\
 #define STRING2 "inc2.h"
 """)
 
-test.run(arguments = '--implicit-cache .')
+test.run(chdir = 'work1', arguments = '--implicit-cache .')
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "sub1/.sconsign",
+         arguments = "work1/sub1/.sconsign",
          stdout = """\
 hello.exe: None \S+ None
 hello.obj: None \S+ None
@@ -100,7 +105,7 @@ hello.obj: None \S+ None
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-v sub1/.sconsign",
+         arguments = "-v work1/sub1/.sconsign",
          stdout = """\
 hello.exe:
     timestamp: None
@@ -114,7 +119,7 @@ hello.obj:
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-b -v sub1/.sconsign",
+         arguments = "-b -v work1/sub1/.sconsign",
          stdout = """\
 hello.exe:
     bsig: \S+
@@ -124,7 +129,7 @@ hello.obj:
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-c -v sub1/.sconsign",
+         arguments = "-c -v work1/sub1/.sconsign",
          stdout = """\
 hello.exe:
     csig: None
@@ -134,14 +139,14 @@ hello.obj:
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-e hello.obj sub1/.sconsign",
+         arguments = "-e hello.obj work1/sub1/.sconsign",
          stdout = """\
 hello.obj: None \S+ None
 """)
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-e hello.obj -e hello.exe -e hello.obj sub1/.sconsign",
+         arguments = "-e hello.obj -e hello.exe -e hello.obj work1/sub1/.sconsign",
          stdout = """\
 hello.obj: None \S+ None
 hello.exe: None \S+ None
@@ -150,7 +155,7 @@ hello.obj: None \S+ None
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "sub2/.sconsign",
+         arguments = "work1/sub2/.sconsign",
          stdout = """\
 hello.exe: None \S+ None
 hello.obj: None \S+ None
@@ -161,7 +166,7 @@ hello.obj: None \S+ None
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-i -v sub2/.sconsign",
+         arguments = "-i -v work1/sub2/.sconsign",
          stdout = """\
 hello.exe:
 hello.obj:
@@ -173,7 +178,7 @@ hello.obj:
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-e hello.obj sub2/.sconsign sub1/.sconsign",
+         arguments = "-e hello.obj work1/sub2/.sconsign work1/sub1/.sconsign",
          stdout = """\
 hello.obj: None \S+ None
         %s
@@ -182,9 +187,9 @@ hello.obj: None \S+ None
 """ % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
        string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
 
-test.run(arguments = '--clean .')
+test.run(chdir = 'work1', arguments = '--clean .')
 
-test.write('SConstruct', """
+test.write(['work1', 'SConstruct'], """
 SourceSignatures('timestamp')
 TargetSignatures('content')
 env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
@@ -193,14 +198,13 @@ env2 = env1.Copy(CPPPATH = ['sub2'])
 env2.Program('sub2/hello.c')
 """)
 
-import time
 time.sleep(1)
 
-test.run(arguments = '. --max-drift=1')
+test.run(chdir = 'work1', arguments = '. --max-drift=1')
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "sub1/.sconsign")
+         arguments = "work1/sub1/.sconsign")
 
 test.fail_test(not sort_match(test, test.stdout(), """\
 hello.exe: None \S+ None
@@ -210,7 +214,7 @@ hello.obj: None \S+ None
 
 test.run(interpreter = TestSCons.python,
          program = sconsign,
-         arguments = "-r sub1/.sconsign")
+         arguments = "-r work1/sub1/.sconsign")
 
 test.fail_test(not sort_match(test, test.stdout(), """\
 hello.exe: None \S+ None
@@ -218,4 +222,235 @@ hello.c: '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' None \d+
 hello.obj: None \S+ None
 """))
 
+
+##############################################################################
+
+test.write(['work2', 'SConstruct'], """
+SConsignFile()
+env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
+env1.Program('sub1/hello.c')
+env2 = env1.Copy(CPPPATH = ['sub2'])
+env2.Program('sub2/hello.c')
+""")
+
+test.write(['work2', 'sub1', 'hello.c'], r"""\
+int
+main(int argc, char *argv[])
+{
+       argv[argc++] = "--";
+       printf("sub1/hello.c\n");
+       exit (0);
+}
+""")
+
+test.write(['work2', 'sub2', 'hello.c'], r"""\
+#include <inc1.h>
+#include <inc2.h>
+int
+main(int argc, char *argv[])
+{
+       argv[argc++] = "--";
+       printf("sub2/goodbye.c\n");
+       exit (0);
+}
+""")
+
+test.write(['work2', 'sub2', 'inc1.h'], r"""\
+#define STRING1 "inc1.h"
+""")
+
+test.write(['work2', 'sub2', 'inc2.h'], r"""\
+#define STRING2 "inc2.h"
+""")
+
+test.run(chdir = 'work2', arguments = '--implicit-cache .')
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.exe: None \S+ None
+hello.obj: None \S+ None
+=== sub2:
+hello.exe: None \S+ None
+hello.obj: None \S+ None
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-v work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.exe:
+    timestamp: None
+    bsig: \S+
+    csig: None
+hello.obj:
+    timestamp: None
+    bsig: \S+
+    csig: None
+=== sub2:
+hello.exe:
+    timestamp: None
+    bsig: \S+
+    csig: None
+hello.obj:
+    timestamp: None
+    bsig: \S+
+    csig: None
+    implicit:
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-b -v work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.exe:
+    bsig: \S+
+hello.obj:
+    bsig: \S+
+=== sub2:
+hello.exe:
+    bsig: \S+
+hello.obj:
+    bsig: \S+
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-c -v work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.exe:
+    csig: None
+hello.obj:
+    csig: None
+=== sub2:
+hello.exe:
+    csig: None
+hello.obj:
+    csig: None
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-e hello.obj work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.obj: None \S+ None
+=== sub2:
+hello.obj: None \S+ None
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-e hello.obj -e hello.exe -e hello.obj work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.obj: None \S+ None
+hello.exe: None \S+ None
+hello.obj: None \S+ None
+=== sub2:
+hello.obj: None \S+ None
+        %s
+        %s
+hello.exe: None \S+ None
+hello.obj: None \S+ None
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-i -v work2/.sconsign.dbm",
+         stdout = """\
+=== sub1:
+hello.exe:
+hello.obj:
+=== sub2:
+hello.exe:
+hello.obj:
+    implicit:
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.run(chdir = 'work2', arguments = '--clean .')
+
+test.write(['work2','SConstruct'], """
+SConsignFile('my_sconsign')
+SourceSignatures('timestamp')
+TargetSignatures('content')
+env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
+env1.Program('sub1/hello.c')
+env2 = env1.Copy(CPPPATH = ['sub2'])
+env2.Program('sub2/hello.c')
+""")
+
+time.sleep(1)
+
+test.run(chdir = 'work2', arguments = '. --max-drift=1')
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-d sub1 -f dbm work2/my_sconsign")
+
+test.fail_test(not sort_match(test, test.stdout(), """\
+=== sub1:
+hello.exe: None \S+ None
+hello.obj: None \S+ None
+hello.c: \d+ None \d+
+"""))
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-r -d sub1 -f dbm work2/my_sconsign")
+
+test.fail_test(not sort_match(test, test.stdout(), """\
+=== sub1:
+hello.exe: None \S+ None
+hello.obj: None \S+ None
+hello.c: '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' None \d+
+"""))
+
+##############################################################################
+
+test.write('bad_sconsign', "bad_sconsign\n")
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-f dbm no_sconsign",
+         stderr = "sconsign: \[Errno 2\] No such file or directory: 'no_sconsign'\n")
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-f dbm bad_sconsign",
+         stderr = "sconsign: ignoring invalid .sconsign.dbm file `bad_sconsign': db type could not be determined\n")
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-f sconsign no_sconsign",
+         stderr = "sconsign: \[Errno 2\] No such file or directory: 'no_sconsign'\n")
+
+test.run(interpreter = TestSCons.python,
+         program = sconsign,
+         arguments = "-f sconsign bad_sconsign",
+         stderr = "sconsign: ignoring invalid .sconsign file `bad_sconsign'\n")
+
+
 test.pass_test()