Anonymous avatar Anonymous committed 4808a67

Merged revisions 2527-2645 via svnmerge from
http://scons.tigris.org/svn/scons/branches/core

........
r2528 | stevenknight | 2007-12-13 06:08:21 -0800 (Thu, 13 Dec 2007) | 5 lines

Remove the .del_binfo() method, no longer needed since the Big Signature
Refactoring causes us to visit every Node in order during the DAG walk,
and the BuildInfo object now just holds information for storage in the
.sconsign file.
........
r2529 | stevenknight | 2007-12-13 13:17:15 -0800 (Thu, 13 Dec 2007) | 3 lines

Fix the --keep-going flag so it builds all possible targets even when
a later top-level target depends on a child that failed its build.
........
r2530 | stevenknight | 2007-12-14 04:02:05 -0800 (Fri, 14 Dec 2007) | 4 lines

Issue 1715: BuildDir(duplicate=0) support for Tex/LaTeX.
Re-run LaTeX in response to package warnings.
(Rob Managan)
........
r2531 | stevenknight | 2007-12-14 07:14:31 -0800 (Fri, 14 Dec 2007) | 3 lines

Refactor the max_drift logic around fetching stored signatures into
its own new method.
........
r2532 | stevenknight | 2007-12-14 07:18:44 -0800 (Fri, 14 Dec 2007) | 3 lines

Have get_csig() return the stored content signature if max_drift
says it's okay.
........
r2533 | stevenknight | 2007-12-14 18:34:51 -0800 (Fri, 14 Dec 2007) | 2 lines

Issue 1859: Support SWIG statements like %module(directors="1").
........
r2534 | stevenknight | 2007-12-15 03:51:13 -0800 (Sat, 15 Dec 2007) | 3 lines

Python 2.1 portability fix w.r.t. "import SCons" and "import
SCons.platform.win32" and binding local variables and whatnot.
........
r2535 | stevenknight | 2007-12-15 03:51:56 -0800 (Sat, 15 Dec 2007) | 2 lines

Python 1.5 fix: use the -classic flag when invoking SWIG.
........
r2536 | stevenknight | 2007-12-15 06:03:48 -0800 (Sat, 15 Dec 2007) | 4 lines

Support subclasses of the new-style str() class as input to Builders
and the like. Also speed up all of the Util.is_*() functions when using
new-style classes by just using isinstance() internally.
........
r2537 | stevenknight | 2007-12-15 06:35:49 -0800 (Sat, 15 Dec 2007) | 3 lines

Issue 1851: Fix being able to use $PDB and $WINDOWS_INSERT_MANIFEST together.
(Benoit Belley)
........
r2538 | stevenknight | 2007-12-15 06:59:43 -0800 (Sat, 15 Dec 2007) | 3 lines

Handle dangling entries for the Intel C compiler in the Windows registry.
(Benoit Belley)
........
r2539 | stevenknight | 2007-12-15 09:51:59 -0800 (Sat, 15 Dec 2007) | 2 lines

Reorganize library-related tests into a separate subdirectory.
........
r2540 | stevenknight | 2007-12-15 09:57:29 -0800 (Sat, 15 Dec 2007) | 4 lines

Issue 1850: better support for non-standard shared library prefixes and
suffixes by stripping all prefixes and suffixes in lists of $SHLIBPREFIXES
and $SHLIBSUFFIXES. (Benoit Belley)
........
r2541 | stevenknight | 2007-12-15 18:49:15 -0800 (Sat, 15 Dec 2007) | 2 lines

Python 1.5 portability fixes.
........
r2542 | stevenknight | 2007-12-15 19:02:39 -0800 (Sat, 15 Dec 2007) | 3 lines

Issue 1768: Have the D language scanner search for .di files as well
as .d files. (Jerome Berger)
........
r2543 | stevenknight | 2007-12-16 14:31:40 -0800 (Sun, 16 Dec 2007) | 3 lines

Add a find_include_names() method to the Scanner.Classic class to abstract
out how included names can be generated by subclasses. (Jerome Berger)
........
r2544 | stevenknight | 2007-12-16 14:31:54 -0800 (Sun, 16 Dec 2007) | 3 lines

Add a find_include_names() method to the Scanner.Classic class to abstract
out how included names can be generated by subclasses. (Jerome Berger)
........
r2545 | stevenknight | 2007-12-16 15:04:43 -0800 (Sun, 16 Dec 2007) | 3 lines

Issue 1860: Support the D scanner returning multiple modules from a
single import statement. (Jerome Berger)
........
r2546 | stevenknight | 2007-12-16 17:41:17 -0800 (Sun, 16 Dec 2007) | 3 lines

Issue 1861: Fix the ability to #include a file (or search other $*PATH
variables) that has an absoluate path.
........
r2547 | stevenknight | 2007-12-18 08:09:59 -0800 (Tue, 18 Dec 2007) | 2 lines

Replace uses of "is_List() or is_Tuple()" with is_Sequence().
........
r2548 | stevenknight | 2007-12-18 08:13:14 -0800 (Tue, 18 Dec 2007) | 2 lines

Report the incorrect value in assertions.
........
r2549 | stevenknight | 2007-12-19 07:58:56 -0800 (Wed, 19 Dec 2007) | 3 lines

Fix handling #includes of absolute path names when the path doesn't
exist (implicitly, because it's #ifdef'ed out).
........
r2550 | stevenknight | 2007-12-19 08:29:24 -0800 (Wed, 19 Dec 2007) | 4 lines

Fix test path examination when the temporary directory location
is redirected via symlinks (e.g. /usr/tmp -> /var/tmp on Red Hat).
(Benoit Belley)
........
r2551 | stevenknight | 2007-12-19 08:30:17 -0800 (Wed, 19 Dec 2007) | 2 lines

Fix scons-time path reporting when symlinks are involved. (Benoit Belley)
........
r2552 | stevenknight | 2007-12-19 22:51:18 -0800 (Wed, 19 Dec 2007) | 4 lines

Issue 1855: Reduce the worker thread stack size to a default of 256
Kbytes. Add a --stack-size= command-line option, also configurable
via SetOption('stack_size'). (Benoit Belley)
........
r2553 | stevenknight | 2007-12-20 18:25:50 -0800 (Thu, 20 Dec 2007) | 2 lines

Skip this test if SWIG isn't installed.
........
r2554 | stevenknight | 2007-12-20 18:26:21 -0800 (Thu, 20 Dec 2007) | 2 lines

Accomodate slightly different permissions errors on Ubuntu Gutsy.
........
r2555 | stevenknight | 2007-12-21 02:12:09 -0800 (Fri, 21 Dec 2007) | 3 lines

Fix a Python 2.2 quirk in the reported file name ("<string>") when
encountering a SyntaxError in a SConstruct file.
........
r2556 | stevenknight | 2007-12-21 02:12:35 -0800 (Fri, 21 Dec 2007) | 2 lines

Enforce order between the build of f1.out and f2.out.
........
r2557 | stevenknight | 2007-12-21 02:12:55 -0800 (Fri, 21 Dec 2007) | 2 lines

Don't die if the Python version doesn't have os.path.realpath().
........
r2558 | stevenknight | 2007-12-21 02:13:19 -0800 (Fri, 21 Dec 2007) | 2 lines

Refactor the test/build-errors.py script into separate scripts for each test.
........
r2559 | stevenknight | 2007-12-21 08:08:12 -0800 (Fri, 21 Dec 2007) | 3 lines

Issue 1864: Add a CheckDeclaration() call to configure contexts.
(David Cournapeau)
........
r2560 | stevenknight | 2007-12-21 08:18:47 -0800 (Fri, 21 Dec 2007) | 2 lines

Issue 1865: Improve the CheckTypeSize() code. (David Cournapeau)
........
r2561 | stevenknight | 2007-12-21 08:21:47 -0800 (Fri, 21 Dec 2007) | 2 lines

Fix os.path.realpath() handling (a Python 2.1 portability issue).
........
r2562 | stevenknight | 2007-12-21 14:08:39 -0800 (Fri, 21 Dec 2007) | 2 lines

Split CPPDEFINES.py into separate sub-test scripts.
........
r2563 | stevenknight | 2007-12-21 15:56:26 -0800 (Fri, 21 Dec 2007) | 6 lines

Support proper expansion of construction variables containing lists
within expansions like $CPPPATH.
Change env.subst() to return a list, not a joined string, when the
input is a list.
(Konstantin Bozhikov)
........
r2564 | stevenknight | 2007-12-22 04:15:11 -0800 (Sat, 22 Dec 2007) | 2 lines

Normalize the ModDate field when comparing generated PDF files.
........
r2565 | stevenknight | 2007-12-22 22:01:45 -0800 (Sat, 22 Dec 2007) | 5 lines

Java test refactoring to commonize construction environment initialization
and searching for javac / javah / jar / rmic.
Don't look for *_Skel.class files to be created by Java 1.[56].
Minor Java code changes to deal with compiler warnings.
........
r2566 | stevenknight | 2007-12-23 05:20:45 -0800 (Sun, 23 Dec 2007) | 2 lines

Don't still look for *_Skel.class files.
........
r2567 | stevenknight | 2007-12-23 07:30:36 -0800 (Sun, 23 Dec 2007) | 5 lines

Fix Intel C compiler issues:
Issue 1863: Fix failure to match /opt/intel_cc_* directories. (Benoit Belley)
Issue 1866: Fix topdir when the version isn't specified. (Jonas Olsson)
Issue 1867: Fix use of network licenses. (Jonas Olsson)
........
r2573 | stevenknight | 2008-01-01 09:59:16 -0800 (Tue, 01 Jan 2008) | 3 lines

Add asynchronous subprocess communication via new start() and finish()
methods.
........
r2574 | stevenknight | 2008-01-01 10:02:26 -0800 (Tue, 01 Jan 2008) | 4 lines

