summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2005-05-07 19:19:41 (GMT)
committerSteven Knight <knight@baldmt.com>2005-05-07 19:19:41 (GMT)
commit730081593efc2ac5c17def1650ad0b39c4d0e72a (patch)
tree3379689db64e402fa4720e60ce7ecd1f307ad762
parentd629cdf3f3b332a1b3c9c9fa550c90b8dd710766 (diff)
downloadSCons-730081593efc2ac5c17def1650ad0b39c4d0e72a.zip
SCons-730081593efc2ac5c17def1650ad0b39c4d0e72a.tar.gz
SCons-730081593efc2ac5c17def1650ad0b39c4d0e72a.tar.bz2
Make SConsignFile() behavior the default.
-rw-r--r--doc/man/scons.133
-rw-r--r--src/CHANGES.txt5
-rw-r--r--src/RELEASE.txt28
-rw-r--r--src/engine/SCons/Environment.py7
-rw-r--r--src/engine/SCons/EnvironmentTests.py4
-rw-r--r--src/engine/SCons/Node/FS.py14
-rw-r--r--src/engine/SCons/SConfTests.py27
-rw-r--r--src/engine/SCons/SConsign.py110
-rw-r--r--src/engine/SCons/SConsignTests.py89
-rw-r--r--src/engine/SCons/dblite.py5
-rw-r--r--test/Configure.py1
-rw-r--r--test/Repository/SConsignFile.py61
-rw-r--r--test/option-n.py2
-rw-r--r--test/sconsign/corrupt.py100
-rw-r--r--test/sconsign/nonwritable.py (renamed from test/sconsign.py)69
-rw-r--r--test/sconsign/old.py (renamed from test/old-sconsign.py)9
-rw-r--r--test/sconsign/script.py (renamed from test/sconsign-script.py)17
-rw-r--r--test/subdivide.py7
18 files changed, 469 insertions, 119 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index cb1334b..33c7a19 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -3983,13 +3983,17 @@ SConscript('bar/SConscript') # will chdir to bar
This tells
.B scons
to store all file signatures
-in the specified
+in the specified database
.IR file .
If the
.I file
-is omitted,
-.B .sconsign.dbm
+name is omitted,
+.B .sconsign
is used by default.
+(The actual file name(s) stored on disk
+may have an appropriated suffix appended
+by the
+.IR dbm_module .)
If
.I file
is not an absolute path name,
@@ -3997,6 +4001,20 @@ the file is placed in the same directory as the top-level
.B SConstruct
file.
+If
+.I file
+is
+.BR None ,
+then
+.B scons
+will store file signatures
+in a separate
+.B .sconsign
+file in each directory,
+not in one global database file.
+(This was the default behavior
+prior to SCons 0.96.91 and 0.97.)
+
The optional
.I dbm_module
argument can be used to specify
@@ -4010,8 +4028,9 @@ and which works on all Python versions from 1.5.2 on.
Examples:
.ES
-# Stores signatures in ".sconsign.dbm"
-# in the top-level SConstruct directory.
+# Explicitly stores signatures in ".sconsign.dblite"
+# in the top-level SConstruct directory (the
+# default behavior).
SConsignFile()
# Stores signatures in the file "etc/scons-signatures"
@@ -4020,6 +4039,10 @@ SConsignFile("etc/scons-signatures")
# Stores signatures in the specified absolute file name.
SConsignFile("/home/me/SCons/signatures")
+
+# Stores signatures in a separate .sconsign file
+# in each directory.
+SConsignFile(None)
.EE
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index cfd7097..5513057 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -266,6 +266,11 @@ RELEASE 0.97 - XXX
that accomodate parser generators that write header files to a
different suffix than the hard-coded .hpp when the -d option is used.
+ - The default behavior is now to store signature information in a
+ single .sconsign.dblite file in the top-level SConstruct directory.
+ The old behavior of a separate .sconsign file in each directory can
+ be specified by calling SConsignFile(None).
+
From Wayne Lee:
- Avoid "maximum recursion limit" errors when removing $(-$) pairs
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
index 9c09e60..47b0031 100644
--- a/src/RELEASE.txt
+++ b/src/RELEASE.txt
@@ -58,7 +58,33 @@ RELEASE 0.97 - XXX
or /usr/local was passed as the source to a Builder or Command()
call, in which case SCons would scan the entire directory tree.
- -- SIGNATURE CHANGES WILL CAUSE LIKELY REBUILDS AFTER UPGRADE
+ -- SIGNATURES ARE NOW STORED IN AN SConsignFile() BY DEFAULT,
+ CAUSING LIKELY REBUILDS; SPECIAL NOTE CONCERNING INTERACTION
+ WITH REPOSITORIES
+
+ The default behavior has been changed to store signature
+ information in a single .sconsign.dblite file in the top-level
+ SConstruct file. This will cause rebuilds on upgrade to 0.97,
+ unless you were already calling the SConsignFile() function in
+ your SConscript files.
+
+ The previous default behavior was to store signature information
+ in a .sconsign file in each directory that contained target
+ files that SCons knew about. The old behavior may be preserved
+ by specifying:
+
+ SConsignFile(None)
+
+ in any SConscript file.
+
+ If you are using the Repository feature, are not already using
+ the SConsignFile() function in your build, you *must* add
+ SConsignFile(None) to your build to keep interoperating with an
+ existing Repository that uses the old behavior of a .sconsign
+ file in each directory. Alternatively, you can rebuild the
+ Repository with the new default behavior.
+
+ -- OTHER SIGNATURE CHANGES WILL CAUSE LIKELY REBUILDS AFTER UPGRADE
This release adds several changes to the signature mechanism that
will cause SCons to rebuild most configurations after upgrading
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index b813cf1..58440d6 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -1367,9 +1367,10 @@ class Base(SubstitutionEnvironment):
return apply(SCons.Scanner.Scanner, nargs, nkw)
def SConsignFile(self, name=".sconsign", dbm_module=None):
- name = self.subst(name)
- if not os.path.isabs(name):
- name = os.path.join(str(self.fs.SConstruct_dir), name)
+ if not name is None:
+ name = self.subst(name)
+ if not os.path.isabs(name):
+ name = os.path.join(str(self.fs.SConstruct_dir), name)
SCons.SConsign.File(name, dbm_module)
def SideEffect(self, side_effect, target):
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index fcd0fee..495fab0 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -2568,6 +2568,10 @@ def generate(env):
env.SConsignFile()
assert fnames[5] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
assert dbms[5] == None, dbms
+
+ env.SConsignFile(None)
+ assert fnames[6] == None, fnames
+ assert dbms[6] == None, dbms
finally:
SCons.SConsign.File = save_SConsign_File
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 75efc15..9d7086c 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -422,6 +422,10 @@ class Base(SCons.Node.Node):
self.path = name
else:
self.path = directory.entry_path(name)
+ if directory.tpath == '.':
+ self.tpath = name
+ else:
+ self.tpath = directory.entry_tpath(name)
self.path_elements = directory.path_elements + [self]
self.dir = directory
@@ -732,6 +736,7 @@ class FS(LocalFS):
self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
self.Top.path = '.'
+ self.Top.tpath = '.'
self._cwd = self.Top
def clear_cache(self):
@@ -1102,6 +1107,7 @@ class Dir(Base):
def addRepository(self, dir):
if dir != self and not dir in self.repositories:
self.repositories.append(dir)
+ dir.tpath = '.'
self.__clearRepositoryCache()
def up(self):
@@ -1284,6 +1290,9 @@ class Dir(Base):
def entry_path(self, name):
return self.path + os.sep + name
+ def entry_tpath(self, name):
+ return self.tpath + os.sep + name
+
def must_be_a_Dir(self):
"""Called to make sure a Node is a Dir. Since we're already
one, this is a no-op for us."""
@@ -1389,6 +1398,7 @@ class RootDir(Dir):
# won't gag won't it calls some of our methods.
self.abspath = ''
self.path = ''
+ self.tpath = ''
self.path_elements = []
self.duplicate = 0
Base.__init__(self, name, self, fs)
@@ -1397,6 +1407,7 @@ class RootDir(Dir):
# initial drive letter (the name) plus the directory separator.
self.abspath = name + os.sep
self.path = name + os.sep
+ self.tpath = name + os.sep
self._morph()
def __str__(self):
@@ -1408,6 +1419,9 @@ class RootDir(Dir):
def entry_path(self, name):
return self.path + name
+ def entry_tpath(self, name):
+ return self.tpath + name
+
def is_under(self, dir):
if self is dir:
return 1
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
index a5e0700..2685c2b 100644
--- a/src/engine/SCons/SConfTests.py
+++ b/src/engine/SCons/SConfTests.py
@@ -44,10 +44,15 @@ class SConfTestCase(unittest.TestCase):
def setUp(self):
# we always want to start with a clean directory
+ self.save_cwd = os.getcwd()
self.test = TestCmd.TestCmd(workdir = '')
+ os.chdir(self.test.workpath(''))
def tearDown(self):
self.test.cleanup()
+ import SCons.SConsign
+ SCons.SConsign.Reset()
+ os.chdir(self.save_cwd)
def _resetSConfState(self):
# Ok, this is tricky, and i do not know, if everything is sane.
@@ -104,7 +109,7 @@ class SConfTestCase(unittest.TestCase):
log_file=self.test.workpath('config.log'))
try:
res = checks( self, sconf, TryFunc )
- assert res[0] and not res[1]
+ assert res[0] and not res[1], res
finally:
sconf.Finish()
@@ -115,7 +120,7 @@ class SConfTestCase(unittest.TestCase):
log_file=self.test.workpath('config.log'))
try:
res = checks( self, sconf, TryFunc )
- assert res[0] and not res[1]
+ assert res[0] and not res[1], res
finally:
sconf.Finish()
# we should have exactly one one error cached
@@ -136,7 +141,7 @@ class SConfTestCase(unittest.TestCase):
try:
res = checks( self, sconf, TryFunc )
log = self.test.read( self.test.workpath('config.log') )
- assert res[0] and res[1]
+ assert res[0] and res[1], res
finally:
sconf.Finish()
@@ -231,8 +236,8 @@ int main() {
log_file=self.test.workpath('config.log'))
try:
res = checks(sconf)
- assert res[0][0] and res[0][1] == "Hello"
- assert not res[1][0] and res[1][1] == ""
+ assert res[0][0] and res[0][1] == "Hello", res
+ assert not res[1][0] and res[1][1] == "", res
finally:
sconf.Finish()
log = self.test.read( self.test.workpath('config.log') )
@@ -244,8 +249,8 @@ int main() {
log_file=self.test.workpath('config.log'))
try:
res = checks(sconf)
- assert res[0][0] and res[0][1] == "Hello"
- assert not res[1][0] and res[1][1] == ""
+ assert res[0][0] and res[0][1] == "Hello", res
+ assert not res[1][0] and res[1][1] == "", res
finally:
sconf.Finish()
# we should have exactly one error cached
@@ -271,9 +276,9 @@ int main() {
log_file=self.test.workpath('config.log'))
try:
(ret, output) = sconf.TryAction(action=actionOK)
- assert ret and output == "RUN OK"
+ assert ret and output == "RUN OK", (ret, output)
(ret, output) = sconf.TryAction(action=actionFAIL)
- assert not ret and output == ""
+ assert not ret and output == "", (ret, output)
finally:
sconf.Finish()
@@ -514,7 +519,7 @@ int main() {
"""
(ret, output) = test.TryRun( prog, ".c" )
test.Result( ret )
- assert ret and output == "Hello"
+ assert ret and output == "Hello", (ret, output)
return ret
@@ -525,7 +530,7 @@ int main() {
log_file=self.test.workpath('config.log'))
try:
ret = sconf.CheckCustom()
- assert ret
+ assert ret, ret
finally:
sconf.Finish()
diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py
index 212ec8d..9b5c420 100644
--- a/src/engine/SCons/SConsign.py
+++ b/src/engine/SCons/SConsign.py
@@ -35,15 +35,68 @@ import os.path
import string
import time
+import SCons.dblite
import SCons.Node
import SCons.Sig
import SCons.Warnings
+from SCons.Debug import Trace
+
+def corrupt_dblite_warning(filename):
+ SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
+ "Ignoring corrupt .sconsign file: %s"%filename)
+
+SCons.dblite.ignore_corrupt_dbfiles = 1
+SCons.dblite.corruption_warning = corrupt_dblite_warning
+
#XXX Get rid of the global array so this becomes re-entrant.
sig_files = []
-# Handle to open database object if using the DB SConsign implementation.
-database = None
+# Info for the database SConsign implementation (now the default):
+# "DataBase" is a dictionary that maps top-level SConstruct directories
+# to open database handles.
+# "DB_Module" is the Python database module to create the handles.
+# "DB_Name" is the base name of the database file (minus any
+# extension the underlying DB module will add).
+DataBase = {}
+DB_Module = SCons.dblite
+DB_Name = ".sconsign"
+DB_sync_list = []
+
+def Get_DataBase(dir):
+ global DataBase, DB_Module, DB_Name
+ top = dir.fs.Top
+ if not os.path.isabs(DB_Name) and top.repositories:
+ mode = "c"
+ for d in [top] + top.repositories:
+ if dir.is_under(d):
+ try:
+ return DataBase[d], mode
+ except KeyError:
+ path = d.entry_abspath(DB_Name)
+ try: db = DataBase[d] = DB_Module.open(path, mode)
+ except (IOError, OSError): pass
+ else:
+ if mode != "r":
+ DB_sync_list.append(db)
+ return db, mode
+ mode = "r"
+ try:
+ return DataBase[top], "c"
+ except KeyError:
+ db = DataBase[top] = DB_Module.open(DB_Name, "c")
+ DB_sync_list.append(db)
+ return db, "c"
+ except TypeError:
+ print "DataBase =", DataBase
+ raise
+
+def Reset():
+ """Reset global state. Used by unit tests that end up using
+ SConsign multiple times to get a clean slate for each test."""
+ global sig_files, DB_sync_list
+ sig_files = []
+ DB_sync_list = []
if os.sep == '/':
norm_entry = lambda s: s
@@ -55,9 +108,9 @@ def write():
global sig_files
for sig_file in sig_files:
sig_file.write(sync=0)
- if database:
+ for db in DB_sync_list:
try:
- syncmethod = database.sync
+ syncmethod = db.sync
except AttributeError:
pass # Not all anydbm modules have sync() methods.
else:
@@ -94,19 +147,25 @@ class Base:
self.entries[filename] = obj
self.dirty = 1
+ def do_not_set_entry(self, filename, obj):
+ pass
+
class DB(Base):
"""
A Base subclass that reads and writes signature information
- from a global .sconsign.dbm file.
+ from a global .sconsign.db* file--the actual file suffix is
+ determined by the specified database module.
"""
def __init__(self, dir, module=None):
Base.__init__(self, module)
self.dir = dir
+ db, mode = Get_DataBase(dir)
+
+ tpath = norm_entry(dir.tpath)
try:
- global database
- rawentries = database[norm_entry(self.dir.path)]
+ rawentries = db[tpath]
except KeyError:
pass
else:
@@ -119,18 +178,25 @@ class DB(Base):
raise
except:
SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
- "Ignoring corrupt sconsign entry : %s"%self.dir.path)
+ "Ignoring corrupt sconsign entry : %s"%self.dir.tpath)
+
+ if mode == "r":
+ # This directory is actually under a repository, which means
+ # likely they're reaching in directly for a dependency on
+ # a file there. Don't actually set any entry info, so we
+ # won't try to write to that .sconsign.dblite file.
+ self.set_entry = self.do_not_set_entry
global sig_files
sig_files.append(self)
def write(self, sync=1):
if self.dirty:
- global database
- database[norm_entry(self.dir.path)] = cPickle.dumps(self.entries, 1)
+ db, mode = Get_DataBase(self.dir)
+ db[norm_entry(self.dir.tpath)] = cPickle.dumps(self.entries, 1)
if sync:
try:
- syncmethod = database.sync
+ syncmethod = db.sync
except AttributeError:
# Not all anydbm modules have sync() methods.
pass
@@ -223,19 +289,19 @@ class DirFile(Dir):
except OSError:
pass
-ForDirectory = DirFile
+ForDirectory = DB
def File(name, dbm_module=None):
"""
- Arrange for all signatures to be stored in a global .sconsign.dbm
+ Arrange for all signatures to be stored in a global .sconsign.db*
file.
"""
- global database
- if database is None:
- if dbm_module is None:
- import SCons.dblite
- dbm_module = SCons.dblite
- database = dbm_module.open(name, "c")
-
- global ForDirectory
- ForDirectory = DB
+ global ForDirectory, DB_Name, DB_Module
+ if name is None:
+ ForDirectory = DirFile
+ DB_Module = None
+ else:
+ ForDirectory = DB
+ DB_Name = name
+ if not dbm_module is None:
+ DB_Module = dbm_module
diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py
index c2f40bd..025ad8e 100644
--- a/src/engine/SCons/SConsignTests.py
+++ b/src/engine/SCons/SConsignTests.py
@@ -28,6 +28,8 @@ import sys
import TestCmd
import unittest
+import SCons.dblite
+
import SCons.SConsign
class BuildInfo:
@@ -41,12 +43,30 @@ class DummyModule:
def from_string(self, sig):
return int(sig)
-class BaseTestCase(unittest.TestCase):
+class FS:
+ def __init__(self, top):
+ self.Top = top
+ self.Top.repositories = []
+
+class DummyNode:
+ def __init__(self, path='not_a_valid_path'):
+ self.path = path
+ self.tpath = path
+ self.fs = FS(self)
+
+class SConsignTestCase(unittest.TestCase):
+ def setUp(self):
+ self.save_cwd = os.getcwd()
+ self.test = TestCmd.TestCmd(workdir = '')
+ os.chdir(self.test.workpath(''))
+ def tearDown(self):
+ self.test.cleanup()
+ SCons.SConsign.Reset()
+ os.chdir(self.save_cwd)
+
+class BaseTestCase(SConsignTestCase):
def runTest(self):
- class DummyNode:
- path = 'not_a_valid_path'
-
aaa = BuildInfo('aaa')
bbb = BuildInfo('bbb')
bbb.arg1 = 'bbb arg1'
@@ -96,14 +116,11 @@ class BaseTestCase(unittest.TestCase):
assert e.name == 'fff', e.name
assert e.arg == 'fff arg', e.arg
-class SConsignDBTestCase(unittest.TestCase):
+class SConsignDBTestCase(SConsignTestCase):
def runTest(self):
- class DummyNode:
- def __init__(self, path):
- self.path = path
- save_database = SCons.SConsign.database
- SCons.SConsign.database = {}
+ save_DataBase = SCons.SConsign.DataBase
+ SCons.SConsign.DataBase = {}
try:
d1 = SCons.SConsign.DB(DummyNode('dir1'))
d1.set_entry('aaa', BuildInfo('aaa name'))
@@ -137,14 +154,11 @@ class SConsignDBTestCase(unittest.TestCase):
hhh = d32.get_entry('hhh')
assert hhh.name == 'hhh name'
finally:
- SCons.SConsign.database = save_database
+ SCons.SConsign.DataBase = save_DataBase
-class SConsignDirFileTestCase(unittest.TestCase):
+class SConsignDirFileTestCase(SConsignTestCase):
def runTest(self):
- class DummyNode:
- path = 'not_a_valid_path'
-
foo = BuildInfo('foo')
bar = BuildInfo('bar')
@@ -169,52 +183,65 @@ class SConsignDirFileTestCase(unittest.TestCase):
assert e.arg == 'bbb arg', e.arg
-class SConsignFileTestCase(unittest.TestCase):
+class SConsignFileTestCase(SConsignTestCase):
def runTest(self):
- test = TestCmd.TestCmd(workdir = '')
+ test = self.test
file = test.workpath('sconsign_file')
- assert SCons.SConsign.database is None, SCons.SConsign.database
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name == ".sconsign", SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
SCons.SConsign.File(file)
- assert not SCons.SConsign.database is SCons.dblite, SCons.SConsign.database
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
+
+ SCons.SConsign.File(None)
+
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is None, SCons.SConsign.DB_Module
class Fake_DBM:
def open(self, name, mode):
self.name = name
self.mode = mode
return self
+ def __getitem__(self, key):
+ pass
+ def __setitem__(self, key, value):
+ pass
fake_dbm = Fake_DBM()
SCons.SConsign.File(file, fake_dbm)
- assert not SCons.SConsign.database is None, SCons.SConsign.database
+ assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
+ assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name
+ assert SCons.SConsign.DB_Module is fake_dbm, SCons.SConsign.DB_Module
assert not hasattr(fake_dbm, 'name'), fake_dbm
assert not hasattr(fake_dbm, 'mode'), fake_dbm
- SCons.SConsign.database = None
-
- SCons.SConsign.File(file, fake_dbm)
+ SCons.SConsign.ForDirectory(DummyNode(test.workpath('dir')))
- assert not SCons.SConsign.database is None, SCons.SConsign.database
+ assert not SCons.SConsign.DataBase is None, SCons.SConsign.DataBase
assert fake_dbm.name == file, fake_dbm.name
assert fake_dbm.mode == "c", fake_dbm.mode
-class writeTestCase(unittest.TestCase):
+class writeTestCase(SConsignTestCase):
def runTest(self):
- class DummyNode:
- path = 'not_a_valid_path'
-
- test = TestCmd.TestCmd(workdir = '')
+ test = self.test
file = test.workpath('sconsign_file')
class Fake_DBM:
+ def __getitem__(self, key):
+ return None
def __setitem__(self, key, value):
pass
def open(self, name, mode):
@@ -225,10 +252,10 @@ class writeTestCase(unittest.TestCase):
fake_dbm = Fake_DBM()
- SCons.SConsign.database = None
+ SCons.SConsign.DataBase = {}
SCons.SConsign.File(file, fake_dbm)
- f = SCons.SConsign.DirFile(DummyNode(), DummyModule())
+ f = SCons.SConsign.DB(DummyNode(), DummyModule())
f.set_entry('foo', BuildInfo('foo'))
f.set_entry('bar', BuildInfo('bar'))
diff --git a/src/engine/SCons/dblite.py b/src/engine/SCons/dblite.py
index 00f8274..637e503 100644
--- a/src/engine/SCons/dblite.py
+++ b/src/engine/SCons/dblite.py
@@ -14,6 +14,9 @@ _open = __builtin__.open # avoid name clash
keep_all_files = 00000
ignore_corrupt_dbfiles = 0
+def corruption_warning(filename):
+ print "Warning: Discarding corrupt database:", filename
+
if hasattr(types, 'UnicodeType'):
def is_string(s):
t = type(s)
@@ -64,7 +67,7 @@ class dblite:
except cPickle.UnpicklingError:
if (ignore_corrupt_dbfiles == 0): raise
if (ignore_corrupt_dbfiles == 1):
- print "Warning: Discarding corrupt database:", self._file_name
+ corruption_warning(self._file_name)
def __del__(self):
if (self._needs_sync):
diff --git a/test/Configure.py b/test/Configure.py
index 515ae70..83155bb 100644
--- a/test/Configure.py
+++ b/test/Configure.py
@@ -509,6 +509,7 @@ int main() {
os.path.join("build", "sub", "SConscript"))
shutil.rmtree(test.workpath(work_dir, ".sconf_temp"))
+ os.unlink(test.workpath(work_dir, ".sconsign.dblite"))
# now with SConscriptChdir(1)
test.run(chdir=work_dir, arguments='chdir=yes')
diff --git a/test/Repository/SConsignFile.py b/test/Repository/SConsignFile.py
new file mode 100644
index 0000000..9643cb8
--- /dev/null
+++ b/test/Repository/SConsignFile.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that the Repository option works when recording signature
+information in an SConsignFile().
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('repository', 'work')
+
+SConstruct_contents = """\
+SConsignFile('sconsignfile')
+env = Environment()
+result = env.Command('file.out', 'file.in', Copy("$TARGET", "$SOURCE"))
+Default(result)
+"""
+
+test.write(['repository', 'SConstruct'], SConstruct_contents)
+
+test.write(['repository', 'file.in'], "repository/file.in\n")
+
+test.run(chdir='repository')
+
+test.must_match(['repository', 'file.out'], "repository/file.in\n")
+
+test.write(['work', 'SConstruct'], SConstruct_contents)
+
+test.run(chdir='work',
+ arguments='-Y ../repository -n --debug=explain',
+ stdout=test.wrap_stdout("scons: `file.out' is up to date.\n"))
+
+test.must_not_exist(['work', 'file.out'])
+
+test.pass_test()
diff --git a/test/option-n.py b/test/option-n.py
index aa0b8f2..769c4dd 100644
--- a/test/option-n.py
+++ b/test/option-n.py
@@ -120,7 +120,7 @@ test.fail_test(not os.path.exists(test.workpath('f1.out')))
expect = test.wrap_stdout("""\
%s build.py f1.out
""" % python)
-test.unlink('.sconsign')
+test.unlink('.sconsign.dblite')
test.write('f1.out', "X1.out\n")
test.run(arguments = '-n f1.out', stdout = expect)
test.run(arguments = '-n f1.out', stdout = expect)
diff --git a/test/sconsign/corrupt.py b/test/sconsign/corrupt.py
new file mode 100644
index 0000000..fd46de4
--- /dev/null
+++ b/test/sconsign/corrupt.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test that we get proper warnings when .sconsign* files are corrupt.
+"""
+
+import os
+import TestSCons
+import TestCmd
+import cPickle
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+test.subdir('work1', ['work1', 'sub'],
+ 'work2', ['work2', 'sub'])
+
+work1__sconsign_dblite = test.workpath('work1', '.sconsign.dblite')
+work2_sub__sconsign = test.workpath('work2', 'sub', '.sconsign')
+
+SConstruct_contents = """\
+def build1(target, source, env):
+ open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
+ return None
+
+B1 = Builder(action = build1)
+env = Environment(BUILDERS = { 'B1' : B1})
+env.B1(target = 'sub/foo.out', source = 'foo.in')
+"""
+
+
+
+test.write(['work1', 'SConstruct'], SConstruct_contents)
+
+test.write(['work1', 'foo.in'], "work1/foo.in\n")
+
+stderr = '''
+scons: warning: Ignoring corrupt .sconsign file: \.sconsign\.dblite
+.*
+'''
+
+stdout = test.wrap_stdout('build1\(\["sub.foo\.out"\], \["foo\.in"\]\)\n')
+
+test.write(work1__sconsign_dblite, 'not:a:sconsign:file')
+test.run(chdir='work1', arguments='.', stderr=stderr, stdout=stdout)
+
+test.write(work1__sconsign_dblite, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0')
+test.run(chdir='work1', arguments='.', stderr=stderr, stdout=stdout)
+
+
+
+# Test explicitly using a .sconsign file in each directory.
+
+SConstruct_contents = """\
+SConsignFile(None)
+""" + SConstruct_contents
+
+test.write(['work2', 'SConstruct'], SConstruct_contents)
+
+test.write(['work2', 'foo.in'], "work2/foo.in\n")
+
+stderr = '''
+scons: warning: Ignoring corrupt .sconsign file: sub.\.sconsign
+.*
+'''
+
+stdout = test.wrap_stdout('build1\(\["sub.foo\.out"\], \["foo\.in"\]\)\n')
+
+test.write(work2_sub__sconsign, 'not:a:sconsign:file')
+test.run(chdir='work2', arguments='.', stderr=stderr, stdout=stdout)
+
+test.write(work2_sub__sconsign, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0')
+test.run(chdir='work2', arguments='.', stderr=stderr, stdout=stdout)
+
+
+
+test.pass_test()
diff --git a/test/sconsign.py b/test/sconsign/nonwritable.py
index e9a6609..9ad679c 100644
--- a/test/sconsign.py
+++ b/test/sconsign/nonwritable.py
@@ -24,6 +24,10 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+"""
+Test that things still work when a .sconsign* file is not writable.
+"""
+
import os
import TestSCons
import TestCmd
@@ -31,9 +35,21 @@ import cPickle
test = TestSCons.TestSCons(match = TestCmd.match_re)
-test.subdir('sub1', 'sub2', 'sub3')
-
-test.write('SConstruct', """
+test.subdir('work1',
+ ['work1', 'sub1'],
+ ['work1', 'sub2'],
+ ['work1', 'sub3'],
+ 'work2',
+ ['work2', 'sub1'],
+ ['work2', 'sub2'],
+ ['work2', 'sub3'])
+
+work1__sconsign_dblite = test.workpath('work1', '.sconsign.dblite')
+work2_sub1__sconsign = test.workpath('work2', 'sub1', '.sconsign')
+work2_sub2__sconsign = test.workpath('work2', 'sub2', '.sconsign')
+work2_sub3__sconsign = test.workpath('work2', 'sub3', '.sconsign')
+
+SConstruct_contents = """\
def build1(target, source, env):
open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
return None
@@ -52,48 +68,37 @@ env = Environment(BUILDERS = { 'B1' : B1, 'B2' : B2 })
env.B1(target = 'sub1/foo.out', source = 'foo.in')
env.B2(target = 'sub2/foo.out', source = 'foo.in')
env.B2(target = 'sub3/foo.out', source = 'foo.in')
-""")
+"""
-test.write('foo.in', "foo.in\n")
-sub1__sconsign = test.workpath('sub1', '.sconsign')
-sub2__sconsign = test.workpath('sub2', '.sconsign')
-sub3__sconsign = test.workpath('sub3', '.sconsign')
-cPickle.dump({}, open(sub1__sconsign, 'wb'), 1)
-cPickle.dump({}, open(sub2__sconsign, 'wb'), 1)
+test.write(['work1', 'SConstruct'], SConstruct_contents)
-os.chmod(sub1__sconsign, 0444)
+test.write(['work1', 'foo.in'], "work1/foo.in\n")
-test.run(arguments = '.')
+test.write(work1__sconsign_dblite, "")
-test.fail_test(test.read(sub1__sconsign) == "")
-test.fail_test(test.read(sub2__sconsign) == "")
+os.chmod(work1__sconsign_dblite, 0444)
-os.chmod(sub1__sconsign, 0666)
+test.run(chdir='work1', arguments='.')
-test.write('SConstruct', """
-def build1(target, source, env):
- open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read())
- return None
-B1 = Builder(action = build1)
-env = Environment(BUILDERS = { 'B1' : B1})
-env.B1(target = 'sub1/foo.out', source = 'foo.in')
-""")
-stderr = '''
-scons: warning: Ignoring corrupt .sconsign file: sub1.\.sconsign
-.*
-'''
+SConstruct_contents = """\
+SConsignFile(None)
+""" + SConstruct_contents
+
+test.write(['work2', 'SConstruct'], SConstruct_contents)
+
+test.write(['work2', 'foo.in'], "work2/foo.in\n")
+
+cPickle.dump({}, open(work2_sub1__sconsign, 'wb'), 1)
+cPickle.dump({}, open(work2_sub2__sconsign, 'wb'), 1)
-stdout = test.wrap_stdout('build1\(\["sub1.foo\.out"\], \["foo\.in"\]\)\n')
+os.chmod(work2_sub1__sconsign, 0444)
-test.write(sub1__sconsign, 'not:a:sconsign:file')
-test.run(arguments = '.', stderr=stderr, stdout=stdout)
+test.run(chdir='work2', arguments='.')
-test.write(sub1__sconsign, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0')
-test.run(arguments = '.', stderr=stderr, stdout=stdout)
test.pass_test()
diff --git a/test/old-sconsign.py b/test/sconsign/old.py
index 6c057dd..703a924 100644
--- a/test/old-sconsign.py
+++ b/test/sconsign/old.py
@@ -113,8 +113,11 @@ for key, val in old_db.items():
db.sync()
""")
+
+
# Now generate a simple .sconsign file for a simple build.
test.write(['src1', 'SConstruct'], """\
+SConsignFile(None)
import os
def cat(env, source, target):
target = str(target[0])
@@ -154,10 +157,10 @@ for sconsign in sconsign_list:
test.up_to_date(chdir='src1', arguments='.')
+
+
# Now do the same with SConsignFile().
test.write(['src2', 'SConstruct'], """\
-SConsignFile()
-
import os
def cat(env, source, target):
target = str(target[0])
@@ -189,4 +192,6 @@ for sconsign in sconsign_list:
test.up_to_date(chdir='src2', arguments='.')
+
+
test.pass_test()
diff --git a/test/sconsign-script.py b/test/sconsign/script.py
index 7a24aef..9758d5a 100644
--- a/test/sconsign-script.py
+++ b/test/sconsign/script.py
@@ -74,6 +74,7 @@ test.subdir('work1', ['work1', 'sub1'], ['work1', 'sub2'],
'work2', ['work2', 'sub1'], ['work2', 'sub2'])
test.write(['work1', 'SConstruct'], """
+SConsignFile(None)
env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
env1.Program('sub1/hello.c')
env2 = env1.Copy(CPPPATH = ['sub2'])
@@ -221,18 +222,18 @@ test.run(chdir = 'work1', arguments = '. --max-drift=1 --debug=stacktrace')
test.run_sconsign(arguments = "-e hello.exe -e hello.obj work1/sub1/.sconsign",
stdout = """\
-hello.exe: None \d+ None
- hello.obj: \d+
-hello.obj: None \d+ None
- hello.c: \d+
+hello.exe: None \S+ None
+ hello.obj: \S+
+hello.obj: None \S+ None
+ hello.c: \S+
""")
test.run_sconsign(arguments = "-e hello.exe -e hello.obj -r work1/sub1/.sconsign",
stdout = """\
-hello.exe: None \d+ None
- hello.obj: \d+
-hello.obj: None \d+ None
- hello.c: \d+
+hello.exe: None \S+ None
+ hello.obj: \S+
+hello.obj: None \S+ None
+ hello.c: \S+
""")
diff --git a/test/subdivide.py b/test/subdivide.py
index f6aa4f1..18219bf 100644
--- a/test/subdivide.py
+++ b/test/subdivide.py
@@ -25,8 +25,9 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
-Verify that rebuilds do not occur when TargetSignatures()
-content is used to subdivide a dependency tree.
+Verify that rebuilds do not occur when SConsignFile(None) is used to
+put a .sconsign file in each directory, and TargetSignatures('content')
+is used to subdivide a dependency tree.
"""
import os.path
@@ -46,6 +47,7 @@ test = TestSCons.TestSCons()
test.subdir('src', ['src', 'sub'])
test.write('SConstruct', """\
+SConsignFile(None)
TargetSignatures('content')
env = Environment()
env.SConscript('src/SConstruct', exports=['env'])
@@ -53,6 +55,7 @@ env.Object('foo.c')
""")
test.write(['src', 'SConstruct'], """\
+SConsignFile(None)
TargetSignatures('content')
env = Environment()
p = env.Program('prog', ['main.c', '../foo%s', 'sub/bar.c'])