Merged revisions 2928-2932,2934-2946 via svnmerge from
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 18 May 2008 05:12:35 +0000 (05:12 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 18 May 2008 05:12:35 +0000 (05:12 +0000)
http://scons.tigris.org/svn/scons/branches/core

........
  r2932 | garyo | 2008-04-30 09:14:38 -0700 (Wed, 30 Apr 2008) | 1 line

  Add doc for site_scons dir and related options.  Closes issue #1996.
........
  r2934 | stevenknight | 2008-04-30 22:05:38 -0700 (Wed, 30 Apr 2008) | 3 lines

  Issue 2039:  Fix a syntax error in Intel C compiler support on Windows.
  (Jonas Olsson)
........
  r2935 | belley | 2008-05-01 06:59:21 -0700 (Thu, 01 May 2008) | 10 lines

  test/KeyboardInterrupt.py no longer hangs under Cygwin.

  There seems to be a bug on Cygwin where the compiler process hangs
  after sending the SIGINT signal to the process group. It is probably a
  bug in cygwin1.dll, or maybe in the Python 'C' code or the Python
  subprocess module. We therefore do not use 'killpg' on Cygwin.

  Benoit
........
  r2936 | belley | 2008-05-01 07:10:23 -0700 (Thu, 01 May 2008) | 12 lines

  cell_contents not defined in python < 2.5. [Issue 2035]

  Some versions of Python supports lexical scoping of local variables
  (aka closures) but not the cell_contents attribute that would allow
  the FunctionAction signature that take the content of these closure
  cells into account. The access to the cell_contents attribute is now
  protected with a try/except AttributeError so that at least the other
  parts of the function signature are properly computed.

  Benoit
........
  r2937 | stevenknight | 2008-05-01 12:08:25 -0700 (Thu, 01 May 2008) | 3 lines

  When running tests, allow the user to set the $SCONSIGN variable to pick a
  specific sconsign script to execute.
........
  r2938 | stevenknight | 2008-05-02 19:13:14 -0700 (Fri, 02 May 2008) | 2 lines

  Set svn:ignore to '*.py[co]'.
........
  r2939 | stevenknight | 2008-05-08 21:07:18 -0700 (Thu, 08 May 2008) | 4 lines

  Issue 2033:  Fix excessive memory use when a Python Value node's
  representation is stored in a .sconsign file and then re-stored
  after being interpreted with escaped backslashes and quotes.
........
  r2940 | cournape | 2008-05-16 03:56:49 -0700 (Fri, 16 May 2008) | 1 line

  Fix issue 2054.
........
  r2941 | stevenknight | 2008-05-16 12:02:45 -0700 (Fri, 16 May 2008) | 8 lines

  Issue 2045:  After a Node has failed its build and we're propagating
  the failure to other Nodes on the candidate list, don't marke candidate
  Nodes as FAILED if they've already been visited and been determined
  to be up-to-date.  This avoids problems with Configure tests not
  running because failure of an earlier Configure tests caused (e.g.)
  /usr/bin/g++ to get marked as FAILED, making SCons think it doesn't
  need to bother trying to rebuild anything that will use it...
........
  r2942 | stevenknight | 2008-05-16 12:10:14 -0700 (Fri, 16 May 2008) | 8 lines

  Improve the regular expressions used by the test infrastructure to
  examine SCons output to decide if a list of targets are considered
  up-to-date or not.  The new code uses the re.escape() function instead
  of hand-escaping '.' and '\n'.  This required a little restructuring in
  the not_up_to_date() method to escape the characters within the arguments
  themselves, but not the surrounding characters we use to construct the
  regex that makes sure those strings *don't* exist in the output.
........
  r2943 | stevenknight | 2008-05-16 14:04:23 -0700 (Fri, 16 May 2008) | 3 lines

  Issue 2049:  Handle multiple pipe-separated values in Visual Studio for
  INCLUDE, LIB and PATH.  Still only uses Win32, not any other values.
........
  r2944 | stevenknight | 2008-05-16 18:36:27 -0700 (Fri, 16 May 2008) | 2 lines

  Record changes by David Cournapeau and Benoit Belley.
........
  r2945 | stevenknight | 2008-05-17 07:13:46 -0700 (Sat, 17 May 2008) | 3 lines

  Pass in dc as a keyword argument to _smartLink for older Python versions
  without nested scopes.
........
  r2946 | stevenknight | 2008-05-17 07:14:01 -0700 (Sat, 17 May 2008) | 3 lines

  Expect a warning about shadowing global variables on Python 2.1.
  (This code can go away after we release 1.0.)
........

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

26 files changed:
QMTest/TestSCons.py
QMTest/TestSConsign.py
doc/user/add-method.in
doc/user/add-method.xml
doc/user/builders-writing.in
doc/user/builders-writing.xml
doc/user/main.in
doc/user/main.xml
src/CHANGES.txt
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py
src/engine/SCons/Node/Alias.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/Python.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/Tool/dmd.py
src/engine/SCons/Tool/g77.py
src/engine/SCons/Tool/intelc.py
src/engine/SCons/Tool/msvc.py
src/script/sconsign.py
test/Actions/function.py
test/Configure/build-fail.py [new file with mode: 0644]
test/Configure/implicit-cache.py [new file with mode: 0644]
test/KeyboardInterrupt.py
test/Value.py
test/explain/basic.py

index 1277af5749d86f869eb41b79e3e22429165d4ff8..cfcbfb14ca884dc76e81f813d846c1b65cf1453c 100644 (file)
@@ -338,9 +338,8 @@ class TestSCons(TestCommon):
             if options:
                 arguments = options + " " + arguments
         kw['arguments'] = arguments
-        kw['stdout'] = self.wrap_stdout(read_str = read_str, build_str = s)
-        kw['stdout'] = string.replace(kw['stdout'],'\n','\\n')
-        kw['stdout'] = string.replace(kw['stdout'],'.','\\.')
+        stdout = self.wrap_stdout(read_str = read_str, build_str = s)
+        kw['stdout'] = re.escape(stdout)
         kw['match'] = self.match_re_dotall
         apply(self.run, [], kw)
 
@@ -350,14 +349,14 @@ class TestSCons(TestCommon):
         This function is most useful in conjunction with the -n option.
         """
         s = ""
-        for  arg in string.split(arguments):
-            s = s + "(?!scons: `%s' is up to date.)" % arg
+        for arg in string.split(arguments):
+            s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg)
             if options:
                 arguments = options + " " + arguments
+        s = '('+s+'[^\n]*\n)*'
         kw['arguments'] = arguments
-        kw['stdout'] = self.wrap_stdout(build_str="("+s+"[^\n]*\n)*")
-        kw['stdout'] = string.replace(kw['stdout'],'\n','\\n')
-        kw['stdout'] = string.replace(kw['stdout'],'.','\\.')
+        stdout = re.escape(self.wrap_stdout(build_str='ARGUMENTSGOHERE'))
+        kw['stdout'] = string.replace(stdout, 'ARGUMENTSGOHERE', s)
         kw['match'] = self.match_re_dotall
         apply(self.run, [], kw)
 
@@ -833,7 +832,7 @@ print "self._msvs_versions =", str(env['MSVS']['VERSIONS'])
             def __init__(self, p):
                 self.pos = p
 
-        def matchPart(log, logfile, lastEnd):
+        def matchPart(log, logfile, lastEnd, NoMatch=NoMatch):
             m = re.match(log, logfile[lastEnd:])
             if not m:
                 raise NoMatch, lastEnd
index d144040068ea038f30499f979b045912143aba53..fd8ef30614403fdd60600fb431fb259927f4aac7 100644 (file)
@@ -54,13 +54,23 @@ class TestSConsign(TestSCons):
             'interpreter' : python,     # imported from TestSCons
         }
 
-        if os.path.exists(self.script_path('sconsign.py')):
-            sconsign = 'sconsign.py'
-        elif os.path.exists(self.script_path('sconsign')):
-            sconsign = 'sconsign'
-        else:
-            print "Can find neither 'sconsign.py' nor 'sconsign' scripts."
-            self.no_result()
+        if not kw.has_key('program'):
+            kw['program'] = os.environ.get('SCONS')
+            if not kw['program']:
+                if os.path.exists('scons'):
+                    kw['program'] = 'scons'
+                else:
+                    kw['program'] = 'scons.py'
+
+        sconsign = os.environ.get('SCONSIGN')
+        if not sconsign:
+            if os.path.exists(self.script_path('sconsign.py')):
+                sconsign = 'sconsign.py'
+            elif os.path.exists(self.script_path('sconsign')):
+                sconsign = 'sconsign'
+            else:
+                print "Can find neither 'sconsign.py' nor 'sconsign' scripts."
+                self.no_result()
         self.set_sconsign(sconsign)
 
     def script_path(self, script):
index 853b9a862bd7865a36c7c2aa2bbb822dce204dc2..a0a60391c521bbb433817a079658879d5cbf3379 100644 (file)
@@ -72,7 +72,7 @@
 
   <scons_example name="ex2">
      <file name="SConstruct" printme="1">
-     import sys;
+     import sys
      def BuildTestProg(env, testfile, resourcefile, testdir="tests"):
          """Build the test program;
          prepends "test_" to src and target, and puts target into testdir."""
     <scons_output_command>scons -Q</scons_output_command>
   </scons_output>
 
+  <para>
+  Using AddMethod is better than just adding an instance method to an
+  Environment because it gets called as a proper method, and AddMethod
+  provides for copying the method to any copies of the Environment
+  instance.
+  </para>
index 22f5f1a5fdadb91468826adbb2677ac30f955481..7e84b808f4c582b439de1242b09a32fe6d1610fe 100644 (file)
@@ -98,3 +98,9 @@
     cc -o tests/test_stuff test_stuff.o
   </screen>
 
+  <para>
+  Using AddMethod is better than just adding an instance method to an
+  Environment because it gets called as a proper method, and AddMethod
+  provides for copying the method to any copies of the Environment
+  instance.
+  </para>
index 2285ac8894472179b19b5e870c8af4a10948a30d..67a95f924b638b07fa939c3dc11bed333da4ed29 100644 (file)
@@ -761,6 +761,144 @@ This functionality could be invoked as in the following example:
 
   </section>
 
+  <section>
+  <title>Where To Put Your Custom Builders and Tools</title>
+
+  <para>
+
+  The <filename>site_scons</filename> directory gives you a place to
+  put Python modules you can import into your SConscripts
+  (site_scons), add-on tools that can integrate into &SCons;
+  (site_scons/site_tools), and a site_scons/site_init.py file that
+  gets read before any &SConstruct; or &SConscript;, allowing you to
+  change &SCons;'s default behavior.
+
+  </para>
+
+  <para>
+
+  If you get a tool from somewhere (the &SCons; wiki or a third party,
+  for instance) and you'd like to use it in your project, the
+  <filename>site_scons</filename> dir is the simplest place to put it.
+  Tools come in two flavors; either a Python function that operates on
+  an &Environment; or a Python file containing two functions, exists()
+  and generate().
+
+  </para>
+
+  <para>
+
+  A single-function Tool can just be included in your
+  <filename>site_scons/site_init.py</filename> file where it will be
+  parsed and made available for use.  For instance, you could have a
+  <filename>site_scons/site_init.py</filename> file like this:
+
+  </para>
+
+  <scons_example name="site1">
+    <file name="site_scons/site_init.py" printme=1>
+      def TOOL_ADD_HEADER(env):
+         """A Tool to add a header from $HEADER to the source file"""
+         add_header = Builder(action=['echo "$HEADER" > $TARGET',
+                                      'cat $SOURCE >> $TARGET'])
+         env.Append(BUILDERS = {'AddHeader' : add_header})
+         env['HEADER'] = '' # set default value
+    </file>
+    <file name="SConstruct">
+      env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+      env.AddHeader('tgt', 'src')
+    </file>
+    <file name="src">
+      hi there
+    </file>
+  </scons_example>
+
+  <para>
+
+  and a &SConstruct; like this:
+
+  </para>
+
+  <sconstruct>
+      # Use TOOL_ADD_HEADER from site_scons/site_init.py
+      env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+      env.AddHeader('tgt', 'src')
+  </sconstruct>
+
+  <para>
+
+    The <function>TOOL_ADD_HEADER</function> tool method will be
+    called to add the <function>AddHeader</function> tool to the
+    environment.
+
+  </para>
+
+  <!-- 
+  <scons_output example="site1" os="posix">
+     <scons_output_command>scons -Q</scons_output_command>
+  </scons_output>
+  -->
+
+  <para>
+    Similarly, a more full-fledged tool with
+    <function>exists()</function> and <function>generate()</function>
+    methods can be installed in
+    <filename>site_scons/site_tools/toolname.py</filename>.  Since
+    <filename>site_scons/site_tools</filename> is automatically added
+    to the head of the tool search path, any tool found there will be
+    available to all environments.  Furthermore, a tool found there
+    will override a built-in tool of the same name, so if you need to
+    change the behavior of a built-in tool, site_scons gives you the
+    hook you need.
+  </para>
+
+  <para>
+    Many people have a library of utility Python functions they'd like
+    to include in &SConscript;s; just put that module in
+    <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+    choice.  For instance you can do something like this in
+    <filename>site_scons/my_utils.py</filename> to add a build_id method:
+  </para>
+    
+  <scons_example name="site2">
+    <file name="site_scons/my_utils.py" printme=1>
+      def build_id():
+         """Return a build ID (stub version)"""
+        return "100"
+    </file>
+    <file name="SConscript">
+      import my_utils
+      print "build_id=" + my_utils.build_id()
+    </file>
+  </scons_example>
+
+  <para>
+
+  And then in your &SConscript; or any sub-&SConscript; anywhere in
+  your build, you can import <filename>my_utils</filename> and use it:
+
+  </para>
+
+  <sconstruct>
+      import my_utils
+      print "build_id=" + my_utils.build_id()
+  </sconstruct>
+
+  <para>
+
+    If you have a machine-wide site dir you'd like to use instead of
+    <filename>./site_scons</filename>, use the
+    <literal>--site-dir</literal> option to point to your dir.
+    <filename>site_init.py</filename> and
+    <filename>site_tools</filename> will be located under that dir.
+    To avoid using a <filename>site_scons</filename> dir at all, even
+    if it exists, use the <literal>--no-site-dir</literal> option.
+
+  </para>
+
+  </section>
+
+
   <!--
 
   <section>
index 8ab4fcae1b82cc26bf18c8ae9a3073008e153d5d..df3affa8fc7b14a9cde4a5f503dd385fc16441c2 100644 (file)
@@ -659,6 +659,129 @@ This functionality could be invoked as in the following example:
 
   </section>
 
+  <section>
+  <title>Where To Put Your Custom Builders and Tools</title>
+
+  <para>
+
+  The <filename>site_scons</filename> directory gives you a place to
+  put Python modules you can import into your SConscripts
+  (site_scons), add-on tools that can integrate into &SCons;
+  (site_scons/site_tools), and a site_scons/site_init.py file that
+  gets read before any &SConstruct; or &SConscript;, allowing you to
+  change &SCons;'s default behavior.
+
+  </para>
+
+  <para>
+
+  If you get a tool from somewhere (the &SCons; wiki or a third party,
+  for instance) and you'd like to use it in your project, the
+  <filename>site_scons</filename> dir is the simplest place to put it.
+  Tools come in two flavors; either a Python function that operates on
+  an &Environment; or a Python file containing two functions, exists()
+  and generate().
+
+  </para>
+
+  <para>
+
+  A single-function Tool can just be included in your
+  <filename>site_scons/site_init.py</filename> file where it will be
+  parsed and made available for use.  For instance, you could have a
+  <filename>site_scons/site_init.py</filename> file like this:
+
+  </para>
+
+  <programlisting>
+      def TOOL_ADD_HEADER(env):
+         """A Tool to add a header from $HEADER to the source file"""
+         add_header = Builder(action=['echo "$HEADER" &gt; $TARGET',
+                                      'cat $SOURCE &gt;&gt; $TARGET'])
+         env.Append(BUILDERS = {'AddHeader' : add_header})
+         env['HEADER'] = '' # set default value
+  </programlisting>
+
+  <para>
+
+  and a &SConstruct; like this:
+
+  </para>
+
+  <programlisting>
+      # Use TOOL_ADD_HEADER from site_scons/site_init.py
+      env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+      env.AddHeader('tgt', 'src')
+  </programlisting>
+
+  <para>
+
+    The <function>TOOL_ADD_HEADER</function> tool method will be
+    called to add the <function>AddHeader</function> tool to the
+    environment.
+
+  </para>
+
+  <!-- 
+  <scons_output example="site1" os="posix">
+     <scons_output_command>scons -Q</scons_output_command>
+  </scons_output>
+  -->
+
+  <para>
+    Similarly, a more full-fledged tool with
+    <function>exists()</function> and <function>generate()</function>
+    methods can be installed in
+    <filename>site_scons/site_tools/toolname.py</filename>.  Since
+    <filename>site_scons/site_tools</filename> is automatically added
+    to the head of the tool search path, any tool found there will be
+    available to all environments.  Furthermore, a tool found there
+    will override a built-in tool of the same name, so if you need to
+    change the behavior of a built-in tool, site_scons gives you the
+    hook you need.
+  </para>
+
+  <para>
+    Many people have a library of utility Python functions they'd like
+    to include in &SConscript;s; just put that module in
+    <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+    choice.  For instance you can do something like this in
+    <filename>site_scons/my_utils.py</filename> to add a build_id method:
+  </para>
+    
+  <programlisting>
+      def build_id():
+         """Return a build ID (stub version)"""
+        return "100"
+  </programlisting>
+
+  <para>
+
+  And then in your &SConscript; or any sub-&SConscript; anywhere in
+  your build, you can import <filename>my_utils</filename> and use it:
+
+  </para>
+
+  <programlisting>
+      import my_utils
+      print "build_id=" + my_utils.build_id()
+  </programlisting>
+
+  <para>
+
+    If you have a machine-wide site dir you'd like to use instead of
+    <filename>./site_scons</filename>, use the
+    <literal>--site-dir</literal> option to point to your dir.
+    <filename>site_init.py</filename> and
+    <filename>site_tools</filename> will be located under that dir.
+    To avoid using a <filename>site_scons</filename> dir at all, even
+    if it exists, use the <literal>--no-site-dir</literal> option.
+
+  </para>
+
+  </section>
+
+
   <!--
 
   <section>
index 8981b148d302e1f58fb23836877718d9b3330c27..0dbadfc8315f3c11e1154a17d26cf57c560efe2f 100644 (file)
 
   XXX CheckTypeSize()
 
-  XXX Glob()
-
   XXX Progress()
 
   XXX - - diskcheck=
 
-  XXX site_scons
-  XXX - - site-dir
-  XXX - - no-site-dir
-
   XXX - - warn=
 
   XXX ARGLIST
index 8981b148d302e1f58fb23836877718d9b3330c27..0dbadfc8315f3c11e1154a17d26cf57c560efe2f 100644 (file)
 
   XXX CheckTypeSize()
 
-  XXX Glob()
-
   XXX Progress()
 
   XXX - - diskcheck=
 
-  XXX site_scons
-  XXX - - site-dir
-  XXX - - no-site-dir
-
   XXX - - warn=
 
   XXX ARGLIST
index 57cafa584098434748a5f9b79d0fe67903228a4c..5a780b51c664df87d06e94f006e5b4457f8766b4 100644 (file)
@@ -8,6 +8,36 @@
 
 
 
+RELEASE 0.98.4 - XXX
+
+  From Benoit Belley:
+
+  - Fix calculation of signatures for Python function actions with
+    closures in Python versions before 2.5.
+
+  From David Cournapeau:
+
+  - Fix the initialization of $SHF77FLAGS so it includes $F77FLAGS.
+
+  From Jonas Olsson:
+
+  - Fix a syntax error in the Intel C compiler support on Windows.
+
+  From Steven Knight:
+
+  - Change how we represent Python Value Nodes when printing and when
+    stored in .sconsign files (to avoid blowing out memory by storing
+    huge strings in .sconsign files after multiple runs using Configure
+    contexts cause the Value strings to be re-escaped each time).
+
+  - Fix a regression in not executing configuration checks after failure
+    of any configuration check that used the same compiler or other tool.
+
+  - Handle multiple destinations in Visual Studio 8 settings for the
+    analogues to the INCLUDE, LIBRARY and PATH variables.
+
+
+
 RELEASE 0.98.3 - Tue, 29 Apr 2008 22:40:12 -0700
 
   From Greg Noel:
index d2211b7eb5a121c61e7fc13cea949b36f5865a2e..1bc9baacd45337b85b3ca2c3ea6a54c3a9b5dcd4 100644 (file)
@@ -277,7 +277,10 @@ def _function_contents(func):
         closure = []
 
     #xxx = [_object_contents(x.cell_contents) for x in closure]
-    xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+    try:
+        xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+    except AttributeError:
+        xxx = []
     contents.append(',(' + string.join(xxx, ',') + ')')
 
     return string.join(contents, '')
@@ -809,7 +812,13 @@ class FunctionAction(_ActionAction):
                 return c
         def array(a):
             def quote(s):
-                return '"' + str(s) + '"'
+                try:
+                    str_for_display = s.str_for_display
+                except AttributeError:
+                    s = repr(s)
+                else:
+                    s = str_for_display()
+                return s
             return '[' + string.join(map(quote, a), ", ") + ']'
         try:
             strfunc = self.execfunction.strfunction
index a503981ee5d072c52a0311e2e422c31c17af42c3..2fb3b066cbd46bd346dbccbe728b10df4e8334c2 100644 (file)
@@ -172,6 +172,8 @@ class Environment:
 class DummyNode:
     def __init__(self, name):
         self.name = name
+    def str_for_display(self):
+        return '"' + self.name + '"'
     def __str__(self):
         return self.name
     def rfile(self):
@@ -432,10 +434,10 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a("out", "in", env)
             assert result.status == 7, result
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             a.chdir = 'xyz'
-            expect = 'os.chdir(%s)\nexecfunc(["out"], ["in"])\nos.chdir(%s)\n'
+            expect = "os.chdir(%s)\nexecfunc(['out'], ['in'])\nos.chdir(%s)\n"
 
             sio = StringIO.StringIO()
             sys.stdout = sio
@@ -458,7 +460,7 @@ class _ActionActionTestCase(unittest.TestCase):
             result = b("out", "in", env)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'firstfunc(["out"], ["in"])\nexecfunc(["out"], ["in"])\n', s
+            assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\n", s
 
             SCons.Action.execute_actions = 0
 
@@ -467,14 +469,14 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = b("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'firstfunc(["out"], ["in"])\nexecfunc(["out"], ["in"])\nlastfunc(["out"], ["in"])\n', s
+            assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\nlastfunc(['out'], ['in'])\n", s
 
             SCons.Action.print_actions_presub = 1
             SCons.Action.execute_actions = 1
@@ -484,35 +486,35 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a("out", "in", env)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a("out", "in", env, presub=0)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a("out", "in", env, presub=1)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = b(["out"], "in", env, presub=1)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  firstfunc(target, source, env)\nfirstfunc(["out"], ["in"])\nBuilding out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  firstfunc(target, source, env)\nfirstfunc(['out'], ['in'])\nBuilding out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = b(["out", "list"], "in", env, presub=1)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out and list with action:\n  firstfunc(target, source, env)\nfirstfunc(["out", "list"], ["in"])\nBuilding out and list with action:\n  execfunc(target, source, env)\nexecfunc(["out", "list"], ["in"])\n', s
+            assert s == "Building out and list with action:\n  firstfunc(target, source, env)\nfirstfunc(['out', 'list'], ['in'])\nBuilding out and list with action:\n  execfunc(target, source, env)\nexecfunc(['out', 'list'], ['in'])\n", s
 
             a2 = SCons.Action.Action(execfunc)
 
@@ -521,14 +523,14 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a2("out", "in", env)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a2("out", "in", env, presub=0)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             SCons.Action.execute_actions = 0
 
@@ -537,7 +539,7 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a2("out", "in", env, presub=0)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
@@ -568,7 +570,7 @@ class _ActionActionTestCase(unittest.TestCase):
                 result.append(s)
             env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line
             a("output", "input", env)
-            assert result == ['execfunc(["output"], ["input"])'], result
+            assert result == ["execfunc(['output'], ['input'])"], result
             
 
         finally:
index bb23d3f670762ffc92da6ad6374ac180d5e50ad0..93558f900e39199e3b67b5fa7c7fd7ce02d286d8 100644 (file)
@@ -74,6 +74,9 @@ class Alias(SCons.Node.Node):
         SCons.Node.Node.__init__(self)
         self.name = name
 
+    def str_for_display(self):
+        return '"' + self.__str__() + '"'
+
     def __str__(self):
         return self.name
 
index bd8b564de22908106e73bf26339e2d92ea3404c8..b154e34a99c073f1cd71ede93ec752f557e92296 100644 (file)
@@ -544,6 +544,9 @@ class Base(SCons.Node.Node):
         self.cwd = None # will hold the SConscript directory for target nodes
         self.duplicate = directory.duplicate
 
+    def str_for_display(self):
+        return '"' + self.__str__() + '"'
+
     def must_be_same(self, klass):
         """
         This node, which already existed, is being looked up as the
index 7cdea14c60b6884dca52fef2d99430718363787b..741d7e9f6601dd71b7e3cf87d93502d755360796 100644 (file)
@@ -56,9 +56,12 @@ class Value(SCons.Node.Node):
         if not built_value is None:
             self.built_value = built_value
 
-    def __str__(self):
+    def str_for_display(self):
         return repr(self.value)
 
+    def __str__(self):
+        return str(self.value)
+
     def make_ready(self):
         self.get_csig()
 
index 6f93ca7d932db3ab04578f906fc982025502c8a2..27dd1f0f9dd6e560cfb74ff60d3026a8eeeec434 100644 (file)
@@ -529,7 +529,7 @@ class Taskmaster:
         while self.candidates:
             candidates = self.candidates
             self.candidates = []
-            self.will_not_build(candidates)
+            self.will_not_build(candidates, lambda n: n.state < NODE_UP_TO_DATE)
         return None
 
     def _find_next_ready_node(self):
@@ -634,6 +634,9 @@ class Taskmaster:
             # take a stab at evaluating them (or their children).
             children_not_visited.reverse()
             self.candidates.extend(self.order(children_not_visited))
+            #if T and children_not_visited:
+            #    T.write('Taskmaster:      adding to candidates: %s\n' % map(str, children_not_visited))
+            #    T.write('Taskmaster:      candidates now: %s\n' % map(str, self.candidates))
 
             # Skip this node if any of its children have failed.
             #
@@ -731,7 +734,7 @@ class Taskmaster:
 
         return task
 
-    def will_not_build(self, nodes):
+    def will_not_build(self, nodes, mark_fail=lambda n: n.state != NODE_FAILED):
         """
         Perform clean-up about nodes that will never be built.
         """
@@ -742,8 +745,8 @@ class Taskmaster:
         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
+            if mark_fail(node):
+                node.set_state(NODE_FAILED)
                 parents = node.waiting_parents
                 to_visit = to_visit | parents
                 pending_children = pending_children - parents
@@ -759,8 +762,8 @@ class Taskmaster:
                         to_visit.remove(node)
                     else:
                         break
-                if node.state != NODE_FAILED:
-                    node.state = NODE_FAILED
+                if mark_fail(node):
+                    node.set_state(NODE_FAILED)
                     parents = node.waiting_parents
                     to_visit = to_visit | parents
                     pending_children = pending_children - parents
index a173162c9b4c787c3db45d44b03ea2aa88b031b2..53432c6ff876a155a693c898a614efc5efd45b59 100644 (file)
@@ -194,7 +194,7 @@ def generate(env):
             env['SMART_LINKCOM'] = smart_link[linkcom]
         except KeyError:
             def _smartLink(source, target, env, for_signature,
-                           defaultLinker=linkcom):
+                           defaultLinker=linkcom, dc=dc):
                 if isD(source):
                     try:
                         libs = env['LIBS']
