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 # ugly hack for ubuntu karmic
137 # ToDo: auto-configure alike support
138 if os.path.exists('/usr/lib/libboost_regex-mt.so'):
139 env.Append( BOOST_VARIANT = '-mt' )
141 # Set variables from command line
142 senfutil.parseArguments(
144 BoolVariable('final', 'Build final (optimized) build', False),
145 BoolVariable('debug', 'Link in debug symbols', False),
146 BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
147 BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
150 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
151 import SparseTestHack
152 env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
154 if env.has_key('only_tests') : env['sparse_tests'] = True
157 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
158 # Create it even when cleaning, to silence the doxygen builder warnings
159 if not os.path.exists("doclib/Doxyfile.local"):
160 Execute(Touch("doclib/Doxyfile.local"))
162 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
163 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
164 env.Execute([ "$SCONS prepare" ])
169 SConscript("debian/SConscript")
171 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
172 if env['sparse_tests']:
173 import SparseTestHack
174 SparseTestHack.setup(env)
175 if env.subst('$BUILDDIR') == '#':
176 SConscript("SConscript")
178 SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
179 SConscript("Examples/SConscript")
180 SConscript("HowTos/SConscript")
181 SConscript("doclib/SConscript")
182 if env['sparse_tests']:
183 verbose = 'test_changes' in COMMAND_LINE_TARGETS
184 SparseTestHack.build(env, verbose, verbose)
186 ###########################################################################
187 # Define build targets
189 #### install_all, default, all_tests, all
190 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
191 'site_scons/senfutil.py',
192 'site_scons/yaptu.py' ])
193 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
194 FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
195 env.Install('${INCLUDEINSTALLDIR}', 'boost')
197 env.Alias('install_all', env.FindInstalledFiles())
198 env.Alias('default', DEFAULT_TARGETS)
199 env.Alias('all_tests', env.FindAllBoostUnitTests())
200 env.Alias('test_changes', 'all_tests')
201 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
204 env.PhonyTarget('prepare', [], [])
207 for test in env.FindAllBoostUnitTests():
208 stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
209 [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
210 [ """valgrind --tool=memcheck
212 --suppressions=${SOURCES[1]}
214 ${SOURCES[0]} $BOOSTTESTARGS;
215 [ $$? -ne 99 ] || exit 1""".replace("\n"," "),
217 alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
218 env.Alias('all_valgrinds', alias)
221 env.PhonyTarget('lcov', [], [
222 '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
223 '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
224 '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
225 '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
226 'rm /tmp/senf_lcov.info' ])
227 if env.GetOption('clean'):
228 env.Clean('lcov', [ os.path.join(path,f)
229 for path, subdirs, files in os.walk('.')
230 for pattern in ('*.gcno', '*.gcda', '*.gcov')
231 for f in fnmatch.filter(files,pattern) ] +
232 [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
235 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
236 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
238 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
239 env.Clean('all', [ os.path.join(path,f)
240 for path, subdirs, files in os.walk('.')
241 for pattern in env['CLEAN_PATTERNS']
242 for f in fnmatch.filter(files,pattern) ])
243 # Disable writing to the deleted .sconsign file
244 import SCons.SConsign
245 SCons.SConsign.write = lambda : None
247 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
248 Execute(Touch(".prepare-stamp"))
250 ### execute targets on remote hosts
251 for target in COMMAND_LINE_TARGETS:
253 realtarget, host = target.split('@',1)
254 cwd=env.GetLaunchDir()
255 home=os.environ['HOME']+'/'
256 if cwd.startswith(home) : cwd = cwd[len(home):]
257 args = [ '$SCONSARGS' ]
258 if env.GetLaunchDir() != os.getcwd():
260 env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
261 HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)