Add sparse_tests and only_tests build feature
[senf.git] / SConstruct
1 # -*- python -*-
2
3 import sys, os.path, fnmatch
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 all_docs       Build documentation for all modules
25 all            Build everything
26 install_all    Install SENF into $$PREFIX
27 deb            Build debian source and binary package
28 debsrc         Build debian source package
29 debbin         Build debian binary package
30 linklint       Check links of doxygen documentation with 'linklint'
31 fixlinks       Fix broken links in doxygen documentation
32 all_valgrinds  Run all tests under valgrind/memcheck
33 lcov           Generate test coverage output in doc/lcov and lcov.info
34
35 You may execute targets on a remote host (if the directory layout is the same)
36 by calling
37
38     scons <target>@[<user>@]<host>
39 """)
40
41 env.Append(
42    ENV                    = { 'PATH' : os.environ.get('PATH'), 
43                               'HOME' : os.environ.get('HOME'),
44                               'SSH_AGENT_PID': os.environ.get('SSH_AGENT_PID'),
45                               'SSH_AUTH_SOCK': os.environ.get('SSH_AUTH_SOCK') },
46    CLEAN_PATTERNS         = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*' ],
47
48    BUILDDIR               = '${FLAVOR and "#/build/$FLAVOR" or "#"}',
49    CPPPATH                = [ '$BUILDDIR', '#' ],
50    LOCALLIBDIR            = '$BUILDDIR',
51    LIBPATH                = [ '$LOCALLIBDIR' ],
52    LIBS                   = [ '$LIBSENF$LIBADDSUFFIX', '$EXTRA_LIBS' ],
53    EXTRA_LIBS             = [ 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', 
54                               '$BOOSTFSLIB' ], 
55    TEST_EXTRA_LIBS        = [  ],
56    VALGRINDARGS           = [ '--num-callers=50' ],
57
58    PREFIX                 = '#/dist',
59    LIBINSTALLDIR          = '$PREFIX${syslayout and "/lib" or ""}',
60    BININSTALLDIR          = '$PREFIX${syslayout and "/bin" or ""}',
61    INCLUDEINSTALLDIR      = '$PREFIX${syslayout and "/include" or ""}',
62    CONFINSTALLDIR         = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
63    OBJINSTALLDIR          = '$CONFINSTALLDIR',
64    DOCINSTALLDIR          = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
65    SCONSINSTALLDIR        = '$CONFINSTALLDIR/site_scons',
66
67    CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
68    CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
69
70    # These options are insane. Only useful for inline debugging. Need at least 1G free RAM
71    INLINE_OPTS_DEBUG      = [ '-finline-limit=20000', '-fvisibility-inlines-hidden', 
72                               '-fno-inline-functions', '-Winline' 
73                               '--param','large-function-growth=10000',
74                               '--param', 'large-function-insns=10000', 
75                               '--param','inline-unit-growth=10000' ],
76    INLINE_OPTS_NORMAL     = [ '-finline-limit=5000' ],
77    INLINE_OPTS            = [ '$INLINE_OPTS_NORMAL' ],
78    CXXFLAGS               = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
79                               '$CXXFLAGS_' ],
80    CXXFLAGS_              = senfutil.BuildTypeOptions('CXXFLAGS'),
81    CXXFLAGS_final         = [ '-O3' ],
82    CXXFLAGS_normal        = [ '-O0', '-g' ],
83    CXXFLAGS_debug         = [ '$CXXFLAGS_normal' ],
84
85    CPPDEFINES             = [ '$expandLogOption', '$CPPDEFINES_' ],
86    expandLogOption        = senfutil.expandLogOption,
87    CPPDEFINES_            = senfutil.BuildTypeOptions('CPPDEFINES'),
88    CPPDEFINES_final       = [ ],
89    CPPDEFINES_normal      = [ 'SENF_DEBUG' ],
90    CPPDEFINES_debug       = [ '$CPPDEFINES_normal' ],
91
92    LINKFLAGS              = [ '-rdynamic', '$LINKFLAGS_' ],
93    LINKFLAGS_             = senfutil.BuildTypeOptions('LINKFLAGS'),
94    LINKFLAGS_final        = [ ],
95    LINKFLAGS_normal       = [ '-Wl,-S' ],
96    LINKFLAGS_debug        = [ '-g' ],
97 )
98
99 env.SetDefault(
100     LIBSENF           = "senf",
101     LCOV              = "lcov",
102     GENHTML           = "genhtml",
103     SCONSBIN          = env.File("#/tools/scons"),
104     SCONSARGS          = [ '-Q', '-j$CONCURRENCY_LEVEL', 'debug=$debug', 'final=$final' ],
105     SCONS             = "@$SCONSBIN $SCONSARGS",
106     CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
107     TOPDIR            = env.Dir('#').abspath,
108     LIBADDSUFFIX      = '${FLAVOR and "_$FLAVOR" or ""}',
109     OBJADDSUFFIX      = '${LIBADDSUFFIX}',
110     FLAVOR            = '',
111 )
112
113 # Set variables from command line
114 senfutil.parseArguments(
115     env,
116     BoolVariable('final', 'Build final (optimized) build', False),
117     BoolVariable('debug', 'Link in debug symbols', False),
118     BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
119     BoolVariable('sparse_tests', 'Link tests against object files and not the senf lib', False)
120 )
121
122 if env.has_key('only_tests') : env['sparse_tests'] = True
123 Export('env')
124
125 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
126 # Create it even when cleaning, to silence the doxygen builder warnings
127 if not os.path.exists("doclib/Doxyfile.local"):
128     Execute(Touch("doclib/Doxyfile.local"))
129
130 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
131    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
132     env.Execute([ "$SCONS prepare" ])
133
134 # Load SConscripts
135
136 SConscriptChdir(0)
137 SConscript("debian/SConscript")
138 SConscriptChdir(1)
139 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
140 if env['sparse_tests']:
141     import SparseTestHack
142     SparseTestHack.setup(env)
143 if env.subst('$BUILDDIR') == '#':
144     SConscript("SConscript")
145 else:
146     SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
147 SConscript("Examples/SConscript")
148 SConscript("HowTos/SConscript")
149 SConscript("doclib/SConscript")
150 if env['sparse_tests']:
151     SparseTestHack.build(env)
152
153 ###########################################################################
154 # Define build targets
155
156 #### install_all, default, all_tests, all
157 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
158                                     'site_scons/senfutil.py',
159                                     'site_scons/yaptu.py' ])
160 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
161                FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
162 env.Install('${INCLUDEINSTALLDIR}', 'boost')
163
164 env.Alias('install_all', env.FindInstalledFiles())
165 env.Alias('default', DEFAULT_TARGETS)
166 env.Alias('all_tests', env.FindAllBoostUnitTests())
167 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
168
169 #### prepare
170 env.PhonyTarget('prepare', [], [])
171
172 #### valgrind
173 for test in env.FindAllBoostUnitTests():
174     stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'), 
175                         [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
176                         [ """valgrind --tool=memcheck 
177                                       --error-exitcode=99 
178                                       --suppressions=${SOURCES[1]}
179                                       $VALGRINDARGS
180                                           ${SOURCES[0]} $BOOSTTESTARGS;
181                              [ $$? -ne 99 ] || exit 1""".replace("\n"," "),
182                           Touch("$TARGET") ])
183     alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
184     env.Alias('all_valgrinds', alias)
185
186 ### lcov
187 env.PhonyTarget('lcov', [], [
188         '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
189         '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
190         '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
191         '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
192         'rm /tmp/senf_lcov.info' ])
193 if env.GetOption('clean'): 
194     env.Clean('lcov', [ os.path.join(path,f)
195                         for path, subdirs, files in os.walk('.')
196                         for pattern in ('*.gcno', '*.gcda', '*.gcov')
197                         for f in fnmatch.filter(files,pattern) ] + 
198                       [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
199     
200 #### clean
201 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
202 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
203
204 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
205     env.Clean('all', [ os.path.join(path,f)
206                        for path, subdirs, files in os.walk('.')
207                        for pattern in env['CLEAN_PATTERNS']
208                        for f in fnmatch.filter(files,pattern) ])
209     # Disable writing to the deleted .sconsign file
210     import SCons.SConsign
211     SCons.SConsign.write = lambda : None
212
213 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
214     Execute(Touch(".prepare-stamp"))
215
216 ### execute targets on remote hosts
217 for target in COMMAND_LINE_TARGETS:
218     if '@' in target:
219         realtarget, host = target.split('@',1)
220         cwd=env.GetLaunchDir()
221         home=os.environ['HOME']+'/'
222         if cwd.startswith(home) : cwd = cwd[len(home):]
223         args = [ '$SCONSARGS' ]
224         if env.GetLaunchDir() != os.getcwd():
225             args.append('-u')
226         env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
227                         HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)