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