Fix LIBADDSUFFIX support
[senf.git] / SConstruct
1 # -*- python -*-
2
3 import sys, glob, os.path, datetime, pwd, time, fnmatch, string
4 sys.path.append(Dir('#/senfscons').abspath)
5 sys.path.append(Dir('#/doclib').abspath)
6 import SENFSCons
7
8 ###########################################################################
9
10 # This hack is needed for SCons V 0.96.1 compatibility. In current SCons versions
11 # we can just use 'env.AlwaysBuild(env.Alias(target), [], action)'
12 def PhonyTarget(env, target, action, sources=[]):
13     env.AlwaysBuild(env.Command(target + '.phony', [ 'SConstruct' ] + sources, env.Action(action)))
14     env.Alias(target, target + '.phony')
15
16 def updateRevision(target, source, env):
17     rev = env['ENV']['REVISION'][1:]
18     if ':' in rev:
19         print
20         print "Working copy not clean. Run 'svn update'"
21         print
22         return 1
23     if 'm' in rev and not ARGUMENTS.get('force_deb'):
24         print
25         print "Working copy contains local changes. Commit first"
26         print
27         return 1
28     if 's' in rev:
29         rev = rev[:-1]
30     if 'm' in rev:
31         rev = rev[:-1]
32     url = None
33     for line in os.popen("svn info"):
34         elts=line.split(':',1)
35         if elts[0] == 'URL':
36             url = elts[1].strip()
37     version = None
38     if '/tags/' in url:
39         version = url.rsplit('/',1)[-1].split('_',1)[0]
40         if version[0] not in string.digits:
41             version = None
42     if version is None:
43         version = '1:0r%s' % rev
44     changelog = file('debian/changelog.template').read() % {
45         'version': version,
46         'rev': rev,
47         'user': pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0].strip(),
48         'date': time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) }
49     file('debian/changelog','w').write(changelog)
50
51 def nonemptyFile(f):
52     try: return os.stat(f).st_size > 0
53     except OSError: return False
54
55 def checkLocalConf(target, source, env):
56     if [ True for f in env['LOCAL_CONFIG_FILES'] if nonemptyFile(f) ]:
57         print
58         print "You have made local modifications to one of the following local configuration"
59         print "files:"
60         for f in env['LOCAL_CONFIG_FILES']:
61             print "    ",f
62         print
63         print "Building a debian package would remove those files."
64         print
65         print "To continue, remove the offending file(s) and try again. Alternatively,"
66         print "build a source package using 'scons debsrc' and may then build debian"
67         print "binary packages from this source-package without disrupting your local"
68         print "configuration."
69         print
70         return 1
71
72 ###########################################################################
73 # Load utilities and setup libraries and configure build
74
75 SENFSCons.UseBoost()
76 SENFSCons.UseSTLPort()
77 env = SENFSCons.MakeEnvironment()
78
79 env.Help("""
80 Additional top-level build targets:
81
82 prepare      Create all source files not part of the repository
83 all_tests    Build and run unit tests for all modules
84 all_docs     Build documentation for all modules
85 all          Build everything
86 install_all  Install SENF into $PREFIX
87 deb          Build debian source and binary package
88 debsrc       Build debian source package
89 debbin       Build debian binary package
90 linklint     Check links of doxygen documentation with 'linklint'
91 fixlinks     Fix broken links in doxygen documentation
92 valgrind     Run all tests under valgrind/memcheck
93 """)
94
95 if os.environ.get('debian_build'):
96     rev = os.popen("dpkg-parsechangelog | awk '/^Version:/{print $2}'").read().strip()
97 else:
98     rev = 'r' + os.popen("svnversion").read().strip().lower()
99
100 logname = os.environ.get('LOGNAME')
101 if not logname:
102     logname = pwd.getpwuid(os.getuid()).pw_name
103
104 def dpkgIgnoredFilesOpts(target, source, env, for_signature):
105     return [ '-I%s' % os.path.split(f)[1] for f in env.subst('$DPKG_IGNORED_FILES').split() ]
106
107 # Options used to debug inlining:
108 #
109 # INLINE_OPTS = [ '-finline-limit=20000', '--param','large-function-growth=10000',
110 #                 '--param', 'large-function-insns=10000', '--param','inline-unit-growth=10000',
111 #                 '-fvisibility-inlines-hidden', '-fno-inline-functions', '-Winline' ]
112 #
113 # BEWARE: You need lots of ram to compile with these settings (approx 1G)
114 #
115
116 INLINE_OPTS = [ '-finline-limit=5000' ]
117
118 env.Append(
119    CPPPATH = [ '#/include' ],
120    CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long' ] + INLINE_OPTS,
121    LIBS = [ 'readline', 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB' ],
122    TEST_EXTRA_LIBS = [ '$BOOSTFSLIB' ],
123    DOXY_XREF_TYPES = [ 'bug', 'fixme', 'todo', 'idea' ],
124    DOXY_HTML_XSL = '#/doclib/html-munge.xsl',
125    ENV = { 'TODAY' : str(datetime.date.today()),
126            'REVISION' : rev,
127            'LOGNAME' : logname, # needed by the debian build scripts
128            'CONCURRENCY_LEVEL' : env.GetOption('num_jobs') or "1",
129            'SCONS' : 1,
130            'PATH' : os.environ.get('PATH')
131          },
132    LOCAL_CONFIG_FILES = [ 'Doxyfile.local', 'SConfig', 'local_config.hh' ],
133    DPKG_IGNORED_FILES = [ '$LOCAL_CONFIG_FILES', '.svn', '_templates' ],
134    DPKG_IGNORED_FILES_OPTS = dpkgIgnoredFilesOpts,
135    CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign', '.sconsign.dblite' ],
136    BUILDPACKAGE_COMMAND = "dpkg-buildpackage -us -uc -rfakeroot $DPKG_IGNORED_FILES_OPTS",
137    TOP_INCLUDES = [ 'Packets', 'PPI', 'Scheduler', 'Socket', 'Utils', 'Console',
138                     'config.hh', 'local_config.hh' ],
139 )
140
141 def parseLogOption(value):
142     stream, area, level = ( x.strip() for x in value.strip().split('|') )
143     if stream  : stream = ''.join('(%s)' % x for x in stream.split('::') )
144     else       : stream = '(_)'
145     if area : area = ''.join( '(%s)' % x for x in area.split('::') )
146     else    : area = '(_)'
147     return '(( %s,%s,%s ))' % (stream,area,level)
148
149 def expandLogOption(target, source, env, for_signature):
150     return ' '.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )
151
152 if env.subst('$LOGLEVELS'):
153     env.Append( expandLogOption=expandLogOption )
154     env.Append( CPPDEFINES = { 'SENF_LOG_CONF': '$expandLogOption' } )
155
156 env.SetDefault(
157        LIBSENF = "senf"
158 )
159
160 Export('env')
161
162 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
163 # Create it even when cleaning, to silence the doxygen builder warnings
164 if not os.path.exists("Doxyfile.local"):
165     Execute(Touch("Doxyfile.local"))
166
167 # Create local_config.h
168 if not env.GetOption('clean') and not os.path.exists("local_config.hh"):
169     Execute(Touch("local_config.hh"))
170
171 ###########################################################################
172 # Define build targets
173
174 # Before defining any targets, check wether this is the first build in
175 # pristine directory tree. If so, call 'scons prepare' so the dependencies
176 # created later are correct
177
178 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
179    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
180     env.Execute([ "scons prepare" ])
181
182 env.Clean('all', '.prepare-stamp')
183
184 # Not nice, but until we get to fixing the dependency jungle
185 # concerning generated sources ...
186 scripts = []
187 dependencies = []
188
189 SConscript(glob.glob("*/SConscript"))
190
191 SENFSCons.StandardTargets(env)
192 SENFSCons.GlobalTargets(env)
193 SENFSCons.Doxygen(env)
194 SENFSCons.DoxyXRef(env,
195                    HTML_HEADER = '#/doclib/doxy-header.html',
196                    HTML_FOOTER = '#/doclib/doxy-footer.html')
197
198 SENFSCons.InstallIncludeFiles(env, [ 'config.hh' ])
199
200 # Build combined library 'libsenf'
201 libsenf = env.Library(env.subst("$LIBSENF$LIBADDSUFFIX"), env['ALLOBJECTS'])
202 env.Default(libsenf)
203 env.Clean('all', libsenf)
204 env.Alias('default', libsenf)
205
206 env.Alias('install_all', env.Install('$LIBINSTALLDIR', libsenf))
207
208 if env.GetOption('clean'):
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
214 PhonyTarget(env, 'deb', [
215     checkLocalConf,
216     updateRevision,
217     "$BUILDPACKAGE_COMMAND",
218     "fakeroot ./debian/rules debclean"
219 ])
220
221 PhonyTarget(env, 'debsrc', [
222     updateRevision,
223     "$BUILDPACKAGE_COMMAND -S",
224 ])
225
226 PhonyTarget(env, 'debbin', [
227     checkLocalConf,
228     updateRevision,
229     "$BUILDPACKAGE_COMMAND -b",
230     "fakeroot ./debian/rules debclean"
231 ])
232
233 PhonyTarget(env, 'linklint', [
234     'rm -rf linklint',
235     'linklint -doc linklint -limit 99999999 `find -type d -name html -printf "/%P/@ "`',
236     '[ ! -r linklint/errorX.html ] || python doclib/linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
237     '[ ! -r linklint/errorX.html.new ] || mv linklint/errorX.html.new linklint/errorX.html',
238     '[ ! -r linklint/errorAX.html ] || python doclib/linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
239     '[ ! -r linklint/errorAX.html.new ] || mv linklint/errorAX.html.new linklint/errorAX.html',
240     'echo -e "\\nLokal link check results: linklint/index.html\\nRemote link check results: linklint/urlindex.html\\n"',
241 ])
242
243 PhonyTarget(env, 'fixlinks', [
244     'python doclib/fix-links.py -v -s .svn -s linklint -s debian linklint/errorX.txt linklint/errorAX.txt',
245 ])
246
247 PhonyTarget(env, 'prepare', [])
248
249 PhonyTarget(env, 'valgrind', [
250     'find -name .test.bin | while read test; do echo; echo "Running $$test"; echo; valgrind --tool=memcheck --error-exitcode=99 --suppressions=valgrind.sup $$test $BOOSTTESTARGS; [ $$? -ne 99 ] || exit 1; done'
251     ], [ 'all_tests' ])
252
253 env.Clean('all', env.Dir('linklint'))
254
255 env.Clean('all','.prepare-stamp')
256 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
257     Execute(Touch(".prepare-stamp"))