index 058b061d074d1e7c29ecc2afd6ab5a5cdcc07aa3..66d3d89e00f856f964f7b1a710c418d889cecacc 100644 (file)
@@ -48,8 +48,8 @@ def generate(env):
         env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS')
         env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS')
     else:
-        env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$SHFORTRANFLAGS -fPIC')
-        env['SHF77FLAGS'] = SCons.Util.CLVar('$SHF77FLAGS -fPIC')
+        env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC')
+        env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC')
 
     env['FORTRAN'] = fcomp
     env['SHFORTRAN'] = '$FORTRAN'
index db5d06ec0b9016609f8e9d2970e111ccfcc7ce99..020a6f7051bdc8894b3a4838904412d65b97449a 100644 (file)
@@ -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.keys():
+            for p in paths:
                 try:
                     path=get_intel_registry_value(p[1], version, abi)
                     # These paths may have $(ICInstallDir)
index a1067c08d27e88d6ec96f33a2683e6b7f73c472a..1a42d6207cc1b86c75887e5f18d99d50d5ce7d5b 100644 (file)
@@ -203,6 +203,11 @@ def _parse_msvc8_overrides(version,platform,suite):
         user_settings = doc.getElementsByTagName('UserSettings')[0]
         tool_options = user_settings.getElementsByTagName('ToolsOptions')[0]
         tool_options_categories = tool_options.getElementsByTagName('ToolsOptionsCategory')
