Fix last test_changes commit ...
[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 'test_changes' in COMMAND_LINE_TARGETS and not env.has_key('only_tests'):
123     import SparseTestHack
124     env['only_tests'] = " ".join(x.abspath for x in SparseTestHack.findSCMChanges(env))
125
126 if env.has_key('only_tests') : env['sparse_tests'] = True
127 Export('env')
128
129 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
130 # Create it even when cleaning, to silence the doxygen builder warnings
131 if not os.path.exists("doclib/Doxyfile.local"):
132     Execute(Touch("doclib/Doxyfile.local"))
133
134 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
135    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
136     env.Execute([ "$SCONS prepare" ])
137
138 # Load SConscripts
139
140 SConscriptChdir(0)
141 SConscript("debian/SConscript")
142 SConscriptChdir(1)
143 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
144 if env['sparse_tests']:
145     import SparseTestHack
146     SparseTestHack.setup(env)
147 if env.subst('$BUILDDIR') == '#':
148     SConscript("SConscript")
149 else:
150     SConscript("SConscript", variant_dir=env.subst('$BUILDDIR'), src_dir='#', duplicate=False)
151 SConscript("Examples/SConscript")
152 SConscript("HowTos/SConscript")
153 SConscript("doclib/SConscript")
154 if env['sparse_tests']:
155     SparseTestHack.build(env, 'test_changes' in COMMAND_LINE_TARGETS)
156
157 ###########################################################################
158 # Define build targets
159
160 #### install_all, default, all_tests, all
161 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
162                                     'site_scons/senfutil.py',
163                                     'site_scons/yaptu.py' ])
164 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
165                FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
166 env.Install('${INCLUDEINSTALLDIR}', 'boost')
167
168 env.Alias('install_all', env.FindInstalledFiles())
169 env.Alias('default', DEFAULT_TARGETS)
170 env.Alias('all_tests', env.FindAllBoostUnitTests())
171 env.Alias('test_changes', 'all_tests')
172 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
173
174 #### prepare
175 env.PhonyTarget('prepare', [], [])
176
177 #### valgrind
178 for test in env.FindAllBoostUnitTests():
179     stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'), 
180                         [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
181                         [ """valgrind --tool=memcheck 
182                                       --error-exitcode=99 
183                                       --suppressions=${SOURCES[1]}
184                                       $VALGRINDARGS
185                                           ${SOURCES[0]} $BOOSTTESTARGS;
186                              [ $$? -ne 99 ] || exit 1""".replace("\n"," "),
187                           Touch("$TARGET") ])
188     alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
189     env.Alias('all_valgrinds', alias)
190
191 ### lcov
192 env.PhonyTarget('lcov', [], [
193         '$SCONS debug=1 BUILDDIR="#/build/lcov" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
194         '$LCOV --follow --directory $TOPDIR/build/lcov/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
195         '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
196         '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
197         'rm /tmp/senf_lcov.info' ])
198 if env.GetOption('clean'): 
199     env.Clean('lcov', [ os.path.join(path,f)
200                         for path, subdirs, files in os.walk('.')
201                         for pattern in ('*.gcno', '*.gcda', '*.gcov')
202                         for f in fnmatch.filter(files,pattern) ] + 
203                       [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('build/lcov') ])
204     
205 #### clean
206 env.Clean('all', ('.prepare-stamp', env.Dir('dist'), env.Dir('build')))
207 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
208
209 if env.GetOption('clean') and 'all' in BUILD_TARGETS:
210     env.Clean('all', [ os.path.join(path,f)
211                        for path, subdirs, files in os.walk('.')
212                        for pattern in env['CLEAN_PATTERNS']
213                        for f in fnmatch.filter(files,pattern) ])
214     # Disable writing to the deleted .sconsign file
215     import SCons.SConsign
216     SCons.SConsign.write = lambda : None
217
218 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
219     Execute(Touch(".prepare-stamp"))
220
221 ### execute targets on remote hosts
222 for target in COMMAND_LINE_TARGETS:
223     if '@' in target:
224         realtarget, host = target.split('@',1)
225         cwd=env.GetLaunchDir()
226         home=os.environ['HOME']+'/'
227         if cwd.startswith(home) : cwd = cwd[len(home):]
228         args = [ '$SCONSARGS' ]
229         if env.GetLaunchDir() != os.getcwd():
230             args.append('-u')
231         env.PhonyTarget(target, [], [ "ssh $HOST scons $SCONSARGS -C $DIR $RTARGET" ],
232                         HOST=host, RTARGET=realtarget, DIR=cwd, SCONSARGS=args)