Minor code cleanup: attach the version string to the options parser
object, instead of passing it in to deal with the lack of nested scopes
in Python 1.5.2.
........
r2575 | stevenknight | 2008-01-01 10:08:46 -0800 (Tue, 01 Jan 2008) | 4 lines

Rename the CacheDir class and let the name CacheDir be a variable that
can be reset at will, depending on whether CacheDir() support is enabled
or disabled at any particular time.
........
r2576 | stevenknight | 2008-01-01 10:14:58 -0800 (Tue, 01 Jan 2008) | 2 lines

Restore the Node.del_binfo() method and its call in Node.clear().
........
r2577 | stevenknight | 2008-01-02 07:51:25 -0800 (Wed, 02 Jan 2008) | 6 lines

Refactor CacheDir support (again) for --interactive mode. Delay effects
of --cache-* settings until they're needed by getting rid of the Null()
object pattern and the functional programming idiom of replacing the
CacheDebug method. Have the Environment.CacheDir() method just record
the path for later instantiation.
........
r2578 | stevenknight | 2008-01-02 18:48:12 -0800 (Wed, 02 Jan 2008) | 3 lines

Issue 1657: Add a --interactive option to create a command-line
interpreter for re-building targets without re-reading SConscript files.
........
r2579 | stevenknight | 2008-01-02 21:54:38 -0800 (Wed, 02 Jan 2008) | 2 lines

Python 1.5.2 portability fix (no use of +=).
........
r2580 | stevenknight | 2008-01-02 21:54:47 -0800 (Wed, 02 Jan 2008) | 3 lines

Use a regular expression to avoid having to match a specific
MD5 checksum value in the --cache-debug output.
........
r2581 | stevenknight | 2008-01-02 21:54:59 -0800 (Wed, 02 Jan 2008) | 4 lines

Don't bother looking for shlex.split(), since our compatibility layer
provides it in older Python version. Make the compatibility version of
shlex.split() not treat '.' as a token separator.
........
r2582 | stevenknight | 2008-01-02 21:56:15 -0800 (Wed, 02 Jan 2008) | 3 lines

Python 1.5.2 portability fixes: no list comprehensions, no nested
scopes, no "for x in" a dictionary.
........
r2583 | stevenknight | 2008-01-03 07:39:59 -0800 (Thu, 03 Jan 2008) | 3 lines

Fix a left-over use of a string method.
Fix printing --interactive help text, which I outright broke last checkin.
........
r2584 | stevenknight | 2008-01-03 07:58:56 -0800 (Thu, 03 Jan 2008) | 4 lines

Import the vanilla Python2.5 shlex module, which we'll use as a basis
for retrofitting to old Python versions to provide shlex.split()
functionality.
........
r2585 | stevenknight | 2008-01-03 08:01:02 -0800 (Thu, 03 Jan 2008) | 3 lines

Modifications to the vanilla Python 2.5 shlex module to make it work
back to Python 1.5.
........
r2586 | stevenknight | 2008-01-03 08:04:31 -0800 (Thu, 03 Jan 2008) | 3 lines

Use the new shlex compatibility module if we're using an old version of
Python with a native shlex module that has no shlex.split() function.
........
r2587 | stevenknight | 2008-01-03 09:31:15 -0800 (Thu, 03 Jan 2008) | 3 lines

Fix the ParseFlags() unit test now that we have a real shlex.split()
function even on earlier Python versions.
........
r2588 | stevenknight | 2008-01-06 04:52:05 -0800 (Sun, 06 Jan 2008) | 3 lines

Add compat/_scons_shlex.py to exception lists for __copyright__ and
__revision__ strings.
........
r2589 | stevenknight | 2008-01-06 06:32:07 -0800 (Sun, 06 Jan 2008) | 2 lines

Remove leftover debug print.
........
r2590 | stevenknight | 2008-01-06 07:35:46 -0800 (Sun, 06 Jan 2008) | 3 lines

Change the test to work by wrapping the public .__call__() method
of the C scanner, instead of the internal .scan() method.
........
r2591 | stevenknight | 2008-01-06 07:39:12 -0800 (Sun, 06 Jan 2008) | 3 lines

Use the public CScan.path() method, not the internal CScan.path_function
attribute.
........
r2592 | stevenknight | 2008-01-07 02:55:53 -0800 (Mon, 07 Jan 2008) | 2 lines

Use a tuple instead of a list for the cpp module path(s).
........
r2593 | stevenknight | 2008-01-07 03:10:28 -0800 (Mon, 07 Jan 2008) | 2 lines

Don't die if a macro function expands to a non-string (an integer).
........
r2594 | stevenknight | 2008-01-07 03:29:12 -0800 (Mon, 07 Jan 2008) | 3 lines

Python 1.5 throws TypeError, not AttributeError if you try to
string.split() a non-string value.
........
r2595 | stevenknight | 2008-01-07 03:30:18 -0800 (Mon, 07 Jan 2008) | 3 lines

Reduce duplicate execution of individual test_*() unit test methods
by eliminating duplicates (if the set() type is avaiable).
........
r2596 | stevenknight | 2008-01-07 06:57:30 -0800 (Mon, 07 Jan 2008) | 6 lines

Add a basic test of in-line #include handling.
Sort the test names.
Don't os.path.join() the directory name if we
find the file in the current directory.
Use os.curdir instead of hard-coding '.' as the current directory.
........
r2597 | stevenknight | 2008-01-07 06:59:29 -0800 (Mon, 07 Jan 2008) | 3 lines

Read files with a new .read_file() method, so it can be overridden
by subclasses.
........
r2598 | stevenknight | 2008-01-07 17:59:50 -0800 (Mon, 07 Jan 2008) | 6 lines

Record the name of the file currently being processed.

Make the public API (the .__call__() method) passing in a file name to
be opened, and have it call a new, separate .process_contents() method
(the old .__call__() method) for handling in-memory strings.
........
r2599 | stevenknight | 2008-01-07 20:03:18 -0800 (Mon, 07 Jan 2008) | 3 lines

Make the test failure informative when we don't find the includes
we expect by printing the expected string and actual output.
........
r2600 | stevenknight | 2008-01-07 20:24:21 -0800 (Mon, 07 Jan 2008) | 2 lines

Handle no white space after #include (e.g. #include<foo.h>).
........
r2601 | stevenknight | 2008-01-07 21:01:27 -0800 (Mon, 07 Jan 2008) | 4 lines

Fixes for older Python versions:
No tempfile.mktemp(prefix=) argument.
No string methods.
........
r2602 | stevenknight | 2008-01-08 20:57:30 -0800 (Tue, 08 Jan 2008) | 3 lines

Fix command-line editing of --interactive mode with the readline module
by only changing sys.stdout to our Unbuffered class if it isn't a tty.
........
r2603 | stevenknight | 2008-01-08 22:12:20 -0800 (Tue, 08 Jan 2008) | 4 lines

Fix the --interactive "build" command with no targets: build the
specified Default() targets; issue an error message but don't exit if
Default(None) is explicity specified.
........
r2604 | stevenknight | 2008-01-09 05:00:36 -0800 (Wed, 09 Jan 2008) | 9 lines

Improve Python functions used as actions by incorporating into their
build signatures:
- literal values referenced by the byte code.
- values of default arguments
- code of nested functions
- values of variables captured by closures
- names of referenced global variables and functions
(Benoit Belley)
........
r2605 | stevenknight | 2008-01-09 06:39:03 -0800 (Wed, 09 Jan 2008) | 4 lines

Add a Configure.Define() method for adding arbitrary #define lines
to generated configure header files.
(David Cournapeau)
........
r2606 | stevenknight | 2008-01-09 07:33:21 -0800 (Wed, 09 Jan 2008) | 4 lines

Issue 1858: Fix the closing message when --clean and --keep-going are
both used so it only reports errors if some actually occurred.
(Benoit Belley)
........
r2607 | stevenknight | 2008-01-09 07:51:55 -0800 (Wed, 09 Jan 2008) | 3 lines

Issue 1843: Add a gfortran Tool module for the GNU F95/F2003 compiler.
(David Cournapeau)
........
r2608 | stevenknight | 2008-01-09 09:31:15 -0800 (Wed, 09 Jan 2008) | 4 lines

Issue 1733: If $JARCHDIR isn't set explicitly, use the .java_classdir
attribute that was set when the Java() Builder built the .class files.
(Jan Nijtmans)
........
r2609 | stevenknight | 2008-01-09 11:27:28 -0800 (Wed, 09 Jan 2008) | 4 lines

Allow Scanner.FindPathDirs objects to not take a dir= keyword argument
when called. (The code already detects that and uses the current
directory if necessary.)
........
r2610 | stevenknight | 2008-01-09 12:23:26 -0800 (Wed, 09 Jan 2008) | 3 lines

Allow subclass overrides of results-handling by the addition of
new initialize_result() and finalize_result() methods.
........
r2611 | stevenknight | 2008-01-09 14:49:50 -0800 (Wed, 09 Jan 2008) | 6 lines

Capture new C Scanner glue code that knows how to use $CPPDEFINES to
evaluate CPP #if/#ifdef/#elif/#else lines. Currently disabled (including
the test script that validates the behavior) while we look for the right
way to let users configure the feature, and work on performance issues
with its O(N*M) algorithm.
........
r2612 | stevenknight | 2008-01-24 20:42:57 -0800 (Thu, 24 Jan 2008) | 3 lines

Fix regular expression comparisons on Windows by escaping the \ path
separators.
........
r2613 | stevenknight | 2008-01-24 20:49:04 -0800 (Thu, 24 Jan 2008) | 3 lines

Rename a created stub script from "cmd.py" so it doesn't mistakenly
get imported by the "import cmd" statement in Script/Interactive.py.
........
r2614 | stevenknight | 2008-01-24 20:56:05 -0800 (Thu, 24 Jan 2008) | 4 lines

Fix a race condition between the actions executed by the worker threads
by having the dependent action print its own execution line, and telling
SCons to treat it silently (strfunction=None).
........
r2615 | stevenknight | 2008-01-24 20:59:03 -0800 (Thu, 24 Jan 2008) | 2 lines

