src/wscript
changeset 8253 6faee3d1d1d0
parent 8248 986f7db9f056
parent 7545 ac0569e8cb7d
child 8339 bfa05e51eecf
--- a/src/wscript	Tue Oct 04 10:16:06 2011 +0200
+++ b/src/wscript	Mon Oct 10 18:05:18 2011 +0200
@@ -6,15 +6,13 @@
 import types
 import warnings
 
+from waflib.Errors import WafError
+
 import TaskGen
 import Task
 import Options
 import Build
 import Utils
-import Constants
-
-import ccroot
-ccroot.USE_TOP_LEVEL = True
 
 import wutils
 
@@ -23,54 +21,21 @@
 except NameError:
     from sets import Set as set # Python 2.3 fallback
 
-all_modules = [
-    'core',
-    'network',
-    'config-store',
-    'internet',
-    'propagation',
-    'point-to-point',
-    'csma',
-    'emu',
-    'bridge',
-    'tap-bridge',
-    'virtual-net-device',
-    'applications',
-    'nix-vector-routing',
-    'olsr',
-    'aodv',
-    'dsdv',
-    'click',
-    'openflow',
-    'mobility',
-    'wifi',
-    'netanim',
-    'stats',
-    'uan',
-    'spectrum',
-    'mesh',   
-    'test',
-    'test/ns3tcp',
-    'test/ns3wifi',
-    'flow-monitor',
-    'wimax',
-    'lte',
-    'mpi',
-    'topology-read',
-    'energy',
-    'tools',
-    'visualizer',
-    'point-to-point-layout',
-    'csma-layout',
-    'template',
-    'buildings',
-    ]
 
-def set_options(opt):
-    opt.sub_options('core')
-    opt.sub_options('click')
-    opt.sub_options('openflow')
+all_modules = []
+for dirname in os.listdir('src'):
+    if dirname.startswith('.') or dir == 'CVS':
+        continue
+    path = os.path.join('src', dirname)
+    if not os.path.isdir(path):
+        continue
+    if os.path.exists(os.path.join(path, 'wscript')):
+        all_modules.append(dirname)
+all_modules.sort()
 
