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