Remove left-over commented-out lines.
........
r2616 | stevenknight | 2008-01-24 21:59:49 -0800 (Thu, 24 Jan 2008) | 7 lines

Windows portability in --interactive mode and its tests:

Quote target names that may have spaces in them. Use the .exe suffix
on a generated executable. Use the subprocess .wait() method to get the
subprocess exit status when shelling out on Windows. Use an Unbuffered
object for stderr (when it's not a tty).
........
r2617 | stevenknight | 2008-01-24 22:14:49 -0800 (Thu, 24 Jan 2008) | 3 lines

Issue 1886: Fix the ability to build Aliases in --interactive mode.
(Gary Oberbrunner)
........
r2618 | stevenknight | 2008-01-24 22:33:29 -0800 (Thu, 24 Jan 2008) | 3 lines

Issue 1886: Handle Python versions that throw TypeError when they can't
pickle a nested function. (Gary Oberbrunner)
........
r2619 | stevenknight | 2008-01-24 22:38:44 -0800 (Thu, 24 Jan 2008) | 3 lines

Fix the LoadableModule.py test when run on Intel Macs (look for the
string i386 in the file output, in addition to ppc).
........
r2620 | stevenknight | 2008-01-25 06:50:43 -0800 (Fri, 25 Jan 2008) | 4 lines

Issue 1892: use "link" instead of "gnulink" for the Mac tool chain,
since it doesn't understand the -rpath option and can't use $RPATH.
(David Cournapeau)
........
r2621 | stevenknight | 2008-01-25 07:51:56 -0800 (Fri, 25 Jan 2008) | 2 lines

Issue 1893: add Intel C compiler support on Mac OS X. (Benoit Belley)
........
r2622 | stevenknight | 2008-01-25 21:48:16 -0800 (Fri, 25 Jan 2008) | 2 lines

Fix how we handle falling back to timestamps when no md5.py module exists.
........
r2623 | stevenknight | 2008-01-26 16:55:56 -0800 (Sat, 26 Jan 2008) | 5 lines

Work around a metaclass / new.instancemethod() bug in base Python 2.2 by
disallowing --debug=memoizer functionality if Python can't handle the
Memoizer initialization (much like we do for earlier Python versions
that don't have metaclasses at all).
........
r2624 | stevenknight | 2008-01-26 18:22:14 -0800 (Sat, 26 Jan 2008) | 4 lines

Fix CacheDir by simplifying how the NullEnvironment hands back something
that looks enough like a CacheDir object that the rest of the code
doesn't require special handling.
........
r2625 | stevenknight | 2008-01-26 20:56:17 -0800 (Sat, 26 Jan 2008) | 2 lines

Have the "scons-time time" subcommand handle empty files gracefully.
........
r2626 | stevenknight | 2008-01-26 20:57:21 -0800 (Sat, 26 Jan 2008) | 3 lines

Add a Trace() statement to the Node.changed() method if the dependency
lists are different lengths.
........
r2627 | stevenknight | 2008-01-26 21:30:59 -0800 (Sat, 26 Jan 2008) | 3 lines

Have the "scons-time time --which" subcommand handle files that don't
contain the requested results
........
r2628 | stevenknight | 2008-01-26 21:52:51 -0800 (Sat, 26 Jan 2008) | 2 lines

Fix the ability to draw vertical bars with --fmt gnuplot option.
........
r2629 | stevenknight | 2008-01-26 22:23:10 -0800 (Sat, 26 Jan 2008) | 3 lines

Allow "scons-time run" to copy non-archive files for timing.
Document the archive_list config file variable.
........
r2630 | stevenknight | 2008-01-27 10:38:11 -0800 (Sun, 27 Jan 2008) | 3 lines

Use the maximum Y value, not the maximum X value, as the top Y endpoint
of a vertical bar drawn with --fmt=gnuplot.
........
r2631 | stevenknight | 2008-01-27 12:05:40 -0800 (Sun, 27 Jan 2008) | 2 lines

Make scons-time more robust when handling log files that have no results.
........
r2632 | stevenknight | 2008-01-27 12:49:02 -0800 (Sun, 27 Jan 2008) | 2 lines

Rotate label positions so they don't overwrite each other.
........
r2633 | stevenknight | 2008-01-27 16:21:17 -0800 (Sun, 27 Jan 2008) | 2 lines

Extend vertical bars to graph top, not maximum X value.
........
r2634 | stevenknight | 2008-01-27 18:08:19 -0800 (Sun, 27 Jan 2008) | 2 lines

Capture three configurations for timing various aspects of SCons.
........
r2635 | stevenknight | 2008-01-28 04:55:12 -0800 (Mon, 28 Jan 2008) | 2 lines

Fix jar calls to use "tf" instead of "-t -f" for compatibility with Sun.
........
r2636 | stevenknight | 2008-01-28 12:49:58 -0800 (Mon, 28 Jan 2008) | 6 lines

Refactor cut-and-paste tempdir_re() function into a common method
in QMTest/TestSCons_time.py.
In the refactored code, fix typo of os.path.relpath() where we meant
os.path.realpath(), so we follow the /tmp -> /private/tmp symlink on
Mac OS X.
........
r2637 | stevenknight | 2008-01-28 15:18:14 -0800 (Mon, 28 Jan 2008) | 5 lines

Apple portability in the test for explicit "No such file" error messages
from trying to fork()/exec() a non-existent file name.
Refactor the tests for (non-)expected output in stderr so they're
informative if they fail.
........
r2638 | stevenknight | 2008-01-28 17:54:29 -0800 (Mon, 28 Jan 2008) | 3 lines

Make the test output deterministic by making the InstallAs() targets
(file[23].out) depend on the Install() target (file1.out).
........
r2639 | stevenknight | 2008-01-28 21:37:38 -0800 (Mon, 28 Jan 2008) | 4 lines

On Mac OS X, add -w to LINKFLAGS to suppress warnings about the
directories we specify as -L arguments which don't actually exist.
We just want to make sure that the right directory names show up.
........
r2640 | stevenknight | 2008-01-28 21:38:36 -0800 (Mon, 28 Jan 2008) | 3 lines

On Mac OS X, the generated include file for C++ just tacks ".h" on the end
of the generated .cpp file name. Define $YACCHXXFILESUFFIX accordingly.
........
r2641 | stevenknight | 2008-01-29 04:56:18 -0800 (Tue, 29 Jan 2008) | 3 lines

Add the src/CHANGES.txt for the previous change (Mac OS X bison behavior).
Add a "bison" application entity to the DocBook infrastructure.
........
r2642 | stevenknight | 2008-01-30 05:09:02 -0800 (Wed, 30 Jan 2008) | 5 lines

Improve QT tests for Mac OS X:
More general regular expression match for a "Generated moc file" warning.
Copy libmyqt.dylib to the same directory as the "aaa" executable so
it's found when we run it.
........
r2643 | stevenknight | 2008-01-30 05:19:23 -0800 (Wed, 30 Jan 2008) | 2 lines

Skip the test of Java handling SWIG dependencies if swig isn't installed.
........
r2644 | stevenknight | 2008-01-30 06:44:30 -0800 (Wed, 30 Jan 2008) | 2 lines

Remove left-over print statement.
........
r2645 | stevenknight | 2008-01-30 06:52:54 -0800 (Wed, 30 Jan 2008) | 2 lines

Mac OS X fix: use .dylib, not .so, in the list of "weird suffixes" we test.
........

Comments (0)

Files changed (186)

QMTest/TestCmd.py

 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
 __author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCmd.py 0.30.D001 2007/10/01 16:53:55 knight"
-__version__ = "0.30"
+__revision__ = "TestCmd.py 0.31.D001 2008/01/01 09:05:59 knight"
+__version__ = "0.31"
 
+import errno
 import os
 import os.path
-import popen2
 import re
 import shutil
 import stat
 
     default_sleep_seconds = 1
 
+
+
+try:
+    import subprocess
+except ImportError:
+    # The subprocess module doesn't exist in this version of Python,
+    # so we're going to cobble up something that looks just enough
+    # like its API for our purposes below.
+    import new
+
+    subprocess = new.module('subprocess')
+
+    subprocess.PIPE = 'PIPE'
+    subprocess.STDOUT = 'STDOUT'
+    subprocess.mswindows = (sys.platform == 'win32')
+
+    try:
+        import popen2
+        popen2.Popen3
+    except AttributeError:
+        class Popen3:
+            universal_newlines = 1
+            def __init__(self, command, **kw):
+                if sys.platform == 'win32' and command[0] == '"':
+                    command = '"' + command + '"'
+                (stdin, stdout, stderr) = os.popen3(' ' + command)
+                self.stdin = stdin
+                self.stdout = stdout
+                self.stderr = stderr
+            def close_output(self):
+                self.stdout.close()
+                self.resultcode = self.stderr.close()
+            def wait(self):
+                return self.resultcode
+
+    else:
+        try:
+            popen2.Popen4
+        except AttributeError:
+            # A cribbed Popen4 class, with some retrofitted code from
+            # the Python 1.5 Popen3 class methods to do certain things
+            # by hand.
+            class Popen4(popen2.Popen3):
+                childerr = None
+
+                def __init__(self, cmd, bufsize=-1):
+                    p2cread, p2cwrite = os.pipe()
+                    c2pread, c2pwrite = os.pipe()
+                    self.pid = os.fork()
+                    if self.pid == 0:
+                        # Child
+                        os.dup2(p2cread, 0)
+                        os.dup2(c2pwrite, 1)
+                        os.dup2(c2pwrite, 2)
+                        for i in range(3, popen2.MAXFD):
+                            try:
+                                os.close(i)
+                            except: pass
+                        try:
+                            os.execvp(cmd[0], cmd)
+                        finally:
+                            os._exit(1)
+                        # Shouldn't come here, I guess
+                        os._exit(1)
+                    os.close(p2cread)
+                    self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
+                    os.close(c2pwrite)
+                    self.fromchild = os.fdopen(c2pread, 'r', bufsize)
+                    popen2._active.append(self)
+
+            popen2.Popen4 = Popen4
+
+        class Popen3(popen2.Popen3, popen2.Popen4):
+            universal_newlines = 1
+            def __init__(self, command, **kw):
+                if kw.get('stderr') == 'STDOUT':
+                    apply(popen2.Popen4.__init__, (self, command, 1))
+                else:
+                    apply(popen2.Popen3.__init__, (self, command, 1))
+                self.stdin = self.tochild
+                self.stdout = self.fromchild
+                self.stderr = self.childerr
+
+    subprocess.Popen = Popen3
+
+
+
+# From Josiah Carlson,
+# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554
+
+PIPE = subprocess.PIPE
+
+if subprocess.mswindows:
+    from win32file import ReadFile, WriteFile
+    from win32pipe import PeekNamedPipe
+    import msvcrt
+else:
+    import select
+    import fcntl
+
+    try:                    fcntl.F_GETFL
+    except AttributeError:  fcntl.F_GETFL = 3
+
+    try:                    fcntl.F_SETFL
+    except AttributeError:  fcntl.F_SETFL = 4
+
+class Popen(subprocess.Popen):
+    def recv(self, maxsize=None):
+        return self._recv('stdout', maxsize)
+
+    def recv_err(self, maxsize=None):
+        return self._recv('stderr', maxsize)
+
+    def send_recv(self, input='', maxsize=None):
+        return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
+
+    def get_conn_maxsize(self, which, maxsize):
+        if maxsize is None:
+            maxsize = 1024
+        elif maxsize < 1:
+            maxsize = 1
+        return getattr(self, which), maxsize
+
+    def _close(self, which):
+        getattr(self, which).close()
+        setattr(self, which, None)
+
+    if subprocess.mswindows:
+        def send(self, input):
+            if not self.stdin:
+                return None
+
+            try:
+                x = msvcrt.get_osfhandle(self.stdin.fileno())
+                (errCode, written) = WriteFile(x, input)
+            except ValueError:
+                return self._close('stdin')
+            except (subprocess.pywintypes.error, Exception), why:
+                if why[0] in (109, errno.ESHUTDOWN):
+                    return self._close('stdin')
+                raise
+
+            return written
+
+        def _recv(self, which, maxsize):
+            conn, maxsize = self.get_conn_maxsize(which, maxsize)
+            if conn is None:
+                return None
+
+            try:
+                x = msvcrt.get_osfhandle(conn.fileno())
+                (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
+                if maxsize < nAvail:
+                    nAvail = maxsize
+                if nAvail > 0:
+                    (errCode, read) = ReadFile(x, nAvail, None)
+            except ValueError:
+                return self._close(which)
+            except (subprocess.pywintypes.error, Exception), why:
+                if why[0] in (109, errno.ESHUTDOWN):
+                    return self._close(which)
+                raise
+
+            #if self.universal_newlines:
+            #    read = self._translate_newlines(read)
+            return read
+
+    else:
+        def send(self, input):
+            if not self.stdin:
+                return None
+
+            if not select.select([], [self.stdin], [], 0)[1]:
+                return 0
+
+            try:
+                written = os.write(self.stdin.fileno(), input)
+            except OSError, why:
+                if why[0] == errno.EPIPE: #broken pipe
+                    return self._close('stdin')
+                raise
+
+            return written
+
+        def _recv(self, which, maxsize):
+            conn, maxsize = self.get_conn_maxsize(which, maxsize)
+            if conn is None:
+                return None
+
+            try:
+                flags = fcntl.fcntl(conn, fcntl.F_GETFL)
+            except TypeError:
+                flags = None
+            else:
+                if not conn.closed:
+                    fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+
+            try:
+                if not select.select([conn], [], [], 0)[0]:
+                    return ''
+
+                r = conn.read(maxsize)
+                if not r:
+                    return self._close(which)
+
+                #if self.universal_newlines:
+                #    r = self._translate_newlines(r)
+                return r
+            finally:
+                if not conn.closed and not flags is None:
+                    fcntl.fcntl(conn, fcntl.F_SETFL, flags)
+
+disconnect_message = "Other end disconnected!"
+
+def recv_some(p, t=.1, e=1, tr=5, stderr=0):
+    if tr < 1:
+        tr = 1
+    x = time.time()+t
+    y = []
+    r = ''
+    pr = p.recv
+    if stderr:
+        pr = p.recv_err
+    while time.time() < x or r:
+        r = pr()
+        if r is None:
+            if e:
+                raise Exception(disconnect_message)
+            else:
+                break
+        elif r:
+            y.append(r)
+        else:
+            time.sleep(max((x-time.time())/tr, 0))
+    return ''.join(y)
+
+def send_all(p, data):
+    while len(data):
+        sent = p.send(data)
+        if sent is None:
+            raise Exception(disconnect_message)
+        data = buffer(data, sent)
+
+
+
 class TestCmd:
     """Class TestCmd
     """
         dir = self.canonicalize(dir)
         os.rmdir(dir)
 
-    def run(self, program = None,
-                  interpreter = None,
-                  arguments = None,
-                  chdir = None,
-                  stdin = None,
-                  universal_newlines = None):
-        """Runs a test of the program or script for the test
-        environment.  Standard output and error output are saved for
-        future retrieval via the stdout() and stderr() methods.
+    def start(self, program = None,
+                    interpreter = None,
+                    arguments = None,
+                    universal_newlines = None,
+                    **kw):
+        """
+        Starts a program or script for the test environment.
 
         The specified program will have the original directory
-        prepending unless it is enclosed in a [list].
+        prepended unless it is enclosed in a [list].
         """
-        if chdir:
-            oldcwd = os.getcwd()
-            if not os.path.isabs(chdir):
-                chdir = os.path.join(self.workpath(chdir))
-            if self.verbose:
-                sys.stderr.write("chdir(" + chdir + ")\n")
-            os.chdir(chdir)
         if program:
             if type(program) == type('') and not os.path.isabs(program):
                 program = os.path.join(self._cwd, program)
         if universal_newlines is None:
             universal_newlines = self.universal_newlines
 
-        try:
-            import subprocess
-        except ImportError:
-            try:
-                Popen3 = popen2.Popen3
-            except AttributeError:
-                class Popen3:
-                    def __init__(self, command):
-                        (stdin, stdout, stderr) = os.popen3(' ' + command)
-                        self.stdin = stdin
-                        self.stdout = stdout
-                        self.stderr = stderr
-                    def close_output(self):
-                        self.stdout.close()
-                        self.resultcode = self.stderr.close()
-                    def wait(self):
-                        return self.resultcode
-                if sys.platform == 'win32' and cmd_string[0] == '"':
-                    cmd_string = '"' + cmd_string + '"'
-                p = Popen3(cmd_string)
-            else:
-                p = Popen3(cmd, 1)
-                p.stdin = p.tochild
-                p.stdout = p.fromchild
-                p.stderr = p.childerr
+        combine = kw.get('combine', self.combine)
+        if combine:
+            stderr_value = subprocess.STDOUT
         else:
-            p = subprocess.Popen(cmd,
-                                 stdin=subprocess.PIPE,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE,
-                                 universal_newlines=universal_newlines)
+            stderr_value = subprocess.PIPE
 
+        return Popen(cmd,
+                     stdin=subprocess.PIPE,
+                     stdout=subprocess.PIPE,
+                     stderr=stderr_value,
+                     universal_newlines=universal_newlines)
+
+    def finish(self, popen, **kw):
+        """
+        Finishes and waits for the process being run under control of
+        the specified popen argument, recording the exit status,
+        standard output and error output.
+        """
+        popen.stdin.close()
+        self.status = popen.wait()
+        if not self.status:
+            self.status = 0
+        self._stdout.append(popen.stdout.read())
+        if popen.stderr:
+            stderr = popen.stderr.read()
+        else:
+            stderr = ''
+        self._stderr.append(stderr)
+
+    def run(self, program = None,
+                  interpreter = None,
+                  arguments = None,
+                  chdir = None,
+                  stdin = None,
+                  universal_newlines = None):
+        """Runs a test of the program or script for the test
+        environment.  Standard output and error output are saved for
+        future retrieval via the stdout() and stderr() methods.
+
+        The specified program will have the original directory
+        prepended unless it is enclosed in a [list].
+        """
+        if chdir:
+            oldcwd = os.getcwd()
+            if not os.path.isabs(chdir):
+                chdir = os.path.join(self.workpath(chdir))
+            if self.verbose:
+                sys.stderr.write("chdir(" + chdir + ")\n")
+            os.chdir(chdir)
+        p = self.start(program, interpreter, arguments, universal_newlines)
         if stdin:
             if is_List(stdin):
                 for line in stdin:
         p.stdin.close()
 
         out = p.stdout.read()
-        err = p.stderr.read()
+        if p.stderr is None:
+            err = ''
+        else:
+            err = p.stderr.read()
         try:
-            p.close_output()
+            close_output = p.close_output
         except AttributeError:
             p.stdout.close()
-            p.stderr.close()
+            if not p.stderr is None:
+                p.stderr.close()
+        else:
+            close_output()
+
+        self._stdout.append(out)
+        self._stderr.append(err)
 
         self.status = p.wait()
         if not self.status:
             self.status = 0
 
-        if self.combine:
-            self._stdout.append(out + err)
-        else:
-            self._stdout.append(out)
-            self._stderr.append(err)
-
         if chdir:
             os.chdir(oldcwd)
         if self.verbose >= 2:
     def readable(self, top, read=1):
         """Make the specified directory tree readable (read == 1)
         or not (read == None).
+
+        This method has no effect on Windows systems, which use a
+        completely different mechanism to control file readability.
         """
 
+        if sys.platform == 'win32':
+            return
+
         if read:
             def do_chmod(fname):
                 try: st = os.stat(fname)
                 except OSError: pass
-                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0400))
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IREAD))
         else:
             def do_chmod(fname):
                 try: st = os.stat(fname)
                 except OSError: pass
-                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0400))
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IREAD))
 
         if os.path.isfile(top):
             # If it's a file, that's easy, just chmod it.
         or not (write == None).
         """
 
-        if write:
-            def do_chmod(fname):
-                try: st = os.stat(fname)
-                except OSError: pass
-                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200))
+        if sys.platform == 'win32':
+
+            if write:
+                def do_chmod(fname):
+                    try: os.chmod(fname, stat.S_IWRITE)
+                    except OSError: pass
+            else:
+                def do_chmod(fname):
+                    try: os.chmod(fname, stat.S_IREAD)
+                    except OSError: pass
+
         else:
-            def do_chmod(fname):
-                try: st = os.stat(fname)
-                except OSError: pass
-                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200))
+
+            if write:
+                def do_chmod(fname):
+                    try: st = os.stat(fname)
+                    except OSError: pass
+                    else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0200))
+            else:
+                def do_chmod(fname):
+                    try: st = os.stat(fname)
+                    except OSError: pass
+                    else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0200))
 
         if os.path.isfile(top):
             do_chmod(top)
     def executable(self, top, execute=1):
         """Make the specified directory tree executable (execute == 1)
         or not (execute == None).
+
+        This method has no effect on Windows systems, which use a
+        completely different mechanism to control file executability.
         """
 