+
+
+def options(opt):
     opt.add_option('--enable-rpath',
                    help=("Link programs with rpath"
                          " (normally not needed, see "
@@ -82,20 +47,15 @@
                    help=("Build only these modules (and dependencies)"),
                    dest='enable_modules')
 
+    for module in all_modules:
+        opt.sub_options(module, mandatory=False)
+
+
 def configure(conf):
-    conf.sub_config('core')
-    conf.sub_config('emu')
-    conf.sub_config('tap-bridge')
-    conf.sub_config('config-store')
-    conf.sub_config('internet')
-    conf.sub_config('netanim')
-    conf.sub_config('test')
-    conf.sub_config('click')
-    conf.sub_config('openflow')
-    conf.sub_config('stats')
-    conf.sub_config('visualizer')
+    for module in all_modules:
+        conf.sub_config(module, mandatory=False)
 
-    blddir = os.path.abspath(os.path.join(conf.blddir, conf.env.variant()))
+    blddir = os.path.abspath(os.path.join(conf.bldnode.abspath(), conf.variant))
     conf.env.append_value('NS3_MODULE_PATH', blddir)
     if Options.options.enable_rpath:
         conf.env.append_value('RPATH', '-Wl,-rpath=%s' % (os.path.join(blddir),))
@@ -104,131 +64,85 @@
     conf.env['NS3_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_modules]
 
 
-class ns3module_taskgen(TaskGen.task_gen):
-    def __init__(self, *args, **kwargs):
-        super(ns3module_taskgen, self).__init__(*args, **kwargs)
-        self.libs = []
 
-    def apply(self):
-        static_enabled = False
-        shared_enabled = True
-        bld = self.bld
-        if bld.env['ENABLE_STATIC_NS3']:
-            static_enabled = True
-            shared_enabled = False
-        if bld.env['ENABLE_SHARED_AND_STATIC_NS3']:
-            static_enabled = True
-            shared_enabled = True
-
-        assert self.name.startswith("ns3-")
-        name = self.name.split("ns3-")[1]
-
-        if static_enabled:
-            static = self._create_ns3_module(self.bld, name, self.dependencies, True)
-            self.libs.append(static)
-        else:
-            static = None
-
-        if shared_enabled:
-            shared = self._create_ns3_module(self.bld, name, self.dependencies, False)
-            self.libs.append(shared)
-        else:
-            shared = None
-
-        if static is not None and shared is None:
-            static.name = self.name + "--lib"
-            static.uselib_local = ['ns3-%s--lib' % (dep,) for dep in self.dependencies]
-
-        elif shared is not None and static is None:
-            shared.name = self.name + "--lib"
-            shared.uselib_local = ['ns3-%s--lib' % (dep,) for dep in self.dependencies]
-        else:
-            shared.name = self.name + "--lib"
-            shared.uselib_local = ['ns3-%s--lib' % (dep,) for dep in self.dependencies]
-            static.name = self.name + "--static"
-            static.uselib_local = ['ns3-%s--static' % (dep,) for dep in self.dependencies]
-
-        if not self.test:
-            pcfile = bld.new_task_gen('ns3pcfile')
-            pcfile.module = self
-
-
-    def _create_ns3_module(self, bld, name, dependencies, static):
-
-        # FIXME: env modifications are overwritten by parent caller
-
-        # Create a separate library for this module.
-        if static:
-            module = bld.new_task_gen('cxx', 'cstaticlib')
-        else:
-            module = bld.new_task_gen('cxx', 'cshlib')
-
-        module.source = self.source
-        module.env = self.env.copy()
-        features = list(self.features)
-        features.remove("ns3module")
-        module.features.extend(features)
-        module.path = self.path
-        module.uselib = self.uselib
-        if hasattr(self, 'includes'):
-            module.includes = self.includes
-        if hasattr(self, "is_ns3_module"):
-            module.is_ns3_module = self.is_ns3_module
-
-        module.is_static = static
-        module.vnum = wutils.VNUM
-        # Add the proper path to the module's name.
-        module.target = '%s/ns3-%s' % (bld.srcnode.relpath_gen(self.path), name)
-        # Set the libraries this module depends on.  
-        module.module_deps = list(dependencies)
-        if not static:
-            module.env.append_value('CXXFLAGS', module.env['shlib_CXXFLAGS'])
-            module.env.append_value('CCFLAGS', module.env['shlib_CXXFLAGS'])
-            # Turn on the link flags for shared libraries if we have the
-            # proper compiler and platform.
-            if module.env['CXX_NAME'] in ['gcc', 'icc'] and module.env['WL_SONAME_SUPPORTED']:
-                # Get the module library name without any relative paths
-                # at its beginning because all of the libraries will end
-                # up in the same directory.
-                module_library_name = os.path.basename(ccroot.get_target_name(module))
-                module.env.append_value('LINKFLAGS', '-Wl,--soname=%s' % module_library_name)
-        elif module.env['CXX_NAME'] in ['gcc', 'icc'] and \
-                os.uname()[4] == 'x86_64' and \
-                module.env['ENABLE_PYTHON_BINDINGS']:
-            # enable that flag for static builds only on x86-64 platforms
-            # when gcc is present and only when we want python bindings
-            # (it's more efficient to not use this option if we can avoid it)
-            module.env.append_value('CXXFLAGS', '-mcmodel=large')
-            module.env.append_value('CCFLAGS', '-mcmodel=large')
-        module.env.append_value('CXXDEFINES', "NS3_MODULE_COMPILATION")
-        module.env.append_value('CCDEFINES', "NS3_MODULE_COMPILATION")
-
-        module.install_path = "${LIBDIR}"
-
-        return module
+# we need the 'ns3module' waf "feature" to be created because code
+# elsewhere looks for it to find the ns3 module objects.
+@TaskGen.feature('ns3module')
+def _add_test_code(module):
+    pass
 
 
 def create_ns3_module(bld, name, dependencies=(), test=False):
-    module = bld.new_task_gen('ns3module')
-    module.bld = bld
+    static = bool(bld.env.ENABLE_STATIC_NS3)
+    # Create a separate library for this module.
+    if static:
+        module = bld.new_task_gen(features=['cxx', 'cxxstlib', 'ns3module'])
+    else:
+        module = bld.new_task_gen(features=['cxx', 'cxxshlib', 'ns3module'])
+    module.target = '%s/ns3-%s' % (bld.srcnode.relpath_gen(module.path), name)
+    linkflags = []
+    cxxflags = []
+    ccflags = []
+    if not static:
+        cxxflags = module.env['shlib_CXXFLAGS']
+        ccflags = module.env['shlib_CXXFLAGS']
+        # Turn on the link flags for shared libraries if we have the
+        # proper compiler and platform.
+        if module.env['CXX_NAME'] in ['gcc', 'icc'] and module.env['WL_SONAME_SUPPORTED']:
+            # Get the module library name without any relative paths
+            # at its beginning because all of the libraries will end
+            # up in the same directory.
+            module_library_name = module.env.cshlib_PATTERN % (os.path.basename(module.target),)
+            linkflags = '-Wl,--soname=' + module_library_name
+    cxxdefines = ["NS3_MODULE_COMPILATION"]
+    ccdefines = ["NS3_MODULE_COMPILATION"]
+
+    module.env.append_value('CXXFLAGS', cxxflags)
+    module.env.append_value('CCFLAGS', ccflags)
+    module.env.append_value('LINKFLAGS', linkflags)
+    module.env.append_value('CXXDEFINES', cxxdefines)
+    module.env.append_value('CCDEFINES', ccdefines)
+
+    module.is_static = static
+    module.vnum = wutils.VNUM
+    # Add the proper path to the module's name.
+    # Set the libraries this module depends on.  
+    module.module_deps = list(dependencies)
+
+    module.install_path = "${LIBDIR}"
+
     module.name = "ns3-" + name
     module.dependencies = dependencies
     # Initially create an empty value for this because the pcfile
     # writing task assumes every module has a uselib attribute.
     module.uselib = ''
-    module.uselib_local = ['ns3-' + dep for dep in dependencies]
-    module.module_deps = list(dependencies)
+    module.use = ['ns3-' + dep for dep in dependencies]
     module.test = test
     module.is_ns3_module = True
+    module.ns3_dir_location = bld.path.relpath_gen(bld.srcnode)
 
+    module.env.append_value("INCLUDES", '#')
+
+    pcfilegen = bld(features='ns3pcfile')
+    pcfilegen.module = module
+    
     return module
 
+@TaskGen.feature("ns3testlib")
+@TaskGen.before_method("apply_incpaths")
+def apply_incpaths_ns3testlib(self):
+    if not self.source:
+        return
+    testdir = self.source[-1].parent.relpath_gen(self.bld.srcnode)
+    self.env.append_value("DEFINES", 'NS_TEST_SOURCEDIR="%s"' % (testdir,))
+
 
 def create_ns3_module_test_library(bld, name):
     # Create an ns3 module for the test library that depends only on
     # the module being tested.
     library_name = name + "-test"
-    library = bld.create_ns3_module(library_name, [name], test = True)
+    library = bld.create_ns3_module(library_name, [name], test=True)
+    library.features.append("ns3testlib")
 
     # Modify attributes for the test library that are different from a
     # normal module.
@@ -237,7 +151,7 @@
     library.module_name = 'ns3-' + name
 
     # Add this module and test library to the list.
-    bld.env.append_value('NS3_MODULES_WITH_TEST_LIBRARIES', (library.module_name, library.name))
+    bld.env.append_value('NS3_MODULES_WITH_TEST_LIBRARIES', [(library.module_name, library.name)])
 
     # Set the include path from the build directory to modules. 
     relative_path_from_build_to_here = bld.path.relpath_gen(bld.bldnode)
@@ -254,6 +168,9 @@
 
 
 def ns3_python_bindings(bld):
+    if Options.options.apiscan:
+        return
+
     # this method is called from a module wscript, so remember bld.path is not bindings/python!
     module_abs_src_path = bld.path.abspath()
     module = os.path.basename(module_abs_src_path)
@@ -262,8 +179,6 @@
 
     if not env['ENABLE_PYTHON_BINDINGS']:
         return
-    if env['BINDINGS_TYPE'] not in ('modular', 'both'):
-        return
 
     bindings_dir = bld.path.find_dir("bindings")
     if bindings_dir is None or not os.path.exists(bindings_dir.abspath()):
@@ -301,6 +216,7 @@
             source=("bindings/%s.py" % (module_py_name,)),
             target=('%s/%s.py' % (module_target_dir, module_py_name)))
         extension_name = '_%s' % (module_py_name,)
+        bld.install_files('${PYTHONDIR}/ns', ["bindings/%s.py" % (module_py_name,)])
     else:
         extension_name = module_py_name
 
@@ -308,7 +224,9 @@
     #if not debug:
     #    target.append('ns3modulegen.log')
 
-    argv = ['NS3_ENABLED_FEATURES=${FEATURES}', '${PYTHON}']
+    argv = ['NS3_ENABLED_FEATURES=${FEATURES}',
+            'GCC_RTTI_ABI_COMPLETE=${GCC_RTTI_ABI_COMPLETE}',
+            '${PYTHON}']
     #if debug:
     #    argv.extend(["-m", "pdb"])
     
@@ -321,40 +239,42 @@
         if was_enabled:
             features.append(name)
 
-    bindgen = bld.new_task_gen('command', source=source, target=target, command=argv)
+    bindgen = bld.new_task_gen(features=['command'], source=source, target=target, command=argv)
     bindgen.env['FEATURES'] = ','.join(features)
-    bindgen.dep_vars = ['FEATURES']
+    bindgen.dep_vars = ['FEATURES', "GCC_RTTI_ABI_COMPLETE"]
     bindgen.before = 'cxx'
-    bindgen.after = 'gen_ns3_module_header_task'
+    bindgen.after = 'gen_ns3_module_header'
     bindgen.name = "pybindgen(ns3 module %s)" % module
+    bindgen.install_path = None
 
     # generate the extension module
-    pymod = bld.new_task_gen(features='cxx cshlib pyext')
+    pymod = bld.new_task_gen(features='cxx cxxshlib pyext')
     pymod.source = ['bindings/ns3module.cc']
     pymod.target = '%s/%s' % (module_target_dir, extension_name)
     pymod.name = 'ns3module_%s' % module
-    pymod.uselib_local = ["%s--lib" % mod for mod in pymod.env['NS3_ENABLED_MODULES']] #  Should be '"ns3-"+module', but see bug 1117
+    pymod.use = ["%s" % mod for mod in pymod.env['NS3_ENABLED_MODULES']] #  Should be '"ns3-"+module', but see bug 1117
     if pymod.env['ENABLE_STATIC_NS3']:
         if sys.platform == 'darwin':
             pymod.env.append_value('LINKFLAGS', '-Wl,-all_load')
-            for mod in pymod.uselib_local:
-                mod = mod.split("--lib")[0]
+            for mod in pymod.usel:
+                #mod = mod.split("--lib")[0]
                 pymod.env.append_value('LINKFLAGS', '-l' + mod)
         else:
             pymod.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic')
-            for mod in pymod.uselib_local:
-                mod = mod.split("--lib")[0]
+            for mod in pymod.use:
+                #mod = mod.split("--lib")[0]
                 pymod.env.append_value('LINKFLAGS', '-l' + mod)
             pymod.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive')
-    defines = list(pymod.env['CXXDEFINES'])
+    defines = list(pymod.env['DEFINES'])
     defines.extend(['NS_DEPRECATED=', 'NS3_DEPRECATED_H'])
     if Options.platform == 'win32':
         try:
             defines.remove('_DEBUG') # causes undefined symbols on win32
         except ValueError:
             pass
-    pymod.env['CXXDEFINES'] = defines
-    pymod.includes = 'bindings'
+    pymod.env['DEFINES'] = defines
+    pymod.includes = '# bindings'
+    pymod.install_path = '${PYTHONDIR}/ns'
     return pymod
 
 
@@ -380,30 +300,35 @@
     bld.add_subdirs(list(all_modules))
 
     for module in all_modules:
-        modheader = bld.new_task_gen('ns3moduleheader')
+        modheader = bld.new_task_gen(features=['ns3moduleheader'])
         modheader.module = module.split('/')[-1]
 
 class ns3pcfile_task(Task.Task):
     after = 'cc cxx'
+
     def __str__(self):
         "string to display to the user"
         tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs])
         return 'pcfile: %s\n' % (tgt_str)
