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