PPI: introduced SENF_PPI_NOTRACE define
[senf.git] / SConstruct
1 # -*- python -*-
2
3 import sys, os.path, fnmatch
4 import SENFSCons, senfutil, senfconf
5
6
7 ###########################################################################
8 # Load utilities and setup libraries and configure build
9
10 env = Environment()
11
12 env.Decider('MD5-timestamp')
13 env.EnsureSConsVersion(1,2)
14
15 # Load all the local SCons tools
16 senfutil.loadTools(env)
17
18 env.Help("""
19 Additional top-level build targets:
20
21 prepare         Create all target files not part of the repository
22 default         Build all default targets (like calling scons with no arguments)
23 examples        Build all examples
24 all_tests       Build and run unit tests for all modules
25 test_changes    Build tests only for files with local changes (queries svn or git)
26 all_docs        Build documentation for all modules
27 all             Build everything
28 install_all     Install SENF into $$PREFIX
29 deb             Build debian source and binary package
30 debsrc          Build debian source package
31 debbin          Build debian binary package
32 linklint        Check links of doxygen documentation with 'linklint'
33 fixlinks        Fix broken links in doxygen documentation
34 all_valgrinds   Run all tests under valgrind/memcheck
35 lcov            Generate test coverage output in doc/lcov and lcov.info
36
37 You may execute targets on a remote host (if the directory layout is the same)
38 by calling
39
40     scons <target>@[<user>@]<host>
41
42 Some more elaborate unit tests may be enabled by setting appropritate variables 
43 in the shell (unix) environment
44
45 SENF_TIMING_CRITICAL_TESTS
46                 Enables unit tests which depend on timing measurements. These
47                 unit tests should only be run on a single core and an otherwise
48                 idle system.
49
50 SENF_WLAN_TEST_INTERFACE
51                 WLAN interface to use for testing. The interface should not be
52                 actively in use.
53
54 SENF_ETH_TEST_INTERFACE
55                 Ethernet interface to use for testing. The interface should not
56                 be actively in use.
57
58 Some unit tests will only run when executed to 'root'.
59 """)
60
61 env.Append(
62    IMPORT_ENV             = [ 'PATH', 'HOME', 'SSH_*', 'SENF*', 'CCACHE_*', 'DISTCC_*' ],
63
64    CLEAN_PATTERNS         = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*',
65                               '.sconf_temp' ],
66
67    BUILDDIR               = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
68    CPPPATH                = [ '#', '$BUILDDIR',
69                               '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
70    LOCALLIBDIR            = '$BUILDDIR',
71    LIBPATH                = [ '$LOCALLIBDIR' ],
72    LIBS                   = [ '$EXTRA_LIBS' ],
73    EXTRA_LIBS             = [ 'rt' ],
74    TEST_EXTRA_LIBS        = [  ],
75    VALGRINDARGS           = [ '--num-callers=50' ],
76
77    PREFIX                 = '#/dist',
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',
85
86    CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
87    CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
88
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',
98                               '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ],
99    CXXFLAGS_              = senfutil.BuildTypeOptions('CXXFLAGS'),
100    CXXFLAGS_final         = [ '-O3' ],
101    CXXFLAGS_normal        = [ '-O2', '-g' ],
102    CXXFLAGS_debug         = [ '-O0', '-g' ],
103
104    CPPDEFINES             = [ '$expandLogOption', '$CPPDEFINES_' ],
105    expandLogOption        = senfutil.expandLogOption,
106    CPPDEFINES_            = senfutil.BuildTypeOptions('CPPDEFINES'),
107    CPPDEFINES_final       = [ 'SENF_PPI_NOTRACE'],
108    CPPDEFINES_normal      = [ 'SENF_DEBUG' ],
109    CPPDEFINES_debug       = [ '$CPPDEFINES_normal' ],
110
111    LINKFLAGS              = [ '-rdynamic', '$LINKFLAGS_' ],
112    LINKFLAGS_             = senfutil.BuildTypeOptions('LINKFLAGS'),
113    LINKFLAGS_final        = [ ],
114    LINKFLAGS_normal       = [ '-Wl,-S' ],
115    LINKFLAGS_debug        = [ '-g' ],
116 )
117
118 env.SetDefault(
119     LIBSENF           = "senf",
120     LCOV              = "lcov",
121     GENHTML           = "genhtml",
122     VALGRIND          = "valgrind",
123     SCONSBIN          = env.File("#/tools/scons"),
124     SCONSARGS         = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
125         [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
126     SCONS             = "@$SCONSBIN $SCONSARGS",
127     CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
128     TOPDIR            = env.Dir('#').abspath,
129     LIBADDSUFFIX      = '${FLAVOR and "_$FLAVOR" or ""}',
130     OBJADDSUFFIX      = '${LIBADDSUFFIX}',
131     FLAVOR            = '',
132 )
133
134 # Set variables from command line
135 senfutil.parseArguments(
136     env,
137     BoolVariable('final', 'Build final (optimized) build', False),
138     BoolVariable('debug', 'Link in debug symbols', False),
139     BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
140     BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
141 )
142
143 # Add UNIX env vars matching IMPORT_ENV patterns into the execution environment
144 env.Append( ENV = dict(( (k,v) 
145                          for pattern in env['IMPORT_ENV']
146                          for k,v in os.environ.iteritems()
147                          if fnmatch.fnmatchcase(k,pattern) )) )
148
149 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
150     import SparseTestHack
151     env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
152
153 if env.has_key('only_tests') : env['sparse_tests'] = True
154 Export('env')
155
156 ###########################################################################
157 # Configure
158
159 @senfconf.Test
160 def CheckValgrind(context):
161     context.Message( "Checking for valgrind... " )
162     ret = context.TryAction(['$VALGRIND --version >$TARGET'])
163     context.Result( ret[1].strip() or False )
164     return ret[0]
165
166 @senfconf.Test
167 def CheckValgrindWildcards(context):
168     context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
169     ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'],
170                             "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
171                             ".sup")
172     context.Result( ret[0] )
173     return ret[0]
174
175 # Default configuration (boost stuff)
176 senfutil.Configure(env)
177
178 conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
179 env.Replace(
180     HAVE_VALGRIND  = conf.CheckValgrind() and conf.CheckValgrindWildcards()
181 )
182 conf.Finish()
183
184 # Only add this here, after all configure checks have run
185
186 env.Append(LIBS = '$LIBSENF$LIBADDSUFFIX',
187            EXTRA_LIBS = [ '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', 
188                           '$BOOSTFSLIB' ])
189
190 ###########################################################################
191
192 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
193 # Create it even when cleaning, to silence the doxygen builder warnings
194 if not os.path.exists("doclib/Doxyfile.local"):
195     Execute(Touch("doclib/Doxyfile.local"))
196
197 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
198    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
199     env.Execute([ "$SCONS prepare" ])
200
201 # Load SConscripts
202
203 SConscriptChdir(0)
204 SConscript("debian/SConscript")
205 SConscriptChdir(1)
206 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
207 if env['sparse_tests']:
208     import SparseTestHack
209     SparseTestHack.setup(env)
210 if env.subst('$BUILDDIR') == '#':
211     SConscript("SConscript")
212 else:
213     SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
214 SConscript("Examples/SConscript")
215 SConscript("HowTos/SConscript")
216 SConscript("doclib/SConscript")
217 if env['sparse_tests']:
218     verbose = 'test_changes' in COMMAND_LINE_TARGETS
219     SparseTestHack.build(env, verbose, verbose)
220
221 ###########################################################################
222 # Define build targets
223
224 #### install_all, default, all_tests, all
225 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
226                                     'site_scons/senfutil.py',
227                                     'site_scons/senfconf.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
241 env.PhonyTarget('prepare', [], [])
242
243 #### valgrind
244 env.Alias('all_valgrinds')
245 if env['HAVE_VALGRIND']:
246     for test in env.FindAllBoostUnitTests():
247         stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'), 
248                             [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
249                             [ """$VALGRIND --tool=memcheck 
250                                           --error-exitcode=1
251                                           --suppressions=${SOURCES[1]}
252                                           $VALGRINDARGS
253                                               ${SOURCES[0]} --result_code=no $BOOSTTESTARGS
254                               """.replace("\n"," "),
255                               Touch("$TARGET") ])
256         alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
257         env.Alias('all_valgrinds', alias)
258
259 ### lcov
260 env.PhonyTarget('lcov', [], [
261         '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
262         '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
263         '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
264         '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
265         'rm /tmp/senf_lcov.info' ])
266 if env.GetOption('clean'): 
267     env.Clean('lcov', [ os.path.join(path,f)
268                         for path, subdirs, files in os.walk('.')
269                         for pattern in ('*.gcno', '*.gcda', '*.gcov')
270                         for f in fnmatch.filter(files,pattern) ] + 
271                       [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
272     
273 #### clean
274 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
275 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
276
277 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
278     env.Clean('all', [ os.path.join(path,f)
279                        for path, subdirs, files in os.walk('.')
280                        for pattern in env['CLEAN_PATTERNS']
281                        for f in fnmatch.filter(files,pattern) ])
282     # Disable writing to the deleted .sconsign file
283     import SCons.SConsign
284     SCons.SConsign.write = lambda : None
285
286 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
287     Execute(Touch(".prepare-stamp"))
288
289 ### execute targets on remote hosts
290 for target in COMMAND_LINE_TARGETS:
291     if '@' in target:
292         realtarget, host = target.split('@',1)
293         cwd=env.GetLaunchDir()
294         home=os.environ['HOME']+'/'
295         if cwd.startswith(home) : cwd = cwd[len(home):]
296         args = [ '$SCONSARGS' ]
297         if env.GetLaunchDir() != os.getcwd():
298             args.append('-u')
299         env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
300                         HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)