+
     def runnable_status(self):
         return super(ns3pcfile_task, self).runnable_status()
+
     def _self_libs(self, env, name, libdir):
         if env['ENABLE_STATIC_NS3']:
-            path_st = 'STATICLIBPATH_ST'
-            lib_st = 'STATICLIB_ST'
-            lib_marker = 'STATICLIB_MARKER'
+            path_st = 'STLIBPATH_ST'
+            lib_st = 'STLIB_ST'
+            lib_marker = 'STLIB_MARKER'
         else:
             path_st = 'LIBPATH_ST'
             lib_st = 'LIB_ST'
             lib_marker = 'SHLIB_MARKER'
-        retval = [env[path_st] % libdir,
-                  env[lib_marker],
-                  env[lib_st] % name]
+        retval = [env[path_st] % libdir]
+        if env[lib_marker]:
+            retval.append(env[lib_marker])
+        retval.append(env[lib_st] % name)
         return retval
+
     def _lib(self, env, dep):
         libpath = env['LIBPATH_%s' % dep]
         linkflags = env['LINKFLAGS_%s' % dep]
@@ -415,37 +340,44 @@
         for lib in libs:
             retval.append(env['LIB_ST'] % lib)
         return retval
+
     def _listify(self, v):
         if isinstance(v, list):
             return v
         else:
             return [v]
