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,
.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
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"
# 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
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
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
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
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):
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
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
self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
self.Top.path = '.'
+ self.Top.tpath = '.'
self._cwd = self.Top
def clear_cache(self):
def addRepository(self, dir):
if dir != self and not dir in self.repositories:
self.repositories.append(dir)
+ dir.tpath = '.'
self.__clearRepositoryCache()
def up(self):
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."""
# 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)
# 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):
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
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.
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()
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
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()
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') )
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
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()
"""
(ret, output) = test.TryRun( prog, ".c" )
test.Result( ret )
- assert ret and output == "Hello"
+ assert ret and output == "Hello", (ret, output)
return ret
log_file=self.test.workpath('config.log'))
try:
ret = sconf.CheckCustom()
- assert ret
+ assert ret, ret
finally:
sconf.Finish()
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
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:
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:
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
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
import TestCmd
import unittest
+import SCons.dblite
+
import SCons.SConsign
class BuildInfo:
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'
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'))
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')
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):
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'))
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)
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):
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')
--- /dev/null
+#!/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()
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)
--- /dev/null
+#!/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()
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+"""
+Test that things still work when a .sconsign* file is not writable.
+"""
+
import os
import TestSCons
import TestCmd
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
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()
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])
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])
test.up_to_date(chdir='src2', arguments='.')
+
+
test.pass_test()
'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'])
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+
""")
__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
test.subdir('src', ['src', 'sub'])
test.write('SConstruct', """\
+SConsignFile(None)
TargetSignatures('content')
env = Environment()
env.SConscript('src/SConstruct', exports=['env'])
""")
test.write(['src', 'SConstruct'], """\
+SConsignFile(None)
TargetSignatures('content')
env = Environment()
p = env.Program('prog', ['main.c', '../foo%s', 'sub/bar.c'])