3 import sys, os.path, fnmatch
4 import SENFSCons, senfutil
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 The following additional targets may be called within subdirectories, either
37 using '$ scons -u <target>' or '$ scons <directory>/<target>:
39 test Build and run unit test for this module
40 doc Build the documentation of this module
41 valgrind Run the unit test of this module under valgrind
43 You may execute targets on a remote host via ssh (if the directory layout is the
46 $ scons <target>@[<user>@]<host>
48 Some more elaborate unit tests may be enabled by setting appropritate variables
49 in the shell (unix) environment
51 SENF_TIMING_CRITICAL_TESTS
52 Enables unit tests which depend on timing measurements. These
53 unit tests should only be run on a single core and an otherwise
56 SENF_WLAN_TEST_INTERFACE
57 WLAN interface to use for testing. The interface should not be
60 SENF_ETH_TEST_INTERFACE
61 Ethernet interface to use for testing. The interface should not
64 Some unit tests will only run when executed to 'root'.
68 expandLogOption = senfutil.expandLogOption,
69 CXXFLAGS_ = env.BuildTypeOptions('CXXFLAGS'),
70 CPPDEFINES_ = env.BuildTypeOptions('CPPDEFINES'),
71 LINKFLAGS_ = env.BuildTypeOptions('LINKFLAGS'),
74 IMPORT_ENV = [ 'PATH', 'HOME', 'SSH_*', 'SENF*', 'CCACHE_*', 'DISTCC_*' ],
76 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*',
79 CPPPATH = [ '#', '$BUILDDIR',
80 '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
81 LOCALLIBDIR = '$BUILDDIR',
82 LIBPATH = [ '$LOCALLIBDIR' ],
83 LIBS = [ '$EXTRA_LIBS' ],
84 EXTRA_LIBS = [ 'rt' ],
85 TEST_EXTRA_LIBS = [ ],
86 VALGRINDARGS = [ '--num-callers=50' ],
88 CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
89 CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
91 # INLINE_OPTS_DEBUG are insane. Only useful for inline debugging. Need at least 1G free RAM
92 INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden',
93 '-fno-inline-functions', '-Winline'
94 '--param','large-function-growth=10000',
95 '--param', 'large-function-insns=10000',
96 '--param','inline-unit-growth=10000' ],
97 INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ],
98 INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ],
99 CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
100 '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ],
101 CXXFLAGS_final = [ '-O3' ],
102 CXXFLAGS_normal = [ '-O2', '-g' ],
103 CXXFLAGS_debug = [ '-O0', '-g' ],
105 CPPDEFINES = [ '$expandLogOption', '$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_final = [ ],
112 LINKFLAGS_normal = [ '-Wl,-S' ],
113 LINKFLAGS_debug = [ '-g' ],
117 LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}',
118 BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}',
119 INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}',
120 CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
121 OBJINSTALLDIR = '$CONFINSTALLDIR',
122 DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
123 SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons',
125 BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
130 VALGRIND = "valgrind",
131 SCONSBIN = env.File("#/tools/scons"),
132 SCONSARGS = ([ '-Q', '-j$CONCURRENCY_LEVEL' ] +
133 [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ]),
134 SCONS = "@$SCONSBIN $SCONSARGS",
135 CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
136 TOPDIR = env.Dir('#').abspath,
137 LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}',
138 OBJADDSUFFIX = '${LIBADDSUFFIX}',
142 # Set variables from command line
143 senfutil.parseArguments(
145 BoolVariable('final', 'Build final (optimized) build', False),
146 BoolVariable('debug', 'Link in debug symbols', False),
147 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
148 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
151 # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment
152 senfutil.importProcessEnv(env)
154 # Handle 'test_changes'
155 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
156 import SparseTestHack
157 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
159 if env.has_key('only_tests') : env['sparse_tests'] = True
163 ###########################################################################
166 SConscript('SConfigure')
168 # Only add this here, after all configure checks have run
170 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
171 EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
174 ###########################################################################
176 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
177 # Create it even when cleaning, to silence the doxygen builder warnings
178 if not os.path.exists("doclib/Doxyfile.local"):
179 Execute(Touch("doclib/Doxyfile.local"))
181 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
182 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
183 env.Execute([ "$SCONS prepare" ])
188 SConscript("debian/SConscript")
190 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
191 if env['sparse_tests']:
192 import SparseTestHack
193 SparseTestHack.setup(env)
194 if env.subst('$BUILDDIR') == '#':
195 SConscript("SConscript")
197 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
198 SConscript("Examples/SConscript")
199 SConscript("HowTos/SConscript")
200 SConscript("doclib/SConscript")
201 if env['sparse_tests']:
202 verbose = 'test_changes' in COMMAND_LINE_TARGETS
203 SparseTestHack.build(env, verbose, verbose)
205 ###########################################################################
206 # Define build targets
208 #### install_all, default, all_tests, all
209 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
210 'site_scons/senfutil.py',
211 'site_scons/senfconf.py',
212 'site_scons/yaptu.py' ])
213 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
214 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
215 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
216 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
218 env.Alias('install_all', env.FindInstalledFiles())
219 env.Alias('default', DEFAULT_TARGETS)
220 env.Alias('all_tests', env.FindAllBoostUnitTests())
221 env.Alias('test_changes', 'all_tests')
222 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
225 env.PhonyTarget('prepare', [], [])
228 env.Alias('all_valgrinds')
229 if env.get('HAVE_VALGRIND'):
230 for test in env.FindAllBoostUnitTests():
231 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
232 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
233 [ """$VALGRIND --tool=memcheck
235 --suppressions=${SOURCES[1]}
237 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
238 """.replace("\n"," "),
240 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
241 env.Alias('all_valgrinds', alias)
244 env.PhonyTarget('lcov', [], [
245 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
246 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
247 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
248 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
249 'rm /tmp/senf_lcov.info' ])
250 if env.GetOption('clean'):
251 env.Clean('lcov', [ os.path.join(path,f)
252 for path, subdirs, files in os.walk('.')
253 for pattern in ('*.gcno', '*.gcda', '*.gcov')
254 for f in fnmatch.filter(files,pattern) ] +
255 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
258 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
259 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
261 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
262 env.Clean('all', [ os.path.join(path,f)
263 for path, subdirs, files in os.walk('.')
264 for pattern in env['CLEAN_PATTERNS']
265 for f in fnmatch.filter(files,pattern) ])
266 # Disable writing to the deleted .sconsign file
267 import SCons.SConsign
268 SCons.SConsign.write = lambda : None
270 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
271 Execute(Touch(".prepare-stamp"))
273 ### execute targets on remote hosts
274 for target in COMMAND_LINE_TARGETS:
276 realtarget, host = target.split('@',1)
277 cwd=env.GetLaunchDir()
278 home=os.environ['HOME']+'/'
279 if cwd.startswith(home) : cwd = cwd[len(home):]
280 args = [ '$SCONSARGS' ]
281 if env.GetLaunchDir() != os.getcwd():
283 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
284 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)
286 env.PhonyTarget('clean', [], [
287 lambda **args: sys.stderr.write(
288 "=================================================================\n"
289 "'clean' is not a valid target. Instead, use\n"
291 "=================================================================\n") ])