+
     def _cflags(self, dep):
         flags = self.env['CFLAGS_%s' % dep]
         return self._listify(flags)
+
     def _cxxflags(self, dep):
         return self._listify(self.env['CXXFLAGS_%s' % dep])
+
     def _defines(self, dep):
-        defines = self.env['CCDEFINES_%s' % dep] + self.env['CXXDEFINES_%s' % dep]
-        return [self.env['CCDEFINES_ST'] % define for define in self.env['CCDEFINES_%s' % dep]] + \
-            [self.env['CXXDEFINES_ST'] % define for define in self.env['CXXDEFINES_%s' % dep]] 
+        return [self.env['DEFINES_ST'] % define for define in self.env['DEFINES_%s' % dep]] 
+
     def _includes(self, dep):
-        includes = self.env['CPPPATH_%s' % dep]
+        includes = self.env['INCLUDES_%s' % dep]
         return [self.env['CPPPATH_ST'] % include for include in includes]
 
-    def _generate_pcfile(self, name, use, uselib_local, prefix, outfilename):
-        outfile = open(outfilename, 'w')
-        includedir = os.path.join(prefix, 'include')
-        libdir = os.path.join(prefix, 'lib')
-        libs = self._self_libs(self.env, name, '${libdir}')
+    def _generate_pcfile(self, name, use, env, outfilename):
+        outfile = open(outfilename, 'wt')
+        prefix = env.PREFIX
+        includedir = env.INCLUDEDIR
+        libdir = env.LIBDIR
+        libs = self._self_libs(env, name, '${libdir}')
         for dep in use:
