From: g0dil Date: Fri, 13 Aug 2010 08:24:42 +0000 (+0000) Subject: Reorganize SCons build system X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=41ac85f6c8a96bdefebdbee4e88193d7f23f50cf;p=senf.git Reorganize SCons build system git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1682 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/SConfigure b/SConfigure new file mode 100644 index 0000000..b8ad5cd --- /dev/null +++ b/SConfigure @@ -0,0 +1,103 @@ +# -*- python -*- + +Import('env') + +########################################################################### +# Custom configuration checks + +@env.ConfTest() +def CheckSTLCopyN(context): + context.Message("Checking for 'copy_n' implementation... ") + versions = [ ('', 'std::copy_n', 'STD'), + ('', '__gnu_cxx::copy_n', 'GNUCXX') ] + for include, name, define in versions: + ret = context.TryCompile("#include %s\n" + "int main(int,char**) { int *a,*b; %s(a,0,b); }\n" + % (include, name), + ".cc") + if ret: + context.Result(name) + context.sconf.Define("HAVE_%s_COPYN" % define, + 1, + "Define one of " + + ", ".join(("HAVE_%s_COPYN" % elt[2] for elt in versions))) + return ret + + context.Result(False) + return False + + +@env.ConfTest() +def CheckTempBufferStrategy(context): + context.Message("Checking for optimal temporary buffer strategy... ") + + def check(): + # locals + ret = context.TryCompile("void test(int n){int a[n];}",".cc") + if ret: return "locals" + + # alloca + ret = context.TryCompile("#include \n" + "void test(int a){void *b(alloca(a));}" + ".cc") + if ret: return "alloca" + + # fallback: new + return "new" + + ret = check() + context.Result(ret) + context.sconf.Define("SENF_BUFFER_USE_%s" % ret.upper(), + 1, + "Define one of SENF_BUFFER_USE_LOCALS, SENF_BUFFER_USE_ALLOCA, " + "SENF_BUFFER_USE_NEW") + return ret + +@env.ConfTest() +def CheckValgrind(context): + context.Message( "Checking for valgrind... " ) + ret = context.TryAction(['$VALGRIND --version >$TARGET']) + context.Result( ret[1].strip() or False ) + return ret[0] + +@env.ConfTest() +def CheckValgrindWildcards(context): + context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " ) + ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'], + "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n", + ".sup") + context.Result( ret[0] ) + return ret[0] + +########################################################################### + +conf = env.Configure(clean=False, + help=False, + config_h="#/senf/autoconf.hh") + +# Boost +res = conf.CheckBoostVersion() +if not res : conf.Fail("Boost includes not found") + +res = conf.CheckBoostVariants() + +res = conf.CheckCXXHeader("boost/bimap.hpp") +conf.env.Replace(NEED_BOOST_EXT = not res) + +res = conf.CheckCXXHeader("boost/spirit/include/classic.hpp") + +# Compiler support +res = conf.CheckTempBufferStrategy() + +# Standard library stuff +res = conf.CheckSTLCopyN() +if not res : conf.Fail("No 'copy_n' implementation found") + +res = conf.CheckFunc("timerfd_create") + +# valgrind +res = conf.CheckValgrind() \ + and conf.CheckValgrindWildcards() +conf.env.Replace(HAVE_VALGRIND = res) + +env = conf.Finish() diff --git a/SConstruct b/SConstruct index c204326..9538cfc 100644 --- a/SConstruct +++ b/SConstruct @@ -1,7 +1,7 @@ # -*- python -*- import sys, os.path, fnmatch -import SENFSCons, senfutil, senfconf +import SENFSCons, senfutil ########################################################################### # Load utilities and setup libraries and configure build @@ -57,81 +57,79 @@ SENF_ETH_TEST_INTERFACE Some unit tests will only run when executed to 'root'. """) +env.Replace( + expandLogOption = senfutil.expandLogOption, + CXXFLAGS_ = env.BuildTypeOptions('CXXFLAGS'), + CPPDEFINES_ = env.BuildTypeOptions('CPPDEFINES'), + LINKFLAGS_ = env.BuildTypeOptions('LINKFLAGS'), +) env.Append( - IMPORT_ENV = [ 'PATH', 'HOME', 'SSH_*', 'SENF*', 'CCACHE_*', 'DISTCC_*' ], - - CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*', + IMPORT_ENV = [ 'PATH', 'HOME', 'SSH_*', 'SENF*', 'CCACHE_*', 'DISTCC_*' ], + + CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*', '.sconf_temp' ], - BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}', - CPPPATH = [ '#', '$BUILDDIR', - '${NEED_BOOST_EXT and "#/boost_ext" or None}' ], - LOCALLIBDIR = '$BUILDDIR', - LIBPATH = [ '$LOCALLIBDIR' ], - LIBS = [ '$EXTRA_LIBS' ], - EXTRA_LIBS = [ 'rt' ], - TEST_EXTRA_LIBS = [ ], - VALGRINDARGS = [ '--num-callers=50' ], - - PREFIX = '#/dist', - LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}', - BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}', - INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}', - CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}', - OBJINSTALLDIR = '$CONFINSTALLDIR', - DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}', - SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons', - - CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ], - CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ], - - # These options are insane. Only useful for inline debugging. Need at least 1G free RAM - INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden', - '-fno-inline-functions', '-Winline' - '--param','large-function-growth=10000', - '--param', 'large-function-insns=10000', - '--param','inline-unit-growth=10000' ], - INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ], - INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ], - CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS', - '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ], - CXXFLAGS_ = senfutil.BuildTypeOptions('CXXFLAGS'), - CXXFLAGS_final = [ '-O3' ], - CXXFLAGS_normal = [ '-O2', '-g' ], - CXXFLAGS_debug = [ '-O0', '-g' ], - - CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ], - expandLogOption = senfutil.expandLogOption, - CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'), - CPPDEFINES_final = [ 'SENF_PPI_NOTRACE', 'BOOST_NO_MT' ], - CPPDEFINES_normal = [ 'SENF_DEBUG' ], - CPPDEFINES_debug = [ '$CPPDEFINES_normal' ], - - LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ], - LINKFLAGS_ = senfutil.BuildTypeOptions('LINKFLAGS'), - LINKFLAGS_final = [ ], - LINKFLAGS_normal = [ '-Wl,-S' ], - LINKFLAGS_debug = [ '-g' ], + CPPPATH = [ '#', '$BUILDDIR', + '${NEED_BOOST_EXT and "#/boost_ext" or None}' ], + LOCALLIBDIR = '$BUILDDIR', + LIBPATH = [ '$LOCALLIBDIR' ], + LIBS = [ '$EXTRA_LIBS' ], + EXTRA_LIBS = [ 'rt' ], + TEST_EXTRA_LIBS = [ ], + VALGRINDARGS = [ '--num-callers=50' ], + + CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ], + CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ], + + # INLINE_OPTS_DEBUG are insane. Only useful for inline debugging. Need at least 1G free RAM + INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden', + '-fno-inline-functions', '-Winline' + '--param','large-function-growth=10000', + '--param', 'large-function-insns=10000', + '--param','inline-unit-growth=10000' ], + INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ], + INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ], + CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS', + '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ], + CXXFLAGS_final = [ '-O3' ], + CXXFLAGS_normal = [ '-O2', '-g' ], + CXXFLAGS_debug = [ '-O0', '-g' ], + + CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ], + CPPDEFINES_final = [ 'SENF_PPI_NOTRACE', 'BOOST_NO_MT' ], + CPPDEFINES_normal = [ 'SENF_DEBUG' ], + CPPDEFINES_debug = [ '$CPPDEFINES_normal' ], + + LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ], + LINKFLAGS_final = [ ], + LINKFLAGS_normal = [ '-Wl,-S' ], + LINKFLAGS_debug = [ '-g' ], ) - env.SetDefault( - LIBSENF = "senf", - LCOV = "lcov", - GENHTML = "genhtml", - VALGRIND = "valgrind", - SCONSBIN = env.File("#/tools/scons"), - SCONSARGS = ([ '-Q', '-j$CONCURRENCY_LEVEL' ] + - [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ]), - SCONS = "@$SCONSBIN $SCONSARGS", - CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1, - TOPDIR = env.Dir('#').abspath, - LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}', - OBJADDSUFFIX = '${LIBADDSUFFIX}', - FLAVOR = '', -) - -env.Replace( - _defines = senfutil.expandDefines + PREFIX = '#/dist', + LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}', + BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}', + INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}', + CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}', + OBJINSTALLDIR = '$CONFINSTALLDIR', + DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}', + SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons', + + BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}', + + LIBSENF = "senf", + LCOV = "lcov", + GENHTML = "genhtml", + VALGRIND = "valgrind", + SCONSBIN = env.File("#/tools/scons"), + SCONSARGS = ([ '-Q', '-j$CONCURRENCY_LEVEL' ] + + [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ]), + SCONS = "@$SCONSBIN $SCONSARGS", + CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1, + TOPDIR = env.Dir('#').abspath, + LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}', + OBJADDSUFFIX = '${LIBADDSUFFIX}', + FLAVOR = '', ) # Set variables from command line @@ -144,43 +142,21 @@ senfutil.parseArguments( ) # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment -env.Append( ENV = dict(( (k,v) - for pattern in env['IMPORT_ENV'] - for k,v in os.environ.iteritems() - if fnmatch.fnmatchcase(k,pattern) )) ) +senfutil.importProcessEnv(env) +# Handle 'test_changes' if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'): import SparseTestHack env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env)) if env.has_key('only_tests') : env['sparse_tests'] = True + Export('env') ########################################################################### # Configure -@senfconf.Test -def CheckValgrind(context): - context.Message( "Checking for valgrind... " ) - ret = context.TryAction(['$VALGRIND --version >$TARGET']) - context.Result( ret[1].strip() or False ) - return ret[0] - -@senfconf.Test -def CheckValgrindWildcards(context): - context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " ) - ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'], - "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n", - ".sup") - context.Result( ret[0] ) - return ret[0] - -def customChecks(conf): - conf.env.Replace( - HAVE_VALGRIND = conf.CheckValgrind() and conf.CheckValgrindWildcards() - ) - -senfutil.Configure(env, customChecks) +SConscript('SConfigure') # Only add this here, after all configure checks have run diff --git a/site_scons/senfconf.py b/site_scons/senfconf.py deleted file mode 100644 index 89ef0c2..0000000 --- a/site_scons/senfconf.py +++ /dev/null @@ -1,88 +0,0 @@ -# from __future__ import with_statement - -_configTests = {} - -# Fix for SCons 0.97 compatibility -import SCons.SConf -try: SCons.SConf.SConfBase.Define -except AttributeError: - import string - def Define(self, name, value = None, comment = None): - lines = [] - if comment: - comment_str = "/* %s */" % comment - lines.append(comment_str) - if value is not None: - define_str = "#define %s %s" % (name, value) - else: - define_str = "#define %s" % name - lines.append(define_str) - lines.append('') - self.config_h_text = self.config_h_text + string.join(lines, '\n') - SCons.SConf.SConfBase.Define = Define - - -def Tests(): - global _configTests - return _configTests - -def Test(func): - global _configTests - _configTests[func.__name__] = func - return func - -# class TemporaryContext: -# def __init__(self, context): -# self._context = context -# self._env = self._context.env.Clone() - -# def __enter__(self): -# return None - -# def __exit__(self,*args): -# self._context.env.Replace(**self._env.Dictionary()) -# return False - -@Test -def CheckBoostVersion(context): - context.Message( "Checking boost version... " ) - ret = context.TryRun("#include \n" - "#include \n" - "int main(int, char **) { std::cout << BOOST_LIB_VERSION << std::endl; }", - ".cc")[-1].strip() - if not ret: - context.Result("no boost includes found") - context.env.Replace( BOOST_VERSION = '' ) - return None - else: - context.Result(ret) - context.env.Replace( BOOST_VERSION = ret ) - return ret - -@Test -def CheckBoostVariants(context, *variants): - useVariant = None -# with TemporaryContext(context): - try: - _env = context.env.Clone() - for variant in variants: - if variant : variantStr = "'%s'" % variant - else : variantStr = "default" - context.Message( "Checking boost %s variant... " % variantStr ) - context.env.Replace( BOOST_VARIANT=variant ) - context.env.Append( _LIBFLAGS = ' -Wl,-Bstatic -l$BOOSTTESTLIB -Wl,-Bdynamic' ) - ret = context.TryLink("#define BOOST_AUTO_TEST_MAIN\n" - "#include \n" - "#include \n" - "BOOST_AUTO_TEST_CASE(test) { BOOST_CHECK(true); }\n", - ".cc") - context.Result( ret ) - if ret and useVariant is None: - useVariant = variant - finally: - context.env.Replace(**_env.Dictionary()) - if useVariant is not None and not context.env.GetOption('no_progress'): - print "Using %s boost variant." % ( - useVariant and "'%s'" % useVariant or "default") - context.env.Replace( BOOST_VARIANT = useVariant ) - return useVariant diff --git a/site_scons/senfutil.py b/site_scons/senfutil.py index cca87a3..fce11f0 100644 --- a/site_scons/senfutil.py +++ b/site_scons/senfutil.py @@ -1,8 +1,5 @@ -import os.path, glob, site_tools.Yaptu +import os, os.path, site_tools.Yaptu, types, re, fnmatch from SCons.Script import * -import SCons.Defaults, SCons.Util -import senfconf -import types, re senfutildir = os.path.dirname(__file__) @@ -13,83 +10,8 @@ except NameError: Variables = Options BoolVariable = BoolOption -def parseLogOption(value): - stream, area, level = ( x.strip() for x in value.strip().split('|') ) - stream = ''.join('(%s)' % x for x in stream.split('::') ) - if area : area = ''.join( '(%s)' % x for x in area.split('::') ) - else : area = '(_)' - return '((%s,%s,%s))' % (stream,area,level) +########################################################################### -def expandLogOption(target, source, env, for_signature): - if env.subst('$LOGLEVELS'): - return [ 'SENF_LOG_CONF="' + ''.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )+'"'] - else: - return [] - -class BuildTypeOptions: - def __init__(self, var): - self._var = var - - def __call__(self, target, source, env, for_signature): - type = env['final'] and "final" or env['debug'] and "debug" or "normal" - return env[self._var + "_" + type] - -_DOLLAR_RE = re.compile(r'\$([a-zA-Z_][\.\w]*)|\${([^}]*)}') - -def _expandDefines(defs, target, source, env, for_signature): - rv = [] - if SCons.Util.is_Dict(defs): - keys = defs.keys() - keys.sort() - defs = [ (k,defs[k]) for k in keys ] - elif not SCons.Util.is_List(defs): - defs = [ defs ] - for elt in defs: - if SCons.Util.is_String(elt): - m = _DOLLAR_RE.match(elt) - if m: - match = m.group(1) or m.group(2) - try: rv.extend(_expandDefines(eval(match, env.gvars(), env.lvars()), - target, source, env, for_signature)) - except NameError: pass - except IndexError: pass - else: - rv.append(env.subst(elt)) - elif callable(elt): - rv.extend(_expandDefines(elt(target, source, env, for_signature), - target, source, env, for_signature)) - elif SCons.Util.is_Sequence(elt): - if len(elt)<2 or elt[1] is None: - rv.append(env.subst(elt[0])) - else: - rv.append(env.subst(elt[0]) + "=" + env.subst(elt[1])) - else: - rv.append(str(elt)) - return rv - -def expandDefines(prefix, defs, suffix, env): - """Expand defines in using . Calls SCons.Defaults._concat_ixes -to append prefix/suffix before/after each define. - - callable - Call the callable and replace it with the call result. If the result - is a list, the list is processed recursively. It the result is a - dictionary it is converted into a list of typles and processed - recursively. - '$' or '${}' - Replace the element with the variable expansion. If the result is a - list, the list is processed recursively. It the result is a - dictionary it is converted into a list of typles and processed - recursively. - '' - Define a symbol with that (expanded!) name - iterable - The iteratble must have two elments. The first element defines the - symbol name, the second the symbol value.""" - - defs = _expandDefines(defs, None, None, env, False) - return SCons.Defaults._concat_ixes(prefix, defs, suffix, env) - def loadTools(env): global senfutildir tooldir = os.path.join(senfutildir, 'site_tools') @@ -123,6 +45,32 @@ Special command line parameters: env.Replace(**{k: v}) env.Append(ARGUMENT_VARIABLES = {k:v}) +def importProcessEnv(env): + env.Append( ENV = dict(( (k,v) + for pattern in env.get('IMPORT_ENV',[]) + for k,v in os.environ.iteritems() + if fnmatch.fnmatchcase(k,pattern) )) ) + + +###########################################################################$ +# SENF log option parsing + +def parseLogOption(value): + stream, area, level = ( x.strip() for x in value.strip().split('|') ) + stream = ''.join('(%s)' % x for x in stream.split('::') ) + if area : area = ''.join( '(%s)' % x for x in area.split('::') ) + else : area = '(_)' + return '((%s,%s,%s))' % (stream,area,level) + +def expandLogOption(target, source, env, for_signature): + if env.subst('$LOGLEVELS'): + return [ 'SENF_LOG_CONF="' + ''.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )+'"'] + else: + return [] + +########################################################################### +# client SENF detection/configuration + def detect_senf(env,senf_path, try_flavors): """Detect senf with flavor in 'try_flavors' somewhere in 'senf_path'. @@ -156,7 +104,6 @@ The environment 'env' is updated in the following way: return True return False - def SetupForSENF(env, senf_path = [], flavor=None): try_flavors = [ '', 'g' ] if flavor is not None: @@ -177,9 +124,14 @@ def SetupForSENF(env, senf_path = [], flavor=None): CPPPATH = [ '$SENFINCDIR' ], LIBPATH = [ '$SENFDIR' ], ) + + conf = env.Configure(clean=False, help=False) + if not conf.CheckBoostVersion(): + conf.Fail("Boost includes not found") + conf.CheckBoostVariants() + conf.Finish() env.Replace( - _defines = expandDefines, expandLogOption = expandLogOption, ) env.SetDefault( @@ -198,34 +150,34 @@ def SetupForSENF(env, senf_path = [], flavor=None): CPPPATH = [ '${NEED_BOOST_EXT and "$SENFINCDIR/boost_ext" or None}' ], CPPDEFINES = [ '$expandLogOption' ], CXXFLAGS = [ '-Wno-long-long', '-fno-strict-aliasing' ], - LIBS = [ 'senf$LIBADDSUFFIX', 'rt', - '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', '$BOOSTFSLIB' ], LINKFLAGS = [ '-rdynamic' ], + LIBS = [ 'senf$LIBADDSUFFIX', 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', + '$BOOSTSIGNALSLIB', '$BOOSTFSLIB' ], ) try: - env.MergeFlags(file(env.File('$BUNDLEDIR/senf${LIBADDSUFFIX}.conf').abspath).read()) + path = env.File('$BUNDLEDIR/senf${LIBADDSUFFIX}.conf').abspath + env.MergeFlags(file(path).read()) except IOError: # Really should never happen since detect_senf looks for this file ... pass +########################################################################### +# Helpers + def DefaultOptions(env): env.Replace( - _defines = expandDefines, expandLogOption = expandLogOption, + CXXFLAGS_ = env.BuildTypeOptions('CXXFLAGS'), + CPPDEFINES_ = env.BuildTypeOptions('CPPDEFINES'), + LINKFLAGS_ = env.BuildTypeOptions('LINKFLAGS'), + LOGLEVELS_ = env.BuildTypeOptions('LOGLEVELS'), ) env.Append( CXXFLAGS = [ '$CXXFLAGS_' ], - CXXFLAGS_ = BuildTypeOptions('CXXFLAGS'), - CPPDEFINES = [ '$CPPDEFINES_' ], - CPPDEFINES_ = BuildTypeOptions('CPPDEFINES'), - LINKFLAGS = [ '$LINKFLAGS_' ], - LINKFLAGS_ = BuildTypeOptions('LINKFLAGS'), - LOGLEVELS = [ '$LOGLEVELS_' ], - LOGLEVELS_ = BuildTypeOptions('LOGLEVELS'), ) env.SetDefault( CXXFLAGS_final = [], @@ -280,88 +232,6 @@ def Glob(env, exclude=[], subdirs=[]): testSources.sort() return (sources, testSources) - -@senfconf.Test -def CheckSTLCopyN(context): - context.Message("Checking for 'copy_n' implementation... ") - versions = [ ('', 'std::copy_n', 'STD'), - ('', '__gnu_cxx::copy_n', 'GNUCXX') ] - for include, name, define in versions: - ret = context.TryCompile("#include %s\n" - "int main(int,char**) { int *a,*b; %s(a,0,b); }\n" - % (include, name), - ".cc") - if ret: - context.Result(name) - context.sconf.Define("HAVE_%s_COPYN" % define, - 1, - "Define one of " - + ", ".join(("HAVE_%s_COPYN" % elt[2] for elt in versions))) - return ret - - context.Result(False) - return False - - -@senfconf.Test -def CheckTempBufferStrategy(context): - context.Message("Checking for optimal temporary buffer strategy... ") - - def check(): - # locals - ret = context.TryCompile("void test(int n){int a[n];}",".cc") - if ret: return "locals" - - # alloca - ret = context.TryCompile("#include \n" - "void test(int a){void *b(alloca(a));}" - ".cc") - if ret: return "alloca" - - # fallback: new - return "new" - - ret = check() - context.Result(ret) - context.sconf.Define("SENF_BUFFER_USE_%s" % ret.upper(), - 1, - "Define one of SENF_BUFFER_USE_LOCALS, SENF_BUFFER_USE_ALLOCA, " - "SENF_BUFFER_USE_NEW") - return ret - - -def Fail(msg): - SCons.Util.display("scons: *** %s" % msg) - Exit(1) - - -def Configure(env, customChecks=None): - conf = env.Configure(clean=False, - help=False, - custom_tests=senfconf.Tests(), - config_h="#/senf/autoconf.hh") - - # Boost - if not conf.CheckBoostVersion(): - Fail("Boost includes not found") - if not conf.env['ARGUMENT_VARIABLES'].has_key('BOOST_VARIANT'): conf.CheckBoostVariants( '', 'mt' ) - conf.env.Replace(NEED_BOOST_EXT = not conf.CheckCXXHeader("boost/bimap.hpp")) - conf.CheckCXXHeader("boost/spirit/include/classic.hpp") - - # Compiler support - conf.CheckTempBufferStrategy() - - # Standard library stuff - if not conf.CheckSTLCopyN(): - Fail("No 'copy_n' implementation found") - conf.CheckFunc("timerfd_create") - - # User checks - if customChecks: - customChecks(conf) - - conf.Finish() - tagfiles = None def Doxygen(env, doxyheader=None, doxyfooter=None, doxycss=None, mydoxyfile=False, senfdoc_path=[], diff --git a/site_scons/site_tools/Boost.py b/site_scons/site_tools/Boost.py index bc6e0e6..2f12041 100644 --- a/site_scons/site_tools/Boost.py +++ b/site_scons/site_tools/Boost.py @@ -6,6 +6,7 @@ import os import sys import tempfile import SCons.Scanner.C +import CustomTests # ARGH ... Why do they put a '+' in the module name ???????? SCons.Tool.cplusplus=getattr(__import__('SCons.Tool.c++', globals(), locals(), []).Tool, 'c++') @@ -117,9 +118,57 @@ def NopAction(env, target, source): def nopstr(target, source, env) : return '' return env.Action(nop, nopstr) +ConfTest = CustomTests.ConfTest() + +@ConfTest +def CheckBoostVersion(context): + context.Message( "Checking boost version... " ) + ret = context.TryRun("#include \n" + "#include \n" + "int main(int, char **) { std::cout << BOOST_LIB_VERSION << std::endl; }", + ".cc")[-1].strip() + if not ret: + context.Result("no boost includes found") + context.env.Replace( BOOST_VERSION = '' ) + return None + else: + context.Result(ret) + context.env.Replace( BOOST_VERSION = ret ) + return ret + +@ConfTest +def CheckBoostVariants(context, *variants): + if not variants : variants = ('','mt') + useVariant = None + if context.env['BOOST_VARIANT'] is not None: + useVariant = context.env['BOOST_VARIANT'] + try: + _env = context.env.Clone() + for variant in variants: + if variant : variantStr = "'%s'" % variant + else : variantStr = "default" + context.Message( "Checking boost %s variant... " % variantStr ) + context.env.Replace( BOOST_VARIANT=variant ) + context.env.Append( _LIBFLAGS = ' -Wl,-Bstatic -l$BOOSTTESTLIB -Wl,-Bdynamic' ) + ret = context.TryLink("#define BOOST_AUTO_TEST_MAIN\n" + "#include \n" + "#include \n" + "BOOST_AUTO_TEST_CASE(test) { BOOST_CHECK(true); }\n", + ".cc") + context.Result( ret ) + if ret and useVariant is None: + useVariant = variant + finally: + context.env.Replace(**_env.Dictionary()) + if useVariant is not None and not context.env.GetOption('no_progress'): + print "Using %s boost variant." % ( + useVariant and "'%s'" % useVariant or "default") + context.env.Replace( BOOST_VARIANT = useVariant ) + return useVariant + def generate(env): env.SetDefault( - BOOST_VARIANT = '', + BOOST_VARIANT = None, _BOOST_VARIANT = '${BOOST_VARIANT and "-" or None}$BOOST_VARIANT', BOOSTTESTLIB = 'boost_unit_test_framework$_BOOST_VARIANT', @@ -129,7 +178,10 @@ def generate(env): BOOSTSIGNALSLIB = 'boost_signals$_BOOST_VARIANT', BOOSTTESTARGS = [ '--build_info=yes', '--log_level=test_suite' ], - ) + ) + env.Append( + CUSTOM_TESTS = ConfTest.tests, + ) env['BUILDERS']['BoostUnitTest'] = BoostUnitTest env['BUILDERS']['FindAllBoostUnitTests'] = FindAllBoostUnitTests diff --git a/site_scons/site_tools/BuildTypeOptions.py b/site_scons/site_tools/BuildTypeOptions.py new file mode 100644 index 0000000..75b60ac --- /dev/null +++ b/site_scons/site_tools/BuildTypeOptions.py @@ -0,0 +1,78 @@ +import SCons.Util, SCons.Defaults +import re + +class BuildTypeOptions: + def __init__(self, var): + self._var = var + + def __call__(self, target, source, env, for_signature): + type = env['final'] and "final" or env['debug'] and "debug" or "normal" + return env[self._var + "_" + type] + +_DOLLAR_RE = re.compile(r'\$([a-zA-Z_][\.\w]*)|\${([^}]*)}') + +def _expandDefines(defs, target, source, env, for_signature): + rv = [] + if SCons.Util.is_Dict(defs): + keys = defs.keys() + keys.sort() + defs = [ (k,defs[k]) for k in keys ] + elif not SCons.Util.is_List(defs): + defs = [ defs ] + for elt in defs: + if SCons.Util.is_String(elt): + m = _DOLLAR_RE.match(elt) + if m: + match = m.group(1) or m.group(2) + try: rv.extend(_expandDefines(eval(match, env.gvars(), env.lvars()), + target, source, env, for_signature)) + except NameError: pass + except IndexError: pass + else: + rv.append(env.subst(elt)) + elif callable(elt): + rv.extend(_expandDefines(elt(target, source, env, for_signature), + target, source, env, for_signature)) + elif SCons.Util.is_Sequence(elt): + if len(elt)<2 or elt[1] is None: + rv.append(env.subst(elt[0])) + else: + rv.append(env.subst(elt[0]) + "=" + env.subst(elt[1])) + else: + rv.append(str(elt)) + return rv + +def expandDefines(prefix, defs, suffix, env): + """Expand defines in using . Calls SCons.Defaults._concat_ixes +to append prefix/suffix before/after each define. + + callable + Call the callable and replace it with the call result. If the result + is a list, the list is processed recursively. It the result is a + dictionary it is converted into a list of typles and processed + recursively. + '$' or '${}' + Replace the element with the variable expansion. If the result is a + list, the list is processed recursively. It the result is a + dictionary it is converted into a list of typles and processed + recursively. + '' + Define a symbol with that (expanded!) name + iterable + The iteratble must have two elments. The first element defines the + symbol name, the second the symbol value.""" + + defs = _expandDefines(defs, None, None, env, False) + return SCons.Defaults._concat_ixes(prefix, defs, suffix, env) + +def _BuildTypeOptions(env, var) : return BuildTypeOptions(var) + +def generate(env): + env.Replace( + _defines = expandDefines, + ) + + env.AddMethod(_BuildTypeOptions, 'BuildTypeOptions') + +def exists(env): + return True diff --git a/site_scons/site_tools/CustomTests.py b/site_scons/site_tools/CustomTests.py new file mode 100644 index 0000000..81a8eba --- /dev/null +++ b/site_scons/site_tools/CustomTests.py @@ -0,0 +1,122 @@ +import SCons.Environment +import SCons.Util, SCons.Script + +# Fix for SCons 0.97 compatibility +import SCons.SConf +try: SCons.SConf.SConfBase.Define +except AttributeError: + import string + def Define(self, name, value = None, comment = None): + lines = [] + if comment: + comment_str = "/* %s */" % comment + lines.append(comment_str) + if value is not None: + define_str = "#define %s %s" % (name, value) + else: + define_str = "#define %s" % name + lines.append(define_str) + lines.append('') + self.config_h_text = self.config_h_text + string.join(lines, '\n') + SCons.SConf.SConfBase.Define = Define + +class ConfTest(object): + """Configuration test decorator. + +This class is used as a decorator to define SCons Configure +tests. There are three ways to use this facility: + + +DIRECT WITH EXTERNAL REGISTRY + +In this scenario, you pass the test registry, a map, as argument to +the decorator. This usage is primarily interesting for tool writers: + + from CustomTests import ConfTest + + MY_TESTS = {} + + @ConfTest(MY_TESTS) + def CheckMyFoo(context): + pass + + def generate(env): + env.Append(CUSTOM_TESTS = MY_TESTS) + +This will place CheckMyFoo into the MY_TESTS dictionary which you can +later add to the CUSTOM_TESTS environment variable. + + +USING A REGISTRY ALIAS + +In this scenario, you define a local alias for the decorator which +serves as the registry. This usage is again primarily interesting for +tool and extension writers: + + import CustomTests + + MyConfTest = CustomTests.ConfTest() + + @MyConfTest + def CheckMyFoo(context): + pass + + def generate(env): + env.Append(CUSTOM_TESTS = MyConfTest.tests) + +Logically this is like the previous method. + + +USING THE ENVIRONMENT MEMBER + +This usage is interesting for SConstruct and SConscript writers: + + env = Environment() + env.Tool('CustomTests') + + @env.ConfTest() + def CheckMyFoo(context): + pass + + conf = env.Configure() + conf.CheckMyFoo() + env = conf.Finish() + +The new configuration test is automatically added to +env['CUSTOM_TESTS'] and is thus automatically available to all +configuration contexts created from the environment. +""" + + def __init__(self, registry=None): + if registry is None: + self.tests = {} + elif isinstance(registry, SCons.Environment.SubstitutionEnvironment): + self.tests =registry['CUSTOM_TESTS'] + else: + self.tests = registry + + def __call__(self, func): + self.tests[func.__name__] = func + return func + +DefaultTest = ConfTest() + +def Configure(self, *args, **kw): + try: kw['custom_tests'].update(self['CUSTOM_TESTS']) + except KeyError: kw['custom_tests'] = dict(self['CUSTOM_TESTS']) + return self._CustomTests_orig_Configure(*args, **kw) + +@DefaultTest +def Fail(context, msg): + SCons.Util.display("scons: *** %s" % msg) + SCons.Script.Exit(1) + +def generate(env): + env.Append( CUSTOM_TESTS = DefaultTest.tests ) + env._CustomTests_orig_Configure = env.Configure + env.AddMethod(Configure) + env.AddMethod(ConfTest) + env.AddMethod(Fail) + +def exists(env): + return True