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