+        environment_var_map = {
+            'IncludeDirectories' : 'INCLUDE',
+            'LibraryDirectories' : 'LIBRARY',
+            'ExecutableDirectories' : 'PATH',
+        }
         for category in tool_options_categories:
             category_name = category.attributes.get('name')
             if category_name is not None and category_name.value == 'Projects':
@@ -215,21 +220,21 @@ def _parse_msvc8_overrides(version,platform,suite):
                             property_name = property.attributes.get('name')
                             if property_name is None:
                                 continue
-                            elif property_name.value == 'IncludeDirectories':
-                                include_dirs = property.childNodes[0].data
-                                # ToDo: Support for other destinations than Win32
-                                include_dirs = include_dirs.replace('Win32|', '')
-                                dirs['INCLUDE'] = include_dirs
-                            elif property_name.value == 'LibraryDirectories':
-                                lib_dirs = property.childNodes[0].data.replace('Win32|', '')
-                                # ToDo: Support for other destinations than Win32
-                                lib_dirs = lib_dirs.replace('Win32|', '')
-                                dirs['LIBRARY'] = lib_dirs
-                            elif property_name.value == 'ExecutableDirectories':
-                                path_dirs = property.childNodes[0].data.replace('Win32|', '')
-                                # ToDo: Support for other destinations than Win32
-                                path_dirs = path_dirs.replace('Win32|', '')
-                                dirs['PATH'] = path_dirs
+                            var_name = environment_var_map.get(property_name)
+                            if var_name:
+                                data = property.childNodes[0].data
+                                value_list = string.split(data, '|')
+                                if len(value_list) == 1:
+                                    dirs[var_name] = value_list[0]
+                                else:
+                                    while value_list:
+                                        dest, value = value_list[:2]
+                                        del value_list[:2]
+                                        # ToDo: Support for destinations
+                                        # other than Win32
+                                        if dest == 'Win32':
+                                            dirs[var_name] = value
+                                            break
     else:
         # There are no default directories in the registry for VS8 Express :(
         raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
