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', 'BOOST_NO_MT' ],
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' ] +
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 def customChecks(conf):
181 HAVE_VALGRIND = conf.CheckValgrind() and conf.CheckValgrindWildcards()
184 senfutil.Configure(env, customChecks)
186 # Only add this here, after all configure checks have run
188 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
189 EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
192 ###########################################################################
194 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
195 # Create it even when cleaning, to silence the doxygen builder warnings
196 if not os.path.exists("doclib/Doxyfile.local"):
197 Execute(Touch("doclib/Doxyfile.local"))
199 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
200 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
201 env.Execute([ "$SCONS prepare" ])
206 SConscript("debian/SConscript")
208 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
209 if env['sparse_tests']:
210 import SparseTestHack
211 SparseTestHack.setup(env)
212 if env.subst('$BUILDDIR') == '#':
213 SConscript("SConscript")
215 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
216 SConscript("Examples/SConscript")
217 SConscript("HowTos/SConscript")
218 SConscript("doclib/SConscript")
219 if env['sparse_tests']:
220 verbose = 'test_changes' in COMMAND_LINE_TARGETS
221 SparseTestHack.build(env, verbose, verbose)
223 ###########################################################################
224 # Define build targets
226 #### install_all, default, all_tests, all
227 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
228 'site_scons/senfutil.py',
229 'site_scons/senfconf.py',
230 'site_scons/yaptu.py' ])
231 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
232 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
233 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
234 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
236 env.Alias('install_all', env.FindInstalledFiles())
237 env.Alias('default', DEFAULT_TARGETS)
238 env.Alias('all_tests', env.FindAllBoostUnitTests())
239 env.Alias('test_changes', 'all_tests')
240 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
243 env.PhonyTarget('prepare', [], [])
246 env.Alias('all_valgrinds')
247 if env['HAVE_VALGRIND']:
248 for test in env.FindAllBoostUnitTests():
249 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
250 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
251 [ """$VALGRIND --tool=memcheck
253 --suppressions=${SOURCES[1]}
255 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
256 """.replace("\n"," "),
258 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
259 env.Alias('all_valgrinds', alias)
262 env.PhonyTarget('lcov', [], [
263 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
264 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
265 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
266 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
267 'rm /tmp/senf_lcov.info' ])
268 if env.GetOption('clean'):
269 env.Clean('lcov', [ os.path.join(path,f)
270 for path, subdirs, files in os.walk('.')
271 for pattern in ('*.gcno', '*.gcda', '*.gcov')
272 for f in fnmatch.filter(files,pattern) ] +
273 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
276 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
277 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
279 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
280 env.Clean('all', [ os.path.join(path,f)
281 for path, subdirs, files in os.walk('.')
282 for pattern in env['CLEAN_PATTERNS']
283 for f in fnmatch.filter(files,pattern) ])
284 # Disable writing to the deleted .sconsign file
285 import SCons.SConsign
286 SCons.SConsign.write = lambda : None
288 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
289 Execute(Touch(".prepare-stamp"))
291 ### execute targets on remote hosts
292 for target in COMMAND_LINE_TARGETS:
294 realtarget, host = target.split('@',1)
295 cwd=env.GetLaunchDir()
296 home=os.environ['HOME']+'/'
297 if cwd.startswith(home) : cwd = cwd[len(home):]
298 args = [ '$SCONSARGS' ]
299 if env.GetLaunchDir() != os.getcwd():
301 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
302 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)