g++/final: don't add extra code to check for buffer overflows (-fno-stack-protector)
[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                                "${profile and '-pg' or None}" ],
109     CXXFLAGS_final         = [ '-O3', '-fno-threadsafe-statics','-fno-stack-protector',
110                                '-ffunction-sections' ],
111     CXXFLAGS_normal        = [ '-O2', '-g' ],
112     CXXFLAGS_debug         = [ '-O0', '-g' ],
113
114     CPPDEFINES             = [ '$expandLogOption', '$CPPDEFINES_' ],
115     CPPDEFINES_final       = [ 'SENF_PPI_NOTRACE', 'NDEBUG', 
116                                'BOOST_NO_MT', 'BOOST_DISABLE_ASSERTS', 'BOOST_DISABLE_THREADS' ],
117     CPPDEFINES_normal      = [ 'SENF_DEBUG' ],
118     CPPDEFINES_debug       = [ '$CPPDEFINES_normal' ],
119
120     LINKFLAGS              = [ '-rdynamic', '$LINKFLAGS_', "${profile and '-pg' or None}" ],
121     LINKFLAGS_final        = [ '-Wl,--gc-sections' ],
122     LINKFLAGS_normal       = [ '-Wl,-S' ],
123     LINKFLAGS_debug        = [ '-g' ],
124 )
125 env.SetDefault(
126     PREFIX                 = '#/dist',
127     LIBINSTALLDIR          = '$PREFIX${syslayout and "/lib" or ""}',
128     BININSTALLDIR          = '$PREFIX${syslayout and "/bin" or ""}',
129     INCLUDEINSTALLDIR      = '$PREFIX${syslayout and "/include" or ""}',
130     CONFINSTALLDIR         = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
131     OBJINSTALLDIR          = '$CONFINSTALLDIR',
132     DOCINSTALLDIR          = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
133     SCONSINSTALLDIR        = '$CONFINSTALLDIR/site_scons',
134
135     BUILDDIR               = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
136     LOCALLIBDIR            = '$BUILDDIR',
137
138     LIBSENF                = "senf",
139     LCOV                   = "lcov",
140     GENHTML                = "genhtml",
141     VALGRIND               = "valgrind",
142     SCONSBIN               = env.File("#/tools/scons"),
143     SCONSARGS              = ([ '-Q', '-j$CONCURRENCY_LEVEL' ] +
144                               [ '%s=%s' % (k,v) for k,v in ARGLIST ]),
145     SCONS                  = "@$SCONSBIN $SCONSARGS",
146     CONCURRENCY_LEVEL      = env.GetOption('num_jobs') or 1,
147     TOPDIR                 = env.Dir('#').abspath,
148     LIBADDSUFFIX           = '${FLAVOR and "_$FLAVOR" or ""}',
149     OBJADDSUFFIX           = '${LIBADDSUFFIX}',
150     FLAVOR                 = '',
151
152     PARSEFLAGS             = '',
153 )
154
155 # Set variables from command line
156 senfutil.parseArguments(
157     env,
158     BoolVariable('final', 'Build final (optimized) build', False),
159     BoolVariable('debug', 'Link in debug symbols', False),
160     BoolVariable('profile', 'compile and link with the profiling enabled option', False),
161     BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
162     BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
163 )
164
165 # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment
166 senfutil.importProcessEnv(env)
167
168 # Handle 'test_changes'
169 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
170     import SparseTestHack
171     env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
172
173 if env.has_key('only_tests') : env['sparse_tests'] = True
174
175 Export('env')
176
177 ###########################################################################
178 # Configure
179
180 SConscript('SConfigure')
181
182 # Only add this here, after all configure checks have run
183
184 env.Append(LIBS = [ '$LIBSENF$LIBADDSUFFIX',
185                     '$BOOSTREGEXLIB', '$BOOSTSIGNALSLIB',
186                     '$BOOSTFSLIB', '$BOOSTSYSTEMLIB' ])
187
188 ###########################################################################
189
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"))
194
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" ])
198
199 # Load SConscripts
200
201 SConscriptChdir(0)
202 SConscript("debian/SConscript")
203 SConscriptChdir(1)
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")
210 else:
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)
218
219 ###########################################################################
220 # Define build targets
221
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_ext')
229 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
230
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' ])
236
237 #### prepare and -c some
238 env.PhonyTarget('prepare', [], [])
239 env.PhonyTarget('some', [], [])
240
241 #### valgrind
242 env.Alias('all_valgrinds')
243 if env.get('HAVE_VALGRIND'):
244     for test in env.FindAllBoostUnitTests():
245         stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'),
246                             [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
247                             [ """$VALGRIND --tool=memcheck
248                                           --error-exitcode=1
249                                           --suppressions=${SOURCES[1]}
250                                           $VALGRINDARGS
251                                               ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
252                               """.replace("\n"," "),
253                               Touch("$TARGET") ])
254         alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
255         env.Alias('all_valgrinds', alias)
256
257 ### lcov
258 env.PhonyTarget('lcov', [], [
259         '$SCONS'
260         '    debug=1'
261         '    BUILDDIR="#/build/lcov"'
262         '    CCFLAGS+="-fprofile-arcs -ftest-coverage"'
263         '    LIBS+="gcov"'
264         '        all_tests',
265         '$LCOV'
266         '    --follow'
267         '    --directory $TOPDIR/build/lcov/senf'
268         '    --capture'
269         '    --output-file /tmp/senf_lcov.info'
270         '    --base-directory $TOPDIR',
271         '$LCOV'
272         '    --output-file lcov.info'
273         '    --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*"',
274         '$GENHTML'
275         '    --output-directory doc/lcov'
276         '    --title all_tests lcov.info',
277         'rm /tmp/senf_lcov.info' ])
278 senfutil.CleanGlob(env, ['lcov','some','all'], [ '*.gcno', '*.gcda', '*.gcov' ])
279 env.Clean(['lcov', 'all'], [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
280
281 #### clean
282
283 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
284
285 senfutil.CleanGlob(env, 'all', '$CLEAN_PATTERNS')
286 senfutil.CleanGlob(env, ['some', 'all'], '$CLEAN_SOME_PATTERNS')
287
288 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
289     env.Depends('all', ('lcov', 'all_valgrinds'))
290     # Disable writing to the deleted .sconsign file
291     import SCons.SConsign
292     SCons.SConsign.write = lambda : None
293
294 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
295     Execute(Touch(".prepare-stamp"))
296
297 ### execute targets on remote hosts
298 for target in COMMAND_LINE_TARGETS:
299     if '@' in target:
300         realtarget, host = target.split('@',1)
301         cwd=env.GetLaunchDir()
302         home=os.environ['HOME']+'/'
303         if cwd.startswith(home) : cwd = cwd[len(home):]
304         args = [ '$SCONSARGS' ]
305         if env.GetLaunchDir() != os.getcwd():
306             args.append('-u')
307         env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
308                         HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)
309
310 env.PhonyTarget('clean', [], [
311         lambda **args: sys.stderr.write(
312             "=================================================================\n"
313             "'clean' is not a valid target, use the '-c' option instead:\n"
314             "    $ scons -c all\n"
315             "=================================================================\n") ])