Fix various SConf bugs. (Christoph Wiedemann)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 21 Oct 2003 05:17:36 +0000 (05:17 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 21 Oct 2003 05:17:36 +0000 (05:17 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@825 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/SConf.py
src/engine/SCons/SConfTests.py
src/engine/SCons/Script/SConscript.py
test/Configure.py
test/SConscript.py

index 22edc262ff46a1f99996d11a56e8e8a8af9cf011..b7793236dceb299fc08f37b4f603ccbad011f322 100644 (file)
@@ -168,6 +168,13 @@ RELEASE X.XX - XXX
 
   - Fix a bug in detection of Qt installed on the local system.
 
+  - Support returning Python 2.3 BooleanType values from Configure checks.
+
+  - Provide an error message if someone mistakenly tries to call a
+    Configure check from within a Builder function.
+
+  - Support calling a Builder when a Configure context is still open.
+
 
 
 RELEASE 0.92 - Wed, 20 Aug 2003 03:45:28 -0500
index 2ad0a329a6eafc04fdce141b5aa766b1e7e3363a..d079feb2ed07675a6d096ef60d0ea7f9359ebbbc 100644 (file)
@@ -1443,6 +1443,9 @@ class EnvironmentTestCase(unittest.TestCase):
         # Configure() will write to a local temporary file.
         test = TestCmd.TestCmd(workdir = '')
         save = os.getcwd()
+        # Configure() will test, if we are reading a SConscript file
+        import SCons.Script.SConscript
+        SCons.Script.SConscript.sconscript_reading = 1
 
         try:
             os.chdir(test.workpath())
index 0fbde14c5504c3fe96e85642eed3550d36cd0ccf..144099f896e73a4392f0e30f40759eef345efc43 100644 (file)
@@ -33,7 +33,7 @@ import os
 import string
 import sys
 import traceback
-from types import *
+import types
 
 import SCons.Action
 import SCons.Builder
@@ -70,6 +70,8 @@ def _stringSource( target, source, env ):
     return (target[0].get_path() + ' <- \n  |' +
             string.replace( env['SCONF_TEXT'], "\n", "\n  |" ) )
 
+BooleanTypes = [types.IntType]
+if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
 
 class SConf:
     """This is simply a class to represent a configure context. After
@@ -92,6 +94,9 @@ class SConf:
         Note also the conf_dir and log_file arguments (you may want to
         build tests in the BuildDir, not in the SourceDir)
         """
+        import SCons.Script.SConscript
+        if not SCons.Script.SConscript.sconscript_reading:
+            raise SCons.Errors.UserError, "Calling Configure from Builders is not supported."
         global SConfFS
         if not SConfFS:
             SConfFS = SCons.Node.FS.FS(SCons.Node.FS.default_fs.pathTop)
@@ -246,6 +251,9 @@ class SConf:
         suff = self.env.subst( builder.builder.suffix )
         target = self.confdir.File(pref + f + suff)
         self.env['SCONF_TEXT'] = text
+        self.env['PIPE_BUILD'] = 1
+        self.env['PSTDOUT'] = self.logstream
+        self.env['PSTDERR'] = self.logstream
         if text != None:
             source = self.confdir.File(f + extension)
             sourceNode = self.env.SConfSourceBuilder(target=source,
@@ -260,6 +268,10 @@ class SConf:
         nodesToBeBuilt.extend(nodes)
         ret = self.BuildNodes(nodesToBeBuilt)
 
+        # clean up environment
+        del self.env['PIPE_BUILD']
+        del self.env['PSTDOUT']
+        del self.env['PSTDERR']
         del self.env['SCONF_TEXT']
 
         _ac_build_counter = _ac_build_counter + 1
@@ -440,9 +452,6 @@ class SConf:
             # the build system not to override it with a eventually
             # existing file with the same name in the source directory
             self.logfile.dir.add_ignore( [self.logfile] )
-            self.env['PIPE_BUILD'] = 1
-            self.env['PSTDOUT'] = self.logstream
-            self.env['PSTDERR'] = self.logstream
 
             tb = traceback.extract_stack()[-3]
             
@@ -471,10 +480,6 @@ class SConf:
         if self.logstream != None:
             self.logstream.close()
             self.logstream = None
-            # clean up environment
-            del self.env['PIPE_BUILD']
-            del self.env['PSTDOUT']
-            del self.env['PSTDERR']
         # remove the SConfSourceBuilder from the environment
         blds = self.env['BUILDERS']
         del blds['SConfSourceBuilder']
@@ -529,15 +534,15 @@ class CheckContext:
         'failed'.
         The result is only displayed when self.did_show_result is not set.
         """
-        if type(res) == IntType:
+        if type(res) in BooleanTypes:
             if res:
                 text = "ok"
             else:
                 text = "failed"
-        elif type(res) == StringType:
+        elif type(res) == types.StringType:
             text = res
         else:
-            raise TypeError, "Expected string or int"
+            raise TypeError, "Expected string, int or bool, got " + str(type(res))
 
         if self.did_show_result == 0:
             if self.cached:
@@ -578,12 +583,12 @@ class CheckContext:
     def BuildProg(self, text, ext):
         # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
         res = self.TryBuild(self.env.Program, text, ext)
-        if type(res) == IntType:
+        if type(res) in BooleanTypes:
             if res:
                 ret = ""
             else:
                 ret = "failed to build test program"
-        elif type(res) == StringType:
+        elif type(res) == types.StringType:
             ret = res
         else:
             raise TypeError, "Expected string or int"
@@ -592,12 +597,12 @@ class CheckContext:
     def CompileProg(self, text, ext):
         # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
         res = self.TryBuild(self.env.Object, text, ext)
-        if type(res) == IntType:
+        if type(res) in BooleanTypes:
             if res:
                 ret = ""
             else:
                 ret = "failed to compile test program"
-        elif type(res) == StringType:
+        elif type(res) == types.StringType:
             ret = res
         else:
             raise TypeError, "Expected string or int"
index 78b27bd3e03df65ee768c04dc903a9567bba0d8b..2028c3bd3456d6b23e83edb7ea27affe1f4429f0 100644 (file)
@@ -64,6 +64,8 @@ class SConfTestCase(unittest.TestCase):
         # we only use SCons.Environment and SCons.SConf for these tests.
         import SCons.Environment
         import SCons.SConf
+        import SCons.Script.SConscript
+        SCons.Script.SConscript.sconscript_reading = 1
         self.Environment = SCons.Environment
         self.SConf = SCons.SConf
         # and we need a new environment, cause references may point to
index 92cd68dd8f55a5932914fb243395b798a20a1d4a..8212061510d6ae0cb7161e65dd5de7a3d2e8f11c 100644 (file)
@@ -67,6 +67,9 @@ global_exports = {}
 # chdir flag
 sconscript_chdir = 1
 
+# will be set to 1, if we are reading a SConscript
+sconscript_reading = 0
+
 def _scons_add_args(alist):
     global arguments
     for arg in alist:
@@ -139,6 +142,8 @@ def Return(*vars):
         stack[-1].retval = tuple(retval)
 
 def _SConscript(fs, *files, **kw):
+    global sconscript_reading
+    sconscript_reading = 1
     top = fs.Top
     sd = fs.SConstruct_dir.rdir()
     exports = kw.get('exports', [])
@@ -215,6 +220,7 @@ def _SConscript(fs, *files, **kw):
                                      f.path)
                 
         finally:
+            sconscript_reading = 0
             sys.path = old_sys_path
             frame = stack.pop()
             try:
index 917046906636a75d486d7eb9742f508156576fd9..2b134dc2a37cf3e608bd88d91a90df4e90b49562 100644 (file)
@@ -40,32 +40,38 @@ else:
 # to use cygwin compilers on cmd.exe -> uncomment following line
 #lib = 'm'
 
-oldPwd = os.getcwd()
+work_cnt = 0
+work_dir = None
 python = TestSCons.python
-test = None
-
-def reset(dot = 1):
-    global test, oldPwd
-    os.chdir( oldPwd )
-    TestSCons.scons = None
-    test = TestSCons.TestSCons()
-    if dot == 1:
+test = TestSCons.TestSCons()
+
+
+def reset(match = 1):
+    global test, work_dir, work_cnt
+    work_cnt = work_cnt + 1
+    work_dir='test%d' % work_cnt
+    test.subdir(work_dir)
+    if match == 0:
+        test.match_func = TestCmd.match_re
+    elif match == 1:
         test.match_func = TestCmd.match_re_dotall
-    
+    elif match == 2:
+        test.match_func = TestCmd.match_exact
 
 def checkFiles(test, files):
+    global work_dir
     for f in files:
-        test.fail_test( not os.path.isfile( test.workpath(f) ) )
+        test.fail_test( not os.path.isfile( test.workpath(work_dir,f) ) )
 
 
 def checkLog( test, logfile, numUpToDate, numCache ):
-    test.fail_test(not os.path.exists(test.workpath(logfile)))
-    log = test.read(test.workpath(logfile))
+    test.fail_test(not os.path.exists(test.workpath(work_dir, logfile)))
+    log = test.read(test.workpath(work_dir, logfile))
     try:
         test.fail_test( len( re.findall( "is up to date", log ) ) != numUpToDate )
         test.fail_test( len( re.findall( "\(cached\): Building \S+ failed in a previous run.", log ) ) != numCache )
     except:
-        print "contents of log ", test.workpath(logfile), "\n", log
+        print "contents of log ", test.workpath(work_dir, logfile), "\n", log
         raise 
 
 
@@ -73,9 +79,9 @@ try:
 
     # 1.1 if checks are ok, the cache mechanism should work
 
-    reset(dot=0)
+    reset(match=2)
     
-    test.write( 'SConstruct', """
+    test.write([work_dir,  'SConstruct'], """
 env = Environment()
 import os
 env['ENV']['PATH'] = os.environ['PATH']
@@ -102,17 +108,17 @@ Checking for C++ header file vector... yes
 """ % (lib, lib))
 
 
-    test.run(stdout = required_stdout)
+    test.run(chdir=work_dir, stdout = required_stdout)
     checkLog(test,'config.log', 0, 0 )
 
-    test.run(stdout = required_stdout)
+    test.run(chdir=work_dir, stdout = required_stdout)
     checkLog(test,'config.log',12, 0 )
 
     # 1.2 if checks are not ok, the cache mechanism should work as well
     #     (via explicit cache)
-    reset(dot = 0)              # match exactly, "()" is a regexp thing
+    reset(match=2)              # match exactly, "()" is a regexp thing
 
-    test.write( 'SConstruct', """
+    test.write([work_dir,  'SConstruct'], """
 env = Environment()
 import os
 env['ENV']['PATH'] = os.environ['PATH']
@@ -132,10 +138,10 @@ Checking for main() in C library no_c_library_SAFFDG... no
 """)
 
 
-    test.run(stdout = required_stdout)
+    test.run(chdir=work_dir, stdout = required_stdout)
     checkLog(test, 'config.log', 0, 0 )
     
-    test.run(stdout = required_stdout)
+    test.run(chdir=work_dir, stdout = required_stdout)
     checkLog(test, 'config.log', 2, 2 )
 
 
@@ -143,7 +149,7 @@ Checking for main() in C library no_c_library_SAFFDG... no
     reset()
     
 
-    test.write( 'SConstruct', """
+    test.write([work_dir,  'SConstruct'], """
 env = Environment()
 import os
 env['ENV']['PATH'] = os.environ['PATH']
@@ -154,11 +160,11 @@ env = conf.Finish()
 Export( 'env' )
 SConscript( 'SConscript' )
 """)
-    test.write( 'SConscript', """
+    test.write([work_dir,  'SConscript'], """
 Import( 'env' )
 env.Program( 'TestProgram', 'TestProgram.c' )
 """)
-    test.write( 'TestProgram.c', """
+    test.write([work_dir,  'TestProgram.c'], """
 #include <stdio.h>
 
 int main() {
@@ -170,10 +176,10 @@ int main() {
     """Checking for C header file math.h... yes
 Checking for C header file no_std_c_header.h... no
 """)
-    test.run( stdout = required_stdout )
+    test.run(chdir=work_dir,  stdout = required_stdout )
     checkLog( test, 'config.log', 0, 0 )
     
-    test.run( stdout = required_stdout )
+    test.run(chdir=work_dir,  stdout = required_stdout )
     checkLog( test, 'config.log', 3, 1 )
 
 
@@ -181,7 +187,7 @@ Checking for C header file no_std_c_header.h... no
     reset()
     
 
-    test.write( 'SConstruct', """
+    test.write([work_dir,  'SConstruct'], """
 env = Environment(LOGFILE='build/config.log')
 import os
 env['ENV']['PATH'] = os.environ['PATH']
@@ -194,11 +200,11 @@ Export( 'env' )
 # print open( 'build/config.log' ).readlines()
 SConscript( 'build/SConscript' )
 """)
-    test.write( 'SConscript', """
+    test.write([work_dir,  'SConscript'], """
 Import( 'env' )
 env.Program( 'TestProgram', 'TestProgram.c' )
 """)
-    test.write( 'TestProgram.c', """
+    test.write([work_dir,  'TestProgram.c'], """
 #include <stdio.h>
 
 int main() {
@@ -210,18 +216,18 @@ int main() {
     """Checking for C header file math.h... yes
 Checking for C header file no_std_c_header.h... no
 """)
-    test.run( stdout = required_stdout )
+    test.run(chdir=work_dir,  stdout = required_stdout )
     checkLog( test, 'build/config.log', 0, 0 )
     
-    test.run( stdout = required_stdout )
+    test.run(chdir=work_dir,  stdout = required_stdout )
     checkLog( test, 'build/config.log', 3, 1 )
     
     # 2.3 test that Configure calls in SConscript files work
     #     even if BuildDir is set
     reset()
 
-    test.subdir( 'sub', ['sub', 'local'] )
-    test.write( 'SConstruct', """
+    test.subdir( [work_dir, 'sub'], [work_dir, 'sub', 'local'] )
+    test.write([work_dir,  'SConstruct'], """
 opts = Options()
 opts.Add('chdir')
 env = Environment(options=opts)
@@ -232,11 +238,12 @@ else:
 BuildDir( 'build', '.' )
 SConscript( 'build/SConscript' )
 """)
-    test.write( 'sub/local/local_header.h', "/* Hello World */" )
-    test.write( 'SConscript', """
+    test.write([work_dir,  'sub', 'local', 'local_header.h'],
+               "/* Hello World */" )
+    test.write([work_dir,  'SConscript'], """
 SConscript( 'sub/SConscript' )
 """)
-    test.write( 'sub/SConscript', """
+    test.write([work_dir,  'sub', 'SConscript'], """
 def CustomTest(context):
   context.Message('Executing Custom Test ... ')
   ret = context.TryCompile('#include "local_header.h"', '.c')
@@ -261,10 +268,10 @@ if not conf.CustomTest():
 env = conf.Finish()
 env.Program( 'TestProgram', 'TestProgram.c' )
 """)
-    test.write( 'sub/TestProgram.h', """
+    test.write([work_dir, 'sub', 'TestProgram.h'], """
 /* Just a test header */
 """)
-    test.write( 'sub/TestProgram.c', """
+    test.write([work_dir, 'sub', 'TestProgram.c'], """
 #include "TestProgram.h"
 #include <stdio.h>
 
@@ -279,22 +286,22 @@ Checking for C header file no_std_c_header.h... no
 Executing Custom Test ... ok
 """)
     # first with SConscriptChdir(0)
-    test.run(stdout = required_stdout, arguments='chdir=no')
+    test.run(chdir=work_dir, stdout = required_stdout, arguments='chdir=no')
     checkFiles( test, [".sconf_temp/.cache", "config.log"] )
     checkLog( test, 'config.log', 0, 0 )
 
-    test.run(stdout = required_stdout, arguments='chdir=no')
+    test.run(chdir=work_dir, stdout = required_stdout, arguments='chdir=no')
     checkFiles( test, [".sconf_temp/.cache", "config.log"] )
     checkLog( test, 'config.log', 5, 1 )
 
-    shutil.rmtree(test.workpath(".sconf_temp"))
+    shutil.rmtree(test.workpath(work_dir, ".sconf_temp"))
 
     # now with SConscriptChdir(1)
-    test.run(stdout = required_stdout, arguments='chdir=yes')
+    test.run(chdir=work_dir, stdout = required_stdout, arguments='chdir=yes')
     checkFiles( test, [".sconf_temp/.cache", "config.log"] )
     checkLog( test, 'config.log', 0, 0 )
 
-    test.run(stdout = required_stdout, arguments='chdir=yes')
+    test.run(chdir=work_dir, stdout = required_stdout, arguments='chdir=yes')
     checkFiles( test, [".sconf_temp/.cache", "config.log"] )
     checkLog( test, 'config.log', 5, 1 )
 
@@ -307,8 +314,8 @@ Executing Custom Test ... ok
     linkFAIL = "void myFunc(); int main() { myFunc(); }"
     runOK = compileOK
     runFAIL = "int main() { return 1; }"
-    test.write('pyAct.py', 'import sys\nprint sys.argv[1]\nsys.exit(int(sys.argv[1]))\n') 
-    test.write('SConstruct', """ 
+    test.write([work_dir, 'pyAct.py'], 'import sys\nprint sys.argv[1]\nsys.exit(int(sys.argv[1]))\n') 
+    test.write([work_dir, 'SConstruct'], """ 
 def CheckCustom(test):
     test.Message( 'Executing MyTest ... ' )
     retCompileOK = test.TryCompile( '%s', '.c' )
@@ -336,12 +343,49 @@ env = conf.Finish()
        python, python ) )
     required_stdout = test.wrap_stdout(build_str='.*',
                                        read_str="Executing MyTest ... ok\n")
-    test.run(stdout = required_stdout)
+    test.run(chdir=work_dir, stdout = required_stdout)
     checkLog( test, 'config.log', 0, 0 )
 
-    test.run(stdout = required_stdout)
+    test.run(chdir=work_dir, stdout = required_stdout)
     checkLog( test, 'config.log', 12, 4 )
 
+    # 4.1 test that calling normal builders from an actual configuring
+    # environment works
+    reset()
+
+    test.write([work_dir, 'cmd.py'], r"""
+import sys
+sys.stderr.write( 'Hello World on stderr\n' )
+sys.stdout.write( 'Hello World on stdout\n' )
+open(sys.argv[1], 'w').write( 'Hello World\n' )
+""")
+
+    test.write([work_dir, 'SConstruct'], """
+env = Environment()
+def CustomTest(*args):
+    return 0
+conf = env.Configure(custom_tests = {'MyTest' : CustomTest})
+if not conf.MyTest():
+    env.Command("hello", [], "%s cmd.py $TARGET")
+env = conf.Finish()
+""" % python)
+    test.run(chdir=work_dir, stderr="Hello World on stderr\n")
+
+    # 4.2 test that calling Configure from a builder results in a
+    # readable Error
+    reset(match=2)
+    
+    test.write([work_dir, 'SConstruct'], """
+def ConfigureAction(target, source, env):
+    env.Configure()
+    return 0
+env = Environment(BUILDERS = {'MyAction' :
+                              Builder(action=Action(ConfigureAction))})
+env.MyAction('target', [])
+""")
+    test.run(chdir=work_dir, status=2,
+             stderr="scons: *** Calling Configure from Builders is not supported.\n")
+    
     test.pass_test()
     
 finally:
index d889c441a5a213404d2403f285281bfc04210a40..69b7a822eaf3db5135b0726a6b423c58313578c1 100644 (file)
@@ -384,4 +384,14 @@ test.run(arguments = ".",
          stdout = test.wrap_stdout(read_str = "SConstruct\nsub/SConscript\nx = xxx\n",
                                    build_str = "scons: `.' is up to date.\n"))
 
+test.write("SConstruct", """\
+def builder(target, source, env):
+    import SCons.Script.SConscript
+    assert SCons.Script.SConscript.sconscript_reading == 0
+env = Environment(BUILDERS={'builder':Builder(action=builder)})
+env.builder('test',[])
+import SCons.Script.SConscript
+assert SCons.Script.SConscript.sconscript_reading == 1
+""")
+
 test.pass_test()