-            libs = libs + self._lib(self.env, dep)
-        for dep in uselib_local:
-            libs = libs + [self.env['LIB_ST'] % dep]
+            libs += self._lib(env, dep)
+        for dep in env.LIBS:
+            libs += self.env['LIB_ST'] % dep
         cflags = [self.env['CPPPATH_ST'] % '${includedir}']
+        requires = []
         for dep in use:
             cflags = cflags + self._cflags(dep) + self._cxxflags(dep) + \
                 self._defines(dep) + self._includes(dep)
+            if dep.startswith('ns3-'):
+                requires.append("lib"+dep)
         print >> outfile, """
 prefix=%s
 libdir=%s
@@ -456,65 +388,57 @@
 Version: devel
 Libs: %s
 Cflags: %s
+Requires: %s
 """ % (prefix, libdir, includedir,
-       name, name, ' '.join(libs), ' '.join(cflags))
+       name, name, ' '.join(libs), ' '.join(cflags), ' '.join(requires))
         outfile.close()
 
     def run(self):
-        output_filename = self.outputs[0].bldpath(self.env)
-        self._generate_pcfile(self.module.name, self.module.uselib, 
-                              self.module.uselib_local,
-                              self.env['PREFIX'], output_filename)
+        output_filename = self.outputs[0].abspath()
+        self._generate_pcfile(self.module.name, 
+                              self.module.to_list(self.module.use),
+                              self.env, output_filename)
+
 
