From 3bdcea46577a5ea16cd20d201fe54cdadf5153ee Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 30 Jul 2004 17:00:27 +0000 Subject: Treat file extensions with all digits as part of the base name. (Gary Oberbrunne) --- src/CHANGES.txt | 4 +++ src/RELEASE.txt | 18 +++++++++---- src/engine/SCons/Util.py | 34 +++++++++++++++++++---- src/engine/SCons/UtilTests.py | 15 +++++++++++ test/suffixes.py | 63 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 test/suffixes.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 5042043..11532e6 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -179,6 +179,10 @@ RELEASE 0.96 - XXX - Avoid stack traces when trying to read dangling symlinks. + - Treat file "extensions" that only contain digits as part of the + file basename. This supports version numbers as part of shared + library names, for example. + From Simon Perkins: - Fix a bug introduced in building shared libraries under MinGW. diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 7917a7e..2228a83 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -37,10 +37,10 @@ RELEASE 0.96 - XXX import anydbm SConsignFile('.sconsign_file_name', anydbm) - - The internal format of .sconsign files has been changed. - This may cause warnings about "ignoring corrupt .sconsign files" - and rebuilds when you use SCons 0.96 for the first time in a tre - that was previously gbuilt with SCons 0.95 or earlier. + - The internal format of .sconsign files has been changed. This might + cause warnings about "ignoring corrupt .sconsign files" and rebuilds + when you use SCons 0.96 for the first time in a tree that was + previously built with SCons 0.95 or earlier. - The scan_check function that can be supplied to a custom Scanner now must take two arguments, the Node to be checked and a construction @@ -53,7 +53,6 @@ RELEASE 0.96 - XXX the following scanner, you must now add the suffix to a construction environment through which you plan to call the scanner, as follows: - CScan.add_skey('.x') => env.Append(CPPSUFFIXES = ['.x']) DScan.add_skey('.x') => env.Append(DSUFFIXES = ['.x']) FortranScan.add_skey('.x') => env.Append(FORTRANSUFFIXES = ['.x']) @@ -62,6 +61,15 @@ RELEASE 0.96 - XXX the seperate and more flexible "target_factory" and "source_factory" keywords should be used instead. + - SCons now treats file "extensions" that contain all digits (for + example, "file.123") as part of the file basename, for easier + handling of version numbers in the names of shared libraries + and other files. Builders will now add their file extensions to + file names specified with all-digit extensions. If you need to + generate a file with an all-digit extension using a Builder that + adds a file extension, you can preserve the previous behavior by + wrapping the file name in a File() call. + - The behavior of the env.Append() and env.Prepend() methods has changed when appending a string value to a UserList, or vice versa. They now behave like normal Python addition of a string to diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index e2399a8..998a01b 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -98,15 +98,39 @@ _altsep = os.altsep if _altsep is None and sys.platform == 'win32': # My ActivePython 2.0.1 doesn't set os.altsep! What gives? _altsep = '/' +if _altsep: + def rightmost_separator(path, sep, _altsep=_altsep): + rfind = string.rfind + return max(rfind(path, sep), rfind(path, _altsep)) +else: + rightmost_separator = string.rfind + +# First two from the Python Cookbook, just for completeness. +# (Yeah, yeah, YAGNI...) +def containsAny(str, set): + """Check whether sequence str contains ANY of the items in set.""" + for c in set: + if c in str: return 1 + return 0 + +def containsAll(str, set): + """Check whether sequence str contains ALL of the items in set.""" + for c in set: + if c not in str: return 0 + return 1 + +def containsOnly(str, set): + """Check whether sequence str contains ONLY items in set.""" + for c in str: + if c not in set: return 0 + return 1 def splitext(path): "Same as os.path.splitext() but faster." - if _altsep: - sep = max(string.rfind(path, os.sep), string.rfind(path, _altsep)) - else: - sep = string.rfind(path, os.sep) + sep = rightmost_separator(path, os.sep) dot = string.rfind(path, '.') - if dot > sep: + # An ext is only real if it has at least one non-digit char + if dot > sep and not containsOnly(path[dot:], "0123456789."): return path[:dot],path[dot:] else: return path,"" diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index 713f522..1bce1af 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -1509,6 +1509,21 @@ class UtilTestCase(unittest.TestCase): r = adjustixes('dir/file', 'pre-', '-suf') assert r == os.path.join('dir', 'pre-file-suf'), r + def test_containsAny(self): + """Test the containsAny() function""" + assert containsAny('*.py', '*?[]') + assert not containsAny('file.txt', '*?[]') + + def test_containsAll(self): + """Test the containsAll() function""" + assert containsAll('43221', '123') + assert not containsAll('134', '123') + + def test_containsOnly(self): + """Test the containsOnly() function""" + assert containsOnly('.83', '0123456789.') + assert not containsOnly('43221', '123') + if __name__ == "__main__": suite = unittest.makeSuite(UtilTestCase, 'test_') if not unittest.TextTestRunner().run(suite).wasSuccessful(): diff --git a/test/suffixes.py b/test/suffixes.py new file mode 100644 index 0000000..5b9a413 --- /dev/null +++ b/test/suffixes.py @@ -0,0 +1,63 @@ +#!/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 handling of file suffixes. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +def cat(env, source, target): + target = str(target[0]) + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() +Cat = Builder(action=cat, suffix='.out') +env = Environment(BUILDERS = {'Cat':Cat}) +env.Cat('file1', 'file1.in') +env.Cat('file2.out', 'file2.in') +env.Cat('file3.xyz', 'file3.in') +env.Cat('file4.123', 'file4.in') +""") + +test.write('file1.in', "file1.in\n") +test.write('file2.in', "file2.in\n") +test.write('file3.in', "file3.in\n") +test.write('file4.in', "file4.in\n") + +test.run(arguments = '.') + +test.must_match('file1.out', "file1.in\n") +test.must_match('file2.out', "file2.in\n") +test.must_match('file3.xyz', "file3.in\n") +test.must_match('file4.123.out', "file4.in\n") + +test.pass_test() -- cgit v0.12