# -*- python -*-
-import sys, glob, os.path, datetime, pwd, time, fnmatch
-sys.path.append('senfscons')
-import SENFSCons
+import sys, glob, os.path, fnmatch
+import SENFSCons, senfutil
###########################################################################
+# Load utilities and setup libraries and configure build
-# This hack is needed for SCons V 0.96.1 compatibility. In current SCons versions
-# we can just use 'env.AlwaysBuild(env.Alias(target), [], action)'
-def PhonyTarget(env, target, action):
- env.AlwaysBuild(env.Command(target + '.phony', 'SConstruct', env.Action(action)))
- env.Alias(target, target + '.phony')
-
-def updateRevision(target, source, env):
- rev = env['ENV']['REVISION'][1:]
- if ':' in rev:
- print
- print "Working copy not clean. Run 'svn update'"
- print
- return 1
- if 'm' in rev and not ARGUMENTS.get('force_deb'):
- print
- print "Working copy contains local changes. Commit first"
- print
- return 1
- if 's' in rev:
- rev = rev[:-1]
- if 'm' in rev:
- rev = rev[:-1]
- changelog = file('debian/changelog.template').read() % {
- 'rev': rev,
- 'user': pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0].strip(),
- 'date': time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) }
- file('debian/changelog','w').write(changelog)
-
-def nonemptyFile(f):
- try: return os.stat(f).st_size > 0
- except OSError: return False
-
-def checkLocalConf(target, source, env):
- if [ True for f in env['CONFIG_FILES'] if nonemptyFile(f) ]:
- print
- print "You have made local modifications to one of the following local configuration"
- print "files:"
- for f in env['CONFIG_FILES']:
- print " ",f
- print
- print "Building a debian package would remove those files."
- print
- print "To continue, remove the offending file(s) and try again. Alternatively,"
- print "build a source package using 'scons debsrc' and may then build debian"
- print "binary packages from this source-package without disrupting your local"
- print "configuration."
- print
- return 1
+env = Environment()
-###########################################################################
-# Load utilities and setup libraries and configure build
+env.Decider('MD5-timestamp')
+env.EnsureSConsVersion(1,2)
-SENFSCons.UseBoost()
-SENFSCons.UseSTLPort()
-env = SENFSCons.MakeEnvironment()
+# Load all the local SCons tools
+env.Tool('Doxygen')
+env.Tool('Dia2Png')
+env.Tool('PkgDraw')
+env.Tool('InstallSubdir')
+env.Tool('CopyToDir')
+env.Tool('Boost')
+env.Tool('CombinedObject')
+env.Tool('PhonyTarget')
+env.Tool('InstallDir')
env.Help("""
Additional top-level build targets:
-prepare Create all source files not part of the repository
+prepare Create all target files not part of the repository
+default Build all default targets (like calling scons with no arguments)
+examples Build all examples
all_tests Build and run unit tests for all modules
all_docs Build documentation for all modules
all Build everything
-install_all Install SENF into $PREFIX
+install_all Install SENF into $$PREFIX
deb Build debian source and binary package
debsrc Build debian source package
debbin Build debian binary package
linklint Check links of doxygen documentation with 'linklint'
fixlinks Fix broken links in doxygen documentation
+valgrind Run all tests under valgrind/memcheck
+lcov Generate test coverage output in doc/lcov and lcov.info
""")
-if os.environ.get('debian_build'):
- rev = os.popen("dpkg-parsechangelog | awk '/^Version:/{print $2}'").read().strip()
-else:
- rev = 'r' + os.popen("svnversion").read().strip().lower()
-
-logname = os.environ.get('LOGNAME')
-if not logname:
- logname = pwd.getpwuid(os.getuid()).pw_name
+env.Append(
+ ENV = { 'PATH' : os.environ.get('PATH'), 'HOME' : os.environ.get('HOME') },
+ CLEAN_PATTERNS = [ '*~', '#*#', '*.pyc', 'semantic.cache', '.sconsign*',
+ '*.gcno', '*.gcda', '*.gcov' ],
+
+ CPPPATH = [ '#' ],
+ LOCALLIBDIR = '#',
+ LIBPATH = [ '$LOCALLIBDIR' ],
+ LIBS = [ '$LIBSENF$LIBADDSUFFIX', 'rt', '$BOOSTREGEXLIB',
+ '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', '$BOOSTFSLIB' ],
+ TEST_EXTRA_LIBS = [ ],
+
+ PREFIX = '#/dist',
+ LIBINSTALLDIR = '$PREFIX${syslayout and "/lib" or ""}',
+ BININSTALLDIR = '$PREFIX${syslayout and "/bin" or ""}',
+ INCLUDEINSTALLDIR = '$PREFIX${syslayout and "/include" or ""}',
+ CONFINSTALLDIR = '${syslayout and "$LIBINSTALLDIR/senf" or "$PREFIX"}',
+ OBJINSTALLDIR = '$CONFINSTALLDIR',
+ DOCINSTALLDIR = '$PREFIX${syslayout and "/share/doc/senf" or "/manual"}',
+ SCONSINSTALLDIR = '$CONFINSTALLDIR/site_scons',
+
+ CPP_INCLUDE_EXTENSIONS = [ '.h', '.hh', '.ih', '.mpp', '.cci', '.ct', '.cti' ],
+ CPP_EXCLUDE_EXTENSIONS = [ '.test.hh' ],
+
+ # These options are insane. Only useful for inline debugging. Need at least 1G free RAM
+ INLINE_OPTS_DEBUG = [ '-finline-limit=20000', '-fvisibility-inlines-hidden',
+ '-fno-inline-functions', '-Winline'
+ '--param','large-function-growth=10000',
+ '--param', 'large-function-insns=10000',
+ '--param','inline-unit-growth=10000' ],
+ INLINE_OPTS_NORMAL = [ '-finline-limit=5000' ],
+ INLINE_OPTS = [ '$INLINE_OPTS_NORMAL' ],
+ CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long', '$INLINE_OPTS',
+ '$CXXFLAGS_' ],
+ CXXFLAGS_ = senfutil.BuildTypeOptions('CXXFLAGS'),
+ CXXFLAGS_final = [ '-O3' ],
+ CXXFLAGS_normal = [ '-O0', '-g' ],
+ CXXFLAGS_debug = [ '$CXXFLAGS_normal' ],
+
+ CPPDEFINES = [ '$expandLogOption', '$CPPDEFINES_' ],
+ expandLogOption = senfutil.expandLogOption,
+ CPPDEFINES_ = senfutil.BuildTypeOptions('CPPDEFINES'),
+ CPPDEFINES_final = [ ],
+ CPPDEFINES_normal = [ 'SENF_DEBUG' ],
+ CPPDEFINES_debug = [ '$CPPDEFINES_normal' ],
+
+ LINKFLAGS = [ '-rdynamic', '$LINKFLAGS_' ],
+ LINKFLAGS_ = senfutil.BuildTypeOptions('LINKFLAGS'),
+ LINKFLAGS_final = [ ],
+ LINKFLAGS_normal = [ '-Wl,-S' ],
+ LINKFLAGS_debug = [ '-g' ],
+)
-def configFilesOpts(target, source, env, for_signature):
- return [ '-I%s' % os.path.split(f)[1] for f in env['CONFIG_FILES'] ]
+env.SetDefault(
+ LIBSENF = "senf",
+ LCOV = "lcov",
+ GENHTML = "genhtml",
+ SCONS = "./tools/scons -j$CONCURRENCY_LEVEL",
+ CONCURRENCY_LEVEL = env.GetOption('num_jobs') or 1,
+ TOPDIR = env.Dir('#').abspath,
+)
-env.Append(
- CPPPATH = [ '#/include' ],
- LIBS = [ 'iberty', '$BOOSTREGEXLIB', '$BOOSTFSLIB' ],
- DOXY_XREF_TYPES = [ 'bug', 'fixme', 'todo', 'idea' ],
- DOXY_HTML_XSL = '#/doclib/html-munge.xsl',
- ENV = { 'TODAY' : str(datetime.date.today()),
- 'REVISION' : rev,
- 'LOGNAME' : logname, # needed by the debian build scripts
- 'CONCURRENCY_LEVEL' : env.GetOption('num_jobs') or "1",
- 'SCONS' : 1
- },
- CONFIG_FILES = [ 'Doxyfile.local', 'SConfig', 'local_config.hh' ],
- CONFIG_FILES_OPTS = configFilesOpts,
- CLEAN_PATTERNS = [ '*.pyc', 'semantic.cache', '.sconsign', '.sconsign.dblite' ],
- BUILDPACKAGE_COMMAND = "dpkg-buildpackage -us -uc -rfakeroot -I.svn $CONFIG_FILES_OPTS",
- TOP_INCLUDES = [ 'Packets', 'PPI', 'Scheduler', 'Socket', 'Utils',
- 'config.hh', 'local_config.hh' ]
+# Set variables from command line
+senfutil.parseArguments(
+ env,
+ BoolVariable('final', 'Build final (optimized) build', False),
+ BoolVariable('debug', 'Link in debug symbols', False),
+ BoolVariable('syslayout', 'Install in to system layout directories (lib/, include/ etc)', False),
)
Export('env')
# Create Doxyfile.local otherwise doxygen will barf on this non-existent file
-if not env.GetOption('clean') and not os.path.exists("Doxyfile.local"):
- Execute(Touch("Doxyfile.local"))
-
-# Create local_config.h
-if not env.GetOption('clean') and not os.path.exists("local_config.hh"):
- Execute(Touch("local_config.hh"))
-
-###########################################################################
-# Define build targets
-
-# Before defining any targets, check wether this is the first build in
-# pristine directory tree. If so, call 'scons prepare' so the dependencies
-# created later are correct
+# Create it even when cleaning, to silence the doxygen builder warnings
+if not os.path.exists("doclib/Doxyfile.local"):
+ Execute(Touch("doclib/Doxyfile.local"))
if not env.GetOption('clean') and not os.path.exists(".prepare-stamp") \
- and not os.environ.get("SCONS"):
+ and not os.environ.get("SCONS") and COMMAND_LINE_TARGETS != [ 'prepare' ]:
env.Execute([ "scons prepare" ])
-env.Clean('all', '.prepare-stamp')
-
-SConscript(glob.glob("*/SConscript"))
+# Load SConscripts
-SENFSCons.StandardTargets(env)
-SENFSCons.GlobalTargets(env)
-SENFSCons.Doxygen(env)
-SENFSCons.DoxyXRef(env,
- HTML_HEADER = '#/doclib/doxy-header-overview.html',
- HTML_FOOTER = '#/doclib/doxy-footer.html')
+SConscriptChdir(0)
+SConscript("debian/SConscript")
+SConscriptChdir(1)
+if os.path.exists('SConscript.local') : SConscript('SConscript.local')
+SConscript("senf/SConscript")
+SConscript("Examples/SConscript")
+SConscript("HowTos/SConscript")
+SConscript("doclib/SConscript")
-SENFSCons.InstallIncludeFiles(env, [ 'config.hh' ])
+###########################################################################
+# Define build targets
-# Build combined library 'libsenf'
-libsenf = env.Library(
- SENFSCons.LibPath('senf'),
- Flatten([ env.File(SENFSCons.LibPath(lib)).sources for lib in env['ALLLIBS'] ]))
+#### libsenf.a
+libsenf = env.Library("$LOCALLIBDIR/${LIBSENF}${LIBADDSUFFIX}", env['ALLOBJECTS'])
env.Default(libsenf)
-env.Clean('all', 'libsenf.a')
-env.Alias('default', 'libsenf.a')
-
-env.Alias('install_all', env.Install('$LIBINSTALLDIR', libsenf))
-
-env.Clean('all', [ os.path.join(path,f)
- for path, subdirs, files in os.walk('.')
- for pattern in env['CLEAN_PATTERNS']
- for f in fnmatch.filter(files,pattern) ])
-
-PhonyTarget(env, 'deb', [
- checkLocalConf,
- updateRevision,
- "$BUILDPACKAGE_COMMAND",
-])
-
-PhonyTarget(env, 'debsrc', [
- updateRevision,
- "$BUILDPACKAGE_COMMAND -S",
-])
-
-PhonyTarget(env, 'debbin', [
- checkLocalConf,
- updateRevision,
- "$BUILDPACKAGE_COMMAND -nc",
-])
-
-PhonyTarget(env, 'linklint', [
- 'rm -rf linklint',
- 'linklint -doc linklint -limit 99999999 `find -type d -name html -printf "/%P/@ "`',
- '[ ! -r linklint/errorX.html ] || python linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
- '[ ! -r linklint/errorX.html.new ] || mv linklint/errorX.html.new linklint/errorX.html',
- '[ ! -r linklint/errorAX.html ] || python linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
- '[ ! -r linklint/errorAX.html.new ] || mv linklint/errorAX.html.new linklint/errorAX.html',
- 'echo -e "\\nLokal link check results: linklint/index.html\\nRemote link check results: linklint/urlindex.html\\n"',
-])
-
-PhonyTarget(env, 'fixlinks', [
- 'python doclib/fix-links.py -v -s .svn -s linklint -s debian linklint/errorX.txt linklint/errorAX.txt',
-])
-
-PhonyTarget(env, 'prepare', [])
-
-env.Clean('all', env.Dir('linklint'))
-
-env.Clean('all','.prepare-stamp')
+env.Install('$LIBINSTALLDIR', libsenf)
+
+def create(target, source, env):
+ file(str(target[0]), 'w').write(source[0].get_contents()+"\n")
+env['BUILDERS']['CreateFile'] = Builder(action = create)
+
+conf = env.CreateFile("${LOCALLIBDIR}/${LIBSENF}${LIBADDSUFFIX}.conf",
+ env.Value(env.subst("$_CPPDEFFLAGS")))
+env.Default(conf)
+env.Install('$CONFINSTALLDIR', conf)
+
+#### install_all, default, all_tests, all
+env.Install('${SCONSINSTALLDIR}', [ 'site_scons/__init__.py',
+ 'site_scons/senfutil.py',
+ 'site_scons/yaptu.py' ])
+env.InstallDir('${SCONSINSTALLDIR}', [ 'site_scons/site_tools', 'site_scons/lib' ],
+ FILTER_SUFFIXES=[ '','.css','.pl','.py','.sh','.sty','.xml','.xsl','.yap' ])
+env.Install('${INCLUDEINSTALLDIR}', 'boost')
+
+env.Alias('install_all', env.FindInstalledFiles())
+env.Alias('default', DEFAULT_TARGETS)
+env.Alias('all_tests', env.FindAllBoostUnitTests())
+env.Alias('all', [ 'default', 'all_tests', 'examples', 'all_docs' ])
+
+#### prepare
+env.PhonyTarget('prepare', [], [])
+
+#### valgrind
+env.PhonyTarget('valgrind', [ 'all_tests' ], [ """
+ find -name .test.bin
+ | while read test; do
+ echo;
+ echo "Running $$test";
+ echo;
+ valgrind --tool=memcheck --error-exitcode=99 --suppressions=tools/valgrind.sup
+ $$test $BOOSTTESTARGS;
+ [ $$? -ne 99 ] || exit 1;
+ done
+""".replace("\n"," ") ])
+
+### lcov
+env.PhonyTarget('lcov', [], [
+ '$SCONS debug=1 CCFLAGS+="-fprofile-arcs -ftest-coverage" LIBS+="gcov" all_tests',
+ '$LCOV --directory $TOPDIR/senf --capture --output-file /tmp/senf_lcov.info --base-directory $TOPDIR',
+ '$LCOV --output-file lcov.info --remove /tmp/senf_lcov.info "*/include/*" "*/boost/*" "*.test.*" ',
+ '$GENHTML --output-directory doc/lcov --title all_tests lcov.info',
+ 'rm /tmp/senf_lcov.info' ])
+
+#### clean
+env.Clean('all', '.prepare-stamp')
+env.Clean('all', libsenf)
+env.Clean('all', env.Dir('dist'))
+env.Clean('all', 'lcov.info')
+
+if env.GetOption('clean'):
+ env.Clean('all', [ os.path.join(path,f)
+ for path, subdirs, files in os.walk('.')
+ for pattern in env['CLEAN_PATTERNS']
+ for f in fnmatch.filter(files,pattern) ])
+
if not env.GetOption('clean') and not os.path.exists(".prepare-stamp"):
Execute(Touch(".prepare-stamp"))