3 import sys, os.path, fnmatch
4 import SENFSCons, senfutil, senfconf
7 ###########################################################################
8 # Load utilities and setup libraries and configure build
12 env.Decider('MD5-timestamp')
13 env.EnsureSConsVersion(1,2)
15 # Load all the local SCons tools
16 senfutil.loadTools(env)
19 Additional top-level build targets:
21 prepare Create all target files not part of the repository
22 default Build all default targets (like calling scons with no arguments)
23 examples Build all examples
24 all_tests Build and run unit tests for all modules
25 test_changes Build tests only for files with local changes (queries svn or git)
26 all_docs Build documentation for all modules
28 install_all Install SENF into $$PREFIX
29 deb Build debian source and binary package
30 debsrc Build debian source package
31 debbin Build debian binary package
32 linklint Check links of doxygen documentation with 'linklint'
33 fixlinks Fix broken links in doxygen documentation
34 all_valgrinds Run all tests under valgrind/memcheck
35 lcov Generate test coverage output in doc/lcov and lcov.info
37 You may execute targets on a remote host (if the directory layout is the same)
40 scons <target>@[<user>@]<host>
42 Some more elaborate unit tests may be enabled by setting appropritate variables
43 in the shell (unix) environment
45 SENF_TIMING_CRITICAL_TESTS
46 Enables unit tests which depend on timing measurements. These
47 unit tests should only be run on a single core and an otherwise
50 SENF_WLAN_TEST_INTERFACE
51 WLAN interface to use for testing. The interface should not be
54 SENF_ETH_TEST_INTERFACE
55 Ethernet interface to use for testing. The interface should not
58 Some unit tests will only run when executed to 'root'.
62 IMPORT_ENV = [ 'PATH', 'HOME', 'SSH_*', 'SENF*', 'CCACHE_*', 'DISTCC_*' ],
64 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*',
67 BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
68 CPPPATH = [ '#', '$BUILDDIR',
69 '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
70 LOCALLIBDIR = '$BUILDDIR',
71 LIBPATH = [ '$LOCALLIBDIR' ],
72 LIBS = [ '$EXTRA_LIBS' ],
73 EXTRA_LIBS = [ 'rt' ],
74 TEST_EXTRA_LIBS = [ ],
75 VALGRINDARGS = [ '--num-callers=50' ],
78 LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}',
79 BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}',
80 INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}',
81 CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
82 OBJINSTALLDIR = '$CONFINSTALLDIR',
83 DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
84 SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons',
86 CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
87 CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
89 # These options are insane. Only useful for inline debugging. Need at least 1G free RAM
90 INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden',
91 '-fno-inline-functions', '-Winline'
92 '--param','large-function-growth=10000',
93 '--param', 'large-function-insns=10000',
94 '--param','inline-unit-growth=10000' ],
95 INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ],
96 INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ],
97 CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
98 '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ],
99 CXXFLAGS_ = senfutil.BuildTypeOptions('CXXFLAGS'),
100 CXXFLAGS_final = [ '-O3' ],
101 CXXFLAGS_normal = [ '-O2', '-g' ],
102 CXXFLAGS_debug = [ '-O0', '-g' ],
104 CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ],
105 expandLogOption = senfutil.expandLogOption,
106 CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'),
107 CPPDEFINES_final = [ 'SENF_PPI_NOTRACE'],
108 CPPDEFINES_normal = [ 'SENF_DEBUG' ],
109 CPPDEFINES_debug = [ '$CPPDEFINES_normal' ],
111 LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ],
112 LINKFLAGS_ = senfutil.BuildTypeOptions('LINKFLAGS'),
113 LINKFLAGS_final = [ ],
114 LINKFLAGS_normal = [ '-Wl,-S' ],
115 LINKFLAGS_debug = [ '-g' ],
122 VALGRIND = "valgrind",
123 SCONSBIN = env.File("#/tools/scons"),
124 SCONSARGS = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
125 [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
126 SCONS = "@$SCONSBIN $SCONSARGS",
127 CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
128 TOPDIR = env.Dir('#').abspath,
129 LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}',
130 OBJADDSUFFIX = '${LIBADDSUFFIX}',
135 _defines = senfutil.expandDefines
138 # Set variables from command line
139 senfutil.parseArguments(
141 BoolVariable('final', 'Build final (optimized) build', False),
142 BoolVariable('debug', 'Link in debug symbols', False),
143 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
144 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
147 # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment
148 env.Append( ENV = dict(( (k,v)
149 for pattern in env['IMPORT_ENV']
150 for k,v in os.environ.iteritems()
151 if fnmatch.fnmatchcase(k,pattern) )) )
153 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
154 import SparseTestHack
155 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
157 if env.has_key('only_tests') : env['sparse_tests'] = True
160 ###########################################################################
164 def CheckValgrind(context):
165 context.Message( "Checking for valgrind... " )
166 ret = context.TryAction(['$VALGRIND --version >$TARGET'])
167 context.Result( ret[1].strip() or False )
171 def CheckValgrindWildcards(context):
172 context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
173 ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'],
174 "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
176 context.Result( ret[0] )
179 # Default configuration (boost stuff)
180 senfutil.Configure(env)
182 conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
184 HAVE_VALGRIND = conf.CheckValgrind() and conf.CheckValgrindWildcards()
188 # Only add this here, after all configure checks have run
190 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
191 EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
194 ###########################################################################
196 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
197 # Create it even when cleaning, to silence the doxygen builder warnings
198 if not os.path.exists("doclib/Doxyfile.local"):
199 Execute(Touch("doclib/Doxyfile.local"))
201 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
202 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
203 env.Execute([ "$SCONS prepare" ])
208 SConscript("debian/SConscript")
210 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
211 if env['sparse_tests']:
212 import SparseTestHack
213 SparseTestHack.setup(env)
214 if env.subst('$BUILDDIR') == '#':
215 SConscript("SConscript")
217 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
218 SConscript("Examples/SConscript")
219 SConscript("HowTos/SConscript")
220 SConscript("doclib/SConscript")
221 if env['sparse_tests']:
222 verbose = 'test_changes' in COMMAND_LINE_TARGETS
223 SparseTestHack.build(env, verbose, verbose)
225 ###########################################################################
226 # Define build targets
228 #### install_all, default, all_tests, all
229 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
230 'site_scons/senfutil.py',
231 'site_scons/senfconf.py',
232 'site_scons/yaptu.py' ])
233 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
234 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
235 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
236 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
238 env.Alias('install_all', env.FindInstalledFiles())
239 env.Alias('default', DEFAULT_TARGETS)
240 env.Alias('all_tests', env.FindAllBoostUnitTests())
241 env.Alias('test_changes', 'all_tests')
242 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
245 env.PhonyTarget('prepare', [], [])
248 env.Alias('all_valgrinds')
249 if env['HAVE_VALGRIND']:
250 for test in env.FindAllBoostUnitTests():
251 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
252 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
253 [ """$VALGRIND --tool=memcheck
255 --suppressions=${SOURCES[1]}
257 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
258 """.replace("\n"," "),
260 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
261 env.Alias('all_valgrinds', alias)
264 env.PhonyTarget('lcov', [], [
265 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
266 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
267 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
268 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
269 'rm /tmp/senf_lcov.info' ])
270 if env.GetOption('clean'):
271 env.Clean('lcov', [ os.path.join(path,f)
272 for path, subdirs, files in os.walk('.')
273 for pattern in ('*.gcno', '*.gcda', '*.gcov')
274 for f in fnmatch.filter(files,pattern) ] +
275 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
278 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
279 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
281 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
282 env.Clean('all', [ os.path.join(path,f)
283 for path, subdirs, files in os.walk('.')
284 for pattern in env['CLEAN_PATTERNS']
285 for f in fnmatch.filter(files,pattern) ])
286 # Disable writing to the deleted .sconsign file
287 import SCons.SConsign
288 SCons.SConsign.write = lambda : None
290 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
291 Execute(Touch(".prepare-stamp"))
293 ### execute targets on remote hosts
294 for target in COMMAND_LINE_TARGETS:
296 realtarget, host = target.split('@',1)
297 cwd=env.GetLaunchDir()
298 home=os.environ['HOME']+'/'
299 if cwd.startswith(home) : cwd = cwd[len(home):]
300 args = [ '$SCONSARGS' ]
301 if env.GetLaunchDir() != os.getcwd():
303 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
304 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)