index 2aebf8545de3c59e9d657dfd3d2004347376c51e..7591069644cf343d23c2f68df5e122a547e2dc15 100644 (file)
@@ -274,6 +274,8 @@ def nodeinfo_raw(name, ninfo, prefix=""):
     l = []
     for k in keys:
         l.append('%s: %s' % (repr(k), repr(d.get(k))))
+    if '\n' in name:
+        name = repr(name)
     return name + ': {' + string.join(l, ', ') + '}'
 
 def nodeinfo_cooked(name, ninfo, prefix=""):
@@ -282,6 +284,8 @@ def nodeinfo_cooked(name, ninfo, prefix=""):
     except AttributeError:
         field_list = []
     f = lambda x, ni=ninfo, v=Verbose: field(x, ni, v)
+    if '\n' in name:
+        name = repr(name)
     outlist = [name+':'] + filter(None, map(f, field_list))
     if Verbose:
         sep = '\n    ' + prefix
index 5e755a85b1c260295fafec9e3c6a8ae4ca865634..4fc0dc4ac644ee1beddf0705105bcde8322eb0c5 100644 (file)
@@ -60,7 +60,33 @@ optEnv = Environment(options=options, tools=[])
 
 r = re.compile(optEnv['regexp'])
 
-toto = \
+withClosure = \
+r'''
+def toto(header='%(header)s', trailer='%(trailer)s'):
+    xxx = %(closure_cell_value)s
+    def writeDeps(target, source, env, b=%(b)s, r=r %(extraarg)s ,
+                  header=header, trailer=trailer):
+        """+'"""%(docstring)s"""'+"""
+        def foo(b=b):
+            return %(nestedfuncexp)s
+        f = open(str(target[0]),'wb')
+        f.write(header)
+        for d in env['ENVDEPS']:
+            f.write(d+'%(separator)s')
+        f.write(trailer+'\\n')
+        f.write(str(foo())+'\\n')
+        f.write(r.match('aaaa').group(1)+'\\n')
+        %(extracode)s
+        try:
+           f.write(str(xarg)+'\\n')
+        except NameError:
+           pass
+        f.close()
+
+    return writeDeps
+'''
+
+NoClosure = \
 r'''
 def toto(header='%(header)s', trailer='%(trailer)s'):
     xxx = %(closure_cell_value)s
@@ -86,7 +112,17 @@ def toto(header='%(header)s', trailer='%(trailer)s'):
     return writeDeps
 '''
 