+        if sys.platform == 'win32':
+            return
+
         if execute:
             def do_chmod(fname):
                 try: st = os.stat(fname)
                 except OSError: pass
-                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0100))
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|stat.S_IEXEC))
         else:
             def do_chmod(fname):
                 try: st = os.stat(fname)
                 except OSError: pass
-                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0100))
+                else: os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~stat.S_IEXEC))
 
         if os.path.isfile(top):
             # If it's a file, that's easy, just chmod it.

QMTest/TestCommon.py

 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
 __author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCommon.py 0.30.D001 2007/10/01 16:53:55 knight"
-__version__ = "0.30"
+__revision__ = "TestCommon.py 0.31.D001 2008/01/01 09:05:59 knight"
+__version__ = "0.31"
 
+import copy
 import os
 import os.path
 import stat
             print "Writable files: `%s'" % string.join(writable, "', `")
         self.fail_test(missing + writable)
 
+    def _complete(self, actual_stdout, expected_stdout,
+                        actual_stderr, expected_stderr, status, match):
+        """
+        Post-processes running a subcommand, checking for failure
+        status and displaying output appropriately.
+        """
+        if _failed(self, status):
+            expect = ''
+            if status != 0:
+                expect = " (expected %s)" % str(status)
+            print "%s returned %s%s" % (self.program, str(_status(self)), expect)
+            print self.banner('STDOUT ')
+            print actual_stdout
+            print self.banner('STDERR ')
+            print actual_stderr
+            self.fail_test()
+        if not expected_stdout is None and not match(actual_stdout, expected_stdout):
+            self.diff(expected_stdout, actual_stdout, 'STDOUT ')
+            if actual_stderr:
+                print self.banner('STDERR ')
+                print actual_stderr
+            self.fail_test()
+        if not expected_stderr is None and not match(actual_stderr, expected_stderr):
+            print self.banner('STDOUT ')
+            print actual_stdout
+            self.diff(expected_stderr, actual_stderr, 'STDERR ')
+            self.fail_test()
+
+    def start(self, program = None,
+                    interpreter = None,
+                    arguments = None,
+                    universal_newlines = None,
+                    **kw):
+        """
+        Starts a program or script for the test environment.
+
+        This handles the "options" keyword argument and exceptions.
+        """
+        try:
+            options = kw['options']
+            del kw['options']
+        except KeyError:
+            pass
+        else:
+            if options:
+                if arguments is None:
+                    arguments = options
+                else:
+                    arguments = options + " " + arguments
+        try:
+            return apply(TestCmd.start,
+                         (self, program, interpreter, arguments, universal_newlines),
+                         kw)
+        except KeyboardInterrupt:
+            raise
+        except Exception, e:
+            print self.banner('STDOUT ')
+            try:
+                print self.stdout()
+            except IndexError:
+                pass
+            print self.banner('STDERR ')
+            try:
+                print self.stderr()
+            except IndexError:
+                pass
+            raise e
+
+    def finish(self, popen, stdout = None, stderr = '', status = 0, **kw):
+        """
+        Finishes and waits for the process being run under control of
+        the specified popen argument.  Additional arguments are similar
+        to those of the run() method:
+
+                stdout  The expected standard output from
+                        the command.  A value of None means
+                        don't test standard output.
+
+                stderr  The expected error output from
+                        the command.  A value of None means
+                        don't test error output.
+
+                status  The expected exit status from the
+                        command.  A value of None means don't
+                        test exit status.
+        """
+        apply(TestCmd.finish, (self, popen,), kw)
+        match = kw.get('match', self.match)
+        self._complete(self.stdout(), stdout,
+                       self.stderr(), stderr, status, match)
+
     def run(self, options = None, arguments = None,
                   stdout = None, stderr = '', status = 0, **kw):
         """Runs the program under test, checking that the test succeeded.
             del kw['match']
         except KeyError:
             match = self.match
-        try:
-            apply(TestCmd.run, [self], kw)
-        except KeyboardInterrupt:
-            raise
-        except Exception, e:
-            print self.banner('STDOUT ')
-            try:
-                print self.stdout()
-            except IndexError:
-                pass
-            print self.banner('STDERR ')
-            try:
-                print self.stderr()
-            except IndexError:
-                pass
-            raise e
-        if _failed(self, status):
-            expect = ''
-            if status != 0:
-                expect = " (expected %s)" % str(status)
-            print "%s returned %s%s" % (self.program, str(_status(self)), expect)
-            print self.banner('STDOUT ')
-            print self.stdout()
-            print self.banner('STDERR ')
-            print self.stderr()
-            self.fail_test()
-        if not stdout is None and not match(self.stdout(), stdout):
-            self.diff(stdout, self.stdout(), 'STDOUT ')
-            stderr = self.stderr()
-            if stderr:
-                print self.banner('STDERR ')
-                print stderr
-            self.fail_test()
-        if not stderr is None and not match(self.stderr(), stderr):
-            print self.banner('STDOUT ')
-            print self.stdout()
-            self.diff(stderr, self.stderr(), 'STDERR ')
-            self.fail_test()
+        apply(TestCmd.run, [self], kw)
+        self._complete(self.stdout(), stdout,
+                       self.stderr(), stderr, status, match)
 
     def skip_test(self, message="Skipping test.\n"):
         """Skips a test.

