summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Cassagnol <87133045+jcassagnol-public@users.noreply.github.com>2021-11-09 12:45:16 -0500
committerJacob Cassagnol <87133045+jcassagnol-public@users.noreply.github.com>2021-11-09 12:45:16 -0500
commit699d6b0d571827de973e027d765f64b629f08ed9 (patch)
tree2c96c3c72a94b2cfea0bcbc41b2715c5bd4b7b2f
parent1dc541ef4dafed2cd7af68d23856dd99dd7b5644 (diff)
downloadscons-git-699d6b0d571827de973e027d765f64b629f08ed9.tar.gz
Tests pass in python 3.6 and 3.9 in Linux
Modified failing tests to use the new defaulted .sconsign database based on the hash algorithm For MD5, default database will be .sconsign.dblite For other algorithms the default will be .sconsign_<hashname>.dblite. For all cases where the user changes the hash algorithm used, the database will be .sconsign_<hashname>.dblite (including md5) For sub-scons directories it remains as .sconsign Also added unit-tests for Util.py for the new hash default changes. It's difficult to setup a fips-compliant platform using containers, and instead we mock that. option--config uses multiple types of hash algorithms so was skipped. Removed one f-string (python 3.5 doesn't support those) Corrupt.py is using an explicit .sconsign so that was left as-is, and only the parent default .sconsign was changed for work test 1. A fetch-database name option was added to the testing framework. The unlink_sconsignfile was not updated as no usages of it were found.
-rw-r--r--SCons/SConsign.py11
-rw-r--r--SCons/SConsignTests.py6
-rw-r--r--SCons/Util.py78
-rw-r--r--SCons/UtilTests.py176
-rw-r--r--test/Configure/ConfigureDryRunError.py13
-rw-r--r--test/Configure/VariantDir-SConscript.py2
-rw-r--r--test/Configure/implicit-cache.py28
-rw-r--r--test/Configure/option--config.py6
-rw-r--r--test/Repository/variants.py66
-rw-r--r--test/option/hash-format.py42
-rw-r--r--test/option/option-n.py15
-rw-r--r--test/question/Configure.py14
-rw-r--r--test/sconsign/corrupt.py13
-rw-r--r--test/sconsign/script/Configure.py4
-rw-r--r--testing/framework/TestSCons.py22
15 files changed, 395 insertions, 101 deletions
diff --git a/SCons/SConsign.py b/SCons/SConsign.py
index ade8a10f1..95ceac191 100644
--- a/SCons/SConsign.py
+++ b/SCons/SConsign.py
@@ -64,10 +64,17 @@ def Get_DataBase(dir):
if DB_Name is None:
hash_format = SCons.Util.get_hash_format()
- if hash_format is None:
+ current_hash_algorithm = SCons.Util.get_current_hash_algorithm_used()
+ # if the user left the options defaulted AND the default algorithm set by
+ # SCons is md5, then set the database name to be the special default name
+ #
+ # otherwise, if it defaults to something like 'sha1' or the user explicitly
+ # set 'md5' as the hash format, set the database name to .sconsign_<algorithm>
+ # eg .sconsign_sha1, etc.
+ if hash_format is None and current_hash_algorithm == 'md5':
DB_Name = ".sconsign"
else:
- DB_Name = ".sconsign_%s" % hash_format
+ DB_Name = ".sconsign_%s" % current_hash_algorithm
top = dir.fs.Top
if not os.path.isabs(DB_Name) and top.repositories:
diff --git a/SCons/SConsignTests.py b/SCons/SConsignTests.py
index d4f44183a..e9f20718d 100644
--- a/SCons/SConsignTests.py
+++ b/SCons/SConsignTests.py
@@ -28,6 +28,7 @@ import TestCmd
import SCons.dblite
import SCons.SConsign
+from SCons.Util import get_hash_format, get_current_hash_algorithm_used
class BuildInfo:
def merge(self, object):
@@ -295,7 +296,10 @@ class SConsignFileTestCase(SConsignTestCase):
file = test.workpath('sconsign_file')
assert SCons.SConsign.DataBase == {}, SCons.SConsign.DataBase
- assert SCons.SConsign.DB_Name == ".sconsign", SCons.SConsign.DB_Name
+ if get_hash_format() is None and get_current_hash_algorithm_used() == 'md5':
+ assert SCons.SConsign.DB_Name == ".sconsign", SCons.SConsign.DB_Name
+ else:
+ assert SCons.SConsign.DB_Name == ".sconsign_{}".format(get_current_hash_algorithm_used()), SCons.SConsign.DB_Name
assert SCons.SConsign.DB_Module is SCons.dblite, SCons.SConsign.DB_Module
SCons.SConsign.File(file)
diff --git a/SCons/Util.py b/SCons/Util.py
index 8b79a3e17..cbd99b0b3 100644
--- a/SCons/Util.py
+++ b/SCons/Util.py
@@ -29,7 +29,6 @@ import os
import pprint
import re
import sys
-import inspect
from collections import UserDict, UserList, UserString, OrderedDict
from collections.abc import MappingView
from contextlib import suppress
@@ -1670,12 +1669,12 @@ def AddMethod(obj, function, name=None):
# Default hash function and format. SCons-internal.
-_DEFAULT_HASH_FORMATS = ['md5', 'sha1', 'sha256']
+DEFAULT_HASH_FORMATS = ['md5', 'sha1', 'sha256']
ALLOWED_HASH_FORMATS = []
_HASH_FUNCTION = None
_HASH_FORMAT = None
-def _attempt_init_of_python_3_9_hash_object(hash_function_object):
+def _attempt_init_of_python_3_9_hash_object(hash_function_object, sys_used=sys):
"""Python 3.9 and onwards lets us initialize the hash function object with the
key "usedforsecurity"=false. This lets us continue to use algorithms that have
been deprecated either by FIPS or by Python itself, as the MD5 algorithm SCons
@@ -1696,7 +1695,7 @@ def _attempt_init_of_python_3_9_hash_object(hash_function_object):
# however, for our purposes checking the version is greater than or equal to 3.9 is good enough, as
# the API is guaranteed to have support for the 'usedforsecurity' flag in 3.9. See
# https://docs.python.org/3/library/hashlib.html#:~:text=usedforsecurity for the version support notes.
- if (sys.version_info.major > 3) or (sys.version_info.major == 3 and sys.version_info.minor >= 9):
+ if (sys_used.version_info.major > 3) or (sys_used.version_info.major == 3 and sys_used.version_info.minor >= 9):
return hash_function_object(usedforsecurity=False)
# note that this can throw a ValueError in FIPS-enabled versions of Linux prior to 3.9
@@ -1704,7 +1703,7 @@ def _attempt_init_of_python_3_9_hash_object(hash_function_object):
# the caller to diagnose the ValueError & potentially display the error to screen.
return hash_function_object()
-def _set_allowed_viable_default_hashes(hashlib_used):
+def _set_allowed_viable_default_hashes(hashlib_used, sys_used=sys):
"""Checks if SCons has ability to call the default algorithms normally supported.
This util class is sometimes called prior to setting the user-selected hash algorithm,
@@ -1712,7 +1711,7 @@ def _set_allowed_viable_default_hashes(hashlib_used):
and throw an exception in set_hash_format. A common case is using the SConf options,
which can run prior to main, and thus ignore the options.hash_format variable.
- This function checks the _DEFAULT_HASH_FORMATS and sets the ALLOWED_HASH_FORMATS
+ This function checks the DEFAULT_HASH_FORMATS and sets the ALLOWED_HASH_FORMATS
to only the ones that can be called. In Python >= 3.9 this will always default to
MD5 as in Python 3.9 there is an optional attribute "usedforsecurity" set for the method.
@@ -1724,7 +1723,7 @@ def _set_allowed_viable_default_hashes(hashlib_used):
# otherwise it keeps appending valid formats to the string
ALLOWED_HASH_FORMATS = []
- for test_algorithm in _DEFAULT_HASH_FORMATS:
+ for test_algorithm in DEFAULT_HASH_FORMATS:
_test_hash = getattr(hashlib_used, test_algorithm, None)
# we know hashlib claims to support it... check to see if we can call it.
if _test_hash is not None:
@@ -1733,7 +1732,7 @@ def _set_allowed_viable_default_hashes(hashlib_used):
# throw if it's a bad algorithm, otherwise it will append it to the known
# good formats.
try:
- _attempt_init_of_python_3_9_hash_object(_test_hash)
+ _attempt_init_of_python_3_9_hash_object(_test_hash, sys_used)
ALLOWED_HASH_FORMATS.append(test_algorithm)
except ValueError as e:
_last_error = e
@@ -1761,8 +1760,27 @@ def get_hash_format():
"""
return _HASH_FORMAT
+def _attempt_get_hash_function(hash_name, hashlib_used=hashlib, sys_used=sys):
+ """Wrapper used to try to initialize a hash function given.
-def set_hash_format(hash_format, hashlib_used=hashlib):
+ If successful, returns the name of the hash function back to the user.
+
+ Otherwise returns None.
+ """
+ try:
+ _fetch_hash = getattr(hashlib_used, hash_name, None)
+ if _fetch_hash is None:
+ return None
+ _attempt_init_of_python_3_9_hash_object(_fetch_hash, sys_used)
+ return hash_name
+ except ValueError:
+ # if attempt_init_of_python_3_9 throws, this is typically due to FIPS being enabled
+ # however, if we get to this point, the viable hash function check has either been
+ # bypassed or otherwise failed to properly restrict the user to only the supported
+ # functions. As such throw the UserError as an internal assertion-like error.
+ return None
+
+def set_hash_format(hash_format, hashlib_used=hashlib, sys_used=sys):
"""Sets the default hash format used by SCons.
If `hash_format` is ``None`` or
@@ -1782,7 +1800,7 @@ def set_hash_format(hash_format, hashlib_used=hashlib):
# user can select something not supported by their OS but normally supported by
# SCons, example, selecting MD5 in an OS with FIPS-mode turned on. Therefore we first
# check if SCons supports it, and then if their local OS supports it.
- if hash_format_lower in _DEFAULT_HASH_FORMATS:
+ if hash_format_lower in DEFAULT_HASH_FORMATS:
raise UserError('While hash format "%s" is supported by SCons, the '
'local system indicates only the following hash '
'formats are supported by the hashlib library: %s' %
@@ -1793,7 +1811,7 @@ def set_hash_format(hash_format, hashlib_used=hashlib):
# the hash format isn't supported by SCons in any case. Warn the user, and
# if we detect that SCons supports more algorithms than their local system
# supports, warn the user about that too.
- if ALLOWED_HASH_FORMATS == _DEFAULT_HASH_FORMATS:
+ if ALLOWED_HASH_FORMATS == DEFAULT_HASH_FORMATS:
raise UserError('Hash format "%s" is not supported by SCons. Only '
'the following hash formats are supported: %s' %
(hash_format_lower,
@@ -1805,23 +1823,14 @@ def set_hash_format(hash_format, hashlib_used=hashlib):
'is reporting; SCons supports: %s. Your local system only '
'supports: %s' %
(hash_format_lower,
- ', '.join(_DEFAULT_HASH_FORMATS),
+ ', '.join(DEFAULT_HASH_FORMATS),
', '.join(ALLOWED_HASH_FORMATS))
)
# this is not expected to fail. If this fails it means the set_allowed_viable_default_hashes
# function did not throw, or when it threw, the exception was caught and ignored, or
# the global ALLOWED_HASH_FORMATS was changed by an external user.
- try:
- _HASH_FUNCTION = None
- _attempt_init_of_python_3_9_hash_object(getattr(hashlib_used, hash_format_lower, None))
- _HASH_FUNCTION = hash_format_lower
- except ValueError:
- # if attempt_init_of_python_3_9 throws, this is typically due to FIPS being enabled
- # however, if we get to this point, the viable hash function check has either been
- # bypassed or otherwise failed to properly restrict the user to only the supported
- # functions. As such throw the UserError as an internal assertion-like error.
- pass
+ _HASH_FUNCTION = _attempt_get_hash_function(hash_format_lower, hashlib_used, sys_used)
if _HASH_FUNCTION is None:
from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
@@ -1838,12 +1847,7 @@ def set_hash_format(hash_format, hashlib_used=hashlib):
# in FIPS-compliant systems this usually defaults to SHA1, unless that too has been
# disabled.
for choice in ALLOWED_HASH_FORMATS:
- try:
- _HASH_FUNCTION = None
- _attempt_init_of_python_3_9_hash_object(getattr(hashlib_used, choice, None))
- _HASH_FUNCTION = choice
- except ValueError:
- continue
+ _HASH_FUNCTION = _attempt_get_hash_function(choice, hashlib_used, sys_used)
if _HASH_FUNCTION is not None:
break
@@ -1864,7 +1868,19 @@ def set_hash_format(hash_format, hashlib_used=hashlib):
set_hash_format(None)
-def _get_hash_object(hash_format, hashlib_used=hashlib):
+def get_current_hash_algorithm_used():
+ """Returns the current hash algorithm name used.
+
+ Where the python version >= 3.9, this is expected to return md5.
+ If python's version is <= 3.8, this returns md5 on non-FIPS-mode platforms, and
+ sha1 or sha256 on FIPS-mode Linux platforms.
+
+ This function is primarily useful for testing, where one expects a value to be
+ one of N distinct hashes, and therefore the test needs to know which hash to select.
+ """
+ return _HASH_FUNCTION
+
+def _get_hash_object(hash_format, hashlib_used=hashlib, sys_used=sys):
"""Allocates a hash object using the requested hash format.
Args:
@@ -1879,7 +1895,7 @@ def _get_hash_object(hash_format, hashlib_used=hashlib):
raise UserError('There is no default hash function. Did you call '
'a hashing function before SCons was initialized?')
- return _attempt_init_of_python_3_9_hash_object(getattr(hashlib_used, _HASH_FUNCTION, None))
+ return _attempt_init_of_python_3_9_hash_object(getattr(hashlib_used, _HASH_FUNCTION, None), sys_used)
if not hasattr(hashlib, hash_format):
from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
@@ -1888,7 +1904,7 @@ def _get_hash_object(hash_format, hashlib_used=hashlib):
'Hash format "%s" is not available in your Python interpreter.' %
hash_format)
- return _attempt_init_of_python_3_9_hash_object(getattr(hashlib, hash_format))
+ return _attempt_init_of_python_3_9_hash_object(getattr(hashlib, hash_format), sys_used)
def hash_signature(s, hash_format=None):
diff --git a/SCons/UtilTests.py b/SCons/UtilTests.py
index 616ea37cc..4417ba57d 100644
--- a/SCons/UtilTests.py
+++ b/SCons/UtilTests.py
@@ -26,13 +26,17 @@ import io
import os
import sys
import unittest
-from collections import UserDict, UserList, UserString
+import unittest.mock
+import hashlib
+import warnings
+from collections import UserDict, UserList, UserString, namedtuple
import TestCmd
import SCons.Errors
import SCons.compat
from SCons.Util import (
+ ALLOWED_HASH_FORMATS,
AddPathIfNotExists,
AppendPath,
CLVar,
@@ -42,6 +46,9 @@ from SCons.Util import (
Proxy,
Selector,
WhereIs,
+ _attempt_init_of_python_3_9_hash_object,
+ _get_hash_object,
+ _set_allowed_viable_default_hashes,
adjustixes,
containsAll,
containsAny,
@@ -61,6 +68,7 @@ from SCons.Util import (
is_Tuple,
print_tree,
render_tree,
+ set_hash_format,
silent_intern,
splitext,
to_String,
@@ -838,12 +846,17 @@ class HashTestCase(unittest.TestCase):
'25235f0fcab8767b7b5ac6568786fbc4f7d5d83468f0626bf07c3dbeed391a7a',
'f8d3d0729bf2427e2e81007588356332e7e8c4133fae4bceb173b93f33411d17'),
}.items():
- hs = functools.partial(hash_signature, hash_format=algorithm)
- s = list(map(hs, ('111', '222', '333')))
-
- assert expected[0] == hash_collect(s[0:1], hash_format=algorithm)
- assert expected[1] == hash_collect(s[0:2], hash_format=algorithm)
- assert expected[2] == hash_collect(s, hash_format=algorithm)
+ # if the current platform does not support the algorithm we're looking at,
+ # skip the test steps for that algorithm, but display a warning to the user
+ if algorithm not in ALLOWED_HASH_FORMATS:
+ warnings.warn("Missing hash algorithm {} on this platform, cannot test with it".format(algorithm), ResourceWarning)
+ else:
+ hs = functools.partial(hash_signature, hash_format=algorithm)
+ s = list(map(hs, ('111', '222', '333')))
+
+ assert expected[0] == hash_collect(s[0:1], hash_format=algorithm)
+ assert expected[1] == hash_collect(s[0:2], hash_format=algorithm)
+ assert expected[2] == hash_collect(s, hash_format=algorithm)
def test_MD5signature(self):
"""Test generating a signature"""
@@ -855,11 +868,150 @@ class HashTestCase(unittest.TestCase):
'sha256': ('f6e0a1e2ac41945a9aa7ff8a8aaa0cebc12a3bcc981a929ad5cf810a090e11ae',
'9b871512327c09ce91dd649b3f96a63b7408ef267c8cc5710114e629730cb61f'),
}.items():
- s = hash_signature('111', hash_format=algorithm)
- assert expected[0] == s, s
-
- s = hash_signature('222', hash_format=algorithm)
- assert expected[1] == s, s
+ # if the current platform does not support the algorithm we're looking at,
+ # skip the test steps for that algorithm, but display a warning to the user
+ if algorithm not in ALLOWED_HASH_FORMATS:
+ warnings.warn("Missing hash algorithm {} on this platform, cannot test with it".format(algorithm), ResourceWarning)
+ else:
+ s = hash_signature('111', hash_format=algorithm)
+ assert expected[0] == s, s
+
+ s = hash_signature('222', hash_format=algorithm)
+ assert expected[1] == s, s
+
+# this uses mocking out, which is platform specific, however, the FIPS
+# behavior this is testing is also platform-specific, and only would be
+# visible in hosts running Linux with the fips_mode kernel flag along
+# with using OpenSSL.
+
+class FIPSHashTestCase(unittest.TestCase):
+ def __init__(self, *args, **kwargs):
+ super(FIPSHashTestCase, self).__init__(*args, **kwargs)
+
+ ###############################
+ # algorithm mocks, can check if we called with usedforsecurity=False for python >= 3.9
+ self.fake_md5=lambda usedforsecurity=True: (usedforsecurity, 'md5')
+ self.fake_sha1=lambda usedforsecurity=True: (usedforsecurity, 'sha1')
+ self.fake_sha256=lambda usedforsecurity=True: (usedforsecurity, 'sha256')
+ ###############################
+
+ ###############################
+ # hashlib mocks
+ md5Available = unittest.mock.Mock(md5=self.fake_md5)
+ del md5Available.sha1
+ del md5Available.sha256
+ self.md5Available=md5Available
+
+ md5Default = unittest.mock.Mock(md5=self.fake_md5, sha1=self.fake_sha1)
+ del md5Default.sha256
+ self.md5Default=md5Default
+
+ sha1Default = unittest.mock.Mock(sha1=self.fake_sha1, sha256=self.fake_sha256)
+ del sha1Default.md5
+ self.sha1Default=sha1Default
+
+ sha256Default = unittest.mock.Mock(sha256=self.fake_sha256, **{'md5.side_effect': ValueError, 'sha1.side_effect': ValueError})
+ self.sha256Default=sha256Default
+
+ all_throw = unittest.mock.Mock(**{'md5.side_effect': ValueError, 'sha1.side_effect': ValueError, 'sha256.side_effect': ValueError})
+ self.all_throw=all_throw
+
+ no_algorithms = unittest.mock.Mock()
+ del no_algorithms.md5
+ del no_algorithms.sha1
+ del no_algorithms.sha256
+ self.no_algorithms=no_algorithms
+
+ unsupported_algorithm = unittest.mock.Mock(unsupported=self.fake_sha256)
+ del unsupported_algorithm.md5
+ del unsupported_algorithm.sha1
+ del unsupported_algorithm.sha256
+ self.unsupported_algorithm=unsupported_algorithm
+ ###############################
+
+ ###############################
+ # system version mocks
+ VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
+ v3_8 = VersionInfo(3, 8, 199, 'super-beta', 1337)
+ v3_9 = VersionInfo(3, 9, 0, 'alpha', 0)
+ v4_8 = VersionInfo(4, 8, 0, 'final', 0)
+
+ self.sys_v3_8 = unittest.mock.Mock(version_info=v3_8)
+ self.sys_v3_9 = unittest.mock.Mock(version_info=v3_9)
+ self.sys_v4_8 = unittest.mock.Mock(version_info=v4_8)
+ ###############################
+
+ def test_usedforsecurity_flag_behavior(self):
+ """Test usedforsecurity flag -> should be set to 'True' on older versions of python, and 'False' on Python >= 3.9"""
+ for version, expected in {
+ self.sys_v3_8: (True, 'md5'),
+ self.sys_v3_9: (False, 'md5'),
+ self.sys_v4_8: (False, 'md5'),
+ }.items():
+ assert _attempt_init_of_python_3_9_hash_object(self.fake_md5, version) == expected
+
+ def test_automatic_default_to_md5(self):
+ """Test automatic default to md5 even if sha1 available"""
+ for version, expected in {
+ self.sys_v3_8: (True, 'md5'),
+ self.sys_v3_9: (False, 'md5'),
+ self.sys_v4_8: (False, 'md5'),
+ }.items():
+ _set_allowed_viable_default_hashes(self.md5Default, version)
+ set_hash_format(None, self.md5Default, version)
+ assert _get_hash_object(None, self.md5Default, version) == expected
+
+ def test_automatic_default_to_sha256(self):
+ """Test automatic default to sha256 if other algorithms available but throw"""
+ for version, expected in {
+ self.sys_v3_8: (True, 'sha256'),
+ self.sys_v3_9: (False, 'sha256'),
+ self.sys_v4_8: (False, 'sha256'),
+ }.items():
+ _set_allowed_viable_default_hashes(self.sha256Default, version)
+ set_hash_format(None, self.sha256Default, version)
+ assert _get_hash_object(None, self.sha256Default, version) == expected
+
+ def test_automatic_default_to_sha1(self):
+ """Test automatic default to sha1 if md5 is missing from hashlib entirely"""
+ for version, expected in {
+ self.sys_v3_8: (True, 'sha1'),
+ self.sys_v3_9: (False, 'sha1'),
+ self.sys_v4_8: (False, 'sha1'),
+ }.items():
+ _set_allowed_viable_default_hashes(self.sha1Default, version)
+ set_hash_format(None, self.sha1Default, version)
+ assert _get_hash_object(None, self.sha1Default, version) == expected
+
+ def test_no_available_algorithms(self):
+ """expect exceptions on no available algorithms or when all algorithms throw"""
+ self.assertRaises(SCons.Errors.SConsEnvironmentError, _set_allowed_viable_default_hashes, self.no_algorithms)
+ self.assertRaises(SCons.Errors.SConsEnvironmentError, _set_allowed_viable_default_hashes, self.all_throw)
+ self.assertRaises(SCons.Errors.SConsEnvironmentError, _set_allowed_viable_default_hashes, self.unsupported_algorithm)
+
+ def test_bad_algorithm_set_attempt(self):
+ """expect exceptions on user setting an unsupported algorithm selections, either by host or by SCons"""
+
+ # nonexistant hash algorithm, not supported by SCons
+ _set_allowed_viable_default_hashes(self.md5Available)
+ self.assertRaises(SCons.Errors.UserError, set_hash_format, 'blah blah blah', hashlib_used=self.no_algorithms)
+
+ # md5 is default-allowed, but in this case throws when we attempt to use it
+ _set_allowed_viable_default_hashes(self.md5Available)
+ self.assertRaises(SCons.Errors.UserError, set_hash_format, 'md5', hashlib_used=self.all_throw)
+
+ # user attempts to use an algorithm that isn't supported by their current system but is supported by SCons
+ _set_allowed_viable_default_hashes(self.sha1Default)
+ self.assertRaises(SCons.Errors.UserError, set_hash_format, 'md5', hashlib_used=self.all_throw)
+
+ # user attempts to use an algorithm that is supported by their current system but isn't supported by SCons
+ _set_allowed_viable_default_hashes(self.sha1Default)
+ self.assertRaises(SCons.Errors.UserError, set_hash_format, 'unsupported', hashlib_used=self.unsupported_algorithm)
+
+ def tearDown(self):
+ """Return SCons back to the normal global state for the hashing functions."""
+ _set_allowed_viable_default_hashes(hashlib, sys)
+ set_hash_format(None)
class NodeListTestCase(unittest.TestCase):
diff --git a/test/Configure/ConfigureDryRunError.py b/test/Configure/ConfigureDryRunError.py
index 3648518d2..224154b1e 100644
--- a/test/Configure/ConfigureDryRunError.py
+++ b/test/Configure/ConfigureDryRunError.py
@@ -31,6 +31,8 @@ import os
import TestSCons
+from SCons.Util import get_current_hash_algorithm_used
+
_obj = TestSCons._obj
test = TestSCons.TestSCons()
@@ -65,7 +67,16 @@ test.run(arguments='-n', status=2, stderr=expect)
test.must_not_exist('config.log')
test.subdir('.sconf_temp')
-conftest_0_c = os.path.join(".sconf_temp", "conftest_df286a1d2f67e69d030b4eff75ca7e12_0.c")
+# depending on which default hash function we're using, we'd expect one of the following filenames.
+# The filenames are generated by the conftest changes in #3543 : https://github.com/SCons/scons/pull/3543/files
+possible_filenames = {
+ 'md5': "conftest_df286a1d2f67e69d030b4eff75ca7e12_0.c",
+ 'sha1': "conftest_6e784ac3248d146c68396335df2f9428d78c1d24_0.c",
+ 'sha256': "conftest_be4b3c1d600e20dfc3e8d98748cd8193c850331643466d800ef8a4229a5be410_0.c"
+}
+test_filename = possible_filenames[get_current_hash_algorithm_used()]
+
+conftest_0_c = os.path.join(".sconf_temp", test_filename)
SConstruct_file_line = test.python_file_line(SConstruct_path, 6)[:-1]
expect = """
diff --git a/test/Configure/VariantDir-SConscript.py b/test/Configure/VariantDir-SConscript.py
index deb7b8fc4..5818fc779 100644
--- a/test/Configure/VariantDir-SConscript.py
+++ b/test/Configure/VariantDir-SConscript.py
@@ -128,7 +128,7 @@ test.checkLogAndStdout( ["Checking for C header file math.h... ",
import shutil
shutil.rmtree(test.workpath(".sconf_temp"))
-test.unlink(".sconsign.dblite")
+test.unlink(test.get_sconsignname()+".dblite")
# now with SConscriptChdir(1)
test.run(arguments='chdir=yes')
diff --git a/test/Configure/implicit-cache.py b/test/Configure/implicit-cache.py
index f4f3e94e5..4078a9899 100644
--- a/test/Configure/implicit-cache.py
+++ b/test/Configure/implicit-cache.py
@@ -55,6 +55,7 @@ get longer and longer until it blew out the users's memory.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import TestSConsign
+from SCons.Util import get_hash_format, get_current_hash_algorithm_used
test = TestSConsign.TestSConsign()
@@ -75,7 +76,28 @@ test.write('foo.h', "#define FOO 1\n")
test.run(arguments = '.')
-test.run_sconsign('-d .sconf_temp -e conftest_5a3fa36d51dd2a28d521d6cc0e2e1d04_0.c --raw .sconsign.dblite')
+# depending on which default hash function we're using, we'd expect one of the following filenames.
+# The filenames are generated by the conftest changes in #3543 : https://github.com/SCons/scons/pull/3543/files
+# this test is different than the other tests, as the database name is used here.
+# when the database defaults to md5, that's a different name than when the user selects md5 directly.
+possible_filenames = {
+ 'default': "conftest_5a3fa36d51dd2a28d521d6cc0e2e1d04_0.c",
+ 'md5': "conftest_5a3fa36d51dd2a28d521d6cc0e2e1d04_0.c",
+ 'sha1': "conftest_80e5b88f2c7427a92f0e6c7184f144f874f10e60_0.c",
+ 'sha256': "conftest_ba8270c26647ad00993cd7777f4c5d3751018372b97d16eb993563bea051c3df_0.c"
+}
+# user left algorithm default, it defaulted to md5, with the special database name
+if get_hash_format() is None and get_current_hash_algorithm_used() == 'md5':
+ test_filename = possible_filenames['default']
+# either user selected something (like explicitly setting md5) or algorithm defaulted to something else.
+# SCons can default to something else if it detects the hashlib doesn't support it, example md5 in FIPS
+# mode prior to Python 3.9
+else:
+ test_filename = possible_filenames[get_current_hash_algorithm_used()]
+
+database_name=test.get_sconsignname() + ".dblite"
+
+test.run_sconsign(f'-d .sconf_temp -e {test_filename} --raw {database_name}')
old_sconsign_dblite = test.stdout()
# Second run: Have the configure subsystem also look for foo.h, so
@@ -88,11 +110,11 @@ old_sconsign_dblite = test.stdout()
test.run(arguments = '--implicit-cache USE_FOO=1 .')
-test.run_sconsign('-d .sconf_temp -e conftest_5a3fa36d51dd2a28d521d6cc0e2e1d04_0.c --raw .sconsign.dblite')
+test.run_sconsign(f'-d .sconf_temp -e {test_filename} --raw {database_name}')
new_sconsign_dblite = test.stdout()
if old_sconsign_dblite != new_sconsign_dblite:
- print(".sconsign.dblite did not match:")
+ print(f"{database_name} did not match:")
print("FIRST RUN ==========")
print(old_sconsign_dblite)
print("SECOND RUN ==========")
diff --git a/test/Configure/option--config.py b/test/Configure/option--config.py
index f31336f2c..3b3889267 100644
--- a/test/Configure/option--config.py
+++ b/test/Configure/option--config.py
@@ -32,6 +32,7 @@ import os.path
from TestSCons import TestSCons, ConfigCheckInfo, _obj
from TestCmd import IS_WINDOWS
+from SCons.Util import get_current_hash_algorithm_used
test = TestSCons()
@@ -42,6 +43,11 @@ CR = test.CR # cached rebuild (up to date)
NCF = test.NCF # non-cached build failure
CF = test.CF # cached build failure
+# as this test is somewhat complicated, skip it if the library doesn't support md5
+# as the default hashing algorithm.
+if get_current_hash_algorithm_used() != 'md5':
+ test.skip_test('Skipping test as could not continue without the hash algorithm set to md5!')
+
SConstruct_path = test.workpath('SConstruct')
test.write(SConstruct_path, """
diff --git a/test/Repository/variants.py b/test/Repository/variants.py
index f89605bed..c95e8532a 100644
--- a/test/Repository/variants.py
+++ b/test/Repository/variants.py
@@ -216,12 +216,14 @@ repository/src1/bbb.c: REPOSITORY_FOO
repository/src1/main.c: REPOSITORY_FOO
""")
+database_name=test.get_sconsignname()
+
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
test.run(program=repository_build2_foo_src2_xxx_xxx, stdout="""\
repository/src2/include/my_string.h: FOO
@@ -236,11 +238,11 @@ repository/src2/xxx/main.c: BAR
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
# Make the entire repository non-writable, so we'll detect
# if we try to write into it accidentally.
@@ -270,11 +272,11 @@ repository/src1/main.c: REPOSITORY_BAR
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
test.up_to_date(chdir='work1', options=opts + " OS=bar", arguments='build1')
@@ -301,11 +303,11 @@ repository/src1/main.c: WORK_BAR
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
test.up_to_date(chdir='work1', options=opts + " OS=bar", arguments='build1')
@@ -319,11 +321,11 @@ repository/src1/main.c: WORK_FOO
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
test.up_to_date(chdir='work1', options=opts + " OS=foo", arguments='build1')
@@ -376,11 +378,11 @@ repository/src2/xxx/main.c: BAR
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
# Ensure file time stamps will be newer.
time.sleep(2)
@@ -411,11 +413,11 @@ repository/src2/xxx/main.c: BAR
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
#
test.unlink(['work2', 'src2', 'include', 'my_string.h'])
@@ -435,11 +437,11 @@ repository/src2/xxx/main.c: BAR
""")
test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', '.sconsign')))
+ test.workpath('repository', 'src1', database_name)))
test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work1', 'src1', '.sconsign')))
-test.fail_test(os.path.exists(test.workpath('work2', 'src2', '.sconsign')))
+ test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
#
test.pass_test()
diff --git a/test/option/hash-format.py b/test/option/hash-format.py
index 9fa10ee84..f0156f3ef 100644
--- a/test/option/hash-format.py
+++ b/test/option/hash-format.py
@@ -27,28 +27,52 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import hashlib
import os
import TestSCons
+import warnings
+from SCons.Util import ALLOWED_HASH_FORMATS, DEFAULT_HASH_FORMATS
# Test passing the hash format by command-line.
INVALID_ALGORITHM = 'testfailure'
-for algorithm in ['md5', 'sha1', 'sha256', INVALID_ALGORITHM, None]:
+
+for algorithm in [*DEFAULT_HASH_FORMATS, INVALID_ALGORITHM, None]:
test = TestSCons.TestSCons()
test.dir_fixture('hash-format')
+ # Expect failure due to an unsupported/invalid algorithm.
+ # The error message however changes if SCons detects that the host system doesn't support one or more algorithms
+ # Primary reason the message changes is so user doesn't have to start with unsupported algorithm A and then attempt
+ # to switch to unsupported algorithm B.
+ # On normal systems (allowed=default) this will output a fixed message, but on FIPS-enabled or other weird systems
+ # that don't have default allowed algorithms, it informs the user of the mismatch _and_ the currently supported
+ # algorithms on the system they're using.
+ # In Python 3.9 this becomes somewhat obselete as the hashlib is informed we don't use hashing for security but
+ # for loose integrity.
if algorithm == INVALID_ALGORITHM:
- # Expect failure due to an unsupported/invalid algorithm.
- test.run('--hash-format=%s .' % algorithm, stderr=r"""
-scons: \*\*\* Hash format "{}" is not supported by SCons. Only the following hash formats are supported: md5, sha1, sha256
+ if ALLOWED_HASH_FORMATS == DEFAULT_HASH_FORMATS:
+ test.run('--hash-format=%s .' % algorithm, stderr=r"""
+scons: \*\*\* Hash format "{}" is not supported by SCons. Only the following hash formats are supported: {}
+File "[^"]+", line \d+, in \S+
+""".format(algorithm, ', '.join(DEFAULT_HASH_FORMATS)), status=2, match=TestSCons.match_re)
+ else:
+ test.run('--hash-format=%s .' % algorithm, stderr=r"""
+scons: \*\*\* Hash format "{}" is not supported by SCons. SCons supports more hash formats than your local system is reporting; SCons supports: {}. Your local system only supports: {}
File "[^"]+", line \d+, in \S+
-""".format(algorithm), status=2, match=TestSCons.match_re)
+""".format(algorithm, ', '.join(DEFAULT_HASH_FORMATS), ', '.join(ALLOWED_HASH_FORMATS)), status=2, match=TestSCons.match_re)
continue
elif algorithm is not None:
- # Skip any algorithm that the Python interpreter doesn't have.
- if hasattr(hashlib, algorithm):
+ if algorithm in ALLOWED_HASH_FORMATS:
expected_dblite = test.workpath('.sconsign_%s.dblite' % algorithm)
test.run('--hash-format=%s .' % algorithm)
else:
- print('Skipping test with --hash-format=%s because that '
- 'algorithm is not available.' % algorithm)
+ test.run('--hash-format=%s' % algorithm, stderr=r"""
+scons: \*\*\* While hash format "{}" is supported by SCons, the local system indicates only the following hash formats are supported by the hashlib library: {}
+File "[^"]+", line \d+, in \S+
+Error in atexit._run_exitfuncs:
+Traceback \(most recent call last\):
+ File "[^"]+", line \d+, in \S+
+ assert csig == '[a-z0-9]+', csig
+AssertionError: [a-z0-9]+
+""".format(algorithm, ', '.join(ALLOWED_HASH_FORMATS)), status=2, match=TestSCons.match_re)
+ continue
else:
# The SConsign file in the hash-format folder has logic to call
# SCons.Util.set_hash_format('sha256') if the default algorithm is
diff --git a/test/option/option-n.py b/test/option/option-n.py
index e647b8ea1..eb8eb62d3 100644
--- a/test/option/option-n.py
+++ b/test/option/option-n.py
@@ -43,6 +43,7 @@ import os
import re
import TestSCons
+from SCons.Util import get_current_hash_algorithm_used
_python_ = TestSCons._python_
@@ -118,7 +119,7 @@ test.fail_test(not os.path.exists(test.workpath('f1.out')))
expect = test.wrap_stdout("""\
%(_python_)s build.py f1.out
""" % locals())
-test.unlink('.sconsign.dblite')
+test.unlink(test.get_sconsignname()+'.dblite')
test.write('f1.out', "X1.out\n")
test.run(arguments='-n f1.out', stdout=expect)
test.run(arguments='-n f1.out', stdout=expect)
@@ -204,13 +205,23 @@ test.run(arguments="-n", stderr=stderr, status=2,
test.fail_test(os.path.exists(test.workpath("configure", "config.test")))
test.fail_test(os.path.exists(test.workpath("configure", "config.log")))
+
+# depending on which default hash function we're using, we'd expect one of the following filenames.
+# The filenames are generated by the conftest changes in #3543 : https://github.com/SCons/scons/pull/3543/files
+possible_filenames = {
+ 'md5': "conftest_b10a8db164e0754105b7a99be72e3fe5_0.in",
+ 'sha1': "conftest_0a4d55a8d778e5022fab701977c5d840bbc486d0_0.in",
+ 'sha256': "conftest_a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e_0.in"
+}
+test_filename = possible_filenames[get_current_hash_algorithm_used()]
+
# test that targets are not built, if conf_dir exists.
# verify that .cache and config.log are not created.
# an error should be raised
stderr = r"""
scons: \*\*\* Cannot update configure test "%s" within a dry-run\.
File \S+, line \S+, in \S+
-""" % re.escape(os.path.join("config.test", "conftest_b10a8db164e0754105b7a99be72e3fe5_0.in"))
+""" % re.escape(os.path.join("config.test", test_filename))
test.subdir(['configure', 'config.test'])
test.run(arguments="-n", stderr=stderr, status=2,
chdir=test.workpath("configure"))
diff --git a/test/question/Configure.py b/test/question/Configure.py
index 7df29f57d..d01d0faf7 100644
--- a/test/question/Configure.py
+++ b/test/question/Configure.py
@@ -36,6 +36,7 @@ import re
import TestCmd
import TestSCons
+from SCons.Util import get_current_hash_algorithm_used
test = TestSCons.TestSCons(match = TestCmd.match_re_dotall)
@@ -80,13 +81,22 @@ test.run(arguments="-q aaa.out",stderr=stderr,status=2)
test.must_not_exist(test.workpath("config.test"))
test.must_not_exist(test.workpath("config.log"))
+# depending on which default hash function we're using, we'd expect one of the following filenames.
+# The filenames are generated by the conftest changes in #3543 : https://github.com/SCons/scons/pull/3543/files
+possible_filenames = {
+ 'md5': "conftest_b10a8db164e0754105b7a99be72e3fe5_0.in",
+ 'sha1': "conftest_0a4d55a8d778e5022fab701977c5d840bbc486d0_0.in",
+ 'sha256': "conftest_a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e_0.in"
+}
+test_filename = possible_filenames[get_current_hash_algorithm_used()]
+
# test that targets are not built, if conf_dir exists.
# verify that .cache and config.log are not created.
# an error should be raised
stderr=r"""
scons: \*\*\* Cannot update configure test "%s" within a dry-run\.
File \S+, line \S+, in \S+
-""" % re.escape(os.path.join("config.test", "conftest_b10a8db164e0754105b7a99be72e3fe5_0.in"))
+""" % re.escape(os.path.join("config.test", test_filename))
test.subdir('config.test')
@@ -94,7 +104,7 @@ test.run(arguments="-q aaa.out",stderr=stderr,status=2)
test.must_not_exist(test.workpath("config.test", ".cache"))
test.must_not_exist(test.workpath("config.test", "conftest_0"))
-test.must_not_exist(test.workpath("config.test", "conftest_b10a8db164e0754105b7a99be72e3fe5_0.in"))
+test.must_not_exist(test.workpath("config.test", test_filename))
test.must_not_exist(test.workpath("config.log"))
# test that no error is raised, if all targets are up-to-date. In this
diff --git a/test/sconsign/corrupt.py b/test/sconsign/corrupt.py
index 25b48e243..61da3a249 100644
--- a/test/sconsign/corrupt.py
+++ b/test/sconsign/corrupt.py
@@ -30,14 +30,19 @@ Test that we get proper warnings when .sconsign* files are corrupt.
import TestSCons
import TestCmd
+import re
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')
+database_filename = test.get_sconsignname() + ".dblite"
+
+# for test1 we're using the default database filename
+work1__sconsign_dblite = test.workpath('work1', database_filename)
+# for test 2 we have an explicit hardcode to .sconsign
+work2_sub__sconsign = test.workpath('work2', 'sub', ".sconsign")
SConstruct_contents = """\
def build1(target, source, env):
@@ -57,9 +62,9 @@ test.write(['work1', 'SConstruct'], SConstruct_contents)
test.write(['work1', 'foo.in'], "work1/foo.in\n")
stderr = r'''
-scons: warning: Ignoring corrupt .sconsign file: \.sconsign\.dblite
+scons: warning: Ignoring corrupt .sconsign file: {}
.*
-'''
+'''.format(re.escape(database_filename))
stdout = test.wrap_stdout(r'build1\(\["sub.foo\.out"\], \["foo\.in"\]\)' + '\n')
diff --git a/test/sconsign/script/Configure.py b/test/sconsign/script/Configure.py
index 7bf1f05e7..02a2c2044 100644
--- a/test/sconsign/script/Configure.py
+++ b/test/sconsign/script/Configure.py
@@ -90,7 +90,9 @@ conftest_%(sig_re)s_0_%(sig_re)s%(_obj)s:
%(CC_file)s: %(sig_re)s \d+ \d+
""" % locals()
-test.run_sconsign(arguments = ".sconsign",
+# grab .sconsign or .sconsign_<hashname>
+database_name=test.get_sconsignname()
+test.run_sconsign(arguments = database_name,
stdout = expect)
test.pass_test()
diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py
index 2a19818ee..29bd1235e 100644
--- a/testing/framework/TestSCons.py
+++ b/testing/framework/TestSCons.py
@@ -45,6 +45,7 @@ from collections import namedtuple
from TestCommon import *
from TestCommon import __all__
+from SCons.Util import get_hash_format, get_current_hash_algorithm_used
from TestCmd import Popen
from TestCmd import PIPE
@@ -719,6 +720,27 @@ class TestSCons(TestCommon):
for p in patterns:
result.extend(sorted(glob.glob(p)))
return result
+
+ def get_sconsignname(self):
+ """Get the scons database name used, and return both the prefix and full filename.
+ if the user left the options defaulted AND the default algorithm set by
+ SCons is md5, then set the database name to be the special default name
+
+ otherwise, if it defaults to something like 'sha1' or the user explicitly
+ set 'md5' as the hash format, set the database name to .sconsign_<algorithm>
+ eg .sconsign_sha1, etc.
+
+ Returns:
+ a pair containing: the current dbname, the dbname.dblite filename
+ """
+ hash_format = get_hash_format()
+ current_hash_algorithm = get_current_hash_algorithm_used()
+ if hash_format is None and current_hash_algorithm == 'md5':
+ return ".sconsign"
+ else:
+ database_prefix=".sconsign_%s" % current_hash_algorithm
+ return database_prefix
+
def unlink_sconsignfile(self, name='.sconsign.dblite'):
"""Delete the sconsign file.