-exec( toto % optEnv )
+try:
+    # Check that lexical closure are supported
+    def a():
+        x = 0
+        def b():
+            return x
+        return b
+    a().func_closure[0].cell_contents
+    exec( withClosure % optEnv )
+except (AttributeError, TypeError):
+    exec( NoClosure % optEnv )
 
 genHeaderBld = SCons.Builder.Builder(
     action = SCons.Action.Action(
@@ -122,15 +158,25 @@ scons: `.' is up to date.
 scons: done building targets.
 """
 
-def runtest( arguments, expectedOutFile, expectedRebuild=True):
+import sys
+if sys.version[:3] == '2.1':
+    expectedStderr = """\
+%s:79: SyntaxWarning: local name 'x' in 'a' shadows use of 'x' as global in nested scope 'b'
+  def a():
+""" % test.workpath('SConstruct')
+else:
+    expectedStderr = ""
+
+def runtest(arguments, expectedOutFile, expectedRebuild=True, stderr=expectedStderr):
     test.run(arguments=arguments,
-             stdout=expectedRebuild and rebuildstr or nobuildstr)
+             stdout=expectedRebuild and rebuildstr or nobuildstr,
+             stderr=expectedStderr)
     test.must_match('Out.gen.h', expectedOutFile)
 
     # Should not be rebuild when ran a second time with the same
     # arguments.
 
-    test.run(arguments = arguments, stdout=nobuildstr)
+    test.run(arguments = arguments, stdout=nobuildstr, stderr=expectedStderr)
     test.must_match('Out.gen.h', expectedOutFile)
 
 
diff --git a/test/Configure/build-fail.py b/test/Configure/build-fail.py
new file mode 100644 (file)
index 0000000..2facb81
--- /dev/null
@@ -0,0 +1,90 @@
+#!/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 Configure tests work even after an earlier test fails.
+
+This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler
+as having failed (because it was on the candidates list as the implicit
+command dependency for both the object file and executable generated
+for the configuration test) and then avoid trying to rebuild anything
+else that used the "failed" Node.
+
+Thanks to Ben Webb for the test case.
+"""
+
+import os
+
+import TestSCons
+
+_obj = TestSCons._obj
+
+test = TestSCons.TestSCons(match = TestSCons.match_re_dotall)
+
+test.subdir('a', 'b')
+
+a_boost_hpp = os.path.join('..', 'a', 'boost.hpp')
+b_boost_hpp = os.path.join('..', 'b', 'boost.hpp')
+
+test.write('SConstruct', """\
+import os
+def _check(context):
+    for dir in ['a', 'b']:
+        inc = os.path.join('..', dir, 'boost.hpp')
+        result = context.TryRun('''
+        #include "%s"
+
+        int main() { return 0; }
+        ''' % inc, '.cpp')[0]
+        if result:
+            import sys
+            sys.stdout.write('%s: ' % inc)
+            break
+    context.Result(result)
+    return result
+env = Environment()
+conf = env.Configure(custom_tests={'CheckBoost':_check})
+conf.CheckBoost()
+conf.Finish()
+""")
+
+test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""")
+
+expect = test.wrap_stdout(read_str = "%s: yes\n" % b_boost_hpp,
+                          build_str = "scons: `.' is up to date.\n")
+
+test.run(arguments='--config=force', stdout=expect)
+
+expect = test.wrap_stdout(read_str = "%s: yes\n" % a_boost_hpp,
+                          build_str = "scons: `.' is up to date.\n")
+
+test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""")
+
+test.run(arguments='--config=force', stdout=expect)
+
+test.run()
+
+test.pass_test()
diff --git a/test/Configure/implicit-cache.py b/test/Configure/implicit-cache.py
new file mode 100644 (file)
index 0000000..58ae7c9
--- /dev/null
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that use of --implicit-cache with the Python Value Nodes
+used by the Configure subsystem generate the same .sconsign file
+and don't cause it to grow without limit.
+
+This was reported as issue 2033 in the tigris.org bug tracker, by the
+Ardour project.  Prior to 0.98.4, the Value implementation would actually
+return the repr() of its value as the str().  This was done because
+it made saving a Value in a file and reading it back in kind of work,
+because a print a string Value into a file (for example) would in fact
+put quotes around it and be assignable in that file.
+
+The problem is that this would get stored in a .sconsign file as its
+repr(), with the specific problem being that Values with embedded newlines
+would get stored as strings containing backslash+n digraphs *and* the
+quotes at beginning and end of the string::
+
+    '\n#include <math.h>\n\n': {<.sconsign info>}
+
+Then, when we read that back in from the .sconsign file, we would store
+that repr() as a string Value itself, escaping the backslashes and
+including the quotes, so when we stored it the second time it would end
+up looking like:
+
+    "'\\n#include <math.h>\\n\\n'": {<.sconsign info>}
+
+Every time that we would read this value and store it again (because
+something else changed in the .sconf_temp directory), the string would
+get longer and longer until it blew out the users's memory.
+"""
+
+import TestSConsign
+
+test = TestSConsign.TestSConsign()
+
+test.write('SConstruct', """
+env = Environment(CPPPATH=['.'])
+conf = Configure(env)
+conf.CheckHeader( 'math.h' )
+if ARGUMENTS.get('USE_FOO'):
+    conf.CheckHeader( 'foo.h' )
+env = conf.Finish()
+""")
+
+test.write('foo.h', "#define FOO 1\n")
+
+# First run:  Have the configure subsystem only look for math.h, and
+# squirrel away the .sconsign info for the conftest_0.c file that's
+# generated from the Python Value Node that we're using for our test.
+
+test.run(arguments = '.')
+
+test.run_sconsign('-d .sconf_temp -e conftest_0.c --raw .sconsign.dblite')
+old_sconsign_dblite = test.stdout()
+
+# Second run:  Have the configure subsystem also look for foo.h, so
+# that there's a change in the .sconf_temp directory that will cause its
+# .sconsign information to get rewritten from disk.  Squirrel away the
+# .sconsign info for the conftest_0.c file.  The now-fixed bug would show
+# up because the entry would change with the additional string-escaping
+# described above.  The now-correct behavior is that the re-stored value
+# for conftest_0.c doesn't change.
+
+test.run(arguments = '--implicit-cache USE_FOO=1 .')
+
+test.run_sconsign('-d .sconf_temp -e conftest_0.c --raw .sconsign.dblite')
+new_sconsign_dblite = test.stdout()
+
+if old_sconsign_dblite != new_sconsign_dblite:
+    print ".sconsign.dblite did not match:"
+    print "FIRST RUN =========="
+    print old_sconsign_dblite
+    print "SECOND RUN =========="
+    print new_sconsign_dblite
+    test.fail_test()
+
+test.pass_test()
index 25239b5d1b373f7859e823303c06d9780e84ca83..5a061193a3784d219bc36e2e07204d14de0dfb88 100644 (file)
@@ -28,15 +28,10 @@ __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()
 {}
@@ -44,20 +39,47 @@ void foo()
 
 test.write('SConstruct', r"""
 import os
+import sys
 import signal
 
-# Make sure that SCons is a process group leader.
-os.setpgrp()
+if 'killpg' not in dir(os) or 'setpgrp' not in dir(os) or sys.platform == 'cygwin':
+    # The platform does not support process group. Therefore, we
+    # 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.
+    #
+    # Note that this way of performing the test is not as good as
+    # using killpg because the Taskmaster is stopped synchronously. In
+    # addition, the SCons subprocesses (or forked children before the
+    # exec() of the subprocess) are never killed. This therefore
+    # exercise less SCons functionality.
+    #
+    # FIXME: There seems to be a bug on Cygwin where the compiler
+    # process hangs after sending the SIGINT signal to the process
+    # group. It is probably a bug in cygwin1.dll, or maybe in the
+    # Python 'C' code or the Python subprocess module. We therefore do
+    # not use 'killpg' on Cygwin.
+    def explode(env, target, source):
+        handler = signal.getsignal(signal.SIGINT)
+        handler(signal.SIGINT, None)
+        return 0        
+else:
+    # The platform does support process group so we use killpg to send
+    # a SIGINT to everyone.
+
+    # Make sure that SCons is a process group leader.
+    os.setpgrp()
+
+    def explode(env, target, source):
+        os.killpg(0, signal.SIGINT)
 
-all = []
 
-def explode(env, target, source):
-    os.killpg(0, signal.SIGINT)
+all = []
 
 for i in xrange(40):
-    all += Object('toto%5d' % i, 'toto.c')
+    all.extend(Object('toto%5d' % i, 'toto.c'))
 
-all+= Command( 'broken', 'toto.c', explode)
+all.extend(Command( 'broken', 'toto.c', explode))
 
 Default( Alias('all', all))
 """
index 72d499ed14a1fd94d5a5c94e83efd30b26ff005a..01647e39ac4629dd0f3364b2a3355a8966750f20 100644 (file)
@@ -91,12 +91,12 @@ for source_signature in ['MD5', 'timestamp-newer']:
     test.run(arguments='-c')
     test.run()
 
-    out7 = """create_value(["'my value'"], ["f3.out"])"""
-    out8 = """create_value_file(["f5.out"], ["'my value'"])"""
+    out7 = """create_value(['my value'], ["f3.out"])"""
+    out8 = """create_value_file(["f5.out"], ['my value'])"""
 
-    out1 = """create(["f1.out"], ["'/usr/local'"])"""
-    out2 = """create(["f2.out"], ["10"])"""
-    out3 = """create\\(\\["f3.out"\\], \\["<.*.Custom instance at """
+    out1 = """create(["f1.out"], ['/usr/local'])"""
+    out2 = """create(["f2.out"], [10])"""
+    out3 = """create\\(\\["f3.out"\\], \\[<.*.Custom instance at """
     #" <- unconfuses emacs syntax highlighting
 
     test.fail_test(string.find(test.stdout(), out1) == -1)
@@ -114,9 +114,9 @@ for source_signature in ['MD5', 'timestamp-newer']:
     test.up_to_date(arguments='.')
 
     test.run(arguments='prefix=/usr')
-    out4 = """create(["f1.out"], ["'/usr'"])"""
-    out5 = """create(["f2.out"], ["4"])"""
-    out6 = """create\\(\\["f3.out"\\], \\["<.*.Custom instance at """
+    out4 = """create(["f1.out"], ['/usr'])"""
+    out5 = """create(["f2.out"], [4])"""
+    out6 = """create\\(\\["f3.out"\\], \\[<.*.Custom instance at """
     #" <- unconfuses emacs syntax highlighting
     test.fail_test(string.find(test.stdout(), out4) == -1)
     test.fail_test(string.find(test.stdout(), out5) == -1)
@@ -132,7 +132,7 @@ for source_signature in ['MD5', 'timestamp-newer']:
     test.unlink('f3.out')
 
     test.run(arguments='prefix=/var')
-    out4 = """create(["f1.out"], ["'/var'"])"""
+    out4 = """create(["f1.out"], ['/var'])"""
 
     test.fail_test(string.find(test.stdout(), out4) == -1)
     test.fail_test(string.find(test.stdout(), out5) != -1)
index e1e3aecd509498fba653826a3bdce9d8786f63ea..e1198d5aacb95738e3cf1ab6948fbed321b47dab 100644 (file)
@@ -39,9 +39,10 @@ test = TestSCons.TestSCons()
 
 test.subdir(['src'], ['src', 'subdir'])
 
-subdir_file8 = os.path.join('subdir', 'file8')
 subdir_file7 = os.path.join('subdir', 'file7')
 subdir_file7_in = os.path.join('subdir', 'file7.in')
+subdir_file8 = os.path.join('subdir', 'file8')
+subdir_file9 = os.path.join('subdir', 'file9')
 
 cat_py = test.workpath('cat.py')
 inc_aaa = test.workpath('inc', 'aaa')
@@ -122,6 +123,7 @@ file6 = env.Cat('file6', 'file6.in')
 AlwaysBuild(file6)
 env.Cat('subdir/file7', 'subdir/file7.in')
 env.OneCat('subdir/file8', ['subdir/file7.in', env.Value(%(test_value)s)] )
+env.OneCat('subdir/file9', ['subdir/file7.in', env.Value(7)] )
 """ % valueDict )
 
 test_value = '"first"'
@@ -195,6 +197,8 @@ scons: building `%(subdir_file7)s' because it doesn't exist
 %(_python_)s %(cat_py)s %(subdir_file7)s %(subdir_file7_in)s
 scons: building `%(subdir_file8)s' because it doesn't exist
 %(_python_)s %(cat_py)s %(subdir_file8)s %(subdir_file7_in)s
+scons: building `%(subdir_file9)s' because it doesn't exist
+%(_python_)s %(cat_py)s %(subdir_file9)s %(subdir_file7_in)s
 """ % locals())
 
 test.run(chdir='src', arguments=args, stdout=expect)
@@ -248,8 +252,8 @@ scons: rebuilding `file5' because `%(inc_bbb_k)s' changed
 scons: rebuilding `file6' because AlwaysBuild() is specified
 %(_python_)s %(cat_py)s file6 file6.in
 scons: rebuilding `%(subdir_file8)s' because:
-           `"'first'"' is no longer a dependency
-           `'second'' is a new dependency
+           `first' is no longer a dependency
+           `second' is a new dependency
 %(_python_)s %(cat_py)s %(subdir_file8)s %(subdir_file7_in)s
 """ % locals())