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

Change-Id: I769be2650c0c174b9e12e0832a5d2eea9bcad2d6
Refs: #2589
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index d57bfc3..3df13ad 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/.waf-tools/websocket.py b/.waf-tools/websocket.py
index 5617ee4..2aee143 100644
--- a/.waf-tools/websocket.py
+++ b/.waf-tools/websocket.py
@@ -1,17 +1,14 @@
-# encoding: utf-8
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
-from waflib import Options, Logs, Errors
-from waflib.Configure import conf
-
+from waflib import Options, Logs, Errors, Configure
 import re
 
 def addWebsocketOptions(self, opt):
     opt.add_option('--without-websocket', action='store_false', default=True,
-                   dest='with_websocket',
-                   help='Disable WebSocket face support')
+                   dest='with_websocket', help='Disable WebSocket face support')
 setattr(Options.OptionsContext, "addWebsocketOptions", addWebsocketOptions)
 
-@conf
+@Configure.conf
 def checkWebsocket(self, **kw):
     if not self.options.with_websocket:
         return
diff --git a/wscript b/wscript
index 54cbe4e..9f3a0cb 100644
--- a/wscript
+++ b/wscript
@@ -35,14 +35,16 @@
 
 def options(opt):
     opt.load(['compiler_cxx', 'gnu_dirs'])
-    opt.load(['boost', 'unix-socket', 'dependency-checker', 'websocket',
-              'default-compiler-flags', 'coverage', 'pch', 'boost-kqueue',
-              'doxygen', 'sphinx_build', 'type_traits', 'compiler-features'],
+    opt.load(['default-compiler-flags', 'compiler-features', 'type_traits',
+              'coverage', 'pch', 'sanitizers', 'boost', 'boost-kqueue',
+              'dependency-checker', 'unix-socket', 'websocket',
+              'doxygen', 'sphinx_build'],
              tooldir=['.waf-tools'])
 
     nfdopt = opt.add_option_group('NFD Options')
     opt.addUnixOptions(nfdopt)
     opt.addWebsocketOptions(nfdopt)
+
     opt.addDependencyOptions(nfdopt, 'libpcap')
     nfdopt.add_option('--without-libpcap', action='store_true', default=False,
                       dest='without_libpcap',
@@ -63,9 +65,10 @@
 
 def configure(conf):
     conf.load(['compiler_cxx', 'gnu_dirs',
-               'default-compiler-flags', 'pch', 'boost-kqueue',
-               'boost', 'dependency-checker', 'websocket',
-               'doxygen', 'sphinx_build', 'type_traits', 'compiler-features'])
+               'default-compiler-flags', 'compiler-features', 'type_traits',
+               'pch', 'sanitizers', 'boost', 'boost-kqueue',
+               'dependency-checker', 'websocket',
+               'doxygen', 'sphinx_build'])
 
     conf.find_program('bash', var='BASH')
 
@@ -110,7 +113,7 @@
     boost_libs = 'system chrono program_options random thread log log_setup'
     if conf.options.with_tests:
         conf.env['WITH_TESTS'] = 1
-        conf.define('WITH_TESTS', 1);
+        conf.define('WITH_TESTS', 1)
         boost_libs += ' unit_test_framework'
 
     if conf.options.with_other_tests:
@@ -173,8 +176,7 @@
                 int(VERSION_SPLIT[2]),
         VERSION_MAJOR=VERSION_SPLIT[0],
         VERSION_MINOR=VERSION_SPLIT[1],
-        VERSION_PATCH=VERSION_SPLIT[2],
-        )
+        VERSION_PATCH=VERSION_SPLIT[2])
 
     core = bld(
         target='core-objects',
@@ -185,8 +187,7 @@
         use='version NDN_CXX BOOST LIBRT',
         includes='. core',
         export_includes='.',
-        headers='core/common.hpp'
-        )
+        headers='core/common.hpp')
 
     if bld.env['HAVE_CUSTOM_LOGGER']:
         core.use += " CUSTOM_LOGGER"
@@ -204,8 +205,7 @@
                                        'daemon/main.cpp']),
         use='core-objects WEBSOCKET',
         includes='daemon',
-        export_includes='daemon',
-        )
+        export_includes='daemon')
 
     if bld.env['HAVE_LIBPCAP']:
         nfd_objects.source += bld.path.ant_glob('daemon/face/ethernet-*.cpp')
@@ -222,14 +222,12 @@
         name='rib-objects',
         features='cxx',
         source=bld.path.ant_glob(['rib/**/*.cpp']),
-        use='core-objects',
-        )
+        use='core-objects')
 
     bld(target='bin/nfd',
         features='cxx cxxprogram',
         source='daemon/main.cpp',
-        use='daemon-objects rib-objects',
-        )
+        use='daemon-objects rib-objects')
 
     bld.recurse("tools")
     bld.recurse("tests")
@@ -272,8 +270,7 @@
             HTML_FOOTER="../build/docs/named_data_theme/named_data_footer-with-analytics.html" \
                           if os.getenv('GOOGLE_ANALYTICS', None) \
                           else "../docs/named_data_theme/named_data_footer.html",
-            GOOGLE_ANALYTICS=os.getenv('GOOGLE_ANALYTICS', ""),
-            )
+            GOOGLE_ANALYTICS=os.getenv('GOOGLE_ANALYTICS', ""))
 
         bld(features="doxygen",
             doxyfile='docs/doxygen.conf',