-class ns3pcfile_taskgen(TaskGen.task_gen):
-    def __init__(self, *args, **kwargs):
-        super(ns3pcfile_taskgen, self).__init__(*args, **kwargs)
-    def apply(self):
-        output_filename = 'lib%s.pc' % self.module.name
-        output_node = self.path.find_or_declare(output_filename)
-        assert output_node is not None, str(self)
-        task = self.create_task('ns3pcfile', env=self.env)
-        self.bld.install_files(os.path.join('${PREFIX}', 'lib', 'pkgconfig'),
-                               output_node)
-        task.set_outputs([output_node])
-        task.module = self.module
+@TaskGen.feature('ns3pcfile')
+@TaskGen.after_method('process_rule')
+def apply(self):
+    output_filename = 'lib%s.pc' % self.module.name
+    output_node = self.path.find_or_declare(output_filename)
+    assert output_node is not None, str(self)
+    task = self.create_task('ns3pcfile')
+    self.bld.install_files('${LIBDIR}/pkgconfig', output_node)
+    task.set_outputs([output_node])
+    task.module = self.module
+
 
 
-class ns3header_taskgen(TaskGen.task_gen):
-    """A set of NS-3 header files"""
-    COLOR = 'BLUE'
-    def __init__(self, *args, **kwargs):
-        super(ns3header_taskgen, self).__init__(*args, **kwargs)
-        self.install_path = None
-        self.sub_dir = None # if not None, header files will be published as ns3/sub_dir/file.h
-        self.module = None # module name
-        self.mode = 'install'
+@TaskGen.feature('ns3header')
+@TaskGen.after_method('process_rule')
+def apply_ns3header(self):
+    if self.module is None:
+        raise WafError("'module' missing on ns3headers object %s" % self)
+    ns3_dir_node = self.bld.path.find_dir("ns3")
+    for filename in set(self.to_list(self.source)):
+        src_node = self.path.find_resource(filename)
+        if src_node is None:
+            raise WafError("source ns3 header file %s not found" % (filename,))
+        dst_node = ns3_dir_node.find_or_declare(src_node.name)
+        assert dst_node is not None
+        task = self.create_task('ns3header')
+        task.mode = getattr(self, 'mode', 'install')
+        if task.mode == 'install':
+            self.bld.install_files('${PREFIX}/include/ns3', [src_node])
+            task.set_inputs([src_node])
+            task.set_outputs([dst_node])
+        else:
+            task.header_to_remove = dst_node
+    self.headers = set(self.to_list(self.source))
+    self.source = '' # tell WAF not to process these files further
 
-    def apply(self):
-        for filename in set(self.to_list(self.source)):
-            src_node = self.path.find_resource(filename)
-        if self.module is None:
-            raise Utils.WafError("'module' missing on ns3headers object %s" % self)
-        ns3_dir_node = self.bld.path.find_dir("ns3")
-        if self.sub_dir is not None:
-            ns3_dir_node = ns3_dir_node.find_dir(self.sub_dir)
-        for filename in set(self.to_list(self.source)):
-            src_node = self.path.find_resource(filename)
-            if src_node is None:
-                raise Utils.WafError("source ns3 header file %s not found" % (filename,))
-            dst_node = ns3_dir_node.find_or_declare(os.path.basename(filename))
-            assert dst_node is not None
-            task = self.create_task('ns3header', env=self.env)
-            task.mode = self.mode
-            if self.mode == 'install':
-                self.bld.install_files('${PREFIX}/include/ns3', [src_node])
-                task.set_inputs([src_node])
-                task.set_outputs([dst_node])
-            else:
-                task.header_to_remove = dst_node
 
 class ns3header_task(Task.Task):
-    before = 'cc cxx gen_ns3_module_header_task'
+    before = 'cc cxx gen_ns3_module_header'
     color = 'BLUE'
 
     def __str__(self):
