From: g0dil Date: Thu, 8 Oct 2009 19:04:47 +0000 (+0000) Subject: Add sparse_tests and only_tests build feature X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=e87f3008b5ad1c1c3f2ed6b1e49419eb0219eddb;p=senf.git Add sparse_tests and only_tests build feature git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1482 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/SConstruct b/SConstruct index d8c0510..97ef138 100644 --- a/SConstruct +++ b/SConstruct @@ -49,8 +49,9 @@ env.Append( CPPPATH = [ '$BUILDDIR', '#' ], LOCALLIBDIR = '$BUILDDIR', LIBPATH = [ '$LOCALLIBDIR' ], - LIBS = [ '$LIBSENF$LIBADDSUFFIX', 'rt', '$BOOSTREGEXLIB', - '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', '$BOOSTFSLIB' ], + LIBS = [ '$LIBSENF$LIBADDSUFFIX', '$EXTRA_LIBS' ], + EXTRA_LIBS = [ 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', + '$BOOSTFSLIB' ], TEST_EXTRA_LIBS = [ ], VALGRINDARGS = [ '--num-callers=50' ], @@ -115,8 +116,10 @@ senfutil.parseArguments( BoolVariable('final', 'Build final (optimized) build', False), BoolVariable('debug', 'Link in debug symbols', False), BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False), + BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False) ) +if env.has_key('only_tests') : env['sparse_tests'] = True Export('env') # Create Doxyfile.local otherwise doxygen will barf on this non-existent file @@ -134,6 +137,9 @@ SConscriptChdir(0) SConscript("debian/SConscript") SConscriptChdir(1) if os.path.exists('SConscript.local') : SConscript('SConscript.local') +if env['sparse_tests']: + import SparseTestHack + SparseTestHack.setup(env) if env.subst('$BUILDDIR') == '#': SConscript("SConscript") else: @@ -141,6 +147,8 @@ else: SConscript("Examples/SConscript") SConscript("HowTos/SConscript") SConscript("doclib/SConscript") +if env['sparse_tests']: + SparseTestHack.build(env) ########################################################################### # Define build targets diff --git a/site_scons/SENFSCons.py b/site_scons/SENFSCons.py index f4436b2..13f515e 100644 --- a/site_scons/SENFSCons.py +++ b/site_scons/SENFSCons.py @@ -99,9 +99,11 @@ def AllIncludesHH(env, exclude=[]): if f.name not in exclude and not f.name.endswith('.test.hh') ] headers.sort(key=lambda x:x.name) target = env.File("all_includes.hh") - env.Default(env.CreateFile(target, - env.Value("".join([ '#include <%s>\n' % f.srcnode().get_path(env.Dir('#')) - for f in headers ])))) + allinch = env.CreateFile(target, + env.Value("".join([ '#include <%s>\n' % f.srcnode().get_path(env.Dir('#')) + for f in headers ]))) + env.Default(allinch) + env.Depends(allinch, headers) INDEXPAGE=""" /** \mainpage ${TITLE} @@ -132,7 +134,6 @@ def IndexPage(env, name, title, text=""): env.Clean('all',name) env.Clean('all_docs',name) - ########################################################################### # The following functions serve as simple macros for most SConscript files # diff --git a/site_scons/SparseTestHack.py b/site_scons/SparseTestHack.py new file mode 100644 index 0000000..8ae888d --- /dev/null +++ b/site_scons/SparseTestHack.py @@ -0,0 +1,119 @@ +import SCons.Node, SCons.Node.FS, SCons.Util + +##################################################################### +# This IS a hack .. but a very useful one: The hack will remove the +# 'senf' library from the unit-tests and instead add all the object +# files needed explicitly. +# +# This works by building a list of all children (recursively) of the +# test sources. For each child we check, wether a file with the same +# name but an $OBJSUFFIX extension exists as a build target. In that +# case, we add it to the list of needed objects (AND recursively scan +# that objects children). +# +# Additionally, of the variable 'only_tests' is set, it contains a +# list of test source files to be built exclusively: All other test +# sources will NOT be compiled (and their children will therefore not +# be scanned). +# +# There is one restriction: CompileCheck tests are not run when this +# hack is enabled. +##################################################################### + +# Build a list of all object file targets which are needed by 'nodes' +def neededObjects(env,nodes): + if not SCons.Util.is_List(nodes) : nodes = [nodes] + + def walk(nodes): + for node in nodes: + walker = SCons.Node.Walker(node) + while True: + n = walker.next() + if n is None : break + yield n + + seen = {} + rv = {} + + def check(nodes, env=env, seen=seen, rv=rv): + for c in walk(nodes): + if isinstance(c,SCons.Node.FS.File) and not seen.get(c): + seen[c] = True + ob = c.File(env.subst("${SOURCE.dir.abspath}/${OBJPREFIX}${SOURCE.filebase}${OBJSUFFIX}", source=c)) + if ob.is_derived(): + rv[ob] = True + if not seen.get(ob): + seen[ob] = True + check([ob]) + + check(nodes) + + for node in nodes: + rv[node] = True + rv = [ (str(x),x) for x in rv.iterkeys() ] + rv.sort() + return (x[1] for x in rv) + +# Return a map witch has all sources (recursively) of 'nodes' as keys +def recursiveSourcesMap(nodes): + rv = {} + + def check(node, rv=rv): + for src in node.sources: + if not rv.get(src): + rv[src] = True + check(src) + + for node in nodes: + check(node) + + return rv + +# Replacement BoostUnitTest builder. This build just defers the real +# builder call until later +def AutoObjectBoostUnitTest(env, target, tests, **kw): + target = env.arg2nodes(target)[0] + tests = env.arg2nodes(tests) + env.Append(_UNIT_TEST_LIST = (target, tests, kw)) + +def setup(env): + env['BUILDERS']['RealBoostUnitTest'] = env['BUILDERS']['BoostUnitTest'] + env['BUILDERS']['BoostUnitTest'] = AutoObjectBoostUnitTest + env['_UNIT_TEST_LIST'] = [] + +# This needs to be called after all build targets have been set +# up. This is important since the list of object targets needs to be +# complete. +def build(env): + env = env.Clone(LIBS = [ '$EXTRA_LIBS' ]) + if env.has_key("only_tests"): + only_tests = [ env.Dir(env.GetLaunchDir()).File(test) + for test in env.Split(env['only_tests']) ] + else: + only_tests = [] + for target, tests, kw in env['_UNIT_TEST_LIST']: + objects = [] + build = False + for test in tests: + if test.suffix == env['OBJSUFFIX']: + objects.append(test) + else: + if not only_tests or test in only_tests: + objects.extend(env.Object(test)) + build = True + elif test.name == 'main.test.cc': + objects.extend(env.Object(test)) + if not build : continue + + # First step: Get all objects which are needed by any of 'objects' + objects = list(neededObjects(env,objects)) + # Get all the source files needed by those objects + sources = recursiveSourcesMap(objects) + # Now remove any objects which are also sources: When an + # object is a source for another object this means, the target + # object has been built by CombinedObject. Since the target + # object includes all it's source objects, we need to remove + # all those source objects from the object list (if any). + objects = [ ob for ob in objects if not sources.get(ob) ] + + env.RealBoostUnitTest(target, objects, **kw)