Merged revisions 2725-2865 via svnmerge from
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 17 Apr 2008 06:35:51 +0000 (06:35 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 17 Apr 2008 06:35:51 +0000 (06:35 +0000)
http://scons.tigris.org/svn/scons/branches/core

........
  r2731 | stevenknight | 2008-04-01 09:22:37 -0700 (Tue, 01 Apr 2008) | 4 lines

  Fix test/Interactive/version.py, which still had the old hard-coded
  Copyright string, by moving the test copy of the Copyright string
  into QMTest/TestSCons.py, where it's available to all test scripts.
........
  r2732 | stevenknight | 2008-04-02 13:28:32 -0700 (Wed, 02 Apr 2008) | 3 lines

  Issues 317, 323, 329:  Ignore the -d, -e and -w options, which we're
  not going to implement.
........
  r2734 | stevenknight | 2008-04-05 07:50:49 -0700 (Sat, 05 Apr 2008) | 2 lines

  Fix bootstrap.py for pre-2.3 versions of Python, which don't have __file__.
........
  r2735 | stevenknight | 2008-04-05 07:54:44 -0700 (Sat, 05 Apr 2008) | 2 lines

  Fix the non-breaking-hyphen regular expression(s) for Python 1.5.
........
  r2736 | garyo | 2008-04-05 19:15:52 -0700 (Sat, 05 Apr 2008) | 1 line

  Added Users Guide section for AddMethod.
........
  r2737 | stevenknight | 2008-04-08 09:43:31 -0700 (Tue, 08 Apr 2008) | 3 lines

  Don't raise strings as exceptions in the exception-handling unit tests.
  That's now deprecated Python behavior.
........
  r2738 | stevenknight | 2008-04-08 10:36:00 -0700 (Tue, 08 Apr 2008) | 5 lines

  Address a serious inefficiency in Java builds by moving the pre-build
  check for whether source files exist from the Node class to the Executor
  class, so we only have to perform the check once when building a whole
  bunch of targets from a whole bunch of sources.
........
  r2739 | stevenknight | 2008-04-08 13:53:44 -0700 (Tue, 08 Apr 2008) | 2 lines

  Add a backwards-compatibility version of the "itertools" module.
........
  r2740 | stevenknight | 2008-04-08 14:12:05 -0700 (Tue, 08 Apr 2008) | 2 lines

  Issue 1961:  speed up SCons.Util.to_String*() functions.  (Benoit Belley)
........
  r2741 | stevenknight | 2008-04-08 14:15:36 -0700 (Tue, 08 Apr 2008) | 2 lines

  Use the itertools.izip() method for some key Node.FS methods.  (Benoit Belley)
........
  r2742 | stevenknight | 2008-04-08 14:35:35 -0700 (Tue, 08 Apr 2008) | 2 lines

  Issues 1961:  use izip() instead of zip() where possible (Benoit Belley)
........
  r2743 | stevenknight | 2008-04-08 14:48:08 -0700 (Tue, 08 Apr 2008) | 3 lines

  Issue 1961:  more efficient get_contents() implementation for Python
  function Actions.  (Benoit Belley)
........
  r2744 | stevenknight | 2008-04-08 17:55:30 -0700 (Tue, 08 Apr 2008) | 3 lines

  Issue 1961:  make SCons.Node.* state variables global in Taskmaster.py so
  we avoid unneceesary attribute fetches.  (Benoit Belley)
........
  r2745 | stevenknight | 2008-04-08 20:40:31 -0700 (Tue, 08 Apr 2008) | 2 lines

  Issue 1961:  Optimize the code in Node.get_binfo().  (Benoit Belley)
........
  r2746 | stevenknight | 2008-04-08 21:05:17 -0700 (Tue, 08 Apr 2008) | 3 lines

  Issue 1961:  Enhance the backwards-compatibility sets() module with the
  ability to compare sets.  (Benoit Belley)
........
  r2747 | stevenknight | 2008-04-08 22:11:20 -0700 (Tue, 08 Apr 2008) | 2 lines

  Issue 1961:  Optimize Executor.scan().  (Benoit Belley)
........
  r2748 | stevenknight | 2008-04-08 22:17:55 -0700 (Tue, 08 Apr 2008) | 3 lines

  Python 1.5 compatibility:  use for p in paths.keys() for dictionaries.
  (Benoit Belley)
........
  r2749 | stevenknight | 2008-04-08 22:54:02 -0700 (Tue, 08 Apr 2008) | 10 lines

  Issue 1961:  additional build optimizations:
  --  Make taskmastertrace output more useful and readable.
  --  Move dependency cycle checking to avoid re-doing it.
  --  Have Nodes use sets, not dictionaries, to track various things.
  --  Eliminate intermediate function calls from fetching Node children.
  --  Add a Task.needs_execute() method to avoid doing that check over and
      over as part of needs_execute().
  --  Remove the unused Node.found_includes attribute.
  (Benoit Belley)
........
  r2750 | stevenknight | 2008-04-09 14:47:44 -0700 (Wed, 09 Apr 2008) | 4 lines

  Make target Java .class files depend *only* on the input .java files as
  their sources when determining if they require rebuilding.  This eliminates
  O(NxM) checking for every single edge in a big, overly-connected DAG mesh.
........
  r2751 | stevenknight | 2008-04-09 16:58:41 -0700 (Wed, 09 Apr 2008) | 2 lines

  Remove 0.95 and 0.96* release notes.
........
  r2752 | stevenknight | 2008-04-10 02:24:50 -0700 (Thu, 10 Apr 2008) | 2 lines

  Issue 1956:  Fix --debug=stree printing its tree twice.  (Benoit Belley)
........
  r2753 | stevenknight | 2008-04-10 02:33:28 -0700 (Thu, 10 Apr 2008) | 2 lines

  Issue 1896:  Add support for the GDC D language compiler.  (Matthew Wesley)
........
  r2754 | stevenknight | 2008-04-10 02:39:24 -0700 (Thu, 10 Apr 2008) | 2 lines

  Fix tabs.
........
  r2755 | stevenknight | 2008-04-10 02:41:50 -0700 (Thu, 10 Apr 2008) | 3 lines

  Issue 1964:  Fix passing variable names in a list to Return() (as
  already documented in the man page!)  (Mike Wake)
........
  r2756 | stevenknight | 2008-04-10 02:55:40 -0700 (Thu, 10 Apr 2008) | 2 lines

  Support the ability to download 2.6 candidate releases (e.g. 2.6a2).
........
  r2757 | stevenknight | 2008-04-10 02:58:35 -0700 (Thu, 10 Apr 2008) | 3 lines

  Issue 1669:  Fix the ability to use LoadableModule() under MinGW.
  (Johan Boule)
........
  r2758 | stevenknight | 2008-04-10 03:03:15 -0700 (Thu, 10 Apr 2008) | 3 lines

  Update the test/Interactive/tree.py script for Benoit's fix to
  remove duplicate tree printing.
........
  r2759 | stevenknight | 2008-04-10 06:43:44 -0700 (Thu, 10 Apr 2008) | 3 lines

  Fix Tool/dmd.py when no D compiler is installed -- we don't want to
  search for a path if the result is None.
........
  r2760 | GregNoel | 2008-04-10 15:30:34 -0700 (Thu, 10 Apr 2008) | 1 line

  Issue 2009: separate Debug.caller() by functionality
........
  r2761 | stevenknight | 2008-04-11 04:47:25 -0700 (Fri, 11 Apr 2008) | 3 lines

  Issue 1882:  Add the scons.bat directory to %PATH% so it can find python.exe.
  (Anatoly Techtonik)
........
  r2762 | stevenknight | 2008-04-11 09:15:22 -0700 (Fri, 11 Apr 2008) | 4 lines

  Issues 1835,1901:  fix the ability to list a source file multiple
  times for a target by making sure we only store unique entries in the
  .sconsign file.
........
  r2763 | stevenknight | 2008-04-11 10:58:26 -0700 (Fri, 11 Apr 2008) | 4 lines

  Issue 1882:  Fix earlier patch to scons.bat by adding ~dp0;~dp0.. to
  the front of %PATH%, and only executing endlocal on NT-based systems.
  (Anatoly Techtonik)
........
  r2764 | stevenknight | 2008-04-11 13:06:29 -0700 (Fri, 11 Apr 2008) | 4 lines

  Add a Variables object and {Bool,Envum,List,Package,Path}Variable()
  functions as a first step towards eventually deprecating the
  Options object and {Bool,Envum,List,Package,Path}Option() functions.
........
  r2765 | stevenknight | 2008-04-11 18:13:53 -0700 (Fri, 11 Apr 2008) | 3 lines

  Issue 1962:  Capture a test case for ListActions that contain a
  command-line string containing unicode, and Python FunctionAction.
........
  r2766 | stevenknight | 2008-04-11 22:03:14 -0700 (Fri, 11 Apr 2008) | 3 lines

  Issue 1933:  expect .py files generated by the SWIG -python option
  to be in the same (sub)directory as the target.
........
  r2767 | stevenknight | 2008-04-12 06:41:57 -0700 (Sat, 12 Apr 2008) | 2 lines

  Remove the SCons.Options package in favor of the new SCons.Variables package.
........
  r2768 | stevenknight | 2008-04-12 13:33:52 -0700 (Sat, 12 Apr 2008) | 5 lines

  Issue 1971:  Move the incorporation of $CCFLAGS and $SHCCFLAGS directly
  into the C++ command lines (${SHCXX,CXX}COM) instead of through indirect
  expansion of $CXXFLAGS and $SHCXXFLAGS.  This requires removing -fPIC
  from the default setting of $SHCXXFLAGS under the GNU toolchain.
........
  r2769 | stevenknight | 2008-04-13 07:01:27 -0700 (Sun, 13 Apr 2008) | 3 lines

  Fix __all__ definitions in the Variables/*Variable.py file that were
  cause epydoc to blow up when generating developer documentation.
........
  r2770 | stevenknight | 2008-04-13 11:47:49 -0700 (Sun, 13 Apr 2008) | 3 lines

  Add variables= keyword argument to Environment() creation as a first
  step towards deprecating options=.
........
  r2771 | stevenknight | 2008-04-13 11:54:19 -0700 (Sun, 13 Apr 2008) | 2 lines

  Correct underscore-instead-of-hyphen misspellings in option names.
........
  r2772 | bdbaddog | 2008-04-13 17:39:21 -0700 (Sun, 13 Apr 2008) | 3 lines

  Changes to fix 15 tests which were failing on cygwin.
........
  r2773 | GregNoel | 2008-04-13 22:31:07 -0700 (Sun, 13 Apr 2008) | 1 line

  Various fixes to tests on Darwin using 1.5.2
........
  r2774 | stevenknight | 2008-04-14 15:00:44 -0700 (Mon, 14 Apr 2008) | 2 lines

  Python 1.5.2 fix in new test/SWIG/subdir.py script (use the -classic option).
........
  r2775 | belley | 2008-04-14 18:02:40 -0700 (Mon, 14 Apr 2008) | 32 lines

  Dont use KeyboardInterrupt to stop a build! [Issue 1907]

  SCons would often hang after pressing Ctrl-C. I started investigating and I
  realized that most of the Python libraries are not really safe with respect to
  asynchronous exceptions. Although,there are enough try/finally blocks to handle
  exceptions thrown synchronously by the library code, the Python libraries are
  not always protected against exceptions being thrown asynchronously, such as a
  KeyboardInterrupt being thrown at a completely random location.

  For example, the function Queue.empty() does not protect its mutex with a
  try/finally block. If the KeyboardInterrupt exception gets thrown while the
  mutex is held, any further attempt to access the Queue will lead to dead-lock
  (explaining why SCons hangs sometimes after pressing CTRL-C). Even the
  threading.Condition condition variables are not async-exception safe. It
  therefore seems a lost battle to try to stop a build by raising an exception.

  Instead, I have implemented a signal handler that tells the Jobs (and its
  associated Taskmaster) to stop the build. I have been careful to wait after the
  .sconsign file has been written back to re-install the default SIGINT signal
  handler that raises a KeyboardInterrupt exception.

  This patch is submitted against changeset 2773 of branches/core. The
  regression test suite has been run on RHEL4 using Pyhon 2.5.1 and
  1.5.2. My team has been using an SCons build with this patch for a
  while now on Windows, Linux and OSX.

  See:
  http://scons.tigris.org/issues/show_bug.cgi?id=1907

  Benoit Belley
........
  r2777 | cournape | 2008-04-14 20:11:56 -0700 (Mon, 14 Apr 2008) | 3 lines

  Initialized merge tracking via "svnmerge" with revisions "1-2776" from
  http://scons.tigris.org/svn/scons/branches/fortran_refactor
........
  r2788 | stevenknight | 2008-04-14 22:09:27 -0700 (Mon, 14 Apr 2008) | 2 lines

  Fix the print the "script" line in the --version output.
........
  r2789 | stevenknight | 2008-04-14 22:18:27 -0700 (Mon, 14 Apr 2008) | 2 lines

  Add a __COPYRIGHT__ line.
........
  r2790 | stevenknight | 2008-04-14 22:20:39 -0700 (Mon, 14 Apr 2008) | 3 lines

  Issue 2008:  in checkpoint releases, use a '.' to separate (e.g.) 0.98.0
  from 0d20080414.
........
  r2817 | belley | 2008-04-15 06:44:21 -0700 (Tue, 15 Apr 2008) | 13 lines

  TestSCons.up_to_date() should use match_re_dotall

  I changed TestSCons.up_to_date() to use match_re_dotall instead of
  match_exact. This is necessary so that I can call up_to_date() with
  the TestSCons.deprecated_python_expr error message in one of my test.

  Note that TestSCons.not_up_to_date() is already using match_re_dotall.

  Ran the test suite on both Python 2.5.1 and 1.5.2.

  Benoit
........
  r2818 | belley | 2008-04-15 12:10:52 -0700 (Tue, 15 Apr 2008) | 10 lines

  Improved the multiple-parents.py test to also tests the following
  cases:

  d) Some children are ignored
  e) Some children are pre-requesites
  f) Some sources are missing

  The test still passes. No extra bug were found.
........
  r2850 | stevenknight | 2008-04-16 11:15:24 -0700 (Wed, 16 Apr 2008) | 4 lines

  Fix problems with the __del__() method referencing other module functions
  through global variables, which can get deleted out from under us at
  shutdown.
........
  r2851 | stevenknight | 2008-04-16 11:17:07 -0700 (Wed, 16 Apr 2008) | 4 lines

  Fix use of --interactive with -u/-U/-D and VariantDir() by making the
  method that cleans Node states between interactive commands aware of
  the alter_targets() method that tells us about an associated VariantDir().
........

git-svn-id: http://scons.tigris.org/svn/scons/trunk@2868 fdb21ef1-2011-0410-befe-b5e4ea1792b1

136 files changed:
QMTest/TestSCons.py
SConstruct
bin/install-python.sh
bootstrap.py
doc/SConscript
doc/man/scons.1
doc/scons.mod
doc/user/MANIFEST
doc/user/README [new file with mode: 0644]
doc/user/add-method.in [new file with mode: 0644]
doc/user/add-method.xml [new file with mode: 0644]
doc/user/main.in
doc/user/main.xml
src/CHANGES.txt
src/RELEASE.txt
src/engine/MANIFEST.in
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py
src/engine/SCons/Debug.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Executor.py
src/engine/SCons/ExecutorTests.py
src/engine/SCons/Job.py
src/engine/SCons/JobTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Options.py [new file with mode: 0644]
src/engine/SCons/Options/.cvsignore [deleted file]
src/engine/SCons/Platform/posix.py
src/engine/SCons/SConfTests.py
src/engine/SCons/Script/Interactive.py
src/engine/SCons/Script/Main.py
src/engine/SCons/Script/SConsOptions.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Subst.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
src/engine/SCons/Tool/ToolTests.py
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/c++.py
src/engine/SCons/Tool/dmd.py
src/engine/SCons/Tool/dmd.xml
src/engine/SCons/Tool/g++.py
src/engine/SCons/Tool/intelc.py
src/engine/SCons/Tool/javac.py
src/engine/SCons/Tool/mingw.py
src/engine/SCons/Tool/mingw.xml
src/engine/SCons/Tool/packaging/__init__.py
src/engine/SCons/Tool/swig.py
src/engine/SCons/Util.py
src/engine/SCons/Variables/BoolVariable.py [moved from src/engine/SCons/Options/BoolOption.py with 89% similarity]
src/engine/SCons/Variables/BoolVariableTests.py [moved from src/engine/SCons/Options/BoolOptionTests.py with 82% similarity]
src/engine/SCons/Variables/EnumVariable.py [moved from src/engine/SCons/Options/EnumOption.py with 91% similarity]
src/engine/SCons/Variables/EnumVariableTests.py [moved from src/engine/SCons/Options/EnumOptionTests.py with 85% similarity]
src/engine/SCons/Variables/ListVariable.py [moved from src/engine/SCons/Options/ListOption.py with 94% similarity]
src/engine/SCons/Variables/ListVariableTests.py [moved from src/engine/SCons/Options/ListOptionTests.py with 81% similarity]
src/engine/SCons/Variables/PackageVariable.py [moved from src/engine/SCons/Options/PackageOption.py with 94% similarity]
src/engine/SCons/Variables/PackageVariableTests.py [moved from src/engine/SCons/Options/PackageOptionTests.py with 81% similarity]
src/engine/SCons/Variables/PathVariable.py [moved from src/engine/SCons/Options/PathOption.py with 94% similarity]
src/engine/SCons/Variables/PathVariableTests.py [moved from src/engine/SCons/Options/PathOptionTests.py with 82% similarity]
src/engine/SCons/Variables/VariablesTests.py [moved from src/engine/SCons/Options/OptionsTests.py with 91% similarity]
src/engine/SCons/Variables/__init__.py [moved from src/engine/SCons/Options/__init__.py with 91% similarity]
src/engine/SCons/compat/__init__.py
src/engine/SCons/compat/_scons_itertools.py [new file with mode: 0644]
src/engine/SCons/compat/_scons_sets15.py
src/engine/SCons/compat/_scons_textwrap.py
src/engine/SCons/dblite.py
src/script/scons.bat
src/setup.py
test/Actions/function.py
test/Actions/unicode-signature.py [new file with mode: 0644]
test/CXX/CCFLAGS.py [new file with mode: 0644]
test/CXX/CXXCOM.py
test/CXX/CXXCOMSTR.py
test/CXX/SHCXXCOM.py
test/CXX/SHCXXCOMSTR.py
test/Case.py
test/Configure/VariantDir-SConscript.py
test/Copy-Option.py
test/Deprecated/Copy-Method.py [moved from test/Deprecated/Copy.py with 100% similarity]
test/Deprecated/Options/BoolOption.py [moved from test/Options/BoolOption.py with 100% similarity]
test/Deprecated/Options/EnumOption.py [moved from test/Options/EnumOption.py with 100% similarity]
test/Deprecated/Options/ListOption.py [moved from test/Options/ListOption.py with 97% similarity]
test/Deprecated/Options/Options.py [moved from test/Options/Options.py with 100% similarity]
test/Deprecated/Options/PackageOption.py [moved from test/Options/PackageOption.py with 100% similarity]
test/Deprecated/Options/PathOption.py [moved from test/Options/PathOption.py with 100% similarity]
test/Deprecated/Options/chdir.py [moved from test/Options/chdir.py with 100% similarity]
test/Deprecated/Options/help.py [moved from test/Options/help.py with 100% similarity]
test/Deprecated/Options/import.py [moved from test/Options/import.py with 100% similarity]
test/Deprecated/debug-stree.py
test/Fortran/F77COMSTR.py
test/Fortran/F90COMSTR.py
test/Fortran/F95COMSTR.py
test/Fortran/FORTRANCOMSTR.py
test/Fortran/SHF77COMSTR.py
test/Fortran/SHF90COMSTR.py
test/Fortran/SHF95COMSTR.py
test/Fortran/SHFORTRANCOMSTR.py
test/Interactive/taskmastertrace.py
test/Interactive/tree.py
test/Interactive/variant_dir.py [new file with mode: 0644]
test/Interactive/version.py
test/Java/multi-step.py
test/KeyboardInterrupt.py [new file with mode: 0644]
test/LoadableModule.py
test/Parallel/multiple-parents.py [new file with mode: 0644]
test/QT/Tool.py
test/SConscript/Return.py
test/SWIG/subdir.py [new file with mode: 0644]
test/Variables/BoolVariable.py [new file with mode: 0644]
test/Variables/EnumVariable.py [new file with mode: 0644]
test/Variables/ListVariable.py [new file with mode: 0644]
test/Variables/PackageVariable.py [new file with mode: 0644]
test/Variables/PathVariable.py [new file with mode: 0644]
test/Variables/Variables.py [new file with mode: 0644]
test/Variables/chdir.py [new file with mode: 0644]
test/Variables/help.py [new file with mode: 0644]
test/Variables/import.py [new file with mode: 0644]
test/YACC/live.py
test/dependency-cycle.py
test/duplicate-sources.py [new file with mode: 0644]
test/exceptions.py
test/no-global-dependencies.py
test/option-v.py
test/option/d.py [moved from test/option-d.py with 95% similarity]
test/option/environment-overrides.py [moved from test/option-e.py with 86% similarity]
test/option/help-options.py [moved from test/option--H.py with 79% similarity]
test/option/no-print-directory.py [moved from test/option--npd.py with 90% similarity]
test/option/print-directory.py [moved from test/option-w.py with 87% similarity]
test/option/taskmastertrace.py
test/option/tree-all.py
test/option/tree-lib.py
test/symlink/dangling-include.py

index d5aa057f4a7350c1ac67e6b7058101d3be32c3eb..62650406883de55a33f68924285203b4dc0094c7 100644 (file)
@@ -44,6 +44,8 @@ from TestCommon import __all__
 
 default_version = '0.98.0'
 
+copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
+
 SConsVersion = '0.98.0'
 if SConsVersion == '__' + 'VERSION' + '__':
     SConsVersion = default_version
@@ -331,7 +333,9 @@ class TestSCons(TestCommon):
                 arguments = options + " " + arguments
         kw['arguments'] = arguments
         kw['stdout'] = self.wrap_stdout(read_str = read_str, build_str = s)
-        kw['match'] = self.match_exact
+        kw['stdout'] = string.replace(kw['stdout'],'\n','\\n')
+        kw['stdout'] = string.replace(kw['stdout'],'.','\\.')
+        kw['match'] = self.match_re_dotall
         apply(self.run, [], kw)
 
     def not_up_to_date(self, options = None, arguments = None, **kw):
@@ -1020,6 +1024,20 @@ print "self._msvs_versions =", str(env['MSVS']['VERSIONS'])
             time.sleep(1.0)
             waited = waited + 1.0
 
+    def get_alt_cpp_suffix(self):
+        """
+        Many CXX tests have this same logic.
+        They all needed to determine if the current os supports
+        files with .C and .c as different files or not
+        in which case they are instructed to use .cpp instead of .C
+        """
+        if not case_sensitive_suffixes('.c','.C'):
+            alt_cpp_suffix = '.cpp'
+        else:
+            alt_cpp_suffix = '.C'
+        return alt_cpp_suffix
+    
+
 # 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),
index 6614089f53b868d91cf4133dbde68b269f53738e..3f4e0c68dab5b6ec8c8a5478c7b62bbc764c4666 100644 (file)
@@ -4,8 +4,8 @@
 # See the README file for an overview of how SCons is built and tested.
 #
 
-# When this gets changed, you also need to change test/option-v.py
-# so it looks for the right string.
+# When this gets changed, you must also change the copyright_years string
+# in QMTest/TestSCons.py so the test scripts look for the right string.
 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
 
 # This gets inserted into the man pages to reflect the month of release.
@@ -118,7 +118,7 @@ if checkpoint:
         checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time()))
     elif checkpoint == 'r':
         checkpoint = 'r' + revision
-    version = version + checkpoint
+    version = version + '.' + checkpoint
 
 svn_status = None
 svn_status_lines = []
index 699a2807a587263921139f23fcd98e85b7a62d51..65e3fe4b93fd40dd7f546b8219aa3b376e0f0948 100644 (file)
@@ -76,6 +76,7 @@ Command()
 }
 
 for VERSION in $VERSIONS; do
+    DIR=`expr "$VERSION" : '\(...\)'`
     PYTHON=Python-${VERSION}
 
     TAR_GZ=${PYTHON}.tgz
@@ -83,7 +84,7 @@ for VERSION in $VERSIONS; do
         if test ! -d ${DOWNLOADS}; then
             Command mkdir ${DOWNLOADS}
         fi
-        Command "( cd ${DOWNLOADS} && wget ${DOWNLOADS_URL}/${VERSION}/${TAR_GZ} )"
+        Command "( cd ${DOWNLOADS} && wget ${DOWNLOADS_URL}/${DIR}/${TAR_GZ} )"
     fi
 
     Command tar zxf ${DOWNLOADS}/${TAR_GZ}
@@ -115,5 +116,5 @@ EOF
         ${PRINT} cd ..
     )
 
-    Command rm -rf ${Python}
+    Command rm -rf ${PYTHON}
 done
index 441d471552975bd26dfd307ba84933e7722c7135..15a29cb24fd8ed0cdfe16fe729a5c70f1f4286a3 100644 (file)
@@ -81,7 +81,15 @@ local SConstruct file.
 """
 
 bootstrap_dir = 'bootstrap'
-script_dir = os.path.split(__file__)[0]
+try:
+    script_dir = os.path.split(__file__)[0]
+except NameError:
+    # Pre-2.3 versions of Python don't have __file__.
+    script_dir = os.path.split(sys.argv[0])[0]
+    if not script_dir:
+        script_dir = os.getcwd()
+    elif not os.path.is_abs(script_dir):
+        script_dir = os.path.join(os.getcwd(), script_dir)
 if script_dir:
     bootstrap_dir = os.path.join(script_dir, bootstrap_dir)
 pass_through_args = []
index 0c8f070d51b546dab881270ea00e1a450d28c21f..a2174ea3d1ebcafc3a03f4259d7878c401a9a3a0 100644 (file)
@@ -480,7 +480,7 @@ else:
 
     epydoc_commands = [
         Delete('$OUTDIR'),
-        '$EPYDOC $EPYDOCFLAGS --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES',
+        '$EPYDOC $EPYDOCFLAGS --debug --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES',
         Touch('$TARGET'),
     ]
 
index 98e12e612f279f6be1d75c5a26a0960da25b023d..25a17fd8d4aa888901e360ba06072081ee008ff5 100644 (file)
@@ -3891,16 +3891,16 @@ The options supported are:
 .TP 6
 .B cache_debug
 .TP 6
-which corresponds to --cache_debug;
+which corresponds to --cache-debug;
 .TP 6
 .B cache_disable
-which corresponds to --cache_disable;
+which corresponds to --cache-disable;
 .TP 6
 .B cache_force
-which corresponds to --cache_force;
+which corresponds to --cache-force;
 .TP 6
 .B cache_show
-which corresponds to --cache_show;
+which corresponds to --cache-show;
 .TP 6
 .B clean
 which corresponds to -c, --clean and --remove;
@@ -6882,25 +6882,27 @@ if not conf.CheckQt('/usr/lib/qt'):
 env = conf.Finish() 
 .EE
 
-.SS Construction Variable Options
+.SS Command-Line Construction Variables
 
-Often when building software, various options need to be specified at build
-time that are not known when the SConstruct/SConscript files are
-written. For example, libraries needed for the build may be in non-standard
+Often when building software,
+some variables must be specified at build time.
+For example, libraries needed for the build may be in non-standard
 locations, or site-specific compiler options may need to be passed to the
 compiler. 
 .B scons
-provides an Options object for overridding construction variables
+provides a
+.B Variables
+object to support overriding construction variables
 on the command line:
 .ES
 $ scons VARIABLE=foo
 .EE
 The variable values can also be specified in a text-based SConscript file.
-To create an Options object, call the Options() function:
+To create a Variables object, call the Variables() function:
 
 .TP
-.RI Options([ files "], [" args ])
-This creates an Options object that will read construction variables from
+.RI Variables([ files "], [" args ])
+This creates a Variables object that will read construction variables from
 the file or list of filenames specified in
 .IR files .
 If no files are specified,
@@ -6920,16 +6922,16 @@ specified on the command line.
 Example:
 
 .ES
-opts = Options('custom.py')
-opts = Options('overrides.py', ARGUMENTS)
-opts = Options(None, {FOO:'expansion', BAR:7})
+vars = Variables('custom.py')
+vars = Variables('overrides.py', ARGUMENTS)
+vars = Variables(None, {FOO:'expansion', BAR:7})
 .EE
 
-Options objects have the following methods:
+Variables objects have the following methods:
 
 .TP
 .RI Add( key ", [" help ", " default ", " validator ", " converter ])
-This adds a customizable construction variable to the Options object. 
+This adds a customizable construction variable to the Variables object. 
 .I key
 is the name of the variable. 
 .I help 
@@ -6962,19 +6964,19 @@ and then added to the environment.
 Examples:
 
 .ES
-opts.Add('CC', 'The C compiler')
+vars.Add('CC', 'The C compiler')
 
 def validate_color(key, val, env):
     if not val in ['red', 'blue', 'yellow']:
         raise "Invalid color value '%s'" % val
-opts.Add('COLOR', validator=valid_color)
+vars.Add('COLOR', validator=valid_color)
 .EE
 
 .TP
-.RI AddOptions( list )
+.RI AddVariables( list )
 A wrapper script that adds
 multiple customizable construction variables
-to an Options object.
+to a Variables object.
 .I list
 is a list of tuple or list objects
 that contain the arguments
@@ -6983,7 +6985,7 @@ for an individual call to the
 method.
 
 .ES
-opt.AddOptions(
+opt.AddVariables(
        ('debug', '', 0),
        ('CC', 'The C compiler'),
        ('VALIDATE', 'An option for testing validation',
@@ -6998,23 +7000,23 @@ This updates a construction environment
 with the customized construction variables.
 Any specified variables that are
 .I not
-configured for the Options object
+configured for the Variables object
 will be saved and may be
 retrieved with the
-.BR UnknownOptions ()
+.BR UnknownVariables ()
 method, below.
 
 Normally this method is not called directly,
-but is called indirectly by passing the Options object to
+but is called indirectly by passing the Variables object to
 the Environment() function:
 
 .ES
-env = Environment(options=opts)
+env = Environment(variables=vars)
 .EE
 
 .IP
 The text file(s) that were specified
-when the Options object was created
+when the Variables object was created
 are executed as Python scripts,
 and the values of (global) Python variables set in the file
 are added to the construction environment.
@@ -7026,34 +7028,34 @@ CC = 'my_cc'
 .EE
 
 .TP
-.RI UnknownOptions( )
+.RI UnknownVariables( )
 Returns a dictionary containing any
 variables that were specified
 either in the files or the dictionary
-with which the Options object was intialized,
-but for which the Options object was
+with which the Variables object was initialized,
+but for which the Variables object was
 not configured.
 
 .ES
-env = Environment(options=opts)
-for key, value in opts.UnknownOptions():
+env = Environment(variables=vars)
+for key, value in vars.UnknownVariables():
     print "unknown variable:  %s=%s" % (key, value)
 .EE
 
 .TP
 .RI Save( filename ", " env )
-This saves the currently set options into a script file named  
+This saves the currently set variables into a script file named  
 .I filename
 that can be used on the next invocation to automatically load the current
-settings.  This method combined with the Options method can be used to
-support caching of options between runs.
+settings.  This method combined with the Variables method can be used to
+support caching of variables between runs.
 
 .ES
 env = Environment()
-opts = Options(['options.cache', 'custom.py'])
-opts.Add(...)
-opts.Update(env)
-opts.Save('options.cache', env)
+vars = Variables(['variables.cache', 'custom.py'])
+vars.Add(...)
+vars.Update(env)
+vars.Save('variables.cache', env)
 .EE
 
 .TP
@@ -7079,12 +7081,12 @@ and return
 function).
 
 .ES
-Help(opts.GenerateHelpText(env))
-Help(opts.GenerateHelpText(env, sort=cmp))
+Help(vars.GenerateHelpText(env))
+Help(vars.GenerateHelpText(env, sort=cmp))
 .EE
 
 .TP
-.RI FormatOptionHelpText( env ", " opt ", " help ", " default ", " actual )
+.RI FormatVariableHelpText( env ", " opt ", " help ", " default ", " actual )
 This method returns a formatted string
 containing the printable help text
 for one option.
@@ -7106,17 +7108,17 @@ string if you want the entries separated.
 def my_format(env, opt, help, default, actual):
     fmt = "\n%s: default=%s actual=%s (%s)\n"
     return fmt % (opt, default. actual, help)
-opts.FormatOptionHelpText = my_format
+vars.FormatVariableHelpText = my_format
 .EE
 
-To make it more convenient to work with customizable Options,
+To make it more convenient to work with customizable Variables,
 .B scons
 provides a number of functions
 that make it easy to set up
-various types of Options:
+various types of Variables:
 
 .TP
-.RI BoolOption( key ", " help ", " default )
+.RI BoolVariable( key ", " help ", " default )
 Return a tuple of arguments
 to set up a Boolean option.
 The option will use
@@ -7149,7 +7151,7 @@ and
 as false.
 
 .TP
-.RI EnumOption( key ", " help ", " default ", " allowed_values ", [" map ", " ignorecase ])
+.RI EnumVariable( key ", " help ", " default ", " allowed_values ", [" map ", " ignorecase ])
 Return a tuple of arguments
 to set up an option
 whose value may be one
@@ -7196,7 +7198,7 @@ and all input values will be
 converted to lower case.
 
 .TP
-.RI ListOption( key ", " help ", " default ", " names ", [", map ])
+.RI ListVariable( key ", " help ", " default ", " names ", [", map ])
 Return a tuple of arguments
 to set up an option
 whose value may be one or more
@@ -7230,7 +7232,7 @@ in the
 list.
 
 .TP
-.RI PackageOption( key ", " help ", " default )
+.RI PackageVariable( key ", " help ", " default )
 Return a tuple of arguments
 to set up an option
 whose value is a path name
@@ -7268,7 +7270,7 @@ or
 to disable use of the specified option.
 
 .TP
-.RI PathOption( key ", " help ", " default ", [" validator ])
+.RI PathVariable( key ", " help ", " default ", [" validator ])
 Return a tuple of arguments
 to set up an option
 whose value is expected to be a path name.
@@ -7288,18 +7290,18 @@ verify that the specified path
 is acceptable.
 SCons supplies the
 following ready-made validators:
-.BR PathOption.PathExists
+.BR PathVariable.PathExists
 (the default),
 which verifies that the specified path exists;
-.BR PathOption.PathIsFile ,
+.BR PathVariable.PathIsFile ,
 which verifies that the specified path is an existing file;
-.BR PathOption.PathIsDir ,
+.BR PathVariable.PathIsDir ,
 which verifies that the specified path is an existing directory;
-.BR PathOption.PathIsDirCreate ,
+.BR PathVariable.PathIsDirCreate ,
 which verifies that the specified path is a directory
 and will create the specified directory if the path does not exist;
 and
-.BR PathOption.PathAccept ,
+.BR PathVariable.PathAccept ,
 which simply accepts the specific path name argument without validation,
 and which is suitable if you want your users
 to be able to specify a directory path that will be
@@ -7309,7 +7311,7 @@ You may supply your own
 function,
 which must take three arguments
 .RI ( key ,
-the name of the options variable to be set;
+the name of the variable to be set;
 .IR val ,
 the specified value being checked;
 and
@@ -7321,27 +7323,27 @@ if the specified value is not acceptable.
 .RE
 These functions make it
 convenient to create a number
-of options with consistent behavior
+of variables with consistent behavior
 in a single call to the
-.B AddOptions
+.B AddVariables
 method:
 
 .ES
-opts.AddOptions(
-    BoolOption('warnings', 'compilation with -Wall and similiar', 1),
-    EnumOption('debug', 'debug output and symbols', 'no'
+vars.AddVariables(
+    BoolVariable('warnings', 'compilation with -Wall and similiar', 1),
+    EnumVariable('debug', 'debug output and symbols', 'no'
                allowed_values=('yes', 'no', 'full'),
                map={}, ignorecase=0),  # case sensitive
-    ListOption('shared',
+    ListVariable('shared',
                'libraries to build as shared libraries',
                'all',
                names = list_of_libs),
-    PackageOption('x11',
+    PackageVariable('x11',
                   'use X11 installed here (yes = search some places)',
                   'yes'),
-    PathOption('qtdir', 'where the root of Qt is installed', qtdir),
-    PathOption('foopath', 'where the foo library is installed', foopath,
-               PathOption.PathIsDir),
+    PathVariable('qtdir', 'where the root of Qt is installed', qtdir),
+    PathVariable('foopath', 'where the foo library is installed', foopath,
+               PathVariable.PathIsDir),
 
 )
 .EE
@@ -9460,10 +9462,10 @@ The following would allow the C compiler to be specified on the command
 line or in the file custom.py. 
 
 .ES
-opts = Options('custom.py')
-opts.Add('CC', 'The C compiler.')
-env = Environment(options=opts)
-Help(opts.GenerateHelpText(env))
+vars = Variables('custom.py')
+vars.Add('CC', 'The C compiler.')
+env = Environment(variables=vars)
+Help(vars.GenerateHelpText(env))
 .EE
 
 The user could specify the C compiler on the command line:
index 739be581bf07d5b9ce3b04d79c24f810b3582110..ad20b7b897306dd31936766e882feffb3b98a022 100644 (file)
 <!ENTITY AddPostAction "<function>AddPostAction</function>">
 <!ENTITY AddPreAction "<function>AddPreAction</function>">
 <!ENTITY AddOptions "<function>AddOptions</function>">
+<!ENTITY AddVariables "<function>AddVariables</function>">
 <!ENTITY Alias "<function>Alias</function>">
 <!ENTITY Aliases "<function>Aliases</function>">
 <!ENTITY AlwaysBuild "<function>AlwaysBuild</function>">
 <!ENTITY AppendENVPath "<function>AppendENVPath</function>">
 <!ENTITY AppendUnique "<function>AppendUnique</function>">
 <!ENTITY BoolOption "<function>BoolOption</function>">
+<!ENTITY BoolVariable "<function>BoolVariable</function>">
 <!ENTITY Build "<function>Build</function>">
 <!ENTITY CacheDir "<function>CacheDir</function>">
 <!ENTITY Chmod "<function>Chmod</function>">
 <!ENTITY Dump "<function>Dump</function>">
 <!ENTITY Entry "<function>Entry</function>">
 <!ENTITY EnumOption "<function>EnumOption</function>">
+<!ENTITY EnumVariable "<function>EnumVariable</function>">
 <!ENTITY Environment "<function>Environment</function>">
 <!ENTITY Execute "<function>Execute</function>">
 <!ENTITY Export "<function>Export</function>">
 <!ENTITY InstallAs "<function>InstallAs</function>">
 <!ENTITY Link "<function>Link</function>">
 <!ENTITY ListOption "<function>ListOption</function>">
+<!ENTITY ListVariable "<function>ListVariable</function>">
 <!ENTITY Local "<function>Local</function>">
 <!ENTITY Mkdir "<function>Mkdir</function>">
 <!ENTITY Module "<function>Module</function>">
 <!ENTITY NoCache "<function>NoCache</function>">
 <!ENTITY Objects "<function>Objects</function>">
 <!ENTITY Options "<function>Options</function>">
+<!ENTITY Variables "<function>Variables</function>">
 <!ENTITY PackageOption "<function>PackageOption</function>">
+<!ENTITY PackageVariable "<function>PackageVariable</function>">
 <!ENTITY ParseConfig "<function>ParseConfig</function>">
 <!ENTITY PathOption "<function>PathOption</function>">
 <!ENTITY PathOption_PathAccept "<function>PathOption.PathAccept</function>">
 <!ENTITY PathOption_PathIsDir "<function>PathOption.PathIsDir</function>">
 <!ENTITY PathOption_PathIsDirCreate "<function>PathOption.PathIsDirCreate</function>">
 <!ENTITY PathOption_PathIsFile "<function>PathOption.PathIsFile</function>">
+<!ENTITY PathVariable "<function>PathVariable</function>">
+<!ENTITY PathVariable_PathAccept "<function>PathVariable.PathAccept</function>">
+<!ENTITY PathVariable_PathExists "<function>PathVariable.PathExists</function>">
+<!ENTITY PathVariable_PathIsDir "<function>PathVariable.PathIsDir</function>">
+<!ENTITY PathVariable_PathIsDirCreate "<function>PathVariable.PathIsDirCreate</function>">
+<!ENTITY PathVariable_PathIsFile "<function>PathVariable.PathIsFile</function>">
 <!ENTITY Precious "<function>Precious</function>">
 <!ENTITY Prepend "<function>Prepend</function>">
 <!ENTITY PrependENVPath "<function>PrependENVPath</function>">
index 565298ff71c0c2939d860fef65a7edd2b6d2a7b0..b3106af7abada3514b7ed15821e768b7c5ca4c4b 100644 (file)
@@ -1,4 +1,5 @@
 actions.xml
+add-method.xml
 alias.xml
 ant.xml
 builders.xml
diff --git a/doc/user/README b/doc/user/README
new file mode 100644 (file)
index 0000000..7bca314
--- /dev/null
@@ -0,0 +1,19 @@
+# __COPYRIGHT__
+
+When adding a new file, add it to main.xml and MANIFEST.
+
+To build the .xml files from the .in files:
+  scons -D . BUILDDOC=1
+
+Writing examples: here's a simple template.
+
+ <scons_example name="Foo">
+   <file name="SConstruct">
+    env = Environment()
+    print env.Dump("CC")
+   </file>
+ </scons_example>
+
+ <scons_output example="Foo">
+    <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
diff --git a/doc/user/add-method.in b/doc/user/add-method.in
new file mode 100644 (file)
index 0000000..853b9a8
--- /dev/null
@@ -0,0 +1,105 @@
+<!--
+
+  __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.
+
+-->
+
+  <para>
+
+  The env.AddMethod(function, [name]) function is used to add a method
+  to an environment.  It's typically used to add a "pseudo-builder" or
+  wrap up a call to multiple builders.  In the first example, we want
+  to install the program into the standard bin dir, but also copy it
+  into a local install/bin dir that might be used to build a package
+  from.
+
+  </para>
+
+  <scons_example name="ex1">
+     <file name="SConstruct" printme="1">
+     def install_in_bin_dirs(env, source):
+         """Install source in both bin dirs"""
+         i1 = env.Install("$BIN", source)
+         i2 = env.Install("$LOCALBIN", source)
+         return [i1[0], i2][0] # Return a list, like a normal builder
+     env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin')
+     env.AddMethod(install_in_bin_dirs, "InstallInBinDirs")
+     env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs     
+     </file>
+     <file name="hello.c">
+     int main() { printf("Hello, world!\n"); }
+     </file>
+  </scons_example>
+
+  <para>
+  This produces the following:
+  </para>
+
+  <scons_output example="ex1">
+    <scons_output_command>scons -Q</scons_output_command>
+  </scons_output>
+
+  <para>
+
+  It also gives more flexibility in parsing arguments than you can get
+  with a builder.  The next example shows a pseudo-builder with a
+  named argument that modifies the filename, and a separate argument
+  for the resource file (rather than having the builder figure it out
+  by file extension).  Also this example demonstrates using the global
+  AddMethod function to add a method to the global Environment class,
+  so it will be used in all subsequently created environments.
+
+  </para>
+
+  <scons_example name="ex2">
+     <file name="SConstruct" printme="1">
+     import sys;
+     def BuildTestProg(env, testfile, resourcefile, testdir="tests"):
+         """Build the test program;
+         prepends "test_" to src and target, and puts target into testdir."""
+         srcfile="test_%s.c"%testfile
+         if sys.platform=='win32':
+             target="%s/test_%s$EXESUFFIX"%(testdir,[testfile, resourcefile])
+         else:
+             target="%s/test_%s$EXESUFFIX"%(testdir,testfile)
+         p = env.Program(target, srcfile)
+         return p
+     AddMethod(Environment, BuildTestProg)
+
+     # Now use it
+     env=Environment()
+     env.BuildTestProg('stuff', resourcefile='res.rc')
+     </file>
+     <file name="test_stuff.c">
+     int main() { printf("Hello, world!\n"); }
+     </file>
+  </scons_example>
+
+  <para>
+  This produces the following (on Linux, anyway; Windows would include the
+  resource file):
+  </para>
+
+  <scons_output example="ex2">
+    <scons_output_command>scons -Q</scons_output_command>
+  </scons_output>
+
diff --git a/doc/user/add-method.xml b/doc/user/add-method.xml
new file mode 100644 (file)
index 0000000..22f5f1a
--- /dev/null
@@ -0,0 +1,100 @@
+<!--
+
+  __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.
+
+-->
+
+  <para>
+
+  The env.AddMethod(function, [name]) function is used to add a method
+  to an environment.  It's typically used to add a "pseudo-builder" or
+  wrap up a call to multiple builders.  In the first example, we want
+  to install the program into the standard bin dir, but also copy it
+  into a local install/bin dir that might be used to build a package
+  from.
+
+  </para>
+
+  <programlisting>
+     def install_in_bin_dirs(env, source):
+         """Install source in both bin dirs"""
+         i1 = env.Install("$BIN", source)
+         i2 = env.Install("$LOCALBIN", source)
+         return [i1[0], i2][0] # Return a list, like a normal builder
+     env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin')
+     env.AddMethod(install_in_bin_dirs, "InstallInBinDirs")
+     env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs     
+  </programlisting>
+
+  <para>
+  This produces the following:
+  </para>
+
+  <screen>
+    % <userinput>scons -Q</userinput>
+    cc -o hello.o -c hello.c
+    cc -o hello hello.o
+    Install file: "hello" as "install/bin/hello"
+  </screen>
+
+  <para>
+
+  It also gives more flexibility in parsing arguments than you can get
+  with a builder.  The next example shows a pseudo-builder with a
+  named argument that modifies the filename, and a separate argument
+  for the resource file (rather than having the builder figure it out
+  by file extension).  Also this example demonstrates using the global
+  AddMethod function to add a method to the global Environment class,
+  so it will be used in all subsequently created environments.
+
+  </para>
+
+  <programlisting>
+     import sys
+     def BuildTestProg(env, testfile, resourcefile, testdir="tests"):
+         """Build the test program;
+         prepends "test_" to src and target, and puts target into testdir."""
+         srcfile="test_%s.c"%testfile
+         if sys.platform=='win32':
+             target="%s/test_%s$EXESUFFIX"%(testdir,[testfile, resourcefile])
+         else:
+             target="%s/test_%s$EXESUFFIX"%(testdir,testfile)
+         p = env.Program(target, srcfile)
+         return p
+     AddMethod(Environment, BuildTestProg)
+
+     # Now use it
+     env=Environment()
+     env.BuildTestProg('stuff', resourcefile='res.rc')
+  </programlisting>
+
+  <para>
+  This produces the following (on Linux, anyway; Windows would include the
+  resource file):
+  </para>
+
+  <screen>
+    % <userinput>scons -Q</userinput>
+    cc -o test_stuff.o -c test_stuff.c
+    cc -o tests/test_stuff test_stuff.o
+  </screen>
+
index 4095e6b2601289f3dccdf90fc9eb98121e4a0d9a..8981b148d302e1f58fb23836877718d9b3330c27 100644 (file)
@@ -51,6 +51,7 @@
     <!ENTITY builders-built-in SYSTEM "builders-built-in.xml">
     <!ENTITY builders-commands SYSTEM "builders-commands.xml">
     <!ENTITY builders-writing SYSTEM "builders-writing.xml">
+    <!ENTITY add-method SYSTEM "add-method.xml">
     <!ENTITY caching SYSTEM "caching.xml">
     <!ENTITY command-line SYSTEM "command-line.xml">
     <!ENTITY copyright SYSTEM "copyright.xml">
 
   XXX Progress()
 
-  XXX AddMethod()
-
   XXX - - diskcheck=
 
   XXX site_scons
     &builders-commands;
   </chapter>
 
+  <chapter id="chap-add-method">
+    <title>Pseudo-Builders:  the AddMethod function</title>
+    &add-method;
+  </chapter>
+
   <!--
 
   XXX Action()
index 4095e6b2601289f3dccdf90fc9eb98121e4a0d9a..8981b148d302e1f58fb23836877718d9b3330c27 100644 (file)
@@ -51,6 +51,7 @@
     <!ENTITY builders-built-in SYSTEM "builders-built-in.xml">
     <!ENTITY builders-commands SYSTEM "builders-commands.xml">
     <!ENTITY builders-writing SYSTEM "builders-writing.xml">
+    <!ENTITY add-method SYSTEM "add-method.xml">
     <!ENTITY caching SYSTEM "caching.xml">
     <!ENTITY command-line SYSTEM "command-line.xml">
     <!ENTITY copyright SYSTEM "copyright.xml">
 
   XXX Progress()
 
-  XXX AddMethod()
-
   XXX - - diskcheck=
 
   XXX site_scons
     &builders-commands;
   </chapter>
 
+  <chapter id="chap-add-method">
+    <title>Pseudo-Builders:  the AddMethod function</title>
+    &add-method;
+  </chapter>
+
   <!--
 
   XXX Action()
index dee595137c2a174db4a216440929881be9e35356..9550392d6cf1a25729659ee99305e0a63276e1a2 100644 (file)
@@ -8,6 +8,72 @@
 
 
 
+RELEASE X.XX - XXX
+
+  From Benoit Belley:
+
+  - Speed up the SCons.Util.to_string*() functions.
+
+  - Optimize various Node intialization and calculations.
+
+  - Optimize Executor scanning code.
+
+  - Optimize Taskmaster execution, including dependency-cycle checking.
+
+  - Fix the --debug=stree option so it prints its tree once, not twice.
+
+  From Johan BoulÃ:
+
+  - Fix the ability to use LoadableModule() under MinGW.
+
+  From Steven Knight:
+
+  - Make the -d, -e, -w and --no-print-directory options "Ignored for
+    compatibility."  (We're not going to implement them.)
+
+  - Fix a serious inefficiency in how SCons checks for whether any source
+    files are missing when a Builder call creates many targets from many
+    input source files.
+
+  - In Java projects, make the target .class files depend only on the
+    specific source .java files where the individual classes are defined.
+
+  - Don't store duplicate source file entries  in the .sconsign file so
+    we don't endlessly rebuild the target(s) for no reason.
+
+  - Add a Variables object as the first step towards deprecating the
+    Options object name.  Similarly, add BoolVariable(), EnumVariable(),
+    ListVariable(), PackageVariable() and PathVariable() functions
+    as first steps towards replacing BoolOption(), EnumOption(),
+    ListOption(), PackageOption() and PathOption().
+
+  - Change the options= keyword argument to the Environment() function
+    to variables=, to avoid confusion with SCons command-line options.
+    Continue supporting the options= keyword for backwards compatibility.
+
+  - When $SWIGFLAGS contains the -python flag, expect the generated .py
+    file to be in the same (sub)directory as the target.
+
+  - When compiling C++ files, allow $CCFLAGS settings to show up on the
+    command line even when $CXXFLAGS has been redefined.
+
+  - Fix --interactive with -u/-U/-D when a VariantDir() is used.
+
+  From Anatoly Techtonik:
+
+  - Have the scons.bat file add the script execution directory to its
+    local %PATH% on Windows, so the Python executable can be found.
+
+  From Mike Wake:
+
+  - Fix passing variable names as a list to the Return() function.
+
+  From Matthew Wesley:
+
+  - Add support for the GDC 'D' language compiler.
+
+
+
 RELEASE 0.98 - Sun, 30 Mar 2008 23:33:05 -0700
 
   From Benoit Belley:
index 0c4f90564a207e5f0e0258d725d6a973083abea8..7eaaa1cc7f4ffc2f25a8c3a11c6a4b20646c0a97 100644 (file)
@@ -20,10 +20,41 @@ more effectively, please sign up for the scons-users mailing list at:
 
 
 
-RELEASE 0.98 - Sun, 30 Mar 2008 23:33:05 -0700
-
-  This is the ninth beta release of SCons.  Please consult the
-  CHANGES.txt file for a list of specific changes since last release.
+RELEASE 0.98.1 - XXX
+
+  This is an update to the ninth 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.98:
+
+    --  SCONS NO LONGER SETS THE GNU TOOLCHAIN -fPIC FLAG IN $SHCXXFLAGS
+
+        The GNU toolchain support in previous versions of SCons would
+        add the -fPIC flag to the $SHCXXFLAGS construction variable.
+        The -fPIC flag has been now been removed from the default
+        $SHCXXFLAGS setting.  Instead, the $SHCXXCOM construction variable
+        (the default SCons command line for compiling shared objects
+        from C++ source files) has been changed to add the $SHCCFLAGS
+        variable, which contains the -fPIC flag.
+
+        This change was made in order to make the behavior of the default
+        C++ compilation line including $SHCCFLAGS consistent with the
+        default C compilation line including $CCFLAGS.
+
+        This change should have no impact on configurations that use
+        the default $SHCXXCOM command line.  It may have an impact on
+        configurations that were using the default $SHCXXFLAGS value
+        *without* the $SHCCFLAGS variable to get the -fPIC flag into a
+        custom command line.  You can fix these by adding the $SHCCFLAGS
+        to the custom command line.
+
+        Adding $SHCCFLAGS is backwards compatible with older SCons
+        releases, although it might cause the -fPIC flag to be repeated
+        on the command line if you execute it on an older version of
+        SCons that sets -fPIC in both the $SHCCLAFGS and $SHCXXFLAGS
+        variables.  Duplicating the -fPIC flag on the g++ command line
+        will not cause any compilation problems, but the change to the
+        command line may cause SCons to rebuild object files.
 
   Please note the following important changes since release 0.97.0d20071212:
 
@@ -418,442 +449,108 @@ RELEASE 0.98 - Sun, 30 Mar 2008 23:33:05 -0700
         might cause a compatibility issue if a script or other utility
         looks for an exact match of the previous text.
 
-  Please note the following important changes since release 0.96.93:
-
-    --  THE --debug=memoizer OPTION NOW REQUIRES PYTHON 2.2 OR LATER
-
-        The --debug=memoizer option now prints a warning message and
-        does nothing if SCons is run on a version of Python that does
-        not support metaclasses (earlier than Python 2.2).
-
-    --  THE --debug=nomemoizer OPTION DOES NOTHING AND IS NOW DEPRECATED
-
-        The --debug=nomemoizer no longer does anything and instead
-        now generates a warning message about being deprecated.  The
-        --debug=nomemoizer will be removed completely in a future release.
-
-  Please note the following important changes since release 0.96.91:
-
-    --  /opt/bin AND /sw/bin ADDED TO DEFAULT EXECUTION PATH VARIABLES
-
-        On all POSIX systems, the default execution PATH variable has had
-        the /opt/bin directory added after the /usr/local/bin directory
-        and before /bin and /usr/bin directories.  This may cause SCons
-        to find and/or use different compilers, linkers, etc., if you
-        have any same-named utilities installed in /opt/bin that SCons
-        previously found in /bin or /usr/bin.
-
-        On Mac OS X (Darwin) systems, the /sw/bin directory has been added
-        to the end of the default execution PATH.  This may cause SCons
-        to find compilers, linkers and other utilities it previously did
-        not, although it should not otherwise change existing behavior.
-
-    --  Configure.Checklib() ARGUMENTS HAVE CHANGED TO MATCH DOCUMENTATION
-
-        The order of the arguments to the Configure.CheckLib() function
-        has changed to put the "autoadd" keyword argument last, matching
-        the documentation in the man page.  This could cause problems
-        for any calls to Configure.Checklib() that were relying on the
-        order of the arguments.  Specifying all arguments as keyword
-        arguments will work on both older and newer versions of SCons.
-
-    --  env.subst() NO LONGER EXPANDS $TARGET, $SOURCES, etc. BY DEFAULT
-
-        Calls to the env.subst() method to interpolate construction
-        variables in strings no longer automatically expand the special
-        variables $TARGET, $TARGETS, $SOURCE and $SOURCES.  The keyword
-        variables "target" and "source" must now be set to the lists
-        of target and source files to be used in expansion of those
-        variables, when desired.
-
-        This is most likely necessary for any env.subst() calls within
-        a Python function being used as an SCons action for a Builder:
-
-            def build_it(env, target, source):
-                env.subst('$STRING', target=targets, source=sources)
-            MyBuilder = Builder(action=build_it)
-
-        The "target" and "source" keyword arguments are backwards
-        compatible and can be added to SConscript files without breaking
-        builds on systems using older SCons releases.
-
-    --  INTERNAL FUNCTIONS AND CLASSES HAVE MOVED FROM SCons.Util
-
-        All internal functions and classes related to string substitution
-        have been moved out of the SCons.Util module into their own
-        SCons.Subst module.  The following classes have been moved:
-
-                Literal
-                SpecialAttrWrapper
-                NLWrapper
-                Targets_or_Sources
-                Target_or_Source
-
-        And the following functions have moved:
-
-                quote_spaces()
-                escape_list()
-                subst_dict()
-                scons_subst()
-                scons_subst_list()
-                scons_subst_once()
-
-        If your SConscript files have been using any of these function
-        directly from the SCons.Util module (which they ultimately should
-        not be!), you will need to modify them.
-
-  Please note the following important changes since release 0.96.90:
-
-    --  SIGNATURES ARE NOW STORED IN AN SConsignFile() BY DEFAULT,
-        CAUSING LIKELY REBUILDS; SPECIAL NOTE CONCERNING INTERACTION
-        WITH REPOSITORIES
-
-        The default behavior has been changed to store signature
-        information in a single .sconsign.dblite file in the top-level
-        SConstruct file.  This will cause rebuilds on upgrade to 0.97,
-        unless you were already calling the SConsignFile() function in
-        your SConscript files.
-
-        The previous default behavior was to store signature information
-        in a .sconsign file in each directory that contained target
-        files that SCons knew about.  The old behavior may be preserved
-        by specifying:
-
-              SConsignFile(None)
-
-        in any SConscript file.
-
-        If you are using the Repository feature, and are not already
-        using the SConsignFile() function in your build, you *must*
-        add "SConsignFile(None)" to your build configuration to keep
-        interoperating with an existing Repository that uses the old
-        behavior of a .sconsign file in each directory.  Alternatively,
-        you can rebuild the Repository with the new default behavior.
-
-    --  OTHER SIGNATURE CHANGES WILL CAUSE LIKELY REBUILDS AFTER UPGRADE
-
-        This release adds several changes to the signature mechanism that
-        will cause SCons to rebuild most configurations after upgrading
-        (and when switching back to an earlier release from 0.97).
-        These changes are:
-
-          --  NORMALIZED PATHS IN SConsignFile() DATABASES ON WINDOWS
-
-              When using an SConsignFile() database, instead of
-              individual .sconsign files in each directory, the path
-              names are stored in normalized form with / (forward slash)
-              separating the elements.  This may cause rebuilds when
-              upgrading to SCons 0.97 on Windows systems with hierarchical
-              build configurations.
-
-          --  STORED DEPENDENCY PATHS ARE NOW RELATIVE TO THE TARGET
-
-              SCons used to store the paths of all source files and
-              dependencies relative to the top-level SConstruct directory.
-              It now stores them relative to the directory of the
-              associated target file.  This makes it possible to use
-              content signatures to subdivide a dependency tree without
-              causing unnecessary rebuilds due to an intermediate file in
-              one build being treated as a source file in a nother build.
-
-              This is a step towards making it possible to write a
-              hierarchy of SConstruct files that allow developers
-              to build just one portion of a tree wherever there's an
-              SConstruct file.  (Note that this would still require some
-              specific code at the top of each SConstruct file, but we
-              hope to make this an easier/more naturally supported thing
-              in the future.)
-
-          --  PYTHON FUNCTION ACTION SIGNATURES HAVE CHANGED TO AVOID
-              FUTURE REBUILDS AND REBUILDS BETWEEN PYTHON VERSIONS
-
-              SCons Actions for Python functions use the function's
-              byte code to generate their signature.  The byte code
-              in older versions of Python includes indications of the
-              line numbers at which the function's code appeared in
-              its original source file, which means that changes in the
-              location of an otherwise unmodified Python function would
-              trigger rebuilds.  The line number byte codes are now
-              removed from the signature, which will cause any targets
-              built by Python function Actions (including various
-              pre-supplied SCons Actions) to be rebuilt.
-
-          --  REMOVED CONVERSION FROM PRE-0.96 .sconsign FORMATS
-
-              Because this release involves so many other signature
-              changes that cause rebuilds, the support for automatically
-              converting signature information from .sconsign files
-              written by SCons versions prior to 0.96 has been removed.
-
-          --  ORDER OF -o FLAGS ON CERTAIN LINK COMMAND LINES HAS CHANGED
-
-              The -o flag that specifies an output file has been moved
-              on certain linker command lines to place it consistently
-              right after the link command itself.  This will cause
-              recompilation of target files created by these changed
-              lines.
-
-    --  F95 AND F90 COMPILERS ARE NOW PREFERRED OVER F77
-
-        SCons now searches for Fortran 95 and Fortran 90 compilers first
-        in preference to Fortran 77.  This may result in a different
-        Fortran compiler being used by default, although as Fortran 95 and
-        Fortran 90 are backwards compatible with Fortran 77, this should
-        not cause problems for standards-compliant Fortran programs.
-        On systems that have multiple versions of Fortran installed,
-        the Fortran 77 compiler may be explicitly selected by specifying
-        it when creating the construction environment:
-
-            env = Environment(tools = ['default', 'f77'])
-
-    --  SOLARIS DEFAULT SHARED OBJECT PREFIXES AND SUFFIXES HAVE CHANGED
-
-        On Solaris, SCons now builds shared objects from C and C++ source
-        files with a default prefix of "so_" and a default suffix of ".o".
-        The previous default suffix of ".os" caused problems when trying
-        to use SCons with Sun WorkShop.
-
-    --  CACHED Configure() RESULTS ARE STORED IN A DIFFERENT FILE
-
-        The Configure() subsystem now stores its cached results in a
-        different file.  This may cause configuration tests to be re-run
-        the first time after you install 0.97.
-
-    --  setup.py INSTALLS VERSION-NUMBERED SCRIPTS AND DIRS BY DEFAULT
-
-        The setup.py script has been changed to always install SCons in
-        a version-numbered directory (e.g. /usr/local/lib/scons-0.97
-        or D:\Python23\scons-0.97) and with a version-numbered script
-        name (scons-0.97) in addition to the usual installation of an
-        "scons" script name.  A number of new setup.py options allow
-        control over what does or does not get installed, and where.
-        See the README.txt or README files for additional information.
-
-    --  setup.py NOW INSTALLS MAN PAGES ON UNIX AND Linux SYSTEMS
-
-        The SCons setup.py script now installs the "scons.1" and
-        "sconsign.1" man pages on UNIX and Linux systems.  A
-        new --no-install-man
-
-    --  BUILDERS RETURN A LIST-LIKE OBJECT, NOT A REGULAR LIST
-
-        Builder calls now return an object that behaves like a list
-        (and which provides some other functionality), not an underlying
-        Python list.  In general, this should not cause any problems,
-        although it introduces a subtle change in the following behavior:
-
-                obj += env.Object('foo.c')
-
-        If "obj" is a regular Python list, Python will no longer update
-        the "obj" in place, because the return value from env.Object()
-        is no longer the same type.  Python will instead allocate a
-        new object and assign the local variable "obj" to it.  If "obj"
-        is defined in an SConscript file that calls another SConscript
-        file containing the above code, "obj" in the first SConscript
-        file will not contain the object file nodes created by the
-        env.Object() call.
-
-        You can guarantee that a list will be updated in place regardless
-        of which SConscript file defines it and which adds to it by
-        using the list extend() method as follows:
-
-                obj.extend(env.Object('foo.c'))
-
-  Please note the following important changes since release 0.96.1:
-
-    --  DIRECTORY TREES ARE NO LONGER AUTOMATICALLY SCANNED FOR CHANGES
-
-        Custom builders and Command() calls that accept directories as
-        source arguments no longer scan entire on-disk directory trees by
-        default.  This means that their targets will not be automatically
-        rebuilt if a file changes on disk *unless* SCons already knows
-        about the file from a specific Builder or File() call.  Note that
-        the targets will still be rebuilt correctly if a file changes
-        that SCons already knows about due to a Builder or other call.
-
-        The existing behavior of scanning on-disk directory trees for
-        any changed file can be maintained by passing the new DirScanner
-        global directory scanner as the source_scanner keyword argument
-        to the Builder call:
-
-            bld = Builder("build < $SOURCE > $TARGET",
-                          source_scanner = DirScanner)
-
-        The same keyword argument can also be supplied to any Command()
-        calls that need to scan directory trees on-disk for changed files:
-
-            env.Command("archive.out", "directory",
-                        "archiver -o $TARGET $SOURCE",
-                        source_scanner = DirScanner)
-
-        This change was made because scanning directories by default
-        could cause huge slowdowns if a configurable directory like /usr
-        or /usr/local was passed as the source to a Builder or Command()
-        call, in which case SCons would scan the entire directory tree.
-
-    --  ParseConfig() METHOD ADDS LIBRARY FILE NAMES TO THE $LIBS VARIABLE
-
-        The ParseConfig() method now adds library file names returned
-        by the specified *-config command to the $LIBS construction
-        variable, instead of returning them (the same way it handles
-        the -l option).
-
-    --  ParseConfig() METHOD DOESN'T ADD DUPLICATES TO CONSTRUCTION VARIABLES
-
-        By default, the ParseConfig() method now avoids adding duplicate
-        entries to construction variables.  The old behavior may be
-        specified using a new "unique=0" keyword argument.
-
-    --  WINDOWS %TEMP% and %TMP% VARIABLES ARE PROPAGATED AUTOMATICALLY
-
-        The %TEMP% and %TMP% external environment variables are now
-        propagated automatically to the command execution environment on
-        Windows systems.
-
-    --  OUTPUT OF Configure() SUBSYSTEM CHANGED SLIGHTLY
-
-        The Configure() subsystem now reports tests results as "yes" and
-        "no" instead of "ok" and "failed."  This might interfere with any
-        scripts that automatically parse the Configure() output from SCons.
-
-    --  VISUAL STUDIO ATL AND MFC DIRECTORIES NOT ADDED BY DEFAULT
-
-        When compiling with Microsoft Visual Studio, SCons no longer
-        adds the ATL and MFC directories to the INCLUDE and LIB
-        environment variables by default.  If you want these directories
-        included in your environment variables, you should now set the
-        $MSVS_USE_MFC_DIRS *construction* variable when initializing
-        your environment:
-
-            env = Environment(MSVS_USE_MFC_DIRS = 1)
-
-    --  DEPRECATED GLOBAL FUNCTIONS HAVE BEEN REMOVED
-
-        The following deprecated global functions have been removed:
-        ParseConfig(), SetBuildSignatureType(), SetContentSignatureType(),
-        SetJobs() and GetJobs().
-
-    --  DEPRECATED "validater" KEYWORD HAS BEEN REMOVED
-
-        The deprecated "validater" keyword to the Options.Add() method
-        has been removed.
-
-  Please note the following important changes since release 0.95:
-
-    --  BUILDERS NOW ALWAYS RETURN A LIST OF TARGETS
-
-        All Builder calls (both built-in like Program(), Library(),
-        etc. and customer Builders) now always return a list of target
-        Nodes.   If the Builder only builds one target, the Builder
-        call will now return a list containing that target Node, not
-        the target Node itself as it used to do.
-
-        This change should be invisibile to most normal uses of the
-        return values from Builder calls.  It will cause an error if the
-        SConscript file was performing some direct manipulation of the
-        returned Node value.  For example, an attempt to print the name
-        of a target returned by the Object() Builder:
-
-              target = Object('foo.c')
-              # OLD WAY
-              print target
-
-        Will now need to access the first element in the list returned by
-        the Object() call:
-
-              target = Object('foo.c')
-              # NEW WAY
-              print target[0]
-
-        This change was introduced to make the data type returned by Builder
-        calls consistent (always a list), regardless of platform or number
-        of returned targets.
-
-    --  DEFAULT SConsignFile() DATABASE SCHEME HAS CHANGED
-
-        The SConsignFile() function now uses an internally-supplied
-        SCons.dblite module as the default DB scheme for the .sconsign file.
-        If you are using the SConsignFile() function without an explicitly
-        specified dbm_module argument, this will cause all of your targets
-        to be recompiled the first time you use SCons 0.96.  To preserve the
-        previous behavior, specify the "anydbm" module explicitly:
-
-            import anydbm
-            SConsignFile('.sconsign_file_name', anydbm)
-
-    --  INTERNAL .sconsign FILE FORMAT HAS CHANGED
-
-        The internal format of .sconsign files has been changed.  This might
-        cause warnings about "ignoring corrupt .sconsign files" and rebuilds
-        when you use SCons 0.96 for the first time in a tree that was
-        previously built with SCons 0.95 or earlier.
-
-    --  INTERFACE CHANGE OF scan_check FUNCTION TO CUSTOM SCANNERS
-
-        The scan_check function that can be supplied to a custom Scanner now
-        must take two arguments, the Node to be checked and a construction
-        environment.  It previously only used the Node as an argument.
-
-    --  DEFAULT SCANNERS NO LONGER HEED INTERNAL Scanner.add_skey() METHOD
-
-        The internal Scanner.add_skey() method no longer works for the
-        default scanners, which now use construction variables to hold their
-        lists of suffixes.  If you had a custom Tool specification that was
-        reaching into the internals in this way to add a suffix to one of
-        the following scanner, you must now add the suffix to a construction
-        environment through which you plan to call the scanner, as follows:
-
-            CScan.add_skey('.x')       => env.Append(CPPSUFFIXES = ['.x'])
-            DScan.add_skey('.x')       => env.Append(DSUFFIXES = ['.x'])
-            FortranScan.add_skey('.x') => env.Append(FORTRANSUFFIXES = ['.x'])
-
-    --  KEYWORD ARGUMENTS TO Builder() HAVE BEEN REMOVED
-
-        The "node_factory" and "scanner" keyword arguments to the Builder()
-        function have been removed.  In their place, the separate and more
-        flexible "target_factory," "source_factory," "target_scanner" and
-        "source scanner" keywords should be used instead.
-
-    --  ALL-DIGIT FILE "EXTENSIONS" ARE NOW PART OF THE FILE BASENAME
-
-        SCons now treats file "extensions" that contain all digits (for
-        example, "file.123") as part of the file basename, for easier
-        handling of version numbers in the names of shared libraries
-        and other files.  Builders will now add their file extensions to
-        file names specified with all-digit extensions.  If you need to
-        generate a file with an all-digit extension using a Builder that
-        adds a file extension, you can preserve the previous behavior by
-        wrapping the file name in a File() call.
-
-    --  Append()/Prepend() METHODS CHANGED WHEN USING UserList OBJECTS
+  Please note the following planned, future changes:
 
-        The behavior of the env.Append() and env.Prepend() methods has
-        changed when appending a string value to a UserList, or vice versa.
-        They now behave like normal Python addition of a string to
-        a UserList.  Given an initialization and an env.Append() call like:
+    --  THE Options OBJECT AND RELATED FUNCTIONS WILL BE DEPRECATED
+
+        The Options object is being replaced by a new Variables
+        object, which uses a new Variables.AddVariable() method
+        where the previous interface used Options.AddOptions().
+
+        Similarly, the following utility functions are being replaced
+        by the following similarly-named functions:
+
+                BoolOption()            BoolVariable()
+                EnumOption()            EnumVariable()
+                ListOption()            ListVariable()
+                PackageOption()         PackageVariable()
+                PathOption()            PathVariable()
+
+        And also related, the options= keyword argument when creating
+        construction environments with the Environment() functions is
+        being replaced with a variables= keyword argument.
+
+        In some future release a deprecation warning will be added to
+        existing uses of the Options object, its methods, the above
+        utility functions, and the options= keyword argument of the
+        Environment() function.  At some point after the deprecation
+        warning is added, the Options object, related functions and
+        options= keyword argument will be removed entirely.
+
+        You can prepare for this by changing all your uses of the Options
+        object and related functions to the Variables object and the new
+        function names, and changing any uses of the options= keyword
+        argument to variables=.
+
+        NOTE:  CONVERTING TO USING THE NEW Variables OBJECT OR THE
+        RELATED *Variable() FUNCTIONS, OR USING THE NEW variable=
+        KEYWORD ARGUMENT, IS NOT BACKWARDS COMPATIBLE TO VERSIONS OF
+        SCons BEFORE 0.98.  YOUR SConscript FILES WILL NOT WORK ON
+        EARLIER VERSIONS OF SCons AFTER MAKING THIS CHANGE.
+
+        If you change SConscript files in software that you make available
+        for download or otherwise distribute, other users may try to
+        build your software with an earlier version of SCons that does
+        not have the Variables object or related *Variable() functions.
+        We recommend preparing for this in one of two ways:
 
-            env = Environment(VAR1=UserList(['foo']), VAR2='foo')
-            env.Append(VAR1='bar', VAR2=UserList(['bar'])
+            --  Make your SConscript files backwards-compatible by
+                modifying your calls with  Python try:-except: blocks
+                as follows:
 
-        The resulting values of $VAR1 and $VAR2 will now be ['foo', 'b',
-        'a', 'r'] and ['f', 'o', 'o', 'bar'], respectively.  This is because
-        Python UserList objects treat strings as sequences of letters when
-        adding them to the value of the UserList.
+                    try:
+                        vars = Variables('custom.py', ARGUMENTS)
+                        vars.AddVariables(
+                            BoolVariable('WARNINGS', 'cmopile with -Wall', 1),
+                            EnumVariable('DEBUG', 'debug version', 'no'
+                                       allowed_values=('yes', 'no', 'full'),
+                                       map={}, ignorecase=0),
+                            ListVariable('SHAREDLIBS',
+                                         'libraries to build shared',
+                                         'all',
+                                         names = list_of_libs),
+                            PackageVariable('X11',
+                                            'use X11 from here',
+                                            '/usr/bin/X11'),
+                            PathVariable('QTDIR', 'root of Qt', qtdir),
+                        )
+                    except NameError:
+                        vars = Options('custom.py', ARGUMENTS)
+                        vars.AddOptions(
+                            BoolOption('WARNINGS', 'cmopile with -Wall', 1),
+                            EnumOption('DEBUG', 'debug version', 'no'
+                                       allowed_values=('yes', 'no', 'full'),
+                                       map={}, ignorecase=0),
+                            ListOption('SHAREDLIBS',
+                                       'libraries to build shared',
+                                       'all',
+                                       names = list_of_libs),
+                            PackageOption('X11',
+                                          'use X11 from here',
+                                          '/usr/bin/X11'),
+                            PathOption('QTDIR', 'root of Qt', qtdir),
+                        )
+
+                Additionally, you can check for availability of the new
+                variables= keyword argument as follows:
 
-        The old behavior of yielding $VAR1 and $VAR2 values of ['foo',
-        'bar'] when either variable is a UserList object now requires that
-        the string variables be enclosed in a list:
+                    try:
+                        env = Environment(variables=vars)
+                    except TypeError:
+                        env = Environment(options=vars)
 
-            env = Environment(VAR1=UserList(['foo']), VAR2=['foo'])
-            env.Append(VAR1='bar', VAR2=UserList(['bar']))
+                (Note that we plan to maintain the existing Options object
+                name for some time, to ensure backwards compatibility,
+                so in practice it may be easier to just continue to use
+                the old name until you're reasonably sure you won't have
+                people trying to build your software with versions of
+                SCons earlier than 0.98.1.)
 
-        Note that the SCons behavior when appending to normal lists has
-        *not* changed, and the behavior of all of the default values that
-        SCons uses to initialize all construction variables has *not*
-        changed.  This change *only* affects any cases where you explicitly
-        use UserList objects to initialize or append to a variable.
+            --  Use the EnsureSConsVersion() function to provide a
+                descriptive error message if your SConscript files
+                are executed by an earlier version of SCons:
 
-  Please note the following planned, future changes:
+                    EnsureSConsVersion(0, 98, 1)
 
     --  THE BuildDir() METHOD AND FUNCTION WILL BE DEPRECATED
 
index 093fbd95ecc125224f7bff7f5c09ec7bce1f56c4..e4e80462dbd34b291f9daa86fa07107099cb14b8 100644 (file)
@@ -3,6 +3,7 @@ SCons/Action.py
 SCons/Builder.py
 SCons/compat/__init__.py
 SCons/compat/_scons_hashlib.py
+SCons/compat/_scons_itertools.py
 SCons/compat/_scons_optparse.py
 SCons/compat/_scons_sets.py
 SCons/compat/_scons_sets15.py
@@ -27,12 +28,7 @@ SCons/Node/__init__.py
 SCons/Node/Alias.py
 SCons/Node/FS.py
 SCons/Node/Python.py
-SCons/Options/__init__.py
-SCons/Options/BoolOption.py
-SCons/Options/EnumOption.py
-SCons/Options/ListOption.py
-SCons/Options/PackageOption.py
-SCons/Options/PathOption.py
+SCons/Options.py
 SCons/PathList.py
 SCons/Platform/__init__.py
 SCons/Platform/aix.py
@@ -162,4 +158,10 @@ SCons/Tool/wix.py
 SCons/Tool/yacc.py
 SCons/Tool/zip.py
 SCons/Util.py
+SCons/Variables/__init__.py
+SCons/Variables/BoolVariable.py
+SCons/Variables/EnumVariable.py
+SCons/Variables/ListVariable.py
+SCons/Variables/PackageVariable.py
+SCons/Variables/PathVariable.py
 SCons/Warnings.py
index 367174c3bb08df8a966a457f0655cb9ac14ea709..d2211b7eb5a121c61e7fc13cea949b36f5865a2e 100644 (file)
@@ -865,8 +865,11 @@ class FunctionAction(_ActionAction):
         except AttributeError:
             contents = self.funccontents
 
-        return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
-                                                    self.varlist)))
+        result = [contents]
+        for v in self.varlist:
+            result.append(env.subst('${'+v+'}'))
+
+        return string.join(result, '')
 
     def get_implicit_deps(self, target, source, env):
         return []
index 74664b0df8fb2250e539dddd89950919745d132b..f25f2321961584a204881ab440a78be3077d7111 100644 (file)
@@ -1006,10 +1006,13 @@ class CommandActionTestCase(unittest.TestCase):
             expect_nonexecutable = 1
         elif sys.platform == 'cygwin':
             expect_nonexistent = 127
-            expect_nonexecutable = 127
+            # Newer cygwin seems to return 126 for following
+            expect_nonexecutable_file = 126
+            expect_nonexecutable_dir  = 127
         else:
             expect_nonexistent = 127
-            expect_nonexecutable = 126
+            expect_nonexecutable_file = 126
+            expect_nonexecutable_dir  = 126
 
         # Test that a nonexistent command returns 127
         act = SCons.Action.CommandAction(python + "_no_such_command_")
@@ -1020,12 +1023,12 @@ class CommandActionTestCase(unittest.TestCase):
         dir, tail = os.path.split(python)
         act = SCons.Action.CommandAction(dir)
         r = act([], [], env.Clone(out = outfile))
-        assert r.status == expect_nonexecutable, r.status
+        assert r.status == expect_nonexecutable_file, r.status
 
         # Test that trying to execute a non-executable file returns 126
         act = SCons.Action.CommandAction(outfile)
         r = act([], [], env.Clone(out = outfile))
-        assert r.status == expect_nonexecutable, r.status
+        assert r.status == expect_nonexecutable_dir, r.status
 
         act = SCons.Action.CommandAction('%s %s 1' % (_python_, exit_py))
         r = act([], [], env)
index c54eae2c287b0d013bfcaeae09071f841eada28a..095f5a25acc6ab49d45946dfd383f0c7c777d589 100644 (file)
@@ -115,39 +115,57 @@ else:
             res = resource.getrusage(resource.RUSAGE_SELF)
             return res[4]
 
-
-
-caller_dicts = {}
-
-def caller(*backlist):
+# returns caller's stack
+def caller_stack(*backlist):
     import traceback
     if not backlist:
         backlist = [0]
     result = []
     for back in backlist:
         tb = traceback.extract_stack(limit=3+back)
-        key = tb[1][:3]
-        try:
-            entry = caller_dicts[key]
-        except KeyError:
-            entry = caller_dicts[key] = {}
         key = tb[0][:3]
-        entry[key] = entry.get(key, 0) + 1
         result.append('%s:%d(%s)' % func_shorten(key))
     return result
 
+caller_bases = {}
+caller_dicts = {}
+
+# trace a caller's stack
+def caller_trace(back=0):
+    import traceback
+    tb = traceback.extract_stack(limit=3+back)
+    tb.reverse()
+    callee = tb[1][:3]
+    caller_bases[callee] = caller_bases.get(callee, 0) + 1
+    for caller in tb[2:]:
+        caller = callee + caller[:3]
+        try:
+            entry = caller_dicts[callee]
+        except KeyError:
+            caller_dicts[callee] = entry = {}
+        entry[caller] = entry.get(caller, 0) + 1
+        callee = caller
+
+# print a single caller and its callers, if any
+def _dump_one_caller(key, file, level=0):
+    l = []
+    for c,v in caller_dicts[key].items():
+        l.append((-v,c))
+    l.sort()
+    leader = '      '*level
+    for v,c in l:
+        file.write("%s  %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:])))
+        if caller_dicts.has_key(c):
+            _dump_one_caller(c, file, level+1)
+
+# print each call tree
 def dump_caller_counts(file=sys.stdout):
-    keys = caller_dicts.keys()
+    keys = caller_bases.keys()
     keys.sort()
     for k in keys:
-        file.write("Callers of %s:%d(%s):\n" % func_shorten(k))
-        counts = caller_dicts[k]
-        callers = counts.keys()
-        callers.sort()
-        for c in callers:
-            #file.write("    counts[%s] = %s\n" % (c, counts[c]))
-            t = ((counts[c],) + func_shorten(c))
-            file.write("    %6d %s:%d(%s)\n" % t)
+        file.write("Callers of %s:%d(%s), %d calls:\n"
+                    % (func_shorten(k) + (caller_bases[k],)))
+        _dump_one_caller(k, file)
 
 shorten_list = [
     ( '/scons/SCons/',          1),
@@ -168,10 +186,8 @@ def func_shorten(func_tuple):
         if i >= 0:
             if t[1]:
                 i = i + len(t[0])
-            f = f[i:]
-            break
-    return (f,)+func_tuple[1:]
-
+            return (f[i:],)+func_tuple[1:]
+    return func_tuple
 
 
 TraceFP = {}
index ce9803430a8cb9154e4b2479a656cf7d14e73339..82744e082145b65d5c95eec3c5f1b5498b439273 100644 (file)
@@ -857,7 +857,7 @@ class Base(SubstitutionEnvironment):
                  platform=None,
                  tools=None,
                  toolpath=None,
-                 options=None,
+                 variables=None,
                  parse_flags = None,
                  **kw):
         """
@@ -901,14 +901,19 @@ class Base(SubstitutionEnvironment):
         self._dict['PLATFORM'] = str(platform)
         platform(self)
 
-        # Apply the passed-in variables and customizable options to the
+        # Apply the passed-in and customizable variables to the
         # environment before calling the tools, because they may use
         # some of them during initialization.
+        if kw.has_key('options'):
+            # Backwards compatibility:  they may stll be using the
+            # old "options" keyword.
+            variables = kw['options']
+            del kw['options']
         apply(self.Replace, (), kw)
         keys = kw.keys()
-        if options:
-            keys = keys + options.keys()
-            options.Update(self)
+        if variables:
+            keys = keys + variables.keys()
+            variables.Update(self)
 
         save = {}
         for k in keys:
@@ -927,7 +932,7 @@ class Base(SubstitutionEnvironment):
                 tools = ['default']
         apply_tools(self, tools, toolpath)
 
-        # Now restore the passed-in variables and customized options
+        # Now restore the passed-in and customized variables
         # to the environment, since the values the user set explicitly
         # should override any values set by the tools.
         for key, val in save.items():
index 4f48da912790eb36adc2996ff0b9498d493334e4..8ebbbbca6c55e46a4e6bbb5e8c01b3ca44a9251a 100644 (file)
@@ -844,8 +844,8 @@ class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
         assert not env1.has_key('__env__')
         assert not env2.has_key('__env__')
 
-    def test_options(self):
-        """Test that options only get applied once."""
+    def test_variables(self):
+        """Test that variables only get applied once."""
         class FakeOptions:
             def __init__(self, key, val):
                 self.calls = 0
@@ -858,7 +858,7 @@ class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
                 self.calls = self.calls + 1
 
         o = FakeOptions('AAA', 'fake_opt')
-        env = Environment(options=o, AAA='keyword_arg')
+        env = Environment(variables=o, AAA='keyword_arg')
         assert o.calls == 1, o.calls
         assert env['AAA'] == 'fake_opt', env['AAA']
 
index 72220424ae3ee9994ccccf5860f94e96c228c073..a9417bca26b848a8daa6eac156f69994b30f0e1b 100644 (file)
@@ -160,6 +160,16 @@ class Executor:
             self.sources_need_sorting = False
         return self.sources
 
+    def prepare(self):
+        """
+        Preparatory checks for whether this Executor can go ahead
+        and (try to) build its targets.
+        """
+        for s in self.get_sources():
+            if s.missing():
+                msg = "Source `%s' not found, needed by target `%s'."
+                raise SCons.Errors.StopError, msg % (s, self.targets[0])
+
     def add_pre_action(self, action):
         self.pre_actions.append(action)
 
@@ -221,40 +231,35 @@ class Executor:
         This essentially short-circuits an N*M scan of the sources for
         each individual target, which is a hell of a lot more efficient.
         """
-        map(lambda N: N.disambiguate(), node_list)
-
         env = self.get_build_env()
-        select_specific_scanner = lambda t: (t[0], t[1].select(t[0]))
-        remove_null_scanners = lambda t: not t[1] is None
-        add_scanner_path = lambda t, s=self: \
-                                  (t[0], t[1], s.get_build_scanner_path(t[1]))
+
+        deps = []
         if scanner:
-            scanner_list = map(lambda n, s=scanner: (n, s), node_list)
+            for node in node_list:
+                node.disambiguate()
+                scanner = scanner.select(node)
+                if not scanner:
+                    continue
+                path = self.get_build_scanner_path(scanner)
+                deps.extend(node.get_implicit_deps(env, scanner, path))
         else:
             kw = self.get_kw()
-            get_initial_scanners = lambda n, e=env, kw=kw: \
-                                          (n, n.get_env_scanner(e, kw))
-            scanner_list = map(get_initial_scanners, node_list)
-            scanner_list = filter(remove_null_scanners, scanner_list)
-
-        scanner_list = map(select_specific_scanner, scanner_list)
-        scanner_list = filter(remove_null_scanners, scanner_list)
-        scanner_path_list = map(add_scanner_path, scanner_list)
-
-        deps = []
-        for node, scanner, path in scanner_path_list:
-            deps.extend(node.get_implicit_deps(env, scanner, path))
+            for node in node_list:
+                node.disambiguate()
+                scanner = node.get_env_scanner(env, kw)
+                if not scanner:
+                    continue
+                scanner = scanner.select(node)
+                if not scanner:
+                    continue
+                path = self.get_build_scanner_path(scanner)
+                deps.extend(node.get_implicit_deps(env, scanner, path))
 
         deps.extend(self.get_implicit_deps())
 
         for tgt in self.targets:
             tgt.add_to_implicit(deps)
 
-    def get_missing_sources(self):
-        """
-        """
-        return filter(lambda s: s.missing(), self.get_sources())
-
     def _get_unignored_sources_key(self, ignore=()):
         return tuple(ignore)
 
@@ -345,3 +350,5 @@ class Null(_Executor):
         return None
     def cleanup(self):
         pass
+    def prepare(self):
+        pass
index 368e034b673a43c0c4e2c2542522ca886d276da1..12c80b4cedf51f12834afcd0f688355c39c5ad02 100644 (file)
@@ -285,6 +285,24 @@ class ExecutorTestCase(unittest.TestCase):
         x.get_sources()
         assert x.sources == ['s1', 's2', 's3', 's4'], x.sources
 
+    def test_prepare(self):
+        """Test the Executor's prepare() method"""
+        env = MyEnvironment()
+        t1 = MyNode('t1')
+        s1 = MyNode('s1')
+        s2 = MyNode('s2')
+        s3 = MyNode('s3')
+        x = SCons.Executor.Executor('b', env, [{}], [t1], [s1, s2, s3])
+
+        s2.missing_val = True
+
+        try:
+            r = x.prepare()
+        except SCons.Errors.StopError, e:
+            assert str(e) == "Source `s2' not found, needed by target `t1'.", e
+        else:
+            raise AssertionError, "did not catch expected StopError: %s" % r
+
     def test_add_pre_action(self):
         """Test adding pre-actions to an Executor"""
         x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
@@ -403,16 +421,6 @@ class ExecutorTestCase(unittest.TestCase):
         assert t1.implicit == ['scanner-s1', 'scanner-s2'], t1.implicit
         assert t2.implicit == ['scanner-s1', 'scanner-s2'], t2.implicit
 
-    def test_get_missing_sources(self):
-        """Test the ability to check if any sources are missing"""
-        env = MyEnvironment()
-        targets = [MyNode('t')]
-        sources = [MyNode('s1'), MyNode('s2')]
-        x = SCons.Executor.Executor('b', env, [{}], targets, sources)
-        sources[0].missing_val = 1
-        missing = x.get_missing_sources()
-        assert missing == [sources[0]], missing
-
     def test_get_unignored_sources(self):
         """Test fetching the unignored source list"""
         env = MyEnvironment()
index 7b514092269b4d4dc57d4317f03ecf26c21175e4..d93952c2c4f838c1117106de51787c7927499165 100644 (file)
@@ -33,6 +33,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.compat
 
+import os
+import signal
+
 
 # The default stack size (in kilobytes) of the threads used to execute
 # jobs in parallel.
@@ -44,6 +47,7 @@ import SCons.compat
 
 default_stack_size = 256
 
+interrupt_msg = 'Build interrupted.'
 
 class Jobs:
     """An instance of this class initializes N jobs, and provides
@@ -80,21 +84,73 @@ class Jobs:
             self.job = Serial(taskmaster)
             self.num_jobs = 1
 
-    def run(self):
-        """run the job"""
+        self.job.interrupted = False
+
+    def run(self, postfunc=lambda: None):
+        """Run the jobs.
+
+        postfunc() will be invoked after the jobs has run. It will be
+        invoked even if the jobs are interrupted by a keyboard
+        interrupt (well, in fact by a signal such as either SIGINT,
+        SIGTERM or SIGHUP). The execution of postfunc() is protected
+        against keyboard interrupts and is guaranteed to run to
+        completion."""
+        self._setup_sig_handler()
         try:
             self.job.start()
-        except KeyboardInterrupt:
-            # mask any further keyboard interrupts so that scons
-            # can shutdown cleanly:
-            # (this only masks the keyboard interrupt for Python,
-            #  child processes can still get the keyboard interrupt)
-            import signal
-            signal.signal(signal.SIGINT, signal.SIG_IGN)
-            raise
-
-    def cleanup(self):
-        self.job.cleanup()
+        finally:
+            postfunc()
+            self._reset_sig_handler()
+
+    def were_interrupted(self):
+        """Returns whether the jobs were interrupted by a signal."""
+        return self.job.interrupted
+
+    def _setup_sig_handler(self):
+        """Setup an interrupt handler so that SCons can shutdown cleanly in
+        various conditions:
+
+          a) SIGINT: Keyboard interrupt
+          b) SIGTERM: kill or system shutdown
+          c) SIGHUP: Controlling shell exiting
+
+        We handle all of these cases by stopping the taskmaster. It
+        turns out that it very difficult to stop the build process
+        by throwing asynchronously an exception such as
+        KeyboardInterrupt. For example, the python Condition
+        variables (threading.Condition) and Queue's do not seem to
+        asynchronous-exception-safe. It would require adding a whole
+        bunch of try/finally block and except KeyboardInterrupt all
+        over the place.
+
+        Note also that we have to be careful to handle the case when
+        SCons forks before executing another process. In that case, we
+        want the child to exit immediately.
+        """
+        def handler(signum, stack, parentpid=os.getpid()):
+            if os.getpid() == parentpid:
+                self.job.taskmaster.stop()
+                self.job.interrupted = True
+            else:
+                os._exit(2)
+
+        self.old_sigint  = signal.signal(signal.SIGINT, handler)
+        self.old_sigterm = signal.signal(signal.SIGTERM, handler)
+        try:
+            self.old_sighup = signal.signal(signal.SIGHUP, handler)
+        except AttributeError:
+            pass
+
+    def _reset_sig_handler(self):
+        """Restore the signal handlers to their previous state (before the
+         call to _setup_sig_handler()."""
+
+        signal.signal(signal.SIGINT, self.old_sigint)
+        signal.signal(signal.SIGTERM, self.old_sigterm)
+        try:
+            signal.signal(signal.SIGHUP, self.old_sighup)
+        except AttributeError:
+            pass
 
 class Serial:
     """This class is used to execute tasks in series, and is more efficient
@@ -129,11 +185,18 @@ class Serial:
 
             try:
                 task.prepare()
-                task.execute()
-            except KeyboardInterrupt:
-                raise
+                if task.needs_execute():
+                    task.execute()
             except:
-                task.exception_set()
+                if self.interrupted:
+                    try:
+                        raise SCons.Errors.BuildError(
+                            task.targets[0], errstr=interrupt_msg)
+                    except:
+                        task.exception_set()
+                else:
+                    task.exception_set()
+
                 # Let the failed() callback function arrange for the
                 # build to stop if that's appropriate.
                 task.failed()
@@ -141,9 +204,8 @@ class Serial:
                 task.executed()
 
             task.postprocess()
+        self.taskmaster.cleanup()
 
-    def cleanup(self):
-        pass
 
 # Trap import failure so that everything in the Job module but the
 # Parallel class (and its dependent classes) will work if the interpreter
@@ -178,9 +240,6 @@ else:
 
                 try:
                     task.execute()
-                except KeyboardInterrupt:
-                    # be explicit here for test/interrupts.py
-                    ok = False
                 except:
                     task.exception_set()
                     ok = False
@@ -226,16 +285,16 @@ else:
             if 'prev_size' in locals().keys():
                 threading.stack_size(prev_size)
 
-        def put(self, obj):
+        def put(self, task):
             """Put task into request queue."""
-            self.requestQueue.put(obj)
+            self.requestQueue.put(task)
 
-        def get(self, block = True):
+        def get(self):
             """Remove and return a result tuple from the results queue."""
-            return self.resultsQueue.get(block)
+            return self.resultsQueue.get()
 
-        def preparation_failed(self, obj):
-            self.resultsQueue.put((obj, False))
+        def preparation_failed(self, task):
+            self.resultsQueue.put((task, False))
 
         def cleanup(self):
             """
@@ -309,22 +368,21 @@ else:
                     if task is None:
                         break
 
-                    # prepare task for execution
                     try:
+                        # prepare task for execution
                         task.prepare()
-                    except KeyboardInterrupt:
-                        raise
                     except:
-                        # Let the failed() callback function arrange
-                        # for the build to stop if that's appropriate.
                         task.exception_set()
-                        self.tp.preparation_failed(task)
-                        jobs = jobs + 1
-                        continue
-
-                    # dispatch task
-                    self.tp.put(task)
-                    jobs = jobs + 1
+                        task.failed()
+                        task.postprocess()
+                    else:
+                        if task.needs_execute():
+                            # dispatch task
+                            self.tp.put(task)
+                            jobs = jobs + 1
+                        else:
+                            task.executed()
+                            task.postprocess()
 
                 if not task and not jobs: break
 
@@ -332,11 +390,20 @@ else:
                 # back and put the next batch of tasks on the queue.
                 while 1:
                     task, ok = self.tp.get()
-
                     jobs = jobs - 1
+
                     if ok:
                         task.executed()
                     else:
+                        if self.interrupted:
+                            try:
+                                raise SCons.Errors.BuildError(
+                                    task.targets[0], errstr=interrupt_msg)
+                            except:
+                                task.exception_set()
+
+                        # Let the failed() callback function arrange
+                        # for the build to stop if that's appropriate.
                         task.failed()
 
                     task.postprocess()
@@ -344,5 +411,5 @@ else:
                     if self.tp.resultsQueue.empty():
                         break
 
-        def cleanup(self):
             self.tp.cleanup()
+            self.taskmaster.cleanup()
index c4325812f1e0718eec994c0c9533df6eef6b8415..b2a195ead4b56db5042b9a0aaff0ba04ac9e0a2b 100644 (file)
@@ -68,6 +68,9 @@ class Task:
     def _do_something(self):
         pass
 
+    def needs_execute(self):
+        return True
+
     def execute(self):
         self.taskmaster.test_case.failUnless(self.was_prepared,
                                   "the task wasn't prepared")
@@ -120,6 +123,9 @@ class ExceptionTask:
     def prepare(self):
         self.was_prepared = 1
 
+    def needs_execute(self):
+        return True
+
     def execute(self):
         raise Exception
 
@@ -200,6 +206,9 @@ class Taskmaster:
     def exception_set(self):
         pass
 
+    def cleanup(self):
+        pass
+
 SaveThreadPool = None
 ThreadPoolCallList = []
 
index f73161fde071b45bebaae7254187727774ea63e7..6ff3833464965ffd8aab47852d71ab7d5e914981 100644 (file)
@@ -36,6 +36,7 @@ that can be used by scripts or modules looking for the canonical default.
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import fnmatch
+from itertools import izip
 import os
 import os.path
 import re
@@ -1895,7 +1896,7 @@ class Dir(Base):
                         rep_nodes = map(dir.Entry, disk_names)
                         #rep_nodes = [ n.disambiguate() for n in rep_nodes ]
                         rep_nodes = map(lambda n: n.disambiguate(), rep_nodes)
-                        for node, name in zip(rep_nodes, disk_names):
+                        for node, name in izip(rep_nodes, disk_names):
                             n = self.Entry(name)
                             if n.__class__ != node.__class__:
                                 n.__class__ = node.__class__
@@ -2110,7 +2111,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
                 pass
             else:
                 nodes = []
-                for s, ni in zip(strings, nodeinfos):
+                for s, ni in izip(strings, nodeinfos):
                     if not isinstance(s, SCons.Node.Node):
                         s = ni.str_to_node(s)
                     nodes.append(s)
@@ -2119,7 +2120,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
         result = []
         bkids = self.bsources + self.bdepends + self.bimplicit
         bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs
-        for bkid, bkidsig in zip(bkids, bkidsigs):
+        for bkid, bkidsig in izip(bkids, bkidsigs):
             result.append(str(bkid) + ': ' +
                           string.join(bkidsig.format(names=names), ' '))
         result.append('%s [%s]' % (self.bactsig, self.bact))
index 8e9a3f8de7f6a0df1afbdc1adf1f310905036d69..8bceaf6573b2c00a45fa0bb365c3c8eaf669fa8d 100644 (file)
@@ -714,23 +714,23 @@ class NodeTestCase(unittest.TestCase):
         n1 = SCons.Node.Node()
         n1.builder_set(Builder())
         node.implicit = []
-        node.implicit_dict = {}
-        node._add_child(node.implicit, node.implicit_dict, [n1])
+        node.implicit_set = set()
+        node._add_child(node.implicit, node.implicit_set, [n1])
 
         node.prepare()  # should not throw an exception
 
         n2 = SCons.Node.Node()
         n2.linked = 1
         node.implicit = []
-        node.implicit_dict = {}
-        node._add_child(node.implicit, node.implicit_dict, [n2])
+        node.implicit_set = set()
+        node._add_child(node.implicit, node.implicit_set, [n2])
 
         node.prepare()  # should not throw an exception
 
         n3 = SCons.Node.Node()
         node.implicit = []
-        node.implicit_dict = {}
-        node._add_child(node.implicit, node.implicit_dict, [n3])
+        node.implicit_set = set()
+        node._add_child(node.implicit, node.implicit_set, [n3])
 
         node.prepare()  # should not throw an exception
 
@@ -739,8 +739,8 @@ class NodeTestCase(unittest.TestCase):
                 return None
         n4 = MyNode()
         node.implicit = []
-        node.implicit_dict = {}
-        node._add_child(node.implicit, node.implicit_dict, [n4])
+        node.implicit_set = set()
+        node._add_child(node.implicit, node.implicit_set, [n4])
         exc_caught = 0
         try:
             node.prepare()
@@ -810,7 +810,7 @@ class NodeTestCase(unittest.TestCase):
             pass
         else:
             raise "did not catch expected exception"
-        assert node.sources == [zero, one, two, three, four]
+        assert node.sources == [zero, one, two, three, four], node.sources
 
     def test_add_ignore(self):
         """Test adding files whose dependencies should be ignored.
@@ -1033,9 +1033,9 @@ class NodeTestCase(unittest.TestCase):
         node.add_source([n1, n2, n3])
         node.add_dependency([n4, n5, n6])
         node.implicit = []
-        node.implicit_dict = {}
-        node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
-        node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
+        node.implicit_set = set()
+        node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
+        node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
         node.add_ignore([n2, n5, n8, n11])
 
         kids = node.children()
@@ -1064,9 +1064,9 @@ class NodeTestCase(unittest.TestCase):
         node.add_source([n1, n2, n3])
         node.add_dependency([n4, n5, n6])
         node.implicit = []
-        node.implicit_dict = {}
-        node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
-        node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
+        node.implicit_set = set()
+        node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
+        node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
         node.add_ignore([n2, n5, n8, n11])
 
         kids = node.all_children()
@@ -1217,7 +1217,6 @@ class NodeTestCase(unittest.TestCase):
         n.clear()
 
         assert n.includes is None, n.includes
-        assert n.found_includes == {}, n.found_includes
         assert x.cleaned_up
 
     def test_get_subst_proxy(self):
@@ -1241,32 +1240,22 @@ class NodeTestCase(unittest.TestCase):
     def test_postprocess(self):
         """Test calling the base Node postprocess() method"""
         n = SCons.Node.Node()
-        n.waiting_parents = {'foo':1, 'bar':1}
+        n.waiting_parents = set( ['foo','bar'] )
 
         n.postprocess()
-        assert n.waiting_parents == {}, n.waiting_parents
+        assert n.waiting_parents == set(), n.waiting_parents
 
     def test_add_to_waiting_parents(self):
         """Test the add_to_waiting_parents() method"""
         n1 = SCons.Node.Node()
         n2 = SCons.Node.Node()
-        assert n1.waiting_parents == {}, n1.waiting_parents
+        assert n1.waiting_parents == set(), n1.waiting_parents
         r = n1.add_to_waiting_parents(n2)
         assert r == 1, r
-        assert n1.waiting_parents == {n2:1}, n1.waiting_parents
+        assert n1.waiting_parents == set((n2,)), n1.waiting_parents
         r = n1.add_to_waiting_parents(n2)
         assert r == 0, r
 
-    def test_call_for_all_waiting_parents(self):
-        """Test the call_for_all_waiting_parents() method"""
-        n1 = SCons.Node.Node()
-        n2 = SCons.Node.Node()
-        n1.add_to_waiting_parents(n2)
-        result = []
-        def func(node, result=result):
-            result.append(node)
-        n1.call_for_all_waiting_parents(func)
-        assert result == [n1, n2], result
 
 class NodeListTestCase(unittest.TestCase):
     def test___str__(self):
index 2e136f0088334b3872c4edef0682cd2f8a3faabc..c765ee925d028e80d40e8d404ad31a936fc74c4a 100644 (file)
@@ -47,6 +47,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 import SCons.compat
 
 import copy
+from itertools import chain, izip
 import string
 import UserList
 
@@ -75,7 +76,7 @@ executed = 4
 failed = 5
 
 StateString = {
-    0 : "0",
+    0 : "no_state",
     1 : "pending",
     2 : "executing",
     3 : "up_to_date",
@@ -202,15 +203,16 @@ class Node:
         # a class.  (Of course, we could always still do that in the
         # future if we had a good reason to...).
         self.sources = []       # source files used to build node
-        self.sources_dict = {}
+        self.sources_set = set()
+        self._specific_sources = False
         self.depends = []       # explicit dependencies (from Depends)
-        self.depends_dict = {}
+        self.depends_set = set()
         self.ignore = []        # dependencies to ignore
-        self.ignore_dict = {}
+        self.ignore_set = set()
         self.prerequisites = SCons.Util.UniqueList()
         self.implicit = None    # implicit (scanned) dependencies (None means not scanned yet)
-        self.waiting_parents = {}
-        self.waiting_s_e = {}
+        self.waiting_parents = set()
+        self.waiting_s_e = set()
         self.ref_count = 0
         self.wkids = None       # Kids yet to walk, when it's an array
 
@@ -220,7 +222,6 @@ class Node:
         self.noclean = 0
         self.nocache = 0
         self.always_build = None
-        self.found_includes = {}
         self.includes = None
         self.attributes = self.Attrs() # Generic place to stick information about the Node.
         self.side_effect = 0 # true iff this node is a side effect
@@ -330,24 +331,29 @@ class Node:
         is out-of-date and must be rebuilt, but before actually calling
         the method to build the Node.
 
-        This default implemenation checks that all children either exist
-        or are derived, and initializes the BuildInfo structure that
-        will hold the information about how this node is, uh, built.
+        This default implementation checks that explicit or implicit
+        dependencies either exist or are derived, and initializes the
+        BuildInfo structure that will hold the information about how
+        this node is, uh, built.
+
+        (The existence of source files is checked separately by the
+        Executor, which aggregates checks for all of the targets built
+        by a specific action.)
 
         Overriding this method allows for for a Node subclass to remove
         the underlying file from the file system.  Note that subclass
         methods should call this base class method to get the child
         check and the BuildInfo structure.
         """
-        l = self.depends
+        for d in self.depends:
+            if d.missing():
+                msg = "Explicit dependency `%s' not found, needed by target `%s'."
+                raise SCons.Errors.StopError, msg % (d, self)
         if not self.implicit is None:
-            l = l + self.implicit
-        missing_sources = self.get_executor().get_missing_sources() \
-                          + filter(lambda c: c.missing(), l)
-        if missing_sources:
-            desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self)
-            raise SCons.Errors.StopError, desc
-
+            for i in self.implicit:
+                if i.missing():
+                    msg = "Implicit dependency `%s' not found, needed by target `%s'."
+                    raise SCons.Errors.StopError, msg % (i, self)
         self.binfo = self.get_binfo()
 
     def build(self, **kw):
@@ -373,7 +379,7 @@ class Node:
 
         # Clear the implicit dependency caches of any Nodes
         # waiting for this Node to be built.
-        for parent in self.waiting_parents.keys():
+        for parent in self.waiting_parents:
             parent.implicit = None
 
         self.clear()
@@ -398,7 +404,7 @@ class Node:
     #
 
     def add_to_waiting_s_e(self, node):
-        self.waiting_s_e[node] = 1
+        self.waiting_s_e.add(node)
 
     def add_to_waiting_parents(self, node):
         """
@@ -409,23 +415,16 @@ class Node:
         True and False instead...)
         """
         wp = self.waiting_parents
-        if wp.has_key(node):
-            result = 0
-        else:
-            result = 1
-        wp[node] = 1
-        return result
-
-    def call_for_all_waiting_parents(self, func):
-        func(self)
-        for parent in self.waiting_parents.keys():
-            parent.call_for_all_waiting_parents(func)
+        if node in wp:
+            return 0
+        wp.add(node)
+        return 1
 
     def postprocess(self):
         """Clean up anything we don't need to hang onto after we've
         been built."""
         self.executor_cleanup()
-        self.waiting_parents = {}
+        self.waiting_parents = set()
 
     def clear(self):
         """Completely clear a Node of all its cached state (so that it
@@ -444,7 +443,6 @@ class Node:
         except AttributeError:
             pass
         self.includes = None
-        self.found_includes = {}
 
     def clear_memoized_values(self):
         self._memo = {}
@@ -592,9 +590,9 @@ class Node:
     def add_to_implicit(self, deps):
         if not hasattr(self, 'implicit') or self.implicit is None:
             self.implicit = []
-            self.implicit_dict = {}
+            self.implicit_set = set()
             self._children_reset()
-        self._add_child(self.implicit, self.implicit_dict, deps)
+        self._add_child(self.implicit, self.implicit_set, deps)
 
     def scan(self):
         """Scan this node's dependents for implicit dependencies."""
@@ -604,7 +602,7 @@ class Node:
         if not self.implicit is None:
             return
         self.implicit = []
-        self.implicit_dict = {}
+        self.implicit_set = set()
         self._children_reset()
         if not self.has_builder():
             return
@@ -633,7 +631,7 @@ class Node:
                 # one of this node's sources has changed,
                 # so we must recalculate the implicit deps:
                 self.implicit = []
-                self.implicit_dict = {}
+                self.implicit_set = set()
 
         # Have the executor scan the sources.
         executor.scan_sources(self.builder.source_scanner)
@@ -706,33 +704,44 @@ class Node:
         self.binfo = binfo
 
         executor = self.get_executor()
-
-        sources = executor.get_unignored_sources(self.ignore)
-
-        depends = self.depends
-        implicit = self.implicit or []
-
-        if self.ignore:
-            depends = filter(self.do_not_ignore, depends)
-            implicit = filter(self.do_not_ignore, implicit)
-
-        def get_ninfo(node):
-            return node.get_ninfo()
-
-        sourcesigs = map(get_ninfo, sources)
-        dependsigs = map(get_ninfo, depends)
-        implicitsigs = map(get_ninfo, implicit)
+        ignore_set = self.ignore_set
 
         if self.has_builder():
             binfo.bact = str(executor)
             binfo.bactsig = SCons.Util.MD5signature(executor.get_contents())
 
-        binfo.bsources = sources
-        binfo.bdepends = depends
-        binfo.bimplicit = implicit
+        if self._specific_sources:
+            sources = []
+            for s in self.sources:
+                if s not in ignore_set:
+                    sources.append(s)
+        else:
+            sources = executor.get_unignored_sources(self.ignore)
+        seen = set()
+        bsources = []
+        bsourcesigs = []
+        for s in sources:
+            if not s in seen:
+                seen.add(s)
+                bsources.append(s)
+                bsourcesigs.append(s.get_ninfo())
+        binfo.bsources = bsources
+        binfo.bsourcesigs = bsourcesigs
 
-        binfo.bsourcesigs = sourcesigs
+        depends = self.depends
+        dependsigs = []
+        for d in depends:
+            if d not in ignore_set:
+                dependsigs.append(d.get_ninfo())
+        binfo.bdepends = depends
         binfo.bdependsigs = dependsigs
+
+        implicit = self.implicit or []
+        implicitsigs = []
+        for i in implicit:
+            if i not in ignore_set:
+                implicitsigs.append(i.get_ninfo())
+        binfo.bimplicit = implicit
         binfo.bimplicitsigs = implicitsigs
 
         return binfo
@@ -816,7 +825,7 @@ class Node:
     def add_dependency(self, depend):
         """Adds dependencies."""
         try:
-            self._add_child(self.depends, self.depends_dict, depend)
+            self._add_child(self.depends, self.depends_set, depend)
         except TypeError, e:
             e = e.args[0]
             if SCons.Util.is_List(e):
@@ -833,7 +842,7 @@ class Node:
     def add_ignore(self, depend):
         """Adds dependencies to ignore."""
         try:
-            self._add_child(self.ignore, self.ignore_dict, depend)
+            self._add_child(self.ignore, self.ignore_set, depend)
         except TypeError, e:
             e = e.args[0]
             if SCons.Util.is_List(e):
@@ -844,8 +853,10 @@ class Node:
 
     def add_source(self, source):
         """Adds sources."""
+        if self._specific_sources:
+            return
         try:
-            self._add_child(self.sources, self.sources_dict, source)
+            self._add_child(self.sources, self.sources_set, source)
         except TypeError, e:
             e = e.args[0]
             if SCons.Util.is_List(e):
@@ -854,9 +865,9 @@ class Node:
                 s = str(e)
             raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
 
-    def _add_child(self, collection, dict, child):
-        """Adds 'child' to 'collection', first checking 'dict' to see
-        if it's already present."""
+    def _add_child(self, collection, set, child):
+        """Adds 'child' to 'collection', first checking 'set' to see if it's
+        already present."""
         #if type(child) is not type([]):
         #    child = [child]
         #for c in child:
@@ -864,13 +875,17 @@ class Node:
         #        raise TypeError, c
         added = None
         for c in child:
-            if not dict.has_key(c):
+            if c not in set:
+                set.add(c)
                 collection.append(c)
-                dict[c] = 1
                 added = 1
         if added:
             self._children_reset()
 
+    def set_specific_source(self, source):
+        self.add_source(source)
+        self._specific_sources = True
+
     def add_wkid(self, wkid):
         """Add a node to the list of kids waiting to be evaluated"""
         if self.wkids != None:
@@ -882,10 +897,14 @@ class Node:
         # build info that it's cached so we can re-calculate it.
         self.executor_cleanup()
 
-    def do_not_ignore(self, node):
-        return node not in self.ignore
+    memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
+
+    def _children_get(self):
+        try:
+            return self._memo['children_get']
+        except KeyError:
+            pass
 
-    def _all_children_get(self):
         # The return list may contain duplicate Nodes, especially in
         # source trees where there are a lot of repeated #includes
         # of a tangle of .h files.  Profiling shows, however, that
@@ -903,21 +922,22 @@ class Node:
         # using dictionary keys, lose the order, and the only ordered
         # dictionary patterns I found all ended up using "not in"
         # internally anyway...)
-        if self.implicit is None:
-            return self.sources + self.depends
-        else:
-            return self.sources + self.depends + self.implicit
+        if self.ignore_set:
+            if self.implicit is None:
+                iter = chain(self.sources,self.depends)
+            else:
+                iter = chain(self.sources, self.depends, self.implicit)
 
-    memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
+            children = []
+            for i in iter:
+                if i not in self.ignore_set:
+                    children.append(i)
+        else:
+            if self.implicit is None:
+                children = self.sources + self.depends
+            else:
+                children = self.sources + self.depends + self.implicit
 
-    def _children_get(self):
-        try:
-            return self._memo['children_get']
-        except KeyError:
-            pass
-        children = self._all_children_get()
-        if self.ignore:
-            children = filter(self.do_not_ignore, children)
         self._memo['children_get'] = children
         return children
 
@@ -925,7 +945,28 @@ class Node:
         """Return a list of all the node's direct children."""
         if scan:
             self.scan()
-        return self._all_children_get()
+
+        # The return list may contain duplicate Nodes, especially in
+        # source trees where there are a lot of repeated #includes
+        # of a tangle of .h files.  Profiling shows, however, that
+        # eliminating the duplicates with a brute-force approach that
+        # preserves the order (that is, something like:
+        #
+        #       u = []
+        #       for n in list:
+        #           if n not in u:
+        #               u.append(n)"
+        #
+        # takes more cycles than just letting the underlying methods
+        # hand back cached values if a Node's information is requested
+        # multiple times.  (Other methods of removing duplicates, like
+        # using dictionary keys, lose the order, and the only ordered
+        # dictionary patterns I found all ended up using "not in"
+        # internally anyway...)
+        if self.implicit is None:
+            return self.sources + self.depends
+        else:
+            return self.sources + self.depends + self.implicit
 
     def children(self, scan=1):
         """Return a list of the node's direct children, minus those
@@ -1009,7 +1050,7 @@ class Node:
             if t: Trace(': old %s new %s' % (len(then), len(children)))
             result = True
 
-        for child, prev_ni in zip(children, then):
+        for child, prev_ni in izip(children, then):
             if child.changed_since_last_build(self, prev_ni):
                 if t: Trace(': %s changed' % child)
                 result = True
@@ -1152,8 +1193,8 @@ class Node:
         new_bkids    = new.bsources    + new.bdepends    + new.bimplicit
         new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs
 
-        osig = dict(zip(old_bkids, old_bkidsigs))
-        nsig = dict(zip(new_bkids, new_bkidsigs))
+        osig = dict(izip(old_bkids, old_bkidsigs))
+        nsig = dict(izip(new_bkids, new_bkidsigs))
 
         # The sources and dependencies we'll want to report are all stored
         # as relative paths to this target's directory, but we want to
diff --git a/src/engine/SCons/Options.py b/src/engine/SCons/Options.py
new file mode 100644 (file)
index 0000000..0f2fde1
--- /dev/null
@@ -0,0 +1,56 @@
+#
+# __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__"
+
+__doc__ = """Place-holder for the old SCons.Options module hierarchy
+
+This is for backwards compatibility.  The new equivalent is the Variables/
+class hierarchy.  These will have deprecation warnings added (some day),
+and will then be removed entirely (some day).
+"""
+
+import SCons.Variables
+
+class Options(SCons.Variables.Variables):
+
+    def AddOptions(self, *args, **kw):
+        return apply(SCons.Variables.Variables.AddVariables,
+                     (self,) + args,
+                     kw)
+
+    def UnknownOptions(self, *args, **kw):
+        return apply(SCons.Variables.Variables.UnknownVariables,
+                     (self,) + args,
+                     kw)
+
+    def FormatOptionHelpText(self, *args, **kw):
+        return apply(SCons.Variables.Variables.FormatVariableHelpText,
+                     (self,) + args,
+                     kw)
+
+BoolOption      = SCons.Variables.BoolVariable
+EnumOption      = SCons.Variables.EnumVariable
+ListOption      = SCons.Variables.ListVariable
+PackageOption   = SCons.Variables.PackageVariable
+PathOption      = SCons.Variables.PathVariable
diff --git a/src/engine/SCons/Options/.cvsignore b/src/engine/SCons/Options/.cvsignore
deleted file mode 100644 (file)
index 0d20b64..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*.pyc
index afdabe1a793be12fc27e7d4403829cb7f6831caa..2a0e478190c55fc98042d78405fe3905080386ba 100644 (file)
@@ -32,6 +32,7 @@ selection method.
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import errno
 import os
 import os.path
 import popen2
@@ -109,21 +110,25 @@ def fork_spawn(sh, escape, cmd, args, env):
 def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr):
     stdout_eof = stderr_eof = 0
     while not (stdout_eof and stderr_eof):
-        (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], [])
-        if cmd_stdout in i:
-            str = cmd_stdout.read()
-            if len(str) == 0:
-                stdout_eof = 1
-            elif stdout != None:
-                stdout.write(str)
-        if cmd_stderr in i:
-            str = cmd_stderr.read()
-            if len(str) == 0:
-                #sys.__stderr__.write( "stderr_eof=1\n" )
-                stderr_eof = 1
-            else:
-                #sys.__stderr__.write( "str(stderr) = %s\n" % str )
-                stderr.write(str)
+        try:
+            (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], [])
+            if cmd_stdout in i:
+                str = cmd_stdout.read()
+                if len(str) == 0:
+                    stdout_eof = 1
+                elif stdout != None:
+                    stdout.write(str)
+            if cmd_stderr in i:
+                str = cmd_stderr.read()
+                if len(str) == 0:
+                    #sys.__stderr__.write( "stderr_eof=1\n" )
+                    stderr_eof = 1
+                else:
+                    #sys.__stderr__.write( "str(stderr) = %s\n" % str )
+                    stderr.write(str)
+        except select.error, (_errno, _strerror):
+            if _errno != errno.EINTR:
+                raise
 
 def exec_popen3(l, env, stdout, stderr):
     proc = popen2.Popen3(string.join(l), 1)
index f7d33f8e69c83cfe0d5773d99a9800d4ace0be6a..25c89d2afea91f4d043964525cc40cda5d8ef7d3 100644 (file)
@@ -163,9 +163,10 @@ class SConfTestCase(unittest.TestCase):
                     def __init__(self, name):
                         self.name = name
                         self.state = None
-                        self.waiting_parents = {}
+                        self.waiting_parents = set()
                         self.side_effects = []
                         self.builder = None
+                        self.prerequisites = []
                     def disambiguate(self):
                         return self
                     def has_builder(self):
index d18eec1ea083ba69a8a4c9b2b6ae1f4579e50c2d..b1774ebe762eec52c1859120abf7f594a7784d45 100644 (file)
@@ -185,6 +185,16 @@ class SConsInteractiveCmd(cmd.Cmd):
         if not nodes:
             return
 
+        # Call each of the Node's alter_targets() methods, which may
+        # provide additional targets that ended up as part of the build
+        # (the canonical example being a VariantDir() when we're building
+        # from a source directory) and which we therefore need their
+        # state cleared, too.
+        x = []
+        for n in nodes:
+            x.extend(n.alter_targets()[0])
+        nodes.extend(x)
+
         # Clean up so that we can perform the next build correctly.
         #
         # We do this by walking over all the children of the targets,
index 80b9032eeb82c33f4f90947c14165b1f43780d27..75d01176985f5e9dd0f04caf3fa91e251204f375 100644 (file)
@@ -168,28 +168,29 @@ class BuildTask(SCons.Taskmaster.Task):
         self.progress(self.targets[0])
         return SCons.Taskmaster.Task.prepare(self)
 
-    def execute(self):
-        for target in self.targets:
-            if target.get_state() == SCons.Node.up_to_date: 
-                continue
-            if target.has_builder() and not hasattr(target.builder, 'status'):
-                if print_time:
-                    start_time = time.time()
-                    global first_command_start
-                    if first_command_start is None:
-                        first_command_start = start_time
-                SCons.Taskmaster.Task.execute(self)
-                if print_time:
-                    global cumulative_command_time
-                    global last_command_end
-                    finish_time = time.time()
-                    last_command_end = finish_time
-                    cumulative_command_time = cumulative_command_time+finish_time-start_time
-                    sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time))
-                break
+    def needs_execute(self):
+        target = self.targets[0]
+        if target.get_state() == SCons.Node.executing:
+            return True
         else:
             if self.top and target.has_builder():
                 display("scons: `%s' is up to date." % str(self.node))
+            return False
+
+    def execute(self):
+        if print_time:
+            start_time = time.time()
+            global first_command_start
+            if first_command_start is None:
+                first_command_start = start_time
+        SCons.Taskmaster.Task.execute(self)
+        if print_time:
+            global cumulative_command_time
+            global last_command_end
+            finish_time = time.time()
+            last_command_end = finish_time
+            cumulative_command_time = cumulative_command_time+finish_time-start_time
+            sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time))
 
     def do_failed(self, status=2):
         _BuildFailures.append(self.exception[1])
@@ -207,10 +208,15 @@ class BuildTask(SCons.Taskmaster.Task):
         t = self.targets[0]
         if self.top and not t.has_builder() and not t.side_effect:
             if not t.exists():
-                sys.stderr.write("scons: *** Do not know how to make target `%s'." % t)
+                errstr="Do not know how to make target `%s'." % t
+                sys.stderr.write("scons: *** " + errstr)
                 if not self.options.keep_going:
                     sys.stderr.write("  Stop.")
                 sys.stderr.write("\n")
+                try:
+                    raise SCons.Errors.BuildError(t, errstr)
+                except:
+                    self.exception_set()
                 self.do_failed()
             else:
                 print "scons: Nothing to be done for `%s'." % t
@@ -977,11 +983,6 @@ def _build_targets(fs, options, targets, target_top):
     if options.diskcheck:
         SCons.Node.FS.set_diskcheck(options.diskcheck)
 
-    _set_debug_values(options)
-    SCons.Node.implicit_cache = options.implicit_cache
-    SCons.Node.implicit_deps_changed = options.implicit_deps_changed
-    SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
-
     SCons.CacheDir.cache_enabled = not options.cache_disable
     SCons.CacheDir.cache_debug = options.cache_debug
     SCons.CacheDir.cache_force = options.cache_force
@@ -1133,26 +1134,29 @@ def _build_targets(fs, options, targets, target_top):
     memory_stats.append('before building targets:')
     count_stats.append(('pre-', 'build'))
 
-    try:
-        progress_display("scons: " + opening_message)
-        try:
-            jobs.run()
-        except KeyboardInterrupt:
-            # If we are in interactive mode, a KeyboardInterrupt
-            # interrupts only this current run.  Return 'nodes' normally
-            # so that the outer loop can clean up the nodes and continue.
-            if options.interactive:
-                print "Build interrupted."
-                # Continue and return normally
-    finally:
-        jobs.cleanup()
+    def jobs_postfunc(
+        jobs=jobs,
+        options=options,
+        closing_message=closing_message,
+        failure_message=failure_message
+        ):
+        if jobs.were_interrupted():
+            progress_display("scons: Build interrupted.")
+            global exit_status
+            exit_status = 2
+
         if exit_status:
             progress_display("scons: " + failure_message)
         else:
             progress_display("scons: " + closing_message)
         if not options.no_exec:
+            if jobs.were_interrupted():
+                progress_display("scons: writing .sconsign file.")
             SCons.SConsign.write()
 
+    progress_display("scons: " + opening_message)
+    jobs.run(postfunc = jobs_postfunc)
+
     memory_stats.append('after building targets:')
     count_stats.append(('post-', 'build'))
 
@@ -1210,10 +1214,9 @@ def main():
 
     parts = ["SCons by Steven Knight et al.:\n"]
     try:
+        import __main__
         parts.append(version_string("script", __main__))
-    except KeyboardInterrupt:
-        raise
-    except:
+    except (ImportError, AttributeError):
         # On Windows there is no scons.py, so there is no
         # __main__.__version__, hence there is no script version.
         pass 
@@ -1233,7 +1236,7 @@ def main():
         if s:
             exit_status = s
     except KeyboardInterrupt:
-        print "Build interrupted."
+        print("scons: Build interrupted.")
         sys.exit(2)
     except SyntaxError, e:
         _scons_syntax_error(e)
index ee34a85bb9f1f0f0d7a59062d1072e3dfb7518cd..2148068acd8c370de79385e76159c366b6a43a8b 100644 (file)
@@ -26,10 +26,18 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 import SCons.compat
 
 import optparse
+import re
 import string
 import sys
 import textwrap
 
+try:
+    no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')
+except re.error:
+    # Pre-2.0 Python versions don't have the (?<= negative
+    # look-behind assertion.
+    no_hyphen_re = re.compile(r'(\s+|-*\w{2,}-(?=\w{2,}))')
+
 try:
     from gettext import gettext
 except ImportError:
@@ -382,8 +390,16 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
     def format_option(self, option):
         """
         A copy of the normal optparse.IndentedHelpFormatter.format_option()
-        method, snarfed so we can set the subsequent_indent on the
-        textwrap.wrap() call below...
+        method.  This has been snarfed so we can modify text wrapping to
+        out liking:
+
+        --  add our own regular expression that doesn't break on hyphens
+            (so things like --no-print-directory don't get broken); 
+
+        --  wrap the list of options themselves when it's too long
+            (the wrapper.fill(opts) call below);
+        --  set the subsequent_indent when wrapping the help_text.
         """
         # The help for each option consists of two parts:
         #   * the opt strings and metavars
@@ -410,7 +426,11 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
 
         opt_width = self.help_position - self.current_indent - 2
         if len(opts) > opt_width:
-            opts = "%*s%s\n" % (self.current_indent, "", opts)
+            wrapper = textwrap.TextWrapper(width=self.width,
+                                           initial_indent = '  ',
+                                           subsequent_indent = '  ')
+            wrapper.wordsep_re = no_hyphen_re
+            opts = wrapper.fill(opts) + '\n'
             indent_first = self.help_position
         else:                       # start help on same line as opts
             opts = "%*s%-*s  " % (self.current_indent, "", opt_width, opts)
@@ -428,8 +448,10 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
                 help_text = expand_default(option)
 
             # SCons:  indent every line of the help text but the first.
-            help_lines = textwrap.wrap(help_text, self.help_width,
-                                       subsequent_indent = '  ')
+            wrapper = textwrap.TextWrapper(width=self.help_width,
+                                           subsequent_indent = '  ')
+            wrapper.wordsep_re = no_hyphen_re
+            help_lines = wrapper.wrap(help_text)
             result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
             for line in help_lines[1:]:
                 result.append("%*s%s\n" % (self.help_position, "", line))
@@ -503,8 +525,13 @@ def Parser(version):
     # options ignored for compatibility
     def opt_ignore(option, opt, value, parser):
         sys.stderr.write("Warning:  ignoring %s option\n" % opt)
-    op.add_option("-b", "-m", "-S", "-t",
-                  "--no-keep-going", "--stop", "--touch",
+    op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w",
+                  "--environment-overrides",
+                  "--no-keep-going",
+                  "--no-print-directory",
+                  "--print-directory",
+                  "--stop",
+                  "--touch",
                   action="callback", callback=opt_ignore,
                   help="Ignored for compatibility.")
 
@@ -557,13 +584,6 @@ def Parser(version):
                   help = opt_config_help,
                   metavar="MODE")
 
-    def opt_not_yet(option, opt, value, parser):
-        sys.stderr.write("Warning:  the %s option is not yet implemented\n" % opt)
-        sys.exit(0)
-    op.add_option('-d',
-                  action="callback", callback=opt_not_yet,
-                  help = "Print file dependency information.")
-
     op.add_option('-D',
                   dest="climb_up", default=None,
                   action="store_const", const=2,
@@ -836,11 +856,12 @@ def Parser(version):
     # we don't want to change.  These all get a "the -X option is not
     # yet implemented" message and don't show up in the help output.
 
-    op.add_option('-e', '--environment-overrides',
-                  dest="environment_overrides",
-                  action="callback", callback=opt_not_yet,
-                  # help="Environment variables override makefiles."
-                  help=SUPPRESS_HELP)
+    def opt_not_yet(option, opt, value, parser):
+        msg = "Warning:  the %s option is not yet implemented\n" % opt
+        sys.stderr.write(msg)
+        sys.exit(0)
+
+
     op.add_option('-l', '--load-average', '--max-load',
                   nargs=1, type="int",
                   dest="load_average", default=0,
@@ -887,16 +908,6 @@ def Parser(version):
                   dest="no_builtin_rules",
                   # help="Clear default environments and variables."
                   help=SUPPRESS_HELP)
-    op.add_option('-w', '--print-directory',
-                  action="callback", callback=opt_not_yet,
-                  dest="print_directory",
-                  # help="Print the current directory."
-                  help=SUPPRESS_HELP)
-    op.add_option('--no-print-directory',
-                  action="callback", callback=opt_not_yet,
-                  dest="no_print_directory",
-                  # help="Turn off -w, even if it was turned on implicitly."
-                  help=SUPPRESS_HELP)
     op.add_option('--write-filenames',
                   nargs=1, type="string",
                   dest="write_filenames",
index 36a147dba69696441635328c610331eba8667986..7e38ffdefab030d2c1d17606975d25ada1bdb813 100644 (file)
@@ -39,7 +39,6 @@ import SCons.Errors
 import SCons.Node
 import SCons.Node.Alias
 import SCons.Node.FS
-import SCons.Options
 import SCons.Platform
 import SCons.SConf
 import SCons.Script.Main
@@ -141,7 +140,8 @@ call_stack = []
 def Return(*vars, **kw):
     retval = []
     try:
-        for var in vars:
+        fvars = SCons.Util.flatten(vars)
+        for var in fvars:
             for v in string.split(var):
                 retval.append(call_stack[-1].globals[v])
     except KeyError, x:
index ddeaf9e0f6745fcdbc340529d38424b9bd336361..72e31d1e1d14035549d9055faf637666346bc669 100644 (file)
@@ -84,6 +84,7 @@ import SCons.SConf
 import SCons.Subst
 import SCons.Tool
 import SCons.Util
+import SCons.Variables
 import SCons.Defaults
 
 import Main
@@ -138,22 +139,31 @@ call_stack              = _SConscript.call_stack
 Action                  = SCons.Action.Action
 AddMethod               = SCons.Util.AddMethod
 AllowSubstExceptions    = SCons.Subst.SetAllowableExceptions
-BoolOption              = SCons.Options.BoolOption
 Builder                 = SCons.Builder.Builder
 Configure               = _SConscript.Configure
-EnumOption              = SCons.Options.EnumOption
 Environment             = SCons.Environment.Environment
 #OptParser               = SCons.SConsOptions.OptParser
 FindPathDirs            = SCons.Scanner.FindPathDirs
-ListOption              = SCons.Options.ListOption
-PackageOption           = SCons.Options.PackageOption
-PathOption              = SCons.Options.PathOption
 Platform                = SCons.Platform.Platform
 Return                  = _SConscript.Return
 Scanner                 = SCons.Scanner.Base
 Tool                    = SCons.Tool.Tool
 WhereIs                 = SCons.Util.WhereIs
 
+#
+BoolVariable            = SCons.Variables.BoolVariable
+EnumVariable            = SCons.Variables.EnumVariable
+ListVariable            = SCons.Variables.ListVariable
+PackageVariable         = SCons.Variables.PackageVariable
+PathVariable            = SCons.Variables.PathVariable
+
+# Deprecated names that will go away some day.
+BoolOption              = SCons.Options.BoolOption
+EnumOption              = SCons.Options.EnumOption
+ListOption              = SCons.Options.ListOption
+PackageOption           = SCons.Options.PackageOption
+PathOption              = SCons.Options.PathOption
+
 # Action factories.
 Chmod                   = SCons.Defaults.Chmod
 Copy                    = SCons.Defaults.Copy
@@ -262,6 +272,9 @@ def HelpFunction(text):
 sconscript_reading = 0
 
 #
+def Variables(files=[], args=ARGUMENTS):
+    return SCons.Variables.Variables(files, args)
+
 def Options(files=[], args=ARGUMENTS):
     return SCons.Options.Options(files, args)
 
index 7a565baab7aeb16877b104a6e84e814ac84bb9f8..52aaae5426b6ca50aeedc39ddd81e57a8a8dfbbd 100644 (file)
@@ -565,7 +565,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
 #    except KeyError:
 #        Subst_List_Strings[strSubst] = 1
 #    import SCons.Debug
-#    SCons.Debug.caller(1)
+#    SCons.Debug.caller_trace(1)
     class ListSubber(UserList.UserList):
         """A class to construct the results of a scons_subst_list() call.
 
index 66202dc53239961a4f7fb2e67fe4562ab90800ff..8a0fcf79ad0824f04a69f19689c0fbbc9933b7ed 100644 (file)
@@ -52,16 +52,22 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.compat
 
+from itertools import chain
 import operator
 import string
 import sys
 import traceback
 
-import SCons.Node
 import SCons.Errors
+import SCons.Node
 
 StateString = SCons.Node.StateString
-
+NODE_NO_STATE = SCons.Node.no_state
+NODE_PENDING = SCons.Node.pending
+NODE_EXECUTING = SCons.Node.executing
+NODE_UP_TO_DATE = SCons.Node.up_to_date
+NODE_EXECUTED = SCons.Node.executed
+NODE_FAILED = SCons.Node.failed
 
 
 # A subsystem for recording stats about how different Nodes are handled by
@@ -165,6 +171,17 @@ class Task:
             self.display(self.tm.message)
             self.tm.message = None
 
+        # Let the targets take care of any necessary preparations.
+        # This includes verifying that all of the necessary sources
+        # and dependencies exist, removing the target file(s), etc.
+        #
+        # As of April 2008, the get_executor().prepare() method makes
+        # sure that all of the aggregate sources necessary to build this
+        # Task's target(s) exist in one up-front check.  The individual
+        # target t.prepare() methods check that each target's explicit
+        # or implicit dependencies exists, and also initialize the
+        # .sconsign info.
+        self.targets[0].get_executor().prepare()
         for t in self.targets:
             t.prepare()
             for s in t.side_effects:
@@ -175,6 +192,17 @@ class Task:
         """
         return self.node
 
+    def needs_execute(self):
+        """
+        Called to determine whether the task's execute() method should
+        be run.
+
+        This method allows one to skip the somethat costly execution
+        of the execute() method in a seperate thread. For example,
+        that would be unnecessary for up-to-date targets.
+        """
+        return True
+
     def execute(self):
         """
         Called to execute the task.
@@ -192,8 +220,6 @@ class Task:
                     break
             if not everything_was_cached:
                 self.targets[0].build()
-        except KeyboardInterrupt:
-            raise
         except SystemExit:
             exc_value = sys.exc_info()[1]
             raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code)
@@ -212,10 +238,10 @@ class Task:
         the Node's callback methods.
         """
         for t in self.targets:
-            if t.get_state() == SCons.Node.executing:
+            if t.get_state() == NODE_EXECUTING:
                 for side_effect in t.side_effects:
-                    side_effect.set_state(SCons.Node.no_state)
-                t.set_state(SCons.Node.executed)
+                    side_effect.set_state(NODE_NO_STATE)
+                t.set_state(NODE_EXECUTED)
 
     def executed_with_callbacks(self):
         """
@@ -231,10 +257,10 @@ class Task:
         or not the target was an actual built target or a source Node.
         """
         for t in self.targets:
-            if t.get_state() == SCons.Node.executing:
+            if t.get_state() == NODE_EXECUTING:
                 for side_effect in t.side_effects:
-                    side_effect.set_state(SCons.Node.no_state)
-                t.set_state(SCons.Node.executed)
+                    side_effect.set_state(NODE_NO_STATE)
+                t.set_state(NODE_EXECUTED)
                 t.built()
             t.visited()
 
@@ -250,8 +276,12 @@ class Task:
         """
         Explicit stop-the-build failure.
         """
-        for t in self.targets:
-            t.set_state(SCons.Node.failed)
+        
+        # Invoke fail_continue() to clean-up the pending children
+        # list.
+        self.fail_continue()
+
+        # Tell the taskmaster to not start any new tasks
         self.tm.stop()
 
         # We're stopping because of a build failure, but give the
@@ -267,11 +297,43 @@ class Task:
         This sets failure status on the target nodes and all of
         their dependent parent nodes.
         """
+        
+        pending_children = self.tm.pending_children
+
+        to_visit = set()
         for t in self.targets:
             # Set failure state on all of the parents that were dependent
             # on this failed build.
-            def set_state(node): node.set_state(SCons.Node.failed)
-            t.call_for_all_waiting_parents(set_state)
+            if t.state != NODE_FAILED:
+                t.state = NODE_FAILED
+                parents = t.waiting_parents
+                to_visit = to_visit | parents
+                pending_children = pending_children - parents
+
+        try:
+            while 1:
+                try:
+                    node = to_visit.pop()
+                except AttributeError:
+                    # Python 1.5.2
+                    if len(to_visit):
+                        node = to_visit[0]
+                        to_visit.remove(node)
+                    else:
+                        break
+                if node.state != NODE_FAILED:
+                    node.state = NODE_FAILED
+                    parents = node.waiting_parents
+                    to_visit = to_visit | parents
+                    pending_children = pending_children - parents
+        except KeyError:
+            # The container to_visit has been emptied.
+            pass
+
+        # We have the stick back the pending_children list into the
+        # task master because the python 1.5.2 compatibility does not
+        # allow us to use in-place updates
+        self.tm.pending_children = pending_children
 
     def make_ready_all(self):
         """
@@ -282,9 +344,9 @@ class Task:
         """
         self.out_of_date = self.targets[:]
         for t in self.targets:
-            t.disambiguate().set_state(SCons.Node.executing)
+            t.disambiguate().set_state(NODE_EXECUTING)
             for s in t.side_effects:
-                s.set_state(SCons.Node.executing)
+                s.set_state(NODE_EXECUTING)
 
     def make_ready_current(self):
         """
@@ -294,6 +356,7 @@ class Task:
         This is the default behavior for building only what's necessary.
         """
         self.out_of_date = []
+        needs_executing = False
         for t in self.targets:
             try:
                 t.disambiguate().make_ready()
@@ -301,13 +364,24 @@ class Task:
                                 (not t.always_build and t.is_up_to_date())
             except EnvironmentError, e:
                 raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename)
-            if is_up_to_date:
-                t.set_state(SCons.Node.up_to_date)
-            else:
+
+            if not is_up_to_date:
                 self.out_of_date.append(t)
-                t.set_state(SCons.Node.executing)
+                needs_executing = True
+
+        if needs_executing:
+            for t in self.targets:
+                t.set_state(NODE_EXECUTING)
                 for s in t.side_effects:
-                    s.set_state(SCons.Node.executing)
+                    s.set_state(NODE_EXECUTING)
+        else:                
+            for t in self.targets:
+                # We must invoke visited() to ensure that the node
+                # information has been computed before allowing the
+                # parent nodes to execute. (That could occur in a
+                # parallel build...)
+                t.visited()
+                t.set_state(NODE_UP_TO_DATE)
 
     make_ready = make_ready_current
 
@@ -333,24 +407,25 @@ class Task:
 
         parents = {}
         for t in targets:
-            for p in t.waiting_parents.keys():
+            for p in t.waiting_parents:
                 parents[p] = parents.get(p, 0) + 1
 
         for t in targets:
             for s in t.side_effects:
-                if s.get_state() == SCons.Node.executing:
-                    s.set_state(SCons.Node.no_state)
-                    for p in s.waiting_parents.keys():
-                        if not parents.has_key(p):
-                            parents[p] = 1
-                for p in s.waiting_s_e.keys():
+                if s.get_state() == NODE_EXECUTING:
+                    s.set_state(NODE_NO_STATE)
+                    for p in s.waiting_parents:
+                        parents[p] = parents.get(p, 0) + 1
+                for p in s.waiting_s_e:
                     if p.ref_count == 0:
                         self.tm.candidates.append(p)
+                        self.tm.pending_children.discard(p)
 
         for p, subtract in parents.items():
             p.ref_count = p.ref_count - subtract
             if p.ref_count == 0:
                 self.tm.candidates.append(p)
+                self.tm.pending_children.discard(p)
 
         for t in targets:
             t.postprocess()
@@ -409,12 +484,15 @@ class Task:
         raise exc_type, exc_value, exc_traceback
 
 
-def find_cycle(stack):
-    if stack[0] == stack[-1]:
-        return stack
-    for n in stack[-1].waiting_parents.keys():
+def find_cycle(stack, visited):
+    if stack[-1] in visited:
+        return None
+    visited.add(stack[-1])
+    for n in stack[-1].waiting_parents:
         stack.append(n)
-        if find_cycle(stack):
+        if stack[0] == stack[-1]:
+            return stack
+        if find_cycle(stack, visited):
             return stack
         stack.pop()
     return None
@@ -437,6 +515,8 @@ class Taskmaster:
         self.message = None
         self.trace = trace
         self.next_candidate = self.find_next_candidate
+        self.pending_children = set()
+
 
     def find_next_candidate(self):
         """
@@ -505,10 +585,12 @@ class Taskmaster:
         self.ready_exc = None
 
         T = self.trace
+        if T: T.write('\nTaskmaster: Looking for a node to evaluate\n')
 
         while 1:
             node = self.next_candidate()
             if node is None:
+                if T: T.write('Taskmaster: No candidate anymore.\n\n')
                 return None
 
             node = node.disambiguate()
@@ -522,29 +604,27 @@ class Taskmaster:
                 S.considered = S.considered + 1
             else:
                 S = None
-
-            if T: T.write('Taskmaster: %s:' % repr(str(node)))
-
-            # Skip this node if it has already been evaluated:
-            if state > SCons.Node.pending:
+  
+            if T: T.write('Taskmaster:     Considering node <%-10s %s> and its children:\n' % 
+                          (StateString[node.get_state()], repr(str(node))))
+
+            if state == NODE_NO_STATE:
+                # Mark this node as being on the execution stack:
+                node.set_state(NODE_PENDING)
+            elif state > NODE_PENDING:
+                # Skip this node if it has already been evaluated:
                 if S: S.already_handled = S.already_handled + 1
-                if T: T.write(' already handled (%s)\n' % StateString[state])
+                if T: T.write('Taskmaster:        already handled (executed)\n')
                 continue
 
-            # Mark this node as being on the execution stack:
-            node.set_state(SCons.Node.pending)
-
             try:
-                children = node.children() + node.prerequisites
+                children = node.children()
             except SystemExit:
                 exc_value = sys.exc_info()[1]
                 e = SCons.Errors.ExplicitExit(node, exc_value.code)
                 self.ready_exc = (SCons.Errors.ExplicitExit, e)
-                if T: T.write(' SystemExit\n')
+                if T: T.write('Taskmaster:        SystemExit\n')
                 return node
-            except KeyboardInterrupt:
-                if T: T.write(' KeyboardInterrupt\n')
-                raise
             except:
                 # We had a problem just trying to figure out the
                 # children (like a child couldn't be linked in to a
@@ -552,70 +632,36 @@ class Taskmaster:
                 # raise the exception when the Task is "executed."
                 self.ready_exc = sys.exc_info()
                 if S: S.problem = S.problem + 1
-                if T: T.write(' exception\n')
+                if T: T.write('Taskmaster:        exception while scanning children.\n')
                 return node
 
-            if T and children:
-                c = map(str, children)
-                c.sort()
-                T.write(' children:\n    %s\n   ' % c)
-
-            childstate = map(lambda N: (N, N.get_state()), children)
-
-            # Detect dependency cycles:
-            pending_nodes = filter(lambda I: I[1] == SCons.Node.pending, childstate)
-            if pending_nodes:
-                for p in pending_nodes:
-                    cycle = find_cycle([p[0], node])
-                    if cycle:
-                        desc = "Dependency cycle: " + string.join(map(str, cycle), " -> ")
-                        if T: T.write(' dependency cycle\n')
-                        raise SCons.Errors.UserError, desc
-
-            not_built = filter(lambda I: I[1] <= SCons.Node.executing, childstate)
-            if not_built:
-                # We're waiting on one or more derived targets that have
-                # not yet finished building.
-
-                not_visited = filter(lambda I: not I[1], not_built)
-                if not_visited:
-                    # Some of them haven't even been visited yet.
-                    # Add them to the list so that on some next pass
-                    # we can take a stab at evaluating them (or
-                    # their children).
-                    not_visited = map(lambda I: I[0], not_visited)
-                    not_visited.reverse()
-                    self.candidates.extend(self.order(not_visited))
-
-                n_b_nodes = map(lambda I: I[0], not_built)
-
-                # Add this node to the waiting parents lists of anything
-                # we're waiting on, with a reference count so we can be
-                # put back on the list for re-evaluation when they've
-                # all finished.
-                map(lambda n, P=node: n.add_to_waiting_parents(P), n_b_nodes)
-                node.ref_count = len(set(n_b_nodes))
-
-                if S: S.not_built = S.not_built + 1
-                if T:
-                    c = map(str, n_b_nodes)
-                    c.sort()
-                    T.write(' waiting on unfinished children:\n    %s\n' % c)
-                continue
+            children_not_visited = []
+            children_pending = set()
+            children_not_ready = []
+            children_failed = False
 
-            # Skip this node if it has side-effects that are
-            # currently being built:
-            side_effects = filter(lambda N:
-                                  N.get_state() == SCons.Node.executing,
-                                  node.side_effects)
-            if side_effects:
-                map(lambda n, P=node: n.add_to_waiting_s_e(P), side_effects)
-                if S: S.side_effects = S.side_effects + 1
-                if T:
-                    c = map(str, side_effects)
-                    c.sort()
-                    T.write(' waiting on side effects:\n    %s\n' % c)
-                continue
+            for child in chain(children,node.prerequisites):
+                childstate = child.get_state()
+
+                if T: T.write('Taskmaster:        <%-10s %s>\n' % 
+                              (StateString[childstate], repr(str(child))))
+
+                if childstate == NODE_NO_STATE:
+                    children_not_visited.append(child)
+                elif childstate == NODE_PENDING:
+                    children_pending.add(child)
+                elif childstate == NODE_FAILED:
+                    children_failed = True
+
+                if childstate <= NODE_EXECUTING:
+                    children_not_ready.append(child)
+
+
+            # These nodes have not even been visited yet.  Add
+            # them to the list so that on some next pass we can
+            # take a stab at evaluating them (or their children).
+            children_not_visited.reverse()
+            self.candidates.extend(self.order(children_not_visited))
 
             # Skip this node if any of its children have failed.
             #
@@ -635,21 +681,47 @@ class Taskmaster:
             # Note that even if one of the children fails, we still
             # added the other children to the list of candidate nodes
             # to keep on building (--keep-going).
-            failed_children = filter(lambda I: I[1] == SCons.Node.failed,
-                                     childstate)
-            if failed_children:
-                node.set_state(SCons.Node.failed)
+            if children_failed:
+                node.set_state(NODE_FAILED)
+
                 if S: S.child_failed = S.child_failed + 1
-                if T:
-                    c = map(lambda I: str(I[0]), failed_children)
-                    c.sort()
-                    T.write(' children failed:\n    %s\n' % c)
+                if T: T.write('Taskmaster:****** <%-10s %s>\n' % 
+                              (StateString[node.get_state()], repr(str(node))))
+                continue
+
+            if children_not_ready:
+                for child in children_not_ready:
+                    # We're waiting on one or more derived targets
+                    # that have not yet finished building.
+                    if S: S.not_built = S.not_built + 1
+
+                    # Add this node to the waiting parents lists of
+                    # anything we're waiting on, with a reference
+                    # count so we can be put back on the list for
+                    # re-evaluation when they've all finished.
+                    node.ref_count =  node.ref_count + child.add_to_waiting_parents(node)
+
+                self.pending_children = self.pending_children | children_pending
+                
+                continue
+
+            # Skip this node if it has side-effects that are
+            # currently being built:
+            wait_side_effects = False
+            for se in node.side_effects:
+                if se.get_state() == NODE_EXECUTING:
+                    se.add_to_waiting_s_e(node)
+                    wait_side_effects = True
+
+            if wait_side_effects:
+                if S: S.side_effects = S.side_effects + 1
                 continue
 
             # The default when we've gotten through all of the checks above:
             # this node is ready to be built.
             if S: S.build = S.build + 1
-            if T: T.write(' evaluating %s\n' % node)
+            if T: T.write('Taskmaster: Evaluating <%-10s %s>\n' % 
+                          (StateString[node.get_state()], repr(str(node))))
             return node
 
         return None
@@ -671,8 +743,6 @@ class Taskmaster:
         task = self.tasker(self, tlist, node in self.original_top, node)
         try:
             task.make_ready()
-        except KeyboardInterrupt:
-            raise
         except:
             # We had a problem just trying to get this task ready (like
             # a child couldn't be linked in to a VariantDir when deciding
@@ -692,3 +762,18 @@ class Taskmaster:
         Stops the current build completely.
         """
         self.next_candidate = self.no_next_candidate
+
+    def cleanup(self):
+        """
+        Check for dependency cycles.
+        """
+        if self.pending_children:
+            desc = 'Found dependency cycle(s):\n'
+            for node in self.pending_children:
+                cycle = find_cycle([node], set())
+                if cycle:
+                    desc = desc + "  " + string.join(map(str, cycle), " -> ") + "\n"
+                else:
+                    desc = desc + "  Internal Error: no cycle found for node %s (%s)\n" %  \
+                        (node, repr(node)) 
+            raise SCons.Errors.UserError, desc
index 9a7969b31045e8a82b22404ff458f6bb5ee41610..88d30782fe458b30bbd8a0ecbc7171f2c2719427 100644 (file)
@@ -56,8 +56,8 @@ class Node:
         self.state = SCons.Node.no_state
         self.prepared = None
         self.ref_count = 0
-        self.waiting_parents = {}
-        self.waiting_s_e = {}
+        self.waiting_parents = set()
+        self.waiting_s_e = set()
         self.side_effect = 0
         self.side_effects = []
         self.alttargets = []
@@ -119,17 +119,10 @@ class Node:
 
     def add_to_waiting_parents(self, node):
         wp = self.waiting_parents
-        if wp.has_key(node):
-            result = 0
-        else:
-            result = 1
-        wp[node] = 1
-        return result
-
-    def call_for_all_waiting_parents(self, func):
-        func(self)
-        for parent in self.waiting_parents.keys():
-            parent.call_for_all_waiting_parents(func)
+        if node in wp:
+            return 0
+        wp.add(node)
+        return 1
 
     def get_state(self):
         return self.state
@@ -166,13 +159,16 @@ class Node:
 
     def postprocess(self):
         self.postprocessed = 1
+        self.waiting_parents = set()
 
     def get_executor(self):
-        class Executor:
-            pass
-        e = Executor()
-        e.targets = self.targets
-        return e
+        if not hasattr(self, 'executor'):
+            class Executor:
+                def prepare(self):
+                    pass
+            self.executor = Executor()
+        self.executor.targets = self.targets
+        return self.executor
 
 class OtherError(Exception):
     pass
@@ -469,7 +465,7 @@ class TaskmasterTestCase(unittest.TestCase):
         t.postprocess()
 
         s = n1.get_state()
-        assert s == SCons.Node.up_to_date, s
+        assert s == SCons.Node.executed, s
         s = n2.get_state()
         assert s == SCons.Node.executed, s
 
@@ -815,6 +811,22 @@ class TaskmasterTestCase(unittest.TestCase):
         assert n9.prepared
         assert n10.prepared
 
+        # Make sure we call an Executor's prepare() method.
+        class ExceptionExecutor:
+            def prepare(self):
+                raise Exception, "Executor.prepare() exception"
+
+        n11 = Node("n11")
+        n11.executor = ExceptionExecutor()
+        tm = SCons.Taskmaster.Taskmaster([n11])
+        t = tm.next_task()
+        try:
+            t.prepare()
+        except Exception, e:
+            assert str(e) == "Executor.prepare() exception", e
+        else:
+            raise AssertionError, "did not catch expected exception"
+
     def test_execute(self):
         """Test executing a task
         """
@@ -937,37 +949,46 @@ class TaskmasterTestCase(unittest.TestCase):
         ]
         assert str(exc_value) in exception_values, exc_value
 
-        t.exception_set(("exception 1", None))
+        class Exception1(Exception):
+            pass
+
+        t.exception_set((Exception1, None))
         try:
             t.exception_raise()
         except:
             exc_type, exc_value = sys.exc_info()[:2]
-            assert exc_type == "exception 1", exc_type
-            assert exc_value is None, exc_value
+            assert exc_type == Exception1, exc_type
+            assert str(exc_value) == '', exc_value
         else:
             assert 0, "did not catch expected exception"
 
-        t.exception_set(("exception 2", "xyzzy"))
+        class Exception2(Exception):
+            pass
+
+        t.exception_set((Exception2, "xyzzy"))
         try:
             t.exception_raise()
         except:
             exc_type, exc_value = sys.exc_info()[:2]
-            assert exc_type == "exception 2", exc_type
-            assert exc_value == "xyzzy", exc_value
+            assert exc_type == Exception2, exc_type
+            assert str(exc_value) == "xyzzy", exc_value
         else:
             assert 0, "did not catch expected exception"
 
+        class Exception3(Exception):
+            pass
+
         try:
             1/0
         except:
             tb = sys.exc_info()[2]
-        t.exception_set(("exception 3", "arg", tb))
+        t.exception_set((Exception3, "arg", tb))
         try:
             t.exception_raise()
         except:
             exc_type, exc_value, exc_tb = sys.exc_info()
-            assert exc_type == 'exception 3', exc_type
-            assert exc_value == "arg", exc_value
+            assert exc_type == Exception3, exc_type
+            assert str(exc_value) == "arg", exc_value
             import traceback
             x = traceback.extract_tb(tb)[-1]
             y = traceback.extract_tb(exc_tb)[-1]
@@ -1030,16 +1051,29 @@ class TaskmasterTestCase(unittest.TestCase):
 
         value = trace.getvalue()
         expect = """\
-Taskmaster: 'n1': evaluating n1
-Taskmaster: 'n1': already handled (executed)
-Taskmaster: 'n3': children:
-    ['n1', 'n2']
-    waiting on unfinished children:
-    ['n2']
-Taskmaster: 'n2': evaluating n2
-Taskmaster: 'n3': children:
-    ['n1', 'n2']
-    evaluating n3
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'n1'> and its children:
+Taskmaster: Evaluating <pending    'n1'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <executed   'n1'> and its children:
+Taskmaster:        already handled (executed)
+Taskmaster:     Considering node <no_state   'n3'> and its children:
+Taskmaster:        <executed   'n1'>
+Taskmaster:        <no_state   'n2'>
+Taskmaster:     Considering node <no_state   'n2'> and its children:
+Taskmaster: Evaluating <pending    'n2'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <pending    'n3'> and its children:
+Taskmaster:        <executed   'n1'>
+Taskmaster:        <executed   'n2'>
+Taskmaster: Evaluating <pending    'n3'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
+
 """
         assert value == expect, value
 
index b9230f14108ed9d9c3b72baca34aa194b6bbae4a..779aaaad66249a3b77ff98b9e056e8f84fb2fa87 100644 (file)
@@ -54,7 +54,6 @@ class ToolTestCase(unittest.TestCase):
         t = SCons.Tool.Tool('g++')
         t(env)
         assert (env['CXX'] == 'c++' or env['CXX'] == 'g++'), env['CXX']
-        assert env['CXXFLAGS'] == ['$CCFLAGS'], env['CXXFLAGS']
         assert env['INCPREFIX'] == '-I', env['INCPREFIX']
         assert env['TOOLS'] == ['g++'], env['TOOLS']
 
index 1e69f1e263f45ee5c21a499557858b8890fee1ac..e0ada5e74ec56c52233601ad0bd239929ce03df1 100644 (file)
@@ -163,10 +163,10 @@ class Tool:
                 kw = self.init_kw
         env.Append(TOOLS = [ self.name ])
         if hasattr(self, 'options'):
-            from SCons.Options import Options
+            import SCons.Variables
             if not env.has_key('options'):
                 from SCons.Script import ARGUMENTS
-                env['options']=Options(args=ARGUMENTS)
+                env['options']=SCons.Variables.Variables(args=ARGUMENTS)
             opts=env['options']
 
             self.options(opts)
index d9370b051b130161a11c98a6e9e94c1a0ed5451c..a4375c0110ed91b3ea0ed96dad3417167008610c 100644 (file)
@@ -71,13 +71,13 @@ def generate(env):
         shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
 
     SCons.Tool.cc.add_common_cc_variables(env)
-        
+
     env['CXX']        = 'c++'
-    env['CXXFLAGS']   = SCons.Util.CLVar('$CCFLAGS')
-    env['CXXCOM']     = '$CXX -o $TARGET -c $CXXFLAGS $_CCCOMCOM $SOURCES'
+    env['CXXFLAGS']   = SCons.Util.CLVar('')
+    env['CXXCOM']     = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
     env['SHCXX']      = '$CXX'
-    env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
-    env['SHCXXCOM']   = '$SHCXX -o $TARGET -c $SHCXXFLAGS $_CCCOMCOM $SOURCES'
+    env['SHCXXFLAGS'] = SCons.Util.CLVar('')
+    env['SHCXXCOM']   = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
 
     env['CPPDEFPREFIX']  = '-D'
     env['CPPDEFSUFFIX']  = ''
index 64ffb68053037f66b7bb3abcbb00d8bea0768660..a173162c9b4c787c3db45d44b03ea2aa88b031b2 100644 (file)
@@ -14,14 +14,15 @@ use absolute paths.  To hack around it, add '#/blah'.  This will link
 blah.lib from the directory where SConstruct resides.
 
 Compiler variables:
-    DC - The name of the D compiler to use.  Defaults to dmd.
+    DC - The name of the D compiler to use.  Defaults to dmd or gdmd,
+    whichever is found.
     DPATH - List of paths to search for import modules.
     DVERSIONS - List of version tags to enable when compiling.
     DDEBUG - List of debug tags to enable when compiling.
 
 Linker related variables:
     LIBS - List of library files to link in.
-    DLINK - Name of the linker to use.  Defaults to dmd.
+    DLINK - Name of the linker to use.  Defaults to dmd or gdmd.
     DLINKFLAGS - List of linker flags.
 
 Lib tool variables:
@@ -93,7 +94,8 @@ def generate(env):
     static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter)
     shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter)
 
-    env['DC'] = 'dmd'
+    dc = env.Detect(['dmd', 'gdmd'])
+    env['DC'] = dc
     env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES'
     env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}  $)'
     env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}  $)'
@@ -105,14 +107,15 @@ def generate(env):
     env['DVERSIONS'] = []
     env['DDEBUG'] = []
 
-    # Add the path to the standard library.
-    # This is merely for the convenience of the dependency scanner.
-    dmd_path = env.WhereIs('dmd')
-    if dmd_path:
-        x = string.rindex(dmd_path, 'dmd')
-        phobosDir = dmd_path[:x] + '/../src/phobos'
-        if os.path.isdir(phobosDir):
-            env.Append(DPATH = [phobosDir])
+    if dc:
+        # Add the path to the standard library.
+        # This is merely for the convenience of the dependency scanner.
+        dmd_path = env.WhereIs(dc)
+        if dmd_path:
+            x = string.rindex(dmd_path, dc)
+            phobosDir = dmd_path[:x] + '/../src/phobos'
+            if os.path.isdir(phobosDir):
+                env.Append(DPATH = [phobosDir])
 
     env['DINCPREFIX'] = '-I'
     env['DINCSUFFIX'] = ''
@@ -198,7 +201,10 @@ def generate(env):
                     except KeyError:
                         libs = []
                     if 'phobos' not in libs:
-                        env.Append(LIBS = ['phobos'])
+                        if dc is 'dmd':
+                            env.Append(LIBS = ['phobos'])
+                        elif dc is 'gdmd':
+                            env.Append(LIBS = ['gphobos'])
                     if 'pthread' not in libs:
                         env.Append(LIBS = ['pthread'])
                     if 'm' not in libs:
@@ -209,4 +215,4 @@ def generate(env):
         env['LINKCOM'] = '$SMART_LINKCOM '
 
 def exists(env):
-    return env.Detect('dmd')
+    return env.Detect(['dmd', 'gdmd'])
index cab3ab47d26d300d80c61bb595a0613543efd328..835a4eb9a26b40b9808ca5f593bd0766b2390012 100644 (file)
@@ -6,7 +6,8 @@ See its __doc__ string for a discussion of the format.
 -->
 <tool name="dmd">
 <summary>
-Sets construction variables for the Digital Mars D compiler.
+Sets construction variables for D language compilers
+(the Digital Mars D compiler, or GDC).
 </summary>
 <sets>
 <!--
index 0576b90f2c0f748ab913641ec692b9983d068129..9c7e4772bfa5680e2b870228ae4e057ec71e1abc 100644 (file)
@@ -63,18 +63,16 @@ def generate(env):
         env['SHOBJSUFFIX'] = '$OBJSUFFIX'
     elif env['PLATFORM'] == 'hpux':
         # Original line from Christian Engel added -DPIC:
-        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC')
-        env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC')
+        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC')
         env['SHOBJSUFFIX'] = '.pic.o'
     elif env['PLATFORM'] == 'sunos':
         # Original line from Christian Engel added -DPIC:
-        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC')
-        env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC')
+        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC')
         env['SHOBJSUFFIX'] = '.pic.o'
     else:
         # Original line from Christian Engel added -DPIC:
-        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC')
-        env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC')
+        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC')
+        pass
     # determine compiler version
     if env['CXX']:
         line = os.popen(env['CXX'] + ' --version').readline()
index 02cc52af3b70d3faa33b653ccd8bc526617dc458..db5d06ec0b9016609f8e9d2970e111ccfcc7ce99 100644 (file)
@@ -375,14 +375,14 @@ def generate(env, version=None, abi=None, topdir=None, verbose=0):
                    'LIB'             : 'lib',
                    'PATH'            : 'bin',
                    'LD_LIBRARY_PATH' : 'lib'}
-            for p in paths:
+            for p in paths.keys():
                 env.PrependENVPath(p, os.path.join(topdir, paths[p]))
         if is_mac:
             paths={'INCLUDE'         : 'include',
                    'LIB'             : 'lib',
                    'PATH'            : 'bin',
                    'LD_LIBRARY_PATH' : 'lib'}
-            for p in paths:
+            for p in paths.keys():
                 env.PrependENVPath(p, os.path.join(topdir, paths[p]))
         if is_windows:
             #       env key    reg valname   default subdir of top
@@ -394,7 +394,7 @@ def generate(env, version=None, abi=None, topdir=None, verbose=0):
             if version is None:
                 version = ''
             # Each path has a registry entry, use that or default to subdir
-            for p in paths:
+            for p in paths.keys():
                 try:
                     path=get_intel_registry_value(p[1], version, abi)
                     # These paths may have $(ICInstallDir)
index a0207272ac394ee0fe2d847b953b9422411eaed2..cb79301e7d0a03d33402366cfc54ef4d274568ef 100644 (file)
@@ -92,8 +92,9 @@ def emit_java_classes(target, source, env):
             raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__)
 
     version = env.get('JAVAVERSION', '1.4')
-    tlist = []
+    full_tlist = []
     for f in slist:
+        tlist = []
         source_file_based = True
         pkg_dir = None
         if not f.is_derived():
@@ -124,7 +125,12 @@ def emit_java_classes(target, source, env):
             t.attributes.java_classname = classname(base)
             tlist.append(t)
 
-    return tlist, slist
+        for t in tlist:
+            t.set_specific_source([f])
+
+        full_tlist.extend(tlist)
+
+    return full_tlist, slist
 
 JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR')
 
index 359c40ffb2f18cdedd4134898e364dbe8230fa1e..8c5e68acb55dab2d27df94904a41e7773e4a9473 100644 (file)
@@ -122,6 +122,7 @@ def generate(env):
     env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
     env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
     env['SHLINKCOM']   = shlib_action
+    env['LDMODULECOM'] = shlib_action
     env.Append(SHLIBEMITTER = [shlib_emitter])
     env['AS'] = 'as'
 
index 048124c91efb8f780d02c5a8575a31a7877e23d5..f3354c198cf7edf5ccb2048d4e54f05cf53e8cdb 100644 (file)
@@ -15,6 +15,7 @@ CXX
 SHCXXFLAGS
 SHLINKFLAGS
 SHLINKCOM
+LDMODULECOM
 AS
 WINDOWSDEFPREFIX
 WINDOWSDEFSUFFIX
index 79cd4abc0319361ccaef5d5627e3d98b9bfab17e..02be24f0639824d3a5ca8c892f90cd7b502b3c06 100644 (file)
@@ -29,7 +29,7 @@ SCons Packaging Tool.
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.Environment
-from SCons.Options import *
+from SCons.Variables import *
 from SCons.Errors import *
 from SCons.Util import is_List, make_path_relative
 from SCons.Warnings import warn, Warning
@@ -217,11 +217,11 @@ def exists(env):
 
 # XXX
 def options(opts):
-    opts.AddOptions(
-        EnumOption( 'PACKAGETYPE',
-                    'the type of package to create.',
-                    None, allowed_values=map( str, __all__ ),
-                    ignorecase=2
+    opts.AddVariables(
+        EnumVariable( 'PACKAGETYPE',
+                     'the type of package to create.',
+                     None, allowed_values=map( str, __all__ ),
+                     ignorecase=2
                   )
     )
 
index eba49a77436cf41a3d3e4cce484d8e8488b843f6..16f938e0c6b863e5d793be700d563baea794dc0f 100644 (file)
@@ -62,7 +62,8 @@ def _swigEmitter(target, source, env):
         if "-python" in flags and "-noproxy" not in flags:
             if mnames is None:
                 mnames = _reModule.findall(open(src).read())
-            target.extend(map(lambda m: m + ".py", mnames))
+            target.extend(map(lambda m, d=target[0].dir:
+                                     d.File(m + ".py"), mnames))
         if "-java" in flags:
             if mnames is None:
                 mnames = _reModule.findall(open(src).read())
index 311c6a8f2622ca236874893f7e6f794d512998ff..af0978644395aa9d98de6a5b2ac279499ddf2f86 100644 (file)
@@ -109,41 +109,6 @@ def updrive(path):
         path = string.upper(drive) + rest
     return path
 
-#
-# Generic convert-to-string functions that abstract away whether or
-# not the Python we're executing has Unicode support.  The wrapper
-# to_String_for_signature() will use a for_signature() method if the
-# specified object has one.
-#
-if hasattr(types, 'UnicodeType'):
-    UnicodeType = types.UnicodeType
-    def to_String(s):
-        if isinstance(s, UserString):
-            t = type(s.data)
-        else:
-            t = type(s)
-        if t is UnicodeType:
-            return unicode(s)
-        else:
-            return str(s)
-else:
-    to_String = str
-
-def to_String_for_signature(obj):
-    try:
-        f = obj.for_signature
-    except AttributeError:
-        return to_String_for_subst(obj)
-    else:
-        return f()
-
-def to_String_for_subst(s):
-    if is_Sequence( s ):
-        return string.join( map(to_String_for_subst, s) )
-    
-    return to_String( s )
-
-
 class CallableComposite(UserList):
     """A simple composite callable class that, when called, will invoke all
     of its contained callables with the same arguments."""
@@ -213,7 +178,17 @@ class DisplayEngine:
 
     def print_it(self, text, append_newline=1):
         if append_newline: text = text + '\n'
-        sys.stdout.write(text)
+        try:
+            sys.stdout.write(text)
+        except IOError:
+            # Stdout might be connected to a pipe that has been closed
+            # by now. The most likely reason for the pipe being closed
+            # is that the user has press ctrl-c. It this is the case,
+            # then SCons is currently shutdown. We therefore ignore
+            # IOError's here so that SCons can continue and shutdown
+            # properly so that the .sconsign is correctly written
+            # before SCons exits.
+            pass
 
     def dont_print(self, text, append_newline=1):
         pass
@@ -437,6 +412,41 @@ except TypeError:
             else:
                 flatten_sequence(item, result)
         return result
+
+    #
+    # Generic convert-to-string functions that abstract away whether or
+    # not the Python we're executing has Unicode support.  The wrapper
+    # to_String_for_signature() will use a for_signature() method if the
+    # specified object has one.
+    #
+    if hasattr(types, 'UnicodeType'):
+        UnicodeType = types.UnicodeType
+        def to_String(s):
+            if isinstance(s, UserString):
+                t = type(s.data)
+            else:
+                t = type(s)
+            if t is UnicodeType:
+                return unicode(s)
+            else:
+                return str(s)
+    else:
+        to_String = str
+
+    def to_String_for_signature(obj):
+        try:
+            f = obj.for_signature
+        except AttributeError:
+            return to_String_for_subst(obj)
+        else:
+            return f()
+
+    def to_String_for_subst(s):
+        if is_Sequence( s ):
+            return string.join( map(to_String_for_subst, s) )
+
+        return to_String( s )
+
 else:
     # A modern Python version with new-style classes, so we can just use
     # isinstance().
@@ -459,6 +469,10 @@ else:
     # with basestring. (at least on Python 2.5.1)
     StringTypes = (str, unicode, UserString)
 
+    # Empirically, it is faster to check explicitely for str and
+    # unicode than for basestring.
+    BaseStringTypes = (str, unicode)
+
     def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
         return isinstance(obj, DictTypes)
 
@@ -526,6 +540,56 @@ else:
         return result
 
 
+    #
+    # Generic convert-to-string functions that abstract away whether or
+    # not the Python we're executing has Unicode support.  The wrapper
+    # to_String_for_signature() will use a for_signature() method if the
+    # specified object has one.
+    #
+    def to_String(s, 
+                  isinstance=isinstance, str=str,
+                  UserString=UserString, BaseStringTypes=BaseStringTypes):
+        if isinstance(s,BaseStringTypes):
+            # Early out when already a string!
+            return s
+        elif isinstance(s, UserString):
+            # s.data can only be either a unicode or a regular
+            # string. Please see the UserString initializer.
+            return s.data
+        else:
+            return str(s)
+
+    def to_String_for_subst(s, 
+                            isinstance=isinstance, join=string.join, str=str, to_String=to_String,
+                            BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes,
+                            UserString=UserString):
+                            
+        # Note that the test cases are sorted by order of probability.
+        if isinstance(s, BaseStringTypes):
+            return s
+        elif isinstance(s, SequenceTypes):
+            l = []
+            for e in s:
+                l.append(to_String_for_subst(e))
+            return join( s )
+        elif isinstance(s, UserString):
+            # s.data can only be either a unicode or a regular
+            # string. Please see the UserString initializer.
+            return s.data
+        else:
+            return str(s)
+
+    def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst, 
+                                AttributeError=AttributeError):
+        try:
+            f = obj.for_signature
+        except AttributeError:
+            return to_String_for_subst(obj)
+        else:
+            return f()
+
+
+
 # The SCons "semi-deep" copy.
 #
 # This makes separate copies of lists (including UserList objects)
@@ -1267,8 +1331,18 @@ class Unbuffered:
     def __init__(self, file):
         self.file = file
     def write(self, arg):
-        self.file.write(arg)
-        self.file.flush()
+        try:
+            self.file.write(arg)
+            self.file.flush()
+        except IOError:
+            # Stdout might be connected to a pipe that has been closed
+            # by now. The most likely reason for the pipe being closed
+            # is that the user has press ctrl-c. It this is the case,
+            # then SCons is currently shutdown. We therefore ignore
+            # IOError's here so that SCons can continue and shutdown
+            # properly so that the .sconsign is correctly written
+            # before SCons exits.
+            pass
     def __getattr__(self, attr):
         return getattr(self.file, attr)
 
similarity index 89%
rename from src/engine/SCons/Options/BoolOption.py
rename to src/engine/SCons/Variables/BoolVariable.py
index 80b607d437a56cf2d1a6ab17fa0a40a79c9e1d0e..92936e6d4556b0c877d1d6cfe2547fa7a08ff79c 100644 (file)
@@ -1,11 +1,11 @@
-"""engine.SCons.Options.BoolOption
+"""engine.SCons.Variables.BoolVariable
 
 This file defines the option type for SCons implementing true/false values.
 
 Usage example:
 
-  opts = Options()
-  opts.Add(BoolOption('embedded', 'build for an embedded system', 0))
+  opts = Variables()
+  opts.Add(BoolVariable('embedded', 'build for an embedded system', 0))
   ...
   if env['embedded'] == 1:
     ...
@@ -36,7 +36,7 @@ Usage example:
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-__all__ = ('BoolOption')
+__all__ = ['BoolVariable',]
 
 import string
 
@@ -56,7 +56,7 @@ def _text2bool(val):
     See '__true_strings' and '__false_strings' for values considered
     'true' or 'false respectivly.
 
-    This is usable as 'converter' for SCons' Options.
+    This is usable as 'converter' for SCons' Variables.
     """
     lval = string.lower(val)
     if lval in __true_strings: return True
@@ -68,14 +68,14 @@ def _validator(key, val, env):
     """
     Validates the given value to be either '0' or '1'.
     
-    This is usable as 'validator' for SCons' Options.
+    This is usable as 'validator' for SCons' Variables.
     """
     if not env[key] in (True, False):
         raise SCons.Errors.UserError(
             'Invalid value for boolean option %s: %s' % (key, env[key]))
 
 
-def BoolOption(key, help, default):
+def BoolVariable(key, help, default):
     """
     The input parameters describe a boolen option, thus they are
     returned with the correct converter and validator appended. The
similarity index 82%
rename from src/engine/SCons/Options/BoolOptionTests.py
rename to src/engine/SCons/Variables/BoolVariableTests.py
index 07b5b7952b72e317a6f1d49b735692fb1ec76141..feb4e4eb8e47f44a618b3d69f42c8a254df5962d 100644 (file)
@@ -29,13 +29,13 @@ import sys
 import unittest
 
 import SCons.Errors
-import SCons.Options
+import SCons.Variables
 
-class BoolOptionTestCase(unittest.TestCase):
-    def test_BoolOption(self):
-        """Test BoolOption creation"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.BoolOption('test', 'test option help', 0))
+class BoolVariableTestCase(unittest.TestCase):
+    def test_BoolVariable(self):
+        """Test BoolVariable creation"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
 
         o = opts.options[0]
         assert o.key == 'test', o.key
@@ -45,9 +45,9 @@ class BoolOptionTestCase(unittest.TestCase):
         assert not o.converter is None, o.converter
 
     def test_converter(self):
-        """Test the BoolOption converter"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.BoolOption('test', 'test option help', 0))
+        """Test the BoolVariable converter"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
 
         o = opts.options[0]
 
@@ -86,9 +86,9 @@ class BoolOptionTestCase(unittest.TestCase):
         assert caught, "did not catch expected ValueError"
 
     def test_validator(self):
-        """Test the BoolOption validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.BoolOption('test', 'test option help', 0))
+        """Test the BoolVariable validator"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
 
         o = opts.options[0]
 
@@ -118,6 +118,6 @@ class BoolOptionTestCase(unittest.TestCase):
 
 
 if __name__ == "__main__":
-    suite = unittest.makeSuite(BoolOptionTestCase, 'test_')
+    suite = unittest.makeSuite(BoolVariableTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
similarity index 91%
rename from src/engine/SCons/Options/EnumOption.py
rename to src/engine/SCons/Variables/EnumVariable.py
index d4e2ac1934993c6ce8026d9e2a872f274e263424..e1b20d2c5b70ac2d0ab8cb3d73d9bbf1a105bdfd 100644 (file)
@@ -1,12 +1,12 @@
-"""engine.SCons.Options.EnumOption
+"""engine.SCons.Variables.EnumVariable
 
 This file defines the option type for SCons allowing only specified
 input-values.
 
 Usage example:
 
-  opts = Options()
-  opts.Add(EnumOption('debug', 'debug output and symbols', 'no',
+  opts = Variables()
+  opts.Add(EnumVariable('debug', 'debug output and symbols', 'no',
                       allowed_values=('yes', 'no', 'full'),
                       map={}, ignorecase=2))
   ...
@@ -39,7 +39,7 @@ Usage example:
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-__all__ = ('EnumOption',)
+__all__ = ['EnumVariable',]
 
 import string
 
@@ -51,14 +51,14 @@ def _validator(key, val, env, vals):
             'Invalid value for option %s: %s' % (key, val))
 
 
-def EnumOption(key, help, default, allowed_values, map={}, ignorecase=0):
+def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0):
     """
     The input parameters describe a option with only certain values
     allowed. They are returned with an appropriate converter and
     validator appended. The result is usable for input to
-    Options.Add().
+    Variables.Add().
 
-    'key' and 'default' are the values to be passed on to Options.Add().
+    'key' and 'default' are the values to be passed on to Variables.Add().
 
     'help' will be appended by the allowed values automatically
 
similarity index 85%
rename from src/engine/SCons/Options/EnumOptionTests.py
rename to src/engine/SCons/Variables/EnumVariableTests.py
index 20ae6c35e090df7134227a0cd5073a426a5d9eb1..0f9afdec21288b3f0dd27ea83288e2a3b56c12ac 100644 (file)
@@ -27,13 +27,13 @@ import sys
 import unittest
 
 import SCons.Errors
-import SCons.Options
+import SCons.Variables
 
-class EnumOptionTestCase(unittest.TestCase):
-    def test_EnumOption(self):
-        """Test EnumOption creation"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.EnumOption('test', 'test option help', 0,
+class EnumVariableTestCase(unittest.TestCase):
+    def test_EnumVariable(self):
+        """Test EnumVariable creation"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {}))
 
@@ -45,9 +45,9 @@ class EnumOptionTestCase(unittest.TestCase):
         assert not o.converter is None, o.converter
 
     def test_converter(self):
-        """Test the EnumOption converter"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.EnumOption('test', 'test option help', 0,
+        """Test the EnumVariable converter"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0,
                                           ['one', 'two', 'three']))
 
         o = opts.options[0]
@@ -56,8 +56,8 @@ class EnumOptionTestCase(unittest.TestCase):
             x = o.converter(a)
             assert x == a, x
 
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.EnumOption('test', 'test option help', 0,
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.EnumVariable('test', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'1' : 'one',
                                            '2' : 'two',
@@ -80,20 +80,20 @@ class EnumOptionTestCase(unittest.TestCase):
         x = o.converter('3')
         assert x == 'three', x
 
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.EnumOption('test0', 'test option help', 0,
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.EnumVariable('test0', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'a' : 'one',
                                            'b' : 'two',
                                            'c' : 'three'},
                                           ignorecase=0))
-        opts.Add(SCons.Options.EnumOption('test1', 'test option help', 0,
+        opts.Add(SCons.Variables.EnumVariable('test1', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'a' : 'one',
                                            'b' : 'two',
                                            'c' : 'three'},
                                           ignorecase=1))
-        opts.Add(SCons.Options.EnumOption('test2', 'test option help', 0,
+        opts.Add(SCons.Variables.EnumVariable('test2', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'a' : 'one',
                                            'b' : 'two',
@@ -131,21 +131,21 @@ class EnumOptionTestCase(unittest.TestCase):
             assert x == l[2], "o2 got %s, expected %s" % (x, l[2])
 
     def test_validator(self):
-        """Test the EnumOption validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.EnumOption('test0', 'test option help', 0,
+        """Test the EnumVariable validator"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.EnumVariable('test0', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'a' : 'one',
                                            'b' : 'two',
                                            'c' : 'three'},
                                           ignorecase=0))
-        opts.Add(SCons.Options.EnumOption('test1', 'test option help', 0,
+        opts.Add(SCons.Variables.EnumVariable('test1', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'a' : 'one',
                                            'b' : 'two',
                                            'c' : 'three'},
                                           ignorecase=1))
-        opts.Add(SCons.Options.EnumOption('test2', 'test option help', 0,
+        opts.Add(SCons.Variables.EnumVariable('test2', 'test option help', 0,
                                           ['one', 'two', 'three'],
                                           {'a' : 'one',
                                            'b' : 'two',
@@ -193,6 +193,6 @@ class EnumOptionTestCase(unittest.TestCase):
 
 
 if __name__ == "__main__":
-    suite = unittest.makeSuite(EnumOptionTestCase, 'test_')
+    suite = unittest.makeSuite(EnumVariableTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
similarity index 94%
rename from src/engine/SCons/Options/ListOption.py
rename to src/engine/SCons/Variables/ListVariable.py
index 69549055c209c720c2da75744dec8e4955060765..37e30944abe3c84424bea291c8e10d024f458896 100644 (file)
@@ -1,4 +1,4 @@
-"""engine.SCons.Options.ListOption
+"""engine.SCons.Variables.ListVariable
 
 This file defines the option type for SCons implementing 'lists'.
 
@@ -11,8 +11,8 @@ Usage example:
 
   list_of_libs = Split('x11 gl qt ical')
 
-  opts = Options()
-  opts.Add(ListOption('shared',
+  opts = Variables()
+  opts.Add(ListVariable('shared',
                       'libraries to build as shared libraries',
                       'all',
                       elems = list_of_libs))
@@ -52,7 +52,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 # Know Bug: This should behave like a Set-Type, but does not really,
 # since elements can occur twice.
 
-__all__ = ('ListOption',)
+__all__ = ['ListVariable',]
 
 import string
 import UserList
@@ -60,7 +60,7 @@ import UserList
 import SCons.Util
 
 
-class _ListOption(UserList.UserList):
+class _ListVariable(UserList.UserList):
     def __init__(self, initlist=[], allowedElems=[]):
         UserList.UserList.__init__(self, filter(None, initlist))
         self.allowedElems = allowedElems[:]
@@ -103,7 +103,7 @@ def _converter(val, allowedElems, mapdict):
         if notAllowed:
             raise ValueError("Invalid value(s) for option: %s" %
                              string.join(notAllowed, ','))
-    return _ListOption(val, allowedElems)
+    return _ListVariable(val, allowedElems)
 
 
 ## def _validator(key, val, env):
@@ -113,7 +113,7 @@ def _converter(val, allowedElems, mapdict):
 ##     return 1
 
 
-def ListOption(key, help, default, names, map={}):
+def ListVariable(key, help, default, names, map={}):
     """
     The input parameters describe a 'package list' option, thus they
     are returned with the correct converter and validater appended. The
similarity index 81%
rename from src/engine/SCons/Options/ListOptionTests.py
rename to src/engine/SCons/Variables/ListVariableTests.py
index 22378bcfbc81239ae014ec3b5ed2fa5993dc9755..9363f6d541f4d95dcef36c311fbd39036f14db83 100644 (file)
@@ -28,13 +28,13 @@ import sys
 import unittest
 
 import SCons.Errors
-import SCons.Options
+import SCons.Variables
 
-class ListOptionTestCase(unittest.TestCase):
-    def test_ListOption(self):
-        """Test ListOption creation"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.ListOption('test', 'test option help', 'all',
+class ListVariableTestCase(unittest.TestCase):
+    def test_ListVariable(self):
+        """Test ListVariable creation"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all',
                                           ['one', 'two', 'three']))
 
         o = opts.options[0]
@@ -44,8 +44,8 @@ class ListOptionTestCase(unittest.TestCase):
         assert o.validator is None, o.validator
         assert not o.converter is None, o.converter
 
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.ListOption('test2', 'test2 help',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.ListVariable('test2', 'test2 help',
                                           ['one', 'three'],
                                           ['one', 'two', 'three']))
 
@@ -53,9 +53,9 @@ class ListOptionTestCase(unittest.TestCase):
         assert o.default == 'one,three'
 
     def test_converter(self):
-        """Test the ListOption converter"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.ListOption('test', 'test option help', 'all',
+        """Test the ListVariable converter"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all',
                                           ['one', 'two', 'three'],
                                           {'ONE':'one', 'TWO':'two'}))
 
@@ -112,9 +112,9 @@ class ListOptionTestCase(unittest.TestCase):
         assert caught, "did not catch expected ValueError"
 
     def test_copy(self):
-        """Test copying a ListOption like an Environment would"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.ListOption('test', 'test option help', 'all',
+        """Test copying a ListVariable like an Environment would"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all',
                                           ['one', 'two', 'three']))
 
         o = opts.options[0]
@@ -123,6 +123,6 @@ class ListOptionTestCase(unittest.TestCase):
         n = l.__class__(copy.copy(l))
 
 if __name__ == "__main__":
-    suite = unittest.makeSuite(ListOptionTestCase, 'test_')
+    suite = unittest.makeSuite(ListVariableTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
similarity index 94%
rename from src/engine/SCons/Options/PackageOption.py
rename to src/engine/SCons/Variables/PackageVariable.py
index 3b4f0cec8a841a90fcb999511351ad4baf45fcf8..6be260d88579f763151015316886ac2c5ecf0a2f 100644 (file)
@@ -1,4 +1,4 @@
-"""engine.SCons.Options.PackageOption
+"""engine.SCons.Variables.PackageVariable
 
 This file defines the option type for SCons implementing 'package
 activation'.
@@ -15,8 +15,8 @@ Usage example:
 
   To replace autoconf's --with-xxx=yyy 
 
-  opts = Options()
-  opts.Add(PackageOption('x11',
+  opts = Variables()
+  opts.Add(PackageVariable('x11',
                          'use X11 installed here (yes = search some places',
                          'yes'))
   ...
@@ -52,7 +52,7 @@ Usage example:
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-__all__ = ('PackageOption')
+__all__ = ['PackageVariable',]
 
 import string
 
@@ -86,7 +86,7 @@ def _validator(key, val, env, searchfunc):
             'Path does not exist for option %s: %s' % (key, val))
 
 
-def PackageOption(key, help, default, searchfunc=None):
+def PackageVariable(key, help, default, searchfunc=None):
     # NB: searchfunc is currenty undocumented and unsupported
     """
     The input parameters describe a 'package list' option, thus they
similarity index 81%
rename from src/engine/SCons/Options/PackageOptionTests.py
rename to src/engine/SCons/Variables/PackageVariableTests.py
index 68f14e5d73ab94e5d0d515eed0a61c59c136d493..54116cf9c26bf0772cd4b12119783645447c18cc 100644 (file)
@@ -29,15 +29,15 @@ import sys
 import unittest
 
 import SCons.Errors
-import SCons.Options
+import SCons.Variables
 
 import TestCmd
 
-class PackageOptionTestCase(unittest.TestCase):
-    def test_PackageOption(self):
-        """Test PackageOption creation"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path'))
+class PackageVariableTestCase(unittest.TestCase):
+    def test_PackageVariable(self):
+        """Test PackageVariable creation"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path'))
 
         o = opts.options[0]
         assert o.key == 'test', o.key
@@ -47,9 +47,9 @@ class PackageOptionTestCase(unittest.TestCase):
         assert not o.converter is None, o.converter
 
     def test_converter(self):
-        """Test the PackageOption converter"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path'))
+        """Test the PackageVariable converter"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path'))
 
         o = opts.options[0]
 
@@ -80,7 +80,7 @@ class PackageOptionTestCase(unittest.TestCase):
 
         # Make sure the converter returns True if we give it str(True) and
         # False when we give it str(False).  This assures consistent operation
-        # through a cycle of Options.Save(<file>) -> Options(<file>).
+        # through a cycle of Variables.Save(<file>) -> Variables(<file>).
         x = o.converter(str(True))
         assert x == True, "converter returned a string when given str(True)"
 
@@ -88,9 +88,9 @@ class PackageOptionTestCase(unittest.TestCase):
         assert x == False, "converter returned a string when given str(False)"
 
     def test_validator(self):
-        """Test the PackageOption validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path'))
+        """Test the PackageVariable validator"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PackageVariable('test', 'test option help', '/default/path'))
 
         test = TestCmd.TestCmd(workdir='')
         test.write('exists', 'exists\n')
@@ -115,6 +115,6 @@ class PackageOptionTestCase(unittest.TestCase):
 
 
 if __name__ == "__main__":
-    suite = unittest.makeSuite(PackageOptionTestCase, 'test_')
+    suite = unittest.makeSuite(PackageVariableTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
similarity index 94%
rename from src/engine/SCons/Options/PathOption.py
rename to src/engine/SCons/Variables/PathVariable.py
index 995eeb982ee054677856d7da3e4d5e2e92291d58..e31de961e22a44b40fa32e4dce1df35ab2a16ac3 100644 (file)
@@ -1,10 +1,10 @@
-"""SCons.Options.PathOption
+"""SCons.Variables.PathVariable
 
 This file defines an option type for SCons implementing path settings.
 
 To be used whenever a a user-specified path override should be allowed.
 
-Arguments to PathOption are:
+Arguments to PathVariable are:
   option-name  = name of this option on the command line (e.g. "prefix")
   option-help  = help string for option
   option-dflt  = default value for this option
@@ -30,16 +30,16 @@ Usage example:
   Examples:
       prefix=/usr/local
 
-  opts = Options()
+  opts = Variables()
 
-  opts = Options()
-  opts.Add(PathOption('qtdir',
+  opts = Variables()
+  opts.Add(PathVariable('qtdir',
                       'where the root of Qt is installed',
                       qtdir, PathIsDir))
-  opts.Add(PathOption('qt_includes',
+  opts.Add(PathVariable('qt_includes',
                       'where the Qt includes are installed',
                       '$qtdir/includes', PathIsDirCreate))
-  opts.Add(PathOption('qt_libraries',
+  opts.Add(PathVariable('qt_libraries',
                       'where the Qt library is installed',
                       '$qtdir/lib'))
 
@@ -70,12 +70,14 @@ Usage example:
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+__all__ = ['PathVariable',]
+
 import os
 import os.path
 
 import SCons.Errors
 
-class _PathOptionClass:
+class _PathVariableClass:
 
     def PathAccept(self, key, val, env):
         """Accepts any path, no checking done."""
@@ -136,4 +138,4 @@ class _PathOptionClass:
             return (key, '%s ( /path/to/%s )' % (help, key), default,
                     validator, None)
 
-PathOption = _PathOptionClass()
+PathVariable = _PathVariableClass()
similarity index 82%
rename from src/engine/SCons/Options/PathOptionTests.py
rename to src/engine/SCons/Variables/PathVariableTests.py
index 22e79ae8d161967a034b92804d34af116c40b9c3..980cc5ed2cb57b7e485ea1a3a22b3cd9680fbd6c 100644 (file)
@@ -28,15 +28,15 @@ import sys
 import unittest
 
 import SCons.Errors
-import SCons.Options
+import SCons.Variables
 
 import TestCmd
 
-class PathOptionTestCase(unittest.TestCase):
-    def test_PathOption(self):
-        """Test PathOption creation"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+class PathVariableTestCase(unittest.TestCase):
+    def test_PathVariable(self):
+        """Test PathVariable creation"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path'))
 
@@ -49,11 +49,11 @@ class PathOptionTestCase(unittest.TestCase):
 
     def test_PathExists(self):
         """Test the PathExists validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path',
-                                          SCons.Options.PathOption.PathExists))
+                                          SCons.Variables.PathVariable.PathExists))
 
         test = TestCmd.TestCmd(workdir='')
         test.write('exists', 'exists\n')
@@ -72,11 +72,11 @@ class PathOptionTestCase(unittest.TestCase):
 
     def test_PathIsDir(self):
         """Test the PathIsDir validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path',
-                                          SCons.Options.PathOption.PathIsDir))
+                                          SCons.Variables.PathVariable.PathIsDir))
 
         test = TestCmd.TestCmd(workdir='')
         test.subdir('dir')
@@ -104,11 +104,11 @@ class PathOptionTestCase(unittest.TestCase):
 
     def test_PathIsDirCreate(self):
         """Test the PathIsDirCreate validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path',
-                                          SCons.Options.PathOption.PathIsDirCreate))
+                                          SCons.Variables.PathVariable.PathIsDirCreate))
 
         test = TestCmd.TestCmd(workdir='')
         test.write('file', "file\n")
@@ -129,11 +129,11 @@ class PathOptionTestCase(unittest.TestCase):
 
     def test_PathIsFile(self):
         """Test the PathIsFile validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path',
-                                          SCons.Options.PathOption.PathIsFile))
+                                          SCons.Variables.PathVariable.PathIsFile))
 
         test = TestCmd.TestCmd(workdir='')
         test.subdir('dir')
@@ -161,11 +161,11 @@ class PathOptionTestCase(unittest.TestCase):
 
     def test_PathAccept(self):
         """Test the PathAccept validator"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path',
-                                          SCons.Options.PathOption.PathAccept))
+                                          SCons.Variables.PathVariable.PathAccept))
 
         test = TestCmd.TestCmd(workdir='')
         test.subdir('dir')
@@ -182,9 +182,9 @@ class PathOptionTestCase(unittest.TestCase):
         o.validator('X', dne, {})
 
     def test_validator(self):
-        """Test the PathOption validator argument"""
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test',
+        """Test the PathVariable validator argument"""
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test',
                                           'test option help',
                                           '/default/path'))
 
@@ -207,8 +207,8 @@ class PathOptionTestCase(unittest.TestCase):
         def my_validator(key, val, env):
             raise Exception, "my_validator() got called for %s, %s!" % (key, val)
 
-        opts = SCons.Options.Options()
-        opts.Add(SCons.Options.PathOption('test2',
+        opts = SCons.Variables.Variables()
+        opts.Add(SCons.Variables.PathVariable('test2',
                                           'more help',
                                           '/default/path/again',
                                           my_validator))
@@ -226,6 +226,6 @@ class PathOptionTestCase(unittest.TestCase):
 
 
 if __name__ == "__main__":
-    suite = unittest.makeSuite(PathOptionTestCase, 'test_')
+    suite = unittest.makeSuite(PathVariableTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
similarity index 91%
rename from src/engine/SCons/Options/OptionsTests.py
rename to src/engine/SCons/Variables/VariablesTests.py
index 5ec9d7a01cbed30c9595f7ac66cc258b4b01cad1..cd6ab1d03e54a2d86873e7d2e7225b89f2b8f1d0 100644 (file)
@@ -28,7 +28,7 @@ import sys
 import unittest
 import TestSCons
 
-import SCons.Options
+import SCons.Variables
 import SCons.Subst
 import SCons.Warnings
 
@@ -57,11 +57,11 @@ def checkSave(file, expected):
     execfile(file, gdict, ldict)
     assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
 
-class OptionsTestCase(unittest.TestCase):
+class VariablesTestCase(unittest.TestCase):
 
     def test_keys(self):
-        """Test the Options.keys() method"""
-        opts = SCons.Options.Options()
+        """Test the Variables.keys() method"""
+        opts = SCons.Variables.Variables()
 
         opts.Add('VAR1')
         opts.Add('VAR2',
@@ -73,8 +73,8 @@ class OptionsTestCase(unittest.TestCase):
         assert keys == ['VAR1', 'VAR2'], keys
 
     def test_Add(self):
-        """Test adding to an Options object"""
-        opts = SCons.Options.Options()
+        """Test adding to a Variables object"""
+        opts = SCons.Variables.Variables()
 
         opts.Add('VAR')
         opts.Add('ANSWER',
@@ -107,11 +107,11 @@ class OptionsTestCase(unittest.TestCase):
         test_it('foo-bar')
         test_it('foo.bar')
 
-    def test_AddOptions(self):
-        """Test adding a list of options to an Options object"""
-        opts = SCons.Options.Options()
+    def test_AddVariables(self):
+        """Test adding a list of options to a Variables object"""
+        opts = SCons.Variables.Variables()
 
-        opts.AddOptions(('VAR2',),
+        opts.AddVariables(('VAR2',),
                         ('ANSWER2',
                          'THE answer to THE question',
                          "42",
@@ -137,7 +137,7 @@ class OptionsTestCase(unittest.TestCase):
         # Test that a default value is validated correctly.
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -158,7 +158,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=54')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -186,7 +186,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=42')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -207,7 +207,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=10')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -228,7 +228,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=10')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -246,7 +246,7 @@ class OptionsTestCase(unittest.TestCase):
         # or args.
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
         
         opts.Add('ANSWER',
                  help='THE answer to THE question',
@@ -259,7 +259,7 @@ class OptionsTestCase(unittest.TestCase):
         # Test that a default value of None is all right.
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
-        opts = SCons.Options.Options(file)
+        opts = SCons.Variables.Variables(file)
 
         opts.Add('ANSWER',
                  "This is the answer",
@@ -278,7 +278,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=42')
-        opts = SCons.Options.Options(file, {'ANSWER':54})
+        opts = SCons.Variables.Variables(file, {'ANSWER':54})
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -298,7 +298,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=54')
-        opts = SCons.Options.Options(file, {'ANSWER':42})
+        opts = SCons.Variables.Variables(file, {'ANSWER':42})
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -315,7 +315,7 @@ class OptionsTestCase(unittest.TestCase):
         test = TestSCons.TestSCons()
         file = test.workpath('custom.py')
         test.write('custom.py', 'ANSWER=54')
-        opts = SCons.Options.Options(file, {'ANSWER':54})
+        opts = SCons.Variables.Variables(file, {'ANSWER':54})
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -328,11 +328,11 @@ class OptionsTestCase(unittest.TestCase):
         assert env['ANSWER'] == 54
 
     def test_Save(self):
-        """Testing saving Options"""
+        """Testing saving Variables"""
 
         test = TestSCons.TestSCons()
         cache_file = test.workpath('cached.options')
-        opts = SCons.Options.Options()
+        opts = SCons.Variables.Variables()
 
         def bool_converter(val):
             if val in [1, 'y']: val = 1
@@ -387,7 +387,7 @@ class OptionsTestCase(unittest.TestCase):
             
         test = TestSCons.TestSCons()
         cache_file = test.workpath('cached.options')
-        opts = SCons.Options.Options()
+        opts = SCons.Variables.Variables()
         
         opts.Add('THIS_USED_TO_BREAK',
                  'An option to test',
@@ -412,7 +412,7 @@ class OptionsTestCase(unittest.TestCase):
 
     def test_GenerateHelpText(self):
         """Test generating the default format help text"""
-        opts = SCons.Options.Options()
+        opts = SCons.Variables.Variables()
 
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -468,14 +468,14 @@ B: b - alpha test
         text = opts.GenerateHelpText(env, sort=cmp)
         assert text == expectAlpha, text
 
-    def test_FormatOptionHelpText(self):
+    def test_FormatVariableHelpText(self):
         """Test generating custom format help text"""
-        opts = SCons.Options.Options()
+        opts = SCons.Variables.Variables()
 
         def my_format(env, opt, help, default, actual, aliases):
             return '%s %s %s %s %s\n' % (opt, default, actual, help, aliases)
 
-        opts.FormatOptionHelpText = my_format
+        opts.FormatVariableHelpText = my_format
 
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -517,11 +517,11 @@ B 42 54 b - alpha test ['B']
 
 
 
-class UnknownOptionsTestCase(unittest.TestCase):
+class UnknownVariablesTestCase(unittest.TestCase):
 
     def test_unknown(self):
-        """Test the UnknownOptions() method"""
-        opts = SCons.Options.Options()
+        """Test the UnknownVariables() method"""
+        opts = SCons.Variables.Variables()
         
         opts.Add('ANSWER',
                  'THE answer to THE question',
@@ -535,7 +535,7 @@ class UnknownOptionsTestCase(unittest.TestCase):
         env = Environment()
         opts.Update(env, args)
 
-        r = opts.UnknownOptions()
+        r = opts.UnknownVariables()
         assert r == {'UNKNOWN' : 'unknown'}, r
         assert env['ANSWER'] == 'answer', env['ANSWER']
 
@@ -543,8 +543,8 @@ class UnknownOptionsTestCase(unittest.TestCase):
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()
-    tclasses = [ OptionsTestCase,
-                 UnknownOptionsTestCase ]
+    tclasses = [ VariablesTestCase,
+                 UnknownVariablesTestCase ]
     for tclass in tclasses:
         names = unittest.getTestCaseNames(tclass, 'test_')
         suite.addTests(map(tclass, names))
similarity index 91%
rename from src/engine/SCons/Options/__init__.py
rename to src/engine/SCons/Variables/__init__.py
index 9389c7b7b56ae5310a9c1216fb2ce3de2167216d..a86cd34b65e88f52b5575e52d17bbf8a40af6460 100644 (file)
@@ -1,6 +1,6 @@
-"""engine.SCons.Options
+"""engine.SCons.Variables
 
-This file defines the Options class that is used to add user-friendly
+This file defines the Variables class that is used to add user-friendly
 customizable variables to an SCons build.
 """
 
@@ -40,14 +40,14 @@ import SCons.Errors
 import SCons.Util
 import SCons.Warnings
 
-from BoolOption import BoolOption  # okay
-from EnumOption import EnumOption  # okay
-from ListOption import ListOption  # naja
-from PackageOption import PackageOption # naja
-from PathOption import PathOption # okay
+from BoolVariable import BoolVariable  # okay
+from EnumVariable import EnumVariable  # okay
+from ListVariable import ListVariable  # naja
+from PackageVariable import PackageVariable # naja
+from PathVariable import PathVariable # okay
 
 
-class Options:
+class Variables:
     instance=None
 
     """
@@ -72,16 +72,16 @@ class Options:
 
         # create the singleton instance
         if is_global:
-            self=Options.instance
+            self=Variables.instance
 
-            if not Options.instance:
-                Options.instance=self
+            if not Variables.instance:
+                Variables.instance=self
 
     def _do_add(self, key, help="", default=None, validator=None, converter=None):
-        class Option:
+        class Variable:
             pass
 
-        option = Option()
+        option = Variable()
 
         # if we get a list or a tuple, we take the first element as the
         # option key and store the remaining in aliases.
@@ -123,11 +123,11 @@ class Options:
 
         if not SCons.Util.is_String(key) or \
            not SCons.Environment.is_valid_construction_var(key):
-            raise SCons.Errors.UserError, "Illegal Options.Add() key `%s'" % str(key)
+            raise SCons.Errors.UserError, "Illegal Variables.Add() key `%s'" % str(key)
 
         self._do_add(key, help, default, validator, converter)
 
-    def AddOptions(self, *optlist):
+    def AddVariables(self, *optlist):
         """
         Add a list of options.
 
@@ -135,7 +135,7 @@ class Options:
         to the underlying method for adding options.
 
         Example:
-          opt.AddOptions(
+          opt.AddVariables(
             ('debug', '', 0),
             ('CC', 'The C compiler'),
             ('VALIDATE', 'An option for testing validation', 'notset',
@@ -213,7 +213,7 @@ class Options:
             if option.validator and values.has_key(option.key):
                 option.validator(option.key, env.subst('${%s}'%option.key), env)
 
-    def UnknownOptions(self):
+    def UnknownVariables(self):
         """
         Returns any options in the specified arguments lists that
         were not known, declared options in this object.
@@ -288,7 +288,7 @@ class Options:
                 actual = env.subst('${%s}' % opt.key)
             else:
                 actual = None
-            return self.FormatOptionHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases)
+            return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases)
         lines = filter(None, map(format, options))
 
         return string.join(lines, '')
@@ -296,7 +296,7 @@ class Options:
     format  = '\n%s: %s\n    default: %s\n    actual: %s\n'
     format_ = '\n%s: %s\n    default: %s\n    actual: %s\n    aliases: %s\n'
 
-    def FormatOptionHelpText(self, env, key, help, default, actual, aliases=[]):
+    def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]):
         # Don't display the key name itself as an alias.
         aliases = filter(lambda a, k=key: a != k, aliases)
         if len(aliases)==0:
index 1d36dbc56e373984952156737a9a7ff36f693d04..20fb8ede5ac1cc9669fb8294cc19ce8491f0debf 100644 (file)
@@ -138,7 +138,12 @@ except AttributeError:
         return result
     fnmatch.filter = filter
     del filter
-   
+
+try:
+    import itertools
+except ImportError:
+    # Pre-2.3 Python has no itertools module.
+    import_as('_scons_itertools', 'itertools')
 
 # If we need the compatibility version of textwrap, it  must be imported
 # before optparse, which uses it.
diff --git a/src/engine/SCons/compat/_scons_itertools.py b/src/engine/SCons/compat/_scons_itertools.py
new file mode 100644 (file)
index 0000000..9720daf
--- /dev/null
@@ -0,0 +1,118 @@
+#
+# __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__"
+
+__doc__ = """
+Implementations of itertools functions for Python versions that don't
+have iterators.
+
+These implement the functions by creating the entire list, not returning
+it element-by-element as the real itertools functions do.  This means
+that early Python versions won't get the performance benefit of using
+the itertools, but we can still use them so the later Python versions
+do get the advantages of using iterators.
+
+Because we return the entire list, we intentionally do not implement the
+itertools functions that "return" infinitely-long lists: the count(),
+cycle() and repeat() functions.  Other functions below have remained
+unimplemented simply because they aren't being used (yet) and it wasn't
+obvious how to do it.  Or, conversely, we only implemented those functions
+that *were* easy to implement (mostly because the Python documentation
+contained examples of equivalent code).
+
+Note that these do not have independent unit tests, so it's possible
+that there are bugs.
+"""
+
+def chain(*iterables):
+    result = []
+    for x in iterables:
+        result.extend(list(x))
+    return result
+
+def count(n=0):
+    # returns infinite length, should not be supported
+    raise NotImplementedError
+
+def cycle(iterable):
+    # returns infinite length, should not be supported
+    raise NotImplementedError
+
+def dropwhile(predicate, iterable):
+    result = []
+    for x in iterable:
+        if not predicate(x):
+            result.append(x)
+            break
+    result.extend(iterable)
+    return result
+
+def groupby(iterable, *args):
+    raise NotImplementedError
+
+def ifilter(predicate, iterable):
+    result = []
+    if predicate is None:
+        predicate = bool
+    for x in iterable:
+        if predicate(x):
+            result.append(x)
+    return result
+
+def ifilterfalse(predicate, iterable):
+    result = []
+    if predicate is None:
+        predicate = bool
+    for x in iterable:
+        if not predicate(x):
+            result.append(x)
+    return result
+
+def imap(function, *iterables):
+    return apply(map, (function,) + tuple(iterables))
+
+def islice(*args, **kw):
+    raise NotImplementedError
+
+def izip(*iterables):
+    return apply(zip, iterables)
+
+def repeat(*args, **kw):
+    # returns infinite length, should not be supported
+    raise NotImplementedError
+
+def starmap(*args, **kw):
+    raise NotImplementedError
+
+def takewhile(predicate, iterable):
+    result = []
+    for x in iterable:
+        if predicate(x):
+            result.append(x)
+        else:
+            break
+    return result
+
+def tee(*args, **kw):
+    raise NotImplementedError
index b3d0bb324ceff0d08a0749c9e4555ba42f902b44..1fe5a4f77a8fc5134306067ea79a898dd51babcf 100644 (file)
@@ -26,10 +26,11 @@ class Set:
         if seq:
             for elem in seq:
                 if elem not in self.elems:
+                    hash(elem)
                     self.elems.append(elem)
 
     def __str__(self):
-        return "{%s}" % string.join(map(str, self.elems), ", ")
+        return "set([%s])" % string.join(map(str, self.elems), ", ")
 
 
     def copy(self):
@@ -56,6 +57,7 @@ class Set:
     def add(self, elem):
         """Add one element to the set."""
         if elem not in self.elems:
+            hash(elem)
             self.elems.append(elem)
 
     def remove(self, elem):
@@ -157,3 +159,12 @@ class Set:
             return 0
         else:
             return len(self - other) == 0
+
+    def __cmp__(self, other):
+        """Returns 1 if the sets are equal."""
+        if self.__lt__(other):
+            return -1
+        elif other.__lt__(self):
+            return 1
+        else:
+            return 0
index 1e0e411897593395b5013a56e9614bc163286503..72ed9b9c5e759216e922d85f2ddde2984ab97bd4 100644 (file)
@@ -90,13 +90,16 @@ class TextWrapper:
     # splits into
     #   Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option!
     # (after stripping out empty strings).
-    wordsep_re = re.compile(r'(\s+|'                  # any whitespace
-                            r'-*\w{2,}-(?=\w{2,}))')   # hyphenated words
-                            # Earlier Python's don't have the (?<=
-                            # negative look-behind assertion.  It doesn't
-                            # matter for the simple input SCons is going to
-                            # give it, so just comment it out.
-                            #r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')   # em-dash
+    try:
+        wordsep_re = re.compile(r'(\s+|'                  # any whitespace
+                                r'[^\s\w]*\w{2,}-(?=\w{2,})|' # hyphenated words
+                                r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')   # em-dash
+    except re.error:
+        # Pre-2.0 Python versions don't have the (?<= negative look-behind
+        # assertion.  It mostly doesn't matter for the simple input
+        # SCons is going to give it, so just leave it out.
+        wordsep_re = re.compile(r'(\s+|'                    # any whitespace
+                                r'-*\w{2,}-(?=\w{2,}))')    # hyphenated words
 
     # XXX will there be a locale-or-charset-aware version of
     # string.lowercase in 2.3?
index 36840c3878ac4dab86db1fe87c3e780ebc5d27ca..437f05a37d721d92695b3cbd28ff1264ffa310d6 100644 (file)
@@ -5,12 +5,9 @@ import cPickle
 import time
 import shutil
 import os
-import os.path
 import types
 import __builtin__
 
-_open = __builtin__.open # avoid name clash
-
 keep_all_files = 00000
 ignore_corrupt_dbfiles = 0
 
@@ -35,6 +32,24 @@ tmp_suffix = '.tmp'
 
 class dblite:
 
+  # Squirrel away references to the functions in various modules
+  # that we'll use when our __del__() method calls our sync() method
+  # during shutdown.  We might get destroyed when Python is in the midst
+  # of tearing down the different modules we import in an essentially
+  # arbitrary order, and some of the various modules's global attributes
+  # may already be wiped out from under us.
+  #
+  # See the discussion at:
+  #   http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html
+
+  _open = __builtin__.open
+  _cPickle_dump = cPickle.dump
+  _os_chmod = os.chmod
+  _os_rename = os.rename
+  _os_unlink = os.unlink
+  _shutil_copyfile = shutil.copyfile
+  _time_time = time.time
+
   def __init__(self, file_base_name, flag, mode):
     assert flag in (None, "r", "w", "c", "n")
     if (flag is None): flag = "r"
@@ -51,14 +66,14 @@ class dblite:
     self._dict = {}
     self._needs_sync = 00000
     if (self._flag == "n"):
-      _open(self._file_name, "wb", self._mode)
+      self._open(self._file_name, "wb", self._mode)
     else:
       try:
-        f = _open(self._file_name, "rb")
+        f = self._open(self._file_name, "rb")
       except IOError, e:
         if (self._flag != "c"):
           raise e
-        _open(self._file_name, "wb", self._mode)
+        self._open(self._file_name, "wb", self._mode)
       else:
         p = f.read()
         if (len(p) > 0):
@@ -75,8 +90,8 @@ class dblite:
 
   def sync(self):
     self._check_writable()
-    f = _open(self._tmp_name, "wb", self._mode)
-    cPickle.dump(self._dict, f, 1)
+    f = self._open(self._tmp_name, "wb", self._mode)
+    self._cPickle_dump(self._dict, f, 1)
     f.close()
     # Windows doesn't allow renaming if the file exists, so unlink
     # it first, chmod'ing it to make sure we can do so.  On UNIX, we
@@ -84,15 +99,15 @@ class dblite:
     # (e.g. from a previous run as root).  We should still be able to
     # unlink() the file if the directory's writable, though, so ignore
     # any OSError exception  thrown by the chmod() call.
-    try: os.chmod(self._file_name, 0777)
+    try: self._os_chmod(self._file_name, 0777)
     except OSError: pass
-    os.unlink(self._file_name)
-    os.rename(self._tmp_name, self._file_name)
+    self._os_unlink(self._file_name)
+    self._os_rename(self._tmp_name, self._file_name)
     self._needs_sync = 00000
     if (keep_all_files):
-      shutil.copyfile(
+      self._shutil_copyfile(
         self._file_name,
-        self._file_name + "_" + str(int(time.time())))
+        self._file_name + "_" + str(int(self._time_time())))
 
   def _check_writable(self):
     if (self._flag == "r"):
index f927bb19962eaecba5d8cbda059e4fe508022980..56443c8fcc26fcd50e4470c75fa4bd5c093428dc 100644 (file)
@@ -7,10 +7,14 @@ python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, '
 @REM no way to set exit status of this script for 9x/Me\r
 goto endscons\r
 :WinNT\r
-@REM set path=%path%;%~dp0\r
+setlocal\r
+@REM ensure the script will be executed with the Python it was installed for\r
+set path=%~dp0;%~dp0..;%path%\r
 python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %*\r
-if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endscons\r
+if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endsconsnt\r
 if errorlevel 9009 echo you do not have python in your PATH\r
 @REM color 00 causes this script to exit with non-zero exit status\r
 if errorlevel 1 color 00\r
+:endsconsnt\r
+endlocal\r
 :endscons\r
index 4660b2fc1035838b907abec098ff9fb14b3e290d..281a69cd972c0a1938c0b70dacaa3955e9aa862f 100644 (file)
@@ -376,12 +376,13 @@ arguments = {
     'packages'         : ["SCons",
                           "SCons.compat",
                           "SCons.Node",
-                          "SCons.Options",
                           "SCons.Platform",
                           "SCons.Scanner",
                           "SCons.Script",
                           "SCons.Tool",
-                          "SCons.Tool.packaging"],
+                          "SCons.Tool.packaging",
+                          "SCons.Variables",
+                         ],
     'package_dir'      : {'' : 'engine'},
     'data_files'       : [('man/man1', man_pages)],
     'scripts'          : scripts,
index 00aa688b890c4c9fb7140aa49d56b30d43643a6b..5e755a85b1c260295fafec9e3c6a8ae4ca865634 100644 (file)
@@ -41,8 +41,8 @@ import re
 import SCons.Action
 import SCons.Builder
 
-options = Options()
-options.AddOptions(
+options = Variables()
+options.AddVariables(
     ('header', 'Header string (default cell argument)', 'Head:'),
     ('trailer', 'Trailer string (default cell argument)', 'Tail'),
     ('NbDeps', 'Number of dependencies', '2'),
diff --git a/test/Actions/unicode-signature.py b/test/Actions/unicode-signature.py
new file mode 100644 (file)
index 0000000..dd7f883
--- /dev/null
@@ -0,0 +1,65 @@
+#!/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 an Action list with a string command containing a Unicode file
+name, and a Python function action, works corectly.  This verifies that
+the signatures of the two actions can be concatenated without encoding
+Unicode problems.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+try:
+    unicode
+except NameError:
+    import sys
+    msg = "Unicode not supported by Python version %s; skipping test\n"
+    test.skip_test(msg % sys.version[:3])
+
+test.write('SConstruct', """
+fnode = File(u'foo.txt')
+
+def funcact(target, source, env):
+    open(str(target[0]), 'wb').write("funcact\\n")
+    for i in range(300):
+        pass
+    return 0
+
+env = Environment()
+
+env.Command(fnode, [], ["echo $TARGET", funcact])
+""")
+
+test.run(arguments = '.')
+
+test.must_match('foo.txt', "funcact\n")
+
+test.up_to_date(arguments = '.')
+
+test.pass_test()
diff --git a/test/CXX/CCFLAGS.py b/test/CXX/CCFLAGS.py
new file mode 100644 (file)
index 0000000..a559de3
--- /dev/null
@@ -0,0 +1,68 @@
+#!/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 we can set both $CCFLAGS and $CXXFLAGS and have them
+both show up on the compilation lines for C++ source files.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+foo = Environment(CCFLAGS = '-DFOO', CXXFLAGS = '-DCXX')
+bar = Environment(CCFLAGS = '-DBAR', CXXFLAGS = '-DCXX')
+foo_obj = foo.Object(target = 'foo', source = 'prog.cpp')
+bar_obj = bar.Object(target = 'bar', source = 'prog.cpp')
+foo.Program(target = 'foo', source = foo_obj)
+bar.Program(target = 'bar', source = bar_obj)
+""")
+
+test.write('prog.cpp', r"""
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(int argc, char *argv[])
+{
+        argv[argc++] = "--";
+#ifdef FOO
+        printf("prog.c:  FOO\n");
+#endif
+#ifdef BAR
+        printf("prog.c:  BAR\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.pass_test()
index c98fb8c86ad43d90db0b46fb269b069d30ec1b29..5fc40d05b53d3a5ec7d8254fca6b9e26048197fd 100644 (file)
@@ -49,10 +49,7 @@ for l in filter(lambda l: l[:7] != '/*c++*/', infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.c') == os.path.normcase('.C'):
-    alt_cpp_suffix = '.cpp'
-else:
-    alt_cpp_suffix = '.C'
+alt_cpp_suffix=test.get_alt_cpp_suffix()
 
 test.write('SConstruct', """
 env = Environment(CXXCOM = r'%(_python_)s mycc.py $TARGET $SOURCE',
index 23bd3e632deb21cfd37937e363a043ecc4ffb541..df4a87b7c37ae38d888889b4acb0f9d0b6dc328b 100644 (file)
@@ -50,10 +50,7 @@ for l in filter(lambda l: l != '/*c++*/\n', infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.c') == os.path.normcase('.C'):
-    alt_cpp_suffix = '.cpp'
-else:
-    alt_cpp_suffix = '.C'
+alt_cpp_suffix=test.get_alt_cpp_suffix()
 
 test.write('SConstruct', """
 env = Environment(CXXCOM = r'%(_python_)s mycc.py $TARGET $SOURCE',
index 600049e15c04a1e1c93cf96b2587143cbd0acea5..628bc00188d0c21c9bfb13bad5bcad33c6f773e0 100644 (file)
@@ -49,10 +49,7 @@ for l in filter(lambda l: l[:7] != '/*c++*/', infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.c') == os.path.normcase('.C'):
-    alt_cpp_suffix = '.cpp'
-else:
-    alt_cpp_suffix = '.C'
+alt_cpp_suffix=test.get_alt_cpp_suffix()
 
 test.write('SConstruct', """
 env = Environment(SHCXXCOM = r'%(_python_)s mycc.py $TARGET $SOURCE',
index 8e55441765179a02894c8023bf3836bdd49b2918..dd92541524b82e961be85c62e549bd1e8ff27fd5 100644 (file)
@@ -50,10 +50,7 @@ for l in filter(lambda l: l != '/*c++*/\n', infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.c') == os.path.normcase('.C'):
-    alt_cpp_suffix = '.cpp'
-else:
-    alt_cpp_suffix = '.C'
+alt_cpp_suffix=test.get_alt_cpp_suffix()
 
 test.write('SConstruct', """
 env = Environment(SHCXXCOM = r'%(_python_)s mycc.py $TARGET $SOURCE',
index 663aa40d8b0d15fe66efa652acc4008b2fef2c16..4f089275354aef2b6433a7274c0bc2350af3bec1 100644 (file)
@@ -65,8 +65,8 @@ void bar() {
 }
 """)
 
-if sys.platform == 'darwin':
-    test.skip_test("Skipping test on Darwin/OSX; it has partial case sensitivity.")
+if sys.platform[:6] == 'darwin':
+    test.skip_test("Skipping test on Darwin/OSX; it has partial case sensitivity.\n")
 
 if sys.platform in ['cygwin', 'win32']:
     sys.stdout.write("Using case-insensitive filesystem, testing for failure\n")
index a9371f94e844c4aa23403b4cde9dd2a80339b078..1fffacf7ed19c17e7f72c35b8db0e4c339fa4dd5 100644 (file)
@@ -45,7 +45,7 @@ NCF = test.NCF  # non-cached build failure
 CF  = test.CF   # cached build failure
 
 test.write('SConstruct', """\
-opts = Options()
+opts = Variables()
 opts.Add('chdir')
 env = Environment(options=opts)
 if env['chdir'] == 'yes':
index 25b60544773c0c1c7180e2b152ea9e56b9f447b3..98967622db2c6f425620e11214004cd6305008b6 100644 (file)
@@ -25,7 +25,7 @@
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 """
-Test that setting Options in an Environment doesn't prevent the
+Test that setting Variables in an Environment doesn't prevent the
 Environment from being copied.
 """
 
@@ -37,11 +37,11 @@ test.write('SConstruct', """
 gpib_options = ['NI_GPIB', 'NI_ENET']
 gpib_include = '/'
 
-#0.96 broke copying  ListOptions ???
-opts = Options('config.py', ARGUMENTS)
-opts.AddOptions(
-    BoolOption('gpib', 'enable gpib support', 1),
-    ListOption('gpib_options',
+#0.96 broke copying  ListVariables ???
+opts = Variables('config.py', ARGUMENTS)
+opts.AddVariables(
+    BoolVariable('gpib', 'enable gpib support', 1),
+    ListVariable('gpib_options',
         'whether and what kind of gpib support shall be enabled',
         'all',
         gpib_options),
similarity index 97%
rename from test/Options/ListOption.py
rename to test/Deprecated/Options/ListOption.py
index 5611e40d10d15ee7621968742f539d019e446c94..68d2ee87e8aa6ee888c7ff39d7eac6e247cc65e8 100644 (file)
@@ -34,6 +34,7 @@ import string
 
 import TestSCons
 
+
 test = TestSCons.TestSCons()
 
 SConstruct_path = test.workpath('SConstruct')
@@ -80,7 +81,9 @@ test.run()
 check(['all', '1', 'gl ical qt x11', 'gl ical qt x11',
        "['gl ical qt x11']"])
 
-test.must_match(test.workpath('scons.options'), "shared = 'all'"+os.linesep)
+test.must_match(test.workpath('scons.options'),
+                "shared = 'all'"+os.linesep,
+                mode='r')
 
 check(['all', '1', 'gl ical qt x11', 'gl ical qt x11',
        "['gl ical qt x11']"])
index 8907c6c286ed841721f2a7c9e8183f24430f6807..9b6e067abcc596f4eaa4ee0f7ec3d058a7ffd42d 100644 (file)
@@ -100,7 +100,7 @@ stree = """
 
 test.run(arguments = "--debug=stree foo.xxx",
          stderr = stderr)
-test.fail_test(string.find(test.stdout(), stree) == -1)
+test.fail_test(string.count(test.stdout(), stree) != 1)
 
 stree2 = """
  E         = exists
@@ -132,6 +132,6 @@ test.run(arguments = '-c foo.xxx')
 
 test.run(arguments = "--no-exec --debug=stree foo.xxx",
          stderr = stderr)
-test.fail_test(string.find(test.stdout(), stree2) == -1)
+test.fail_test(string.count(test.stdout(), stree2) != 1)
 
 test.pass_test()
index 7f6f1a1f7e63b057c869b08cf104814bb6ca2c73..b8a5e44a195c8538c0a9036bc77c3f46270b6ceb 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     f77pp = 'f77'
 else:
     f77pp = 'f77pp'
index e2e3cf5ae4396412be307c79f69d7411819e1f6f..27fd3326ab37bf9289348df0d8af3380511fdb63 100644 (file)
@@ -45,12 +45,11 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     f90pp = 'f90'
 else:
     f90pp = 'f90pp'
 
-
 test.write('SConstruct', """
 env = Environment(F90COM = r'%(_python_)s myfc.py f90 $TARGET $SOURCES',
                   F90COMSTR = 'Building f90 $TARGET from $SOURCES',
index ed7d1e8a660dcc54b8910980cd7831a98c268d6a..371fbd528df6f0c209bb9a55ad5e12a321108333 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     f95pp = 'f95'
 else:
     f95pp = 'f95pp'
index fd318c3abbabe17b46daf2c921817378fb0e5dbb..f942669d6dbb75871126b9cbce9a674a3b2dd652 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     fortranpp = 'fortran'
 else:
     fortranpp = 'fortranpp'
index 2bedb4898889ba5919ce2c143213415d48b64c77..4f08e264409bf61c2897d35f041154e8fa0111b1 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     f77pp = 'f77'
 else:
     f77pp = 'f77pp'
index 08208fa469448ce9f6413a46dde4474f3f554df2..2d512a566862d4221b2b0182a66efff5c0cb017d 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     f90pp = 'f90'
 else:
     f90pp = 'f90pp'
index 71a5627135ea5adc320673d36df2dc91a893fcaa..ebefac4d9e60ca18b94448c3deea712f86f08b82 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     f95pp = 'f95'
 else:
     f95pp = 'f95pp'
index 52b20c229d3baa6392a0bca6288bc31e8dbfece1..13e76e3acfe54cdc79591521e1db5becb84108e6 100644 (file)
@@ -45,7 +45,7 @@ for l in filter(lambda l, fl=fline: l != fl, infile.readlines()):
 sys.exit(0)
 """)
 
-if os.path.normcase('.f') == os.path.normcase('.F'):
+if not TestSCons.case_sensitive_suffixes('.f','.F'):
     fortranpp = 'fortran'
 else:
     fortranpp = 'fortranpp'
index 23b9ad9030d4f19d9f6cdd6cb3eeeff983c32a90..f0ff81faa83bff04a02e20e3df951e3e9484a586 100644 (file)
@@ -69,15 +69,22 @@ scons.send("build foo.out\n")
 expect_stdout = """\
 scons>>> Copy("foo.out", "foo.in")
 Touch("1")
-scons>>> Taskmaster: 'foo.out': children:
-    ['foo.in']
-    waiting on unfinished children:
-    ['foo.in']
-Taskmaster: 'foo.in': evaluating foo.in
-Taskmaster: 'foo.out': children:
-    ['foo.in']
-    evaluating foo.out
+scons>>> 
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'foo.out'> and its children:
+Taskmaster:        <no_state   'foo.in'>
+Taskmaster:     Considering node <no_state   'foo.in'> and its children:
+Taskmaster: Evaluating <pending    'foo.in'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <pending    'foo.out'> and its children:
+Taskmaster:        <up_to_date 'foo.in'>
+Taskmaster: Evaluating <pending    'foo.out'>
 Copy("foo.out", "foo.in")
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
+
 scons>>> Touch("2")
 scons>>> scons: `foo.out' is up to date.
 scons>>> 
index 96e7d4ade895e29dede301cb925c110d13d6cc53..fcf35c6ef20cad7f1ae97c66051ae0c327d274d4 100644 (file)
@@ -79,8 +79,6 @@ scons>>> Copy("foo.out", "foo.in")
   +-foo.in
 scons>>> Touch("2")
 scons>>> scons: `foo.out' is up to date.
-+-foo.out
-  +-foo.in
 scons>>> 
 """
 
diff --git a/test/Interactive/variant_dir.py b/test/Interactive/variant_dir.py
new file mode 100644 (file)
index 0000000..7de25cc
--- /dev/null
@@ -0,0 +1,115 @@
+#!/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__"
+
+"""
+XXX Put a description of the test here.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('markers',
+            'work',
+            ['work', 'sub1'])
+
+marker_1 = test.workpath('markers', '1')
+marker_2 = test.workpath('markers', '2')
+
+test.write(['work', 'SConstruct'], """\
+# build the plugin binaries
+basepath = str(Dir('#').abspath)
+env = Environment()
+env.Append(BASEPATH=basepath)
+env.Append(ENV = {'BASEPATH' : str(Dir('#').abspath)})
+SConscript( 'sub1/SConscript',
+            variant_dir = 'build', 
+            duplicate=False, 
+            exports='env')
+Command(r'%(marker_1)s', [], Touch('$TARGET'))
+Command(r'%(marker_2)s', [], Touch('$TARGET'))
+""" % locals())
+
+test.write(['work', 'sub1', 'SConscript'], """\
+Import('env')
+env.Program('hello.c')
+""")
+
+test.write(['work', 'sub1', 'hello.c'], """\
+#include <stdio.h>
+#include <stdlib.h>
+int main( int iArgC, char *cpArgV[] )
+{
+    printf("hello\\n");
+    exit (0);
+}
+""")
+
+
+
+# The "chdir =" keyword argument in the test.start() call has no effect.
+# Work around it for now.
+import os
+os.chdir('work/sub1')
+scons = test.start(chdir = 'work/sub1', arguments = '-Q -u --interactive')
+
+scons.send("shell pwd\n")
+
+scons.send("build\n")
+
+scons.send("build %s\n" % marker_1)
+
+test.wait_for(marker_1)
+
+test.run(program = test.workpath('work/build/hello'), stdout="hello\n")
+
+
+
+test.write(['work', 'sub1', 'hello.c'], """\
+#include <stdio.h>
+#include <stdlib.h>
+int main( int iArgC, char *cpArgV[] )
+{
+    printf("hello 2\\n");
+    exit (0);
+}
+""")
+
+scons.send("build\n")
+
+scons.send("build %s\n" % marker_2)
+
+test.wait_for(marker_2)
+
+test.run(program = test.workpath('work/build/hello'), stdout="hello 2\n")
+
+
+
+test.finish(scons)
+
+
+
+test.pass_test()
index 84f70e85f256b7b1bac9f2fbee3cec8a4390ad5b..37163a311b2ff9d1cb10f7a22f931f441d9db88b 100644 (file)
@@ -40,11 +40,9 @@ test.write('SConstruct', "")
 # by the packaging build.
 copyright_marker = '__' + 'COPYRIGHT' + '__'
 
-copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007'
-
 fmt = '(%s|Copyright \\(c\\) %s The SCons Foundation)\n'
 
-copyright_line = fmt % (copyright_marker, copyright_years)
+copyright_line = fmt % (copyright_marker, TestSCons.copyright_years)
 
 
 
index cfa69063492cea88e2c8b7c4ad4ebffb3727dddd..b5f24f9435db6edfe8078d61f259d16f066c49f0 100644 (file)
@@ -83,7 +83,7 @@ env.Append(CPPPATH='.')
 
 env.VariantDir('buildout', 'src', duplicate=0)
 
-if sys.platform=='darwin':
+if sys.platform[:6]=='darwin':
    env.Append(CPPPATH=['/System/Library/Frameworks/JavaVM.framework/Headers'])
 
 #If you do not have swig on your system please remove 'buildout/jni/SConscript' line from next call
diff --git a/test/KeyboardInterrupt.py b/test/KeyboardInterrupt.py
new file mode 100644 (file)
index 0000000..25239b5
--- /dev/null
@@ -0,0 +1,94 @@
+#!/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 we handle keyboard interrupts (CTRL-C) correctly.
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+if 'killpg' not in dir(os) or 'setpgrp' not in dir(os):
+    test.skip_test("This Python version does not support killing process groups; skipping test.\n")
+
+test.write('toto.c', r"""
+void foo()
+{}
+""")
+
+test.write('SConstruct', r"""
+import os
+import signal
+
+# Make sure that SCons is a process group leader.
+os.setpgrp()
+
+all = []
+
+def explode(env, target, source):
+    os.killpg(0, signal.SIGINT)
+
+for i in xrange(40):
+    all += Object('toto%5d' % i, 'toto.c')
+
+all+= Command( 'broken', 'toto.c', explode)
+
+Default( Alias('all', all))
+"""
+)
+
+interruptedStr = """\
+.*\
+scons: Build interrupted\\.
+.*\
+scons: building terminated because of errors\\.
+.*\
+scons: writing .sconsign file\\.
+.*\
+"""
+
+def runtest(arguments):
+    test.run(arguments='-c')
+    test.run(arguments=arguments, status=2,
+             stdout=interruptedStr, stderr=r'.*', match=TestSCons.match_re_dotall)
+
+for i in range(2):
+    runtest('-j1')
+    runtest('-j4')
+    runtest('-j8')
+    runtest('-j16')
+    runtest('-j32')
+    runtest('-j64')
+
+    runtest('-j1 --random')
+    runtest('-j4 --random')
+    runtest('-j8 --random')
+    runtest('-j16 --random')
+    runtest('-j32 --random')
+    runtest('-j64 --random')
index 4a8e1eda6bd04bdcf105fb51ec64b3fc7b677916..1c4ae31038062ff23bff0d2d846aed89d677886b 100644 (file)
@@ -42,6 +42,7 @@ use_dl_lib = "env.Program(target = 'dlopenprog', source = 'dlopenprog.c', LIBS=[
 
 dlopen_line = {
     'darwin' : no_dl_lib,
+    'darwin8' : no_dl_lib,   # ONLY NEEDED FOR 1.5.2
     'freebsd4' : no_dl_lib,
     'linux2' : use_dl_lib,
 }
@@ -53,7 +54,7 @@ env = Environment()
 env.LoadableModule(target = 'foo1', source = 'f1.c')
 """ + dlopen_line.get(sys.platform, ''))
 
-    
+
 test.write('f1.c', r"""
 #include <stdio.h>
 
@@ -91,9 +92,9 @@ main(int argc, char *argv[])
 }
 """
 
-# Darwin dlopen()s a bundle name "foo1",
+# Darwin dlopen()s a bundle named "foo1",
 # other systems dlopen() a traditional libfoo1.so file.
-foo1_name = {'darwin' : 'foo1'}.get(sys.platform, dll_+'foo1'+_dll)
+foo1_name = {'darwin' : 'foo1'}.get(sys.platform[:6], dll_+'foo1'+_dll)
 
 test.write('dlopenprog.c',
            string.replace(dlopenprog, '__foo1_name__', foo1_name))
@@ -112,7 +113,7 @@ if sys.platform in platforms_with_dlopen:
     os.environ['LD_LIBRARY_PATH'] = test.workpath()
     test.run(program = test.workpath('dlopenprog'),
              stdout = "f1.c\ndlopenprog.c\n")
-                                 
+                 
 
 
 test.pass_test()
diff --git a/test/Parallel/multiple-parents.py b/test/Parallel/multiple-parents.py
new file mode 100644 (file)
index 0000000..f81adf5
--- /dev/null
@@ -0,0 +1,173 @@
+#!/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.
+#
+
+"""
+Verify that a failed build action with -j works as expected.
+"""
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+try:
+    import threading
+except ImportError:
+    # if threads are not supported, then
+    # there is nothing to test
+    TestCmd.no_result()
+    sys.exit()
+
+
+test = TestSCons.TestSCons()
+
+# Test that we can handle parallel builds with a dependency graph
+# where:
+#    a) Some nodes have multiple parents
+#    b) Some targets fail building
+#    c) Some targets succeed building
+#    d) Some children are ignored
+#    e) Some children are pre-requesites
+#    f) Some sources are missing
+
+test.write('SConstruct', """
+def fail_action(target = None, source = None, env = None):
+    return 2
+
+failed0  = Command(target='failed00',  source='', action=fail_action)
+ok0      = Command(target='ok00',      source='', action=Touch('${TARGET}'))
+prereq0  = Command(target='prereq00',  source='', action=Touch('${TARGET}'))
+ignore0  = Command(target='ignore00',  source='', action=Touch('${TARGET}'))
+igreq0   = Command(target='igreq00',   source='', action=Touch('${TARGET}'))
+missing0 = Command(target='missing00', source='MissingSrc', action=Touch('${TARGET}'))
+
+prev_level  = failed0 + ok0 + ignore0
+prev_prereq = prereq0
+prev_ignore = ignore0
+prev_igreq  = igreq0
+
+for i in range(1,20):
+    
+    failed = Command(target='failed%02d' % i,  source='', action=fail_action)
+    ok     = Command(target='ok%02d' % i,      source='', action=Touch('${TARGET}'))
+    prereq = Command(target='prereq%02d' % i,  source='', action=Touch('${TARGET}'))
+    ignore = Command(target='ignore%02d' % i,  source='', action=Touch('${TARGET}'))
+    igreq  = Command(target='igreq%02d' % i,   source='', action=Touch('${TARGET}'))
+    missing = Command(target='missing%02d' %i, source='MissingSrc', action=Touch('${TARGET}'))
+
+    next_level = failed + ok + ignore + igreq + missing
+
+    for j in range(1,10):
+        a = Alias('a%02d%02d' % (i,j), prev_level)
+
+        Requires(a, prev_prereq)
+        Ignore(a, prev_ignore)
+
+        Requires(a, prev_igreq)
+        Ignore(a, prev_igreq)
+
+        next_level = next_level + a
+
+    prev_level  = next_level
+    prev_prereq = prereq
+    prev_ignore = ignore
+    prev_igreq  = igreq
+
+all = Alias('all', prev_level)
+
+Requires(all, prev_prereq)
+Ignore(all,  prev_ignore)
+
+Requires(all, prev_igreq)
+Ignore(all,  prev_igreq)
+
+Default(all)
+""")
+
+re_error = """\
+(scons: \\*\\*\\* \\[failed\\d+] Error 2\\n)|\
+(scons: \\*\\*\\* Source `MissingSrc' not found, needed by target `missing\\d+'\\.(  Stop\\.)?\\n)\
+"""
+
+re_errors = "(" + re_error + ")+"
+
+test.run(arguments = 'all',
+         status = 2,
+         stderr = "scons: *** [failed19] Error 2\n")
+test.must_not_exist(test.workpath('ok'))
+
+
+for i in range(5):
+    test.run(arguments = '-c all')
+
+    test.run(arguments = '-j8 all',
+             status = 2,
+             stderr = re_errors,
+             match=TestSCons.match_re_dotall)
+
+
+for i in range(5):
+    test.run(arguments = '-c all')
+
+    test.run(arguments = '-j 8 -k all',
+             status = 2,
+             stderr = re_errors,
+             match=TestSCons.match_re_dotall)
+    for i in range(20):
+        test.must_exist(test.workpath('ok%02d' % i))
+        test.must_exist(test.workpath('prereq%02d' % i))
+        test.must_not_exist(test.workpath('ignore%02d' % i))
+        test.must_exist(test.workpath('igreq%02d' % i))
+
+
+for i in range(5):
+    test.run(arguments = 'all --random',
+             status = 2,
+             stderr = re_errors,
+             match=TestSCons.match_re_dotall)
+    test.must_not_exist(test.workpath('ok'))
+
+for i in range(5):
+    test.run(arguments = '-c all')
+
+    test.run(arguments = '-j8 --random all',
+             status = 2,
+             stderr = re_errors,
+             match=TestSCons.match_re_dotall)
+
+for i in range(5):
+    test.run(arguments = '-c all')
+
+    test.run(arguments = '-j 8 -k --random all',
+             status = 2,
+             stderr = re_errors,
+             match=TestSCons.match_re_dotall)
+    for i in range(20):
+        test.must_exist(test.workpath('ok%02d' % i))
+        test.must_exist(test.workpath('prereq%02d' % i))
+        test.must_not_exist(test.workpath('ignore%02d' % i))
+        test.must_exist(test.workpath('igreq%02d' % i))
+
+test.pass_test()
index 3ccabf7fa909827fe1735c352d96757f0b3c8c73..6c4a2ebee126f630e49204c3613f8f6b86d382f7 100644 (file)
@@ -133,8 +133,8 @@ def AttemptLinkWithVariables(context, variables, code, extension, prefix):
 
 env = Environment(CPPPATH=['.'], LIBPATH=['.'], LIBS=[])
 
-opts = Options('lprof.conf') 
-opts.Add(PathOption("qt_directory", "Path to Qt directory", "/"))
+opts = Variables('lprof.conf') 
+opts.Add(PathVariable("qt_directory", "Path to Qt directory", "/"))
 opts.Update(env)
 
 env['QT_LIB'] = 'qt-mt'
index 453c0b8133ee16104001b2a7ec7a3bea5e38b8d4..2ef4559d19072223675f5e6ddae9c04914964c1d 100644 (file)
@@ -38,11 +38,14 @@ SConscript('SConscript1')
 x = SConscript('SConscript2')
 y, z = SConscript('SConscript3')
 a4, b4 = SConscript('SConscript4')
+foo, bar = SConscript('SConscript5')
 print "x =", x
 print "y =", y
 print "z =", z
 print "a4 =", a4
 print "b4 =", b4
+print "foo =", foo
+print "bar =", bar
 """)
 
 test.write('SConscript1', """\
@@ -75,6 +78,13 @@ b4 = 'b-after'
 print "line 8"
 """)
 
+test.write('SConscript5', """\
+foo = 'foo'
+bar = 'bar'
+Return(["foo", "bar"])
+print "line 9"
+""")
+
 expect = """\
 line 1
 line 3
@@ -86,6 +96,8 @@ y = 8
 z = 9
 a4 = aaa
 b4 = bbb
+foo = foo
+bar = bar
 """
 
 test.run(arguments = '-q -Q', stdout=expect)
diff --git a/test/SWIG/subdir.py b/test/SWIG/subdir.py
new file mode 100644 (file)
index 0000000..4905d3e
--- /dev/null
@@ -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__"
+
+"""
+Verify that we expect the .py file created by the -python flag to be in
+the same subdirectory as the taget.
+"""
+
+import os
+import sys
+
+import TestSCons
+
+# swig-python expects specific filenames.
+# the platform specific suffix won't necessarily work.
+if sys.platform == 'win32':
+    _dll = '.dll'
+else:
+    _dll   = '.so' 
+
+test = TestSCons.TestSCons()
+
+test.subdir('sub')
+
+swig = test.where_is('swig')
+
+if not swig:
+    test.skip_test('Can not find installed "swig", skipping test.\n')
+
+python = test.get_platform_python()
+_python_ = test.get_quoted_platform_python()
+
+# handle testing on other platforms:
+ldmodule_prefix = '_'
+
+python_include_dir = test.get_python_inc()
+
+Python_h = os.path.join(python_include_dir, 'Python.h')
+
+if not os.path.exists(Python_h):
+    test.skip_test('Can not find %s, skipping test.\n' % Python_h)
+
+python_frameworks_flags = test.get_python_frameworks_flags()
+
+test.write('SConstruct', """
+env = Environment(SWIGFLAGS='-python',
+                  CPPPATH='%(python_include_dir)s/',
+                  LDMODULEPREFIX='%(ldmodule_prefix)s',
+                  LDMODULESUFFIX='%(_dll)s',
+                  FRAMEWORKSFLAGS='%(python_frameworks_flags)s',
+                  )
+
+import sys
+if sys.version[0] == '1':
+    # SWIG requires the -classic flag on pre-2.0 Python versions.
+    env.Append(SWIGFLAGS = ' -classic')
+
+env.LoadableModule('sub/_foo',
+                   ['sub/foo.i', 'sub/foo.c'],
+                   LDMODULEPREFIX='')
+""" % locals())
+
+test.write(['sub', 'foo.i'], """\
+%module foo
+%{
+/* Put header files here (optional) */
+/*
+ * This duplication shouldn't be necessary, I guess, but it seems
+ * to suppress "cast to pointer from integer of different size"
+ * warning messages on some systems.
+ */
+extern char *foo_string();
+%}
+
+extern char *foo_string();
+""")
+
+test.write(['sub', 'foo.c'], """\
+char *
+foo_string()
+{
+    return "This is foo.c!";
+}
+""")
+
+test.run(arguments = '.')
+
+test.up_to_date(options = '--debug=explain', arguments = '.')
+
+test.pass_test()
diff --git a/test/Variables/BoolVariable.py b/test/Variables/BoolVariable.py
new file mode 100644 (file)
index 0000000..1bda19b
--- /dev/null
@@ -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 the BoolVariable canned Variable type.
+"""
+
+import os.path
+import string
+
+try:
+    True, False
+except NameError:
+    True = (0 == 0)
+    False = (0 != 0)
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+SConstruct_path = test.workpath('SConstruct')
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
+
+
+
+test.write(SConstruct_path, """\
+from SCons.Variables import BoolVariable
+
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    BoolVariable('warnings', 'compilation with -Wall and similiar', 1),
+    BoolVariable('profile', 'create profiling informations', 0),
+    )
+
+env = Environment(variables=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['warnings']
+print env['profile']
+
+Default(env.Alias('dummy', None))
+""")
+
+
+
+test.run()
+check([str(True), str(False)])
+
+test.run(arguments='warnings=0 profile=no profile=true')
+check([str(False), str(True)])
+
+expect_stderr = """
+scons: *** Error converting option: warnings
+Invalid value for boolean option: irgendwas
+""" + test.python_file_line(SConstruct_path, 9)
+
+test.run(arguments='warnings=irgendwas', stderr = expect_stderr, status=2)
+
+
+
+test.pass_test()
diff --git a/test/Variables/EnumVariable.py b/test/Variables/EnumVariable.py
new file mode 100644 (file)
index 0000000..6966509
--- /dev/null
@@ -0,0 +1,102 @@
+#!/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 the EnumVariable canned Variable type.
+"""
+
+import os.path
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+SConstruct_path = test.workpath('SConstruct')
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
+
+
+
+test.write(SConstruct_path, """\
+from SCons.Variables import EnumVariable
+
+list_of_libs = Split('x11 gl qt ical')
+
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    EnumVariable('debug', 'debug output and symbols', 'no',
+               allowed_values=('yes', 'no', 'full'),
+               map={}, ignorecase=0),  # case sensitive
+    EnumVariable('guilib', 'gui lib to use', 'gtk',
+               allowed_values=('motif', 'gtk', 'kde'),
+               map={}, ignorecase=1), # case insensitive
+    EnumVariable('some', 'some option', 'xaver',
+               allowed_values=('xaver', 'eins'),
+               map={}, ignorecase=2), # make lowercase
+    )
+
+env = Environment(variables=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['debug']
+print env['guilib']
+print env['some']
+
+Default(env.Alias('dummy', None))
+""")
+
+
+test.run(); check(['no', 'gtk', 'xaver'])
+
+test.run(arguments='debug=yes guilib=Motif some=xAVER')
+check(['yes', 'Motif', 'xaver'])
+
+test.run(arguments='debug=full guilib=KdE some=EiNs')
+check(['full', 'KdE', 'eins'])
+
+expect_stderr = """
+scons: *** Invalid value for option debug: FULL
+""" + test.python_file_line(SConstruct_path, 18)
+
+test.run(arguments='debug=FULL', stderr=expect_stderr, status=2)
+
+expect_stderr = """
+scons: *** Invalid value for option guilib: irgendwas
+""" + test.python_file_line(SConstruct_path, 18)
+
+test.run(arguments='guilib=IrGeNdwas', stderr=expect_stderr, status=2)
+
+expect_stderr = """
+scons: *** Invalid value for option some: irgendwas
+""" + test.python_file_line(SConstruct_path, 18)
+
+test.run(arguments='some=IrGeNdwas', stderr=expect_stderr, status=2)
+
+
+test.pass_test()
diff --git a/test/Variables/ListVariable.py b/test/Variables/ListVariable.py
new file mode 100644 (file)
index 0000000..90d80ed
--- /dev/null
@@ -0,0 +1,172 @@
+#!/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 the ListVariable canned Variable type.
+"""
+
+import os
+import os.path
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+SConstruct_path = test.workpath('SConstruct')
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    r = result[1:len(expect)+1]
+    assert r == expect, (r, expect)
+
+
+
+test.write(SConstruct_path, """\
+from SCons.Variables import ListVariable
+
+list_of_libs = Split('x11 gl qt ical')
+
+optsfile = 'scons.variables'
+opts = Variables(optsfile, args=ARGUMENTS)
+opts.AddVariables(
+    ListVariable('shared',
+               'libraries to build as shared libraries',
+               'all',
+               names = list_of_libs,
+               map = {'GL':'gl', 'QT':'qt'}),
+    )
+
+env = Environment(variables=opts)
+opts.Save(optsfile, env)
+Help(opts.GenerateHelpText(env))
+
+print env['shared']
+if 'ical' in env['shared']: print '1'
+else: print '0'
+for x in  env['shared']:
+    print x,
+print
+print env.subst('$shared')
+# Test subst_path() because it's used in $CPPDEFINES expansions.
+print env.subst_path('$shared')
+Default(env.Alias('dummy', None))
+""")
+
+test.run()
+check(['all', '1', 'gl ical qt x11', 'gl ical qt x11',
+       "['gl ical qt x11']"])
+
+test.must_match(test.workpath('scons.variables'), "shared = 'all'"+os.linesep)
+
+check(['all', '1', 'gl ical qt x11', 'gl ical qt x11',
+       "['gl ical qt x11']"])
+
+test.run(arguments='shared=none')
+check(['none', '0', '', '', "['']"])
+
+test.run(arguments='shared=')
+check(['none', '0', '', '', "['']"])
+
+test.run(arguments='shared=x11,ical')
+check(['ical,x11', '1', 'ical x11', 'ical x11',
+       "['ical x11']"])
+
+test.run(arguments='shared=x11,,ical,,')
+check(['ical,x11', '1', 'ical x11', 'ical x11',
+       "['ical x11']"])
+
+test.run(arguments='shared=GL')
+check(['gl', '0', 'gl', 'gl'])
+
+test.run(arguments='shared=QT,GL')
+check(['gl,qt', '0', 'gl qt', 'gl qt', "['gl qt']"])
+
+
+expect_stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+""" + test.python_file_line(SConstruct_path, 15)
+
+test.run(arguments='shared=foo', stderr=expect_stderr, status=2)
+
+# be paranoid in testing some more combinations
+
+expect_stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+""" + test.python_file_line(SConstruct_path, 15)
+
+test.run(arguments='shared=foo,ical', stderr=expect_stderr, status=2)
+
+expect_stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+""" + test.python_file_line(SConstruct_path, 15)
+
+test.run(arguments='shared=ical,foo', stderr=expect_stderr, status=2)
+
+expect_stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+""" + test.python_file_line(SConstruct_path, 15)
+
+test.run(arguments='shared=ical,foo,x11', stderr=expect_stderr, status=2)
+
+expect_stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo,bar
+""" + test.python_file_line(SConstruct_path, 15)
+
+test.run(arguments='shared=foo,x11,,,bar', stderr=expect_stderr, status=2)
+
+
+
+test.write('SConstruct', """
+from SCons.Variables import ListVariable
+
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    ListVariable('gpib',
+               'comment',
+               ['ENET', 'GPIB'],
+               names = ['ENET', 'GPIB', 'LINUX_GPIB', 'NO_GPIB']),
+    )
+
+env = Environment(variables=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['gpib']
+Default(env.Alias('dummy', None))
+""")
+
+test.run(stdout=test.wrap_stdout(read_str="ENET,GPIB\n", build_str="""\
+scons: Nothing to be done for `dummy'.
+"""))
+
+
+
+test.pass_test()
diff --git a/test/Variables/PackageVariable.py b/test/Variables/PackageVariable.py
new file mode 100644 (file)
index 0000000..4d36252
--- /dev/null
@@ -0,0 +1,89 @@
+#!/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 the PackageVariable canned Variable type.
+"""
+
+import os.path
+import string
+
+try:
+    True, False
+except NameError:
+    True = (0 == 0)
+    False = (0 != 0)
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+SConstruct_path = test.workpath('SConstruct')
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
+
+
+
+test.write(SConstruct_path, """\
+from SCons.Variables import PackageVariable
+
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    PackageVariable('x11',
+                  'use X11 installed here (yes = search some places',
+                  'yes'),
+    )
+
+env = Environment(variables=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['x11']
+Default(env.Alias('dummy', None))
+""")
+
+test.run()
+check([str(True)])
+
+test.run(arguments='x11=no')
+check([str(False)])
+
+test.run(arguments='x11=0')
+check([str(False)])
+
+test.run(arguments=['x11=%s' % test.workpath()])
+check([test.workpath()])
+
+expect_stderr = """
+scons: *** Path does not exist for option x11: /non/existing/path/
+""" + test.python_file_line(SConstruct_path, 10)
+
+test.run(arguments='x11=/non/existing/path/', stderr=expect_stderr, status=2)
+
+
+
+test.pass_test()
diff --git a/test/Variables/PathVariable.py b/test/Variables/PathVariable.py
new file mode 100644 (file)
index 0000000..a2f96a7
--- /dev/null
@@ -0,0 +1,285 @@
+#!/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 the PathVariable canned option type, with tests for its 
+various canned validators.
+"""
+
+import os.path
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+SConstruct_path = test.workpath('SConstruct')
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
+
+#### test PathVariable ####
+
+test.subdir('lib', 'qt', ['qt', 'lib'], 'nolib' )
+workpath = test.workpath()
+libpath = os.path.join(workpath, 'lib')
+
+test.write(SConstruct_path, """\
+from SCons.Variables import PathVariable
+
+qtdir = r'%s'
+
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    PathVariable('qtdir', 'where the root of Qt is installed', qtdir),
+    PathVariable('qt_libraries', 'where the Qt library is installed', r'%s'),
+    )
+
+env = Environment(variables=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['qtdir']
+print env['qt_libraries']
+print env.subst('$qt_libraries')
+
+Default(env.Alias('dummy', None))
+""" % (workpath, os.path.join('$qtdir', 'lib') ))
+
+qtpath = workpath
+libpath = os.path.join(qtpath, 'lib')
+test.run()
+check([qtpath, os.path.join('$qtdir', 'lib'), libpath])
+
+qtpath = os.path.join(workpath, 'qt')
+libpath = os.path.join(qtpath, 'lib')
+test.run(arguments=['qtdir=%s' % qtpath])
+check([qtpath, os.path.join('$qtdir', 'lib'), libpath])
+
+qtpath = workpath
+libpath = os.path.join(qtpath, 'nolib')
+test.run(arguments=['qt_libraries=%s' % libpath])
+check([qtpath, libpath, libpath])
+
+qtpath = os.path.join(workpath, 'qt')
+libpath = os.path.join(workpath, 'nolib')
+test.run(arguments=['qtdir=%s' % qtpath, 'qt_libraries=%s' % libpath])
+check([qtpath, libpath, libpath])
+
+qtpath = os.path.join(workpath, 'non', 'existing', 'path')
+SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 11)[:-1]
+
+expect_stderr = """
+scons: *** Path for option qtdir does not exist: %(qtpath)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['qtdir=%s' % qtpath], stderr=expect_stderr, status=2)
+
+expect_stderr = """
+scons: *** Path for option qt_libraries does not exist: %(qtpath)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['qt_libraries=%s' % qtpath], stderr=expect_stderr, status=2)
+
+
+
+default_file = test.workpath('default_file')
+default_subdir = test.workpath('default_subdir')
+
+existing_subdir = test.workpath('existing_subdir')
+test.subdir(existing_subdir)
+
+existing_file = test.workpath('existing_file')
+test.write(existing_file, "existing_file\n")
+
+non_existing_subdir = test.workpath('non_existing_subdir')
+non_existing_file = test.workpath('non_existing_file')
+
+
+
+test.write('SConstruct', """\
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathAccept),
+    )
+
+env = Environment(variables=opts)
+
+print env['X']
+
+Default(env.Alias('dummy', None))
+""" % default_subdir)
+
+test.run()
+check([default_subdir])
+
+test.run(arguments=['X=%s' % existing_file])
+check([existing_file])
+
+test.run(arguments=['X=%s' % non_existing_file])
+check([non_existing_file])
+
+test.run(arguments=['X=%s' % existing_subdir])
+check([existing_subdir])
+
+test.run(arguments=['X=%s' % non_existing_subdir])
+check([non_existing_subdir])
+
+test.must_not_exist(non_existing_file)
+test.must_not_exist(non_existing_subdir)
+
+
+
+test.write(SConstruct_path, """\
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathIsFile),
+    )
+
+env = Environment(variables=opts)
+
+print env['X']
+
+Default(env.Alias('dummy', None))
+""" % default_file)
+
+SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 6)[:-1]
+
+expect_stderr = """
+scons: *** File path for option X does not exist: %(default_file)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(status=2, stderr=expect_stderr)
+
+test.write(default_file, "default_file\n")
+
+test.run()
+check([default_file])
+
+expect_stderr = """
+scons: *** File path for option X is a directory: %(existing_subdir)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['X=%s' % existing_subdir], status=2, stderr=expect_stderr)
+
+test.run(arguments=['X=%s' % existing_file])
+check([existing_file])
+
+expect_stderr = """
+scons: *** File path for option X does not exist: %(non_existing_file)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['X=%s' % non_existing_file], status=2, stderr=expect_stderr)
+
+
+
+test.write('SConstruct', """\
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathIsDir),
+    )
+
+env = Environment(variables=opts)
+
+print env['X']
+
+Default(env.Alias('dummy', None))
+""" % default_subdir)
+
+expect_stderr = """
+scons: *** Directory path for option X does not exist: %(default_subdir)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(status=2, stderr=expect_stderr)
+
+test.subdir(default_subdir)
+
+test.run()
+check([default_subdir])
+
+expect_stderr = """
+scons: *** Directory path for option X is a file: %(existing_file)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['X=%s' % existing_file],
+         status=2,
+         stderr=expect_stderr)
+
+test.run(arguments=['X=%s' % existing_subdir])
+check([existing_subdir])
+
+expect_stderr = """
+scons: *** Directory path for option X does not exist: %(non_existing_subdir)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['X=%s' % non_existing_subdir],
+         status=2,
+         stderr=expect_stderr)
+
+
+
+test.write('SConstruct', """\
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathIsDirCreate),
+    )
+
+env = Environment(variables=opts)
+
+print env['X']
+
+Default(env.Alias('dummy', None))
+""" % default_subdir)
+
+test.run()
+check([default_subdir])
+
+expect_stderr = """
+scons: *** Path for option X is a file, not a directory: %(existing_file)s
+%(SConstruct_file_line)s
+""" % locals()
+
+test.run(arguments=['X=%s' % existing_file], status=2, stderr=expect_stderr)
+
+test.run(arguments=['X=%s' % existing_subdir])
+check([existing_subdir])
+
+test.run(arguments=['X=%s' % non_existing_subdir])
+check([non_existing_subdir])
+
+test.must_exist(non_existing_subdir)
+
+
+
+test.pass_test()
diff --git a/test/Variables/Variables.py b/test/Variables/Variables.py
new file mode 100644 (file)
index 0000000..8f32f61
--- /dev/null
@@ -0,0 +1,364 @@
+#!/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 TestSCons
+import string
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+import string
+env = Environment()
+print env['CC']
+print string.join(env['CCFLAGS'])
+Default(env.Alias('dummy', None))
+""")
+test.run()
+cc, ccflags = string.split(test.stdout(), '\n')[1:3]
+
+test.write('SConstruct', """
+import string
+
+# test validator.  Change a key and add a new one to the environment
+def validator(key, value, environ):
+    environ[key] = "v"
+    environ["valid_key"] = "v"
+
+
+def old_converter (value):
+    return "old_converter"
+
+def new_converter (value, env):
+    return "new_converter"
+
+
+opts = Variables('custom.py')
+opts.Add('RELEASE_BUILD',
+         'Set to 1 to build a release build',
+         0,
+         None,
+         int)
+
+opts.Add('DEBUG_BUILD',
+         'Set to 1 to build a debug build',
+         1,
+         None,
+         int)
+
+opts.Add('CC',
+         'The C compiler')
+
+opts.Add('VALIDATE',
+         'An option for testing validation',
+         "notset",
+         validator,
+         None)
+
+opts.Add('OLD_CONVERTER',
+         'An option for testing converters that take one parameter',
+         "foo",
+         None,
+         old_converter)
+
+opts.Add('NEW_CONVERTER',
+         'An option for testing converters that take two parameters',
+         "foo",
+         None,
+         new_converter)
+
+opts.Add('UNSPECIFIED',
+         'An option with no value')
+
+def test_tool(env):
+    if env['RELEASE_BUILD']:
+        env.Append(CCFLAGS = '-O')
+    if env['DEBUG_BUILD']:
+        env.Append(CCFLAGS = '-g')
+
+
+env = Environment(variables=opts, tools=['default', test_tool])
+
+Help('Variables settable in custom.py or on the command line:\\n' + opts.GenerateHelpText(env))
+
+print env['RELEASE_BUILD']
+print env['DEBUG_BUILD']
+print env['CC']
+print string.join(env['CCFLAGS'])
+print env['VALIDATE']
+print env['valid_key']
+
+# unspecified variables should not be set:
+assert not env.has_key('UNSPECIFIED')
+
+# undeclared variables should be ignored:
+assert not env.has_key('UNDECLARED')
+
+# calling Update() should not effect variables that
+# are not declared on the variables object:
+r = env['RELEASE_BUILD']
+opts = Variables()
+opts.Update(env)
+assert env['RELEASE_BUILD'] == r
+
+Default(env.Alias('dummy', None))
+
+""")
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
+
+test.run()
+check(['0', '1', cc, string.strip(ccflags + ' -g'), 'v', 'v'])
+
+test.run(arguments='RELEASE_BUILD=1')
+check(['1', '1', cc, string.strip(ccflags + ' -O -g'), 'v', 'v'])
+
+test.run(arguments='RELEASE_BUILD=1 DEBUG_BUILD=0')
+check(['1', '0', cc, string.strip(ccflags + ' -O'), 'v', 'v'])
+
+test.run(arguments='CC=not_a_c_compiler')
+check(['0', '1', 'not_a_c_compiler', string.strip(ccflags + ' -g'), 'v', 'v'])
+
+test.run(arguments='UNDECLARED=foo')
+check(['0', '1', cc, string.strip(ccflags + ' -g'), 'v', 'v'])
+
+test.run(arguments='CCFLAGS=--taco')
+check(['0', '1', cc, string.strip(ccflags + ' -g'), 'v', 'v'])
+
+test.write('custom.py', """
+DEBUG_BUILD=0
+RELEASE_BUILD=1
+""")
+
+test.run()
+check(['1', '0', cc, string.strip(ccflags + ' -O'), 'v', 'v'])
+
+test.run(arguments='DEBUG_BUILD=1')
+check(['1', '1', cc, string.strip(ccflags + ' -O -g'), 'v', 'v'])
+
+test.run(arguments='-h',
+         stdout = """\
+scons: Reading SConscript files ...
+1
+0
+%s
+%s
+v
+v
+scons: done reading SConscript files.
+Variables settable in custom.py or on the command line:
+
+RELEASE_BUILD: Set to 1 to build a release build
+    default: 0
+    actual: 1
+
+DEBUG_BUILD: Set to 1 to build a debug build
+    default: 1
+    actual: 0
+
+CC: The C compiler
+    default: None
+    actual: %s
+
+VALIDATE: An option for testing validation
+    default: notset
+    actual: v
+
+OLD_CONVERTER: An option for testing converters that take one parameter
+    default: foo
+    actual: old_converter
+
+NEW_CONVERTER: An option for testing converters that take two parameters
+    default: foo
+    actual: new_converter
+
+UNSPECIFIED: An option with no value
+    default: None
+    actual: None
+
+Use scons -H for help about command-line options.
+"""%(cc, ccflags and ccflags + ' -O' or '-O', cc))
+
+# Test saving of variables and multi loading
+#
+test.write('SConstruct', """
+opts = Variables(['custom.py', 'variables.saved'])
+opts.Add('RELEASE_BUILD',
+         'Set to 1 to build a release build',
+         0,
+         None,
+         int)
+
+opts.Add('DEBUG_BUILD',
+         'Set to 1 to build a debug build',
+         1,
+         None,
+         int)
+
+opts.Add('UNSPECIFIED',
+         'An option with no value')
+
+env = Environment(variables = opts)
+
+print env['RELEASE_BUILD']
+print env['DEBUG_BUILD']
+
+opts.Save('variables.saved', env)
+""")
+
+# Check the save file by executing and comparing against
+# the expected dictionary
+def checkSave(file, expected):
+    gdict = {}
+    ldict = {}
+    execfile(file, gdict, ldict)
+    assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
+
+# First test with no command line variables
+# This should just leave the custom.py settings
+test.run()
+check(['1','0'])
+checkSave('variables.saved', { 'RELEASE_BUILD':1, 'DEBUG_BUILD':0})
+
+# Override with command line arguments
+test.run(arguments='DEBUG_BUILD=3')
+check(['1','3'])
+checkSave('variables.saved', {'RELEASE_BUILD':1, 'DEBUG_BUILD':3})
+
+# Now make sure that saved variables are overridding the custom.py
+test.run()
+check(['1','3'])
+checkSave('variables.saved', {'DEBUG_BUILD':3, 'RELEASE_BUILD':1})
+
+# Load no variables from file(s)
+# Used to test for correct output in save option file
+test.write('SConstruct', """
+opts = Variables()
+opts.Add('RELEASE_BUILD',
+         'Set to 1 to build a release build',
+         '0',
+         None,
+         int)
+
+opts.Add('DEBUG_BUILD',
+         'Set to 1 to build a debug build',
+         '1',
+         None,
+         int)
+
+opts.Add('UNSPECIFIED',
+         'An option with no value')
+
+opts.Add('LISTOPTION_TEST',
+         'testing list option persistence',
+         'none',
+         names = ['a','b','c',])
+
+env = Environment(variables = opts)
+
+print env['RELEASE_BUILD']
+print env['DEBUG_BUILD']
+print env['LISTOPTION_TEST']
+
+opts.Save('variables.saved', env)
+""")
+
+# First check for empty output file when nothing is passed on command line
+test.run()
+check(['0','1'])
+checkSave('variables.saved', {})
+
+# Now specify one option the same as default and make sure it doesn't write out
+test.run(arguments='DEBUG_BUILD=1')
+check(['0','1'])
+checkSave('variables.saved', {})
+
+# Now specify same option non-default and make sure only it is written out
+test.run(arguments='DEBUG_BUILD=0 LISTOPTION_TEST=a,b')
+check(['0','0'])
+checkSave('variables.saved',{'DEBUG_BUILD':0, 'LISTOPTION_TEST':'a,b'})
+
+test.write('SConstruct', """
+opts = Variables('custom.py')
+opts.Add('RELEASE_BUILD',
+         'Set to 1 to build a release build',
+         0,
+         None,
+         int)
+
+opts.Add('DEBUG_BUILD',
+         'Set to 1 to build a debug build',
+         1,
+         None,
+         int)
+
+opts.Add('CC',
+         'The C compiler')
+
+opts.Add('UNSPECIFIED',
+         'An option with no value')
+
+env = Environment(variables=opts)
+
+Help('Variables settable in custom.py or on the command line:\\n' + opts.GenerateHelpText(env,sort=cmp))
+
+""")
+
+test.run(arguments='-h',
+         stdout = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+Variables settable in custom.py or on the command line:
+
+CC: The C compiler
+    default: None
+    actual: %s
+
+DEBUG_BUILD: Set to 1 to build a debug build
+    default: 1
+    actual: 0
+
+RELEASE_BUILD: Set to 1 to build a release build
+    default: 0
+    actual: 1
+
+UNSPECIFIED: An option with no value
+    default: None
+    actual: None
+
+Use scons -H for help about command-line options.
+"""%cc)
+
+test.write('SConstruct', """
+import SCons.Variables
+env1 = Environment(variables = Variables())
+env2 = Environment(variables = SCons.Variables.Variables())
+""")
+
+test.run()
+
+test.pass_test()
diff --git a/test/Variables/chdir.py b/test/Variables/chdir.py
new file mode 100644 (file)
index 0000000..ad907c6
--- /dev/null
@@ -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 we can chdir() to the directory in which an Variables
+file lives by using the __name__ value.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('bin', 'subdir')
+
+test.write('SConstruct', """\
+opts = Variables('../bin/opts.cfg', ARGUMENTS)
+opts.Add('VARIABLE')
+Export("opts")
+SConscript('subdir/SConscript')
+""")
+
+SConscript_contents = """\
+Import("opts")
+env = Environment()
+opts.Update(env)
+print "VARIABLE =", repr(env['VARIABLE'])
+"""
+
+test.write(['bin', 'opts.cfg'], """\
+import os
+import os.path
+os.chdir(os.path.split(__name__)[0])
+execfile('opts2.cfg')
+""")
+
+test.write(['bin', 'opts2.cfg'], """\
+VARIABLE = 'opts2.cfg value'
+""")
+
+test.write(['subdir', 'SConscript'], SConscript_contents)
+
+expect = """\
+VARIABLE = 'opts2.cfg value'
+"""
+
+test.run(arguments = '-q -Q .', stdout=expect)
+
+test.pass_test()
diff --git a/test/Variables/help.py b/test/Variables/help.py
new file mode 100644 (file)
index 0000000..dcea68e
--- /dev/null
@@ -0,0 +1,151 @@
+#!/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 the Variables help messages.
+"""
+
+import os.path
+import string
+
+try:
+    True, False
+except NameError:
+    True = (0 == 0)
+    False = (0 != 0)
+
+str_True = str(True)
+str_False = str(False)
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+
+
+workpath = test.workpath()
+qtpath  = os.path.join(workpath, 'qt')
+libpath = os.path.join(qtpath, 'lib')
+libdirvar = os.path.join('$qtdir', 'lib')
+
+test.subdir(qtpath)
+test.subdir(libpath)
+         
+test.write('SConstruct', """
+from SCons.Variables import BoolVariable, EnumVariable, ListVariable, \
+   PackageVariable, PathVariable
+
+list_of_libs = Split('x11 gl qt ical')
+qtdir = r'%(qtpath)s'
+
+opts = Variables(args=ARGUMENTS)
+opts.AddVariables(
+    BoolVariable('warnings', 'compilation with -Wall and similiar', 1),
+    BoolVariable('profile', 'create profiling informations', 0),
+    EnumVariable('debug', 'debug output and symbols', 'no',
+               allowed_values=('yes', 'no', 'full'),
+               map={}, ignorecase=0),  # case sensitive
+    EnumVariable('guilib', 'gui lib to use', 'gtk',
+               allowed_values=('motif', 'gtk', 'kde'),
+               map={}, ignorecase=1), # case insensitive
+    EnumVariable('some', 'some option', 'xaver',
+               allowed_values=('xaver', 'eins'),
+               map={}, ignorecase=2), # make lowercase
+    ListVariable('shared',
+               'libraries to build as shared libraries',
+               'all',
+               names = list_of_libs),
+    PackageVariable('x11',
+                  'use X11 installed here (yes = search some places)',
+                  'yes'),
+    PathVariable('qtdir', 'where the root of Qt is installed', qtdir),
+    PathVariable('qt_libraries',
+               'where the Qt library is installed',
+               r'%(libdirvar)s'),
+    )
+
+env = Environment(variables=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['warnings']
+print env['profile']
+
+Default(env.Alias('dummy', None))
+""" % locals())
+
+
+test.run(arguments='-h',
+         stdout = """\
+scons: Reading SConscript files ...
+%(str_True)s
+%(str_False)s
+scons: done reading SConscript files.
+
+warnings: compilation with -Wall and similiar (yes|no)
+    default: 1
+    actual: %(str_True)s
+
+profile: create profiling informations (yes|no)
+    default: 0
+    actual: %(str_False)s
+
+debug: debug output and symbols (yes|no|full)
+    default: no
+    actual: no
+
+guilib: gui lib to use (motif|gtk|kde)
+    default: gtk
+    actual: gtk
+
+some: some option (xaver|eins)
+    default: xaver
+    actual: xaver
+
+shared: libraries to build as shared libraries
+    (all|none|comma-separated list of names)
+    allowed names: x11 gl qt ical
+    default: all
+    actual: x11 gl qt ical
+
+x11: use X11 installed here (yes = search some places)
+    ( yes | no | /path/to/x11 )
+    default: yes
+    actual: %(str_True)s
+
+qtdir: where the root of Qt is installed ( /path/to/qtdir )
+    default: %(qtpath)s
+    actual: %(qtpath)s
+
+qt_libraries: where the Qt library is installed ( /path/to/qt_libraries )
+    default: %(libdirvar)s
+    actual: %(libpath)s
+
+Use scons -H for help about command-line options.
+""" % locals())
+
+
+
+test.pass_test()
diff --git a/test/Variables/import.py b/test/Variables/import.py
new file mode 100644 (file)
index 0000000..899870b
--- /dev/null
@@ -0,0 +1,69 @@
+#!/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 an Variables file in a different directory can import
+a module in that directory.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+workpath = test.workpath('')
+
+test.subdir('bin', 'subdir')
+
+test.write('SConstruct', """\
+opts = Variables('../bin/opts.cfg', ARGUMENTS)
+opts.Add('VARIABLE')
+Export("opts")
+SConscript('subdir/SConscript')
+""")
+
+SConscript_contents = """\
+Import("opts")
+env = Environment()
+opts.Update(env)
+print "VARIABLE =", env.get('VARIABLE')
+"""
+
+test.write(['bin', 'opts.cfg'], """\
+import sys
+from local_options import VARIABLE
+""" % locals())
+
+test.write(['bin', 'local_options.py'], """\
+VARIABLE = 'bin/local_options.py'
+""")
+
+test.write(['subdir', 'SConscript'], SConscript_contents)
+
+expect = "VARIABLE = bin/local_options.py\n"
+
+test.run(arguments = '-q -Q .', stdout = expect)
+
+test.pass_test()
index 0e5f15622cc7f668c70f5eb0b5d1a89ae1aa5f2a..3c5050ec565dd35ae8e17ff313ebefa950784c96 100644 (file)
@@ -104,7 +104,7 @@ graph:        GRAPH_T
 """)
 
 import sys
-if sys.platform == 'darwin':
+if sys.platform[:6] == 'darwin':
    file_hpp = 'file.cpp.h'
 else:
    file_hpp = 'file.hpp'
index b2a89743268a88de96001c44ac52be25b757e9d3..3cfe6975b7e5a69910193e95436ebf92e6a3ddbb 100644 (file)
@@ -49,7 +49,10 @@ f1(void)
 """)
 
 test.run(arguments = ".", stderr=r"""
-scons: \*\*\* Dependency cycle: .*foo1.* -> .*foo3.* -> .*foo2.* -> .*foo1.*
+scons: \*\*\* Found dependency cycle\(s\):
+  .*foo1.* -> .*foo3.* -> .*foo2.* -> .*foo1.*
+  .*foo3.* -> .*foo2.* -> .*foo1.* -> .*foo3.*
+
 .*
 """, status=2)
 
diff --git a/test/duplicate-sources.py b/test/duplicate-sources.py
new file mode 100644 (file)
index 0000000..8d517e5
--- /dev/null
@@ -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 specifying a source file more than once works correctly
+and dos not cause a rebuild.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+def cat(target, source, env):
+    t = open(str(target[0]), 'wb')
+    for s in source:
+        t.write(open(str(s), 'rb').read())
+    t.close()
+env = Environment(BUILDERS = {'Cat' : Builder(action = cat)})
+env.Cat('out.txt', ['f1.in', 'f2.in', 'f1.in'])
+""")
+
+test.write('f1.in', "f1.in\n")
+test.write('f2.in', "f2.in\n")
+
+test.run(arguments='--debug=explain .')
+
+test.must_match('out.txt', "f1.in\nf2.in\nf1.in\n")
+
+test.up_to_date(options='--debug=explain', arguments='.')
+
+test.pass_test()
index c4bfb378b9fbeb043fef9bd4a836822590812dd5..b0939c0cb14fe91aaaeaf60c2a3134ba26f75ced 100644 (file)
@@ -109,7 +109,7 @@ test.run(arguments = '.', status = 2, stderr = expected_stderr)
 
 expected_stderr_list = [
     "scons: *** [out.f1] Error 1\n",
-    "scons: *** Source `in.f2' not found, needed by target `out.f2'.  Stop.\n",
+    "scons: *** Source `in.f2' not found, needed by target `out.f2'.\n",
     "scons: *** [out.f3] Error 1\n",
 ]
 
@@ -120,7 +120,7 @@ expected_stderr_list = [
 # walk of '.' and are already considered up-to-date when we kick off the
 # "simultaneous" builds of the output (target) files.
 
-test.run(arguments = '-j7 .', status = 2, stderr = None)
+test.run(arguments = '-j7 -k .', status = 2, stderr = None)
 
 missing = []
 for es in expected_stderr_list:
index 168a880caeecd7a1efb2dd745692450512163548..3b6ba977c54bd03c156c88d96966c664a2a5321e 100644 (file)
@@ -45,10 +45,10 @@ test.subdir('dir1')
 test.subdir('dir2')
 
 test.write('SConstruct', """\
-opts = Options()
-opts.AddOptions(
-    BoolOption('view_all_dependencies', 'View all dependencies', True),
-    BoolOption('duplicate', 'Duplicate sources to variant dir', True)
+opts = Variables()
+opts.AddVariables(
+    BoolVariable('view_all_dependencies', 'View all dependencies', True),
+    BoolVariable('duplicate', 'Duplicate sources to variant dir', True)
 )
 
 env = Environment(options=opts)
index 433d6d04019d55acbb1e89b675f4ecd49bbc56ae..970dbd439347d43957298a16945fb5d01a189d53 100644 (file)
@@ -38,11 +38,9 @@ test.write('SConstruct', "")
 # by the packaging build.
 copyright_marker = '__' + 'COPYRIGHT' + '__'
 
-copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
-
 fmt = '(%s|Copyright \\(c\\) %s The SCons Foundation)\n'
 
-copyright_line = fmt % (copyright_marker, copyright_years)
+copyright_line = fmt % (copyright_marker, TestSCons.copyright_years)
 
 # Windows may or may not print a line for the script version
 # depending on whether it's invoked through scons.py or scons.bat.
similarity index 95%
rename from test/option-d.py
rename to test/option/d.py
index 58bc9e480b90bf7d3848f3b71676b6e61ba8c6d5..0f8a9a55dba3a8714b5992189335af8471eee5f9 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+"""
+Verify that the -d option is ignored.
+"""
+
 import TestSCons
 
 test = TestSCons.TestSCons()
 
-test.pass_test()        #XXX Short-circuit until this is supported.
+test.write('SConstruct', "")
+
+test.run(arguments = '-d .',
+         stderr = "Warning:  ignoring -d option\n")
+
+test.pass_test()
+
+#
 
 test.subdir('subdir')
 
similarity index 86%
rename from test/option-e.py
rename to test/option/environment-overrides.py
index 1862ae7dc3d5f0c3d8678f90c9bc3e3a8f7770aa..078bfeaeeb830a14a03e81ea721838031419b38d 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+"""
+Verify that the -e and --environment-overrides options are ignored.
+"""
+
 import TestSCons
-import string
-import sys
 
 test = TestSCons.TestSCons()
 
 test.write('SConstruct', "")
 
 test.run(arguments = '-e .',
-         stderr = "Warning:  the -e option is not yet implemented\n")
+         stderr = "Warning:  ignoring -e option\n")
 
 test.run(arguments = '--environment-overrides .',
-         stderr = "Warning:  the --environment-overrides option is not yet implemented\n")
+         stderr = "Warning:  ignoring --environment-overrides option\n")
 
 test.pass_test()
  
similarity index 79%
rename from test/option--H.py
rename to test/option/help-options.py
index 0cf3b2bf45f43b3be8f9613f55f841ee30c438cb..5f8270fa2b44e20e70b6b875036948b1c3515110 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-import copy
+"""
+Verify behavior of the -H and --help-options options.
+"""
+
+import re
 import string
-import sys
 
 import TestSCons
 
@@ -41,16 +44,25 @@ test.fail_test(string.find(test.stdout(), '--debug=TYPE') == -1)
 
 # Validate that the help output lists the options in case-insensitive
 # alphabetical order.
-lines = string.split(test.stdout(), '\n')
+
+# Don't include in the sorted comparison the options that are ignored
+# for compatibility.  They're all printed at the top of the list.
+ignored_re = re.compile('.*Ignored for compatibility\\.\n', re.S)
+stdout = ignored_re.sub('', test.stdout())
+
+lines = string.split(stdout, '\n')
 lines = filter(lambda x: x[:3] == '  -', lines)
 lines = map(lambda x: x[3:], lines)
 lines = map(lambda x: x[0] == '-' and x[1:] or x, lines)
 options = map(lambda x: string.split(x)[0], lines)
 options = map(lambda x: x[-1] == ',' and x[:-1] or x, options)
 lowered = map(lambda x: string.lower(x), options)
-sorted = copy.copy(lowered)
+sorted = lowered[:]
 sorted.sort()
-test.fail_test(lowered != sorted)
+if lowered != sorted:
+    print "lowered =", lowered
+    print "sorted =", sorted
+    test.fail_test()
 
 test.pass_test()
  
similarity index 90%
rename from test/option--npd.py
rename to test/option/no-print-directory.py
index 352196a3e70e03a3b681b4ca50820a58700ba76a..bb0abbf697b84c8b9a2e5fb227927a593ce3304a 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+"""
+Verify that the --no-print-directory option is ignored.
+"""
+
 import TestSCons
-import string
-import sys
 
 test = TestSCons.TestSCons()
 
 test.write('SConstruct', "")
 
 test.run(arguments = '--no-print-directory .',
-         stderr = "Warning:  the --no-print-directory option is not yet implemented\n")
+         stderr = "Warning:  ignoring --no-print-directory option\n")
 
 test.pass_test()
  
similarity index 87%
rename from test/option-w.py
rename to test/option/print-directory.py
index 759131c3afcef26f94b1e7a70f39dafc68157241..a5d8fcf677a1e6e883211c55439b6c861b701b55 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+"""
+Verify that the -w and --print-directory options are ignored.
+"""
+
 import TestSCons
-import string
-import sys
 
 test = TestSCons.TestSCons()
 
 test.write('SConstruct', "")
 
 test.run(arguments = '-w .',
-         stderr = "Warning:  the -w option is not yet implemented\n")
+         stderr = "Warning:  ignoring -w option\n")
 
 test.run(arguments = '--print-directory .',
-         stderr = "Warning:  the --print-directory option is not yet implemented\n")
+         stderr = "Warning:  ignoring --print-directory option\n")
 
 test.pass_test()
  
index 31395046f16c15952ba871cf707cc8b5bb575e07..30e15aa0d609cecb08122df5809c65f95053075d 100644 (file)
@@ -46,23 +46,43 @@ env.Command('Tfile.mid', 'Tfile.in', Copy('$TARGET', '$SOURCE'))
 test.write('Tfile.in', "Tfile.in\n")
 
 expect_stdout = test.wrap_stdout("""\
-Taskmaster: '.': children:
-    ['SConstruct', 'Tfile.in', 'Tfile.mid', 'Tfile.out']
-    waiting on unfinished children:
-    ['SConstruct', 'Tfile.in', 'Tfile.mid', 'Tfile.out']
-Taskmaster: 'SConstruct': evaluating SConstruct
-Taskmaster: 'Tfile.in': evaluating Tfile.in
-Taskmaster: 'Tfile.mid': children:
-    ['Tfile.in']
-    evaluating Tfile.mid
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   '.'> and its children:
+Taskmaster:        <no_state   'SConstruct'>
+Taskmaster:        <no_state   'Tfile.in'>
+Taskmaster:        <no_state   'Tfile.mid'>
+Taskmaster:        <no_state   'Tfile.out'>
+Taskmaster:     Considering node <no_state   'SConstruct'> and its children:
+Taskmaster: Evaluating <pending    'SConstruct'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending    'Tfile.in'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'Tfile.mid'> and its children:
+Taskmaster:        <up_to_date 'Tfile.in'>
+Taskmaster: Evaluating <pending    'Tfile.mid'>
 Copy("Tfile.mid", "Tfile.in")
-Taskmaster: 'Tfile.out': children:
-    ['Tfile.mid']
-    evaluating Tfile.out
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'Tfile.out'> and its children:
+Taskmaster:        <executed   'Tfile.mid'>
+Taskmaster: Evaluating <pending    'Tfile.out'>
 Copy("Tfile.out", "Tfile.mid")
-Taskmaster: '.': children:
-    ['SConstruct', 'Tfile.in', 'Tfile.mid', 'Tfile.out']
-    evaluating .
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <pending    '.'> and its children:
+Taskmaster:        <up_to_date 'SConstruct'>
+Taskmaster:        <up_to_date 'Tfile.in'>
+Taskmaster:        <executed   'Tfile.mid'>
+Taskmaster:        <executed   'Tfile.out'>
+Taskmaster: Evaluating <pending    '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
+
 """)
 
 test.run(arguments='--taskmastertrace=- .', stdout=expect_stdout)
@@ -81,21 +101,41 @@ Copy("Tfile.out", "Tfile.mid")
 test.run(arguments='--taskmastertrace=trace.out .', stdout=expect_stdout)
 
 expect_trace = """\
-Taskmaster: '.': children:
-    ['SConstruct', 'Tfile.in', 'Tfile.mid', 'Tfile.out']
-    waiting on unfinished children:
-    ['SConstruct', 'Tfile.in', 'Tfile.mid', 'Tfile.out']
-Taskmaster: 'SConstruct': evaluating SConstruct
-Taskmaster: 'Tfile.in': evaluating Tfile.in
-Taskmaster: 'Tfile.mid': children:
-    ['Tfile.in']
-    evaluating Tfile.mid
-Taskmaster: 'Tfile.out': children:
-    ['Tfile.mid']
-    evaluating Tfile.out
-Taskmaster: '.': children:
-    ['SConstruct', 'Tfile.in', 'Tfile.mid', 'Tfile.out']
-    evaluating .
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   '.'> and its children:
+Taskmaster:        <no_state   'SConstruct'>
+Taskmaster:        <no_state   'Tfile.in'>
+Taskmaster:        <no_state   'Tfile.mid'>
+Taskmaster:        <no_state   'Tfile.out'>
+Taskmaster:     Considering node <no_state   'SConstruct'> and its children:
+Taskmaster: Evaluating <pending    'SConstruct'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending    'Tfile.in'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'Tfile.mid'> and its children:
+Taskmaster:        <up_to_date 'Tfile.in'>
+Taskmaster: Evaluating <pending    'Tfile.mid'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <no_state   'Tfile.out'> and its children:
+Taskmaster:        <executed   'Tfile.mid'>
+Taskmaster: Evaluating <pending    'Tfile.out'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster:     Considering node <pending    '.'> and its children:
+Taskmaster:        <up_to_date 'SConstruct'>
+Taskmaster:        <up_to_date 'Tfile.in'>
+Taskmaster:        <executed   'Tfile.mid'>
+Taskmaster:        <executed   'Tfile.out'>
+Taskmaster: Evaluating <pending    '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
+
 """
 
 test.must_match('trace.out', expect_trace)
index 163d286a3a54ff93f52a77d63a61384c25c139b1..0a0af7dc506d1e421763bfffd802c8b4adf931fe 100644 (file)
@@ -97,8 +97,8 @@ tree1 = """
 """ % locals()
 
 test.run(arguments = "--tree=all Foo.xxx")
-if string.find(test.stdout(), tree1) == -1:
-    sys.stdout.write('Did not find expected tree in the following output:\n')
+if string.count(test.stdout(), tree1) != 1:
+    sys.stdout.write('Did not find expected tree (or found duplicate) in the following output:\n')
     sys.stdout.write(test.stdout())
     test.fail_test()
 
@@ -163,14 +163,14 @@ tree3 = """
 """ % locals()
 
 test.run(arguments = "--tree=all,prune .")
-if string.find(test.stdout(), tree3) == -1:
-    sys.stdout.write('Did not find expected tree in the following output:\n')
+if string.count(test.stdout(), tree3) != 1:
+    sys.stdout.write('Did not find expected tree (or found duplicate) in the following output:\n')
     sys.stdout.write(test.stdout())
     test.fail_test()
 
 test.run(arguments = "--tree=prune .")
-if string.find(test.stdout(), tree3) == -1:
-    sys.stdout.write('Did not find expected tree in the following output:\n')
+if string.count(test.stdout(), tree3) != 1:
+    sys.stdout.write('Did not find expected tree (or found duplicate) in the following output:\n')
     sys.stdout.write(test.stdout())
     test.fail_test()
 
@@ -203,14 +203,14 @@ tree4 = """
 test.run(arguments = '-c Foo.xxx')
 
 test.run(arguments = "--no-exec --tree=all,status Foo.xxx")
-if string.find(test.stdout(), tree4) == -1:
-    sys.stdout.write('Did not find expected tree in the following output:\n')
+if string.count(test.stdout(), tree4) != 1:
+    sys.stdout.write('Did not find expected tree (or found duplicate) in the following output:\n')
     sys.stdout.write(test.stdout())
     test.fail_test()
 
 test.run(arguments = "--no-exec --tree=status Foo.xxx")
-if string.find(test.stdout(), tree4) == -1:
-    sys.stdout.write('Did not find expected tree in the following output:\n')
+if string.count(test.stdout(), tree4) != 1:
+    sys.stdout.write('Did not find expected tree (or found duplicate) in the following output:\n')
     sys.stdout.write(test.stdout())
     test.fail_test()
 
@@ -226,8 +226,8 @@ THIS SHOULD CAUSE A BUILD FAILURE
 test.run(arguments = "--tree=all Foo.xxx",
          status = 2,
          stderr = None)
-if string.find(test.stdout(), tree1) == -1:
-    sys.stdout.write('Did not find expected tree in the following output:\n')
+if string.count(test.stdout(), tree1) != 1:
+    sys.stdout.write('Did not find expected tree (or found duplicate) in the following output:\n')
     sys.stdout.write(test.stdout())
     test.fail_test()
 
index fc29d50d2a695c82382fd30362e8a24b505e28e8..8858b74423c710dd5674b331cc9a67ad9e2be9a4 100644 (file)
@@ -47,7 +47,7 @@ env = Environment(LIBPREFIX='',
                   EXESUFFIX='.exe')
 env.AppendENVPath('PATH', '.')
 l = env.Library( 'util.lib', 'util.c' )
-p = env.Program( 'test.exe', 'main.c', LIBS=l )
+p = env.Program( 'test_tree_lib.exe', 'main.c', LIBS=l )
 env.Command( 'foo.h', p, '$SOURCE > $TARGET')
 """)
 
@@ -71,7 +71,7 @@ util(void)
 """)
 
 expect = """
-  +-test.exe
+  +-test_tree_lib.exe
     +-main.obj
     +-util.lib
       +-util.obj
index b5ea1e0cb514173a45690bfd9cbeb3b054de371b..61ef07d8f5508277f6cad1e6798df5f3625fc58d 100644 (file)
@@ -51,7 +51,7 @@ test.write('foo.c', """\
 test.symlink('nonexistent', 'foo.h')
 
 expect = """\
-scons: *** Source `foo.h' not found, needed by target `%s'.  Stop.
+scons: *** Implicit dependency `foo.h' not found, needed by target `%s'.  Stop.
 """% foo_obj
 
 test.run(arguments = '.', status = 2, stderr = expect)