3 import sys, glob, os.path, datetime, pwd, time, fnmatch
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"
31 changelog = file('debian/changelog.template').read() % {
33 'user': pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0].strip(),
34 'date': time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) }
35 file('debian/changelog','w').write(changelog)
38 try: return os.stat(f).st_size > 0
39 except OSError: return False
41 def checkLocalConf(target, source, env):
42 if [ True for f in env['LOCAL_CONFIG_FILES'] if nonemptyFile(f) ]:
44 print "You have made local modifications to one of the following local configuration"
46 for f in env['LOCAL_CONFIG_FILES']:
49 print "Building a debian package would remove those files."
51 print "To continue, remove the offending file(s) and try again. Alternatively,"
52 print "build a source package using 'scons debsrc' and may then build debian"
53 print "binary packages from this source-package without disrupting your local"
54 print "configuration."
58 def getLibDepends(script):
60 return os.popen("perl -0777 -n -e '$,=\" \"; print $1=~m/'\"'\"'([^'\"'\"']*)'\"'\"'/g if /LIBS\s*=\s*\[([^\]]*)\]/' %s" % script).read().split()
62 # Original topological sort code written by Ofer Faigon
63 # (www.bitformation.com) and used with permission
64 def topological_sort(items, partial_order):
65 """Perform topological sort.
66 items is a list of items to be sorted.
67 partial_order is a list of pairs. If pair (a,b) is in it, it means
68 that item a should appear before item b.
69 Returns a list of the items in one of the possible orders, or None
70 if partial_order contains a loop.
72 def add_node(graph, node):
73 if not graph.has_key(node):
75 def add_arc(graph, fromnode, tonode):
76 graph[fromnode].append(tonode)
77 graph[tonode][0] = graph[tonode][0] + 1
81 for a,b in partial_order:
83 roots = [node for (node,nodeinfo) in graph.items() if nodeinfo[0] == 0]
84 while len(roots) != 0:
87 for child in graph[root][1:]:
88 graph[child][0] = graph[child][0] - 1
89 if graph[child][0] == 0:
92 if len(graph.items()) != 0:
93 raise RuntimeError, "Loop detected in partial_order"
95 ###########################################################################
96 # Load utilities and setup libraries and configure build
99 SENFSCons.UseSTLPort()
100 env = SENFSCons.MakeEnvironment()
103 Additional top-level build targets:
105 prepare Create all source files not part of the repository
106 all_tests Build and run unit tests for all modules
107 all_docs Build documentation for all modules
109 install_all Install SENF into $PREFIX
110 deb Build debian source and binary package
111 debsrc Build debian source package
112 debbin Build debian binary package
113 linklint Check links of doxygen documentation with 'linklint'
114 fixlinks Fix broken links in doxygen documentation
115 valgrind Run all tests under valgrind/memcheck
118 if os.environ.get('debian_build'):
119 rev = os.popen("dpkg-parsechangelog | awk '/^Version:/{print $2}'").read().strip()
121 rev = 'r' + os.popen("svnversion").read().strip().lower()
123 logname = os.environ.get('LOGNAME')
125 logname = pwd.getpwuid(os.getuid()).pw_name
127 def configFilesOpts(target, source, env, for_signature):
128 return [ '-I%s' % os.path.split(f)[1] for f in env['LOCAL_CONFIG_FILES'] ]
131 CPPPATH = [ '#/include' ],
132 LIBS = [ 'iberty', '$BOOSTREGEXLIB' ],
133 TEST_EXTRA_LIBS = [ '$BOOSTFSLIB' ],
134 DOXY_XREF_TYPES = [ 'bug', 'fixme', 'todo', 'idea' ],
135 DOXY_HTML_XSL = '#/doclib/html-munge.xsl',
136 ENV = { 'TODAY' : str(datetime.date.today()),
138 'LOGNAME' : logname, # needed by the debian build scripts
139 'CONCURRENCY_LEVEL' : env.GetOption('num_jobs') or "1",
141 'PATH' : os.environ.get('PATH')
143 LOCAL_CONFIG_FILES = [ 'Doxyfile.local', 'SConfig', 'local_config.hh' ],
144 CONFIG_FILES_OPTS = configFilesOpts,
145 CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign', '.sconsign.dblite' ],
146 BUILDPACKAGE_COMMAND = "dpkg-buildpackage -us -uc -rfakeroot -I.svn -I_templates $CONFIG_FILES_OPTS",
147 TOP_INCLUDES = [ 'Packets', 'PPI', 'Scheduler', 'Socket', 'Utils',
148 'config.hh', 'local_config.hh' ],
157 # Create Doxyfile.local otherwise doxygen will barf on this non-existent file
158 # Create it even when cleaning, to silence the doxygen builder warnings
159 if not os.path.exists("Doxyfile.local"):
160 Execute(Touch("Doxyfile.local"))
162 # Create local_config.h
163 if not env.GetOption('clean') and not os.path.exists("local_config.hh"):
164 Execute(Touch("local_config.hh"))
166 ###########################################################################
167 # Define build targets
169 # Before defining any targets, check wether this is the first build in
170 # pristine directory tree. If so, call 'scons prepare' so the dependencies
171 # created later are correct
173 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
174 and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
175 env.Execute([ "scons prepare" ])
177 env.Clean('all', '.prepare-stamp')
179 # Not nice, but until we get to fixing the dependency jungle
180 # concerning generated sources ...
184 for script in glob.glob("*/SConscript"):
185 depends = getLibDepends(script)
186 script = script.split('/',1)[0]
187 scripts.append(script)
188 dependencies += [ (dep, script) for dep in depends ]
190 for subdir in topological_sort(scripts, dependencies):
191 SConscript(os.path.join(subdir, "SConscript"))
193 SENFSCons.StandardTargets(env)
194 SENFSCons.GlobalTargets(env)
195 SENFSCons.Doxygen(env)
196 SENFSCons.DoxyXRef(env,
197 HTML_HEADER = '#/doclib/doxy-header.html',
198 HTML_FOOTER = '#/doclib/doxy-footer.html')
200 SENFSCons.InstallIncludeFiles(env, [ 'config.hh' ])
202 # Build combined library 'libsenf'
203 libsenf = env.Library(
204 'senf${LIBADDSUFFIX}',
205 Flatten([ env.File(SENFSCons.LibPath(lib)).sources for lib in env['ALLLIBS'] ]))
207 env.Clean('all', libsenf)
208 env.Alias('default', libsenf)
210 env.Alias('install_all', env.Install('$LIBINSTALLDIR', libsenf))
212 env.Clean('all', [ os.path.join(path,f)
213 for path, subdirs, files in os.walk('.')
214 for pattern in env['CLEAN_PATTERNS']
215 for f in fnmatch.filter(files,pattern) ])
217 PhonyTarget(env, 'deb', [
220 "$BUILDPACKAGE_COMMAND",
221 "fakeroot ./debian/rules debclean"
224 PhonyTarget(env, 'debsrc', [
226 "$BUILDPACKAGE_COMMAND -S",
229 PhonyTarget(env, 'debbin', [
232 "$BUILDPACKAGE_COMMAND -b",
233 "fakeroot ./debian/rules debclean"
236 PhonyTarget(env, 'linklint', [
238 'linklint -doc linklint -limit 99999999 `find -type d -name html -printf "/%P/@ "`',
239 '[ ! -r linklint/errorX.html ] || python doclib/linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
240 '[ ! -r linklint/errorX.html.new ] || mv linklint/errorX.html.new linklint/errorX.html',
241 '[ ! -r linklint/errorAX.html ] || python doclib/linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
242 '[ ! -r linklint/errorAX.html.new ] || mv linklint/errorAX.html.new linklint/errorAX.html',
243 'echo -e "\\nLokal link check results: linklint/index.html\\nRemote link check results: linklint/urlindex.html\\n"',
246 PhonyTarget(env, 'fixlinks', [
247 'python doclib/fix-links.py -v -s .svn -s linklint -s debian linklint/errorX.txt linklint/errorAX.txt',
250 PhonyTarget(env, 'prepare', [])
252 PhonyTarget(env, 'valgrind', [
253 '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'
256 env.Clean('all', env.Dir('linklint'))
258 env.Clean('all','.prepare-stamp')
259 if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
260 Execute(Touch(".prepare-stamp"))