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 = [ ],
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}',
134 # Set variables from command line
135 senfutil.parseArguments(
137 BoolVariable('final', 'Build final (optimized) build', False),
138 BoolVariable('debug', 'Link in debug symbols', False),
139 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
140 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
143 # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment
144 env.Append( ENV = dict(( (k,v)
145 for pattern in env['IMPORT_ENV']
146 for k,v in os.environ.iteritems()
147 if fnmatch.fnmatchcase(k,pattern) )) )
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 ###########################################################################
160 def CheckValgrind(context):
161 context.Message( "Checking for valgrind... " )
162 ret = context.TryAction(['$VALGRIND --version >$TARGET'])
163 context.Result( ret[1].strip() or False )
167 def CheckValgrindWildcards(context):
168 context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
169 ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'],
170 "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
172 context.Result( ret[0] )
175 # Default configuration (boost stuff)
176 senfutil.Configure(env)
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/senfconf.py',
228 'site_scons/yaptu.py' ])
229 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
230 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
231 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
232 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
234 env.Alias('install_all', env.FindInstalledFiles())
235 env.Alias('default', DEFAULT_TARGETS)
236 env.Alias('all_tests', env.FindAllBoostUnitTests())
237 env.Alias('test_changes', 'all_tests')
238 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
241 env.PhonyTarget('prepare', [], [])
244 env.Alias('all_valgrinds')
245 if env['HAVE_VALGRIND']:
246 for test in env.FindAllBoostUnitTests():
247 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
248 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
249 [ """$VALGRIND --tool=memcheck
251 --suppressions=${SOURCES[1]}
253 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
254 """.replace("\n"," "),
256 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
257 env.Alias('all_valgrinds', alias)
260 env.PhonyTarget('lcov', [], [
261 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
262 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
263 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
264 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
265 'rm /tmp/senf_lcov.info' ])
266 if env.GetOption('clean'):
267 env.Clean('lcov', [ os.path.join(path,f)
268 for path, subdirs, files in os.walk('.')
269 for pattern in ('*.gcno', '*.gcda', '*.gcov')
270 for f in fnmatch.filter(files,pattern) ] +
271 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
274 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
275 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
277 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
278 env.Clean('all', [ os.path.join(path,f)
279 for path, subdirs, files in os.walk('.')
280 for pattern in env['CLEAN_PATTERNS']
281 for f in fnmatch.filter(files,pattern) ])
282 # Disable writing to the deleted .sconsign file
283 import SCons.SConsign
284 SCons.SConsign.write = lambda : None
286 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
287 Execute(Touch(".prepare-stamp"))
289 ### execute targets on remote hosts
290 for target in COMMAND_LINE_TARGETS:
292 realtarget, host = target.split('@',1)
293 cwd=env.GetLaunchDir()
294 home=os.environ['HOME']+'/'
295 if cwd.startswith(home) : cwd = cwd[len(home):]
296 args = [ '$SCONSARGS' ]
297 if env.GetLaunchDir() != os.getcwd():
299 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
300 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)