goes by and developers contribute new useful tests.)
.TP
-.RI Configure.CheckCHeader( self ", " header )
+.RI Configure.CheckCHeader( self ", " header ", [" include_quotes ])
Checks if
.I header
-is usable in the C-language.
+is usable in the C-language. The optional argument
+.I include_quotes
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote (both default
+to \N'34')
Returns 1 on success and 0 on failure.
.TP
-.RI Configure.CheckCXXHeader( self ", " header )
+.RI Configure.CheckCXXHeader( self ", " header ", [" include_quotes ])
Checks if
.I header
-is usable in the C++ language.
-Returns 1 on success and 0 on failure.
+is usable in the C++ language. The optional argument
+.I include_quotes
+must be
+a two character string, where the first character denotes the opening
+quote and the second character denotes the closing quote (both default
+to \N'34')
+Returns 1 on success and 0 on failure.
.TP
.RI Configure.CheckLib( self ", [" library ", " symbol ", " autoadd ])
outfile = test.workpath('outfile')
outfile2 = test.workpath('outfile2')
+pipe_file = test.workpath('pipe.out')
scons_env = SCons.Environment.Environment()
def test_pipe_execute(self):
"""Test capturing piped output from an action
"""
- pipe_file = open( test.workpath('pipe.out'), "w" )
+ pipe = open( pipe_file, "w" )
self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1,
- PSTDOUT = pipe_file, PSTDERR = pipe_file)
+ PSTDOUT = pipe, PSTDERR = pipe)
# everything should also work when piping output
self.test_execute()
self.env['PSTDOUT'].close()
- pipe_out = test.read( test.workpath('pipe.out') )
+ pipe_out = test.read( pipe_file )
if sys.platform == 'win32':
cr = '\r'
else:
cr = ''
- found = re.findall( "act.py: stdout: executed act.py%s\nact.py: stderr: executed act.py%s\n" % (cr, cr), pipe_out )
+ act_out = "act.py: stdout: executed act.py"
+ act_err = "act.py: stderr: executed act.py"
+ found = re.findall( "%s%s\n%s%s\n" % (act_out, cr, act_err, cr),
+ pipe_out )
assert len(found) == 8, found
+ # test redirection operators
+ def test_redirect(self, redir):
+ cmd = r'%s %s %s xyzzy %s' % (python, act_py, outfile, redir)
+ pipe = open( pipe_file, "w" )
+ act = SCons.Action.CommandAction(cmd)
+ r = act([], [], self.env.Copy(PSTDOUT = pipe, PSTDERR = pipe))
+ pipe.close()
+ assert r == 0
+ return (test.read(outfile2, 'r'), test.read(pipe_file, 'r'))
+
+ (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2 )
+ assert redirected == "%s\n" % act_out
+ assert pipe_out == "%s\n" % act_err
+
+ (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2 )
+ assert redirected == "%s\n" % act_err
+ assert pipe_out == "%s\n" % act_out
+
+ (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2 )
+ assert (redirected == "%s\n%s\n" % (act_out, act_err) or
+ redirected == "%s\n%s\n" % (act_err, act_out))
+ assert pipe_out == ""
+
+
def test_set_handler(self):
"""Test setting the command handler...
"""
creating it first if necessary."""
if not self._sconsign:
import SCons.Sig
- self._sconsign = SCons.Sig.SConsignFile(self)
+ self._sconsign = SCons.Sig.SConsignFileFactory(self)
return self._sconsign
def srcnode(self):
# the command name and the command's stdout is written to stdout
# the command's stderr is written to stderr
s = _get_env_command( sh, escape, cmd, args, env)
- # write the command line out
- if stdout != None:
- stdout.write(string.join(args) + '\n')
proc = popen2.Popen3(s, 1)
# process stdout
if stdout != None:
- #for line in proc.fromchild.xreadlines():
- # stdout.write(line)
- while 1:
- line = proc.fromchild.readline()
- if not line:
- break
- stdout.write(line)
+ stdout.write(proc.fromchild.read())
# process stderr
if stderr != None:
- #for line in proc.childerr.xreadlines():
- # stderr.write(line)
- while 1:
- line = proc.childerr.readline()
- if not line:
- break
- stderr.write(line)
+ stderr.write(proc.childerr.read())
stat = proc.wait()
if stat & 0xff:
return stat | 0x80
return stat >> 8
-
+
def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr):
# spawn using fork / exec and providing a pipe for the command's
# stdout / stderr stream
(rFdOut, wFdOut) = os.pipe()
rFdErr = rFdOut
wFdErr = wFdOut
- # write the command line out
- if stdout != None:
- stdout.write(string.join(args) + '\n')
# do the fork
pid = os.fork()
if not pid:
pid, stat = os.waitpid(pid, 0)
os.close( wFdOut )
if stdout != stderr:
- os.close( wFdErr )
+ os.close( wFdErr )
childOut = os.fdopen( rFdOut )
if stdout != stderr:
childErr = os.fdopen( rFdErr )
childErr = childOut
# process stdout
if stdout != None:
- #for line in childOut.xreadlines():
- # stdout.write(line)
- while 1:
- line = childOut.readline()
- if not line:
- break
- stdout.write(line)
+ stdout.write( childOut.read() )
# process stderr
if stderr != None:
- #for line in childErr.xreadlines():
- # stderr.write(line)
- while 1:
- line = childErr.readline()
- if not line:
- break
- stdout.write(line)
+ stderr.write( childErr.read() )
os.close( rFdOut )
if stdout != stderr:
os.close( rFdErr )
return stat | 0x80
return stat >> 8
-
-
+
+
def generate(env):
# If the env command exists, then we can use os.system()
# scons.
def piped_spawn(sh, escape, cmd, args, env, stdout, stderr):
+ # There is no direct way to do that in python. What we do
+ # here should work for most cases:
+ # In case stdout (stderr) is not redirected to a file,
+ # we redirect it into a temporary file tmpFileStdout
+ # (tmpFileStderr) and copy the contents of this file
+ # to stdout (stderr) given in the argument
if not sh:
sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
return 127
else:
- # NOTE: This is just a big, big hack. What we do is simply pipe the
- # output to a temporary file and then write it to the streams.
- # I DO NOT know the effect of adding these to a command line that
- # already has indirection symbols.
- tmpFile = os.path.normpath(tempfile.mktemp())
- args.append(">" + str(tmpFile))
- args.append("2>&1")
- if stdout != None:
- # ToDo: use the printaction instead of that
- stdout.write(string.join(args) + "\n")
+ # one temporary file for stdout and stderr
+ tmpFileStdout = os.path.normpath(tempfile.mktemp())
+ tmpFileStderr = os.path.normpath(tempfile.mktemp())
+
+ # check if output is redirected
+ stdoutRedirected = 0
+ stderrRedirected = 0
+ for arg in args:
+ # are there more possibilities to redirect stdout ?
+ if (string.find( arg, ">", 0, 1 ) != -1 or
+ string.find( arg, "1>", 0, 2 ) != -1):
+ stdoutRedirected = 1
+ # are there more possibilities to redirect stderr ?
+ if string.find( arg, "2>", 0, 2 ) != -1:
+ stderrRedirected = 1
+
+ # redirect output of non-redirected streams to our tempfiles
+ if stdoutRedirected == 0:
+ args.append(">" + str(tmpFileStdout))
+ if stderrRedirected == 0:
+ args.append("2>" + str(tmpFileStderr))
+
+ # actually do the spawn
try:
- try:
- args = [sh, '/C', escape(string.join(args)) ]
- ret = os.spawnve(os.P_WAIT, sh, args, env)
- except OSError, e:
- ret = exitvalmap[e[0]]
+ args = [sh, '/C', escape(string.join(args)) ]
+ ret = os.spawnve(os.P_WAIT, sh, args, env)
+ except OSError, e:
+ # catch any error
+ ret = exitvalmap[e[0]]
+ if stderr != None:
stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+ # copy child output from tempfiles to our streams
+ # and do clean up stuff
+ if stdout != None and stdoutRedirected == 0:
try:
- input = open( tmpFile, "r" )
- while 1:
- line = input.readline()
- if not line:
- break
- if stdout != None:
- stdout.write(line)
- if stderr != None and stderr != stdout:
- stderr.write(line)
- finally:
- input.close()
- finally:
- try:
- os.remove( tmpFile )
- except OSError:
- # What went wrong here ??
+ stdout.write(open( tmpFileStdout, "r" ).read())
+ os.remove( tmpFileStdout )
+ except:
pass
+
+ if stderr != None and stderrRedirected == 0:
+ try:
+ stderr.write(open( tmpFileStderr, "r" ).read())
+ os.remove( tmpFileStderr )
+ except:
+ pass
return ret
def spawn(sh, escape, cmd, args, env):
import os
import shutil
import sys
+import traceback
from types import *
import SCons.Action
import SCons.Util
import SCons.Warnings
+# First i thought of using a different filesystem as the default_fs,
+# but it showed up that there are too many side effects in doing that.
+SConfFS=SCons.Node.FS.default_fs
+
_ac_build_counter = 0
_ac_config_counter = 0
_activeSConfObjects = {}
pass
SCons.Warnings.enableWarningClass( SConfWarning )
-
+# action to create the source
def _createSource( target, source, env ):
- fd = open(str(target[0]), "w")
+ fd = open(target[0].get_path(), "w")
fd.write(env['SCONF_TEXT'])
fd.close()
+def _stringSource( target, source, env ):
+ import string
+ return (target[0].get_path() + ' <- \n |' +
+ string.replace( env['SCONF_TEXT'], "\n", "\n |" ) )
+
class SConf:
"""This is simply a class to represent a configure context. After
"""
def __init__(self, env, custom_tests = {}, conf_dir='#/.sconf_temp',
- log_file='#config.log'):
+ log_file='#/config.log'):
"""Constructor. Pass additional tests in the custom_tests-dictinary,
e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
defines a custom test.
Note also the conf_dir and log_file arguments (you may want to
build tests in the BuildDir, not in the SourceDir)
"""
+ global SConfFS
+ if not SConfFS:
+ SConfFS = SCons.Node.FS.FS(SCons.Node.FS.default_fs.pathTop)
if len(_activeSConfObjects.keys()) > 0:
raise (SCons.Errors.UserError,
"Only one SConf object may be active at one time")
self.env = env
if log_file != None:
- self.logfile = SCons.Node.FS.default_fs.File(log_file)
+ self.logfile = SConfFS.File(log_file)
else:
self.logfile = None
self.logstream = None
}
self.AddTests(default_tests)
self.AddTests(custom_tests)
- self.confdir = SCons.Node.FS.default_fs.Dir(conf_dir)
+ self.confdir = SConfFS.Dir(conf_dir)
+ self.calc = None
self.cache = {}
self._startup()
self._shutdown()
return self.env
- def setCache(self, nodes, already_done = []):
+ def _setCache(self, nodes, already_done = []):
# Set up actions used for caching errors
# Caching positive tests should not be necessary, cause
# the build system knows, if test objects/programs/outputs
# tests
if (n.has_builder() and
not n in already_done):
- n.add_pre_action(SCons.Action.Action(self._preCache))
- n.add_post_action(SCons.Action.Action(self._postCache))
- already_done.append( n )
- self.setCache(n.children())
+ n.add_pre_action(SCons.Action.Action(self._preCache,
+ self._stringCache))
+ n.add_post_action(SCons.Action.Action(self._postCache,
+ self._stringCache))
+ already_done.append( n )
+ self._setCache(n.children())
def BuildNodes(self, nodes):
"""
sys.stdout = self.logstream
oldStderr = sys.stderr
sys.stderr = self.logstream
-
- self.setCache( nodes )
+ self._setCache( nodes )
ret = 1
try:
- oldPwd = SCons.Node.FS.default_fs.getcwd()
- SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Top)
# ToDo: use user options for calc
- calc = SCons.Sig.Calculator(max_drift=0)
+ self.calc = SCons.Sig.Calculator(max_drift=0)
tm = SCons.Taskmaster.Taskmaster( nodes,
SConfBuildTask,
- calc )
+ self.calc )
# we don't want to build tests in parallel
jobs = SCons.Job.Jobs(1, tm )
try:
jobs.run()
+ except SCons.Errors.BuildError, e:
+ sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
+ if e.errstr == 'Exception':
+ traceback.print_exception(e.args[0], e.args[1], e.args[2])
except:
- pass
+ raise
for n in nodes:
state = n.get_state()
state != SCons.Node.up_to_date):
# the node could not be built. we return 0 in this case
ret = 0
- SCons.Node.FS.default_fs.chdir(oldPwd)
finally:
if self.logstream != None:
# restore stdout / stderr
nodesToBeBuilt = []
- #target = self.confdir.File("conftest_" + str(_ac_build_counter))
f = "conftest_" + str(_ac_build_counter)
- target = os.path.join(str(self.confdir), f)
+ pref = self.env.subst( builder.builder.prefix )
+ suff = self.env.subst( builder.builder.suffix )
+ target = self.confdir.File(pref + f + suff)
self.env['SCONF_TEXT'] = text
if text != None:
source = self.confdir.File(f + extension)
compilation was successful, 0 otherwise. The target is saved in
self.lastTarget (for further processing).
"""
- #ok = self.TryCompile( text, extension)
- #if( ok ):
return self.TryBuild(self.env.Program, text, extension )
- #else:
- # return 0
def TryRun(self, text, extension ):
"""Compiles and runs the program given in text, using extension
ok = self.TryLink(text, extension)
if( ok ):
prog = self.lastTarget
- output = SCons.Node.FS.default_fs.File(str(prog)+'.out')
- node = self.no_pipe_env.Command(output, prog, "%s >%s" % (str(prog),
- str(output)))
+ output = SConfFS.File(prog.get_path()+'.out')
+ node = self.env.Command(output, prog, "%s > $TARGET" % (prog.get_path()))
ok = self.BuildNodes([node])
if ok:
outputStr = output.get_contents()
# We record errors in the cache. Only non-exisiting targets may
# have recorded errors
needs_rebuild = target[0].exists()
- buildSig = target[0].builder.action.get_contents(target, source, env)
+ buildSig = target[0].calc_signature(self.calc)
for node in source:
if node.get_state() != SCons.Node.up_to_date:
# if any of the sources has changed, we cannot use our cache
needs_rebuild = 1
- if not self.cache.has_key( str(target[0]) ):
+ if not self.cache.has_key( target[0].get_path() ):
# We have no recorded error, so we try to build the target
needs_rebuild = 1
else:
- lastBuildSig = self.cache[str(target[0])]['builder']
+ lastBuildSig = self.cache[target[0].get_path()]['builder']
if lastBuildSig != buildSig:
needs_rebuild = 1
if not needs_rebuild:
# When we are here, we can savely pass the recorded error
print ('(cached): Building "%s" failed in a previous run.' %
- str(target[0]))
+ target[0].get_path())
return 1
else:
# Otherwise, we try to record an error
- self.cache[str(target[0])] = {
+ self.cache[target[0].get_path()] = {
'builder' : buildSig
}
# Action after target is successfully built
#
# No error during build -> remove the recorded error
- del self.cache[str(target[0])]
+ del self.cache[target[0].get_path()]
+
+ def _stringCache(self, target, source, env):
+ return None
def _loadCache(self):
# try to load build-error cache
try:
- cacheDesc = cPickle.load(open(str(self.confdir.File(".cache"))))
+ cacheDesc = cPickle.load(open(self.confdir.File(".cache").get_path()))
if cacheDesc['scons_version'] != SCons.__version__:
raise Exception, "version mismatch"
self.cache = cacheDesc['data']
except:
self.cache = {}
- #SCons.Warnings.warn( SConfWarning,
- # "Couldn't load SConf cache (assuming empty)" )
def _dumpCache(self):
# try to dump build-error cache
try:
cacheDesc = {'scons_version' : SCons.__version__,
'data' : self.cache }
- cPickle.dump(cacheDesc, open(str(self.confdir.File(".cache")),"w"))
- except:
- SCons.Warnings.warn( SConfWarning,
- "Couldn't dump SConf cache" )
-
- def createDir(self, node):
- if not node.up().exists():
- self.createDir( node.up() )
- if not node.exists():
- SCons.Node.FS.Mkdir(node, None, self.env)
+ cPickle.dump(cacheDesc, open(self.confdir.File(".cache").get_path(),"w"))
+ except Exception, e:
+ # this is most likely not only an IO error, but an error
+ # inside SConf ...
+ SCons.Warnings.warn( SConfWarning, "Couldn't dump SConf cache" )
+
+ def _createDir( self, node ):
+ dirName = node.get_path()
+ if not os.path.isdir( dirName ):
+ os.makedirs( dirName )
node._exists = 1
def _startup(self):
"""
global _ac_config_counter
global _activeSConfObjects
-
- #def createDir( node, self = self ):
- # if not node.up().exists():
- # createDir( node.up() )
- # if not node.exists():
- # SCons.Node.FS.Mkdir(node, None, self.env)
- # node._exists = 1
- self.createDir(self.confdir)
- # we don't want scons to build targets confdir automatically
- # cause we are doing it 'by hand'
+ global SConfFS
+
+ self.lastEnvFs = self.env.fs
+ self.env.fs = SConfFS
+ self._createDir(self.confdir)
self.confdir.up().add_ignore( [self.confdir] )
- self.confdir.set_state( SCons.Node.up_to_date )
-
- self.no_pipe_env = self.env.Copy()
- # piped spawn will print its own actions (CHANGE THIS!)
- SCons.Action.print_actions = 0
if self.logfile != None:
# truncate logfile, if SConf.Configure is called for the first time
# in a build
log_mode = "w"
else:
log_mode = "a"
- self.logstream = open(str(self.logfile), log_mode)
+ self.logstream = open(self.logfile.get_path(), log_mode)
# logfile may stay in a build directory, so we tell
# the build system not to override it with a eventually
# existing file with the same name in the source directory
self.env['PIPE_BUILD'] = 1
self.env['PSTDOUT'] = self.logstream
self.env['PSTDERR'] = self.logstream
- else:
+
+ tb = traceback.extract_stack()[-3]
+
+ self.logstream.write( '\nfile %s,line %d:\n\tConfigure( confdir = %s )\n\n' %
+ (tb[0], tb[1], self.confdir.get_path()) )
+ else:
self.logstream = None
# we use a special builder to create source files from TEXT
- action = SCons.Action.Action(_createSource,varlist=['SCONF_TEXT'])
+ action = SCons.Action.Action(_createSource,
+ _stringSource,
+ varlist=['SCONF_TEXT'])
sconfSrcBld = SCons.Builder.Builder(action=action)
self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
self.active = 1
if not self.active:
raise SCons.Errors.UserError, "Finish may be called only once!"
- # Piped Spawn print its own actions. CHANGE THIS!
- SCons.Action.print_actions = 1
if self.logstream != None:
self.logstream.close()
self.logstream = None
self.active = 0
del _activeSConfObjects[self]
self._dumpCache()
-
+ self.env.fs = self.lastEnvFs
class CheckContext:
"""Provides a context for configure tests. Defines how a test writes to the
def __getattr__( self, attr ):
if( attr == 'env' ):
return self.sconf.env
+ elif( attr == 'lastTarget' ):
+ return self.sconf.lastTarget
else:
raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
-def CheckCHeader(test, header):
+def _header_prog( header, include_quotes ):
+ return "#include %s%s%s\n\n" % (include_quotes[0],
+ header,
+ include_quotes[1])
+
+def CheckCHeader(test, header, include_quotes='""'):
"""
A test for a c header file.
"""
# ToDo: Support also system header files (i.e. #include <header.h>)
- test.Message("Checking for C header %s... " % header)
- ret = test.TryCompile("#include \"%s\"\n\n" % header, ".c")
+ test.Message("Checking for C header %s ... " % header)
+ ret = test.TryCompile(_header_prog(header, include_quotes), ".c")
test.Result( ret )
return ret
-def CheckCXXHeader(test, header):
+def CheckCXXHeader(test, header, include_quotes='""'):
"""
A test for a c++ header file.
"""
# ToDo: Support also system header files (i.e. #include <header.h>)
- test.Message("Checking for C header %s... " % header)
- ret = test.TryCompile("#include \"%s\"\n\n" % header, ".cpp")
+ test.Message("Checking for C++ header %s ... " % header)
+ ret = test.TryCompile(_header_prog(header, include_quotes), ".cpp")
test.Result( ret )
return ret
compiles without flags.
"""
# ToDo: accept path for the library
- test.Message("Checking for %s in library %s... " % (symbol, library))
+ test.Message("Checking for %s in library %s ... " % (symbol, library))
oldLIBS = test.env.get( 'LIBS', [] )
# NOTE: we allow this at in the case that we don't know what the
#include "%s"
int main() {
%s
+ return 0;
}
""" % (header, call)
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import os
import re
+import string
import StringIO
import sys
+from types import *
import unittest
import TestCmd
-import SCons.Environment
-import SCons.SConf
-
-scons_env = SCons.Environment.Environment()
sys.stdout = StringIO.StringIO()
if sys.platform == 'win32':
else:
existing_lib = "m"
-def clearFileCache(dir):
- # mostly from FS.Dir.__clearRepositoryCache, but set also state
- # of nodes to None
- for node in dir.entries.values():
- if node != dir.dir:
- if node != dir and isinstance(node, SCons.Node.FS.Dir):
- clearFileCache(node)
- else:
- node._srcreps = None
- del node._srcreps
- node._rfile = None
- del node._rfile
- node._rexists = None
- del node._rexists
- node._exists = None
- del node._exists
- node._srcnode = None
- del node._srcnode
- node.set_state(None)
-
class SConfTestCase(unittest.TestCase):
def setUp(self):
- self.test = TestCmd.TestCmd(workdir = '')
+ # we always want to start with a clean directory
+ self.test = TestCmd.TestCmd(workdir = '')
def tearDown(self):
self.test.cleanup()
def _resetSConfState(self):
-
- clearFileCache( SCons.Node.FS.default_fs.Dir(self.test.workpath()) )
- SCons.SConf._ac_config_counter = 0
- SCons.SConf._ac_build_counter = 0
-
+ # Ok, this is tricky, and i do not know, if everything is sane.
+ # We try to reset scons' state (including all global variables)
+ import SCons.Sig
+ SCons.Sig.write() # simulate normal scons-finish
+ for n in sys.modules.keys():
+ if string.split(n, '.')[0] == 'SCons':
+ m = sys.modules[n]
+ if type(m) is ModuleType:
+ # if this is really a scons module, clear its namespace
+ del sys.modules[n]
+ m.__dict__.clear()
+ # we only use SCons.Environment and SCons.SConf for these tests.
+ import SCons.Environment
+ import SCons.SConf
+ self.Environment = SCons.Environment
+ self.SConf = SCons.SConf
+ # and we need a new environment, cause references may point to
+ # old modules (well, at least this is safe ...)
+ self.scons_env = self.Environment.Environment()
+ self.scons_env['ENV']['PATH'] = os.environ['PATH']
+
+ # we want to do some autodetection here
+ # this stuff works with
+ # - cygwin on win32 (using cmd.exe, not bash)
+ # - posix
+ # - msvc on win32 (hopefully)
+ if self.scons_env.subst('$CXX') == 'c++':
+ # better use g++ (which is normally no symbolic link
+ # --> the c++ call fails on cygwin
+ self.scons_env['CXX'] = 'g++'
+ if self.scons_env.subst('$LINK') == 'c++':
+ self.scons_env['LINK'] = 'g++'
+ if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
+ not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
+ not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
+ raise Exception, "This test needs an installed compiler!"
+ if self.scons_env['LINK'] == 'g++':
+ global existing_lib
+ existing_lib = 'm'
def _baseTryXXX(self, TryFunc):
- def checks(sconf, TryFunc):
+ # TryCompile and TryLink are much the same, so we can test them
+ # in one method, we pass the function as a string ('TryCompile',
+ # 'TryLink'), so we are aware of reloading modules.
+
+ def checks(self, sconf, TryFuncString):
+ TryFunc = self.SConf.SConf.__dict__[TryFuncString]
res1 = TryFunc( sconf, "int main() { return 0; }", ".c" )
- res2 = TryFunc( sconf, "not a c program", ".c" )
+ res2 = TryFunc( sconf,
+ '#include "no_std_header.h"\nint main() {return 0; }',
+ '.c' )
return (res1,res2)
-
+
+ # 1. test initial behaviour (check ok / failed)
self._resetSConfState()
- sconf = SCons.SConf.SConf(scons_env,
- conf_dir=self.test.workpath('config.tests'),
- log_file=self.test.workpath('config.log'))
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
try:
- res = checks( sconf, TryFunc )
+ res = checks( self, sconf, TryFunc )
assert res[0] and not res[1]
finally:
sconf.Finish()
-
- # test the caching mechanism
+
+ # 2.1 test the error caching mechanism (no dependencies have changed)
self._resetSConfState()
-
- sconf = SCons.SConf.SConf(scons_env,
- conf_dir=self.test.workpath('config.tests'),
- log_file=self.test.workpath('config.log'))
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
try:
- res = checks( sconf, TryFunc )
+ res = checks( self, sconf, TryFunc )
assert res[0] and not res[1]
finally:
sconf.Finish()
-
# we should have exactly one one error cached
log = self.test.read( self.test.workpath('config.log') )
expr = re.compile( ".*(\(cached\))", re.DOTALL )
firstOcc = expr.match( log )
assert firstOcc != None
secondOcc = expr.match( log, firstOcc.end(0) )
- assert secondOcc == None
+ assert secondOcc == None
+
+ # 2.2 test the error caching mechanism (dependencies have changed)
+ self._resetSConfState()
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
+ test_h = self.test.write( self.test.workpath('config.tests', 'no_std_header.h'),
+ "/* we are changing a dependency now */" );
+ try:
+ res = checks( self, sconf, TryFunc )
+ log = self.test.read( self.test.workpath('config.log') )
+ assert res[0] and res[1]
+ finally:
+ sconf.Finish()
def test_TryCompile(self):
- self._baseTryXXX( SCons.SConf.SConf.TryCompile )
+ """Test SConf.TryCompile
+ """
+ self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
def test_TryLink(self):
- self._baseTryXXX( SCons.SConf.SConf.TryLink )
+ """Test SConf.TryLink
+ """
+ self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink )
def test_TryRun(self):
+ """Test SConf.TryRun
+ """
def checks(sconf):
prog = """
#include <stdio.h>
return (res1, res2)
self._resetSConfState()
- sconf = SCons.SConf.SConf(scons_env,
- conf_dir=self.test.workpath('config.tests'),
- log_file=self.test.workpath('config.log'))
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
try:
res = checks(sconf)
assert res[0][0] and res[0][1] == "Hello"
# test the caching mechanism
self._resetSConfState()
-
- sconf = SCons.SConf.SConf(scons_env,
- conf_dir=self.test.workpath('config.tests'),
- log_file=self.test.workpath('config.log'))
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
try:
res = checks(sconf)
assert res[0][0] and res[0][1] == "Hello"
assert not res[1][0] and res[1][1] == ""
finally:
sconf.Finish()
-
# we should have exactly one one error cached
log = self.test.read( self.test.workpath('config.log') )
expr = re.compile( ".*(\(cached\))", re.DOTALL )
def test_TryAction(self):
+ """Test SConf.TryAction
+ """
def actionOK(target, source, env):
open(str(target[0]), "w").write( "RUN OK" )
return None
def actionFAIL(target, source, env):
return 1
self._resetSConfState()
- sconf = SCons.SConf.SConf(scons_env,
+ sconf = self.SConf.SConf(self.scons_env,
conf_dir=self.test.workpath('config.tests'),
log_file=self.test.workpath('config.log'))
try:
def test_StandardTests(self):
+ """Test standard checks
+ """
def CHeaderChecks( sconf ):
- res1 = sconf.CheckCHeader( "stdio.h" )
+ res1 = sconf.CheckCHeader( "stdio.h", include_quotes="<>" )
res2 = sconf.CheckCHeader( "HopefullyNotCHeader.noh" )
return (res1,res2)
def CXXHeaderChecks(sconf):
- res1 = sconf.CheckCXXHeader( "vector" )
+ res1 = sconf.CheckCXXHeader( "vector", include_quotes="<>" )
res2 = sconf.CheckCXXHeader( "HopefullyNotCXXHeader.noh" )
return (res1,res2)
return ((res1, libs1), (res2, libs2))
self._resetSConfState()
- sconf = SCons.SConf.SConf(scons_env,
- conf_dir=self.test.workpath('config.tests'),
- log_file=self.test.workpath('config.log'))
+ sconf = self.SConf.SConf(self.scons_env,
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
try:
(res1, res2) = CHeaderChecks(sconf)
assert res1 and not res2
sconf.Finish()
def test_CustomChecks(self):
-
+ """Test Custom Checks
+ """
def CheckCustom(test):
test.Message( "Checking UserTest ... " )
prog = """
self._resetSConfState()
- sconf = SCons.SConf.SConf(scons_env,
- custom_tests={'CheckCustom': CheckCustom},
- conf_dir=self.test.workpath('config.tests'),
- log_file=self.test.workpath('config.log'))
+ sconf = self.SConf.SConf(self.scons_env,
+ custom_tests={'CheckCustom': CheckCustom},
+ conf_dir=self.test.workpath('config.tests'),
+ log_file=self.test.workpath('config.log'))
try:
ret = sconf.CheckCustom()
assert ret
if __name__ == "__main__":
suite = unittest.makeSuite(SConfTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful ():
+ res = unittest.TextTestRunner().run(suite)
+ if not res.wasSuccessful():
sys.exit(1)
-
+
default_max_drift = 2*24*60*60
#XXX Get rid of the global array so this becomes re-entrant.
-sig_files = []
+sig_files = {}
# 1 means use build signature for derived source files
# 0 means use content signature for derived source files
def write():
global sig_files
- for sig_file in sig_files:
+ for sig_file in sig_files.values():
sig_file.write()
+
+def SConsignFileFactory( dir, module=None):
+ try:
+ return sig_files[dir.path]
+ except KeyError:
+ sig_files[dir.path] = SConsignFile(dir, module)
+ return sig_files[dir.path]
+
+
class SConsignEntry:
"""Objects of this type are pickled to the .sconsign file, so it
SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
"Ignoring corrupt .sconsign file: %s"%self.sconsign)
- global sig_files
- sig_files.append(self)
-
def get(self, filename):
"""
Get the .sconsign entry for a file
bsig = cache.get_bsig()
if bsig is not None:
return bsig
-
+
sigs = map(lambda n, c=self: n.calc_signature(c), children)
if node.has_builder():
sigs.append(self.module.signature(node.get_executor()))
else:
lib = 'm'
-test = TestSCons.TestSCons()
+# to use cygwin compilers on cmd.exe -> uncomment following line
+#lib = 'm'
+
+oldPwd = os.getcwd()
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.match_func = TestCmd.match_re_dotall
+
+
+def checkFiles(test, files):
+ for f in files:
+ test.fail_test( not os.path.isfile( test.workpath(f) ) )
+
def checkLog( test, logfile, numUpToDate, numCache ):
- test.fail_test(not os.path.exists(test.workpath('config.log')))
+ test.fail_test(not os.path.exists(test.workpath(logfile)))
log = test.read(test.workpath(logfile))
- 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 )
-
+ 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
+ raise
try:
# 1.1 if checks are ok, the cache mechanism should work
+ reset(dot=0)
+
test.write( 'SConstruct', """
env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
conf = Configure(env)
r1 = conf.CheckLibWithHeader( '%s', 'math.h', 'c' )
r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' )
env = conf.Finish()
if not (r1 and r2 and r3 and r4 and r5 and r6):
Exit(1)
-""" % (lib, lib))
+""" % (lib,lib))
required_stdout = test.wrap_stdout(build_str='scons: "." is up to date.\n',
read_str=
"""Checking for main(); in library %s (header math.h) ... ok
Checking for main(); in library None (header math.h) ... ok
-Checking for main in library %s... ok
-Checking for main in library None... ok
-Checking for C header math.h... ok
-Checking for C header vector... ok
+Checking for main in library %s ... ok
+Checking for main in library None ... ok
+Checking for C header math.h ... ok
+Checking for C++ header vector ... ok
""" % (lib, lib))
test.run(stdout = required_stdout)
# 1.2 if checks are not ok, the cache mechanism should work as well
# (via explicit cache)
+ reset()
+
test.write( 'SConstruct', """
env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
conf = Configure(env)
r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error
r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error
required_stdout = test.wrap_stdout(build_str='scons: "." is up to date.\n',
read_str=
- """Checking for C header no_std_c_header.h... failed
-Checking for main in library no_c_library_SAFFDG... failed
+ """Checking for C header no_std_c_header.h ... failed
+Checking for main in library no_c_library_SAFFDG ... failed
""")
test.run(stdout = required_stdout)
# 2.1 test that normal builds work together with Sconf
+ reset()
+
+
test.write( 'SConstruct', """
env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
conf = Configure(env)
r1 = conf.CheckCHeader( 'math.h' )
r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error
test.match_func = TestCmd.match_re_dotall
required_stdout = test.wrap_stdout(build_str='.*',
read_str=
- """Checking for C header math.h... ok
-Checking for C header no_std_c_header.h... failed
+ """Checking for C header math.h ... ok
+Checking for C header no_std_c_header.h ... failed
""")
test.run( stdout = required_stdout )
checkLog( test, 'config.log', 0, 0 )
# 2.2 test that BuildDir builds work together with Sconf
+ reset()
+
+
test.write( 'SConstruct', """
env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
BuildDir( 'build', '.' )
conf = Configure(env, conf_dir='build/config.tests', log_file='build/config.log')
r1 = conf.CheckCHeader( 'math.h' )
""")
required_stdout = test.wrap_stdout(build_str='.*',
read_str=
- """Checking for C header math.h... ok
-Checking for C header no_std_c_header.h... failed
+ """Checking for C header math.h ... ok
+Checking for C header no_std_c_header.h ... failed
""")
test.run( stdout = required_stdout )
checkLog( test, 'build/config.log', 0, 0 )
test.run( 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' )
+ test.write( 'SConstruct', """
+BuildDir( 'build', '.' )
+SConscript( 'build/SConscript' )
+""")
+ test.write( 'SConscript', """
+SConscript( 'sub/SConscript' )
+""")
+ test.write( 'sub/SConscript', """
+env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
+conf = Configure( env )
+conf.CheckCHeader( 'math.h' )
+env = conf.Finish()
+env.Program( 'TestProgram', 'TestProgram.c' )
+""")
+ test.write( 'sub/TestProgram.h', """
+/* Just a test header */
+""")
+ test.write( 'sub/TestProgram.c', """
+#include "TestProgram.h"
+#include <stdio.h>
+
+int main() {
+ printf( "Hello\\n" );
+}
+""")
+ test.run()
+ checkFiles( test, [".sconf_temp/.cache", "config.log"] )
# 3.1 test custom tests
+ reset()
+
+
compileOK = '#include <stdio.h>\\nint main() {printf("Hello");return 0;}'
compileFAIL = "syntax error"
linkOK = compileOK
linkFAIL = "void myFunc(); int main() { myFunc(); }"
runOK = compileOK
runFAIL = "int main() { return 1; }"
- test.write( 'pyAct.py', 'import sys\nopen(sys.argv[1], "w").write(sys.argv[2] + "\\n"),\nsys.exit(int(sys.argv[2]))\n' )
- test.write( 'SConstruct', """
+ test.write('pyAct.py', 'import sys\nprint sys.argv[1]\nsys.exit(int(sys.argv[1]))\n')
+ test.write('SConstruct', """
def CheckCustom(test):
- test.Message( 'Executing MyTest...' )
+ test.Message( 'Executing MyTest ... ' )
retCompileOK = test.TryCompile( '%s', '.c' )
retCompileFAIL = test.TryCompile( '%s', '.c' )
retLinkOK = test.TryLink( '%s', '.c' )
retLinkFAIL = test.TryLink( '%s', '.c' )
(retRunOK, outputRunOK) = test.TryRun( '%s', '.c' )
(retRunFAIL, outputRunFAIL) = test.TryRun( '%s', '.c' )
- (retActOK, outputActOK) = test.TryAction( '%s pyAct.py $TARGET 0' )
- (retActFAIL, outputActFAIL) = test.TryAction( '%s pyAct.py $TARGET 1' )
+ (retActOK, outputActOK) = test.TryAction( '%s pyAct.py 0 > $TARGET' )
+ (retActFAIL, outputActFAIL) = test.TryAction( '%s pyAct.py 1 > $TARGET' )
resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello"
resOK = resOK and retActOK and int(outputActOK)==0
resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!=""
return resOK and not resFAIL
env = Environment()
+import os
+env['ENV']['PATH'] = os.environ['PATH']
conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} )
conf.CheckCustom()
env = conf.Finish()
""" % (compileOK, compileFAIL, linkOK, linkFAIL, runOK, runFAIL,
python, python ) )
required_stdout = test.wrap_stdout(build_str='.*',
- read_str="Executing MyTest...ok\n")
+ read_str="Executing MyTest ... ok\n")
test.run(stdout = required_stdout)
checkLog( test, 'config.log', 0, 0 )