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