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/yaptu.py' ])
227 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
228 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
229 env.Install('${INCLUDEINSTALLDIR}', 'boost')
231 env.Alias('install_all', env.FindInstalledFiles())
232 env.Alias('default', DEFAULT_TARGETS)
233 env.Alias('all_tests', env.FindAllBoostUnitTests())
234 env.Alias('test_changes', 'all_tests')
235 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
238 env.PhonyTarget('prepare', [], [])
241 env.Alias('all_valgrinds')
242 if env['HAVE_VALGRIND']:
243 for test in env.FindAllBoostUnitTests():
244 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
245 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
246 [ """$VALGRIND --tool=memcheck
248 --suppressions=${SOURCES[1]}
250 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
251 """.replace("\n"," "),
253 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
254 env.Alias('all_valgrinds', alias)
257 env.PhonyTarget('lcov', [], [
258 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
259 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
260 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
261 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
262 'rm /tmp/senf_lcov.info' ])
263 if env.GetOption('clean'):
264 env.Clean('lcov', [ os.path.join(path,f)
265 for path, subdirs, files in os.walk('.')
266 for pattern in ('*.gcno', '*.gcda', '*.gcov')
267 for f in fnmatch.filter(files,pattern) ] +
268 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
271 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
272 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
274 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
275 env.Clean('all', [ os.path.join(path,f)
276 for path, subdirs, files in os.walk('.')
277 for pattern in env['CLEAN_PATTERNS']
278 for f in fnmatch.filter(files,pattern) ])
279 # Disable writing to the deleted .sconsign file
280 import SCons.SConsign
281 SCons.SConsign.write = lambda : None
283 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
284 Execute(Touch(".prepare-stamp"))
286 ### execute targets on remote hosts
287 for target in COMMAND_LINE_TARGETS:
289 realtarget, host = target.split('@',1)
290 cwd=env.GetLaunchDir()
291 home=os.environ['HOME']+'/'
292 if cwd.startswith(home) : cwd = cwd[len(home):]
293 args = [ '$SCONSARGS' ]
294 if env.GetLaunchDir() != os.getcwd():
296 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
297 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)