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*' ],
68 BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
69 CPPPATH = [ '#', '$BUILDDIR',
70 '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
71 LOCALLIBDIR = '$BUILDDIR',
72 LIBPATH = [ '$LOCALLIBDIR' ],
73 LIBS = [ '$EXTRA_LIBS' ],
74 EXTRA_LIBS = [ 'rt' ],
75 TEST_EXTRA_LIBS = [ ],
76 VALGRINDARGS = [ '--num-callers=50' ],
79 LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}',
80 BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}',
81 INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}',
82 CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
83 OBJINSTALLDIR = '$CONFINSTALLDIR',
84 DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
85 SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons',
87 CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
88 CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
90 # These options are insane. Only useful for inline debugging. Need at least 1G free RAM
91 INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden',
92 '-fno-inline-functions', '-Winline'
93 '--param','large-function-growth=10000',
94 '--param', 'large-function-insns=10000',
95 '--param','inline-unit-growth=10000' ],
96 INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ],
97 INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ],
98 CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
99 '-pipe', '$CXXFLAGS_' ],
100 CXXFLAGS_ = senfutil.BuildTypeOptions('CXXFLAGS'),
101 CXXFLAGS_final = [ '-O3' ],
102 CXXFLAGS_normal = [ '-O0', '-g' ],
103 CXXFLAGS_debug = [ '$CXXFLAGS_normal' ],
105 CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ],
106 expandLogOption = senfutil.expandLogOption,
107 CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'),
108 CPPDEFINES_final = [ ],
109 CPPDEFINES_normal = [ 'SENF_DEBUG' ],
110 CPPDEFINES_debug = [ '$CPPDEFINES_normal' ],
112 LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ],
113 LINKFLAGS_ = senfutil.BuildTypeOptions('LINKFLAGS'),
114 LINKFLAGS_final = [ ],
115 LINKFLAGS_normal = [ '-Wl,-S' ],
116 LINKFLAGS_debug = [ '-g' ],
119 # Add all UNIX env vars starting with 'SENF' to the execution environment
120 env.Append( ENV = dict(((k,v) for k,v in os.environ.iteritems() if k.startswith("SENF"))) )
126 VALGRIND = "valgrind",
127 SCONSBIN = env.File("#/tools/scons"),
128 SCONSARGS = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
129 [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
130 SCONS = "@$SCONSBIN $SCONSARGS",
131 CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
132 TOPDIR = env.Dir('#').abspath,
133 LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}',
134 OBJADDSUFFIX = '${LIBADDSUFFIX}',
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 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
148 import SparseTestHack
149 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
151 if env.has_key('only_tests') : env['sparse_tests'] = True
154 ###########################################################################
157 # Default configuration (boost stuff)
158 senfutil.Configure(env)
161 def CheckValgrind(context):
162 context.Message( "Checking for valgrind... " )
163 ret = context.TryAction(['valgrind --version >$TARGET'])
164 context.Result( ret[1].strip() or False )
168 def CheckValgrindWildcards(context):
169 context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
170 ret = context.TryAction(['valgrind --suppressions=$SOURCE /bin/true'],
171 "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
173 context.Result( ret[0] )
176 conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
178 HAVE_VALGRIND = conf.CheckValgrind() and conf.CheckValgrindWildcards()
182 # Only add this here, after all configure checks have run
184 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
185 EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
188 ###########################################################################
190 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
191 # Create it even when cleaning, to silence the doxygen builder warnings
192 if not os.path.exists("doclib/Doxyfile.local"):
193 Execute(Touch("doclib/Doxyfile.local"))
195 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
196 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
197 env.Execute([ "$SCONS prepare" ])
202 SConscript("debian/SConscript")
204 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
205 if env['sparse_tests']:
206 import SparseTestHack
207 SparseTestHack.setup(env)
208 if env.subst('$BUILDDIR') == '#':
209 SConscript("SConscript")
211 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
212 SConscript("Examples/SConscript")
213 SConscript("HowTos/SConscript")
214 SConscript("doclib/SConscript")
215 if env['sparse_tests']:
216 verbose = 'test_changes' in COMMAND_LINE_TARGETS
217 SparseTestHack.build(env, verbose, verbose)
219 ###########################################################################
220 # Define build targets
222 #### install_all, default, all_tests, all
223 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
224 'site_scons/senfutil.py',
225 'site_scons/yaptu.py' ])
226 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
227 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
228 env.Install('${INCLUDEINSTALLDIR}', 'boost')
230 env.Alias('install_all', env.FindInstalledFiles())
231 env.Alias('default', DEFAULT_TARGETS)
232 env.Alias('all_tests', env.FindAllBoostUnitTests())
233 env.Alias('test_changes', 'all_tests')
234 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
237 env.PhonyTarget('prepare', [], [])
240 env.Alias('all_valgrinds')
241 if env['HAVE_VALGRIND']:
242 for test in env.FindAllBoostUnitTests():
243 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
244 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
245 [ """$VALGRIND --tool=memcheck
247 --suppressions=${SOURCES[1]}
249 ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
250 """.replace("\n"," "),
252 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
253 env.Alias('all_valgrinds', alias)
256 env.PhonyTarget('lcov', [], [
257 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
258 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
259 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
260 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
261 'rm /tmp/senf_lcov.info' ])
262 if env.GetOption('clean'):
263 env.Clean('lcov', [ os.path.join(path,f)
264 for path, subdirs, files in os.walk('.')
265 for pattern in ('*.gcno', '*.gcda', '*.gcov')
266 for f in fnmatch.filter(files,pattern) ] +
267 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
270 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
271 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
273 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
274 env.Clean('all', [ os.path.join(path,f)
275 for path, subdirs, files in os.walk('.')
276 for pattern in env['CLEAN_PATTERNS']
277 for f in fnmatch.filter(files,pattern) ])
278 # Disable writing to the deleted .sconsign file
279 import SCons.SConsign
280 SCons.SConsign.write = lambda : None
282 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
283 Execute(Touch(".prepare-stamp"))
285 ### execute targets on remote hosts
286 for target in COMMAND_LINE_TARGETS:
288 realtarget, host = target.split('@',1)
289 cwd=env.GetLaunchDir()
290 home=os.environ['HOME']+'/'
291 if cwd.startswith(home) : cwd = cwd[len(home):]
292 args = [ '$SCONSARGS' ]
293 if env.GetLaunchDir() != os.getcwd():
295 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
296 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)