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