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 You may execute targets on a remote host (if the directory layout is the same)
39 scons <target>@[<user>@]<host>
41 Some more elaborate unit tests may be enabled by setting appropritate variables
42 in the shell (unix) environment
44 SENF_TIMING_CRITICAL_TESTS
45 Enables unit tests which depend on timing measurements. These
46 unit tests should only be run on a single core and an otherwise
49 SENF_WLAN_TEST_INTERFACE
50 WLAN interface to use for testing. The interface should not be
53 SENF_ETH_TEST_INTERFACE
54 Ethernet interface to use for testing. The interface should not
57 Some unit tests will only run when executed to 'root'.
61 ENV = { 'PATH' : os.environ.get('PATH'),
62 'HOME' : os.environ.get('HOME'),
63 'SSH_AGENT_PID': os.environ.get('SSH_AGENT_PID'),
64 'SSH_AUTH_SOCK': os.environ.get('SSH_AUTH_SOCK') },
65 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*' ],
67 BUILDDIR = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
68 CPPPATH = [ '$BUILDDIR', '#' ],
69 LOCALLIBDIR = '$BUILDDIR',
70 LIBPATH = [ '$LOCALLIBDIR' ],
71 LIBS = [ '$LIBSENF$LIBADDSUFFIX', '$EXTRA_LIBS' ],
72 EXTRA_LIBS = [ 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
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',
99 CXXFLAGS_ = senfutil.BuildTypeOptions('CXXFLAGS'),
100 CXXFLAGS_final = [ '-O3' ],
101 CXXFLAGS_normal = [ '-O0', '-g' ],
102 CXXFLAGS_debug = [ '$CXXFLAGS_normal' ],
104 CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ],
105 expandLogOption = senfutil.expandLogOption,
106 CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'),
107 CPPDEFINES_final = [ ],
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' ],
118 # Add all UNIX env vars starting with 'SENF' to the execution environment
119 env.Append( ENV = dict(((k,v) for k,v in os.environ.iteritems() if k.startswith("SENF"))) )
125 SCONSBIN = env.File("#/tools/scons"),
126 SCONSARGS = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
127 [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
128 SCONS = "@$SCONSBIN $SCONSARGS",
129 CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
130 TOPDIR = env.Dir('#').abspath,
131 LIBADDSUFFIX = '${FLAVOR and "_$FLAVOR" or ""}',
132 OBJADDSUFFIX = '${LIBADDSUFFIX}',
136 # Set variables from command line
137 senfutil.parseArguments(
139 BoolVariable('final', 'Build final (optimized) build', False),
140 BoolVariable('debug', 'Link in debug symbols', False),
141 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
142 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
145 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
146 import SparseTestHack
147 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
149 if env.has_key('only_tests') : env['sparse_tests'] = True
152 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
153 # Create it even when cleaning, to silence the doxygen builder warnings
154 if not os.path.exists("doclib/Doxyfile.local"):
155 Execute(Touch("doclib/Doxyfile.local"))
157 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
158 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
159 env.Execute([ "$SCONS prepare" ])
164 SConscript("debian/SConscript")
166 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
167 if env['sparse_tests']:
168 import SparseTestHack
169 SparseTestHack.setup(env)
170 if env.subst('$BUILDDIR') == '#':
171 SConscript("SConscript")
173 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
174 SConscript("Examples/SConscript")
175 SConscript("HowTos/SConscript")
176 SConscript("doclib/SConscript")
177 if env['sparse_tests']:
178 verbose = 'test_changes' in COMMAND_LINE_TARGETS
179 SparseTestHack.build(env, verbose, verbose)
181 ###########################################################################
182 # Define build targets
184 #### install_all, default, all_tests, all
185 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
186 'site_scons/senfutil.py',
187 'site_scons/yaptu.py' ])
188 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
189 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
190 env.Install('${INCLUDEINSTALLDIR}', 'boost')
192 env.Alias('install_all', env.FindInstalledFiles())
193 env.Alias('default', DEFAULT_TARGETS)
194 env.Alias('all_tests', env.FindAllBoostUnitTests())
195 env.Alias('test_changes', 'all_tests')
196 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
199 env.PhonyTarget('prepare', [], [])
202 for test in env.FindAllBoostUnitTests():
203 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
204 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
205 [ """valgrind --tool=memcheck
207 --suppressions=${SOURCES[1]}
209 ${SOURCES[0]} $BOOSTTESTARGS;
210 [ $$? -ne 99 ] || exit 1""".replace("\n"," "),
212 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
213 env.Alias('all_valgrinds', alias)
216 env.PhonyTarget('lcov', [], [
217 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
218 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
219 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
220 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
221 'rm /tmp/senf_lcov.info' ])
222 if env.GetOption('clean'):
223 env.Clean('lcov', [ os.path.join(path,f)
224 for path, subdirs, files in os.walk('.')
225 for pattern in ('*.gcno', '*.gcda', '*.gcov')
226 for f in fnmatch.filter(files,pattern) ] +
227 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
230 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
231 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
233 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
234 env.Clean('all', [ os.path.join(path,f)
235 for path, subdirs, files in os.walk('.')
236 for pattern in env['CLEAN_PATTERNS']
237 for f in fnmatch.filter(files,pattern) ])
238 # Disable writing to the deleted .sconsign file
239 import SCons.SConsign
240 SCons.SConsign.write = lambda : None
242 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
243 Execute(Touch(".prepare-stamp"))
245 ### execute targets on remote hosts
246 for target in COMMAND_LINE_TARGETS:
248 realtarget, host = target.split('@',1)
249 cwd=env.GetLaunchDir()
250 home=os.environ['HOME']+'/'
251 if cwd.startswith(home) : cwd = cwd[len(home):]
252 args = [ '$SCONSARGS' ]
253 if env.GetLaunchDir() != os.getcwd():
255 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
256 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)