@@ -525,23 +449,41 @@
         if self.outputs: sep = ' -> '
         else: sep = ''
         if self.mode == 'remove':
-            return 'rm-ns3-header %s\n' % (self.header_to_remove.bldpath(self.env),)
+            return 'rm-ns3-header %s\n' % (self.header_to_remove.abspath(),)
         return 'install-ns3-header: %s%s%s\n' % (src_str, sep, tgt_str)
 
+    def __repr__(self):
+        return str(self)
+
+    def uid(self):
+        try:
+            return self.uid_
+        except AttributeError:
+            m = Utils.md5()
+            up = m.update
+            up(self.__class__.__name__.encode())
+            for x in self.inputs + self.outputs:
+                up(x.abspath().encode())
+            up(self.mode)
+            if self.mode == 'remove':
+                up(self.header_to_remove.abspath().encode())
+            self.uid_ = m.digest()
+            return self.uid_
+
     def runnable_status(self):
         if self.mode == 'remove':
-            if os.path.exists(self.header_to_remove.bldpath(self.env)):
-                return Constants.RUN_ME
+            if os.path.exists(self.header_to_remove.abspath()):
+                return Task.RUN_ME
             else:
-                return Constants.SKIP_ME
+                return Task.SKIP_ME
         else:
             return super(ns3header_task, self).runnable_status()
 
     def run(self):
         if self.mode == 'install':
             assert len(self.inputs) == len(self.outputs)
-            inputs = [node.srcpath(self.env) for node in self.inputs]
-            outputs = [node.bldpath(self.env) for node in self.outputs]
+            inputs = [node.abspath() for node in self.inputs]
+            outputs = [node.abspath() for node in self.outputs]
             for src, dst in zip(inputs, outputs):
                 try:
                     os.chmod(dst, 0600)
@@ -555,7 +497,7 @@
         else:
             assert len(self.inputs) == 0
             assert len(self.outputs) == 0
-            out_file_name = self.header_to_remove.bldpath(self.env)
+            out_file_name = self.header_to_remove.abspath()
             try:
                 os.unlink(out_file_name)
             except OSError, ex:
@@ -566,15 +508,15 @@
 
 class gen_ns3_module_header_task(Task.Task):
     before = 'cc cxx'
-    after = 'ns3header_task'
+    after = 'ns3header'
     color = 'BLUE'
 
     def runnable_status(self):
         if self.mode == 'remove':
-            if os.path.exists(self.header_to_remove.bldpath(self.env)):
-                return Constants.RUN_ME
+            if os.path.exists(self.header_to_remove.abspath()):
+                return Task.RUN_ME
             else:
-                return Constants.SKIP_ME
+                return Task.SKIP_ME
         else:
             return super(gen_ns3_module_header_task, self).runnable_status()
 
@@ -586,24 +528,23 @@
         if self.outputs: sep = ' -> '
         else: sep = ''
         if self.mode == 'remove':
-            return 'rm-module-header %s\n' % (self.header_to_remove.bldpath(self.env),)
+            return 'rm-module-header %s\n' % (self.header_to_remove.abspath(),)
         return 'gen-module-header: %s%s%s\n' % (src_str, sep, tgt_str)
 
     def run(self):
         if self.mode == 'remove':
             assert len(self.inputs) == 0
             assert len(self.outputs) == 0
-            out_file_name = self.header_to_remove.bldpath(self.env)
+            out_file_name = self.header_to_remove.abspath()
             try:
                 os.unlink(out_file_name)
             except OSError, ex:
                 if ex.errno != 2:
                     raise
             return 0
-        
         assert len(self.outputs) == 1
-        out_file_name = self.outputs[0].bldpath(self.env)
-        header_files = [os.path.basename(node.abspath(self.env)) for node in self.inputs]
+        out_file_name = self.outputs[0].get_bld().abspath()#self.env)
+        header_files = [os.path.basename(node.abspath()) for node in self.inputs]
         outfile = file(out_file_name, "w")
         header_files.sort()
 
@@ -631,7 +572,7 @@
         return 0
 
     def sig_explicit_deps(self):
