Utils: Fix hexump() of nevative values
[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             '$BOOSTFSLIB' ],
123    TEST_EXTRA_LIBS = [ ],
124    DOXY_XREF_TYPES = [ 'bug', 'fixme', 'todo', 'idea' ],
125    DOXY_HTML_XSL = '#/doclib/html-munge.xsl',
126    ENV = { 'TODAY' : str(datetime.date.today()),
127            'REVISION' : rev,
128            'LOGNAME' : logname, # needed by the debian build scripts
129            'CONCURRENCY_LEVEL' : env.GetOption('num_jobs') or "1",
130            'SCONS' : 1,
131            'PATH' : os.environ.get('PATH')
132          },
133    LOCAL_CONFIG_FILES = [ 'Doxyfile.local', 'SConfig', 'local_config.hh' ],
134    DPKG_IGNORED_FILES = [ '$LOCAL_CONFIG_FILES', '.svn', '_templates' ],
135    DPKG_IGNORED_FILES_OPTS = dpkgIgnoredFilesOpts,
136    CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign', '.sconsign.dblite' ],
137    BUILDPACKAGE_COMMAND = "dpkg-buildpackage -us -uc -rfakeroot $DPKG_IGNORED_FILES_OPTS",
138    TOP_INCLUDES = [ 'Packets', 'PPI', 'Scheduler', 'Socket', 'Utils', 'Console',
139                     'config.hh', 'local_config.hh' ],
140 )
141
142 def parseLogOption(value):
143     stream, area, level = ( x.strip() for x in value.strip().split('|') )
144     if stream  : stream = ''.join('(%s)' % x for x in stream.split('::') )
145     else       : stream = '(_)'
146     if area : area = ''.join( '(%s)' % x for x in area.split('::') )
147     else    : area = '(_)'
148     return '(( %s,%s,%s ))' % (stream,area,level)
149
150 def expandLogOption(target, source, env, for_signature):
151     return ' '.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )
152
153 if env.subst('$LOGLEVELS'):
154     env.Append( expandLogOption=expandLogOption )
155     env.Append( CPPDEFINES = { 'SENF_LOG_CONF': '$expandLogOption' } )
156
157 env.SetDefault(
158        LIBSENF = "senf"
159 )
160
161 Export('env')
162
163 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
164 # Create it even when cleaning, to silence the doxygen builder warnings
165 if not os.path.exists("Doxyfile.local"):
166     Execute(Touch("Doxyfile.local"))
167
168 # Create local_config.h
169 if not env.GetOption('clean') and not os.path.exists("local_config.hh"):
170     Execute(Touch("local_config.hh"))
171
172 ###########################################################################
173 # Define build targets
174
175 # Before defining any targets, check wether this is the first build in
176 # pristine directory tree. If so, call 'scons prepare' so the dependencies
177 # created later are correct
178
179 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
180    and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
181     env.Execute([ "scons prepare" ])
182
183 env.Clean('all', '.prepare-stamp')
184
185 # Not nice, but until we get to fixing the dependency jungle
186 # concerning generated sources ...
187 scripts = []
188 dependencies = []
189
190 SConscript(glob.glob("*/SConscript"))
191
192 SENFSCons.StandardTargets(env)
193 SENFSCons.GlobalTargets(env)
194 SENFSCons.Doxygen(env)
195 SENFSCons.DoxyXRef(env,
196                    HTML_HEADER = '#/doclib/doxy-header.html',
197                    HTML_FOOTER = '#/doclib/doxy-footer.html')
198
199 SENFSCons.InstallIncludeFiles(env, [ 'config.hh' ])
200
201 # Build combined library 'libsenf'
202 libsenf = env.Library(env.subst("$LIBSENF$LIBADDSUFFIX"), env['ALLOBJECTS'])
203 env.Default(libsenf)
204 env.Clean('all', libsenf)
205 env.Alias('default', libsenf)
206
207 env.Alias('install_all', env.Install('$LIBINSTALLDIR', libsenf))
208
209 if env.GetOption('clean'):
210     env.Clean('all', [ os.path.join(path,f)
211                        for path, subdirs, files in os.walk('.')
212                        for pattern in env['CLEAN_PATTERNS']
213                        for f in fnmatch.filter(files,pattern) ])
214
215 PhonyTarget(env, 'deb', [
216     checkLocalConf,
217     updateRevision,
218     "$BUILDPACKAGE_COMMAND",
219     "fakeroot ./debian/rules debclean"
220 ])
221
222 PhonyTarget(env, 'debsrc', [
223     updateRevision,
224     "$BUILDPACKAGE_COMMAND -S",
225 ])
226
227 PhonyTarget(env, 'debbin', [
228     checkLocalConf,
229     updateRevision,
230     "$BUILDPACKAGE_COMMAND -b",
231     "fakeroot ./debian/rules debclean"
232 ])
233
234 PhonyTarget(env, 'linklint', [
235     'rm -rf linklint',
236     'linklint -doc linklint -limit 99999999 `find -type d -name html -printf "/%P/@ "`',
237     '[ ! -r linklint/errorX.html ] || python doclib/linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
238     '[ ! -r linklint/errorX.html.new ] || mv linklint/errorX.html.new linklint/errorX.html',
239     '[ ! -r linklint/errorAX.html ] || python doclib/linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
240     '[ ! -r linklint/errorAX.html.new ] || mv linklint/errorAX.html.new linklint/errorAX.html',
241     'echo -e "\\nLokal link check results: linklint/index.html\\nRemote link check results: linklint/urlindex.html\\n"',
242 ])
243
244 PhonyTarget(env, 'fixlinks', [
245     'python doclib/fix-links.py -v -s .svn -s linklint -s debian linklint/errorX.txt linklint/errorAX.txt',
246 ])
247
248 PhonyTarget(env, 'prepare', [])
249
250 PhonyTarget(env, 'valgrind', [
251     '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'
252     ], [ 'all_tests' ])
253
254 env.Clean('all', env.Dir('linklint'))
255
256 env.Clean('all','.prepare-stamp')
257 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
258     Execute(Touch(".prepare-stamp"))