build: add waf-tool to simplify building with AddressSanitizer & friends

Change-Id: If36e52f5e49d94ad5846165c50504d06d7d3c4d0
Refs: #2589
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index 07f1feb..28da538 100644
--- a/.waf-tools/default-compiler-flags.py
+++ b/.waf-tools/default-compiler-flags.py
@@ -60,7 +60,7 @@
             supportedFlags += [flag]
 
     self.end_msg(' '.join(supportedFlags))
-    self.env.CXXFLAGS = supportedFlags + self.env.CXXFLAGS
+    self.env.prepend_value('CXXFLAGS', supportedFlags)
 
 @Configure.conf
 def add_supported_linkflags(self, linkflags):
@@ -78,7 +78,7 @@
             supportedFlags += [flag]
 
     self.end_msg(' '.join(supportedFlags))
-    self.env.LINKFLAGS = supportedFlags + self.env.LINKFLAGS
+    self.env.prepend_value('LINKFLAGS', supportedFlags)
 
 
 class CompilerFlags(object):
diff --git a/.waf-tools/sanitizers.py b/.waf-tools/sanitizers.py
new file mode 100644
index 0000000..a8fe55d
--- /dev/null
+++ b/.waf-tools/sanitizers.py
@@ -0,0 +1,22 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+def options(opt):
+    opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers',
+                   help='Comma-separated list of compiler sanitizers to enable [default=none]')
+
+def configure(conf):
+    for san in conf.options.sanitizers.split(','):
+        if not san:
+            continue
+
+        sanflag = '-fsanitize=%s' % san
+        conf.start_msg('Checking if compiler supports %s' % sanflag)
+
+        if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'],
+                          linkflags=[sanflag], mandatory=False):
+            conf.end_msg('yes')
+            conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer'])
+            conf.env.append_unique('LINKFLAGS', [sanflag])
+        else:
+            conf.end_msg('no', color='RED')
+            conf.fatal('%s sanitizer is not supported by the current compiler' % san)
diff --git a/tests/wscript b/tests/wscript
index 9515055..4f7301f 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -11,5 +11,4 @@
         use=['core-objects'] + ['%s-objects' % tool for tool in bld.env['BUILD_TOOLS']],
         headers='../common.hpp boost-test.hpp',
         install_path=None,
-        defines='TMP_TESTS_PATH=\"%s/tmp-tests\"' % bld.bldnode,
-        )
+        defines='TMP_TESTS_PATH=\"%s/tmp-tests\"' % bld.bldnode)
diff --git a/tools/dissect/wscript b/tools/dissect/wscript
index 070ece2..373f6a4 100644
--- a/tools/dissect/wscript
+++ b/tools/dissect/wscript
@@ -7,11 +7,9 @@
         source=bld.path.ant_glob('*.cpp', excl='main.cpp'),
         includes='.',
         export_includes='.',
-        use='core-objects',
-        )
+        use='core-objects')
 
     bld(features='cxx cxxprogram',
         target='../../bin/ndn-dissect',
         source='main.cpp',
-        use='dissect-objects',
-        )
+        use='dissect-objects')
diff --git a/tools/dump/wscript b/tools/dump/wscript
index 32b3695..946b296 100644
--- a/tools/dump/wscript
+++ b/tools/dump/wscript
@@ -25,8 +25,8 @@
     conf.check(header_name="sys/bitypes.h", mandatory=False)
     conf.check(fragment=ATTRIBUTE_CHECK, msg="Checking for __attribute__", mandatory=False)
 
-    conf.check(header_name=["sys/types.h", "sys/time.h", "time.h"], define="TIME_WITH_SYS_TIME",
-               mandatory=False)
+    conf.check(header_name=["sys/types.h", "sys/time.h", "time.h"],
+               define="TIME_WITH_SYS_TIME", mandatory=False)
 
     conf.check_cfg(path='pcap-config',
                    package="libpcap", args=['--libs', '--cflags'],
@@ -38,8 +38,7 @@
         source=bld.path.ant_glob('*.cpp', excl='main.cpp'),
         includes='.',
         export_includes='.',
-        use='core-objects BOOST PCAP',
-        )
+        use='core-objects BOOST PCAP')
 
     bld(features='cxx cxxprogram',
         target='../../bin/ndndump',
diff --git a/tools/peek/wscript b/tools/peek/wscript
index 99b874e..45679a2 100644
--- a/tools/peek/wscript
+++ b/tools/peek/wscript
@@ -6,12 +6,10 @@
         features='cxx',
         target='../../bin/ndnpeek',
         source='ndn-peek.cpp',
-        use='core-objects',
-        )
+        use='core-objects')
 
     bld.program(
         features='cxx',
         target='../../bin/ndnpoke',
         source='ndn-poke.cpp',
-        use='core-objects',
-        )
+        use='core-objects')
diff --git a/tools/pib/wscript b/tools/pib/wscript
index 3e82b60..344cbaf 100644
--- a/tools/pib/wscript
+++ b/tools/pib/wscript
@@ -2,18 +2,14 @@
 top = '../..'
 
 def build(bld):
-
     bld(features=['cxx'],
         name="pib-objects",
         target="pib-objects",
         source=bld.path.ant_glob('**/*.cpp', excl=['ndn-pib.cpp']),
         use='core-objects',
-        install_path=None,
-        )
-
+        install_path=None)
 
     bld(features=['cxx', 'cxxprogram'],
         target='../../bin/ndn-pib',
         source='ndn-pib.cpp',
-        use='pib-objects',
-        )
+        use='pib-objects')
diff --git a/wscript b/wscript
index 9d9a879..76708ce 100644
--- a/wscript
+++ b/wscript
@@ -8,15 +8,17 @@
 
 def options(opt):
     opt.load(['compiler_cxx', 'gnu_dirs'])
+    opt.load(['default-compiler-flags', 'sanitizers', 'sphinx_build', 'boost'],
+             tooldir=['.waf-tools'])
 
-    opt.load(['default-compiler-flags', 'sphinx_build', 'boost'], tooldir=['.waf-tools'])
     opt.add_option('--with-tests', action='store_true', default=False,
                    dest='with_tests', help='''Build unit tests''')
-    opt.recurse("tools")
+
+    opt.recurse('tools')
 
 def configure(conf):
     conf.load(['compiler_cxx', 'gnu_dirs',
-               'default-compiler-flags', 'sphinx_build', 'boost'])
+               'default-compiler-flags', 'sanitizers', 'sphinx_build', 'boost'])
 
     if 'PKG_CONFIG_PATH' not in os.environ:
         os.environ['PKG_CONFIG_PATH'] = Utils.subst_vars('${LIBDIR}/pkgconfig', conf.env)
@@ -35,14 +37,12 @@
 def build(bld):
     bld.env['VERSION'] = VERSION
 
-    bld(
-        target='core-objects',
+    bld(target='core-objects',
         name='core-objects',
         features='cxx',
         source=bld.path.ant_glob(['core/*.cpp']),
         use='NDN_CXX BOOST',
-        export_includes='.',
-        )
+        export_includes='.')
 
     bld.recurse('tools')
     bld.recurse('tests')