-        self.m.update('\n'.join([node.abspath(self.env) for node in self.inputs]))
+        self.m.update('\n'.join([node.abspath() for node in self.inputs]))
         return self.m.digest()
 
     def unique_id(self):
@@ -645,50 +586,46 @@
             return self.uid
 
 
-class ns3moduleheader_taskgen(TaskGen.task_gen):
-    """
-    Generates a 'ns3/foo-module.h' header file that includes all
-    public ns3 headers of a certain module.
-    """
-    COLOR = 'BLUE'
-    def __init__(self, *args, **kwargs):
-        super(ns3moduleheader_taskgen, self).__init__(*args, **kwargs)
-        self.mode = 'install'
+# Generates a 'ns3/foo-module.h' header file that includes all public
+# ns3 headers of a certain module.
+@TaskGen.feature('ns3moduleheader')
+@TaskGen.after_method('process_rule')
+def apply_ns3moduleheader(self):
+    ## get all of the ns3 headers
+    ns3_dir_node = self.bld.path.find_dir("ns3")
+    all_headers_inputs = []
+    found_the_module = False
+    for ns3headers in self.bld.all_task_gen:
+        if 'ns3header' in getattr(ns3headers, "features", []):
+            if ns3headers.module != self.module:
+                continue
+            found_the_module = True
+            for source in ns3headers.headers:
+                source = os.path.basename(source)
+                node = ns3_dir_node.find_or_declare(os.path.basename(source))
+                if node is None:
+                    fatal("missing header file %s" % (source,))
+                all_headers_inputs.append(node)
+    if not found_the_module:
+        raise WafError("error finding headers for module %s" % self.module)
+    if not all_headers_inputs:
+        return
 
-    def apply(self):
-        ## get all of the ns3 headers
-        ns3_dir_node = self.bld.path.find_dir("ns3")
-        all_headers_inputs = []
-        found_the_module = False
-        for ns3headers in self.bld.all_task_gen:
-            if isinstance(ns3headers, ns3header_taskgen):
-                if ns3headers.module != self.module:
-                    continue
-                found_the_module = True
-                for source in set(ns3headers.to_list(ns3headers.source)):
-                    source = os.path.basename(source)
-                    node = ns3_dir_node.find_or_declare(os.path.basename(source))
-                    if node is None:
-                        fatal("missing header file %s" % (source,))
-                    all_headers_inputs.append(node)
-        if not found_the_module:
-            raise Utils.WscriptError("error finding headers for module %s" % self.module)
-        if not all_headers_inputs:
-            return
-        all_headers_outputs = [ns3_dir_node.find_or_declare("%s-module.h" % self.module)]
-        task = self.create_task('gen_ns3_module_header', env=self.env)
-        task.module = self.module
-        task.mode = self.mode
-        if self.mode == 'install':
-            self.bld.install_files('${PREFIX}/include/ns3', 
-                                   ns3_dir_node.find_or_declare("%s-module.h" % self.module))
-            task.set_inputs(all_headers_inputs)
-            task.set_outputs(all_headers_outputs)
-            module_obj = self.bld.name_to_obj("ns3-" + self.module, self.env)
-            assert module_obj is not None, self.module
-            task.module_deps = module_obj.module_deps
-        else:
-            task.header_to_remove = all_headers_outputs[0]
+    try:
+        module_obj = self.bld.get_tgen_by_name("ns3-" + self.module)
+    except WafError: # maybe the module was disabled, and therefore removed
+        return
 
-    def install(self):
-        pass
+    all_headers_outputs = [ns3_dir_node.find_or_declare("%s-module.h" % self.module)]
+    task = self.create_task('gen_ns3_module_header')
+    task.module = self.module
+    task.mode = getattr(self, "mode", "install")
+    if task.mode == 'install':
+        assert module_obj is not None, self.module
+        self.bld.install_files('${PREFIX}/include/ns3', 
+                               ns3_dir_node.find_or_declare("%s-module.h" % self.module))
+        task.set_inputs(all_headers_inputs)
+        task.set_outputs(all_headers_outputs)
+        task.module_deps = module_obj.module_deps
+    else:
+        task.header_to_remove = all_headers_outputs[0]