QMTest/TestSCons.py

 import re
 import string
 import sys
+import time
 
 import __builtin__
 try:
     return str
 
 
-
 class TestSCons(TestCommon):
     """Class for testing SCons.
 
         return x
 
     def normalize_pdf(self, s):
-        s = re.sub(r'/CreationDate \(D:[^)]*\)',
-                   r'/CreationDate (D:XXXX)', s)
+        s = re.sub(r'/(Creation|Mod)Date \(D:[^)]*\)',
+                   r'/\1Date (D:XXXX)', s)
         s = re.sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]',
                    r'/ID [<XXXX> <XXXX>]', s)
         s = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}',
 
         return s
 
-    def java_ENV(self):
+    def java_ENV(self, version=None):
         """
-        Return a default external environment that uses a local Java SDK
-        in preference to whatever's found in the default PATH.
+        Initialize with a default external environment that uses a local
+        Java SDK in preference to whatever's found in the default PATH.
         """
+        try:
+            return self._java_env[version]['ENV']
+        except AttributeError:
+            self._java_env = {}
+        except KeyError:
+            pass
+
         import SCons.Environment
         env = SCons.Environment.Environment()
-        java_path = [
-            '/usr/local/j2sdk1.4.2/bin',
-            '/usr/local/j2sdk1.4.1/bin',
-            '/usr/local/j2sdk1.3.1/bin',
-            '/usr/local/j2sdk1.3.0/bin',
-            '/usr/local/j2sdk1.2.2/bin',
-            '/usr/local/j2sdk1.2/bin',
-            '/usr/local/j2sdk1.1.8/bin',
-            '/usr/local/j2sdk1.1.7/bin',
-            '/usr/local/j2sdk1.1.6/bin',
-            '/usr/local/j2sdk1.1.5/bin',
-            '/usr/local/j2sdk1.1.4/bin',
-            '/usr/local/j2sdk1.1.3/bin',
-            '/usr/local/j2sdk1.1.2/bin',
-            '/usr/local/j2sdk1.1.1/bin',
-            env['ENV']['PATH'],
-        ]
+        self._java_env[version] = env
+
+        def paths(patterns):
+            import glob
+            result = []
+            for p in patterns:
+                paths = glob.glob(p)
+                paths.sort()
+                result.extend(paths)
+            return result
+
+        if version:
+            patterns = [
+                '/usr/lib/jvm/*-%s*/bin' % version,
+                '/usr/local/j2sdk%s*/bin' % version,
+            ]
+            java_path = paths(patterns) + [env['ENV']['PATH']]
+        else:
+            patterns = [
+                '/usr/lib/jvm/*/bin',
+                '/usr/local/j2sdk*/bin',
+            ]
+            java_path = paths(patterns) + [env['ENV']['PATH']]
+
         env['ENV']['PATH'] = string.join(java_path, os.pathsep)
         return env['ENV']
 
+    def java_where_jar(self, version=None):
+        ENV = self.java_ENV(version)
+        if self.detect_tool('jar', ENV=ENV):
+            where_jar = self.detect('JAR', 'jar', ENV=ENV)
+        else:
+            where_jar = self.where_is('jar', ENV['PATH'])
+        if not where_jar:
+            self.skip_test("Could not find Java jar, skipping test(s).\n")
+        return where_jar
+
+    def java_where_java(self, version=None):
+        """
+        Return a path to the java executable.
+        """
+        ENV = self.java_ENV(version)
+        where_java = self.where_is('java', ENV['PATH'])
+        if not where_java:
+            self.skip_test("Could not find Java java, skipping test(s).\n")
+        return where_java
+
+    def java_where_javac(self, version=None):
+        """
+        Return a path to the javac compiler.
+        """
+        ENV = self.java_ENV(version)
+        if self.detect_tool('javac'):
+            where_javac = self.detect('JAVAC', 'javac', ENV=ENV)
+        else:
+            where_javac = self.where_is('javac', ENV['PATH'])
+        if not where_javac:
+            self.skip_test("Could not find Java javac, skipping test(s).\n")
+        self.run(program = where_javac,
+                 arguments = '-version',
+                 stderr=None,
+                 status=None)
+        if version:
+            if string.find(self.stderr(), 'javac %s' % version) == -1:
+                fmt = "Could not find javac for Java version %s, skipping test(s).\n"
+                self.skip_test(fmt % version)
+        else:
+            m = re.search(r'javac (\d\.\d)', self.stderr())
+            if m:
+                version = m.group(1)
+            else:
+                version = None
+        return where_javac, version
+
+    def java_where_javah(self, version=None):
+        ENV = self.java_ENV(version)
+        if self.detect_tool('javah'):
+            where_javah = self.detect('JAVAH', 'javah', ENV=ENV)
+        else:
+            where_javah = self.where_is('javah', ENV['PATH'])
+        if not where_javah:
+            self.skip_test("Could not find Java javah, skipping test(s).\n")
+        return where_javah
+
+    def java_where_rmic(self, version=None):
+        ENV = self.java_ENV(version)
+        if self.detect_tool('rmic'):
+            where_rmic = self.detect('RMIC', 'rmic', ENV=ENV)
+        else:
+            where_rmic = self.where_is('rmic', ENV['PATH'])
+        if not where_rmic:
+            self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n")
+        return where_rmic
+
     def Qt_dummy_installation(self, dir='qt'):
         # create a dummy qt installation
 
         else:
             return distutils.sysconfig.get_python_inc()
 
+    def wait_for(self, fname, timeout=10.0, popen=None):
+        """
+        Waits for the specified file name to exist.
+        """
+        waited = 0.0
+        while not os.path.exists(fname):
+            if timeout and waited >= timeout:
+                sys.stderr.write('timed out waiting for %s to exist\n' % fname)
+                if popen:
+                    popen.stdin.close()
+                    self.status = 1
+                    self.finish(popen)
+                self.fail_test()
+            time.sleep(1.0)
+            waited = waited + 1.0
+
 # In some environments, $AR will generate a warning message to stderr
 # if the library doesn't previously exist and is being created.  One
 # way to fix this is to tell AR to be quiet (sometimes the 'c' flag),

QMTest/TestSCons_time.py

         self.write(python_name, profile_py % d)
         self.run(program = python_name, interpreter = sys.executable)
 
+    def tempdir_re(self, *args):
+        """
+        Returns a regular expression to match a scons-time
+        temporary directory.
+        """
+        import re
+        import tempfile
+
+        sep = re.escape(os.sep)
+        tempdir = tempfile.gettempdir()
+
+        try:
+            realpath = os.path.realpath
+        except AttributeError:
+            pass
+        else:
+            tempdir = realpath(tempdir)
+
+        args = (tempdir, 'scons-time-',) + args
+        x = apply(os.path.join, args)
+        x = re.escape(x)
+        x = string.replace(x, 'time\\-', 'time\\-[^%s]*' % sep)
+        return x
+
     def write_fake_aegis_py(self, name):
         name = self.workpath(name)
         self.write(name, aegis_py)

doc/man/scons-time.1

 .B --aegis=
 command-line option.
 .TP
+.B archive_list
+A list of archives (files or directories)
+that will be copied to the temporary directory
+in which SCons will be invoked.
+.BR .tar ,
+.BR .tar.gz ,
+.BR .tgz
+and
+.BR .zip
+files will have their contents unpacked in
+the temporary directory.
+Directory trees and files will be copied as-is.
+.TP
 .B initial_commands
 A list of commands that will be executed
 before the actual timed
 .BR --implicit-cache .
 
 .TP
+--interactive
+Starts SCons in interactive mode.
+The SConscript files are read once and a
+.B "scons>>>"
+prompt is printed.
+Targets may now be rebuilt by typing commands at interactive prompt
+without having to re-read the SConscript files
+and re-initialize the dependency graph from scratch.
+
+SCons interactive mode supports the following commands:
+
+.RS 10
+.TP 6
+.BI build "[OPTIONS] [TARGETS] ..."
+Builds the specified
+.I TARGETS
+(and their dependencies)
+with the specified
+SCons command-line
+.IR OPTIONS .
+.B b
+and
+.B scons
+are synonyms.
+
+The following SCons command-line options affect the
+.B build
+command:
+
+.ES
+--cache-debug=FILE
+--cache-disable, --no-cache
+--cache-force, --cache-populate
+--cache-show
+--debug=TYPE
+-i, --ignore-errors
+-j N, --jobs=N
+-k, --keep-going
+-n, --no-exec, --just-print, --dry-run, --recon
+-Q
+-s, --silent, --quiet
+-s, --silent, --quiet
+--taskmastertrace=FILE
+--tree=OPTIONS
+.EE
+
+.IP "" 6
+Any other SCons command-line options that are specified
+do not cause errors
+but have no effect on the
+.B build
+command
+(mainly because they affect how the SConscript files are read,
+which only happens once at the beginning of interactive mode).
+
+.TP 6
+.BI clean "[OPTIONS] [TARGETS] ..."
+Cleans the specified
+.I TARGETS
+(and their dependencies)
+with the specified options.
+.B c
+is a synonym.
+This command is itself a synonym for
+.B "build --clean"
+
+.TP 6
+.BI exit
+Exits SCons interactive mode.
+You can also exit by terminating input
+(CTRL+D on UNIX or Linux systems,
+CTRL+Z on Windows systems).
+
+.TP 6
+.BI help "[COMMAND]"
+Provides a help message about
+the commands available in SCons interactive mode.
+If
+.I COMMAND
+is specified,
+.B h
+and
+.B ?
+are synonyms.
+
+.TP 6
+.BI shell "[COMMANDLINE]"
+Executes the specified
+.I COMMANDLINE
+in a subshell.
+If no
+.I COMMANDLINE
+is specified,
+executes the interactive command interpreter
+specified in the
+.B SHELL
+environment variable
+(on UNIX and Linux systems)
+or the
+.B COMSPEC
+environment variable
+(on Windows systems).
+.B sh
+and
+.B !
+are synonyms.
+
+.TP 6
+.B version
+Prints SCons version information.
+.RE
+
+An empty line repeats the last typed command.
+Command-line editing can be used if the
+.B readline
+module is available.
+
+.ES
+$ scons --interactive
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+scons>>> build -n prog
+scons>>> exit
+.EE
+
+.TP
 .RI -j " N" ", --jobs=" N
 Specifies the number of jobs (commands) to run simultaneously.
 If there is more than one 
 will get added to the default toolpath.
 
 .TP
+.RI --stack-size= KILOBYTES
+Set the size stack used to run threads to
+.IR KILOBYTES . 
+This value determines the stack size of the threads used to run jobs.
+These are the threads that execute the actions of the builders for the
+nodes that are out-of-date.
+Note that this option has no effect unless the
+.B num_jobs
+option, which corresponds to -j and --jobs, is larger than one.  Using
+a stack size that is too small may cause stack overflow errors.  This
+usually shows up as segmentation faults that cause scons to abort
+before building anything.  Using a stack size that is too large will
+cause scons to use more memory than required and may slow down the entire
+build process.
+
+The default value is to use a stack size of 256 kilobytes, which should
+be appropriate for most uses.  You should not need to increase this value
+unless you encounter stack overflow errors.
+
+.TP
 -t, --touch
 Ignored for compatibility with GNU
 .BR make .  
 .BR SOURCES .
 These warnings are disabled by default.
 
+.TP
+--warn=stack-size, --warn=no-stack-size
+Enables or disables warnings about requests to set the stack size
+that could not be honored.
+These warnings are enabled by default.
+
 .\" .TP
 .\" .RI --write-filenames= file
 .\" Write all filenames considered into
 g77
 gas
 gcc
+gfortran
 gnulink
 gs
 hpc++
 to be performed:
 
 .RS 10
+.HP 6
 .B timestamp-newer
 Specifies that a target shall be considered out of date and rebuilt
 if the dependency's timestamp is newer than the target file's timestamp.
 .B no_exec
 which corresponds to -n, --no-exec, --just-print, --dry-run and --recon;
 .B num_jobs
-which corresponds to -j and --jobs.
+which corresponds to -j and --jobs;
 .B random
-which corresponds to --random.
+which corresponds to --random; and
+.B stack_size
+which corresponds to --stack-size.
 See the documentation for the
 corresponding command line object for information about each specific
 option.
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
-.RI env.subst( string ", [" raw ", " target ", " source ", " conv ])
+.RI env.subst( input ", [" raw ", " target ", " source ", " conv ])
 Performs construction variable interpolation
-on the specified string argument.
+on the specified string or sequence argument
+.IR input .
 
 By default,
 leading or trailing white space will
 pairs
 (as is done for signature calculation).
 
+If the input is a sequence
+(list or tuple),
+the individual elements of
+the sequence will be expanded,
+and the results will be returned as a list.
+
 The optional
 .I target
 and
 from within a Python function used
 as an SCons action.
 
-By default,
-all returned values are converted
-to their string representation.
+Returned string values or sequence elements
+are converted to their string representation by default.
 The optional
 .I conv
 argument
 .ES
 .EE
 
+.TP
+.RI Configure.CheckDeclaration( self ", " symbol ", [" includes ", " language ])
+Checks if the specified
+.I symbol
+is declared.
+.I includes
+is a string containing one or more
+.B #include
+lines that will be inserted into the program
+that will be run to test for the existence of the type.
+The optional
+.I language
+argument should be
+.B C
+or
+.B C++
+and selects the compiler to be used for the check;
+the default is "C".
+
+.TP
+.RI Configure.Define(self ", " symbol ", [" value ", " comment ])
+This function does not check for anything, but defines a
+preprocessor symbol that will be added to the configuration header file.
+It is the equivalent of AC_DEFINE,
+and defines the symbol
+.I name
+with the optional
+.B value
+and the optional comment
+.BR comment .
+
+.IP
+Examples:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following line in the config header file:
+#    #define A_SYMBOL
+conf.Define('A_SYMBOL')
+
+# Puts the following line in the config header file:
+#    #define A_SYMBOL 1
+conf.Define('A_SYMBOL', 1)
+.EE
+
+.IP
+Be careful about quoting string values, though:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following line in the config header file:
+#    #define A_SYMBOL YA
+conf.Define('A_SYMBOL', "YA")
+
+# Puts the following line in the config header file:
+#    #define A_SYMBOL "YA"
+conf.Define('A_SYMBOL', '"YA"')
+.EE
+
+.IP
+For comment:
+
+.ES
+env = Environment()
+conf = Configure( env )
+
+# Puts the following lines in the config header file:
+#    /* Set to 1 if you have a symbol */
+#    #define A_SYMBOL 1
+conf.Define('A_SYMBOL', 1, 'Set to 1 if you have a symbol')
+.EE
+
 .EE
 You can define your own custom checks. 
 in addition to the predefined checks.
 <!ENTITY as             "<application>as</application>">
 <!ENTITY Autoconf       "<application>Autoconf</application>">
 <!ENTITY Automake       "<application>Automake</application>">
+<!ENTITY bison          "<application>bison</application>">
 <!ENTITY cc             "<application>cc</application>">
 <!ENTITY Cons           "<application>Cons</application>">
 <!ENTITY cp             "<application>cp</application>">
 
 
 
+RELEASE 0.XX - XXX
+
+  From Benoit Belley:
+
+  - Fix the --keep-going flag so it builds all possible targets even when
+    a later top-level target depends on a child that failed its build.
+
+  - Fix being able to use $PDB and $WINDWOWS_INSERT_MANIFEST together.
+
+  - Don't crash if un-installing the Intel C compiler leaves left-over,
+    dangling entries in the Windows registry.
+
+  - Improve support for non-standard library prefixes and suffixes by
+    stripping all prefixes/suffixes from file name string as appropriate.
+
+  - Reduce the default stack size for -j worker threads to 256 Kbytes.
+    Provide user control over this value by adding --stack-size and
+    --warn=stack-size options, and a SetOption('stack_size') function.
+
+  - Fix a crash on Linux systems when trying to use the Intel C compiler
+    and no /opt/intel_cc_* directories are found.
+
+  - Improve using Python functions as actions by incorporating into
+    a FunctionAction's signature:
+      - literal values referenced by the byte code.
+      - values of default arguments
+      - code of nested functions
+      - values of variables captured by closures
+      - names of referenced global variables and functions
+
+  - Fix the closing message when --clean and --keep-going are both
+    used and no errors occur.
+
+  - Add support for the Intel C compiler on Mac OS X.
+
+  From Jérôme Berger:
+
+  - Have the D language scanner search for .di files as well as .d files.
+
+  - Add a find_include_names() method to the Scanner.Classic class to
+    abstract out how included names can be generated by subclasses.
+
+  - Allow the D language scanner to detect multiple modules imported by
+    a single statement.
+
+  From Konstantin Bozhikov:
+
+  - Support expansion of construction variables that contain or refer
+    to lists of other variables or Nodes within expansions like $PCPPATH.
+
+  - Change variable substitution (the env.subst() method) so that an
+    input sequence (list or tuple) is preserved as a list in the output.
+
+  From David Cournapeau:
+
+  - Add a CheckDeclaration() call to configure contexts.
+
+  - Improve the CheckTypeSize() code.
+
+  - Add a Define() call to configure contexts, to add arbitrary #define
+    lines to a generated configure header file.
+
+  - Add a "gfortran" Tool module for the GNU F95/F2003 compiler.
+
+  - Avoid use of -rpath with the Mac OS X linker.
+
+  From Steven Knight:
+
+  - Support the ability to subclass the new-style "str" class as input
+    to Builders.
+
+  - Improve the performance of our type-checking by using isinstance()
+    with new-style classes.
+
+  - Fix #include (and other $*PATH variables searches) of files with
+    absolute path names.  Don't die if they don't exist (due to being
+    #ifdef'ed out or the like).
+
+  - Fix --interactive mode when Default(None) is used.
+
+  - Fix --debug=memoizer to work around a bug in base Python 2.2 metaclass
+    initialization (by just not allowing Memoization in Python versions
+    that have the bug).
+
+  - Have the "scons-time time" subcommand handle empty log files, and
+    log files that contain no results specified by the --which option.
+
+  - Fix the max Y of vertical bars drawn by "scons-time --fmt=gnuplot".
+
+  - On Mac OS X, account for the fact that the header file generated
+    from a C++ file will be named (e.g.) file.cpp.h, not file.hpp.
+
+  From Rob Managan:
+
+  - Enhance TeX and LaTeX support to work with BuildDir(duplicate=0).
+
+  - Re-run LaTeX when it issues a package warning that it must be re-run.
+
+  From Jan Nijtmans:
+
+  - If $JARCHDIR isn't set explicitly, use the .java_classdir attribute
+    that was set when the Java() Builder built the .class files.
+
+  From Gary Oberbrunner:
+
+  - Fix the ability to build an Alias in --interactive mode.
+
+  - Fix the ability to hash the contents of actions for nested Python
+    functions on Python versions where the inability to pickle them
+    returns a TypeError (instead of the documented PicklingError).
+
+  From Jonas Olsson:
+
+  - Fix use of the Intel C compiler when the top compiler directory,
+    but not the compiler version, is specified.
+
+  - Handle Intel C compiler network license files (port@system).
+
+  From Adam Simpkins:
+
+  - Add a --interactive option that starts a session for building (or
+    cleaning) targets without re-reading the SConscript files every time.
+
+  - Fix use of readline command-line editing in --interactive mode.
+
+  - Have the --interactive mode "build" command with no arguments
+    build the specified Default() targets.
+
+  From Ben Webb:
+
+  - Support the SWIG %module statement with following modifiers in
+    parenthese (e.g., '%module(directors="1")').
+
+
+
 RELEASE 0.97.0d20071212 - Wed, 12 Dec 2007 09:29:32 -0600
 
   From Benoit Belley:
   This is the eighth beta release of SCons.  Please consult the
   CHANGES.txt file for a list of specific changes since last release.
 
+  Please note the following important changes since release 0.97.0d20071212:
+
+    --  THE env.subst() METHOD NOW RETURNS A LIST WHEN THE INPUT IS A SEQUENCE
+
+        The env.subst() method now returns a list with the elements
+        expanded when given a list as input.  Previously, the env.subst()
+        method would always turn its result into a string.
+
+        This behavior was changed because way it interfered with
+        being able to include things like lists within the expansion
+        of variables like $CPPPATH and have SCons understand that the
+        elements of the "internal" lists still needed to be treated
+        separately.  This would show up as a list like ['subdir1',
+        'subdir'] showing up in a command line as "-Isubdir1 subdir".
+
+    --  THE Jar() BUILDER NOW USES THE Java() BUILDER CLASSDIR BY DEFAULT
+
+        By default, the Jar() Builder will now use the class directory
+        specified when the Java() builder is called.  So the following
+        input:
+
+            classes = env.Java('classes', 'src')
+            env.Jar('out.jar', classes)
+
+        Will cause "-C classes" to be passed the "jar" command invocation,
+        and the Java classes in the "out.jar" file will not be prefixed
+        "classes/".
+
+        Explicitly setting the $JARCHDIR variable overrides this default
+        behavior.  The old behavior of not passing any -C option to the
+        "jar" command can be preserved by explicitly setting $JARCHDIR
+        to None:
+
+            env = Environment(JARCHDIR = None)
+
+        The above setting is compatible with older versions of SCons.
+
   Please note the following important changes since release 0.97.0d20070918:
 
     --  SCons REDEFINES PYTHON open() AND file() ON Windows TO NOT PASS

src/engine/MANIFEST.in

 SCons/compat/_scons_optparse.py
 SCons/compat/_scons_sets.py
 SCons/compat/_scons_sets15.py
+SCons/compat/_scons_shlex.py
 SCons/compat/_scons_subprocess.py
 SCons/compat/_scons_textwrap.py
 SCons/compat/_scons_UserString.py
 SCons/SConf.py
 SCons/SConsign.py
 SCons/Script/__init__.py
+SCons/Script/Interactive.py
 SCons/Script/Main.py
 SCons/Script/SConscript.py
 SCons/Script/SConsOptions.py
 SCons/Tool/g77.py
 SCons/Tool/gas.py
 SCons/Tool/gcc.py
+SCons/Tool/gfortran.py
 SCons/Tool/gnulink.py
 SCons/Tool/gs.py
 SCons/Tool/hpc++.py

src/engine/SCons/Action.py

 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import cPickle
 import dis
 import os
 import os.path
                 i = i+1
         return string.join(result, '')
 
+
+def _callable_contents(obj):
+    """Return the signature contents of a callable Python object.
+    """
+    try:
+        # Test if obj is a method.
+        return _function_contents(obj.im_func)
+
+    except AttributeError:
+        try:
+            # Test if obj is a callable object.
+            return _function_contents(obj.__call__.im_func)
+
+        except AttributeError:
+            try:
+                # Test if obj is a code object.
+                return _code_contents(obj)
+
+            except AttributeError:
+                    # Test if obj is a function object.
+                    return _function_contents(obj)
+
+
+def _object_contents(obj):
+    """Return the signature contents of any Python object.
+    
+    We have to handle the case where object contains a code object
+    since it can be pickled directly.
+    """
+    try:
+        # Test if obj is a method.
+        return _function_contents(obj.im_func)
+
+    except AttributeError:
+        try:
+            # Test if obj is a callable object.
+            return _function_contents(obj.__call__.im_func)
+
+        except AttributeError:
+            try:
+                # Test if obj is a code object.
+                return _code_contents(obj)
+
+            except AttributeError:
+                try:
+                    # Test if obj is a function object.
+                    return _function_contents(obj)
+
+                except AttributeError:
+                    # Should be a pickable Python object. 
+                    try:
+                        return cPickle.dumps(obj)
+                    except (cPickle.PicklingError, TypeError):
+                        # This is weird, but it seems that nested classes
+                        # are unpickable. The Python docs say it should
+                        # always be a PicklingError, but some Python
+                        # versions seem to return TypeError.  Just do
+                        # the best we can.
+                        return str(obj)
+
+
+def _code_contents(code):
+    """Return the signature contents of a code object.
+
+    By providing direct access to the code object of the
+    function, Python makes this extremely easy.  Hooray!
+    
+    Unfortunately, older versions of Python include line
+    number indications in the compiled byte code.  Boo!
+    So we remove the line number byte codes to prevent
+    recompilations from moving a Python function.
+    """
+
+    contents = []
+
+    # The code contents depends on the number of local variables
+    # but not their actual names.
+    contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
+    try:
+        contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
+    except AttributeError:
+        # Older versions of Python do not support closures.
+        contents.append(",0,0")
+
+    # The code contents depends on any constants accessed by the
+    # function. Note that we have to call _object_contents on each
+    # constants because the code object of nested functions can
+    # show-up among the constants. 
+    # 
+    # Note that we also always ignore the first entry of co_consts
+    # which contains the function doc string. We assume that the
+    # function does not access its doc string.
+    contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
+                                 
+    # The code contents depends on the variable names used to
+    # accessed global variable, as changing the variable name changes
+    # the variable actually accessed and therefore changes the
+    # function result.
+    contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
+
+
+    # The code contents depends on its actual code!!!
+    contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
+
+    return string.join(contents, '')
+
+
+def _function_contents(func):
+    """Return the signature contents of a function."""
+
+    contents = [_code_contents(func.func_code)]
+
+    # The function contents depends on the value of defaults arguments
+    if func.func_defaults:
+        contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
+    else:
+        contents.append(',()')
+
+    # The function contents depends on the closure captured cell values.
+    try:
+        closure = func.func_closure or []
+    except AttributeError:
+        # Older versions of Python do not support closures.
+        closure = []
+
+    #xxx = [_object_contents(x.cell_contents) for x in closure]
+    xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+    contents.append(',(' + string.join(xxx, ',') + ')')
+
+    return string.join(contents, '')
+        
+
 def _actionAppend(act1, act2):
     # This function knows how to slap two actions together.
     # Mainly, it handles ListActions by concatenating into
                     'accepts (target, source, env) as parameters.')
 
         self.execfunction = execfunction
+        try:
+            self.funccontents = _callable_contents(execfunction)
+        except AttributeError:
+            try:
+                # See if execfunction will do the heavy lifting for us.
+                self.gc = execfunction.get_contents
+            except AttributeError:
+                # This is weird, just do the best we can.
+                self.funccontents = _object_contents(execfunction)
+
         apply(_ActionAction.__init__, (self,)+args, kw)
         self.varlist = kw.get('varlist', [])
         self.cmdstr = cmdstr
         return result
 
     def get_contents(self, target, source, env):
-        """Return the signature contents of this callable action.
+        """Return the signature contents of this callable action."""
+        try:
+            contents = self.gc(target, source, env)
+        except AttributeError:
+            contents = self.funccontents
 
