From 86544b7799c2b0478835702040b770c23c523b82 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sun, 7 Jan 2007 07:23:05 +0000 Subject: [PATCH] Merged revisions 1738-1754,1756 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1741 | stevenknight | 2006-12-16 22:51:07 -0600 (Sat, 16 Dec 2006) | 1 line 0.96.D527 - Give the f90 and f95 Tool modules knowledge of how to build source files of earlier Fortran versions. ........ r1742 | stevenknight | 2006-12-16 23:22:54 -0600 (Sat, 16 Dec 2006) | 1 line 0.96.D528 - Better handling of timestamp fallback if there's no md5 module. ........ r1743 | stevenknight | 2006-12-17 00:21:31 -0600 (Sun, 17 Dec 2006) | 1 line 0.96.D529 - Fix portability of new tests on systems that don't have TeX installed. ........ r1744 | stevenknight | 2006-12-19 15:30:16 -0600 (Tue, 19 Dec 2006) | 1 line 0.96.D530 - Eliminate the ListBuilder subclass in favor of using the Executor's target lists. ........ r1745 | stevenknight | 2006-12-19 18:54:26 -0600 (Tue, 19 Dec 2006) | 1 line 0.96.D531 - Eliminate of MultiStepBuilder as a separate Builder subclass. ........ r1746 | garyo | 2006-12-21 13:21:08 -0600 (Thu, 21 Dec 2006) | 1 line Minor doc fix, thanks to Douglas Landgraf. ........ r1747 | stevenknight | 2006-12-21 17:13:55 -0600 (Thu, 21 Dec 2006) | 1 line 0.96.D533 - Add CFLAGS for options common to C/C++. (Gary Oberbrunner) ........ r1748 | stevenknight | 2007-01-03 19:48:05 -0600 (Wed, 03 Jan 2007) | 1 line 0.96.D534 - Fix signature storage when targets are retrieved from CacheDir(). ........ r1749 | stevenknight | 2007-01-04 16:48:47 -0600 (Thu, 04 Jan 2007) | 1 line 0.96.D535 - Teach the lex and yacc tools about target files generated by different flex/bison options, and about Objective C suffixes. (Pupeno) ........ r1750 | stevenknight | 2007-01-04 17:14:38 -0600 (Thu, 04 Jan 2007) | 1 line 0.96.D536 - Refactor duplicate disambiguation logic in Entry.get_contents(). ........ r1751 | stevenknight | 2007-01-05 13:00:54 -0600 (Fri, 05 Jan 2007) | 1 line 0.96.D537 - Fix lprof regression from 0.96.92. ........ r1752 | stevenknight | 2007-01-05 20:43:48 -0600 (Fri, 05 Jan 2007) | 1 line 0.96.D538 - Fix caching of Builder suffix matching (to fix lprof regression). ........ r1753 | stevenknight | 2007-01-06 00:03:16 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D539 - Fix --include-dir when using MinGW. (Paul) ........ r1754 | stevenknight | 2007-01-06 00:24:53 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D540 - Make bootstrap.py something useful to execute SCons out of a source directory. ........ r1756 | stevenknight | 2007-01-06 21:32:11 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D541 - Update the Copyright year string to include 2007. Automate updating the month+year string in man page title headers. Fix hard-coded __revision__ strings that crept into some older tests. ........ git-svn-id: http://scons.tigris.org/svn/scons/trunk@1757 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- QMTest/TestSCons_time.py | 4 +- README | 43 +- SConstruct | 7 +- bootstrap.py | 139 ++++-- doc/man/scons-time.1 | 4 +- doc/man/scons.1 | 5 +- doc/man/sconsign.1 | 2 +- doc/user/copyright.in | 2 +- doc/user/copyright.sgml | 2 +- doc/user/main.in | 4 +- doc/user/main.sgml | 4 +- doc/user/nodes.in | 2 +- doc/user/nodes.sgml | 2 +- src/CHANGES.txt | 32 ++ src/engine/SCons/Builder.py | 301 +++++-------- src/engine/SCons/BuilderTests.py | 62 ++- src/engine/SCons/Environment.py | 15 +- src/engine/SCons/EnvironmentTests.py | 3 + src/engine/SCons/Node/FS.py | 55 ++- src/engine/SCons/Node/FSTests.py | 19 +- src/engine/SCons/Node/__init__.py | 2 +- src/engine/SCons/SConfTests.py | 6 + src/engine/SCons/Script/Main.py | 1 + src/engine/SCons/Taskmaster.py | 10 +- src/engine/SCons/TaskmasterTests.py | 7 + src/engine/SCons/Tool/bcc32.py | 6 +- src/engine/SCons/Tool/cc.py | 6 +- src/engine/SCons/Tool/cc.xml | 26 +- src/engine/SCons/Tool/f90.py | 4 + src/engine/SCons/Tool/f95.py | 7 + src/engine/SCons/Tool/icc.py | 2 +- src/engine/SCons/Tool/lex.py | 53 ++- src/engine/SCons/Tool/mingw.py | 2 +- src/engine/SCons/Tool/msvc.py | 6 +- src/engine/SCons/Tool/mwcc.py | 5 +- src/engine/SCons/Tool/qt.py | 4 +- src/engine/SCons/Tool/yacc.py | 53 ++- src/engine/SCons/Tool/yacc.xml | 17 + src/engine/SCons/Warnings.py | 3 + src/script/scons-time.py | 2 + src/test_copyrights.py | 265 +++++++----- test/Builder/multi/different-actions.py | 60 +++ test/Builder/multi/different-environments.py | 64 +++ test/Builder/multi/different-multi.py | 64 +++ test/Builder/multi/different-order.py | 59 +++ test/Builder/multi/different-overrides.py | 58 +++ test/Builder/multi/different-target-lists.py | 64 +++ test/Builder/multi/error.py | 57 +++ test/Builder/multi/lone-target-list.py | 59 +++ test/Builder/multi/multi.py | 55 +++ test/Builder/multi/same-actions.py | 61 +++ test/Builder/multi/same-overrides.py | 71 ++++ test/Builder/multi/same-targets.py | 57 +++ test/Builder/non-multi.py | 54 +++ test/CC/CFLAGS.py | 113 +++++ test/CC/SHCFLAGS.py | 131 ++++++ test/CacheDir/up-to-date-q.py | 82 ++++ test/LEX/LEX.py | 91 +--- test/LEX/LEXFLAGS.py | 63 +-- test/LEX/live.py | 103 +++++ test/TEX/build_dir.py | 7 +- test/TEX/subdir-input.py | 13 +- test/YACC/YACC.py | 154 +------ test/YACC/YACCFLAGS.py | 76 +--- test/YACC/YACCVCGFILESUFFIX.py | 87 ++++ test/YACC/live.py | 165 ++++++++ test/ignore-command.py | 4 +- test/multi.py | 418 ------------------- test/option-v.py | 2 +- test/silent-command.py | 4 +- test/timestamp-fallback.py | 3 +- 71 files changed, 2243 insertions(+), 1250 deletions(-) create mode 100644 test/Builder/multi/different-actions.py create mode 100644 test/Builder/multi/different-environments.py create mode 100644 test/Builder/multi/different-multi.py create mode 100644 test/Builder/multi/different-order.py create mode 100644 test/Builder/multi/different-overrides.py create mode 100644 test/Builder/multi/different-target-lists.py create mode 100644 test/Builder/multi/error.py create mode 100644 test/Builder/multi/lone-target-list.py create mode 100644 test/Builder/multi/multi.py create mode 100644 test/Builder/multi/same-actions.py create mode 100644 test/Builder/multi/same-overrides.py create mode 100644 test/Builder/multi/same-targets.py create mode 100644 test/Builder/non-multi.py create mode 100644 test/CC/CFLAGS.py create mode 100644 test/CC/SHCFLAGS.py create mode 100644 test/CacheDir/up-to-date-q.py create mode 100644 test/LEX/live.py create mode 100644 test/YACC/YACCVCGFILESUFFIX.py create mode 100644 test/YACC/live.py delete mode 100644 test/multi.py diff --git a/QMTest/TestSCons_time.py b/QMTest/TestSCons_time.py index 90eb5a8e..34caa062 100644 --- a/QMTest/TestSCons_time.py +++ b/QMTest/TestSCons_time.py @@ -11,9 +11,9 @@ from those classes, as well as any overridden or additional methods or attributes defined in this subclass. """ -# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation +# __COPYRIGHT__ -__revision__ = "QMTest/TestSCons_time.py 0.96.C629 2006/11/19 06:39:17 knight" +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path diff --git a/README b/README index 7d5c84b5..518de06e 100644 --- a/README +++ b/README @@ -122,13 +122,21 @@ system, populate the build/scons/ directory by running: $ scons build/scons -If you don't have SCons version 0.96 or later already installed on your -system, you can use SCons itself to populate the build/scons/ directory -with a little more typing. You must first set the SCONS_LIB_DIR -environment variable to the local src/engine subdirectory, and then -execute the local src/script/scons.py script to populate the build/scons/ -subdirectory. You would do this as follows on a Linux or UNIX system -(using sh or a derivative like bash or ksh): +You can also use this version of SCons to populate its own build directory +by using a supplied bootstrap.py script: + + $ python bootstrap.py build/scons + +The bootstrap.py keeps the src/ subdirectory free of compiled Python +(*.pyc or *.pyo) files by copying the necessary SCons files to a local +bootstrap/ subdirectory and executing it from there. + +You can also execute the local SCons directly from the src/ subdirectory +by first setting the SCONS_LIB_DIR environment variable to the local +src/engine subdirectory, and then execute the local src/script/scons.py +script to populate the build/scons/ subdirectory. You would do this as +follows on a Linux or UNIX system (using sh or a derivative like bash +or ksh): $ export SCONS_LIB_DIR=`pwd`/src/engine $ python src/script/scons.py build/scons @@ -136,11 +144,11 @@ subdirectory. You would do this as follows on a Linux or UNIX system Or as follows on Windows: C:\scons\>set SCONS_LIB_DIR=%cd%\src\engine - C:\scons\>python src/script/scons.py build/scons + C:\scons\>python src\script\scons.py build/scons -Either command will populate the build/scons/ directory with the necessary -files and directory structure to use the Python-standard setup script -as follows on Linux or UNIX: +Any of the above commands will populate the build/scons/ directory with +the necessary files and directory structure to use the Python-standard +setup script as follows on Linux or UNIX: # cd build/scons # python setup.py install @@ -238,9 +246,14 @@ modules that make up SCons. The src/script/scons.py wrapper script exists mainly to find the appropriate build engine library and then execute it. In order to make your own change locally and test them by hand, simply -edit modules in the local src/engine/SCons and set the SCONS_LIB_DIR -to point to that directory. Here is one way you can set up environment -variables to do this on a UNIX or Linux system: +edit modules in the local src/engine/SCons subdirectory tree and +either use the local bootstrap.py script: + + $ python bootstrap.py [arguments] + +Or set the SCONS_LIB_DIR to point to the src/engine/SCons directory and +then execute the src/script/scons.py script. Here is one way you can +set up environment variables to do this on a UNIX or Linux system: $ setenv MYSCONS=`pwd`/src $ setenv SCONS_LIB_DIR=$MYSCONS @@ -258,7 +271,7 @@ if the SCons configuration for your project seems to be blocked by an SCons bug, and you want to see if a patch you make actually fixes that bug): - $ python $MYSCONS/script/scons.py -C /some/other/location [arguments] + $ python bootstrap.py -C /some/other/location [arguments] Lastly, if you want to be able to just execute your modified version of SCons from the command line, you can make it executable and add its diff --git a/SConstruct b/SConstruct index 811ed4a1..a61466af 100644 --- a/SConstruct +++ b/SConstruct @@ -6,7 +6,10 @@ # When this gets changed, you also need to change test/option-v.py # so it looks for the right string. -copyright_years = '2001, 2002, 2003, 2004, 2005, 2006' +copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007' + +# This gets inserted into the man pages to reflect the month of release. +month_year = 'January 2007' # # __COPYRIGHT__ @@ -252,6 +255,7 @@ def SCons_revision(target, source, env): contents = string.replace(contents, '__DATE' + '__', env['DATE']) contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER']) contents = string.replace(contents, '__FILE' + '__', str(source[0])) + contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR']) contents = string.replace(contents, '__REVISION' + '__', env['REVISION']) contents = string.replace(contents, '__VERSION' + '__', env['VERSION']) contents = string.replace(contents, '__NULL' + '__', '') @@ -305,6 +309,7 @@ env = Environment( COPYRIGHT = copyright, DATE = date, DEVELOPER = developer, + MONTH_YEAR = month_year, REVISION = revision, VERSION = version, DH_COMPAT = 2, diff --git a/bootstrap.py b/bootstrap.py index c6a7db3a..9a88c1c8 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -1,20 +1,3 @@ -"""bootstrap.py - -This is an Aegis-to-SCons build script that collects a copy of the -current SCons into a bootstrap/ subdirectory and then executes it with -the supplied command-line options. - -Right now, it only understands the SCons -Y option, which is the only -one currently used. It collects the repositories specified by -Y and -searches them, in order, for the pieces of SCons to copy into the local -bootstrap/ subdirectory. - -This is essentially a minimal build of SCons to bootstrap ourselves into -executing it for the full build of all the packages, as specified in our -local SConstruct file. - -""" - # # __COPYRIGHT__ # @@ -44,13 +27,97 @@ import getopt import string import sys +__doc__ = """bootstrap.py + +This script supports "bootstrap" execution of the current SCons in +this local source tree by copying of all necessary Python scripts and +modules from underneath the src/ subdirectory into a subdirectory (named +"bootstrap/" by default), and then executing the copied SCons with the +supplied command-line arguments. + +There are a handful of options that are specific to this bootstrap.py +script and which are *not* passed on to the underlying SCons script. +All of these begin with the string "bootstrap_": + + --bootstrap_dir=DIR + + Sets the name of the directory into which the SCons files will + be copied. The default is "bootstrap" in the local subdirectory. + + --bootstrap_force + + Forces a copy of all necessary files. By default, the + bootstrap.py script only updates the bootstrap copy if the + content of the source copy is different. + + --bootstrap_update + + Only updates the bootstrap subdirectory, and then exits. + +In addition to the above options, the bootstrap.py script understands +the -Y and --repository= options, which are used under Aegis to specify +a search path for the source files that may not have been copied in to +the Aegis change. + +This is essentially a minimal build of SCons to bootstrap ourselves into +executing it for the full build of all the packages, as specified in our +local SConstruct file. +""" + +bootstrap_dir = 'bootstrap' +pass_through_args = [] search = ['.'] +update_only = None + +requires_an_argument = 'bootstrap.py: %s requires an argument\n' + +command_line_args = sys.argv[1:] + +def must_copy(dst, src): + if not os.path.exists(dst): + return 1 + return open(dst, 'rb').read() != open(src, 'rb').read() -opts, args = getopt.getopt(sys.argv[1:], "Y:", []) +while command_line_args: + arg = command_line_args.pop(0) -for o, a in opts: - if o == '-Y': - search.append(a) + if arg == '--bootstrap_dir': + try: + bootstrap_dir = command_line_args.pop(0) + except IndexError: + sys.stderr.write(requires_an_argument % arg) + sys.exit(1) + + elif arg[:16] == '--bootstrap_dir=': + bootstrap_dir = arg[16:] + + elif arg == '--bootstrap_force': + def must_copy(dst, src): + return 1 + + elif arg == '--bootstrap_update': + update_only = 1 + + elif arg in ('-Y', '--repository'): + try: + dir = command_line_args.pop(0) + except IndexError: + sys.stderr.write(requires_an_argument % arg) + sys.exit(1) + else: + search.append(dir) + pass_through_args.extend([arg, dir]) + + elif arg[:2] == '-Y': + search.append(arg[2:]) + pass_through_args.append(arg) + + elif arg[:13] == '--repository=': + search.append(arg[13:]) + pass_through_args.append(arg) + + else: + pass_through_args.append(arg) def find(file, search=search): for dir in search: @@ -68,24 +135,28 @@ MANIFEST_in = find(os.path.join(src_engine, 'MANIFEST.in')) files = [ scons_py ] + map(lambda x: os.path.join(src_engine, x[:-1]), open(MANIFEST_in).readlines()) -subdir = 'bootstrap' - for file in files: src = find(file) - dst = os.path.join(subdir, file) - dir, _ = os.path.split(dst) - if not os.path.isdir(dir): - os.makedirs(dir) - contents = open(src, 'rb').read() - try: os.unlink(dst) - except: pass - open(dst, 'wb').write(contents) - -args = [ sys.executable, os.path.join(subdir, scons_py) ] + sys.argv[1:] + dst = os.path.join(bootstrap_dir, file) + if must_copy(dst, src): + dir = os.path.split(dst)[0] + if not os.path.isdir(dir): + os.makedirs(dir) + try: os.unlink(dst) + except: pass + open(dst, 'wb').write( open(src, 'rb').read() ) + +if update_only: + sys.exit(0) + +args = [ + os.path.split(sys.executable)[1], + os.path.join(bootstrap_dir, scons_py) + ] + pass_through_args sys.stdout.write(string.join(args, " ") + '\n') sys.stdout.flush() -os.environ['SCONS_LIB_DIR'] = os.path.join(subdir, src_engine) +os.environ['SCONS_LIB_DIR'] = os.path.join(bootstrap_dir, src_engine) os.execve(sys.executable, args, os.environ) diff --git a/doc/man/scons-time.1 b/doc/man/scons-time.1 index 50f490fa..b2de0029 100644 --- a/doc/man/scons-time.1 +++ b/doc/man/scons-time.1 @@ -19,7 +19,7 @@ .\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION .\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .\" -.\" doc/man/scons-time.1 0.96.C629 2006/11/18 11:50:43 knight +.\" __FILE__ __REVISION__ __DATE__ __DEVELOPER__ .\" .\" ES - Example Start - indents and turns off line fill .de ES @@ -98,7 +98,7 @@ [\fB--which=\fIWHICH\fR] [\fIARGUMENTS\fR] .. -.TH SCONS-TIME 1 "November 2006" +.TH SCONS-TIME 1 "__MONTH_YEAR__" .SH NAME scons-time \- generate and display SCons timing information '\"========================================================================== diff --git a/doc/man/scons.1 b/doc/man/scons.1 index d45333b1..f4bef97c 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -31,7 +31,7 @@ .fi .RE .. -.TH SCONS 1 "December 2006" +.TH SCONS 1 "__MONTH_YEAR__" .SH NAME scons \- a software construction tool .SH SYNOPSIS @@ -639,7 +639,7 @@ Output looks something like this: .ES $ scons --debug=presub Building myprog.o with action(s): - $SHCC $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + $SHCC $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES ... .EE @@ -3149,6 +3149,7 @@ and added to the following construction variables: -mno-cygwin CCFLAGS, LINKFLAGS -mwindows LINKFLAGS -pthread CCFLAGS, LINKFLAGS +-std= CFLAGS -Wa, ASFLAGS, CCFLAGS -Wl,-rpath= RPATH -Wl,-R, RPATH diff --git a/doc/man/sconsign.1 b/doc/man/sconsign.1 index 7fc32ec2..3d01cf4e 100644 --- a/doc/man/sconsign.1 +++ b/doc/man/sconsign.1 @@ -31,7 +31,7 @@ .RE .fi .. -.TH SCONSIGN 1 "August 2004" +.TH SCONSIGN 1 "__MONTH_YEAR__" .SH NAME sconsign \- print SCons .sconsign file information .SH SYNOPSIS diff --git a/doc/user/copyright.in b/doc/user/copyright.in index 119f7494..76e3e50f 100644 --- a/doc/user/copyright.in +++ b/doc/user/copyright.in @@ -26,7 +26,7 @@
- SCons User's Guide Copyright (c) 2004 Steven Knight + SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
diff --git a/doc/user/copyright.sgml b/doc/user/copyright.sgml index 119f7494..76e3e50f 100644 --- a/doc/user/copyright.sgml +++ b/doc/user/copyright.sgml @@ -26,7 +26,7 @@
- SCons User's Guide Copyright (c) 2004 Steven Knight + SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
diff --git a/doc/user/main.in b/doc/user/main.in index af3b0eed..be302d7e 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -101,10 +101,10 @@ Revision &buildrevision; (&builddate;) - 2004 + 2004, 2005, 2006, 2007 - 2004 + 2004, 2005, 2006, 2007 Steven Knight diff --git a/doc/user/main.sgml b/doc/user/main.sgml index af3b0eed..be302d7e 100644 --- a/doc/user/main.sgml +++ b/doc/user/main.sgml @@ -101,10 +101,10 @@ Revision &buildrevision; (&builddate;) - 2004 + 2004, 2005, 2006, 2007 - 2004 + 2004, 2005, 2006, 2007 Steven Knight diff --git a/doc/user/nodes.in b/doc/user/nodes.in index a5d05dcb..f5faf5ae 100644 --- a/doc/user/nodes.in +++ b/doc/user/nodes.in @@ -296,7 +296,7 @@ import os.path program_list = Program('hello.c') program_name = str(program_list[0]) - if not os.path.exists(program_name) + if not os.path.exists(program_name): print program_name, "does not exist!" diff --git a/doc/user/nodes.sgml b/doc/user/nodes.sgml index b6bcb899..114e9e0a 100644 --- a/doc/user/nodes.sgml +++ b/doc/user/nodes.sgml @@ -300,7 +300,7 @@ import os.path program_list = Program('hello.c') program_name = str(program_list[0]) - if not os.path.exists(program_name) + if not os.path.exists(program_name): print program_name, "does not exist!" diff --git a/src/CHANGES.txt b/src/CHANGES.txt index e7f49370..3678dd18 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -14,6 +14,11 @@ RELEASE 0.97 - XXX - Allow arbitrary white space after a SWIG %module declaration. + From Paul: + + - When compiling resources under MinGW, make sure there's a space + between the --include-dir option and its argument. + From Jay Kint: - Alleviate long command line issues on Windows by executing command @@ -65,6 +70,19 @@ RELEASE 0.97 - XXX files control over what exceptions cause a string to expand to '' vs. terminating processing with an error. + - Allow the f90.py and f95.py Tool modules to compile earlier source + source files of earlier Fortran version. + + - Fix storing signatures of files retrieved from CacheDir() so they're + correctly identified as up-to-date next invocation. + + - Make sure lists of computed source suffixes cached by Builder objects + don't persist across changes to the list of source Builders (so the + addition of suffixes like .ui by the qt.py Tool module take effect). + + - Enhance the bootstrap.py script to allow it to be used to execute + SCons more easily from a checked-out source tree. + From Ben Leslie: - Fix post-Memoizer value caching misspellings in Node.FS._doLookup(). @@ -93,6 +111,9 @@ RELEASE 0.97 - XXX - Eliminate some unnecessary os.path.normpath() calls. + - Add a $CFLAGS variable for C-specific options, leaving $CCFLAGS + for options common to C and C++. + From Tom Parker: - Have the error message print the missing file that Qt can't find. @@ -107,6 +128,17 @@ RELEASE 0.97 - XXX specified on the command line (and not intuited from the old way of calling it with just ".sconsign"). + From Jose Pablo Ezequiel "Pupeno" Fernandez Silva: + + - Give the 'lex' tool knowledge of the additional target files produced + by the flex "--header-file=" and "--tables-file=" options. + + - Give the 'yacc' tool knowledge of the additional target files produced + by the bison "-g", "--defines=" and "--graph=" options. + + - Generate intermediate files with Objective C file suffixes (.m) when + the lex and yacc source files have appropriate suffixes (.lm and .ym). + From Sohail Somain: - Have the mslink.py Tool only look for a 'link' executable on Windows diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index d5f566a3..d625ed7a 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -16,20 +16,9 @@ building new types of files in their configurations, without having to dive any deeper into this subsystem. The base class here is BuilderBase. This is a concrete base class which -does, in fact, represent most Builder objects that we (or users) create. +does, in fact, represent the Builder objects that we (or users) create. -There is (at present) one subclasses: - - MultiStepBuilder - - This is a Builder that knows how to "chain" Builders so that - users can specify a source file that requires multiple steps - to turn into a target file. A canonical example is building a - program from yacc input file, which requires invoking a builder - to turn the .y into a .c, the .c into a .o, and the .o into an - executable program. - -There is also two proxies that look like Builders: +There is also a proxy that looks like a Builder: CompositeBuilder @@ -39,11 +28,6 @@ There is also two proxies that look like Builders: (compilers, compile options) for different flavors of source files. - ListBuilder - - This proxies for a Builder *invocation* where the target - is a list of files, not a single file. - Builders and their proxies have the following public interface methods used by other modules: @@ -227,10 +211,9 @@ class OverrideWarner(UserDict.UserDict): """A class for warning about keyword arguments that we use as overrides in a Builder call. - This class exists to handle the fact that a single MultiStepBuilder - call can actually invoke multiple builders as a result of a single - user-level Builder call. This class only emits the warnings once, - no matter how many Builders are invoked. + This class exists to handle the fact that a single Builder call + can actually invoke multiple builders. This class only emits the + warnings once, no matter how many Builders are invoked. """ def __init__(self, dict): UserDict.UserDict.__init__(self, dict) @@ -240,13 +223,10 @@ class OverrideWarner(UserDict.UserDict): if self.already_warned: return for k in self.keys(): - try: + if misleading_keywords.has_key(k): alt = misleading_keywords[k] - except KeyError: - pass - else: - SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, - "Did you mean to use `%s' instead of `%s'?" % (alt, k)) + msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) + SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) self.already_warned = 1 def Builder(**kw): @@ -284,15 +264,12 @@ def Builder(**kw): elif SCons.Util.is_List(emitter): kw['emitter'] = ListEmitter(emitter) - if kw.has_key('src_builder'): - ret = apply(MultiStepBuilder, (), kw) - else: - ret = apply(BuilderBase, (), kw) + result = apply(BuilderBase, (), kw) if not composite is None: - ret = CompositeBuilder(ret, composite) + result = CompositeBuilder(result, composite) - return ret + return result def _node_errors(builder, env, tlist, slist): """Validate that the lists of target and source nodes are @@ -304,7 +281,7 @@ def _node_errors(builder, env, tlist, slist): # were specified. for t in tlist: if t.side_effect: - raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) + raise UserError, "Multiple ways to build the same target were specified for: %s" % t if t.has_explicit_builder(): if not t.env is None and not t.env is env: action = t.builder.action @@ -312,22 +289,21 @@ def _node_errors(builder, env, tlist, slist): contents = action.get_contents(tlist, slist, env) if t_contents == contents: - SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, - "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), action.genstring(tlist, slist, t.env))) - + msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) + SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) else: - raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t) - + msg = "Two environments with different actions were specified for the same target: %s" % t + raise UserError, msg if builder.multi: if t.builder != builder: - if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder: - raise UserError, "Two different target sets have a target in common: %s"%str(t) - else: - raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.get_name(env), builder.get_name(env), str(t)) - elif isinstance(t.builder, ListBuilder) ^ isinstance(builder, ListBuilder): - raise UserError, "Cannot build same target `%s' as singular and list"%str(t) + msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) + raise UserError, msg + if t.get_executor().targets != tlist: + msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, map(str, t.get_executor().targets), map(str, tlist)) + raise UserError, msg elif t.sources != slist: - raise UserError, "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (str(t), map(str,t.sources), map(str,slist)) + msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist)) + raise UserError, msg if builder.single_source: if len(slist) > 1: @@ -388,6 +364,7 @@ class BuilderBase: name = None, chdir = _null, is_explicit = 1, + src_builder = [], **overrides): if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') self._memo = {} @@ -432,6 +409,10 @@ class BuilderBase: self.executor_kw['chdir'] = chdir self.is_explicit = is_explicit + if not SCons.Util.is_List(src_builder): + src_builder = [ src_builder ] + self.src_builder = src_builder + def __nonzero__(self): raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead" @@ -557,6 +538,9 @@ class BuilderBase: def _execute(self, env, target, source, overwarn={}, executor_kw={}): # We now assume that target and source are lists or None. + if self.src_builder: + source = self.src_builder_sources(env, source, overwarn) + if self.single_source and len(source) > 1 and target is None: result = [] if target is None: target = [None]*len(source) @@ -570,31 +554,26 @@ class BuilderBase: tlist, slist = self._create_nodes(env, target, source) - if len(tlist) == 1: - builder = self - else: - builder = ListBuilder(self, env, tlist) - # Check for errors with the specified target/source lists. - _node_errors(builder, env, tlist, slist) + _node_errors(self, env, tlist, slist) # The targets are fine, so find or make the appropriate Executor to # build this particular list of targets from this particular list of # sources. - if builder.multi: - get_executor = builder.get_multi_executor + if self.multi: + get_executor = self.get_multi_executor else: - get_executor = builder.get_single_executor + get_executor = self.get_single_executor executor = get_executor(env, tlist, slist, executor_kw) # Now set up the relevant information in the target Nodes themselves. for t in tlist: t.cwd = env.fs.getcwd() - t.builder_set(builder) + t.builder_set(self) t.env_set(env) t.add_source(slist) t.set_executor(executor) - t.set_explicit(builder.is_explicit) + t.set_explicit(self.is_explicit) return SCons.Node.NodeList(tlist) @@ -650,35 +629,6 @@ class BuilderBase: suffix = suffix(env, sources) return env.subst(suffix) - def _src_suffixes_key(self, env): - return id(env) - - memoizer_counters.append(SCons.Memoize.CountDict('src_suffixes', _src_suffixes_key)) - - def src_suffixes(self, env): - """ - Returns the list of source suffixes for this Builder. - - The suffix list may contain construction variable expansions, - so we have to evaluate the individual strings. To avoid doing - this over and over, we memoize the results for each construction - environment. - """ - memo_key = id(env) - try: - memo_dict = self._memo['src_suffixes'] - except KeyError: - memo_dict = {} - self._memo['src_suffixes'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - result = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix) - memo_dict[memo_key] = result - return result - def set_src_suffix(self, src_suffix): if not src_suffix: src_suffix = [] @@ -712,72 +662,15 @@ class BuilderBase: """ self.emitter[suffix] = emitter - - -class ListBuilder(SCons.Util.Proxy): - """A Proxy to support building an array of targets (for example, - foo.o and foo.h from foo.y) from a single Action execution. - """ - - def __init__(self, builder, env, tlist): - if __debug__: logInstanceCreation(self, 'Builder.ListBuilder') - SCons.Util.Proxy.__init__(self, builder) - self.builder = builder - self.target_scanner = builder.target_scanner - self.source_scanner = builder.source_scanner - self.env = env - self.tlist = tlist - self.multi = builder.multi - self.single_source = builder.single_source - - def targets(self, node): - """Return the list of targets for this builder instance. + def add_src_builder(self, builder): """ - return self.tlist - - def get_name(self, env): - """Attempts to get the name of the Builder.""" - - return "ListBuilder(%s)" % self.builder.get_name(env) - -class MultiStepBuilder(BuilderBase): - """This is a builder subclass that can build targets in - multiple steps. The src_builder parameter to the constructor - accepts a builder that is called to build sources supplied to - this builder. The targets of that first build then become - the sources of this builder. - - If this builder has a src_suffix supplied, then the src_builder - builder is NOT invoked if the suffix of a source file matches - src_suffix. - """ - - memoizer_counters = [] - - def __init__(self, src_builder, - action = None, - prefix = '', - suffix = '', - src_suffix = '', - target_factory = None, - source_factory = None, - target_scanner = None, - source_scanner = None, - emitter=None, - single_source=0): - if __debug__: logInstanceCreation(self, 'Builder.MultiStepBuilder') - BuilderBase.__init__(self, action, prefix, suffix, src_suffix, - target_factory, source_factory, - target_scanner, source_scanner, emitter, - single_source = single_source) - if not SCons.Util.is_List(src_builder): - src_builder = [ src_builder ] - self.src_builder = src_builder - - def _get_sdict_key(self, env): - return id(env) + Add a new Builder to the list of src_builders. - memoizer_counters.append(SCons.Memoize.CountDict('_get_sdict', _get_sdict_key)) + This requires wiping out cached values so that the computed + lists of source suffixes get re-calculated. + """ + self._memo = {} + self.src_builder.append(builder) def _get_sdict(self, env): """ @@ -788,35 +681,26 @@ class MultiStepBuilder(BuilderBase): This dictionary is used for each target specified, so we save a lot of extra computation by memoizing it for each construction environment. + + Note that this is re-computed each time, not cached, because there + might be changes to one of our source Builders (or one of their + source Builders, and so on, and so on...) that we can't "see." + + The underlying methods we call cache their computed values, + though, so we hope repeatedly aggregating them into a dictionary + like this won't be too big a hit. We may need to look for a + better way to do this if performance data show this has turned + into a significant bottleneck. """ - memo_key = id(env) - try: - memo_dict = self._memo['_get_sdict'] - except KeyError: - memo_dict = {} - self._memo['_get_sdict'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass sdict = {} - for bld in self.src_builder: - if SCons.Util.is_String(bld): - try: - bld = env['BUILDERS'][bld] - except KeyError: - continue + for bld in self.get_src_builders(env): for suf in bld.src_suffixes(env): sdict[suf] = bld - memo_dict[memo_key] = sdict return sdict - def _execute(self, env, target, source, overwarn={}, executor_kw={}): - # We now assume that target and source are lists or None. + def src_builder_sources(self, env, source, overwarn={}): source_factory = env.get_factory(self.source_factory) slist = env.arg2nodes(source, source_factory) - final_sources = [] sdict = self._get_sdict(env) @@ -834,13 +718,15 @@ class MultiStepBuilder(BuilderBase): return suf return None + result = [] + for snode in slist: match_suffix = match_src_suffix(snode) if match_suffix: try: bld = sdict[match_suffix] except KeyError: - final_sources.append(snode) + result.append(snode) else: tlist = bld._execute(env, None, [snode], overwarn) # If the subsidiary Builder returned more than one @@ -848,39 +734,56 @@ class MultiStepBuilder(BuilderBase): # Builder isn't capable of building. if len(tlist) > 1: tlist = filter(match_src_suffix, tlist) - final_sources.extend(tlist) + result.extend(tlist) else: - final_sources.append(snode) + result.append(snode) - return BuilderBase._execute(self, env, target, final_sources, overwarn) + return result + + def _get_src_builders_key(self, env): + return id(env) + + memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) def get_src_builders(self, env): - """Return all the src_builders for this Builder. + """ + Returns the list of source Builders for this Builder. - This is essentially a recursive descent of the src_builder "tree." + This exists mainly to look up Builders referenced as + strings in the 'BUILDER' variable of the construction + environment and cache the result. """ - ret = [] + memo_key = id(env) + try: + memo_dict = self._memo['get_src_builders'] + except KeyError: + memo_dict = {} + self._memo['get_src_builders'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + builders = [] for bld in self.src_builder: if SCons.Util.is_String(bld): - # All Environments should have a BUILDERS - # variable, so no need to check for it. try: bld = env['BUILDERS'][bld] except KeyError: continue - ret.append(bld) - return ret + builders.append(bld) + + memo_dict[memo_key] = builders + return builders - def _src_suffixes_key(self, env): + def _subst_src_suffixes_key(self, env): return id(env) - memoizer_counters.append(SCons.Memoize.CountDict('src_suffixes', _src_suffixes_key)) + memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) - def src_suffixes(self, env): + def subst_src_suffixes(self, env): """ - Returns the list of source suffixes for all src_builders of this - Builder. - The suffix list may contain construction variable expansions, so we have to evaluate the individual strings. To avoid doing this over and over, we memoize the results for each construction @@ -888,19 +791,31 @@ class MultiStepBuilder(BuilderBase): """ memo_key = id(env) try: - memo_dict = self._memo['src_suffixes'] + memo_dict = self._memo['subst_src_suffixes'] except KeyError: memo_dict = {} - self._memo['src_suffixes'] = memo_dict + self._memo['subst_src_suffixes'] = memo_dict else: try: return memo_dict[memo_key] except KeyError: pass - suffixes = BuilderBase.src_suffixes(self, env) + suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix) + memo_dict[memo_key] = suffixes + return suffixes + + def src_suffixes(self, env): + """ + Returns the list of source suffixes for all src_builders of this + Builder. + + This is essentially a recursive descent of the src_builder "tree." + (This value isn't cached because there may be changes in a + src_builder many levels deep that we can't see.) + """ + suffixes = self.subst_src_suffixes(env) for builder in self.get_src_builders(env): suffixes.extend(builder.src_suffixes(env)) - memo_dict[memo_key] = suffixes return suffixes class CompositeBuilder(SCons.Util.Proxy): diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 4e196e26..acf07220 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -147,6 +147,8 @@ class Environment: class MyAction: def __init__(self, action): self.action = action + def __call__(self, *args, **kw): + pass def get_executor(self, env, overrides, tlist, slist, executor_kw): return ['executor'] + [self.action] @@ -716,8 +718,8 @@ class BuilderTestCase(unittest.TestCase): assert 0 - def test_ListBuilder(self): - """Testing ListBuilder class.""" + def test_lists(self): + """Testing handling lists of targets and source""" def function2(target, source, env, tlist = [outfile, outfile2], **kw): for t in target: open(str(t), 'w').write("function2\n") @@ -770,15 +772,17 @@ class BuilderTestCase(unittest.TestCase): assert os.path.exists(test.workpath('sub1')) assert os.path.exists(test.workpath('sub2')) - def test_MultiStepBuilder(self): - """Testing MultiStepBuilder class.""" + def test_src_builder(self): + """Testing Builders with src_builder""" + # These used to be MultiStepBuilder objects until we + # eliminated it as a separate class env = Environment() builder1 = SCons.Builder.Builder(action='foo', src_suffix='.bar', suffix='.foo') - builder2 = SCons.Builder.MultiStepBuilder(action=MyAction('act'), - src_builder = builder1, - src_suffix = '.foo') + builder2 = SCons.Builder.Builder(action=MyAction('act'), + src_builder = builder1, + src_suffix = '.foo') tgt = builder2(env, source=[]) assert tgt == [], tgt @@ -800,22 +804,22 @@ class BuilderTestCase(unittest.TestCase): s = map(str, tgt.sources[0].sources) assert s == ['aaa.bar'], s - builder3 = SCons.Builder.MultiStepBuilder(action = 'foo', - src_builder = 'xyzzy', - src_suffix = '.xyzzy') + builder3 = SCons.Builder.Builder(action = 'foo', + src_builder = 'xyzzy', + src_suffix = '.xyzzy') assert builder3.get_src_builders(Environment()) == [] builder4 = SCons.Builder.Builder(action='bld4', src_suffix='.i', suffix='_wrap.c') - builder5 = SCons.Builder.MultiStepBuilder(action=MyAction('act'), - src_builder=builder4, - suffix='.obj', - src_suffix='.c') - builder6 = SCons.Builder.MultiStepBuilder(action=MyAction('act'), - src_builder=builder5, - suffix='.exe', - src_suffix='.obj') + builder5 = SCons.Builder.Builder(action=MyAction('act'), + src_builder=builder4, + suffix='.obj', + src_suffix='.c') + builder6 = SCons.Builder.Builder(action=MyAction('act'), + src_builder=builder5, + suffix='.exe', + src_suffix='.obj') tgt = builder6(env, 'test', 'test.i')[0] s = str(tgt) assert s == 'test.exe', s @@ -1359,9 +1363,8 @@ class BuilderTestCase(unittest.TestCase): b1 = SCons.Builder.Builder(action='foo', suffix='.o') b2 = SCons.Builder.Builder(action='foo', suffix='.c') - b3 = SCons.Builder.MultiStepBuilder(action='bar', - src_suffix = '.foo', - src_builder = b1) + b3 = SCons.Builder.Builder(action='bar', src_suffix = '.foo', + src_builder = b1) b4 = SCons.Builder.Builder(action={}) b5 = SCons.Builder.Builder(action='foo', name='builder5') b6 = SCons.Builder.Builder(action='foo') @@ -1407,17 +1410,6 @@ class BuilderTestCase(unittest.TestCase): for B in b3.get_src_builders(env2): assert B.get_name(env2) == 'B1' - tgts = b1(env, target = [outfile, outfile2], source='moo') - for t in tgts: - name = t.builder.get_name(env) - assert name == 'ListBuilder(bldr1)', name - # The following are not symbolically correct, because the - # ListBuilder was only created on behalf of env, so it - # would probably be OK if better correctness - # env-to-builder mappings caused this to fail in the - # future. - assert t.builder.get_name(env2) == 'ListBuilder(B1)' - tgt = b4(env, target = 'moo', source='cow') assert tgt[0].builder.get_name(env) == 'bldr4' @@ -1529,7 +1521,7 @@ class CompositeBuilderTestCase(unittest.TestCase): assert isinstance(tgt.builder, SCons.Builder.BuilderBase) tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0] - assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder), tgt.builder.__dict__ + assert isinstance(tgt.builder, SCons.Builder.BuilderBase), tgt.builder.__dict__ bar_bld = SCons.Builder.Builder(action = 'a-bar', src_suffix = '.inb', @@ -1543,10 +1535,10 @@ class CompositeBuilderTestCase(unittest.TestCase): builder.add_action('.bar', 'bar') tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0] - assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder) + assert isinstance(tgt.builder, SCons.Builder.BuilderBase) tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0] - assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder) + assert isinstance(tgt.builder, SCons.Builder.BuilderBase) flag = 0 tgt = builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0] diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 4761ea05..e2883f47 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -54,7 +54,6 @@ import SCons.Node.Python import SCons.Platform import SCons.SConsign import SCons.Sig -import SCons.Sig.MD5 import SCons.Sig.TimeStamp import SCons.Subst import SCons.Tool @@ -513,6 +512,7 @@ class SubstitutionEnvironment: """ dict = { 'ASFLAGS' : [], + 'CFLAGS' : [], 'CCFLAGS' : [], 'CPPDEFINES' : [], 'CPPFLAGS' : [], @@ -641,6 +641,8 @@ class SubstitutionEnvironment: elif arg == '-pthread': dict['CCFLAGS'].append(arg) dict['LINKFLAGS'].append(arg) + elif arg[:5] == '-std=': + dict['CFLAGS'].append(arg) # C only elif arg[0] == '+': dict['CCFLAGS'].append(arg) dict['LINKFLAGS'].append(arg) @@ -1667,8 +1669,15 @@ class Base(SubstitutionEnvironment): def SourceSignatures(self, type): type = self.subst(type) if type == 'MD5': - import SCons.Sig.MD5 - self._calc_module = SCons.Sig.MD5 + try: + import SCons.Sig.MD5 + except ImportError: + msg = "No MD5 module available, using time stamps" + SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) + import SCons.Sig.TimeStamp + self._calc_module = SCons.Sig.TimeStamp + else: + self._calc_module = SCons.Sig.MD5 elif type == 'timestamp': import SCons.Sig.TimeStamp self._calc_module = SCons.Sig.TimeStamp diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index f0f73dac..c015bc1e 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -658,6 +658,7 @@ sys.exit(1) empty = { 'ASFLAGS' : [], + 'CFLAGS' : [], 'CCFLAGS' : [], 'CPPDEFINES' : [], 'CPPFLAGS' : [], @@ -686,6 +687,7 @@ sys.exit(1) "-Wl,-R,rpath2 " + \ "-Wl,-Rrpath3 " + \ "-Wp,-cpp " + \ + "-std=c99 " + \ "-framework Carbon " + \ "-frameworkdir=fwd1 " + \ "-Ffwd2 " + \ @@ -698,6 +700,7 @@ sys.exit(1) d = env.ParseFlags(s) assert d['ASFLAGS'] == ['-as'], d['ASFLAGS'] + assert d['CFLAGS'] == ['-std=c99'] assert d['CCFLAGS'] == ['-X', '-Wa,-as', '-pthread', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 08b8d7d8..8db29280 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -48,7 +48,6 @@ import SCons.Action from SCons.Debug import logInstanceCreation import SCons.Errors import SCons.Node -import SCons.Sig.MD5 import SCons.Subst import SCons.Util import SCons.Warnings @@ -776,7 +775,7 @@ class Entry(Base): def diskcheck_match(self): pass - def disambiguate(self): + def disambiguate(self, must_exist=None): """ """ if self.isdir(): @@ -802,6 +801,9 @@ class Entry(Base): self.srcnode().isdir(): self.__class__ = Dir self._morph() + elif must_exist: + msg = "No such file or directory: '%s'" % self.abspath + raise SCons.Errors.UserError, msg else: self.__class__ = File self._morph() @@ -825,18 +827,17 @@ class Entry(Base): Since this should return the real contents from the file system, we check to see into what sort of subclass we should morph this Entry.""" - if self.isfile(): - self.__class__ = File - self._morph() - return self.get_contents() - if self.isdir(): - self.__class__ = Dir - self._morph() + try: + self = self.disambiguate(must_exist=1) + except SCons.Errors.UserError, e: + # There was nothing on disk with which to disambiguate + # this entry. Leave it as an Entry, but return a null + # string so calls to get_contents() in emitters and the + # like (e.g. in qt.py) don't have to disambiguate by hand + # or catch the exception. + return '' + else: return self.get_contents() - if self.islink(): - return '' # avoid errors for dangling symlinks - msg = "No such file or directory: '%s'" % self.abspath - raise SCons.Errors.UserError, msg def must_be_a_Dir(self): """Called to make sure a Node is a Dir. Since we're an @@ -1259,7 +1260,13 @@ class FS(LocalFS): self.CacheDebug = self.CacheDebugWrite def CacheDir(self, path): - self.CachePath = path + try: + import SCons.Sig.MD5 + except ImportError: + msg = "No MD5 module available, CacheDir() not supported" + SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) + else: + self.CachePath = path def build_dir_target_climb(self, orig, dir, tail): """Create targets in corresponding build directories @@ -2026,16 +2033,23 @@ class File(Base): b = self.is_derived() if not b and not self.has_src_builder(): return None + + retrieved = None if b and self.fs.CachePath: if self.fs.cache_show: if CacheRetrieveSilent(self, [], None, execute=1) == 0: self.build(presub=0, execute=0) - self.set_state(SCons.Node.executed) - return 1 - elif CacheRetrieve(self, [], None, execute=1) == 0: + retrieved = 1 + else: + if CacheRetrieve(self, [], None, execute=1) == 0: + retrieved = 1 + if retrieved: + # Record build signature information, but don't + # push it out to cache. (We just got it from there!) self.set_state(SCons.Node.executed) - return 1 - return None + SCons.Node.Node.built(self) + + return retrieved def built(self): @@ -2285,12 +2299,15 @@ class File(Base): return None, None ninfo = self.get_binfo().ninfo if not hasattr(ninfo, 'bsig'): + import SCons.Errors raise SCons.Errors.InternalError, "cachepath(%s) found no bsig" % self.path elif ninfo.bsig is None: + import SCons.Errors raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path # Add the path to the cache signature, because multiple # targets built by the same action will all have the same # build signature, and we have to differentiate them somehow. + import SCons.Sig.MD5 cache_sig = SCons.Sig.MD5.collect([ninfo.bsig, self.path]) subdir = string.upper(cache_sig[0]) dir = os.path.join(self.fs.CachePath, subdir) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 434709c4..ec3c322d 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1224,12 +1224,9 @@ class FSTestCase(_tempdirTestCase): # test Entry.get_contents() e = fs.Entry('does_not_exist') - exc_caught = 0 - try: - e.get_contents() - except SCons.Errors.UserError: - exc_caught = 1 - assert exc_caught, "Should have caught an IOError" + c = e.get_contents() + assert c == "", c + assert e.__class__ == SCons.Node.FS.Entry test.write("file", "file\n") try: @@ -1250,7 +1247,7 @@ class FSTestCase(_tempdirTestCase): os.symlink('nonexistent', test.workpath('dangling_symlink')) e = fs.Entry('dangling_symlink') c = e.get_contents() - assert e.__class__ == SCons.Node.FS.Entry + assert e.__class__ == SCons.Node.FS.Entry, e.__class__ assert c == "", c test.write("tstamp", "tstamp\n") @@ -1866,12 +1863,8 @@ class EntryTestCase(_tempdirTestCase): assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__ e3n = fs.Entry('e3n') - exc_caught = None - try: - e3n.get_contents() - except SCons.Errors.UserError: - exc_caught = 1 - assert exc_caught, "did not catch expected SCons.Errors.UserError" + e3n.get_contents() + assert e3n.__class__ is SCons.Node.FS.Entry, e3n.__class__ test.subdir('e4d') test.write('e4f', "e4f\n") diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index e5d064e5..e17666bb 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -219,7 +219,7 @@ class Node: # what line in what file created the node, for example). Annotate(self) - def disambiguate(self): + def disambiguate(self, must_exist=None): return self def get_suffix(self): diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index 3ad4cc71..22ec188b 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -201,6 +201,12 @@ class SConfTestCase(unittest.TestCase): pass def calc_signature(self, calc): pass + def get_executor(self): + class Executor: + pass + e = Executor() + e.targets = [self] + return e return [MyNode('n1'), MyNode('n2')] try: self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()}) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 6eedbabf..96f15261 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -961,6 +961,7 @@ def _main(args, parser): SCons.Warnings.DeprecatedWarning, SCons.Warnings.DuplicateEnvironmentWarning, SCons.Warnings.MissingSConscriptWarning, + SCons.Warnings.NoMD5ModuleWarning, SCons.Warnings.NoMetaclassSupportWarning, SCons.Warnings.NoParallelSupportWarning, SCons.Warnings.MisleadingKeywordsWarning, ] diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 2ea3f0da..04ed19a8 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -546,10 +546,7 @@ class Taskmaster: if node is None: return None - try: - tlist = node.builder.targets(node) - except AttributeError: - tlist = [node] + tlist = node.get_executor().targets task = self.tasker(self, tlist, node is self.current_top, node) try: @@ -580,10 +577,7 @@ class Taskmaster: pass def executed(self, node): - try: - tlist = node.builder.targets(node) - except AttributeError: - tlist = [node] + pass def exception_raise(self, exception): exc = exception[:] diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 4fefb9d2..1803eee2 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -162,6 +162,13 @@ class Node: def postprocess(self): self.postprocessed = 1 + def get_executor(self): + class Executor: + pass + e = Executor() + e.targets = self.targets + return e + class OtherError(Exception): pass diff --git a/src/engine/SCons/Tool/bcc32.py b/src/engine/SCons/Tool/bcc32.py index 826373f5..86ca0764 100644 --- a/src/engine/SCons/Tool/bcc32.py +++ b/src/engine/SCons/Tool/bcc32.py @@ -63,10 +63,12 @@ def generate(env): env['CC'] = 'bcc32' env['CCFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC -q $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCCCOM'] = '$SHCC -WD $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' env['CPPDEFPREFIX'] = '-D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '-I' diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py index c4114b73..62b945f6 100644 --- a/src/engine/SCons/Tool/cc.py +++ b/src/engine/SCons/Tool/cc.py @@ -63,10 +63,12 @@ def generate(env): env['CC'] = 'cc' env['CCFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC -o $TARGET -c $CCFLAGS $_CCCOMCOM $SOURCES' + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCCFLAGS $_CCCOMCOM $SOURCES' + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' env['CPPDEFPREFIX'] = '-D' env['CPPDEFSUFFIX'] = '' diff --git a/src/engine/SCons/Tool/cc.xml b/src/engine/SCons/Tool/cc.xml index 84980db1..0ebaf146 100644 --- a/src/engine/SCons/Tool/cc.xml +++ b/src/engine/SCons/Tool/cc.xml @@ -18,9 +18,10 @@ The C compiler. -The command line used to compile a C source file to a (static) object file. -Any options specified in the &cv-CCFLAGS; and &cv-CPPFLAGS; construction variables -are included on this command line. +The command line used to compile a C source file to a (static) object +file. Any options specified in the &cv-CFLAGS;, &cv-CCFLAGS; and +&cv-CPPFLAGS; construction variables are included on this command +line. @@ -38,7 +39,13 @@ env = Environment(CCCOMSTR = "Compiling static object $TARGET") -General options that are passed to the C compiler. +General options that are passed to the C and C++ compilers. + + + + + +General options that are passed to the C compiler (C only; not C++). @@ -92,7 +99,7 @@ The C compiler used for generating shared-library objects. The command line used to compile a C source file to a shared-library object file. -Any options specified in the &cv-SHCCFLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-SHCFLAGS;, &cv-SHCCFLAGS; and &cv-CPPFLAGS; construction variables are included on this command line. @@ -111,7 +118,14 @@ env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET") -Options that are passed to the C compiler +Options that are passed to the C and C++ compilers +to generate shared-library objects. + + + + + +Options that are passed to the C compiler (only; not C++) to generate shared-library objects. diff --git a/src/engine/SCons/Tool/f90.py b/src/engine/SCons/Tool/f90.py index 2e2b5b11..cb450b6d 100644 --- a/src/engine/SCons/Tool/f90.py +++ b/src/engine/SCons/Tool/f90.py @@ -119,6 +119,10 @@ def add_to_env(env): def generate(env): fortran.add_to_env(env) + + import f77 + f77.add_to_env(env) + add_to_env(env) env['_FORTRAND'] = env.Detect(compilers) or 'f90' diff --git a/src/engine/SCons/Tool/f95.py b/src/engine/SCons/Tool/f95.py index 9cd2664c..7adc80b3 100644 --- a/src/engine/SCons/Tool/f95.py +++ b/src/engine/SCons/Tool/f95.py @@ -119,6 +119,13 @@ def add_to_env(env): def generate(env): fortran.add_to_env(env) + + import f77 + f77.add_to_env(env) + + import f90 + f90.add_to_env(env) + add_to_env(env) env['_FORTRAND'] = env.Detect(compilers) or 'f95' diff --git a/src/engine/SCons/Tool/icc.py b/src/engine/SCons/Tool/icc.py index 20bf17ce..90dece72 100644 --- a/src/engine/SCons/Tool/icc.py +++ b/src/engine/SCons/Tool/icc.py @@ -40,7 +40,7 @@ def generate(env): cc.generate(env) env['CC'] = 'icc' - env['CCCOM'] = '$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' env['CPPDEFPREFIX'] = '/D' env['CPPDEFSUFFIX'] = '' diff --git a/src/engine/SCons/Tool/lex.py b/src/engine/SCons/Tool/lex.py index 3331f6cc..31f21a9d 100644 --- a/src/engine/SCons/Tool/lex.py +++ b/src/engine/SCons/Tool/lex.py @@ -33,23 +33,60 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import os.path + +import string + import SCons.Action import SCons.Tool import SCons.Util LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") +def lexEmitter(target, source, env): + sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) + + if sourceExt == ".lm": # If using Objective-C + target = [sourceBase + ".m"] # the extension is ".m". + + # This emitter essentially tries to add to the target all extra + # files generated by flex. + + # Different options that are used to trigger the creation of extra files. + fileGenOptions = ["--header-file=", "--tables-file="] + + for option in SCons.Util.CLVar(env.subst("$LEXFLAGS")): + for fileGenOption in fileGenOptions: + l = len(fileGenOption) + if option[:l] == fileGenOption: + # A file generating option is present, so add the + # file name to the target list. + fileName = string.strip(option[l:]) + target.append(fileName) + return (target, source) + def generate(env): """Add Builders and construction variables for lex to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - c_file.add_action('.l', LexAction) - c_file.add_action('.lex', LexAction) - cxx_file.add_action('.ll', LexAction) + # C + c_file.add_action(".l", LexAction) + c_file.add_emitter(".l", lexEmitter) + + c_file.add_action(".lex", LexAction) + c_file.add_emitter(".lex", lexEmitter) + + # Objective-C + cxx_file.add_action(".lm", LexAction) + cxx_file.add_emitter(".lm", lexEmitter) + + # C++ + cxx_file.add_action(".ll", LexAction) + cxx_file.add_emitter(".ll", lexEmitter) + + env["LEX"] = env.Detect("flex") or "lex" + env["LEXFLAGS"] = SCons.Util.CLVar("") + env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" - env['LEX'] = env.Detect('flex') or 'lex' - env['LEXFLAGS'] = SCons.Util.CLVar('') - env['LEXCOM'] = '$LEX $LEXFLAGS -t $SOURCES > $TARGET' - def exists(env): - return env.Detect(['flex', 'lex']) + return env.Detect(["flex", "lex"]) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index d679b53e..0639535c 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -145,7 +145,7 @@ def generate(env): env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['RCINCPREFIX'] = '--include-dir ' env['RCINCSUFFIX'] = '' - env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX}${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' + env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' env['BUILDERS']['RES'] = res_builder # Some setting from the platform also have to be overridden: diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 80b59263..7e476f57 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -687,10 +687,12 @@ def generate(env): env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS' env['CC'] = 'cl' env['CCFLAGS'] = SCons.Util.CLVar('/nologo') - env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS' + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS' + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' env['CXX'] = '$CC' env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)') env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' diff --git a/src/engine/SCons/Tool/mwcc.py b/src/engine/SCons/Tool/mwcc.py index 52f55ee1..a1ede44a 100644 --- a/src/engine/SCons/Tool/mwcc.py +++ b/src/engine/SCons/Tool/mwcc.py @@ -171,14 +171,15 @@ def generate(env): env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES' env['CC'] = 'mwcc' - env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS' + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' env['CXX'] = 'mwcc' env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' env['SHCC'] = '$CC' env['SHCCFLAGS'] = '$CCFLAGS' - env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS' + env['SHCFLAGS'] = '$CFLAGS' + env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' env['SHCXX'] = '$CXX' env['SHCXXFLAGS'] = '$CXXFLAGS' diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 5077901d..4d290c77 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -313,8 +313,8 @@ def generate(env): env['BUILDERS']['Uic'] = uicBld env['BUILDERS']['Moc'] = mocBld static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - static_obj.src_builder.append('Uic') - shared_obj.src_builder.append('Uic') + static_obj.add_src_builder('Uic') + shared_obj.add_src_builder('Uic') # We use the emitters of Program / StaticLibrary / SharedLibrary # to scan for moc'able files diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index b8916ae6..cbccb294 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -34,6 +34,7 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path +import string import SCons.Defaults import SCons.Tool @@ -42,31 +43,64 @@ import SCons.Util YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") def _yaccEmitter(target, source, env, ysuf, hsuf): + flags = SCons.Util.CLVar(env.subst("$YACCFLAGS")) + targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) + + if '.ym' in ysuf: # If using Objective-C + target = [targetBase + ".m"] # the extension is ".m". + + # If -d is specified on the command line, yacc will emit a .h - # or .hpp file as well as a .c or .cpp file, depending on whether - # the input file is a .y or .yy, respectively. - if len(source) and '-d' in SCons.Util.CLVar(env.subst("$YACCFLAGS")): + # or .hpp file with the same name as the .c or .cpp output file. + if '-d' in flags: + target.append(targetBase + env.subst(hsuf)) + + # If -g is specified on the command line, yacc will emit a .vcg + # file with the same base name as the .y, .yacc, .ym or .yy file. + if "-g" in flags: base, ext = os.path.splitext(SCons.Util.to_String(source[0])) - if ext in ysuf: - base, ext = os.path.splitext(SCons.Util.to_String(target[0])) - target.append(base + env.subst(hsuf)) + target.append(base + env.subst("$YACCVCGFILESUFFIX")) + + # With --defines and --graph, the name of the file is totally defined + # in the options. + fileGenOptions = ["--defines=", "--graph="] + for option in flags: + for fileGenOption in fileGenOptions: + l = len(fileGenOption) + if option[:l] == fileGenOption: + # A file generating option is present, so add the file + # name to the list of targets. + fileName = string.strip(option[l:]) + target.append(fileName) + return (target, source) def yEmitter(target, source, env): return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') +def ymEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') + def yyEmitter(target, source, env): return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') def generate(env): """Add Builders and construction variables for yacc to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - + + # C c_file.add_action('.y', YaccAction) - c_file.add_action('.yacc', YaccAction) - cxx_file.add_action('.yy', YaccAction) c_file.add_emitter('.y', yEmitter) + + c_file.add_action('.yacc', YaccAction) c_file.add_emitter('.yacc', yEmitter) + + # Objective-C + c_file.add_action('.ym', YaccAction) + c_file.add_emitter('.ym', ymEmitter) + + # C++ + cxx_file.add_action('.yy', YaccAction) cxx_file.add_emitter('.yy', yyEmitter) env['YACC'] = env.Detect('bison') or 'yacc' @@ -74,6 +108,7 @@ def generate(env): env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' env['YACCHFILESUFFIX'] = '.h' env['YACCHXXFILESUFFIX'] = '.hpp' + env['YACCVCGFILESUFFIX'] = '.vcg' def exists(env): return env.Detect(['bison', 'yacc']) diff --git a/src/engine/SCons/Tool/yacc.xml b/src/engine/SCons/Tool/yacc.xml index 48bb3236..8a23d0b1 100644 --- a/src/engine/SCons/Tool/yacc.xml +++ b/src/engine/SCons/Tool/yacc.xml @@ -79,3 +79,20 @@ The default value is .hpp. + + + +The suffix of the file +containing the VCG grammar automaton definition +when the + +option is used. +Note that setting this variable does not cause +the parser generator to generate a VCG +file with the specified suffix, +it exists to allow you to specify +what suffix the parser generator will use of its own accord. +The default value is +.vcg. + + diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py index 27614bf9..1b13c96a 100644 --- a/src/engine/SCons/Warnings.py +++ b/src/engine/SCons/Warnings.py @@ -54,6 +54,9 @@ class DuplicateEnvironmentWarning(Warning): class MissingSConscriptWarning(Warning): pass +class NoMD5ModuleWarning(Warning): + pass + class NoMetaclassSupportWarning(Warning): pass diff --git a/src/script/scons-time.py b/src/script/scons-time.py index 1867d44f..b0feaab2 100644 --- a/src/script/scons-time.py +++ b/src/script/scons-time.py @@ -31,6 +31,8 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + import getopt import glob import os diff --git a/src/test_copyrights.py b/src/test_copyrights.py index ce78a8bc..2bc951ed 100644 --- a/src/test_copyrights.py +++ b/src/test_copyrights.py @@ -25,129 +25,188 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ -Verify that we have proper Copyright notices on all the right files -in our distributions. +Verify that we have proper strings like Copyright notices on all the +right files in our distributions. -Note that this is a packaging test, not a functional test, so the -name of this script doesn't end in *Tests.py. +Note that this is a source file and packaging test, not a functional test, +so the name of this script doesn't end in *Tests.py. """ +import fnmatch import os import os.path import re import string +import TestCmd import TestSCons -test = TestSCons.TestSCons() +# Use TestCmd, not TestSCons, so we don't chdir to a temporary directory. +test = TestCmd.TestCmd() -try: - cwd = os.environ['SCONS_CWD'] -except KeyError: - cwd = os.getcwd() +scons_version = TestSCons.SConsVersion def build_path(*args): - return apply(os.path.join, (cwd, 'build',)+args) + return apply(os.path.join, ('build',)+args) build_scons = build_path('scons') -build_local = build_path('scons-local', 'scons-local-'+test.scons_version) +build_local = build_path('scons-local', 'scons-local-'+scons_version) build_src = build_path('scons-src') -class Collect: - expression = re.compile('Copyright.*The SCons Foundation') - def __init__(self, directory, remove_list): - self.copyright = [] - self.no_copyright = [] - self.remove = {} +class Checker: + def __init__(self, directory, search_list = [], remove_list=[]): + self.directory = directory + self.search_list = search_list + self.remove_dict = {} for r in remove_list: - self.remove[os.path.join(directory, r)] = 1 - -def visit(collect, dirname, names): - make_path_tuple = lambda n, d=dirname: (n, os.path.join(d, n)) - for name, path in map(make_path_tuple, names): - if collect.remove.get(path): - names.remove(name) - elif os.path.isfile(path): - if collect.expression.search(open(path, 'r').read()): - collect.copyright.append(path) - else: - collect.no_copyright.append(path) - -# Map each directory to search (dictionary keys) to a list of its -# subsidiary files and directories to exclude from copyright checks. -check = { - build_scons : [ - 'build', - 'build-stamp', - 'configure-stamp', - 'debian', - 'dist', - 'engine/SCons/Conftest.py', - 'engine/SCons/dblite.py', - 'engine/SCons/Optik', - 'MANIFEST', - 'os_spawnv_fix.diff', - 'setup.cfg', - ], - build_local : [ - 'SCons/Conftest.py', - 'SCons/dblite.py', - 'SCons/Optik', - ], - build_src : [ - 'bin', - 'config', - 'debian', - 'doc/design', - 'doc/MANIFEST', - 'doc/python10', - 'doc/reference', - 'doc/man/MANIFEST', - 'doc/user/cons.pl', - 'doc/user/MANIFEST', - 'doc/user/SCons-win32-install-1.jpg', - 'doc/user/SCons-win32-install-2.jpg', - 'doc/user/SCons-win32-install-3.jpg', - 'doc/user/SCons-win32-install-4.jpg', - 'gentoo', - 'QMTest/classes.qmc', - 'QMTest/configuration', - 'QMTest/TestCmd.py', - 'QMTest/TestCommon.py', - 'QMTest/unittest.py', - 'src/os_spawnv_fix.diff', - 'src/MANIFEST.in', - 'src/setup.cfg', - 'src/engine/MANIFEST.in', - 'src/engine/MANIFEST-xml.in', - 'src/engine/setup.cfg', - 'src/engine/SCons/Conftest.py', - 'src/engine/SCons/dblite.py', - 'src/engine/SCons/Optik', - 'src/script/MANIFEST.in', - 'src/script/setup.cfg', - ], -} - -no_copyright = [] -no_result = [] - -for directory, remove_list in check.items(): - if os.path.exists(directory): - c = Collect(directory, remove_list) - os.path.walk(directory, visit, c) - no_copyright.extend(c.no_copyright) - else: - no_result.append(directory) - -if no_copyright: - print "Found the following files with no copyrights:" - print "\t" + string.join(no_copyright, "\n\t") + self.remove_dict[os.path.join(directory, r)] = 1 + + def directory_exists(self): + return os.path.exists(self.directory) + + def remove_path(self, path): + return self.remove_dict.get(path) + + def search_this(self, path): + if self.search_list: + for pattern in self.search_list: + if fnmatch.fnmatch(path, pattern): + return 1 + return None + else: + return os.path.isfile(path) + + def visit(self, result, dirname, names): + make_path_tuple = lambda n, d=dirname: (n, os.path.join(d, n)) + for name, path in map(make_path_tuple, names): + if self.remove_path(path): + names.remove(name) + elif self.search_this(path): + body = open(path, 'r').read() + for expr in self.expressions: + if not expr.search(body): + msg = '%s: missing %s' % (path, repr(expr.pattern)) + result.append(msg) + + def find_missing(self): + result = [] + os.path.walk(self.directory, self.visit, result) + return result + +class CheckUnexpandedStrings(Checker): + expressions = [ + re.compile('__COPYRIGHT__'), + re.compile('__FILE__ __REVISION__ __DATE__ __DEVELOPER__'), + ] + def must_be_built(self): + return None + +class CheckExpandedCopyright(Checker): + expressions = [ + re.compile('Copyright.*The SCons Foundation'), + ] + def must_be_built(self): + return 1 + +check_list = [ + + CheckUnexpandedStrings( + 'src', + search_list = [ '*.py' ], + remove_list = [ + 'engine/SCons/Conftest.py', + 'engine/SCons/dblite.py', + 'engine/SCons/Optik', + ], + ), + + CheckUnexpandedStrings( + 'test', + search_list = [ '*.py' ], + ), + + CheckExpandedCopyright( + build_scons, + remove_list = [ + 'build', + 'build-stamp', + 'configure-stamp', + 'debian', + 'dist', + 'engine/SCons/Conftest.py', + 'engine/SCons/dblite.py', + 'engine/SCons/Optik', + 'MANIFEST', + 'os_spawnv_fix.diff', + 'setup.cfg', + ], + ), + + CheckExpandedCopyright( + build_local, + remove_list = [ + 'SCons/Conftest.py', + 'SCons/dblite.py', + 'SCons/Optik', + ], + ), + + CheckExpandedCopyright( + build_src, + remove_list = [ + 'bin', + 'config', + 'debian', + 'doc/design', + 'doc/MANIFEST', + 'doc/python10', + 'doc/reference', + 'doc/man/MANIFEST', + 'doc/user/cons.pl', + 'doc/user/MANIFEST', + 'doc/user/SCons-win32-install-1.jpg', + 'doc/user/SCons-win32-install-2.jpg', + 'doc/user/SCons-win32-install-3.jpg', + 'doc/user/SCons-win32-install-4.jpg', + 'gentoo', + 'QMTest/classes.qmc', + 'QMTest/configuration', + 'QMTest/TestCmd.py', + 'QMTest/TestCommon.py', + 'QMTest/unittest.py', + 'src/os_spawnv_fix.diff', + 'src/MANIFEST.in', + 'src/setup.cfg', + 'src/engine/MANIFEST.in', + 'src/engine/MANIFEST-xml.in', + 'src/engine/setup.cfg', + 'src/engine/SCons/Conftest.py', + 'src/engine/SCons/dblite.py', + 'src/engine/SCons/Optik', + 'src/script/MANIFEST.in', + 'src/script/setup.cfg', + ], + ), + +] + +missing_strings = [] +not_built = [] + +for collector in check_list: + if collector.directory_exists(): + missing_strings.extend(collector.find_missing()) + elif collector.must_be_built(): + not_built.append(collector.directory) + +if missing_strings: + print "Found the following files with missing strings:" + print "\t" + string.join(missing_strings, "\n\t") test.fail_test(1) -if no_result: - print "Cannot check copyrights, the following have apparently not been built:" - print "\t" + string.join(no_result, "\n\t") +if not_built: + print "Cannot check all strings, the following have apparently not been built:" + print "\t" + string.join(not_built, "\n\t") test.no_result(1) test.pass_test() diff --git a/test/Builder/multi/different-actions.py b/test/Builder/multi/different-actions.py new file mode 100644 index 00000000..ed1c6766 --- /dev/null +++ b/test/Builder/multi/different-actions.py @@ -0,0 +1,60 @@ +#!/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 environments with actions that have different signatures +generate an error. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=Action(build, varlist=['XXX']), multi=1) +env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo') +env2 = env.Clone(XXX = 'var') +env.B(target = 'file6.out', source = 'file6a.in') +env2.B(target = 'file6.out', source = 'file6b.in') +""") + +test.write('file6a.in', 'file6a.in\n') +test.write('file6b.in', 'file6b.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Two environments with different actions were specified for the same target: file6.out +""") + TestSCons.file_expr + +test.pass_test() + +test.run(arguments='file6.out', status=2, stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/different-environments.py b/test/Builder/multi/different-environments.py new file mode 100644 index 00000000..9d1dcc89 --- /dev/null +++ b/test/Builder/multi/different-environments.py @@ -0,0 +1,64 @@ +#!/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 a warning is generated if the calls have different overrides +but the overrides don't appear to affect the build operation. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +_python_ = TestSCons._python_ + +test.write('build.py', r"""#!/usr/bin/env python +import sys +def build(num, target, source): + file = open(str(target), 'wb') + file.write('%s\n'%num) + for s in source: + file.write(open(str(s), 'rb').read()) +build(sys.argv[1],sys.argv[2],sys.argv[3:]) +""") + +test.write('SConstruct', """\ +B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = 'file03.out', source = 'file03a.in', foo=1) +env.B(target = 'file03.out', source = 'file03b.in', foo=2) +""" % locals()) + +test.write('file03a.in', 'file03a.in\n') +test.write('file03b.in', 'file03b.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Two environments with different actions were specified for the same target: file03.out +""") + TestSCons.file_expr + +test.run(arguments='file03.out', status=2, stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/different-multi.py b/test/Builder/multi/different-multi.py new file mode 100644 index 00000000..1f0ef2a1 --- /dev/null +++ b/test/Builder/multi/different-multi.py @@ -0,0 +1,64 @@ +#!/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 trying to call a target with two different "multi" builders +generates an error. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +def build2(env, target, source): + build(env, target, source) + +# Put the names on the Builder objects and in the environment so +# the error output should be consistent regardless of Python version +# or how we mess with the Builder internals. +B = Builder(action=build, multi=1, name='B') +C = Builder(action=build2, multi=1, name='C') +env = Environment(BUILDERS = { 'B' : B, 'C' : C }) +env.B(target = 'file8.out', source = 'file8.in') +env.C(target = 'file8.out', source = 'file8.in') +""") + +test.write('file8a.in', 'file8a.in\n') +test.write('file8b.in', 'file8b.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Two different builders (B and C) were specified for the same target: file8.out +""") + TestSCons.file_expr + +test.run(arguments='file8.out', status=2, stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/different-order.py b/test/Builder/multi/different-order.py new file mode 100644 index 00000000..f4a64435 --- /dev/null +++ b/test/Builder/multi/different-order.py @@ -0,0 +1,59 @@ +#!/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 a "multi" builder can NOT be called multiple times with +target lists that have different orders. This is intentional; the +order of the targets matter to the builder because the build command +can contain things like ${TARGET[0]}. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + for t in target: + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = ['file10a.out', 'file10b.out'], source = 'file10.in') +env.B(target = ['file10b.out', 'file10a.out'], source = 'file10.in') +""") + +test.write('file10.in', 'file10.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Two different target lists have a target in common: file10b.out (from ['file10a.out', 'file10b.out'] and from ['file10b.out', 'file10a.out']) +""") + TestSCons.file_expr + +test.run(arguments='file10.out', status=2, stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/different-overrides.py b/test/Builder/multi/different-overrides.py new file mode 100644 index 00000000..5420a49c --- /dev/null +++ b/test/Builder/multi/different-overrides.py @@ -0,0 +1,58 @@ +#!/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 a warning is generated if the calls have different overrides +but the overrides don't appear to affect the build operation. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = 'file3.out', source = 'file3a.in', foo=1) +env.B(target = 'file3.out', source = 'file3b.in', foo=2) +""") + +test.write('file3a.in', 'file3a.in\n') +test.write('file3b.in', 'file3b.in\n') + +expect = TestSCons.re_escape(""" +scons: warning: Two different environments were specified for target file3.out, +\tbut they appear to have the same action: build(target, source, env) +""") + TestSCons.file_expr + +test.run(arguments='file3.out', stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/different-target-lists.py b/test/Builder/multi/different-target-lists.py new file mode 100644 index 00000000..58221993 --- /dev/null +++ b/test/Builder/multi/different-target-lists.py @@ -0,0 +1,64 @@ +#!/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 a target file can't be in two different target lists. +""" + +# XXX It would be nice if the following two tests could be made to work +# by executing the action once for each unique set of targets. This +# would make it simple to deal with PDB files on Windows like so: +# +# env.Object(['foo.obj', 'vc60.pdb'], 'foo.c') +# env.Object(['bar.obj', 'vc60.pdb'], 'bar.c') + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + for t in target: + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = ['file11a.out', 'file11b.out'], source = 'file11a.in') +env.B(target = ['file11b.out', 'file11c.out'], source = 'file11b.in') +""") + +test.write('file11a.in', 'file11a.in\n') +test.write('file11b.in', 'file11b.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Two different target lists have a target in common: file11b.out (from ['file11a.out', 'file11b.out'] and from ['file11b.out', 'file11c.out']) +""") + TestSCons.file_expr + +test.run(arguments='file11.out', status=2, stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/error.py b/test/Builder/multi/error.py new file mode 100644 index 00000000..4201cfc5 --- /dev/null +++ b/test/Builder/multi/error.py @@ -0,0 +1,57 @@ +#!/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 a builder with "multi" not set generates an error on the +second call. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=0) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = 'file2.out', source = 'file2a.in') +env.B(target = 'file2.out', source = 'file2b.in') +""") + +test.write('file2a.in', 'file2a.in\n') +test.write('file2b.in', 'file2b.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Multiple ways to build the same target were specified for: file2.out (from ['file2a.in'] and from ['file2b.in']) +""") + TestSCons.file_expr + +test.run(arguments='file2.out', status=2, stderr=expect) + +test.pass_test() diff --git a/test/Builder/multi/lone-target-list.py b/test/Builder/multi/lone-target-list.py new file mode 100644 index 00000000..a305b544 --- /dev/null +++ b/test/Builder/multi/lone-target-list.py @@ -0,0 +1,59 @@ +#!/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 a target file can't be a lone target and in a list. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + for t in target: + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = ['file12a.out', 'file12b.out'], source = 'file12a.in') +env.B(target = 'file12a.out', source = 'file12b.in') +""") + +test.write('file12a.in', 'file12a.in\n') +test.write('file12b.in', 'file12b.in\n') + +expect = TestSCons.re_escape(""" +scons: *** Two different target lists have a target in common: file12a.out (from ['file12a.out', 'file12b.out'] and from ['file12a.out']) +""") + TestSCons.file_expr + +test.run(arguments='file12.out', status=2, stderr=expect) + + + +test.pass_test() diff --git a/test/Builder/multi/multi.py b/test/Builder/multi/multi.py new file mode 100644 index 00000000..315599b3 --- /dev/null +++ b/test/Builder/multi/multi.py @@ -0,0 +1,55 @@ +#!/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 a builder with "multi" set can be called multiple times +and the source files are added to the list. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = 'file1.out', source = 'file1a.in') +env.B(target = 'file1.out', source = 'file1b.in') +""") + +test.write('file1a.in', 'file1a.in\n') +test.write('file1b.in', 'file1b.in\n') + +test.run(arguments='file1.out') + +test.must_match('file1.out', "file1a.in\nfile1b.in\n") + +test.pass_test() diff --git a/test/Builder/multi/same-actions.py b/test/Builder/multi/same-actions.py new file mode 100644 index 00000000..afccb652 --- /dev/null +++ b/test/Builder/multi/same-actions.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 two different environments can be used for the same target, +so long as the actions have the same signature; a warning is generated. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env2 = env.Clone(DIFFERENT_VARIABLE = 'true') +env.B(target = 'file5.out', source = 'file5a.in') +env2.B(target = 'file5.out', source = 'file5b.in') +""") + +test.write('file5a.in', 'file5a.in\n') +test.write('file5b.in', 'file5b.in\n') + +expect = TestSCons.re_escape(""" +scons: warning: Two different environments were specified for target file5.out, +\tbut they appear to have the same action: build(target, source, env) +""") + TestSCons.file_expr + +test.run(arguments='file5.out', stderr=expect) + +test.must_match('file5.out', "file5a.in\nfile5b.in\n") + +test.pass_test() diff --git a/test/Builder/multi/same-overrides.py b/test/Builder/multi/same-overrides.py new file mode 100644 index 00000000..d12af218 --- /dev/null +++ b/test/Builder/multi/same-overrides.py @@ -0,0 +1,71 @@ +#!/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 everything works if two multi calls have the same overrides. +""" + +import string + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +_python_ = TestSCons._python_ + +test.write('build.py', r"""#!/usr/bin/env python +import sys +def build(num, target, source): + file = open(str(target), 'wb') + file.write('%s\n'%num) + for s in source: + file.write(open(str(s), 'rb').read()) +build(sys.argv[1],sys.argv[2],sys.argv[3:]) +""") + +test.write('SConstruct', """\ +B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = 'file4.out', source = 'file4a.in', foo=3) +env.B(target = 'file4.out', source = 'file4b.in', foo=3) +""" % locals()) + +test.write('file4a.in', 'file4a.in\n') +test.write('file4b.in', 'file4b.in\n') + +python_expr = string.replace(TestSCons.python, '\\', '\\\\') +act = TestSCons.re_escape('"%s" build.py \$foo \$TARGET \$SOURCES' % python_expr) + +expect = (""" +scons: warning: Two different environments were specified for target file4.out, +\tbut they appear to have the same action: %s +""" % act) + TestSCons.file_expr + +test.run(arguments='file4.out', stderr=expect) + +test.must_match('file4.out', "3\nfile4a.in\nfile4b.in\n") + +test.pass_test() diff --git a/test/Builder/multi/same-targets.py b/test/Builder/multi/same-targets.py new file mode 100644 index 00000000..19efff76 --- /dev/null +++ b/test/Builder/multi/same-targets.py @@ -0,0 +1,57 @@ +#!/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 a "multi" builder can be called multiple times with the same +target list if everything is identical. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +def build(env, target, source): + for t in target: + file = open(str(t), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=1) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = ['file9a.out', 'file9b.out'], source = 'file9a.in') +env.B(target = ['file9a.out', 'file9b.out'], source = 'file9b.in') +""") + +test.write('file9a.in', 'file9a.in\n') +test.write('file9b.in', 'file9b.in\n') + +test.run(arguments='file9b.out') + +test.must_match('file9a.out', "file9a.in\nfile9b.in\n") +test.must_match('file9b.out', "file9a.in\nfile9b.in\n") + +test.pass_test() diff --git a/test/Builder/non-multi.py b/test/Builder/non-multi.py new file mode 100644 index 00000000..1d5822ef --- /dev/null +++ b/test/Builder/non-multi.py @@ -0,0 +1,54 @@ +#!/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 a builder without "multi" set can still be called multiple +times if the calls are the same. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """ +def build(env, target, source): + file = open(str(target[0]), 'wb') + for s in source: + file.write(open(str(s), 'rb').read()) + +B = Builder(action=build, multi=0) +env = Environment(BUILDERS = { 'B' : B }) +env.B(target = 'file7.out', source = 'file7.in') +env.B(target = 'file7.out', source = 'file7.in') +""") + +test.write('file7.in', 'file7.in\n') + +test.run(arguments='file7.out') + +test.must_match('file7.out', "file7.in\n") + +test.pass_test() diff --git a/test/CC/CFLAGS.py b/test/CC/CFLAGS.py new file mode 100644 index 00000000..f14fcc54 --- /dev/null +++ b/test/CC/CFLAGS.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys, string +import TestSCons + +test = TestSCons.TestSCons() + +# Make sure CFLAGS is not passed to CXX by just expanding CXXCOM +test.write('SConstruct', """ +env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') +print env.subst('$CXXCOM') +print env.subst('$CXXCOMSTR') +print env.subst('$SHCXXCOM') +print env.subst('$SHCXXCOMSTR') +""") +test.run(arguments = '.') +test.fail_test(string.find(test.stdout(), "-xyz") != -1) +test.fail_test(string.find(test.stdout(), "-abc") == -1) + + +# Test passing CFLAGS to C compiler by actually compiling programs +if sys.platform == 'win32': + _obj = '.obj' + fooflags = '/nologo -DFOO' + barflags = '/nologo -DBAR' +else: + _obj = '.o' + fooflags = '-DFOO' + barflags = '-DBAR' + + +test.write('SConstruct', """ +foo = Environment(CFLAGS = '%s') +bar = Environment(CFLAGS = '%s') +foo.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +foo.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +foo.Program(target = 'prog', source = 'prog.c', + CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ') +""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) + +test.write('prog.c', r""" +#include +#include + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +#ifdef BAZ + printf("prog.c: BAZ\n"); +#endif + exit (0); +} +""") + + + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('prog'), stdout = """\ +prog.c: FOO +prog.c: BAR +prog.c: BAZ +""") + +test.write('SConstruct', """ +bar = Environment(CFLAGS = '%s') +bar.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +bar.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +""" % (barflags, _obj, _obj, _obj, _obj)) + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") + +test.pass_test() diff --git a/test/CC/SHCFLAGS.py b/test/CC/SHCFLAGS.py new file mode 100644 index 00000000..ef20120b --- /dev/null +++ b/test/CC/SHCFLAGS.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import TestSCons +import os +import string + +test = TestSCons.TestSCons() + +e = test.Environment() +fooflags = e['SHCFLAGS'] + ' -DFOO' +barflags = e['SHCFLAGS'] + ' -DBAR' + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = '.' +if string.find(sys.platform, 'irix') > -1: + os.environ['LD_LIBRARYN32_PATH'] = '.' + +test.write('SConstruct', """ +foo = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1) + +foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') +foo.SharedLibrary(target = 'foo', source = foo_obj) + +bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') +bar.SharedLibrary(target = 'bar', source = bar_obj) + +fooMain = foo.Clone(LIBS='foo', LIBPATH='.') +foomain_obj = fooMain.Object(target='foomain', source='main.c') +fooMain.Program(target='fooprog', source=foomain_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=barmain_obj) +""" % (fooflags, barflags)) + +test.write('foo.def', r""" +LIBRARY "foo" +DESCRIPTION "Foo Shared Library" + +EXPORTS + doIt +""") + +test.write('bar.def', r""" +LIBRARY "bar" +DESCRIPTION "Bar Shared Library" + +EXPORTS + doIt +""") + +test.write('prog.c', r""" +#include + +void +doIt() +{ +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +} +""") + +test.write('main.c', r""" + +void doIt(); + +int +main(int argc, char* argv[]) +{ + doIt(); + return 0; +} +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") + +test.write('SConstruct', """ +bar = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1) + +foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') +bar.SharedLibrary(target = 'foo', source = foo_obj) + +bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') +bar.SharedLibrary(target = 'bar', source = bar_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +foomain_obj = barMain.Object(target='foomain', source='main.c') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=foomain_obj) +barMain.Program(target='fooprog', source=barmain_obj) +""" % (barflags)) + +test.run(arguments = '.') + +test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") + +test.pass_test() diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py new file mode 100644 index 00000000..07123c90 --- /dev/null +++ b/test/CacheDir/up-to-date-q.py @@ -0,0 +1,82 @@ +#!/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 targets retrieved from CacheDir() are reported as +up-to-date by the -q option. + +Thanks to dvitek for the test case. +""" + +# Demonstrate a regression between 0.96.1 and 0.96.93. +# +# SCons would incorrectly believe files are stale if they were retrieved +# from the cache in a previous invocation. +# +# What this script does: +# 1. Set up two identical C project directories called 'alpha' and +# 'beta', which use the same cache +# 2. Invoke scons on 'alpha' +# 3. Invoke scons on 'beta', which successfully draws output +# files from the cache +# 4. Invoke scons again, asserting (with -q) that 'beta' is up to date +# +# Step 4 failed in 0.96.93. In practice, this problem would lead to +# lots of unecessary fetches from the cache during incremental +# builds (because they behaved like non-incremental builds). + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'alpha', 'beta') + +foo_c = """ +int main(){ return 0; } +""" + +sconstruct = """ +import os +CacheDir('%s') +Program('foo', 'foo.c') +""" % test.workpath('cache') + +test.write('alpha/foo.c', foo_c) +test.write('alpha/SConstruct', sconstruct) + +test.write('beta/foo.c', foo_c) +test.write('beta/SConstruct', sconstruct) + +# First build, populates the cache +test.run(chdir = 'alpha', arguments = '.') + +# Second build, everything is a cache hit +test.run(chdir = 'beta', arguments = '.') + +# Since we just built 'beta', it ought to be up to date. +test.run(chdir = 'beta', arguments = '. -q') + +test.pass_test() diff --git a/test/LEX/LEX.py b/test/LEX/LEX.py index 3fd4db3d..91b4614d 100644 --- a/test/LEX/LEX.py +++ b/test/LEX/LEX.py @@ -50,89 +50,26 @@ sys.exit(0) test.write('SConstruct', """ env = Environment(LEX = r'%(_python_)s mylex.py', tools=['default', 'lex']) -env.Program(target = 'aaa', source = 'aaa.l') -env.Program(target = 'bbb', source = 'bbb.lex') +env.CFile(target = 'aaa', source = 'aaa.l') +env.CFile(target = 'bbb', source = 'bbb.lex') +env.CXXFile(target = 'ccc', source = 'ccc.ll') +env.CXXFile(target = 'ddd', source = 'ddd.lm') """ % locals()) -test.write('aaa.l', r""" -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("LEX\n"); - printf("aaa.l\n"); - exit (0); -} -""") - -test.write('bbb.lex', r""" -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("LEX\n"); - printf("bbb.lex\n"); - exit (0); -} -""") +test.write('aaa.l', "aaa.l\nLEX\n") +test.write('bbb.lex', "bbb.lex\nLEX\n") +test.write('ccc.ll', "ccc.ll\nLEX\n") +test.write('ddd.lm', "ddd.lm\nLEX\n") test.run(arguments = '.', stderr = None) -test.run(program = test.workpath('aaa' + _exe), stdout = "mylex.py\naaa.l\n") -test.run(program = test.workpath('bbb' + _exe), stdout = "mylex.py\nbbb.lex\n") - - - -lex = test.where_is('lex') - -if lex: - - test.write("wrapper.py", """import os -import string -import sys -open('%s', 'wb').write("wrapper.py\\n") -os.system(string.join(sys.argv[1:], " ")) -""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) - - test.write('SConstruct', """ -foo = Environment() -lex = foo.Dictionary('LEX') -bar = Environment(LEX = r'%(_python_)s wrapper.py ' + lex) -foo.Program(target = 'foo', source = 'foo.l') -bar.Program(target = 'bar', source = 'bar.l') -""" % locals()) - - lex = r""" -%%%% -a printf("A%sA"); -b printf("B%sB"); -%%%% -int -yywrap() -{ - return 1; -} - -main() -{ - yylex(); -} -""" - - test.write('foo.l', lex % ('foo.l', 'foo.l')) - - test.write('bar.l', lex % ('bar.l', 'bar.l')) - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.fail_test(os.path.exists(test.workpath('wrapper.out'))) - - test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n") - - test.run(arguments = 'bar' + _exe) +# Read in with mode='r' because mylex.py implicitley wrote to stdout +# with mode='w'. +test.must_match('aaa.c', "aaa.l\nmylex.py\n", mode='r') +test.must_match('bbb.c', "bbb.lex\nmylex.py\n", mode='r') +test.must_match('ccc.cc', "ccc.ll\nmylex.py\n", mode='r') +test.must_match('ddd.m', "ddd.lm\nmylex.py\n", mode='r') - test.fail_test(test.read('wrapper.out') != "wrapper.py\n") - test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n") test.pass_test() diff --git a/test/LEX/LEXFLAGS.py b/test/LEX/LEXFLAGS.py index 59239349..b6a06fa1 100644 --- a/test/LEX/LEXFLAGS.py +++ b/test/LEX/LEXFLAGS.py @@ -55,68 +55,17 @@ test.write('SConstruct', """ env = Environment(LEX = r'%(_python_)s mylex.py', LEXFLAGS = '-x', tools=['default', 'lex']) -env.Program(target = 'aaa', source = 'aaa.l') +env.CFile(target = 'aaa', source = 'aaa.l') """ % locals()) -test.write('aaa.l', r""" -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("LEXFLAGS\n"); - printf("aaa.l\n"); - exit (0); -} -""") - -test.run(arguments = 'aaa' + _exe, stderr = None) - -test.run(program = test.workpath('aaa' + _exe), stdout = " -x -t\naaa.l\n") - - - -lex = test.where_is('lex') - -if lex: - - test.write('SConstruct', """ -foo = Environment() -bar = Environment(LEXFLAGS = '-b') -foo.Program(target = 'foo', source = 'foo.l') -bar.Program(target = 'bar', source = 'bar.l') -""") - - lex = r""" -%%%% -a printf("A%sA"); -b printf("B%sB"); -%%%% -int -yywrap() -{ - return 1; -} - -main() -{ - yylex(); -} -""" - - test.write('foo.l', lex % ('foo.l', 'foo.l')) - - test.write('bar.l', lex % ('bar.l', 'bar.l')) - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.fail_test(os.path.exists(test.workpath('lex.backup'))) +test.write('aaa.l', "aaa.l\nLEXFLAGS\n") - test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n") +test.run('.', stderr = None) - test.run(arguments = 'bar' + _exe) +# Read in with mode='r' because mylex.py implicitley wrote to stdout +# with mode='w'. +test.must_match('aaa.c', "aaa.l\n -x -t\n", mode='r') - test.fail_test(not os.path.exists(test.workpath('lex.backup'))) - test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n") test.pass_test() diff --git a/test/LEX/live.py b/test/LEX/live.py new file mode 100644 index 00000000..e917039a --- /dev/null +++ b/test/LEX/live.py @@ -0,0 +1,103 @@ +#!/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 LEX and LEXFLAGS with a live lex. +""" + +import string + +import TestSCons + +_exe = TestSCons._exe +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +lex = test.where_is('lex') or test.where_is('flex') + +if not lex: + test.skip_test('No lex or flex found; skipping test.\n') + + + +test.write("wrapper.py", """import os +import string +import sys +open('%s', 'wb').write("wrapper.py\\n") +os.system(string.join(sys.argv[1:], " ")) +""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) + +test.write('SConstruct', """ +foo = Environment() +lex = foo.Dictionary('LEX') +bar = Environment(LEX = r'%(_python_)s wrapper.py ' + lex, + LEXFLAGS = '-b') +foo.Program(target = 'foo', source = 'foo.l') +bar.Program(target = 'bar', source = 'bar.l') +""" % locals()) + +lex = r""" +%%%% +a printf("A%sA"); +b printf("B%sB"); +%%%% +int +yywrap() +{ + return 1; +} + +main() +{ + yylex(); +} +""" + +test.write('foo.l', lex % ('foo.l', 'foo.l')) + +test.write('bar.l', lex % ('bar.l', 'bar.l')) + +test.run(arguments = 'foo' + _exe, stderr = None) + +test.must_not_exist(test.workpath('wrapper.out')) +test.must_not_exist(test.workpath('lex.backup')) + +test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n") + + + + +test.run(arguments = 'bar' + _exe) + +test.must_match(test.workpath('wrapper.out'), "wrapper.py\n") +test.must_exist(test.workpath('lex.backup')) + +test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n") + + + +test.pass_test() diff --git a/test/TEX/build_dir.py b/test/TEX/build_dir.py index 146f6e1b..49675336 100644 --- a/test/TEX/build_dir.py +++ b/test/TEX/build_dir.py @@ -35,13 +35,18 @@ import TestSCons test = TestSCons.TestSCons() +latex = test.where_is('latex') +if not latex: + test.skip_test("Could not find 'latex'; skipping test.\n") + test.subdir(['docs']) test.write(['SConstruct'], """\ import os -env = Environment(ENV = { 'PATH' : os.environ['PATH'] }) +env = Environment(ENV = { 'PATH' : os.environ['PATH'] }, + TOOLS = ['tex', 'latex', 'dvipdf']) Export(['env']) SConscript(os.path.join('docs', 'SConscript'), diff --git a/test/TEX/subdir-input.py b/test/TEX/subdir-input.py index b92ab732..d16ba5fc 100644 --- a/test/TEX/subdir-input.py +++ b/test/TEX/subdir-input.py @@ -38,11 +38,20 @@ import TestSCons test = TestSCons.TestSCons() +latex = test.where_is('latex') +if not latex: + test.skip_test("Could not find 'latex'; skipping test.\n") + +pdflatex = test.where_is('pdflatex') +if not pdflatex: + test.skip_test("Could not find 'pdflatex'; skipping test.\n") + test.subdir('sub') test.write('SConstruct', """\ -PDF( 'sub/x.tex' ) -DVI( 'sub/x.tex' ) +env = Environment(TOOLS = ['tex', 'pdftex']) +env.PDF( 'sub/x.tex' ) +env.DVI( 'sub/x.tex' ) """) test.write(['sub', 'x.tex'], diff --git a/test/YACC/YACC.py b/test/YACC/YACC.py index d3bc679f..bef959f2 100644 --- a/test/YACC/YACC.py +++ b/test/YACC/YACC.py @@ -65,155 +65,25 @@ sys.exit(0) test.write('SConstruct', """ env = Environment(YACC = r'%(_python_)s myyacc.py', tools=['default', 'yacc']) -env.Program(target = 'aaa', source = 'aaa.y') -env.Program(target = 'bbb', source = 'bbb.yacc') +env.CFile(target = 'aaa', source = 'aaa.y') +env.CFile(target = 'bbb', source = 'bbb.yacc') +env.CXXFile(target = 'ccc', source = 'ccc.yy') +env.CFile(target = 'ddd', source = 'ddd.ym') """ % locals()) -test.write('aaa.y', r""" -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("YACC\n"); - printf("aaa.y\n"); - exit (0); -} -""") - -test.write('bbb.yacc', r""" -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("YACC\n"); - printf("bbb.yacc\n"); - exit (0); -} -""") +test.write('aaa.y', "aaa.y\nYACC\n") +test.write('bbb.yacc', "bbb.yacc\nYACC\n") +test.write('ccc.yy', "ccc.yacc\nYACC\n") +test.write('ddd.ym', "ddd.yacc\nYACC\n") test.run(arguments = '.', stderr = None) -test.run(program = test.workpath('aaa' + _exe), stdout = "myyacc.py\naaa.y\n") -test.run(program = test.workpath('bbb' + _exe), stdout = "myyacc.py\nbbb.yacc\n") - - - -yacc = test.where_is('yacc') - -if yacc: - - test.write("wrapper.py", -"""import os -import string -import sys -open('%s', 'wb').write("wrapper.py\\n") -os.system(string.join(sys.argv[1:], " ")) -""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) - - test.write('SConstruct', """ -foo = Environment(YACCFLAGS='-d') -yacc = foo.Dictionary('YACC') -bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc) -foo.Program(target = 'foo', source = 'foo.y') -bar.Program(target = 'bar', source = 'bar.y') -foo.Program(target = 'hello', source = ['hello.cpp']) -foo.CXXFile(target = 'file.cpp', source = ['file.yy'], YACCFLAGS='-d') -foo.CFile(target = 'not_foo', source = 'foo.y') -""" % locals()) - - yacc = r""" -%%{ -#include - -main() -{ - yyparse(); -} - -yyerror(s) -char *s; -{ - fprintf(stderr, "%%s\n", s); - return 0; -} - -yylex() -{ - int c; - - c = fgetc(stdin); - return (c == EOF) ? 0 : c; -} -%%} -%%%% -input: letter newline { printf("%s\n"); }; -letter: 'a' | 'b'; -newline: '\n'; -""" - - test.write("file.yy", """\ -%token GRAPH_T NODE_T EDGE_T DIGRAPH_T EDGEOP_T SUBGRAPH_T - -%pure_parser - -%% -graph: GRAPH_T - ; - -%% -""") - - test.write("hello.cpp", """\ -#include "file.hpp" - -int main() -{ -} -""") - - test.write('foo.y', yacc % 'foo.y') - - test.write('bar.y', yacc % 'bar.y') - - # Build the foo program - test.run(arguments = 'foo' + _exe, stderr = None) - - test.up_to_date(arguments = 'foo' + _exe) - - test.fail_test(os.path.exists(test.workpath('wrapper.out'))) - - test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "foo.y\n") - - test.fail_test(not os.path.exists(test.workpath('foo.h'))) - - test.run(arguments = '-c .') - - test.fail_test(os.path.exists(test.workpath('foo.h'))) - - # - test.run(arguments = 'not_foo.c') - - test.up_to_date(arguments = 'not_foo.c') - - test.fail_test(os.path.exists(test.workpath('foo.h'))) - test.fail_test(not os.path.exists(test.workpath('not_foo.h'))) - - test.run(arguments = '-c .') - - test.fail_test(os.path.exists(test.workpath('not_foo.h'))) - - # - test.run(arguments = 'bar' + _exe) - - test.up_to_date(arguments = 'bar' + _exe) - - test.fail_test(test.read('wrapper.out') != "wrapper.py\n") +test.must_match('aaa.c', "aaa.y\nmyyacc.py\n") +test.must_match('bbb.c', "bbb.yacc\nmyyacc.py\n") +test.must_match('ccc.cc', "ccc.yacc\nmyyacc.py\n") +test.must_match('ddd.m', "ddd.yacc\nmyyacc.py\n") - test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n") - # - test.run(arguments = '.') - test.up_to_date(arguments = '.') test.pass_test() diff --git a/test/YACC/YACCFLAGS.py b/test/YACC/YACCFLAGS.py index a94bc8f9..b7e2167b 100644 --- a/test/YACC/YACCFLAGS.py +++ b/test/YACC/YACCFLAGS.py @@ -65,83 +65,15 @@ test.write('SConstruct', """ env = Environment(YACC = r'%(_python_)s myyacc.py', YACCFLAGS = '-x', tools=['yacc', '%(linker)s', '%(compiler)s']) -env.Program(target = 'aaa', source = 'aaa.y') +env.CFile(target = 'aaa', source = 'aaa.y') """ % locals()) -test.write('aaa.y', r""" -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("YACCFLAGS\n"); - printf("aaa.y\n"); - exit (0); -} -""") - -test.run(arguments = 'aaa' + _exe, stderr = None) - -test.run(program = test.workpath('aaa' + _exe), stdout = " -x\naaa.y\n") - - - -yacc = test.where_is('yacc') - -if yacc: - - test.write('SConstruct', """ -foo = Environment() -bar = Environment(YACCFLAGS = '-v') -foo.Program(target = 'foo', source = 'foo.y') -bar.Program(target = 'bar', source = 'bar.y') -""") - - yacc = r""" -%%{ -#include - -main() -{ - yyparse(); -} - -yyerror(s) -char *s; -{ - fprintf(stderr, "%%s\n", s); - return 0; -} - -yylex() -{ - int c; - - c = fgetc(stdin); - return (c == EOF) ? 0 : c; -} -%%} -%%%% -input: letter newline { printf("%s\n"); }; -letter: 'a' | 'b'; -newline: '\n'; -""" - - test.write('foo.y', yacc % 'foo.y') - - test.write('bar.y', yacc % 'bar.y') - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.fail_test(os.path.exists(test.workpath('foo.output')) - or os.path.exists(test.workpath('y.output'))) +test.write('aaa.y', "aaa.y\nYACCFLAGS\n") - test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "foo.y\n") +test.run('.', stderr = None) - test.run(arguments = 'bar' + _exe) +test.must_match('aaa.c', "aaa.y\n -x\n") - test.fail_test(not os.path.exists(test.workpath('bar.output')) - and not os.path.exists(test.workpath('y.output'))) - test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n") test.pass_test() diff --git a/test/YACC/YACCVCGFILESUFFIX.py b/test/YACC/YACCVCGFILESUFFIX.py new file mode 100644 index 00000000..b05471a3 --- /dev/null +++ b/test/YACC/YACCVCGFILESUFFIX.py @@ -0,0 +1,87 @@ +#!/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 setting the YACCVCGFILESUFFIX variable. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + + + +test.write('myyacc.py', """\ +import getopt +import os.path +import string +import sys +vcg = None +opts, args = getopt.getopt(sys.argv[1:], 'go:') +for o, a in opts: + if o == '-g': + vcg = 1 + elif o == '-o': + outfile = open(a, 'wb') +for f in args: + infile = open(f, 'rb') + for l in filter(lambda l: l != '/*yacc*/\\n', infile.readlines()): + outfile.write(l) +outfile.close() +if vcg: + base, ext = os.path.splitext(args[0]) + open(base+'.vcgsuffix', 'wb').write(string.join(sys.argv)+'\\n') +sys.exit(0) +""") + +test.write('SConstruct', """ +env = Environment(tools=['default', 'yacc'], + YACC = r'%(_python_)s myyacc.py', + YACCVCGFILESUFFIX = '.vcgsuffix') +env.CXXFile(target = 'aaa', source = 'aaa.yy') +env.CXXFile(target = 'bbb', source = 'bbb.yy', YACCFLAGS = '-g') +""" % locals()) + +test.write('aaa.yy', "aaa.yy\n/*yacc*/\n") +test.write('bbb.yy', "bbb.yy\n/*yacc*/\n") + +test.run(arguments = '.') + +test.must_match('aaa.cc', "aaa.yy\n") +test.must_not_exist('aaa.vcg') +test.must_not_exist('aaa.vcgsuffix') + +test.must_match('bbb.cc', "bbb.yy\n") +test.must_not_exist('bbb.vcg') +test.must_match('bbb.vcgsuffix', "myyacc.py -g -o bbb.cc bbb.yy\n") + +test.up_to_date(arguments = '.') + + + +test.pass_test() diff --git a/test/YACC/live.py b/test/YACC/live.py new file mode 100644 index 00000000..4934570d --- /dev/null +++ b/test/YACC/live.py @@ -0,0 +1,165 @@ +#!/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 YACC and YACCFLAGS with a live yacc compiler. +""" + +import string + +import TestSCons + +_exe = TestSCons._exe +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +yacc = test.where_is('yacc') or test.where_is('bison') + +if not yacc: + test.skip_test('No yacc or bison found; skipping test.\n') + +test.write("wrapper.py", +"""import os +import string +import sys +open('%s', 'wb').write("wrapper.py\\n") +os.system(string.join(sys.argv[1:], " ")) +""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) + +test.write('SConstruct', """ +foo = Environment(YACCFLAGS='-d') +yacc = foo.Dictionary('YACC') +bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc) +foo.Program(target = 'foo', source = 'foo.y') +bar.Program(target = 'bar', source = 'bar.y') +foo.Program(target = 'hello', source = ['hello.cpp']) +foo.CXXFile(target = 'file.cpp', source = ['file.yy'], YACCFLAGS='-d') +foo.CFile(target = 'not_foo', source = 'foo.y') +""" % locals()) + +yacc = r""" +%%{ +#include + +main() +{ + yyparse(); +} + +yyerror(s) +char *s; +{ + fprintf(stderr, "%%s\n", s); + return 0; +} + +yylex() +{ + int c; + + c = fgetc(stdin); + return (c == EOF) ? 0 : c; +} +%%} +%%%% +input: letter newline { printf("%s\n"); }; +letter: 'a' | 'b'; +newline: '\n'; +""" + +test.write("file.yy", """\ +%token GRAPH_T NODE_T EDGE_T DIGRAPH_T EDGEOP_T SUBGRAPH_T + +%pure_parser + +%% +graph: GRAPH_T + ; + +%% +""") + +test.write("hello.cpp", """\ +#include "file.hpp" + +int main() +{ +} +""") + +test.write('foo.y', yacc % 'foo.y') + +test.write('bar.y', yacc % 'bar.y') + + + +test.run(arguments = 'foo' + _exe, stderr = None) + +test.up_to_date(arguments = 'foo' + _exe) + +test.must_not_exist(test.workpath('wrapper.out')) + +test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "foo.y\n") + +test.must_exist(test.workpath('foo.h')) + +test.run(arguments = '-c .') + +test.must_not_exist(test.workpath('foo.h')) + + + +test.run(arguments = 'not_foo.c') + +test.up_to_date(arguments = 'not_foo.c') + +test.must_not_exist(test.workpath('foo.h')) +test.must_exist(test.workpath('not_foo.h')) + +test.run(arguments = '-c .') + +test.must_not_exist(test.workpath('not_foo.h')) + + + +test.run(arguments = 'bar' + _exe) + +test.up_to_date(arguments = 'bar' + _exe) + +test.must_match(test.workpath('wrapper.out'), "wrapper.py\n") + +test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n") + + + +test.run(arguments = '.') + +test.up_to_date(arguments = '.') + + + +test.pass_test() diff --git a/test/ignore-command.py b/test/ignore-command.py index d4de8d3a..c1e6149a 100644 --- a/test/ignore-command.py +++ b/test/ignore-command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation +# __COPYRIGHT__ # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -26,7 +26,7 @@ Test use of a preceding - to ignore the return value from a command. """ -__revision__ = "/home/scons/scons/branch.0/branch.96/baseline/test/option-n.py 0.96.C352 2005/03/26 00:09:23 knight" +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path diff --git a/test/multi.py b/test/multi.py deleted file mode 100644 index 8bcb1f76..00000000 --- a/test/multi.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/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 various cases where a target is "built" by multiple builder calls. -""" - -import os.path -import string - -import TestCmd -import TestSCons - -test = TestSCons.TestSCons(match=TestCmd.match_re) - -_python_ = TestSCons._python_ - -# -# A builder with "multi" set can be called multiple times and -# the source files are added to the list. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = 'file1.out', source = 'file1a.in') -env.B(target = 'file1.out', source = 'file1b.in') -""") - -test.write('file1a.in', 'file1a.in\n') -test.write('file1b.in', 'file1b.in\n') - -test.run(arguments='file1.out') - -test.must_match('file1.out', "file1a.in\nfile1b.in\n") - - -# -# A builder with "multi" not set generates an error on the second call. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=0) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = 'file2.out', source = 'file2a.in') -env.B(target = 'file2.out', source = 'file2b.in') -""") - -test.write('file2a.in', 'file2a.in\n') -test.write('file2b.in', 'file2b.in\n') - -test.run(arguments='file2.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Multiple ways to build the same target were specified for: file2.out (from ['file2a.in'] and from ['file2b.in']) -""") + TestSCons.file_expr) - - -# -# A warning is generated if the calls have different overrides but the -# overrides don't appear to affect the build operation. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = 'file3.out', source = 'file3a.in', foo=1) -env.B(target = 'file3.out', source = 'file3b.in', foo=2) -""") - -test.write('file3a.in', 'file3a.in\n') -test.write('file3b.in', 'file3b.in\n') - -test.run(arguments='file3.out', - stderr=TestSCons.re_escape(""" -scons: warning: Two different environments were specified for target file3.out, -\tbut they appear to have the same action: build(target, source, env) -""") + TestSCons.file_expr) - -# -# A warning is generated if the calls have different overrides but the -# overrides don't appear to affect the build operation. -# - -test.write('build.py',r"""#!/usr/bin/env python -import sys -def build(num, target, source): - file = open(str(target), 'wb') - file.write('%s\n'%num) - for s in source: - file.write(open(str(s), 'rb').read()) -build(sys.argv[1],sys.argv[2],sys.argv[3:]) -""") - -test.write('SConstruct', """ - -B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = 'file03.out', source = 'file03a.in', foo=1) -env.B(target = 'file03.out', source = 'file03b.in', foo=2) -""" % locals()) - -test.write('file03a.in', 'file03a.in\n') -test.write('file03b.in', 'file03b.in\n') - -test.run(arguments='file03.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Two environments with different actions were specified for the same target: file03.out -""") + TestSCons.file_expr) - -# -# Everything works if the two calls have the same overrides. -# - -test.write('build.py',r"""#!/usr/bin/env python -import sys -def build(num, target, source): - file = open(str(target), 'wb') - file.write('%s\n'%num) - for s in source: - file.write(open(str(s), 'rb').read()) -build(sys.argv[1],sys.argv[2],sys.argv[3:]) -""") - -test.write('SConstruct', """ - -B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = 'file4.out', source = 'file4a.in', foo=3) -env.B(target = 'file4.out', source = 'file4b.in', foo=3) -""" % locals()) - -test.write('file4a.in', 'file4a.in\n') -test.write('file4b.in', 'file4b.in\n') - -python_expr = string.replace(TestSCons.python, '\\', '\\\\') -act = TestSCons.re_escape('"%s" build.py \$foo \$TARGET \$SOURCES' % python_expr) - -test.run(arguments='file4.out', - stderr=(""" -scons: warning: Two different environments were specified for target file4.out, -\tbut they appear to have the same action: %s -""" % act) + TestSCons.file_expr) - -test.must_match('file4.out', "3\nfile4a.in\nfile4b.in\n") - - -# -# Two different environments can be used for the same target, so long -# as the actions have the same signature; a warning is generated. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env2 = env.Clone(DIFFERENT_VARIABLE = 'true') -env.B(target = 'file5.out', source = 'file5a.in') -env2.B(target = 'file5.out', source = 'file5b.in') -""") - -test.write('file5a.in', 'file5a.in\n') -test.write('file5b.in', 'file5b.in\n') - -test.run(arguments='file5.out', - stderr=TestSCons.re_escape(""" -scons: warning: Two different environments were specified for target file5.out, -\tbut they appear to have the same action: build(target, source, env) -""") + TestSCons.file_expr) - -test.must_match('file5.out', "file5a.in\nfile5b.in\n") - - -# -# Environments with actions that have different signatures generate -# an error. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=Action(build, varlist=['XXX']), multi=1) -env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo') -env2 = env.Clone(XXX = 'var') -env.B(target = 'file6.out', source = 'file6a.in') -env2.B(target = 'file6.out', source = 'file6b.in') -""") - -test.write('file6a.in', 'file6a.in\n') -test.write('file6b.in', 'file6b.in\n') - -test.run(arguments='file6.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Two environments with different actions were specified for the same target: file6.out -""") + TestSCons.file_expr) - - -# -# A builder without "multi" set can still be called multiple times -# if the calls are the same. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=0) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = 'file7.out', source = 'file7.in') -env.B(target = 'file7.out', source = 'file7.in') -""") - -test.write('file7.in', 'file7.in\n') - -test.run(arguments='file7.out') - -test.must_match('file7.out', "file7.in\n") - - -# -# Trying to call a target with two different "multi" builders -# generates an error. -# - -test.write('SConstruct', """ -def build(env, target, source): - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -def build2(env, target, source): - build(env, target, source) - -B = Builder(action=build, multi=1) -C = Builder(action=build2, multi=1) -env = Environment(BUILDERS = { 'B' : B, 'C' : C }) -env.B(target = 'file8.out', source = 'file8.in') -env.C(target = 'file8.out', source = 'file8.in') -""") - -test.write('file8a.in', 'file8a.in\n') -test.write('file8b.in', 'file8b.in\n') - -test.run(arguments='file8.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Two different builders (B and C) were specified for the same target: file8.out -""") + TestSCons.file_expr) - - -# -# A "multi" builder can be called multiple times with the same target list -# if everything is identical. -# - -test.write('SConstruct', """ -def build(env, target, source): - for t in target: - file = open(str(t), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = ['file9a.out', 'file9b.out'], source = 'file9a.in') -env.B(target = ['file9a.out', 'file9b.out'], source = 'file9b.in') -""") - -test.write('file9a.in', 'file9a.in\n') -test.write('file9b.in', 'file9b.in\n') - -test.run(arguments='file9b.out') - -test.must_match('file9a.out', "file9a.in\nfile9b.in\n") -test.must_match('file9b.out', "file9a.in\nfile9b.in\n") - - -# -# A "multi" builder can NOT be called multiple times with target lists -# that have different orders. This is intentional; the order of the -# targets matter to the builder because the build command can contain -# things like ${TARGET[0]}. -# - -test.write('SConstruct', """ -def build(env, target, source): - for t in target: - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = ['file10a.out', 'file10b.out'], source = 'file10.in') -env.B(target = ['file10b.out', 'file10a.out'], source = 'file10.in') -""") - -test.write('file10.in', 'file10.in\n') - -test.run(arguments='file10.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Two different target sets have a target in common: file10b.out -""") + TestSCons.file_expr) - - -# -# A target file can't be in two different target lists. -# - -# XXX It would be nice if the following two tests could be made to work -# by executing the action once for each unique set of targets. This -# would make it simple to deal with PDB files on Windows like so: -# -# env.Object(['foo.obj', 'vc60.pdb'], 'foo.c') -# env.Object(['bar.obj', 'vc60.pdb'], 'bar.c') - -test.write('SConstruct', """ -def build(env, target, source): - for t in target: - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = ['file11a.out', 'file11b.out'], source = 'file11a.in') -env.B(target = ['file11b.out', 'file11c.out'], source = 'file11b.in') -""") - -test.write('file11a.in', 'file11a.in\n') -test.write('file11b.in', 'file11b.in\n') - -test.run(arguments='file11.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Two different target sets have a target in common: file11b.out -""") + TestSCons.file_expr) - - -# -# A target file can't be a lone target and in a list. -# - -test.write('SConstruct', """ -def build(env, target, source): - for t in target: - file = open(str(target[0]), 'wb') - for s in source: - file.write(open(str(s), 'rb').read()) - -B = Builder(action=build, multi=1) -env = Environment(BUILDERS = { 'B' : B }) -env.B(target = ['file12a.out', 'file12b.out'], source = 'file12a.in') -env.B(target = 'file12a.out', source = 'file12b.in') -""") - -test.write('file12a.in', 'file12a.in\n') -test.write('file12b.in', 'file12b.in\n') - -test.run(arguments='file12.out', - status=2, - stderr=TestSCons.re_escape(""" -scons: *** Cannot build same target `file12a.out' as singular and list -""") + TestSCons.file_expr) - - - -test.pass_test() diff --git a/test/option-v.py b/test/option-v.py index 0626106d..55278abc 100644 --- a/test/option-v.py +++ b/test/option-v.py @@ -38,7 +38,7 @@ test.write('SConstruct', "") # by the packaging build. copyright_marker = '__' + 'COPYRIGHT' + '__' -copyright_years = '2001, 2002, 2003, 2004, 2005, 2006' +copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007' fmt = '(%s|Copyright \\(c\\) %s The SCons Foundation)\n' diff --git a/test/silent-command.py b/test/silent-command.py index 91880bd3..476158a5 100644 --- a/test/silent-command.py +++ b/test/silent-command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation +# __COPYRIGHT__ # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -26,7 +26,7 @@ Test the use of a preceding @ to suppress printing a command. """ -__revision__ = "/home/scons/scons/branch.0/branch.96/baseline/test/option-n.py 0.96.C352 2005/03/26 00:09:23 knight" +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path diff --git a/test/timestamp-fallback.py b/test/timestamp-fallback.py index 288f8d8e..75760233 100644 --- a/test/timestamp-fallback.py +++ b/test/timestamp-fallback.py @@ -49,10 +49,11 @@ raise ImportError os.environ['PYTHONPATH'] = test.workpath('.') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) def build(env, target, source): open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read()) B = Builder(action = build) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(tools = [], BUILDERS = { 'B' : B }) env.B(target = 'f1.out', source = 'f1.in') env.B(target = 'f2.out', source = 'f2.in') env.B(target = 'f3.out', source = 'f3.in') -- 2.26.2