Implement partial BUILDDIR 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 env.Tool('Doxygen')
16 env.Tool('Dia2Png')
17 env.Tool('PkgDraw')
18 env.Tool('InstallSubdir')
19 env.Tool('CopyToDir')
20 env.Tool('Boost')
21 env.Tool('CombinedObject')
22 env.Tool('PhonyTarget')
23 env.Tool('InstallDir')
24 env.Tool('CreateFile')
25
26 env.Help("""
27 Additional top-level build targets:
28
29 prepare        Create all target files not part of the repository
30 default        Build all default targets (like calling scons with no arguments)
31 examples       Build all examples
32 all_tests      Build and run unit tests for all modules
33 all_docs       Build documentation for all modules
34 all            Build everything
35 install_all    Install SENF into $$PREFIX
36 deb            Build debian source and binary package
37 debsrc         Build debian source package
38 debbin         Build debian binary package
39 linklint       Check links of doxygen documentation with 'linklint'
40 fixlinks       Fix broken links in doxygen documentation
41 all_valgrinds  Run all tests under valgrind/memcheck
42 lcov           Generate test coverage output in doc/lcov and lcov.info
43 """)
44
45 env.Append(
46    ENV                    = { 'PATH' : os.environ.get('PATH'), 'HOME' : os.environ.get('HOME') },
47    CLEAN_PATTERNS         = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*' ],
48
49    BUILDDIR               = '#',
50    CPPPATH                = [ '$BUILDDIR', '#' ],
51    LOCALLIBDIR            = '$BUILDDIR',
52    LIBPATH                = [ '$LOCALLIBDIR' ],
53    LIBS                   = [ '$LIBSENF$LIBADDSUFFIX', 'rt', '$BOOSTREGEXLIB', 
54                               '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', '$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     SCONS             = "@$SCONSBIN -Q -j$CONCURRENCY_LEVEL",
105     CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
106     TOPDIR            = env.Dir('#').abspath,
107 )
108
109 # Set variables from command line
110 senfutil.parseArguments(
111     env,
112     BoolVariable('final', 'Build final (optimized) build', False),
113     BoolVariable('debug', 'Link in debug symbols', False),
114     BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
115 )
116
117 Export('env')
118
119 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
120 # Create it even when cleaning, to silence the doxygen builder warnings
121 if not os.path.exists("doclib/Doxyfile.local"):
122     Execute(Touch("doclib/Doxyfile.local"))
123
124 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
125    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
126     env.Execute([ "$SCONS prepare" ])
127
128 # Load SConscripts
129
130 SConscriptChdir(0)
131 SConscript("debian/SConscript")
132 SConscriptChdir(1)
133 if os.path.exists('SConscript.local') : SConscript('SConscript.local')
134 if env['BUILDDIR'] == '#':
135     SConscript("SConscript")
136 else:
137     SConscript("SConscript", variant_dir=env['BUILDDIR'], src_dir='#', duplicate=False)
138 SConscript("Examples/SConscript")
139 SConscript("HowTos/SConscript")
140 SConscript("doclib/SConscript")
141
142 ###########################################################################
143 # Define build targets
144
145 #### install_all, default, all_tests, all
146 env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
147                                     'site_scons/senfutil.py',
148                                     'site_scons/yaptu.py' ])
149 env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
150                FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
151 env.Install('${INCLUDEINSTALLDIR}', 'boost')
152
153 env.Alias('install_all', env.FindInstalledFiles())
154 env.Alias('default', DEFAULT_TARGETS)
155 env.Alias('all_tests', env.FindAllBoostUnitTests())
156 env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
157
158 #### prepare
159 env.PhonyTarget('prepare', [], [])
160
161 #### valgrind
162 for test in env.FindAllBoostUnitTests():
163     stamp = env.Command(test[0].dir.File('.test-valgrind.stamp'), 
164                         [ test[0].dir.File('.test.bin'), 'tools/valgrind.sup' ],
165                         [ """valgrind --tool=memcheck 
166                                       --error-exitcode=99 
167                                       --suppressions=${SOURCES[1]}
168                                       $VALGRINDARGS
169                                           ${SOURCES[0]} $BOOSTTESTARGS;
170                              [ $$? -ne 99 ] || exit 1""".replace("\n"," "),
171                           Touch("$TARGET") ])
172     alias = env.Command(test[0].dir.File('valgrind'), stamp, [ env.NopAction() ])
173     env.Alias('all_valgrinds', alias)
174
175 ### lcov
176 env.PhonyTarget('lcov', [], [
177         '$SCONS debug=1 BUILDDIR="#/lcov-build" CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
178         '$LCOV --follow --directory $TOPDIR/lcov-build/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
179         '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
180         '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
181         'rm /tmp/senf_lcov.info' ])
182 if env.GetOption('clean'): 
183     env.Clean('lcov', [ os.path.join(path,f)
184                         for path, subdirs, files in os.walk('.')
185                         for pattern in ('*.gcno', '*.gcda', '*.gcov')
186                         for f in fnmatch.filter(files,pattern) ] + 
187                       [ 'lcov.info', env.Dir('doc/lcov'), env.Dir('lcov-build') ])
188     
189 #### clean
190 env.Clean('all', ('.prepare-stamp', env.Dir('dist')))
191 if env.GetOption('clean') : env.Depends('all', ('lcov', 'all_valgrinds'))
192
193 if env.GetOption('clean'):
194     env.Clean('all', [ os.path.join(path,f)
195                        for path, subdirs, files in os.walk('.')
196                        for pattern in env['CLEAN_PATTERNS']
197                        for f in fnmatch.filter(files,pattern) ])
198
199 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
200     Execute(Touch(".prepare-stamp"))