3 from __future__ import with_statement
5 import sys, os.path, fnmatch
6 import SENFSCons, senfutil, senfconf
9 ###########################################################################
10 # Load utilities and setup libraries and configure build
14 env.Decider('MD5-timestamp')
15 env.EnsureSConsVersion(1,2)
17 # Load all the local SCons tools
18 senfutil.loadTools(env)
21 Additional top-level build targets:
23 prepare Create all target files not part of the repository
24 default Build all default targets (like calling scons with no arguments)
25 examples Build all examples
26 all_tests Build and run unit tests for all modules
27 test_changes Build tests only for files with local changes (queries svn or git)
28 all_docs Build documentation for all modules
30 install_all Install SENF into $$PREFIX
31 deb Build debian source and binary package
32 debsrc Build debian source package
33 debbin Build debian binary package
34 linklint Check links of doxygen documentation with 'linklint'
35 fixlinks Fix broken links in doxygen documentation
36 all_valgrinds Run all tests under valgrind/memcheck
37 lcov Generate test coverage output in doc/lcov and lcov.info
39 You may execute targets on a remote host (if the directory layout is the same)
42 scons <target>@[<user>@]<host>
44 Some more elaborate unit tests may be enabled by setting appropritate variables
45 in the shell (unix) environment
47 SENF_TIMING_CRITICAL_TESTS
48 Enables unit tests which depend on timing measurements. These
49 unit tests should only be run on a single core and an otherwise
52 SENF_WLAN_TEST_INTERFACE
53 WLAN interface to use for testing. The interface should not be
56 SENF_ETH_TEST_INTERFACE
57 Ethernet interface to use for testing. The interface should not
60 Some unit tests will only run when executed to 'root'.
64 ENV = { 'PATH' : os.environ.get('PATH'),
65 'HOME' : os.environ.get('HOME'),
66 'SSH_AGENT_PID': os.environ.get('SSH_AGENT_PID'),
67 'SSH_AUTH_SOCK': os.environ.get('SSH_AUTH_SOCK') },
68 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*' ],
70 BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
71 CPPPATH = [ '#', '$BUILDDIR',
72 '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
73 LOCALLIBDIR = '$BUILDDIR',
74 LIBPATH = [ '$LOCALLIBDIR' ],
75 LIBS = [ '$EXTRA_LIBS' ],
76 EXTRA_LIBS = [ 'rt' ],
77 TEST_EXTRA_LIBS = [ ],
78 VALGRINDARGS = [ '--num-callers=50' ],
81 LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}',
82 BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}',
83 INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}',
84 CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
85 OBJINSTALLDIR = '$CONFINSTALLDIR',
86 DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
87 SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons',
89 CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
90 CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
92 # These options are insane. Only useful for inline debugging. Need at least 1G free RAM
93 INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden',
94 '-fno-inline-functions', '-Winline'
95 '--param','large-function-growth=10000',
96 '--param', 'large-function-insns=10000',
97 '--param','inline-unit-growth=10000' ],
98 INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ],
99 INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ],
100 CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
102 CXXFLAGS_ = senfutil.BuildTypeOptions('CXXFLAGS'),
103 CXXFLAGS_final = [ '-O3' ],
104 CXXFLAGS_normal = [ '-O0', '-g' ],
105 CXXFLAGS_debug = [ '$CXXFLAGS_normal' ],
107 CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ],
108 expandLogOption = senfutil.expandLogOption,
109 CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'),
110 CPPDEFINES_final = [ ],
111 CPPDEFINES_normal = [ 'SENF_DEBUG' ],
112 CPPDEFINES_debug = [ '$CPPDEFINES_normal' ],
114 LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ],
115 LINKFLAGS_ = senfutil.BuildTypeOptions('LINKFLAGS'),
116 LINKFLAGS_final = [ ],
117 LINKFLAGS_normal = [ '-Wl,-S' ],
118 LINKFLAGS_debug = [ '-g' ],
121 # Add all UNIX env vars starting with 'SENF' to the execution environment
122 env.Append( ENV = dict(((k,v) for k,v in os.environ.iteritems() if k.startswith("SENF"))) )
128 VALGRIND = "valgrind",
129 SCONSBIN = env.File("#/tools/scons"),
130 SCONSARGS = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
131 [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
132 SCONS = "@$SCONSBIN $SCONSARGS",
133 CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
134 TOPDIR = env.Dir('#').abspath,
135 LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}',
136 OBJADDSUFFIX = '${LIBADDSUFFIX}',
140 # Set variables from command line
141 senfutil.parseArguments(
143 BoolVariable('final', 'Build final (optimized) build', False),
144 BoolVariable('debug', 'Link in debug symbols', False),
145 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
146 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
149 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
150 import SparseTestHack
151 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
153 if env.has_key('only_tests') : env['sparse_tests'] = True
156 ###########################################################################
159 # Default configuration (boost stuff)
160 senfutil.Configure(env)
163 def CheckValgrind(context):
164 context.Message( "Checking for valgrind... " )
165 ret = context.TryAction(['valgrind --version >$TARGET'])
166 context.Result( ret[1].strip() or False )
170 def CheckValgrindWildcards(context):
171 context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
172 ret = context.TryAction(['valgrind --suppressions=$SOURCE /bin/true'],
173 "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
175 context.Result( ret[0] )
178 conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
180 HAVE_VALGRIND = conf.CheckValgrind() and conf.CheckValgrindWildcards()
184 # Only add this here, after all configure checks have run
186 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
187 EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
190 ###########################################################################
192 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
193 # Create it even when cleaning, to silence the doxygen builder warnings
194 if not os.path.exists("doclib/Doxyfile.local"):
195 Execute(Touch("doclib/Doxyfile.local"))
197 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
198 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
199 env.Execute([ "$SCONS prepare" ])
204 SConscript("debian/SConscript")
206 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
207 if env['sparse_tests']:
208 import SparseTestHack
209 SparseTestHack.setup(env)
210 if env.subst('$BUILDDIR') == '#':
211 SConscript("SConscript")
213 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
214 SConscript("Examples/SConscript")
215 SConscript("HowTos/SConscript")
216 SConscript("doclib/SConscript")
217 if env['sparse_tests']:
218 verbose = 'test_changes' in COMMAND_LINE_TARGETS
219 SparseTestHack.build(env, verbose, verbose)
221 ###########################################################################
222 # Define build targets
224 #### install_all, default, all_tests, all
225 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
226 'site_scons/senfutil.py',
227 'site_scons/yaptu.py' ])
228 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
229 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
230 env.Install('${INCLUDEINSTALLDIR}', 'boost')
232 env.Alias('install_all', env.FindInstalledFiles())
233 env.Alias('default', DEFAULT_TARGETS)
234 env.Alias('all_tests', env.FindAllBoostUnitTests())
235 env.Alias('test_changes', 'all_tests')
236 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
239 env.PhonyTarget('prepare', [], [])
242 env.Alias('all_valgrinds')
243 if env['HAVE_VALGRIND']:
244 for test in env.FindAllBoostUnitTests():
245 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
246 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
247 [ """$VALGRIND --tool=memcheck
249 --suppressions=${SOURCES[1]}
251 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
252 """.replace("\n"," "),
254 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
255 env.Alias('all_valgrinds', alias)
258 env.PhonyTarget('lcov', [], [
259 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
260 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
261 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
262 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
263 'rm /tmp/senf_lcov.info' ])
264 if env.GetOption('clean'):
265 env.Clean('lcov', [ os.path.join(path,f)
266 for path, subdirs, files in os.walk('.')
267 for pattern in ('*.gcno', '*.gcda', '*.gcov')
268 for f in fnmatch.filter(files,pattern) ] +
269 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
272 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
273 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
275 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
276 env.Clean('all', [ os.path.join(path,f)
277 for path, subdirs, files in os.walk('.')
278 for pattern in env['CLEAN_PATTERNS']
279 for f in fnmatch.filter(files,pattern) ])
280 # Disable writing to the deleted .sconsign file
281 import SCons.SConsign
282 SCons.SConsign.write = lambda : None
284 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
285 Execute(Touch(".prepare-stamp"))
287 ### execute targets on remote hosts
288 for target in COMMAND_LINE_TARGETS:
290 realtarget, host = target.split('@',1)
291 cwd=env.GetLaunchDir()
292 home=os.environ['HOME']+'/'
293 if cwd.startswith(home) : cwd = cwd[len(home):]
294 args = [ '$SCONSARGS' ]
295 if env.GetLaunchDir() != os.getcwd():
297 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
298 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)