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