Merged revisions 2898-2901,2903-2927 via svnmerge from
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 30 Apr 2008 05:39:46 +0000 (05:39 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 30 Apr 2008 05:39:46 +0000 (05:39 +0000)
http://scons.tigris.org/svn/scons/branches/core

........
  r2903 | stevenknight | 2008-04-22 08:23:52 -0700 (Tue, 22 Apr 2008) | 3 lines

  Issue 2019:  don't blow up when a "dictionary" source_scanner doesn't
  have a more-specific scanner entry for a specified source file's suffix.
........
  r2904 | stevenknight | 2008-04-23 11:13:19 -0700 (Wed, 23 Apr 2008) | 3 lines

  Fix regression in use of $CXXFLAGS by the default $SHCXXFLAGS setting.
  (Greg Noel)
........
  r2905 | stevenknight | 2008-04-23 11:23:12 -0700 (Wed, 23 Apr 2008) | 3 lines

  Update the test/CXX/SHCXXFLAGS.py with some more modern idioms copied
  from test/CXX/CXXFLAGS.py.
........
  r2921 | belley | 2008-04-28 08:52:13 -0700 (Mon, 28 Apr 2008) | 98 lines

  Candidate list must be flushed when stopping the taskmaster.
  ============================================================

  The taskmaster's candidate list must be flushed when the taskmaster is stopped,
  either because of a build error or a keyboard interrupt. This involves failing
  each candidates along with their waiting parents.  This is necessary because the
  waiting parents might be in the pending_children set() that is used by the
  taskmaster to determine if cycles exist in the dependency graph. Failure to do
  so leads to confusing error messages such as:

  scons: *** Found dependency cycle(s):

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/DynamicProperty.h
  (<SCons.Node.FS.File instance at 0x95d230c>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Fault/NotSupported.h
  (<SCons.Node.FS.File instance at 0x960634c>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Fault/InvalidRequest.h
  (<SCons.Node.FS.File instance at 0x95d2bac>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Fault/RequestCanceled.h
  (<SCons.Node.FS.File instance at 0x960648c>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Fault/InvalidType.h
  (<SCons.Node.FS.File instance at 0x95d2cec>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Core/InvalidProperty.h
  (<SCons.Node.FS.File instance at 0x9606d2c>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Fault/SecurityError.h
  (<SCons.Node.FS.File instance at 0x96065cc>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Core/PropertyCollector.h
  (<SCons.Node.FS.File instance at 0x9606e6c>)

    Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/Fault/InvalidArgument.h
  (<SCons.Node.FS.File instance at 0x95d2a6c>)

  Internal Error: no cycle found for node
  /bld/vmkernel-main/scons/build/vmodl/obj/generic/Vmodl/Vmomi/DynamicData.h
  (<SCons.Node.FS.File instance at 0x95d21cc>)

  Changes made:

  Job.py:
  ------

  o Implemented an InterruptState object that allows the sharing of the
  interrupt state between the Jobs, the Serial/Parallel and the Worker
  thread object. This allows for somewhat faster interruption when
  Ctrl-C is pressed because the Worker thread will not start any new
  task, i.e. the ones siting in the request queue.

  Taskmaster.py:
  --------------

  o Moved the code contained the function Task.fail_continue() to the
  function Taskmaster.will_not_build(). This function perform the
  clean-up for nodes that will never be built. This includes failing
  these nodes along with their waiting parents and removing them from
  the list of pending children.

  o Improved the taskmaster tracing so that the pending children ref
  counts of nodes is also printed.

  o When stopping the taskmaster, no_next_candidate() now calls
  will_not_build() repetitively to make sure that all candidate nodes
  that will never be build are properly cleaned-up.

  test/Parallel/multiple-parents.py
  ---------------------------------

  Improved the test to also exercise the following functionality:

  o Nodes with multiple targets (not just aliases as it was before).
  o SideEffect()
  o Interrupting the build.

  Submitted against changeset 2920.
  All tests pass with both Python 2.5.1 and 1.5.2

  Benoit
........
  r2922 | stevenknight | 2008-04-28 11:50:10 -0700 (Mon, 28 Apr 2008) | 26 lines

  Issue 2024:  http://scons.tigris.org/issues/show_bug.cgi?id=2024

  Some public packages were importing specific SCons.Options.*Option
  submodules directly, instead of using the SCons.Options package
  itself (which actually exposed all the SCons.Options.*Option variable
  names directly, but what they hey).  Our scripts didn't test for
  direct importing of the submodules, so this broke when I implemented
  backwards comptability with a SCons/Options.py module, instead of a full
  SCons/Options/*.py package.

  This restores the SCons/Options/*.py package files with stub modules that
  provide backwards compatibility (by referring to the new SCons.Variables
  functions and classes) and allow for importing all of

  The test/Deprecated/Options/*Option.py scripts have had specific submodule
  imports added.  To make sure this corresponding "feature" continues to
  work in the future, I'll be following this with another checkin that
  adds submodule imports for the SCons.Variables.*Variable modules.

  I ran this with the SCons/Options.py file in place, and also with
  a compiled SCons/Options.pyc file in place, to try to make sure it
  works regardless.  We've seen issues on Debian where their packaging
  doesn't clean up old *.pyc files when a Python *.py module is removed.
  It looks like this should work regardless of whether the Options.pyc
  file is there or not.
........
  r2923 | stevenknight | 2008-04-28 11:52:52 -0700 (Mon, 28 Apr 2008) | 3 lines

  Update the test/Variables/*Variable.py scripts to verify that importing
  SCons.Variables.*Variable submodules continues to work in the future.
........
  r2924 | belley | 2008-04-28 14:06:54 -0700 (Mon, 28 Apr 2008) | 6 lines

  Fixed a test that my morning change broke. The taskmaster tracing now
  includes the pending children ref count.

  Benoit
........
  r2925 | stevenknight | 2008-04-28 18:00:25 -0700 (Mon, 28 Apr 2008) | 4 lines

  Issue 2028: Make sure already-existing Entry Nodes that we find
  are converted to Dir Nodes when searching a *PATH list for implicit
  dependencies.
........
  r2926 | stevenknight | 2008-04-29 08:11:40 -0700 (Tue, 29 Apr 2008) | 3 lines

  Issue 2034:  Fix $FORTRANMODDIR values like ${TARGET.dir} in the
  default $_FORTANMODFLAG expansion.  (Stefano)
........
  r2927 | stevenknight | 2008-04-29 11:34:02 -0700 (Tue, 29 Apr 2008) | 3 lines

  Restore the SCons.Options package (maintains backwards compatibility of
  importing submodules) to setup.py.
........

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

35 files changed:
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Executor.py
src/engine/SCons/Job.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Options/BoolOption.py [new file with mode: 0644]
src/engine/SCons/Options/EnumOption.py [new file with mode: 0644]
src/engine/SCons/Options/ListOption.py [new file with mode: 0644]
src/engine/SCons/Options/PackageOption.py [new file with mode: 0644]
src/engine/SCons/Options/PathOption.py [new file with mode: 0644]
src/engine/SCons/Options/__init__.py [moved from src/engine/SCons/Options.py with 89% similarity]
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
src/engine/SCons/Tool/FortranCommon.py
src/engine/SCons/Tool/c++.py
src/engine/SCons/Tool/g++.py
src/setup.py
test/CXX/CXXFLAGS.py [new file with mode: 0644]
test/CXX/SHCXXFLAGS.py
test/Deprecated/Options/BoolOption.py
test/Deprecated/Options/EnumOption.py
test/Deprecated/Options/ListOption.py
test/Deprecated/Options/PackageOption.py
test/Deprecated/Options/PathOption.py
test/Fortran/FORTRANMODDIR.py
test/Interactive/taskmastertrace.py
test/Parallel/multiple-parents.py
test/Scanner/Entry-to-Dir.py [new file with mode: 0644]
test/Scanner/source_scanner-dict.py [new file with mode: 0644]
test/Variables/BoolVariable.py
test/Variables/EnumVariable.py
test/Variables/ListVariable.py
test/Variables/PackageVariable.py
test/Variables/PathVariable.py
test/option/taskmastertrace.py

index 37422bc587435e2f403ca86b4107f1190a2c1eee..8a3515f9532503882a8cf54174861fd9e5978426 100644 (file)
@@ -8,6 +8,30 @@
 
 
 
+RELEASE XXX - XXX
+
+  From Greg Noel:
+
+  - Fix use of $CXXFLAGS when building C++ shared object files.
+
+  From Steven Knight:
+
+  - Fix a regression when a Builder's source_scanner doesn't select
+    a more specific scanner for the suffix of a specified source file.
+
+  - Fix the Options object backwards compatibility so people can still
+    "import SCons.Options.{Bool,Enum,List,Package,Path}Option" submodules.
+
+  - Fix searching for implicit dependencies when an Entry Node shows up
+    in the search path list.
+
+  From Stefano:
+
+  - Fix expansion of $FORTRANMODDIR in the default Fortran command line(s)
+    when it's set to something like ${TARGET.dir}.
+
+
+
 RELEASE 0.98.2 - Sun, 20 Apr 2008 23:38:56 -0700
 
   From Steven Knight:
index 4c8b7a7615bf8b686ce8595dfe000417a50603b6..f220946af72879a217ebb2f6633ce44df73927c4 100644 (file)
@@ -28,7 +28,12 @@ SCons/Node/__init__.py
 SCons/Node/Alias.py
 SCons/Node/FS.py
 SCons/Node/Python.py
-SCons/Options.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/PathList.py
 SCons/Platform/__init__.py
 SCons/Platform/aix.py
index a9417bca26b848a8daa6eac156f69994b30f0e1b..435b54ca20fa701c1c3d0b1bc710a5bde47e400e 100644 (file)
@@ -237,11 +237,11 @@ class Executor:
         if scanner:
             for node in node_list:
                 node.disambiguate()
-                scanner = scanner.select(node)
-                if not scanner:
+                s = scanner.select(node)
+                if not s:
                     continue
-                path = self.get_build_scanner_path(scanner)
-                deps.extend(node.get_implicit_deps(env, scanner, path))
+                path = self.get_build_scanner_path(s)
+                deps.extend(node.get_implicit_deps(env, s, path))
         else:
             kw = self.get_kw()
             for node in node_list:
index d93952c2c4f838c1117106de51787c7927499165..545df98ce15dfb43601ddf1d47ef15f6623c223b 100644 (file)
@@ -49,6 +49,18 @@ default_stack_size = 256
 
 interrupt_msg = 'Build interrupted.'
 
+
+class InterruptState:
+   def __init__(self):
+       self.interrupted = False
+
+   def set(self):
+       self.interrupted = True
+
+   def __call__(self):
+       return self.interrupted
+
+
 class Jobs:
     """An instance of this class initializes N jobs, and provides
     methods for starting, stopping, and waiting on all N jobs.
@@ -84,8 +96,6 @@ class Jobs:
             self.job = Serial(taskmaster)
             self.num_jobs = 1
 
-        self.job.interrupted = False
-
     def run(self, postfunc=lambda: None):
         """Run the jobs.
 
@@ -104,7 +114,7 @@ class Jobs:
 
     def were_interrupted(self):
         """Returns whether the jobs were interrupted by a signal."""
-        return self.job.interrupted
+        return self.job.interrupted()
 
     def _setup_sig_handler(self):
         """Setup an interrupt handler so that SCons can shutdown cleanly in
@@ -127,10 +137,10 @@ class Jobs:
         SCons forks before executing another process. In that case, we
         want the child to exit immediately.
         """
-        def handler(signum, stack, parentpid=os.getpid()):
+        def handler(signum, stack, self=self, parentpid=os.getpid()):
             if os.getpid() == parentpid:
                 self.job.taskmaster.stop()
-                self.job.interrupted = True
+                self.job.interrupted.set()
             else:
                 os._exit(2)
 
@@ -170,6 +180,7 @@ class Serial:
         execute (e.g. execute() raised an exception)."""
         
         self.taskmaster = taskmaster
+        self.interrupted = InterruptState()
 
     def start(self):
         """Start the job. This will begin pulling tasks from the taskmaster
@@ -188,7 +199,7 @@ class Serial:
                 if task.needs_execute():
                     task.execute()
             except:
-                if self.interrupted:
+                if self.interrupted():
                     try:
                         raise SCons.Errors.BuildError(
                             task.targets[0], errstr=interrupt_msg)
@@ -221,11 +232,12 @@ else:
         dequeues the task, executes it, and posts a tuple including the task
         and a boolean indicating whether the task executed successfully. """
 
-        def __init__(self, requestQueue, resultsQueue):
+        def __init__(self, requestQueue, resultsQueue, interrupted):
             threading.Thread.__init__(self)
             self.setDaemon(1)
             self.requestQueue = requestQueue
             self.resultsQueue = resultsQueue
+            self.interrupted = interrupted
             self.start()
 
         def run(self):
@@ -239,6 +251,9 @@ else:
                     break
 
                 try:
+                    if self.interrupted():
+                        raise SCons.Errors.BuildError(
+                            task.targets[0], errstr=interrupt_msg)
                     task.execute()
                 except:
                     task.exception_set()
@@ -251,7 +266,7 @@ else:
     class ThreadPool:
         """This class is responsible for spawning and managing worker threads."""
 
-        def __init__(self, num, stack_size):
+        def __init__(self, num, stack_size, interrupted):
             """Create the request and reply queues, and 'num' worker threads.
             
             One must specify the stack size of the worker threads. The
@@ -277,7 +292,7 @@ else:
             # Create worker threads
             self.workers = []
             for _ in range(num):
-                worker = Worker(self.requestQueue, self.resultsQueue)
+                worker = Worker(self.requestQueue, self.resultsQueue, interrupted)
                 self.workers.append(worker)
 
             # Once we drop Python 1.5 we can change the following to:
@@ -348,7 +363,8 @@ else:
             multiple tasks simultaneously. """
 
             self.taskmaster = taskmaster
-            self.tp = ThreadPool(num, stack_size)
+            self.interrupted = InterruptState()
+            self.tp = ThreadPool(num, stack_size, self.interrupted)
 
             self.maxjobs = num
 
@@ -395,7 +411,7 @@ else:
                     if ok:
                         task.executed()
                     else:
-                        if self.interrupted:
+                        if self.interrupted():
                             try:
                                 raise SCons.Errors.BuildError(
                                     task.targets[0], errstr=interrupt_msg)
index 6ff3833464965ffd8aab47852d71ab7d5e914981..bd8b564de22908106e73bf26339e2d92ea3404c8 100644 (file)
@@ -2873,9 +2873,10 @@ class FileFinder:
             node = p.entries[norm_name]
         except KeyError:
             return p.dir_on_disk(name)
-        # Once we move to Python 2.2 we can do:
-        #if isinstance(node, (Dir, Entry)):
-        if isinstance(node, Dir) or isinstance(node, Entry):
+        if isinstance(node, Dir):
+            return node
+        if isinstance(node, Entry):
+            node.must_be_same(Dir)
             return node
         return None
 
@@ -2944,8 +2945,11 @@ class FileFinder:
             #        node = p.entries[norm_name]
             #    except KeyError:
             #        return p.dir_on_disk(name)
-            #    # Once we move to Python 2.2 we can do:
-            #    #if isinstance(node, (Dir, Entry)):
+            #    if isinstance(node, Dir):
+            #        return node
+            #    if isinstance(node, Entry):
+            #        node.must_be_same(Dir)
+            #        return node
             #    if isinstance(node, Dir) or isinstance(node, Entry):
             #        return node
             #    return None
diff --git a/src/engine/SCons/Options/BoolOption.py b/src/engine/SCons/Options/BoolOption.py
new file mode 100644 (file)
index 0000000..c3b50b3
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# __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
+
+BoolOption = SCons.Variables.BoolVariable
diff --git a/src/engine/SCons/Options/EnumOption.py b/src/engine/SCons/Options/EnumOption.py
new file mode 100644 (file)
index 0000000..24ccf28
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# __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
+
+EnumOption = SCons.Variables.EnumVariable
diff --git a/src/engine/SCons/Options/ListOption.py b/src/engine/SCons/Options/ListOption.py
new file mode 100644 (file)
index 0000000..1a56806
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# __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
+
+ListOption = SCons.Variables.ListVariable
diff --git a/src/engine/SCons/Options/PackageOption.py b/src/engine/SCons/Options/PackageOption.py
new file mode 100644 (file)
index 0000000..479a8a2
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# __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
+
+PackageOption = SCons.Variables.PackageVariable
diff --git a/src/engine/SCons/Options/PathOption.py b/src/engine/SCons/Options/PathOption.py
new file mode 100644 (file)
index 0000000..e694127
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# __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
+
+PathOption = SCons.Variables.PathVariable
similarity index 89%
rename from src/engine/SCons/Options.py
rename to src/engine/SCons/Options/__init__.py
index 0f2fde1e088adb66a426ced90f78ead4d1a1e4fb..5cc09a7fd578990999e2d16e1f37cff7a6bba5ab 100644 (file)
@@ -32,6 +32,12 @@ and will then be removed entirely (some day).
 
 import SCons.Variables
 
+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
+
 class Options(SCons.Variables.Variables):
 
     def AddOptions(self, *args, **kw):
@@ -48,9 +54,3 @@ class Options(SCons.Variables.Variables):
         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
index 8a0fcf79ad0824f04a69f19689c0fbbc9933b7ed..6f93ca7d932db3ab04578f906fc982025502c8a2 100644 (file)
@@ -277,9 +277,9 @@ class Task:
         Explicit stop-the-build failure.
         """
         
-        # Invoke fail_continue() to clean-up the pending children
+        # Invoke will_not_build() to clean-up the pending children
         # list.
-        self.fail_continue()
+        self.tm.will_not_build(self.targets)
 
         # Tell the taskmaster to not start any new tasks
         self.tm.stop()
@@ -297,44 +297,8 @@ class Task:
         This sets failure status on the target nodes and all of
         their dependent parent nodes.
         """
+        self.tm.will_not_build(self.targets)
         
-        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.
-            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):
         """
         Marks all targets in a task ready for execution.
@@ -557,7 +521,15 @@ class Taskmaster:
     def no_next_candidate(self):
         """
         Stops Taskmaster processing by not returning a next candidate.
-        """
+        
+        Note that we have to clean-up the Taskmaster candidate list
+        because the cycle detection depends on the fact all nodes have
+        been processed somehow.
+        """
+        while self.candidates:
+            candidates = self.candidates
+            self.candidates = []
+            self.will_not_build(candidates)
         return None
 
     def _find_next_ready_node(self):
@@ -604,9 +576,9 @@ class Taskmaster:
                 S.considered = S.considered + 1
             else:
                 S = None
-  
-            if T: T.write('Taskmaster:     Considering node <%-10s %s> and its children:\n' % 
-                          (StateString[node.get_state()], repr(str(node))))
+
+            if T: T.write('Taskmaster:     Considering node <%-10s %-3s %s> and its children:\n' % 
+                          (StateString[node.get_state()], node.ref_count, repr(str(node))))
 
             if state == NODE_NO_STATE:
                 # Mark this node as being on the execution stack:
@@ -643,8 +615,8 @@ class Taskmaster:
             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 T: T.write('Taskmaster:        <%-10s %-3s %s>\n' % 
+                              (StateString[childstate], child.ref_count, repr(str(child))))
 
                 if childstate == NODE_NO_STATE:
                     children_not_visited.append(child)
@@ -685,8 +657,8 @@ class Taskmaster:
                 node.set_state(NODE_FAILED)
 
                 if S: S.child_failed = S.child_failed + 1
-                if T: T.write('Taskmaster:****** <%-10s %s>\n' % 
-                              (StateString[node.get_state()], repr(str(node))))
+                if T: T.write('Taskmaster:****** <%-10s %-3s %s>\n' % 
+                              (StateString[node.get_state()], node.ref_count, repr(str(node))))
                 continue
 
             if children_not_ready:
@@ -700,6 +672,8 @@ class Taskmaster:
                     # 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)
+                    if T: T.write('Taskmaster:      adjusting ref count: <%-10s %-3s %s>\n' %
+                                  (StateString[node.get_state()], node.ref_count, repr(str(node))))
 
                 self.pending_children = self.pending_children | children_pending
                 
@@ -720,8 +694,8 @@ class Taskmaster:
             # 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('Taskmaster: Evaluating <%-10s %s>\n' % 
-                          (StateString[node.get_state()], repr(str(node))))
+            if T: T.write('Taskmaster: Evaluating <%-10s %-3s %s>\n' % 
+                          (StateString[node.get_state()], node.ref_count, repr(str(node))))
             return node
 
         return None
@@ -757,6 +731,48 @@ class Taskmaster:
 
         return task
 
+    def will_not_build(self, nodes):
+        """
+        Perform clean-up about nodes that will never be built.
+        """
+
+        pending_children = self.pending_children
+
+        to_visit = set()
+        for node in nodes:
+            # Set failure state on all of the parents that were dependent
+            # on this failed build.
+            if node.state != NODE_FAILED:
+                node.state = NODE_FAILED
+                parents = node.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.pending_children = pending_children
+
     def stop(self):
         """
         Stops the current build completely.
@@ -774,6 +790,8 @@ class Taskmaster:
                 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)) 
+                    desc = desc + \
+                        "  Internal Error: no cycle found for node %s (%s) in state %s\n" %  \
+                        (node, repr(node), StateString[node.get_state()]) 
+
             raise SCons.Errors.UserError, desc
index 88d30782fe458b30bbd8a0ecbc7171f2c2719427..932458179f0550f8859b16b2468b3401cb4c7f74 100644 (file)
@@ -1053,23 +1053,24 @@ class TaskmasterTestCase(unittest.TestCase):
         expect = """\
 
 Taskmaster: Looking for a node to evaluate
-Taskmaster:     Considering node <no_state   'n1'> and its children:
-Taskmaster: Evaluating <pending    'n1'>
+Taskmaster:     Considering node <no_state   0   'n1'> and its children:
+Taskmaster: Evaluating <pending    0   'n1'>
 
 Taskmaster: Looking for a node to evaluate
-Taskmaster:     Considering node <executed   'n1'> and its children:
+Taskmaster:     Considering node <executed   0   '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:     Considering node <no_state   0   'n3'> and its children:
+Taskmaster:        <executed   0   'n1'>
+Taskmaster:        <no_state   0   'n2'>
+Taskmaster:      adjusting ref count: <pending    1   'n3'>
+Taskmaster:     Considering node <no_state   0   'n2'> and its children:
+Taskmaster: Evaluating <pending    0   '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:     Considering node <pending    0   'n3'> and its children:
+Taskmaster:        <executed   0   'n1'>
+Taskmaster:        <executed   0   'n2'>
+Taskmaster: Evaluating <pending    0   'n3'>
 
 Taskmaster: Looking for a node to evaluate
 Taskmaster: No candidate anymore.
index 82a5a6c065517a2ca084f8a3ae938408c2ef7e61..825cbe53d1d4e004bdff0b6da3f47a5bbd1c4f6b 100644 (file)
@@ -183,7 +183,7 @@ def add_fortran_to_env(env):
     env['FORTRANMODDIR'] = ''          # where the compiler should place .mod files
     env['FORTRANMODDIRPREFIX'] = ''    # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
     env['FORTRANMODDIRSUFFIX'] = ''    # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
-    env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs)} $)'
+    env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
 
 def add_f77_to_env(env):
     """Add Builders and construction variables for f77 to an Environment."""
index a4375c0110ed91b3ea0ed96dad3417167008610c..f360ed823673610c35b0421c39e1231b41c840d9 100644 (file)
@@ -76,7 +76,7 @@ def generate(env):
     env['CXXFLAGS']   = SCons.Util.CLVar('')
     env['CXXCOM']     = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
     env['SHCXX']      = '$CXX'
-    env['SHCXXFLAGS'] = SCons.Util.CLVar('')
+    env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
     env['SHCXXCOM']   = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
 
     env['CPPDEFPREFIX']  = '-D'
index 9c7e4772bfa5680e2b870228ae4e057ec71e1abc..917229faafc3fbe1fa8a0031dbb561615efe5f84 100644 (file)
@@ -53,26 +53,14 @@ def generate(env):
     env['CXX']        = env.Detect(compilers)
 
     # platform specific settings
-    if env['PLATFORM'] == 'cygwin':
-        env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
-    elif env['PLATFORM'] == 'aix':
-        # Original line from Christian Engel added -DPIC:
-        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC -mminimal-toc')
+    if env['PLATFORM'] == 'aix':
         env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc')
         env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
         env['SHOBJSUFFIX'] = '$OBJSUFFIX'
     elif env['PLATFORM'] == 'hpux':
-        # Original line from Christian Engel added -DPIC:
-        #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 -DPIC')
         env['SHOBJSUFFIX'] = '.pic.o'
-    else:
-        # Original line from Christian Engel added -DPIC:
-        #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC')
-        pass
     # determine compiler version
     if env['CXX']:
         line = os.popen(env['CXX'] + ' --version').readline()
index 281a69cd972c0a1938c0b70dacaa3955e9aa862f..5b2212c5ea6c29ac6f1035fad326ed01e48a756b 100644 (file)
@@ -376,6 +376,7 @@ arguments = {
     'packages'         : ["SCons",
                           "SCons.compat",
                           "SCons.Node",
+                          "SCons.Options",
                           "SCons.Platform",
                           "SCons.Scanner",
                           "SCons.Script",
diff --git a/test/CXX/CXXFLAGS.py b/test/CXX/CXXFLAGS.py
new file mode 100644 (file)
index 0000000..86f14f0
--- /dev/null
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that $CXXFLAGS settings are used to build both static
+and shared object files.
+"""
+
+import os
+import string
+import sys
+
+import TestSCons
+
+_obj = TestSCons._obj
+
+if os.name == 'posix':
+    os.environ['LD_LIBRARY_PATH'] = '.'
+if string.find(sys.platform, 'irix') > -1:
+    os.environ['LD_LIBRARYN32_PATH'] = '.'
+
+test = TestSCons.TestSCons()
+
+e = test.Environment()
+
+test.write('SConstruct', """
+foo = Environment(WINDOWS_INSERT_DEF=1)
+foo.Append(CXXFLAGS = '-DFOO') 
+bar = Environment(WINDOWS_INSERT_DEF=1)
+bar.Append(CXXFLAGS = '-DBAR') 
+foo_obj = foo.SharedObject(target = 'fooshared%(_obj)s', source = 'doIt.cpp')
+bar_obj = bar.SharedObject(target = 'barshared%(_obj)s', source = 'doIt.cpp')
+foo.SharedLibrary(target = 'foo', source = foo_obj)
+bar.SharedLibrary(target = 'bar', source = bar_obj)
+
+fooMain = foo.Clone(LIBS='foo', LIBPATH='.')
+foo_obj = fooMain.Object(target='foomain', source='main.c')
+fooMain.Program(target='fooprog', source=foo_obj)
+
+barMain = bar.Clone(LIBS='bar', LIBPATH='.')
+bar_obj = barMain.Object(target='barmain', source='main.c')
+barMain.Program(target='barprog', source=bar_obj)
+
+foo_obj = foo.Object(target = 'foostatic', source = 'prog.cpp')
+bar_obj = bar.Object(target = 'barstatic', source = 'prog.cpp')
+foo.Program(target = 'foo', source = foo_obj)
+bar.Program(target = 'bar', source = bar_obj)
+""" % locals())
+
+test.write('foo.def', r"""
+LIBRARY        "foo"
+DESCRIPTION    "Foo Shared Library"
+
+EXPORTS
+   doIt
+""")
+
+test.write('bar.def', r"""
+LIBRARY        "bar"
+DESCRIPTION    "Bar Shared Library"
+
+EXPORTS
+   doIt
+""")
+
+test.write('doIt.cpp', r"""
+#include <stdio.h>
+
+extern "C" void
+doIt()
+{
+#ifdef FOO
+        printf("doIt.cpp:  FOO\n");
+#endif
+#ifdef BAR
+        printf("doIt.cpp:  BAR\n");
+#endif
+}
+""")
+
+test.write('main.c', r"""
+
+void doIt();
+
+int
+main(int argc, char* argv[])
+{
+    doIt();
+    return 0;
+}
+""")
+
+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('fooprog'), stdout = "doIt.cpp:  FOO\n")
+test.run(program = test.workpath('barprog'), stdout = "doIt.cpp:  BAR\n")
+test.run(program = test.workpath('foo'), stdout = "prog.c:  FOO\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c:  BAR\n")
+
+
+
+test.write('SConstruct', """
+bar = Environment(WINDOWS_INSERT_DEF=1)
+bar.Append(CXXFLAGS = '-DBAR') 
+foo_obj = bar.SharedObject(target = 'foo%(_obj)s', source = 'doIt.cpp')
+bar_obj = bar.SharedObject(target = 'bar%(_obj)s', source = 'doIt.cpp')
+bar.SharedLibrary(target = 'foo', source = foo_obj)
+bar.SharedLibrary(target = 'bar', source = bar_obj)
+
+barMain = bar.Clone(LIBS='bar', LIBPATH='.')
+foo_obj = barMain.Object(target='foomain', source='main.c')
+bar_obj = barMain.Object(target='barmain', source='main.c')
+barMain.Program(target='barprog', source=foo_obj)
+barMain.Program(target='fooprog', source=bar_obj)
+""" % locals())
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('fooprog'), stdout = "doIt.cpp:  BAR\n")
+test.run(program = test.workpath('barprog'), stdout = "doIt.cpp:  BAR\n")
+
+
+
+test.pass_test()
index bfe1a79e7c56f4483632088f1fad4a3b7d0c4c58..0ce2b15ba1d18c54201a493ef7a79fe22c1c89e9 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-import sys
-import TestSCons
+"""
+Verify that $SHCXXFLAGS settings are used to build shared object files.
+"""
+
 import os
 import string
+import sys
+
+import TestSCons
 
 _obj = TestSCons._obj
 
@@ -39,16 +44,16 @@ if string.find(sys.platform, 'irix') > -1:
 test = TestSCons.TestSCons()
 
 e = test.Environment()
-fooflags = e['SHCXXFLAGS'] + ' -DFOO'
-barflags = e['SHCXXFLAGS'] + ' -DBAR'
 
 test.write('SConstruct', """
-foo = Environment(SHCXXFLAGS = '%s', WINDOWS_INSERT_DEF=1)
-bar = Environment(SHCXXFLAGS = '%s', WINDOWS_INSERT_DEF=1)
-foo.SharedObject(target = 'foo%s', source = 'prog.cpp')
-bar.SharedObject(target = 'bar%s', source = 'prog.cpp')
-foo.SharedLibrary(target = 'foo', source = 'foo%s')
-bar.SharedLibrary(target = 'bar', source = 'bar%s')
+foo = Environment(WINDOWS_INSERT_DEF=1)
+foo.Append(SHCXXFLAGS = '-DFOO') 
+bar = Environment(WINDOWS_INSERT_DEF=1)
+bar.Append(SHCXXFLAGS = '-DBAR') 
+foo_obj = foo.SharedObject(target = 'foo%(_obj)s', source = 'prog.cpp')
+bar_obj = bar.SharedObject(target = 'bar%(_obj)s', source = 'prog.cpp')
+foo.SharedLibrary(target = 'foo', source = foo_obj)
+bar.SharedLibrary(target = 'bar', source = bar_obj)
 
 fooMain = foo.Clone(LIBS='foo', LIBPATH='.')
 foo_obj = fooMain.Object(target='foomain', source='main.c')
@@ -57,7 +62,7 @@ fooMain.Program(target='fooprog', source=foo_obj)
 barMain = bar.Clone(LIBS='bar', LIBPATH='.')
 bar_obj = barMain.Object(target='barmain', source='main.c')
 barMain.Program(target='barprog', source=bar_obj)
-""" % (fooflags, barflags, _obj, _obj, _obj, _obj))
+""" % locals())
 
 test.write('foo.def', r"""
 LIBRARY        "foo"
@@ -108,18 +113,19 @@ test.run(program = test.workpath('fooprog'), stdout = "prog.cpp:  FOO\n")
 test.run(program = test.workpath('barprog'), stdout = "prog.cpp:  BAR\n")
 
 test.write('SConstruct', """
-bar = Environment(SHCXXFLAGS = '%s', WINDOWS_INSERT_DEF=1)
-bar.SharedObject(target = 'foo%s', source = 'prog.cpp')
-bar.SharedObject(target = 'bar%s', source = 'prog.cpp')
-bar.SharedLibrary(target = 'foo', source = 'foo%s')
-bar.SharedLibrary(target = 'bar', source = 'bar%s')
+bar = Environment(WINDOWS_INSERT_DEF=1)
+bar.Append(SHCXXFLAGS = '-DBAR') 
+foo_obj = bar.SharedObject(target = 'foo%(_obj)s', source = 'prog.cpp')
+bar_obj = bar.SharedObject(target = 'bar%(_obj)s', source = 'prog.cpp')
+bar.SharedLibrary(target = 'foo', source = foo_obj)
+bar.SharedLibrary(target = 'bar', source = bar_obj)
 
 barMain = bar.Clone(LIBS='bar', LIBPATH='.')
 foo_obj = barMain.Object(target='foomain', source='main.c')
 bar_obj = barMain.Object(target='barmain', source='main.c')
 barMain.Program(target='barprog', source=foo_obj)
 barMain.Program(target='fooprog', source=bar_obj)
-""" % (barflags, _obj, _obj, _obj, _obj))
+""" % locals())
 
 test.run(arguments = '.')
 
index 38b30d5db765e845a614eddafc79f03b44cf0d98..d45ad1bcd38a70e9f91e745c9ef48de4b55dd2f8 100644 (file)
@@ -50,12 +50,15 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Options.BoolOption import BoolOption
+BO = BoolOption
+
 from SCons.Options import BoolOption
 
 opts = Options(args=ARGUMENTS)
 opts.AddOptions(
     BoolOption('warnings', 'compilation with -Wall and similiar', 1),
-    BoolOption('profile', 'create profiling informations', 0),
+    BO('profile', 'create profiling informations', 0),
     )
 
 env = Environment(options=opts)
@@ -78,7 +81,7 @@ 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.python_file_line(SConstruct_path, 12)
 
 test.run(arguments='warnings=irgendwas', stderr = expect_stderr, status=2)
 
index 610a4854d44b45e7d287afbc1989ca21af2f9c48..29724f3d2fc88f37d6bbd85a44a6ec10ceec34aa 100644 (file)
@@ -44,6 +44,9 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Options.EnumOption import EnumOption
+EO = EnumOption
+
 from SCons.Options import EnumOption
 
 list_of_libs = Split('x11 gl qt ical')
@@ -56,9 +59,9 @@ opts.AddOptions(
     EnumOption('guilib', 'gui lib to use', 'gtk',
                allowed_values=('motif', 'gtk', 'kde'),
                map={}, ignorecase=1), # case insensitive
-    EnumOption('some', 'some option', 'xaver',
-               allowed_values=('xaver', 'eins'),
-               map={}, ignorecase=2), # make lowercase
+    EO('some', 'some option', 'xaver',
+       allowed_values=('xaver', 'eins'),
+       map={}, ignorecase=2), # make lowercase
     )
 
 env = Environment(options=opts)
@@ -82,19 +85,19 @@ check(['full', 'KdE', 'eins'])
 
 expect_stderr = """
 scons: *** Invalid value for option debug: FULL
-""" + test.python_file_line(SConstruct_path, 18)
+""" + test.python_file_line(SConstruct_path, 21)
 
 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.python_file_line(SConstruct_path, 21)
 
 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.python_file_line(SConstruct_path, 21)
 
 test.run(arguments='some=IrGeNdwas', stderr=expect_stderr, status=2)
 
index 1ce20a2979040bf4a031cd76a6fb182eb95db6ac..7e7b18fbd84c82f67de5aff1a650fb5a45043599 100644 (file)
@@ -47,6 +47,9 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Options.ListOption import ListOption
+LO = ListOption
+
 from SCons.Options import ListOption
 
 list_of_libs = Split('x11 gl qt ical')
@@ -59,6 +62,7 @@ opts.AddOptions(
                'all',
                names = list_of_libs,
                map = {'GL':'gl', 'QT':'qt'}),
+    LO('listvariable', 'listvariable help', 'all', names=['l1', 'l2', 'l3'])
     )
 
 env = Environment(options=opts)
@@ -81,7 +85,8 @@ 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)
+expect = "shared = 'all'"+os.linesep+"listvariable = 'all'"+os.linesep
+test.must_match(test.workpath('scons.options'), expect)
 
 check(['all', '1', 'gl ical qt x11', 'gl ical qt x11',
        "['gl ical qt x11']"])
@@ -110,7 +115,7 @@ 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.python_file_line(SConstruct_path, 19)
 
 test.run(arguments='shared=foo', stderr=expect_stderr, status=2)
 
@@ -119,28 +124,28 @@ test.run(arguments='shared=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.python_file_line(SConstruct_path, 19)
 
 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.python_file_line(SConstruct_path, 19)
 
 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.python_file_line(SConstruct_path, 19)
 
 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.python_file_line(SConstruct_path, 19)
 
 test.run(arguments='shared=foo,x11,,,bar', stderr=expect_stderr, status=2)
 
index 79d4dda7ccf2cf6e6c7b7ca4a1de4d9ce4b7632b..b7dbc7107cc7aad4dfa1b0bafbda63e920918a28 100644 (file)
@@ -50,6 +50,9 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Options.PackageOption import PackageOption
+PO = PackageOption
+
 from SCons.Options import PackageOption
 
 opts = Options(args=ARGUMENTS)
@@ -57,6 +60,7 @@ opts.AddOptions(
     PackageOption('x11',
                   'use X11 installed here (yes = search some places',
                   'yes'),
+    PO('package', 'help for package', 'yes'),
     )
 
 env = Environment(options=opts)
@@ -80,7 +84,7 @@ check([test.workpath()])
 
 expect_stderr = """
 scons: *** Path does not exist for option x11: /non/existing/path/
-""" + test.python_file_line(SConstruct_path, 10)
+""" + test.python_file_line(SConstruct_path, 14)
 
 test.run(arguments='x11=/non/existing/path/', stderr=expect_stderr, status=2)
 
index 23fec6e5d370333cc3d8b66b6bfc09d6a0e1be9a..3fa7e818d2b3de789e7d926ab465c5305eb7b6ec 100644 (file)
@@ -49,6 +49,9 @@ workpath = test.workpath()
 libpath = os.path.join(workpath, 'lib')
 
 test.write(SConstruct_path, """\
+from SCons.Options.PathOption import PathOption
+PO = PathOption
+
 from SCons.Options import PathOption
 
 qtdir = r'%s'
@@ -56,7 +59,7 @@ qtdir = r'%s'
 opts = Options(args=ARGUMENTS)
 opts.AddOptions(
     PathOption('qtdir', 'where the root of Qt is installed', qtdir),
-    PathOption('qt_libraries', 'where the Qt library is installed', r'%s'),
+    PO('qt_libraries', 'where the Qt library is installed', r'%s'),
     )
 
 env = Environment(options=opts)
@@ -90,7 +93,7 @@ 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]
+SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 14)[:-1]
 
 expect_stderr = """
 scons: *** Path for option qtdir does not exist: %(qtpath)s
index a81439abf8ba78aa9c647a49112848a87b2ec114..c0e158d79b6ebf67bfee9e39a6655260736f3970 100644 (file)
@@ -54,7 +54,10 @@ test.write('SConstruct', """
 env = Environment(FORTRANCOM = r'%(_python_)s myfortran.py $FORTRANMODDIR $SOURCE $TARGET',
                   FORTRANMODDIR = 'modules')
 env.Object(target = 'test1.obj', source = 'test1.f')
-env.Object(target = 'sub/test2.obj', source = 'test1.f',
+env.Object(target = 'sub2/test2.obj', source = 'test1.f',
+           FORTRANMODDIR='${TARGET.dir}')
+env.Object(target = 'sub3/test3.obj', source = 'test1.f',
+           FORTRANCOM = r'%(_python_)s myfortran.py $_FORTRANMODFLAG $SOURCE $TARGET',
            FORTRANMODDIR='${TARGET.dir}')
 """ % locals())
 
@@ -88,8 +91,11 @@ test.must_match('test1.obj', "myfortran.py wrote test1.obj\n")
 test.must_match(['modules', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n")
 test.must_not_exist(['modules', 'mod_bar.mod'])
 
-test.must_match(['sub', 'test2.obj'], "myfortran.py wrote test2.obj\n")
-test.must_match(['sub', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n")
+test.must_match(['sub2', 'test2.obj'], "myfortran.py wrote test2.obj\n")
+test.must_match(['sub2', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n")
+
+test.must_match(['sub3', 'test3.obj'], "myfortran.py wrote test3.obj\n")
+test.must_match(['sub3', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n")
 
 test.up_to_date(arguments = '.')
 
index f0ff81faa83bff04a02e20e3df951e3e9484a586..e78bdfa2ad686186b2e1db67a5af41818d686033 100644 (file)
@@ -56,6 +56,8 @@ test.write('foo.in', "foo.in 2\n")
 
 scons.send("build --taskmastertrace=- foo.out\n")
 
+test.wait_for(test.workpath('foo.out'))
+
 scons.send("build 2\n")
 
 test.wait_for(test.workpath('2'))
@@ -71,15 +73,16 @@ scons>>> Copy("foo.out", "foo.in")
 Touch("1")
 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:     Considering node <no_state   0   'foo.out'> and its children:
+Taskmaster:        <no_state   0   'foo.in'>
+Taskmaster:      adjusting ref count: <pending    1   'foo.out'>
+Taskmaster:     Considering node <no_state   0   'foo.in'> and its children:
+Taskmaster: Evaluating <pending    0   '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'>
+Taskmaster:     Considering node <pending    0   'foo.out'> and its children:
+Taskmaster:        <up_to_date 0   'foo.in'>
+Taskmaster: Evaluating <pending    0   'foo.out'>
 Copy("foo.out", "foo.in")
 
 Taskmaster: Looking for a node to evaluate
index f81adf59d445bc1efb3e88c38f3c44e9d3aed0c9..d9c414ff806859a232dee2b237fe9144878cbab7 100644 (file)
@@ -50,34 +50,69 @@ test = TestSCons.TestSCons()
 #    c) Some targets succeed building
 #    d) Some children are ignored
 #    e) Some children are pre-requesites
-#    f) Some sources are missing
+#    f) Some children have side-effects
+#    g) Some sources are missing
+#    h) Builds that are interrupted
 
 test.write('SConstruct', """
+opts = Options()
+opts.Add( BoolOption('interrupt', 'Interrupt the build.', 0 ) )
+optEnv = Environment(options=opts)
+
 def fail_action(target = None, source = None, env = None):
     return 2
 
+def simulate_keyboard_interrupt(target = None, source = None, env = None):
+    # Directly invoked the SIGINT handler to simulate a
+    # KeyboardInterrupt. This hack is necessary because there is no
+    # easy way to get access to the current Job/Taskmaster object.
+    import signal
+    handler = signal.getsignal(signal.SIGINT)
+    handler(signal.SIGINT, None)
+    return 0
+
+interrupt = Command(target='interrupt',  source='', action=simulate_keyboard_interrupt)
+
+
 failed0  = Command(target='failed00',  source='', action=fail_action)
-ok0      = Command(target='ok00',      source='', action=Touch('${TARGET}'))
+ok0      = Command(target=['ok00a', 'ok00b', 'ok00c'], 
+                   source='', 
+                   action=[Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}'), Touch('${TARGETS[2]}')])
 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}'))
+withSE0  = Command(target=['withSE00a', 'withSE00b', 'withSE00c'], 
+                   source='', 
+                   action=[Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}'), Touch('${TARGETS[2]}'), 
+                           Touch('side_effect')])
+SideEffect('side_effect', withSE0) 
 
-prev_level  = failed0 + ok0 + ignore0
+prev_level  = failed0 + ok0 + ignore0 + missing0 + withSE0
 prev_prereq = prereq0
 prev_ignore = ignore0
 prev_igreq  = igreq0
 
+if optEnv['interrupt']:
+    prev_level = prev_level + interrupt
+
 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}'))
+    ok     = Command(target=['ok%02da' % i, 'ok%02db' % i, 'ok%02dc' % i], 
+                     source='',
+                     action=[Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}'), Touch('${TARGETS[2]}')])
     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}'))
+    withSE  = Command(target=['withSE%02da' % i, 'withSE%02db' % i, 'withSE%02dc' % i], 
+                       source='', 
+                       action=[Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}'), Touch('${TARGETS[2]}'), 
+                               Touch('side_effect')])
+    SideEffect('side_effect', withSE) 
 
-    next_level = failed + ok + ignore + igreq + missing
+    next_level = failed + ok + ignore + igreq + missing + withSE
 
     for j in range(1,10):
         a = Alias('a%02d%02d' % (i,j), prev_level)
@@ -108,7 +143,8 @@ Default(all)
 
 re_error = """\
 (scons: \\*\\*\\* \\[failed\\d+] Error 2\\n)|\
-(scons: \\*\\*\\* Source `MissingSrc' not found, needed by target `missing\\d+'\\.(  Stop\\.)?\\n)\
+(scons: \\*\\*\\* Source `MissingSrc' not found, needed by target `missing\\d+'\\.(  Stop\\.)?\\n)|\
+(scons: \\*\\*\\* \\[\\w+] Build interrupted\.\\n)\
 """
 
 re_errors = "(" + re_error + ")+"
@@ -116,7 +152,17 @@ re_errors = "(" + re_error + ")+"
 test.run(arguments = 'all',
          status = 2,
          stderr = "scons: *** [failed19] Error 2\n")
-test.must_not_exist(test.workpath('ok'))
+test.must_not_exist(test.workpath('side_effect'))
+for i in range(20):
+    test.must_not_exist(test.workpath('ok%02da' % i))
+    test.must_not_exist(test.workpath('ok%02db' % i))
+    test.must_not_exist(test.workpath('ok%02dc' % i))
+    test.must_not_exist(test.workpath('prereq%02d' % i))
+    test.must_not_exist(test.workpath('ignore%02d' % i))
+    test.must_not_exist(test.workpath('igreq%02d' % i))
+    test.must_not_exist(test.workpath('withSE%02da' % i))
+    test.must_not_exist(test.workpath('withSE%02db' % i))
+    test.must_not_exist(test.workpath('withSE%02dc' % i))
 
 
 for i in range(5):
@@ -135,11 +181,17 @@ for i in range(5):
              status = 2,
              stderr = re_errors,
              match=TestSCons.match_re_dotall)
+    test.must_exist(test.workpath('side_effect'))
     for i in range(20):
-        test.must_exist(test.workpath('ok%02d' % i))
+        test.must_exist(test.workpath('ok%02da' % i))
+        test.must_exist(test.workpath('ok%02db' % i))
+        test.must_exist(test.workpath('ok%02dc' % 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.must_exist(test.workpath('withSE%02da' % i))
+        test.must_exist(test.workpath('withSE%02db' % i))
+        test.must_exist(test.workpath('withSE%02dc' % i))
 
 
 for i in range(5):
@@ -147,7 +199,7 @@ for i in range(5):
              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')
@@ -157,6 +209,7 @@ for i in range(5):
              stderr = re_errors,
              match=TestSCons.match_re_dotall)
 
+
 for i in range(5):
     test.run(arguments = '-c all')
 
@@ -164,10 +217,26 @@ for i in range(5):
              status = 2,
              stderr = re_errors,
              match=TestSCons.match_re_dotall)
+    test.must_exist(test.workpath('side_effect'))
     for i in range(20):
-        test.must_exist(test.workpath('ok%02d' % i))
+        test.must_exist(test.workpath('ok%02da' % i))
+        test.must_exist(test.workpath('ok%02db' % i))
+        test.must_exist(test.workpath('ok%02dc' % 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.must_exist(test.workpath('withSE%02da' % i))
+        test.must_exist(test.workpath('withSE%02db' % i))
+        test.must_exist(test.workpath('withSE%02dc' % i))
+
+
+for i in range(5):
+    test.run(arguments = '-c all')
+
+    test.run(arguments = '-j 8 -k --random interrupt=yes all',
+             status = 2,
+             stderr = re_errors,
+             match=TestSCons.match_re_dotall)
+
 
 test.pass_test()
diff --git a/test/Scanner/Entry-to-Dir.py b/test/Scanner/Entry-to-Dir.py
new file mode 100644 (file)
index 0000000..0b5dbd7
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that an implicit dependency search for a directory for which
+we have an Entry Node works as expected, converting the Entry into a
+Dir Node.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment()
+tc = env.Program('testcase', 'testcase.cpp')
+foo = env.Entry('foo')
+tc[0].all_children()[0].all_children()
+""")
+
+test.write('testcase.cpp', """\
+#if 0
+#include "foo/bar/widget.h"
+#endif
+int main(int argc, char *argv[])
+{
+    return 0;
+}
+""")
+
+test.run(arguments = '.')
+
+# In 0.98.2, re-running failed with a stack trace.
+test.run(arguments = '.')
+
+test.pass_test()
diff --git a/test/Scanner/source_scanner-dict.py b/test/Scanner/source_scanner-dict.py
new file mode 100644 (file)
index 0000000..1ac15a8
--- /dev/null
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that a source_scanner that uses a dictionary to select more
+specific scanners for source file suffixes works correctly, even
+when it's handed a file suffix that it doesn't know how to scan
+(i.e., for which it doesn't have a specific scanner in its dictionary).
+"""
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+test.write('build.py', r"""
+import sys
+output = open(sys.argv[1], 'wb')
+for infile in sys.argv[2:]:
+    input = open(infile, 'rb')
+
+    include_prefix = 'include%s ' % infile[-1]
+
+    def process(infp, outfp, include_prefix=include_prefix):
+        for line in infp.readlines():
+            if line[:len(include_prefix)] == include_prefix:
+                file = line[len(include_prefix):-1]
+                process(open(file, 'rb'), outfp)
+            else:
+                outfp.write(line)
+
+    process(input, output)
+
+sys.exit(0)
+""")
+
+# Execute a subsidiary SConscript just to make sure we can
+# get at the Scanner keyword from there.
+
+test.write('SConstruct', """
+SConscript('SConscript')
+""")
+
+test.write('SConscript', """
+import re
+
+include1_re = re.compile(r'^include1\s+(\S+)$', re.M)
+include2_re = re.compile(r'^include2\s+(\S+)$', re.M)
+include3_re = re.compile(r'^include3\s+(\S+)$', re.M)
+
+def k1_scan(node, env, scanpaths, arg=None):
+    contents = node.get_contents()
+    includes = include1_re.findall(contents)
+    return includes
+
+def k2_scan(node, env, scanpaths, arg=None):
+    contents = node.get_contents()
+    includes = include2_re.findall(contents)
+    return includes
+
+def k3_scan(node, env, scanpaths, arg=None):
+    contents = node.get_contents()
+    includes = include3_re.findall(contents)
+    return includes
+
+kscanner = Scanner({'.k1' : Scanner(k1_scan), '.k2': Scanner(k2_scan)})
+
+b = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES',
+            source_scanner=kscanner)
+env = Environment(BUILDERS={'Build':b})
+
+kscanner.add_scanner('.k3', Scanner(k3_scan))
+
+env.Build('aaa', 'aaa.k1')
+env.Build('bbb', 'bbb.k2')
+env.Build('ccc', 'ccc.k3')
+env.Build('ddd', ['ddd.k4', 'aaa.k1', 'bbb.k2', 'ccc.k3'])
+""" % locals())
+
+test.write('aaa.k1', 
+"""aaa.k1 1
+line 2
+include1 xxx
+include2 yyy
+include3 zzz
+line 6
+""")
+
+test.write('bbb.k2', 
+"""bbb.k2 1
+line 2
+include1 xxx
+include2 yyy
+include3 zzz
+line 6
+""")
+
+test.write('ccc.k3', 
+"""ccc.k3 1
+line 2
+include1 xxx
+include2 yyy
+include3 zzz
+line 6
+""")
+
+test.write('ddd.k4', 
+"""ddd.k4 1
+line 2
+line 3
+""")
+
+test.write('xxx', "xxx 1\n")
+test.write('yyy', "yyy 1\n")
+test.write('zzz', "zzz 1\n")
+
+
+
+
+expect = test.wrap_stdout("""\
+%(_python_)s build.py aaa aaa.k1
+%(_python_)s build.py bbb bbb.k2
+%(_python_)s build.py ccc ccc.k3
+%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3
+""" % locals())
+
+test.run(stdout=expect)
+
+expect_aaa = 'aaa.k1 1\nline 2\nxxx 1\ninclude2 yyy\ninclude3 zzz\nline 6\n'
+expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 1\ninclude3 zzz\nline 6\n'
+expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 1\nline 6\n'
+expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc
+
+test.must_match('aaa', expect_aaa)
+test.must_match('bbb', expect_bbb)
+test.must_match('ccc', expect_ccc)
+test.must_match('ddd', expect_ddd)
+
+test.up_to_date(arguments = '.')
+
+
+
+test.write('zzz', "zzz 2\n")
+
+expect = test.wrap_stdout("""\
+%(_python_)s build.py ccc ccc.k3
+%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3
+""" % locals())
+
+test.run(stdout=expect)
+
+expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 2\nline 6\n'
+expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc
+
+test.must_match('bbb', expect_bbb)
+test.must_match('ddd', expect_ddd)
+
+
+
+test.write('yyy', "yyy 2\n")
+
+expect = test.wrap_stdout("""\
+%(_python_)s build.py bbb bbb.k2
+%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3
+""" % locals())
+
+test.run(stdout=expect)
+
+expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 2\ninclude3 zzz\nline 6\n'
+expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc
+
+test.must_match('bbb', expect_bbb)
+test.must_match('ddd', expect_ddd)
+
+
+
+test.write('xxx', "xxx 2\n")
+
+expect = test.wrap_stdout("""\
+%(_python_)s build.py aaa aaa.k1
+%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3
+""" % locals())
+
+test.run(stdout=expect)
+
+expect_aaa = 'aaa.k1 1\nline 2\nxxx 2\ninclude2 yyy\ninclude3 zzz\nline 6\n'
+expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc
+
+test.must_match('aaa', expect_aaa)
+test.must_match('ddd', expect_ddd)
+
+
+
+test.pass_test()
index 1bda19b2b796a638f51da623b6fe3355a639d568..2ebe7072d733dfd114709cd8de04b6aeba6245a7 100644 (file)
@@ -50,12 +50,15 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Variables.BoolVariable import BoolVariable
+BV = BoolVariable
+
 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),
+    BV('profile', 'create profiling informations', 0),
     )
 
 env = Environment(variables=opts)
@@ -78,7 +81,7 @@ 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.python_file_line(SConstruct_path, 12)
 
 test.run(arguments='warnings=irgendwas', stderr = expect_stderr, status=2)
 
index 69665094c0080b529db11e75387e1d710722e575..4d09587a6d3f72c289872faa1645ca317228d632 100644 (file)
@@ -44,6 +44,9 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Variables.EnumVariable import EnumVariable
+EV = EnumVariable
+
 from SCons.Variables import EnumVariable
 
 list_of_libs = Split('x11 gl qt ical')
@@ -56,9 +59,9 @@ opts.AddVariables(
     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
+    EV('some', 'some option', 'xaver',
+       allowed_values=('xaver', 'eins'),
+       map={}, ignorecase=2), # make lowercase
     )
 
 env = Environment(variables=opts)
@@ -82,19 +85,19 @@ check(['full', 'KdE', 'eins'])
 
 expect_stderr = """
 scons: *** Invalid value for option debug: FULL
-""" + test.python_file_line(SConstruct_path, 18)
+""" + test.python_file_line(SConstruct_path, 21)
 
 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.python_file_line(SConstruct_path, 21)
 
 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.python_file_line(SConstruct_path, 21)
 
 test.run(arguments='some=IrGeNdwas', stderr=expect_stderr, status=2)
 
index 90d80ed13b801848b9dcbea366f077ce86d284a3..740698bb4009bc5c1c7e21a763b17645ca6d1e60 100644 (file)
@@ -46,6 +46,9 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Variables.ListVariable import ListVariable
+LV = ListVariable
+
 from SCons.Variables import ListVariable
 
 list_of_libs = Split('x11 gl qt ical')
@@ -58,6 +61,7 @@ opts.AddVariables(
                'all',
                names = list_of_libs,
                map = {'GL':'gl', 'QT':'qt'}),
+    LV('listvariable', 'listvariable help', 'all', names=['l1', 'l2', 'l3'])
     )
 
 env = Environment(variables=opts)
@@ -80,7 +84,8 @@ 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)
+expect = "shared = 'all'"+os.linesep+"listvariable = 'all'"+os.linesep
+test.must_match(test.workpath('scons.variables'), expect)
 
 check(['all', '1', 'gl ical qt x11', 'gl ical qt x11',
        "['gl ical qt x11']"])
@@ -109,7 +114,7 @@ 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.python_file_line(SConstruct_path, 19)
 
 test.run(arguments='shared=foo', stderr=expect_stderr, status=2)
 
@@ -118,28 +123,28 @@ test.run(arguments='shared=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.python_file_line(SConstruct_path, 19)
 
 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.python_file_line(SConstruct_path, 19)
 
 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.python_file_line(SConstruct_path, 19)
 
 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.python_file_line(SConstruct_path, 19)
 
 test.run(arguments='shared=foo,x11,,,bar', stderr=expect_stderr, status=2)
 
index 4d36252f8290a1f5f5013070921bd171b48f5208..433844983e50abc19389558f38725343dbb19ae6 100644 (file)
@@ -50,6 +50,9 @@ def check(expect):
 
 
 test.write(SConstruct_path, """\
+from SCons.Variables.PackageVariable import PackageVariable
+PV = PackageVariable
+
 from SCons.Variables import PackageVariable
 
 opts = Variables(args=ARGUMENTS)
@@ -57,6 +60,7 @@ opts.AddVariables(
     PackageVariable('x11',
                   'use X11 installed here (yes = search some places',
                   'yes'),
+    PV('package', 'help for package', 'yes'),
     )
 
 env = Environment(variables=opts)
@@ -80,7 +84,7 @@ check([test.workpath()])
 
 expect_stderr = """
 scons: *** Path does not exist for option x11: /non/existing/path/
-""" + test.python_file_line(SConstruct_path, 10)
+""" + test.python_file_line(SConstruct_path, 14)
 
 test.run(arguments='x11=/non/existing/path/', stderr=expect_stderr, status=2)
 
index a2f96a77114678bd5a765d63e1b029172627c635..96da82f1f7c9460f35ee79f563b61a0ac4ae3529 100644 (file)
@@ -49,6 +49,9 @@ workpath = test.workpath()
 libpath = os.path.join(workpath, 'lib')
 
 test.write(SConstruct_path, """\
+from SCons.Variables.PathVariable import PathVariable
+PV = PathVariable
+
 from SCons.Variables import PathVariable
 
 qtdir = r'%s'
@@ -56,7 +59,7 @@ 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'),
+    PV('qt_libraries', 'where the Qt library is installed', r'%s'),
     )
 
 env = Environment(variables=opts)
@@ -90,7 +93,7 @@ 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]
+SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 14)[:-1]
 
 expect_stderr = """
 scons: *** Path for option qtdir does not exist: %(qtpath)s
index 30e15aa0d609cecb08122df5809c65f95053075d..a7d583e586cd190ec379103baee57abda5a6a604 100644 (file)
@@ -48,37 +48,41 @@ test.write('Tfile.in', "Tfile.in\n")
 expect_stdout = test.wrap_stdout("""\
 
 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:     Considering node <no_state   0   '.'> and its children:
+Taskmaster:        <no_state   0   'SConstruct'>
+Taskmaster:        <no_state   0   'Tfile.in'>
+Taskmaster:        <no_state   0   'Tfile.mid'>
+Taskmaster:        <no_state   0   'Tfile.out'>
+Taskmaster:      adjusting ref count: <pending    1   '.'>
+Taskmaster:      adjusting ref count: <pending    2   '.'>
+Taskmaster:      adjusting ref count: <pending    3   '.'>
+Taskmaster:      adjusting ref count: <pending    4   '.'>
+Taskmaster:     Considering node <no_state   0   'SConstruct'> and its children:
+Taskmaster: Evaluating <pending    0   'SConstruct'>
 
 Taskmaster: Looking for a node to evaluate
-Taskmaster:     Considering node <no_state   'Tfile.in'> and its children:
-Taskmaster: Evaluating <pending    'Tfile.in'>
+Taskmaster:     Considering node <no_state   0   'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending    0   '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:     Considering node <no_state   0   'Tfile.mid'> and its children:
+Taskmaster:        <up_to_date 0   'Tfile.in'>
+Taskmaster: Evaluating <pending    0   'Tfile.mid'>
 Copy("Tfile.mid", "Tfile.in")
 
 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:     Considering node <no_state   0   'Tfile.out'> and its children:
+Taskmaster:        <executed   0   'Tfile.mid'>
+Taskmaster: Evaluating <pending    0   'Tfile.out'>
 Copy("Tfile.out", "Tfile.mid")
 
 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:     Considering node <pending    0   '.'> and its children:
+Taskmaster:        <up_to_date 0   'SConstruct'>
+Taskmaster:        <up_to_date 0   'Tfile.in'>
+Taskmaster:        <executed   0   'Tfile.mid'>
+Taskmaster:        <executed   0   'Tfile.out'>
+Taskmaster: Evaluating <pending    0   '.'>
 
 Taskmaster: Looking for a node to evaluate
 Taskmaster: No candidate anymore.
@@ -103,35 +107,39 @@ test.run(arguments='--taskmastertrace=trace.out .', stdout=expect_stdout)
 expect_trace = """\
 
 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:     Considering node <no_state   0   '.'> and its children:
+Taskmaster:        <no_state   0   'SConstruct'>
+Taskmaster:        <no_state   0   'Tfile.in'>
+Taskmaster:        <no_state   0   'Tfile.mid'>
+Taskmaster:        <no_state   0   'Tfile.out'>
+Taskmaster:      adjusting ref count: <pending    1   '.'>
+Taskmaster:      adjusting ref count: <pending    2   '.'>
+Taskmaster:      adjusting ref count: <pending    3   '.'>
+Taskmaster:      adjusting ref count: <pending    4   '.'>
+Taskmaster:     Considering node <no_state   0   'SConstruct'> and its children:
+Taskmaster: Evaluating <pending    0   'SConstruct'>
 
 Taskmaster: Looking for a node to evaluate
-Taskmaster:     Considering node <no_state   'Tfile.in'> and its children:
-Taskmaster: Evaluating <pending    'Tfile.in'>
+Taskmaster:     Considering node <no_state   0   'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending    0   '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:     Considering node <no_state   0   'Tfile.mid'> and its children:
+Taskmaster:        <up_to_date 0   'Tfile.in'>
+Taskmaster: Evaluating <pending    0   '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:     Considering node <no_state   0   'Tfile.out'> and its children:
+Taskmaster:        <executed   0   'Tfile.mid'>
+Taskmaster: Evaluating <pending    0   '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:     Considering node <pending    0   '.'> and its children:
+Taskmaster:        <up_to_date 0   'SConstruct'>
+Taskmaster:        <up_to_date 0   'Tfile.in'>
+Taskmaster:        <executed   0   'Tfile.mid'>
+Taskmaster:        <executed   0   'Tfile.out'>
+Taskmaster: Evaluating <pending    0   '.'>
 
 Taskmaster: Looking for a node to evaluate
 Taskmaster: No candidate anymore.