From: stevenknight Date: Tue, 2 Sep 2003 03:11:08 +0000 (+0000) Subject: Support using a single .sconsign file. (Stephen Kennedy) X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=b69139314fb03fede0117c5c0a8de956ee6903c5;p=scons.git Support using a single .sconsign file. (Stephen Kennedy) git-svn-id: http://scons.tigris.org/svn/scons/trunk@787 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 22cedab3..490975d5 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -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 diff --git a/doc/man/sconsign.1 b/doc/man/sconsign.1 index d5f71407..079e7ff1 100644 --- a/doc/man/sconsign.1 +++ b/doc/man/sconsign.1 @@ -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. diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 7dcdaf94..1eeb7a97 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -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 diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 249c8c6a..a78a2c90 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -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): diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 0f9daa77..1c17ee20 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -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 diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py index af2ebab1..c82d2c5f 100644 --- a/src/engine/SCons/Sig/SigTests.py +++ b/src/engine/SCons/Sig/SigTests.py @@ -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__": diff --git a/src/engine/SCons/Sig/__init__.py b/src/engine/SCons/Sig/__init__.py index b4b70b4c..298db66c 100644 --- a/src/engine/SCons/Sig/__init__.py +++ b/src/engine/SCons/Sig/__init__.py @@ -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 diff --git a/src/script/sconsign.py b/src/script/sconsign.py index d70f02b7..015f1dbe 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -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 index 00000000..29f2af28 --- /dev/null +++ b/test/SConsignFile.py @@ -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() diff --git a/test/sconsign-script.py b/test/sconsign-script.py index eac6ab32..a5c0aa78 100644 --- a/test/sconsign-script.py +++ b/test/sconsign-script.py @@ -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 #include 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 +#include +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()