3 import sys, glob, os.path, datetime, pwd, time, fnmatch, string
4 sys.path.append('senfscons')
7 ###########################################################################
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')
15 def updateRevision(target, source, env):
16 rev = env['ENV']['REVISION'][1:]
19 print "Working copy not clean. Run 'svn update'"
22 if 'm' in rev and not ARGUMENTS.get('force_deb'):
24 print "Working copy contains local changes. Commit first"
32 for line in os.popen("svn info"):
33 elts=line.split(':',1)
38 version = url.rsplit('/',1)[-1].split('_',1)[0]
39 if version[0] not in string.digits:
42 version = '1:0r%s' % rev
43 changelog = file('debian/changelog.template').read() % {
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)
51 try: return os.stat(f).st_size > 0
52 except OSError: return False
54 def checkLocalConf(target, source, env):
55 if [ True for f in env['LOCAL_CONFIG_FILES'] if nonemptyFile(f) ]:
57 print "You have made local modifications to one of the following local configuration"
59 for f in env['LOCAL_CONFIG_FILES']:
62 print "Building a debian package would remove those files."
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."
71 def getLibDepends(script):
73 return os.popen("perl -0777 -n -e '$,=\" \"; print $1=~m/'\"'\"'([^'\"'\"']*)'\"'\"'/g if /LIBS\s*=\s*\[([^\]]*)\]/' %s" % script).read().split()
75 # Original topological sort code written by Ofer Faigon
76 # (www.bitformation.com) and used with permission
77 def topological_sort(items, partial_order):
78 """Perform topological sort.
79 items is a list of items to be sorted.
80 partial_order is a list of pairs. If pair (a,b) is in it, it means
81 that item a should appear before item b.
82 Returns a list of the items in one of the possible orders, or None
83 if partial_order contains a loop.
85 def add_node(graph, node):
86 if not graph.has_key(node):
88 def add_arc(graph, fromnode, tonode):
89 graph[fromnode].append(tonode)
90 graph[tonode][0] = graph[tonode][0] + 1
94 for a,b in partial_order:
96 roots = [node for (node,nodeinfo) in graph.items() if nodeinfo[0] == 0]
97 while len(roots) != 0:
100 for child in graph[root][1:]:
101 graph[child][0] = graph[child][0] - 1
102 if graph[child][0] == 0:
105 if len(graph.items()) != 0:
106 raise RuntimeError, "Loop detected in partial_order"
108 ###########################################################################
109 # Load utilities and setup libraries and configure build
112 SENFSCons.UseSTLPort()
113 env = SENFSCons.MakeEnvironment()
116 Additional top-level build targets:
118 prepare Create all source files not part of the repository
119 all_tests Build and run unit tests for all modules
120 all_docs Build documentation for all modules
122 install_all Install SENF into $PREFIX
123 deb Build debian source and binary package
124 debsrc Build debian source package
125 debbin Build debian binary package
126 linklint Check links of doxygen documentation with 'linklint'
127 fixlinks Fix broken links in doxygen documentation
128 valgrind Run all tests under valgrind/memcheck
131 if os.environ.get('debian_build'):
132 rev = os.popen("dpkg-parsechangelog | awk '/^Version:/{print $2}'").read().strip()
134 rev = 'r' + os.popen("svnversion").read().strip().lower()
136 logname = os.environ.get('LOGNAME')
138 logname = pwd.getpwuid(os.getuid()).pw_name
140 def configFilesOpts(target, source, env, for_signature):
141 return [ '-I%s' % os.path.split(f)[1] for f in env['LOCAL_CONFIG_FILES'] ]
143 # Options used to debug inlining:
145 # INLINE_OPTS = [ '-finline-limit=20000', '--param','large-function-growth=10000',
146 # '--param', 'large-function-insns=10000', '--param','inline-unit-growth=10000',
147 # '-fvisibility-inlines-hidden', '-fno-inline-functions', '-Winline' ]
149 # BEWARE: You need lots of ram to compile with these settings (approx 1G)
152 INLINE_OPTS = [ '-finline-limit=5000' ]
155 CPPPATH = [ '#/include' ],
156 CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long' ] + INLINE_OPTS,
157 LIBS = [ 'readline', 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB' ],
158 TEST_EXTRA_LIBS = [ '$BOOSTFSLIB' ],
159 DOXY_XREF_TYPES = [ 'bug', 'fixme', 'todo', 'idea' ],
160 DOXY_HTML_XSL = '#/doclib/html-munge.xsl',
161 ENV = { 'TODAY' : str(datetime.date.today()),
163 'LOGNAME' : logname, # needed by the debian build scripts
164 'CONCURRENCY_LEVEL' : env.GetOption('num_jobs') or "1",
166 'PATH' : os.environ.get('PATH')
168 LOCAL_CONFIG_FILES = [ 'Doxyfile.local', 'SConfig', 'local_config.hh' ],
169 CONFIG_FILES_OPTS = configFilesOpts,
170 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign', '.sconsign.dblite' ],
171 BUILDPACKAGE_COMMAND = "dpkg-buildpackage -us -uc -rfakeroot -I.svn -I_templates $CONFIG_FILES_OPTS",
172 TOP_INCLUDES = [ 'Packets', 'PPI', 'Scheduler', 'Socket', 'Utils', 'Console',
173 'config.hh', 'local_config.hh' ],
176 def parseLogOption(value):
177 stream, area, level = ( x.strip() for x in value.strip().split('|') )
178 if stream : stream = ''.join('(%s)' % x for x in stream.split('::') )
179 else : stream = '(_)'
180 if area : area = ''.join( '(%s)' % x for x in area.split('::') )
182 return '(( %s,%s,%s ))' % (stream,area,level)
184 def expandLogOption(target, source, env, for_signature):
185 return ' '.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )
187 if env.subst('$LOGLEVELS'):
188 env.Append( expandLogOption=expandLogOption )
189 env.Append( CPPDEFINES = { 'SENF_LOG_CONF': '$expandLogOption' } )
197 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
198 # Create it even when cleaning, to silence the doxygen builder warnings
199 if not os.path.exists("Doxyfile.local"):
200 Execute(Touch("Doxyfile.local"))
202 # Create local_config.h
203 if not env.GetOption('clean') and not os.path.exists("local_config.hh"):
204 Execute(Touch("local_config.hh"))
206 ###########################################################################
207 # Define build targets
209 # Before defining any targets, check wether this is the first build in
210 # pristine directory tree. If so, call 'scons prepare' so the dependencies
211 # created later are correct
213 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
214 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
215 env.Execute([ "scons prepare" ])
217 env.Clean('all', '.prepare-stamp')
219 # Not nice, but until we get to fixing the dependency jungle
220 # concerning generated sources ...
224 for script in glob.glob("*/SConscript"):
225 depends = getLibDepends(script)
226 script = script.split('/',1)[0]
227 scripts.append(script)
228 dependencies += [ (dep, script) for dep in depends ]
230 for subdir in topological_sort(scripts, dependencies):
231 SConscript(os.path.join(subdir, "SConscript"))
233 SENFSCons.StandardTargets(env)
234 SENFSCons.GlobalTargets(env)
235 SENFSCons.Doxygen(env)
236 SENFSCons.DoxyXRef(env,
237 HTML_HEADER = '#/doclib/doxy-header.html',
238 HTML_FOOTER = '#/doclib/doxy-footer.html')
240 SENFSCons.InstallIncludeFiles(env, [ 'config.hh' ])
242 # Build combined library 'libsenf'
243 libsenf = env.Library(
244 'senf${LIBADDSUFFIX}',
245 Flatten([ env.File(SENFSCons.LibPath(lib)).sources for lib in env['ALLLIBS'] ]))
247 env.Clean('all', libsenf)
248 env.Alias('default', libsenf)
250 env.Alias('install_all', env.Install('$LIBINSTALLDIR', libsenf))
252 if env.GetOption('clean'):
253 env.Clean('all', [ os.path.join(path,f)
254 for path, subdirs, files in os.walk('.')
255 for pattern in env['CLEAN_PATTERNS']
256 for f in fnmatch.filter(files,pattern) ])
258 PhonyTarget(env, 'deb', [
261 "$BUILDPACKAGE_COMMAND",
262 "fakeroot ./debian/rules debclean"
265 PhonyTarget(env, 'debsrc', [
267 "$BUILDPACKAGE_COMMAND -S",
270 PhonyTarget(env, 'debbin', [
273 "$BUILDPACKAGE_COMMAND -b",
274 "fakeroot ./debian/rules debclean"
277 PhonyTarget(env, 'linklint', [
279 'linklint -doc linklint -limit 99999999 `find -type d -name html -printf "/%P/@ "`',
280 '[ ! -r linklint/errorX.html ] || python doclib/linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
281 '[ ! -r linklint/errorX.html.new ] || mv linklint/errorX.html.new linklint/errorX.html',
282 '[ ! -r linklint/errorAX.html ] || python doclib/linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
283 '[ ! -r linklint/errorAX.html.new ] || mv linklint/errorAX.html.new linklint/errorAX.html',
284 'echo -e "\\nLokal link check results: linklint/index.html\\nRemote link check results: linklint/urlindex.html\\n"',
287 PhonyTarget(env, 'fixlinks', [
288 'python doclib/fix-links.py -v -s .svn -s linklint -s debian linklint/errorX.txt linklint/errorAX.txt',
291 PhonyTarget(env, 'prepare', [])
293 PhonyTarget(env, 'valgrind', [
294 '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'
297 env.Clean('all', env.Dir('linklint'))
299 env.Clean('all','.prepare-stamp')
300 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
301 Execute(Touch(".prepare-stamp"))