Scons: added profile build option
[senf.git] / SConstruct
1 # -*- python -*-
2
3 import sys, os.path
4 import SENFSCons, senfutil
5
6 ###########################################################################
7 # Load utilities and setup libraries and configure build
8
9 env = Environment()
10
11 env.Decider('MD5-timestamp')
12 env.EnsureSConsVersion(1,2)
13
14 # Load all the local SCons tools
15 senfutil.loadTools(env)
16
17 env.Help("""
18 Additional top-level build targets:
19
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
26 all             Build everything
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
35
36 The following additional targets may be called within subdirectories, either
37 using '$ scons -u <target>'  or '$ scons <directory>/<target>:
38
39 test            Build and run unit test for this module
40 doc             Build the documentation of this module
41 valgrind        Run the unit test of this module under valgrind
42
43 When cleaning up using '$ scons -c <target>', some targets are handled specially:
44
45 all             Remove everything generated by the build including temporary and
46                 backup files
47
48 some            Remove all files not needed for building like temporary or backup
49                 files. This target is only valid when called as clean target.
50
51 You may execute targets on a remote host via ssh (if the directory layout is the
52 same) by calling
53
54     $ scons <target>@[<user>@]<host>
55
56 Some more elaborate unit tests may be enabled by setting appropritate variables
57 in the shell (unix) environment
58
59 SENF_TIMING_CRITICAL_TESTS
60                 Enables unit tests which depend on timing measurements. These
61                 unit tests should only be run on a single core and an otherwise
62                 idle system.
63
64 SENF_WLAN_TEST_INTERFACE
65                 WLAN interface to use for testing. The interface should not be
66                 actively in use.
67
68 SENF_ETH_TEST_INTERFACE
69                 Ethernet interface to use for testing. The interface should not
70                 be actively in use.
71
72 Some unit tests will only run when executed to 'root'.
73 """)
74
75 env.Replace(
76     expandLogOption        = senfutil.expandLogOption,
77     CXXFLAGS_              = env.BuildTypeOptions('CXXFLAGS'),
78     CPPDEFINES_            = env.BuildTypeOptions('CPPDEFINES'),
79     LINKFLAGS_             = env.BuildTypeOptions('LINKFLAGS'),
80 )
81 env.Append(
82     IMPORT_ENV             = [ 'PATH', 'HOME', 'SSH_*', 'SENF*', 'CCACHE_*', 'DISTCC_*' ],
83
84     CLEAN_SOME_PATTERNS    = [ '*~', '#*#', '*.pyc', 'semantic.cache' ],
85     CLEAN_PATTERNS         = [ '.sconsign*', '.sconf_temp' ],
86
87     CPPPATH                = [ '#', '$BUILDDIR',
88                                '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
89     LIBPATH                = [ '$LOCALLIBDIR' ],
90     LIBS                   = [ '$EXTRA_LIBS' ],
91     EXTRA_LIBS             = [ 'rt' ],
92     TEST_EXTRA_LIBS        = [  ],
93     VALGRINDARGS           = [ '--num-callers=50' ],
94
95     CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
96     CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
97
98     # INLINE_OPTS_DEBUG are insane. Only useful for inline debugging. Need at least 1G free RAM
99     INLINE_OPTS_DEBUG      = [ '-finline-limit=20000', '-fvisibility-inlines-hidden',
100                                '-fno-inline-functions', '-Winline'
101                                '--param','large-function-growth=10000',
102                                '--param', 'large-function-insns=10000',
103                                '--param','inline-unit-growth=10000' ],
104     INLINE_OPTS_NORMAL     = [ '-finline-limit=5000', '--param', 'inline-unit-growth=60' ],
105     INLINE_OPTS            = [ '$INLINE_OPTS_NORMAL' ],
106     CXXFLAGS               = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
107                                '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ],
108     CXXFLAGS_final         = [ '-O3' ],
109     CXXFLAGS_normal        = [ '-O2', '-g' ],
110     CXXFLAGS_debug         = [ '-O0', '-g' ],
111
112     CPPDEFINES             = [ '$expandLogOption', '$CPPDEFINES_' ],
113     CPPDEFINES_final       = [ 'SENF_PPI_NOTRACE', 'BOOST_NO_MT', 'NDEBUG', 'BOOST_DISABLE_ASSERTS' ],
114     CPPDEFINES_normal      = [ 'SENF_DEBUG' ],
115     CPPDEFINES_debug       = [ '$CPPDEFINES_normal' ],
116
117     LINKFLAGS              = [ '-rdynamic', '$LINKFLAGS_' ],
118     LINKFLAGS_final        = [ ],
119     LINKFLAGS_normal       = [ '-Wl,-S' ],
120     LINKFLAGS_debug        = [ '-g' ],
121 )
122 env.SetDefault(
123     PREFIX                 = '#/dist',
124     LIBINSTALLDIR          = '$PREFIX${syslayout and "/lib" or ""}',
125     BININSTALLDIR          = '$PREFIX${syslayout and "/bin" or ""}',
126     INCLUDEINSTALLDIR      = '$PREFIX${syslayout and "/include" or ""}',
127     CONFINSTALLDIR         = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
128     OBJINSTALLDIR          = '$CONFINSTALLDIR',
129     DOCINSTALLDIR          = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
130     SCONSINSTALLDIR        = '$CONFINSTALLDIR/site_scons',
131
132     BUILDDIR               = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
133     LOCALLIBDIR            = '$BUILDDIR',
134
135     LIBSENF                = "senf",
136     LCOV                   = "lcov",
137     GENHTML                = "genhtml",
138     VALGRIND               = "valgrind",
139     SCONSBIN               = env.File("#/tools/scons"),
140     SCONSARGS              = ([ '-Q', '-j$CONCURRENCY_LEVEL' ] +
141                               [ '%s=%s' % (k,v) for k,v in ARGLIST ]),
142     SCONS                  = "@$SCONSBIN $SCONSARGS",
143     CONCURRENCY_LEVEL      = env.GetOption('num_jobs') or 1,
144     TOPDIR                 = env.Dir('#').abspath,
145     LIBADDSUFFIX           = '${FLAVOR and "_$FLAVOR" or ""}',
146     OBJADDSUFFIX           = '${LIBADDSUFFIX}',
147     FLAVOR                 = '',
148 )
149
150 # Set variables from command line
151 senfutil.parseArguments(
152     env,
153     BoolVariable('final', 'Build final (optimized) build', False),
154     BoolVariable('debug', 'Link in debug symbols', False),
155     BoolVariable('profile', 'compile and link with the profiling enabled option', False),
156     BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
157     BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
158 )
159
160 # gprof
161 if env['profile']:
162     env.Append(
163         LINKFLAGS          = [ '-pg' ],
164         CXXFLAGS           = [ '-pg' ],
165     )
166
167
168 # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment
169 senfutil.importProcessEnv(env)
170
171 # Handle 'test_changes'
172 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
173     import SparseTestHack
174     env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
175
176 if env.has_key('only_tests') : env['sparse_tests'] = True
177
178 Export('env')
179
180 ###########################################################################
181 # Configure
182
183 SConscript('SConfigure')
184
185 # Only add this here, after all configure checks have run
186
187 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
188            EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
189                           '$BOOSTFSLIB' ])
190
191 ###########################################################################
192
193 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
194 # Create it even when cleaning, to silence the doxygen builder warnings
195 if not os.path.exists("doclib/Doxyfile.local"):
196     Execute(Touch("doclib/Doxyfile.local"))
197
198 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
199    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
200     env.Execute([ "$SCONS prepare" ])
201
202 # Load SConscripts
203
204 SConscriptChdir(0)
205 SConscript("debian/SConscript")
206 SConscriptChdir(1)
207 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
208 if env['sparse_tests']:
209     import SparseTestHack
210     SparseTestHack.setup(env)
211 if env.subst('$BUILDDIR') == '#':
212     SConscript("SConscript")
213 else:
214     SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
215 SConscript("Examples/SConscript")
216 SConscript("HowTos/SConscript")
217 SConscript("doclib/SConscript")
218 if env['sparse_tests']:
219     verbose = 'test_changes' in COMMAND_LINE_TARGETS
220     SparseTestHack.build(env, verbose, verbose)
221
222 ###########################################################################
223 # Define build targets
224
225 #### install_all, default, all_tests, all
226 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
227                                     'site_scons/senfutil.py',
228                                     'site_scons/yaptu.py' ])
229 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
230                FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
231 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
232 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
233
234 env.Alias('install_all', env.FindInstalledFiles())
235 env.Alias('default', DEFAULT_TARGETS)
236 env.Alias('all_tests', env.FindAllBoostUnitTests())
237 env.Alias('test_changes', 'all_tests')
238 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
239
240 #### prepare and -c some
241 env.PhonyTarget('prepare', [], [])
242 env.PhonyTarget('some', [], [])
243
244 #### valgrind
245 env.Alias('all_valgrinds')
246 if env.get('HAVE_VALGRIND'):
247     for test in env.FindAllBoostUnitTests():
248         stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
249                             [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
250                             [ """$VALGRIND --tool=memcheck
251                                           --error-exitcode=1
252                                           --suppressions=${SOURCES[1]}
253                                           $VALGRINDARGS
254                                               ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
255                               """.replace("\n"," "),
256                               Touch("$TARGET") ])
257         alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
258         env.Alias('all_valgrinds', alias)
259
260 ### lcov
261 env.PhonyTarget('lcov', [], [
262         '$SCONS'
263         '    debug=1'
264         '    BUILDDIR="#/build/lcov"'
265         '    CCFLAGS+="-fprofile-arcs -ftest-coverage"'
266         '    LIBS+="gcov"'
267         '        all_tests',
268         '$LCOV'
269         '    --follow'
270         '    --directory $TOPDIR/build/lcov/senf'
271         '    --capture'
272         '    --output-file /tmp/senf_lcov.info'
273         '    --base-directory $TOPDIR',
274         '$LCOV'
275         '    --output-file lcov.info'
276         '    --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*"',
277         '$GENHTML'
278         '    --output-directory doc/lcov'
279         '    --title all_tests lcov.info',
280         'rm /tmp/senf_lcov.info' ])
281 senfutil.CleanGlob(env, ['lcov','some','all'], [ '*.gcno', '*.gcda', '*.gcov' ])
282 env.Clean(['lcov', 'all'], [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
283
284 #### clean
285
286 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
287
288 senfutil.CleanGlob(env, 'all', '$CLEAN_PATTERNS')
289 senfutil.CleanGlob(env, ['some', 'all'], '$CLEAN_SOME_PATTERNS')
290
291 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
292     env.Depends('all', ('lcov', 'all_valgrinds'))
293     # Disable writing to the deleted .sconsign file
294     import SCons.SConsign
295     SCons.SConsign.write = lambda : None
296
297 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
298     Execute(Touch(".prepare-stamp"))
299
300 ### execute targets on remote hosts
301 for target in COMMAND_LINE_TARGETS:
302     if '@' in target:
303         realtarget, host = target.split('@',1)
304         cwd=env.GetLaunchDir()
305         home=os.environ['HOME']+'/'
306         if cwd.startswith(home) : cwd = cwd[len(home):]
307         args = [ '$SCONSARGS' ]
308         if env.GetLaunchDir() != os.getcwd():
309             args.append('-u')
310         env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
311                         HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)
312
313 env.PhonyTarget('clean', [], [
314         lambda **args: sys.stderr.write(
315             "=================================================================\n"
316             "'clean' is not a valid target, use the '-c' option instead:\n"
317             "    $ scons -c all\n"
318             "=================================================================\n") ])