-        By providing direct access to the code object of the
-        function, Python makes this extremely easy.  Hooray!
-
-        Unfortunately, older versions of Python include line
-        number indications in the compiled byte code.  Boo!
-        So we remove the line number byte codes to prevent
-        recompilations from moving a Python function.
-        """
-        execfunction = self.execfunction
-        try:
-            # Test if execfunction is a function.
-            code = execfunction.func_code.co_code
-        except AttributeError:
-            try:
-                # Test if execfunction is a method.
-                code = execfunction.im_func.func_code.co_code
-            except AttributeError:
-                try:
-                    # Test if execfunction is a callable object.
-                    code = execfunction.__call__.im_func.func_code.co_code
-                except AttributeError:
-                    try:
-                        # See if execfunction will do the heavy lifting for us.
-                        gc = self.execfunction.get_contents
-                    except AttributeError:
-                        # This is weird, just do the best we can.
-                        contents = str(self.execfunction)
-                    else:
-                        contents = gc(target, source, env)
-                else:
-                    contents = str(code)
-            else:
-                contents = str(code)
-        else:
-            contents = str(code)
-        contents = remove_set_lineno_codes(contents)
         return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
-                                                     self.varlist)))
+                                                    self.varlist)))
 
     def get_implicit_deps(self, target, source, env):
         return []

src/engine/SCons/ActionTests.py

         def LocalFunc():
             pass
 
-        matches = [
-            "d\000\000S",
-            "d\x00\x00S",
+        func_matches = [
+            "0,0,0,0,(),(),(d\000\000S),(),()",
+            "0,0,0,0,(),(),(d\x00\x00S),(),()",
+            ]
+        
+        meth_matches = [
+            "1,1,0,0,(),(),(d\000\000S),(),()",
+            "1,1,0,0,(),(),(d\x00\x00S),(),()",
         ]
 
         a = SCons.Action.FunctionAction(GlobalFunc)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in func_matches, repr(c)
 
         a = SCons.Action.FunctionAction(LocalFunc)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in func_matches, repr(c)
 
         a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
 
-        matches_foo = map(lambda x: x + "foo", matches)
+        matches_foo = map(lambda x: x + "foo", func_matches)
 
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in func_matches, repr(c)
         c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
         assert c in matches_foo, repr(c)
 
         lc = LocalClass()
         a = SCons.Action.FunctionAction(lc.LocalMethod)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in meth_matches, repr(c)
 
     def test_strfunction(self):
         """Test the FunctionAction.strfunction() method

