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 ENV = { 'PATH' : os.environ.get('PATH'),
63 'HOME' : os.environ.get('HOME'),
64 'SSH_AGENT_PID': os.environ.get('SSH_AGENT_PID'),
65 'SSH_AUTH_SOCK': os.environ.get('SSH_AUTH_SOCK') },
66 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*',
69 BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
70 CPPPATH = [ '#', '$BUILDDIR',
71 '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
72 LOCALLIBDIR = '$BUILDDIR',
73 LIBPATH = [ '$LOCALLIBDIR' ],
74 LIBS = [ '$EXTRA_LIBS' ],
75 EXTRA_LIBS = [ 'rt' ],
76 TEST_EXTRA_LIBS = [ ],
77 VALGRINDARGS = [ '--num-callers=50' ],
80 LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}',
81 BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}',
82 INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}',
83 CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
84 OBJINSTALLDIR = '$CONFINSTALLDIR',
85 DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
86 SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons',
88 CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
89 CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
91 # These options 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_ = senfutil.BuildTypeOptions('CXXFLAGS'),
102 CXXFLAGS_final = [ '-O3' ],
103 CXXFLAGS_normal = [ '-O2', '-g' ],
104 CXXFLAGS_debug = [ '-O0', '-g' ],
106 CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ],
107 expandLogOption = senfutil.expandLogOption,
108 CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'),
109 CPPDEFINES_final = [ ],
110 CPPDEFINES_normal = [ 'SENF_DEBUG' ],
111 CPPDEFINES_debug = [ '$CPPDEFINES_normal' ],
113 LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ],
114 LINKFLAGS_ = senfutil.BuildTypeOptions('LINKFLAGS'),
115 LINKFLAGS_final = [ ],
116 LINKFLAGS_normal = [ '-Wl,-S' ],
117 LINKFLAGS_debug = [ '-g' ],
120 # Add all UNIX env vars starting with 'SENF' to the execution environment
121 env.Append( ENV = dict(((k,v) for k,v in os.environ.iteritems() if k.startswith("SENF"))) )
127 VALGRIND = "valgrind",
128 SCONSBIN = env.File("#/tools/scons"),
129 SCONSARGS = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
130 [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
131 SCONS = "@$SCONSBIN $SCONSARGS",
132 CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
133 TOPDIR = env.Dir('#').abspath,
134 LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}',
135 OBJADDSUFFIX = '${LIBADDSUFFIX}',
139 # Set variables from command line
140 senfutil.parseArguments(
142 BoolVariable('final', 'Build final (optimized) build', False),
143 BoolVariable('debug', 'Link in debug symbols', False),
144 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
145 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
148 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
149 import SparseTestHack
150 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
152 if env.has_key('only_tests') : env['sparse_tests'] = True
155 ###########################################################################
159 def CheckValgrind(context):
160 context.Message( "Checking for valgrind... " )
161 ret = context.TryAction(['$VALGRIND --version >$TARGET'])
162 context.Result( ret[1].strip() or False )
166 def CheckValgrindWildcards(context):
167 context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
168 ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'],
169 "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
171 context.Result( ret[0] )
174 # Default configuration (boost stuff)
175 senfutil.Configure(env)
177 conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
179 HAVE_VALGRIND = conf.CheckValgrind() and conf.CheckValgrindWildcards()
183 # Only add this here, after all configure checks have run
185 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
186 EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
189 ###########################################################################
191 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
192 # Create it even when cleaning, to silence the doxygen builder warnings
193 if not os.path.exists("doclib/Doxyfile.local"):
194 Execute(Touch("doclib/Doxyfile.local"))
196 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
197 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
198 env.Execute([ "$SCONS prepare" ])
203 SConscript("debian/SConscript")
205 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
206 if env['sparse_tests']:
207 import SparseTestHack
208 SparseTestHack.setup(env)
209 if env.subst('$BUILDDIR') == '#':
210 SConscript("SConscript")
212 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
213 SConscript("Examples/SConscript")
214 SConscript("HowTos/SConscript")
215 SConscript("doclib/SConscript")
216 if env['sparse_tests']:
217 verbose = 'test_changes' in COMMAND_LINE_TARGETS
218 SparseTestHack.build(env, verbose, verbose)
220 ###########################################################################
221 # Define build targets
223 #### install_all, default, all_tests, all
224 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
225 'site_scons/senfutil.py',
226 'site_scons/senfconf.py',
227 'site_scons/yaptu.py' ])
228 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
229 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
230 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
231 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
233 env.Alias('install_all', env.FindInstalledFiles())
234 env.Alias('default', DEFAULT_TARGETS)
235 env.Alias('all_tests', env.FindAllBoostUnitTests())
236 env.Alias('test_changes', 'all_tests')
237 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
240 env.PhonyTarget('prepare', [], [])
243 env.Alias('all_valgrinds')
244 if env['HAVE_VALGRIND']:
245 for test in env.FindAllBoostUnitTests():
246 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
247 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
248 [ """$VALGRIND --tool=memcheck
250 --suppressions=${SOURCES[1]}
252 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
253 """.replace("\n"," "),
255 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
256 env.Alias('all_valgrinds', alias)
259 env.PhonyTarget('lcov', [], [
260 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
261 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
262 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
263 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
264 'rm /tmp/senf_lcov.info' ])
265 if env.GetOption('clean'):
266 env.Clean('lcov', [ os.path.join(path,f)
267 for path, subdirs, files in os.walk('.')
268 for pattern in ('*.gcno', '*.gcda', '*.gcov')
269 for f in fnmatch.filter(files,pattern) ] +
270 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
273 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
274 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
276 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
277 env.Clean('all', [ os.path.join(path,f)
278 for path, subdirs, files in os.walk('.')
279 for pattern in env['CLEAN_PATTERNS']
280 for f in fnmatch.filter(files,pattern) ])
281 # Disable writing to the deleted .sconsign file
282 import SCons.SConsign
283 SCons.SConsign.write = lambda : None
285 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
286 Execute(Touch(".prepare-stamp"))
288 ### execute targets on remote hosts
289 for target in COMMAND_LINE_TARGETS:
291 realtarget, host = target.split('@',1)
292 cwd=env.GetLaunchDir()
293 home=os.environ['HOME']+'/'
294 if cwd.startswith(home) : cwd = cwd[len(home):]
295 args = [ '$SCONSARGS' ]
296 if env.GetLaunchDir() != os.getcwd():
298 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
299 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)