2 # \brief SENFSCons package
4 ## \package senfscons.SENFSCons
5 # \brief Build helpers and utilities
7 # The SENFSCons package contains a number of build helpers and
8 # utilities which are used to simplify commmon tasks.
10 # The utitlities of this package are grouped into:
11 # <dl><dt>\ref use</dt><dd>help using complex environments and
12 # configure the construction environmen correspondingly</dd>
14 # <dt>\ref target</dt><dd>simplify building common targest and include
15 # enhanced functionality like unit-testing.</dd></dl>
17 # Additionally for external use are
18 # <dl><dt>MakeEnvironment()</dt><dd>Build construction
21 # <dt>GlobSources()</dt><dd>Utility to find source files</dd></dl>
23 # All other functions are for internal use only.
26 import SCons.Options, SCons.Environment, SCons.Script.SConscript, SCons.Node.FS
27 import SCons.Defaults, SCons.Action
28 from SCons.Script import *
30 ## \defgroup use Predefined Framework Configurators
32 # The following framework configurators are used in the top level \c
33 # SConstruct file to simplify more complex configurations.
35 # Each of the framework configurators introduces additional
36 # configuration parameters to \ref sconfig
38 ## \defgroup target Target Helpers
40 # To specify standard targets, the following helpers can be used. They
41 # automatically integrate several modules (like documentation,
44 ## \defgroup builder Builders
46 # The SENFSCons framework includes a series of builders. Each builder
47 # is defined in it's own package.
49 ## \brief Find normal and test C++ sources
51 # GlobSources() will return a list of all C++ source files (named
52 # "*.cc") as well as a list of all unit-test files (named "*.test.cc")
53 # in the current directory. The sources will be returned as a tuple of
54 # sources, test-sources. The target helpers all accept such a tuple as
55 # their source argument.
56 def GlobSources(env, exclude=[], subdirs=[]):
57 testSources = glob.glob("*.test.cc")
58 sources = [ x for x in glob.glob("*.cc") if x not in testSources and x not in exclude ]
59 for subdir in subdirs:
60 testSources += glob.glob(os.path.join(subdir,"*.test.cc"))
61 sources += [ x for x in glob.glob(os.path.join(subdir,"*.cc"))
62 if x not in testSources and x not in exclude ]
63 return (sources, testSources)
65 def GlobIncludes(env, exclude=[], subdirs=[]):
67 for d in [ '.' ] + subdirs:
68 for f in os.listdir(d):
69 ext = '.' + f.split('.',1)[-1]
71 if ext in env['CPP_INCLUDE_EXTENSIONS'] \
72 and ext not in env['CPP_EXCLUDE_EXTENSIONS'] \
77 def Glob(env, exclude=[], subdirs=[]):
78 return ( GlobSources(env, exclude, subdirs),
79 GlobIncludes(env, exclude, subdirs) )
81 ## \brief Add generic standard targets for every module
83 # This target helper should be called in the top-level \c SConstruct file
84 # as well as in every module \c SConscipt file. It adds general
85 # targets. Right now, these are
86 # \li clean up \c .sconsign, \c .sconf_temp and \c config.log on
87 # <tt>scons -c all</tt>
90 def StandardTargets(env):
91 env.Clean(env.Alias('all'), [ '.sconsign', '.sconf_temp', 'config.log' ])
93 ## \brief Add generic global targets
95 # This target helper should be called in the top-level \c SConstruct
96 # file. It adds general global targets. Right now theese are
97 # \li Make <tt>scons all</tt> build all targets.
100 def GlobalTargets(env):
101 env.Alias('all', [ 'default', 'all_tests', 'all_docs' ])
103 ## \brief Return path of a built library within $LOCALLIBDIR
105 def LibPath(lib): return '${LOCALLIBDIR}/${LIBPREFIX}%s${LIBADDSUFFIX}${LIBSUFFIX}' % lib
107 ## \brief Add explicit test
109 # This target helper will add an explicit test. This is like a unit test but is
110 # built directly against the completed library
113 def Test(env, sources, LIBS = [], OBJECTS = []):
114 test = [ env.BoostUnitTests(
117 test_sources = sources,
118 LIBS = [ '$LIBSENF$LIBADDSUFFIX' ],
120 DEPENDS = [ env.File(LibPath(env['LIBSENF'])) ]) ]
121 compileTestSources = [ src for src in sources
122 if 'COMPILE_CHECK' in file(src).read() ]
123 if compileTestSources:
124 test.extend(env.CompileCheck(source = compileTestSources))
125 env.Alias('all_tests', test)
126 env.Command(env.File('test'), test, [ 'true' ])
127 #env.Alias(env.File('test'), test)
130 ## \brief Build object files
132 # This target helper will build object files from the given
135 # If \a testSources are given, a unit test will be built using the <a
136 # href="http://www.boost.org/libs/test/doc/index.html">Boost.Test</a>
137 # library. \a LIBS may specify any additional library modules <em>from
138 # the same project</em> on which the test depends. Those libraries
139 # will be linked into the final test executable. The test will
140 # automatically be run if the \c test or \c all_tests targets are
143 # If \a sources is a 2-tuple as returned by GlobSources(), it will
144 # provide both \a sources and \a testSources.
147 def Objects(env, sources, testSources = None, OBJECTS = []):
148 if type(sources) == type(()):
149 testSources = sources[1]
151 if type(sources) is not type([]):
152 sources = [ sources ]
157 for source in sources
158 if type(source) is type('') and not source.endswith('.o') ]
160 for source in sources
161 if type(source) is not type('') or source.endswith('.o') ]
163 objects += env.Object(obsources)
166 test = [ env.BoostUnitTests(
169 test_sources = testSources,
170 LIBS = [ '$LIBSENF$LIBADDSUFFIX' ],
172 DEPENDS = [ env.File(LibPath(env['LIBSENF'])) ]) ]
173 compileTestSources = [ src for src in testSources
174 if 'COMPILE_CHECK' in file(src).read() ]
175 if compileTestSources:
176 test.extend(env.CompileCheck(source = compileTestSources))
177 env.Alias('all_tests', test)
178 # Hmm ... here I'd like to use an Alias instead of a file
179 # however the alias does not seem to live in the subdirectory
180 # which breaks 'scons -u test'
181 env.Command(env.File('test'), test, [ 'true' ])
182 #env.Alias(env.File('test'), test)
186 def InstallIncludeFiles(env, files):
187 # Hrmpf ... why do I need this in 0.97??
188 if env.GetOption('clean'):
190 target = env.Dir(env['INCLUDEINSTALLDIR'])
194 env.Alias('install_all', env.Install(target.Dir(src.dir.get_path(base)), src))
196 ## \brief Build documentation with doxygen
199 def Doxygen(env, doxyfile = "Doxyfile", extra_sources = []):
200 # There is one small problem we need to solve with this builder: The Doxygen builder reads
201 # the Doxyfile and thus depends on the environment variables set by doclib/doxygen.sh. We
202 # thus have to provide all necessary definitions here manually via DOXYENV !
204 if type(doxyfile) is type(""):
205 doxyfile = env.File(doxyfile)
207 # Module name is derived from the doxyfile path
208 # Utils/Console/Doxyfile -> Utils_Console
209 module = doxyfile.dir.abspath[len(env.Dir('#').abspath)+1:].replace('/','_')
210 if not module : module = "Main"
212 # Rule to generate tagfile
213 # (need to exclude the 'clean' case, otherwise we'll have duplicate nodes)
214 if not env.GetOption('clean'):
215 tagfile = env.Doxygen(doxyfile,
216 DOXYOPTS = [ '--tagfile-name', '"${MODULE}.tag"',
218 DOXYENV = { 'TOPDIR' : env.Dir('#').abspath,
219 'output_dir' : 'doc',
222 'generate_tagfile': 'doc/${MODULE}.tag' },
224 env.Append(ALL_TAGFILES = tagfile[0].abspath)
225 env.Depends(tagfile, env.File('#/doclib/doxygen.sh'))
227 # Rule to generate HTML documentation
228 doc = env.Doxygen(doxyfile,
229 DOXYOPTS = [ '--tagfiles', '"$ALL_TAGFILES"',
230 '--tagfile-name', '"${MODULE}.tag"',
233 DOXYENV = { 'TOPDIR' : env.Dir('#').abspath,
234 'tagfiles' : '${ALL_TAGFILES}',
235 'output_dir' : 'doc',
238 env.Depends(doc, env.File('#/doclib/doxygen.sh'))
240 # Copy the extra_sources (the images) into the documentation directory
241 # (need to exclude the 'clean' case otherwise there are multiple ways to clean the copies)
242 if not env.GetOption('clean'):
245 [ env.CopyToDir( source=source, target=doc[0].dir )
246 for source in extra_sources ])
248 # Install documentation into DOCINSTALLDIR
249 l = len(env.Dir('#').abspath)
250 env.Alias('install_all',
251 env.Command('$DOCINSTALLDIR' + doc[0].dir.abspath[l:], doc[0].dir,
252 [ SCons.Defaults.Copy('$TARGET','$SOURCE') ]))
255 env.Alias('all_docs', doc)
256 env.Clean('all_docs', doc)
257 env.Clean('all', doc)
261 ## \brief Build library
263 # This target helper will build the given library. The library will be
264 # called lib<i>library</i>.a as is customary on UNIX systems. \a
265 # sources, \a testSources and \a LIBS are directly forwarded to the
266 # Objects build helper.
268 # The library is added to the list of default targets.
271 def Lib(env, sources, testSources = None, OBJECTS = []):
272 objects = Objects(env,sources,testSources,OBJECTS=OBJECTS)
274 env.Append(ALLOBJECTS = objects)
277 ## \brief Build Object from multiple sources
278 def Object(env, target, sources, testSources = None, OBJECTS = []):
279 objects = Objects(env,sources,testSources,OBJECTS=OBJECTS)
282 ob = env.Command(target+"${OBJADDSUFFIX}${OBJSUFFIX}", objects, "ld -r -o $TARGET $SOURCES")
284 env.Alias('default', ob)
285 env.Alias('install_all', env.Install("$OBJINSTALLDIR", ob))
288 ## \brief Build executable
290 # This target helper will build the given binary. The \a sources, \a
291 # testSources and \a LIBS arguments are forwarded to the Objects
292 # builder. The final program will be linked against all the library
293 # modules specified in \a LIBS (those are libraries which are built as
294 # part of the same proejct). To specify non-module libraries, use the
295 # construction environment parameters or the framework helpers.
298 def Binary(env, binary, sources, testSources = None, OBJECTS = []):
299 objects = Objects(env,sources,testSources,OBJECTS=OBJECTS)
302 progEnv = env.Clone()
303 progEnv.Prepend(LIBS = [ '$LIBSENF$LIBADDSUFFIX' ])
304 program = progEnv.ProgramNoScan(target=binary,source=objects+OBJECTS)
306 env.Depends(program, [ env.File(LibPath(env['LIBSENF'])) ])
307 env.Alias('default', program)
308 env.Alias('install_all', env.Install('$BININSTALLDIR', program))
311 def AllIncludesHH(env, headers):
313 target = env.File("all_includes.hh")
314 file(target.abspath,"w").write("".join([ '#include "%s"\n' % f
316 env.Clean('all', target)
318 def PhonyTarget(env, target, action, sources=[]):
319 env.AlwaysBuild(env.Alias(target, sources, env.Action(action)))