Move boost/intrusive to senf/boost_intrusive
[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    ENV                    = { 'PATH' : os.environ.get('PATH'), 
63                               'HOME' : os.environ.get('HOME'),
64                               'SSH_AGENT_PID': os.environ.get('SSH_AGENT_PID'),
65                               'SSH_AUTH_SOCK': os.environ.get('SSH_AUTH_SOCK') },
66    CLEAN_PATTERNS         = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*',
67                               '.sconf_temp' ],
68
69    BUILDDIR               = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
70    CPPPATH                = [ '#', '$BUILDDIR',
71                               '${NEED_BOOST_EXT and "#/boost_ext" or None}' ],
72    LOCALLIBDIR            = '$BUILDDIR',
73    LIBPATH                = [ '$LOCALLIBDIR' ],
74    LIBS                   = [ '$EXTRA_LIBS' ],
75    EXTRA_LIBS             = [ 'rt' ],
76    TEST_EXTRA_LIBS        = [  ],
77    VALGRINDARGS           = [ '--num-callers=50' ],
78
79    PREFIX                 = '#/dist',
80    LIBINSTALLDIR          = '$PREFIX${syslayout and "/lib" or ""}',
81    BININSTALLDIR          = '$PREFIX${syslayout and "/bin" or ""}',
82    INCLUDEINSTALLDIR      = '$PREFIX${syslayout and "/include" or ""}',
83    CONFINSTALLDIR         = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
84    OBJINSTALLDIR          = '$CONFINSTALLDIR',
85    DOCINSTALLDIR          = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
86    SCONSINSTALLDIR        = '$CONFINSTALLDIR/site_scons',
87
88    CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
89    CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
90
91    # These options are insane. Only useful for inline debugging. Need at least 1G free RAM
92    INLINE_OPTS_DEBUG      = [ '-finline-limit=20000', '-fvisibility-inlines-hidden', 
93                               '-fno-inline-functions', '-Winline' 
94                               '--param','large-function-growth=10000',
95                               '--param', 'large-function-insns=10000', 
96                               '--param','inline-unit-growth=10000' ],
97    INLINE_OPTS_NORMAL     = [ '-finline-limit=5000' ],
98    INLINE_OPTS            = [ '$INLINE_OPTS_NORMAL' ],
99    CXXFLAGS               = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
100                               '-pipe', '$CXXFLAGS_', '-fno-strict-aliasing' ],
101    CXXFLAGS_              = senfutil.BuildTypeOptions('CXXFLAGS'),
102    CXXFLAGS_final         = [ '-O3' ],
103    CXXFLAGS_normal        = [ '-O2', '-g' ],
104    CXXFLAGS_debug         = [ '-O0', '-g' ],
105
106    CPPDEFINES             = [ '$expandLogOption', '$CPPDEFINES_' ],
107    expandLogOption        = senfutil.expandLogOption,
108    CPPDEFINES_            = senfutil.BuildTypeOptions('CPPDEFINES'),
109    CPPDEFINES_final       = [ ],
110    CPPDEFINES_normal      = [ 'SENF_DEBUG' ],
111    CPPDEFINES_debug       = [ '$CPPDEFINES_normal' ],
112
113    LINKFLAGS              = [ '-rdynamic', '$LINKFLAGS_' ],
114    LINKFLAGS_             = senfutil.BuildTypeOptions('LINKFLAGS'),
115    LINKFLAGS_final        = [ ],
116    LINKFLAGS_normal       = [ '-Wl,-S' ],
117    LINKFLAGS_debug        = [ '-g' ],
118 )
119
120 # Add all UNIX env vars starting with 'SENF' to the execution environment
121 env.Append( ENV = dict(((k,v) for k,v in os.environ.iteritems() if k.startswith("SENF"))) )
122
123 env.SetDefault(
124     LIBSENF           = "senf",
125     LCOV              = "lcov",
126     GENHTML           = "genhtml",
127     VALGRIND          = "valgrind",
128     SCONSBIN          = env.File("#/tools/scons"),
129     SCONSARGS         = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ] + \
130         [ '%s=%s' % (k,v) for k,v in ARGUMENTS.iteritems() ],
131     SCONS             = "@$SCONSBIN $SCONSARGS",
132     CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
133     TOPDIR            = env.Dir('#').abspath,
134     LIBADDSUFFIX      = '${FLAVOR and "_$FLAVOR" or ""}',
135     OBJADDSUFFIX      = '${LIBADDSUFFIX}',
136     FLAVOR            = '',
137 )
138
139 # Set variables from command line
140 senfutil.parseArguments(
141     env,
142     BoolVariable('final', 'Build final (optimized) build', False),
143     BoolVariable('debug', 'Link in debug symbols', False),
144     BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
145     BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
146 )
147
148 if 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
149     import SparseTestHack
150     env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
151
152 if env.has_key('only_tests') : env['sparse_tests'] = True
153 Export('env')
154
155 ###########################################################################
156 # Configure
157
158 @senfconf.Test
159 def CheckValgrind(context):
160     context.Message( "Checking for valgrind... " )
161     ret = context.TryAction(['$VALGRIND --version >$TARGET'])
162     context.Result( ret[1].strip() or False )
163     return ret[0]
164
165 @senfconf.Test
166 def CheckValgrindWildcards(context):
167     context.Message( "Checking whether valgrind supports '...' wildcards in suppressions... " )
168     ret = context.TryAction(['$VALGRIND --suppressions=$SOURCE /bin/true'],
169                             "{\n test_suppression\n Memcheck:Addr4\n ...\n fun:foo\n}\n",
170                             ".sup")
171     context.Result( ret[0] )
172     return ret[0]
173
174 # Default configuration (boost stuff)
175 senfutil.Configure(env)
176
177 conf = env.Configure(clean=False, help=False, custom_tests = senfconf.Tests())
178 env.Replace(
179     HAVE_VALGRIND  = conf.CheckValgrind() and conf.CheckValgrindWildcards()
180 )
181 conf.Finish()
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/senfconf.py',
227                                     'site_scons/yaptu.py' ])
228 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
229                FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
230 env.Install('${INCLUDEINSTALLDIR}', 'boost_ext')
231 env.Install('${INCLUDEINSTALLDIR}/senf', 'senf/boost_intrusive')
232
233 env.Alias('install_all', env.FindInstalledFiles())
234 env.Alias('default', DEFAULT_TARGETS)
235 env.Alias('all_tests', env.FindAllBoostUnitTests())
236 env.Alias('test_changes', 'all_tests')
237 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
238
239 #### prepare
240 env.PhonyTarget('prepare', [], [])
241
242 #### valgrind
243 env.Alias('all_valgrinds')
244 if env['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 debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
261         '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
262         '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
263         '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
264         'rm /tmp/senf_lcov.info' ])
265 if env.GetOption('clean'): 
266     env.Clean('lcov', [ os.path.join(path,f)
267                         for path, subdirs, files in os.walk('.')
268                         for pattern in ('*.gcno', '*.gcda', '*.gcov')
269                         for f in fnmatch.filter(files,pattern) ] + 
270                       [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
271     
272 #### clean
273 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
274 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
275
276 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
277     env.Clean('all', [ os.path.join(path,f)
278                        for path, subdirs, files in os.walk('.')
279                        for pattern in env['CLEAN_PATTERNS']
280                        for f in fnmatch.filter(files,pattern) ])
281     # Disable writing to the deleted .sconsign file
282     import SCons.SConsign
283     SCons.SConsign.write = lambda : None
284
285 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
286     Execute(Touch(".prepare-stamp"))
287
288 ### execute targets on remote hosts
289 for target in COMMAND_LINE_TARGETS:
290     if '@' in target:
291         realtarget, host = target.split('@',1)
292         cwd=env.GetLaunchDir()
293         home=os.environ['HOME']+'/'
294         if cwd.startswith(home) : cwd = cwd[len(home):]
295         args = [ '$SCONSARGS' ]
296         if env.GetLaunchDir() != os.getcwd():
297             args.append('-u')
298         env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
299                         HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)