src/engine/SCons/CacheDir.py

 
 import SCons.Action
 
+cache_enabled = True
 cache_debug = False
 cache_force = False
 cache_show = False
         except ImportError:
             msg = "No hashlib or MD5 module available, CacheDir() not supported"
             SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
+            self.path = None
         else:
             self.path = path
+        self.current_cache_debug = None
+        self.debugFP = None
 
-    def CacheDebugWrite(self, fmt, target, cachefile):
-        self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
-
-    def CacheDebugQuiet(self, fmt, target, cachefile):
-        pass
-
-    def CacheDebugInit(self, fmt, target, cachefile):
-        if cache_debug:
+    def CacheDebug(self, fmt, target, cachefile):
+        if cache_debug != self.current_cache_debug:
             if cache_debug == '-':
                 self.debugFP = sys.stdout
+            elif cache_debug:
+                self.debugFP = open(cache_debug, 'w')
             else:
-                self.debugFP = open(cache_debug, 'w')
-            self.CacheDebug = self.CacheDebugWrite
-            self.CacheDebug(fmt, target, cachefile)
-        else:
-            self.CacheDebug = self.CacheDebugQuiet
+                self.debugFP = None
+            self.current_cache_debug = cache_debug
+        if self.debugFP:
+            self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
 
-    CacheDebug = CacheDebugInit
+    def is_enabled(self):
+        return (cache_enabled and not self.path is None)
 
     def cachepath(self, node):
         """
         """
+        if not self.is_enabled():
+            return None, None
+
         sig = node.get_cachedir_bsig()
         subdir = string.upper(sig[0])
         dir = os.path.join(self.path, subdir)
         execute the CacheRetrieveFunc and then have the latter
         explicitly check SCons.Action.execute_actions itself.
         """
+        if not self.is_enabled():
+            return False
+
         retrieved = False
 
         if cache_show:
         return retrieved
 
     def push(self, node):
+        if not self.is_enabled():
+            return
         return CachePush(node, [], node.get_build_env())
 
     def push_if_forced(self, node):
         if cache_force:
             return self.push(node)
-
-class Null(SCons.Util.Null):
-    def repr(self):
-        return 'CacheDir.Null()'
-    def cachepath(self, node):
-        return None, None
-    def retrieve(self, node):
-        return False

src/engine/SCons/Conftest.py

 }
 """
 
-        # XXX: Try* vs CompileProg ?
-        st = context.TryCompile(src % (type_name, expect), suffix)
-        if st:
-            _Have(context, "SIZEOF_" + type_name, str(expect))
+        st = context.CompileProg(src % (type_name, expect), suffix)
+        if not st:
             context.Display("yes\n")
+            _Have(context, "SIZEOF_%s" % type_name, expect)
             return expect
         else:
             context.Display("no\n")
     return 0;
 }
     """
-        ret = context.TryRun(src, suffix)
-        st = ret[0]
+        st, out = context.RunProg(src, suffix)
         try:
-            size = int(ret[1])
-            _Have(context, "SIZEOF_" + type_name, str(size))
-            context.Display("%d\n" % size)
+            size = int(out)
         except ValueError:
+            # If cannot convert output of test prog to an integer (the size),
+            # something went wront, so just fail
+            st = 1
             size = 0
-            _LogFailed(context, src, st)
-            context.Display(" Failed !\n")
-        if st:
+
+        if not st:
+            context.Display("yes\n")
+            _Have(context, "SIZEOF_%s" % type_name, size)
             return size
         else:
+            context.Display("no\n")
+            _LogFailed(context, src, st)
             return 0
 
+    return 0
+
+def CheckDeclaration(context, symbol, includes = None, language = None):
+    """Checks whether symbol is declared.
+
+    Use the same test as autoconf, that is test whether the symbol is defined
+    as a macro or can be used as an r-value.