diff --git a/CHANGES.html b/CHANGES.html
index dd68a574b..d2e74c9a8 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -112,14 +112,16 @@ is now exported by WifiNetDevice.
The DequeueAll method of Queue has been renamed Flush
-
-Changes to build system:
-
- The attributes WifiPhy::TxAntennas and WifiPhy::RxAntennas,
and the related accessor methods, were replaced by WifiPhy::MaxSupportedTxSpatialStreams
and WifiPhy::MaxSupportedRxSpatialStreams. A new attribute WifiPhy::Antennas
was added to allow users to define the number of physical antennas on the device.
+
+Changes to build system:
+
+- Modules can now be located in the 'contrib/' directory in addition to 'src/'
+
- Behavior for running Python programs was aligned with that of C++ programs; the list of modules built is no longer printed out.
diff --git a/contrib/wscript b/contrib/wscript
new file mode 100644
index 000000000..1ac1c517a
--- /dev/null
+++ b/contrib/wscript
@@ -0,0 +1,752 @@
+
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+from __future__ import print_function
+import os, os.path
+import sys
+import shutil
+import types
+import warnings
+
+from waflib import TaskGen, Task, Options, Build, Utils
+from waflib.Errors import WafError
+import wutils
+
+try:
+ set
+except NameError:
+ from sets import Set as set # Python 2.3 fallback
+
+
+
+all_modules = []
+for dirname in os.listdir('contrib'):
+ if dirname.startswith('.') or dirname == 'CVS':
+ continue
+ path = os.path.join('contrib', 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 "
+ " --run and --shell; moreover, only works in some"
+ " specific platforms, such as Linux and Solaris)"),
+ action="store_true", dest='enable_rpath', default=False)
+
+ opt.add_option('--enable-modules',
+ help=("Build only these modules (and dependencies)"),
+ dest='enable_modules')
+
+ opt.load('boost', tooldir=['waf-tools'])
+
+ for module in all_modules:
+ opt.recurse(module, mandatory=False)
+
+def configure(conf):
+ conf.env['REQUIRED_BOOST_LIBS'] = []
+ for module in all_modules:
+ conf.recurse (module, name="required_boost_libs", mandatory=False)
+
+ if conf.env['REQUIRED_BOOST_LIBS'] is not []:
+ conf.load('boost')
+ conf.check_boost(lib=' '.join (conf.env['REQUIRED_BOOST_LIBS']), mandatory=False)
+ if not conf.env['LIB_BOOST']:
+ conf.check_boost(lib=' '.join (conf.env['REQUIRED_BOOST_LIBS']), libpath="/usr/lib64", mandatory=False)
+ if not conf.env['LIB_BOOST']:
+ conf.env['LIB_BOOST'] = []
+
+ # Append blddir to the module path before recursing into modules
+ blddir = os.path.abspath(os.path.join(conf.bldnode.abspath(), conf.variant))
+ conf.env.append_value('NS3_MODULE_PATH', blddir)
+
+ for module in all_modules:
+ conf.recurse(module, mandatory=False)
+
+ # Remove duplicate path items
+ conf.env['NS3_MODULE_PATH'] = wutils.uniquify_list(conf.env['NS3_MODULE_PATH'])
+
+ if Options.options.enable_rpath:
+ conf.env.append_value('RPATH', '-Wl,-rpath,%s' % (os.path.join(blddir),))
+
+ ## Used to link the 'test-runner' program with all of ns-3 code
+ conf.env['NS3_CONTRIBUTED_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_modules]
+
+
+
+# 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):
+ static = bool(bld.env.ENABLE_STATIC_NS3)
+ # Create a separate library for this module.
+ if static:
+ module = bld(features='cxx cxxstlib ns3module')
+ else:
+ module = bld(features='cxx cxxshlib ns3module')
+ module.target = '%s/ns%s-%s%s' % (bld.srcnode.path_from(module.path), wutils.VERSION,
+ name, bld.env.BUILD_SUFFIX)
+ 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.use = ['ns3-' + dep for dep in dependencies]
+ module.test = test
+ module.is_ns3_module = True
+ module.ns3_dir_location = bld.path.path_from(bld.srcnode)
+
+ module.env.append_value("INCLUDES", '#')
+
+ module.pcfilegen = bld(features='ns3pcfile')
+ module.pcfilegen.module = module.name
+
+ 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.path_from(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.features += " ns3testlib"
+
+ # Modify attributes for the test library that are different from a
+ # normal module.
+ del library.is_ns3_module
+ library.is_ns3_module_test_library = True
+ 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)])
+
+ # Set the include path from the build directory to modules.
+ relative_path_from_build_to_here = bld.path.path_from(bld.bldnode)
+ include_flag = '-I' + relative_path_from_build_to_here
+ library.env.append_value('CXXFLAGS', include_flag)
+ library.env.append_value('CCFLAGS', include_flag)
+
+ return library
+
+def create_obj(bld, *args):
+ warnings.warn("(in %s) Use bld(...) call now, instead of bld.create_obj(...)" % str(bld.path),
+ DeprecationWarning, stacklevel=2)
+ return bld(*args)
+
+
+def ns3_python_bindings(bld):
+
+ # 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)
+ env = bld.env
+ env.append_value("MODULAR_BINDINGS_MODULES", "ns3-"+module)
+
+ if Options.options.apiscan:
+ return
+
+ if not env['ENABLE_PYTHON_BINDINGS']:
+ return
+
+ bindings_dir = bld.path.find_dir("bindings")
+ if bindings_dir is None or not os.path.exists(bindings_dir.abspath()):
+ warnings.warn("(in %s) Requested to build modular python bindings, but apidefs dir not found "
+ "=> skipped the bindings." % str(bld.path),
+ Warning, stacklevel=2)
+ return
+
+ if ("ns3-%s" % (module,)) not in env.NS3_ENABLED_MODULES:
+ #print "bindings for module %s which is not enabled, skip" % module
+ return
+
+ env.append_value('PYTHON_MODULES_BUILT', module)
+ try:
+ apidefs = env['PYTHON_BINDINGS_APIDEFS'].replace("-", "_")
+ except AttributeError:
+ # we likely got an empty list for env['PYTHON_BINDINGS_APIDEFS']
+ return
+
+ #debug = ('PYBINDGEN_DEBUG' in os.environ)
+ debug = True # XXX
+ source = [bld.srcnode.find_resource('bindings/python/ns3modulegen-modular.py'),
+ bld.path.find_resource("bindings/modulegen__%s.py" % apidefs)]
+
+ modulegen_customizations = bindings_dir.find_resource("modulegen_customizations.py")
+ if modulegen_customizations is not None:
+ source.append(modulegen_customizations)
+
+ modulegen_local = bld.path.find_resource("bindings/modulegen_local.py")
+ # the local customization file may or not exist
+ if modulegen_local is not None:
+ source.append("bindings/modulegen_local.py")
+
+ module_py_name = module.replace('-', '_')
+ module_target_dir = bld.srcnode.find_dir("bindings/python/ns").path_from(bld.path)
+
+ # if bindings/.py exists, it becomes the module frontend, and the C extension befomes _
+ if bld.path.find_resource("bindings/%s.py" % (module_py_name,)) is not None:
+ bld(features='copy',
+ 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('${PYTHONARCHDIR}/ns', ["bindings/%s.py" % (module_py_name,)])
+ else:
+ extension_name = module_py_name
+
+ target = ['bindings/ns3module.cc', 'bindings/ns3module.h', 'bindings/ns3modulegen.log']
+ #if not debug:
+ # target.append('ns3modulegen.log')
+
+ argv = ['NS3_ENABLED_FEATURES=${FEATURES}',
+ 'GCC_RTTI_ABI_COMPLETE=${GCC_RTTI_ABI_COMPLETE}',
+ '${PYTHON}']
+ #if debug:
+ # argv.extend(["-m", "pdb"])
+
+ argv.extend(['${SRC[0]}', module_abs_src_path, apidefs, extension_name, '${TGT[0]}'])
+
+ argv.extend(['2>', '${TGT[2]}']) # 2> ns3modulegen.log
+
+ features = []
+ for (name, caption, was_enabled, reason_not_enabled) in env['NS3_OPTIONAL_FEATURES']:
+ if was_enabled:
+ features.append(name)
+
+ bindgen = bld(features='command', source=source, target=target, command=argv)
+ bindgen.env['FEATURES'] = ','.join(features)
+ bindgen.dep_vars = ['FEATURES', "GCC_RTTI_ABI_COMPLETE"]
+ bindgen.before = 'cxx'
+ bindgen.after = 'gen_ns3_module_header'
+ bindgen.name = "pybindgen(ns3 module %s)" % module
+ bindgen.install_path = None
+
+ # generate the extension module
+ pymod = bld(features='cxx cxxshlib pyext')
+ pymod.source = ['bindings/ns3module.cc']
+ pymod.target = '%s/%s' % (module_target_dir, extension_name)
+ pymod.name = 'ns3module_%s' % module
+ 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.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.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['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['DEFINES'] = defines
+ pymod.includes = '# bindings'
+ pymod.install_path = '${PYTHONARCHDIR}/ns'
+
+ # Workaround to a WAF bug, remove this when ns-3 upgrades to WAF > 1.6.10
+ # https://www.nsnam.org/bugzilla/show_bug.cgi?id=1335
+ # http://code.google.com/p/waf/issues/detail?id=1098
+ if Utils.unversioned_sys_platform() == 'darwin':
+ pymod.mac_bundle = True
+
+ return pymod
+
+
+def build(bld):
+ bld.create_ns3_module = types.MethodType(create_ns3_module, bld)
+ bld.create_ns3_module_test_library = types.MethodType(create_ns3_module_test_library, bld)
+ bld.create_obj = types.MethodType(create_obj, bld)
+ bld.ns3_python_bindings = types.MethodType(ns3_python_bindings, bld)
+
+ # Remove these modules from the list of all modules.
+ for not_built in bld.env['MODULES_NOT_BUILT']:
+
+ # XXX Because these modules are located in subdirectories of
+ # test, their names in the all_modules list include the extra
+ # relative path "test/". If these modules are moved into the
+ # src directory, then this if block should be removed.
+ if not_built == 'ns3tcp' or not_built == 'ns3wifi':
+ not_built = 'test/' + not_built
+
+ if not_built in all_modules:
+ all_modules.remove(not_built)
+
+ bld.recurse(list(all_modules))
+
+ for module in all_modules:
+ modheader = bld(features='ns3moduleheader')
+ modheader.module = module.split('/')[-1]
+
+class ns3pcfile_task(Task.Task):
+ after = 'cxx'
+
+ def __str__(self):
+ "string to display to the user"
+ tgt_str = ' '.join([a.bldpath() 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 = '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]
+ 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]
+ libs = env['LIB_%s' % dep]
+ retval = []
+ for path in libpath:
+ retval.append(env['LIBPATH_ST'] % path)
+ retval = retval + linkflags
+ 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):
+ return [self.env['DEFINES_ST'] % define for define in self.env['DEFINES_%s' % dep]]
+
+ def _includes(self, dep):
+ includes = self.env['INCLUDES_%s' % dep]
+ return [self.env['CPPPATH_ST'] % include for include in includes]
+
+ def _generate_pcfile(self, name, use, env, outfilename):
+ outfile = open(outfilename, 'wt')
+ prefix = env.PREFIX
+ includedir = Utils.subst_vars('${INCLUDEDIR}/%s%s' % (wutils.APPNAME, wutils.VERSION), env)
+ libdir = env.LIBDIR
+ libs = self._self_libs(env, "%s%s-%s%s" % (wutils.APPNAME, wutils.VERSION, name[4:], env.BUILD_SUFFIX), '${libdir}')
+ for dep in use:
+ 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-'):
+ dep_name = dep[4:]
+ requires.append("libns%s-%s%s" % (wutils.VERSION, dep_name, env.BUILD_SUFFIX))
+ print("""\
+prefix=%s
+libdir=%s
+includedir=%s
+
+Name: lib%s
+Description: ns-3 module %s
+Version: %s
+Libs: %s
+Cflags: %s
+Requires: %s\
+""" % (prefix, libdir, includedir,
+ name, name, wutils.VERSION, ' '.join(libs), ' '.join(cflags), ' '.join(requires)), file=outfile)
+ outfile.close()
+
+ def run(self):
+ output_filename = self.outputs[0].abspath()
+ self._generate_pcfile(self.module.name,
+ self.module.to_list(self.module.use),
+ self.env, output_filename)
+
+
+@TaskGen.feature('ns3pcfile')
+@TaskGen.after_method('process_rule')
+def apply(self):
+ module = self.bld.find_ns3_module(self.module)
+ output_filename = 'lib%s.pc' % os.path.basename(module.target)
+ 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 = module
+
+
+
+@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_or_declare("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('${INCLUDEDIR}/%s%s/ns3' % (wutils.APPNAME, wutils.VERSION), [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
+
+
+class ns3header_task(Task.Task):
+ before = 'cxx gen_ns3_module_header'
+ color = 'BLUE'
+
+ def __str__(self):
+ "string to display to the user"
+ env = self.env
+ src_str = ' '.join([a.bldpath() for a in self.inputs])
+ tgt_str = ' '.join([a.bldpath() for a in self.outputs])
+ if self.outputs: sep = ' -> '
+ else: sep = ''
+ if self.mode == 'remove':
+ return 'rm-ns3-header %s' % (self.header_to_remove.abspath(),)
+ return 'install-ns3-header: %s' % (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.encode())
+ 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.abspath()):
+ return Task.RUN_ME
+ else:
+ 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.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, 0o600)
+ except OSError:
+ pass
+ shutil.copy2(src, dst)
+ ## make the headers in builddir read-only, to prevent
+ ## accidental modification
+ os.chmod(dst, 0o400)
+ return 0
+ else:
+ assert len(self.inputs) == 0
+ assert len(self.outputs) == 0
+ out_file_name = self.header_to_remove.abspath()
+ try:
+ os.unlink(out_file_name)
+ except OSError as ex:
+ if ex.errno != 2:
+ raise
+ return 0
+
+
+@TaskGen.feature('ns3privateheader')
+@TaskGen.after_method('process_rule')
+def apply_ns3privateheader(self):
+ if self.module is None:
+ raise WafError("'module' missing on ns3headers object %s" % self)
+ ns3_dir_node = self.bld.path.find_or_declare("ns3/private")
+ 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('ns3privateheader')
+ task.mode = getattr(self, 'mode', 'install')
+ if task.mode == 'install':
+ 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
+
+class ns3privateheader_task(Task.Task):
+ before = 'cxx gen_ns3_module_header'
+ after = 'ns3header'
+ color = 'BLUE'
+
+ def __str__(self):
+ "string to display to the user"
+ env = self.env
+ src_str = ' '.join([a.bldpath() for a in self.inputs])
+ tgt_str = ' '.join([a.bldpath() for a in self.outputs])
+ if self.outputs: sep = ' -> '
+ else: sep = ''
+ if self.mode == 'remove':
+ return 'rm-ns3-header %s' % (self.header_to_remove.abspath(),)
+ return 'install-ns3-header: %s' % (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.encode())
+ 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.abspath()):
+ return Task.RUN_ME
+ else:
+ return Task.SKIP_ME
+ else:
+ return super(ns3privateheader_task, self).runnable_status()
+
+ def run(self):
+ if self.mode == 'install':
+ assert len(self.inputs) == len(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, 0o600)
+ except OSError:
+ pass
+ shutil.copy2(src, dst)
+ ## make the headers in builddir read-only, to prevent
+ ## accidental modification
+ os.chmod(dst, 0o400)
+ return 0
+ else:
+ assert len(self.inputs) == 0
+ assert len(self.outputs) == 0
+ out_file_name = self.header_to_remove.abspath()
+ try:
+ os.unlink(out_file_name)
+ except OSError as ex:
+ if ex.errno != 2:
+ raise
+ return 0
+
+
+class gen_ns3_module_header_task(Task.Task):
+ before = 'cxx'
+ after = 'ns3header'
+ color = 'BLUE'
+
+ def runnable_status(self):
+ if self.mode == 'remove':
+ if os.path.exists(self.header_to_remove.abspath()):
+ return Task.RUN_ME
+ else:
+ return Task.SKIP_ME
+ else:
+ return super(gen_ns3_module_header_task, self).runnable_status()
+
+ def __str__(self):
+ "string to display to the user"
+ env = self.env
+ src_str = ' '.join([a.bldpath() for a in self.inputs])
+ tgt_str = ' '.join([a.bldpath() for a in self.outputs])
+ if self.outputs: sep = ' -> '
+ else: sep = ''
+ if self.mode == 'remove':
+ return 'rm-module-header %s' % (self.header_to_remove.abspath(),)
+ return 'gen-module-header: %s' % (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.abspath()
+ try:
+ os.unlink(out_file_name)
+ except OSError as ex:
+ if ex.errno != 2:
+ raise
+ return 0
+ assert len(self.outputs) == 1
+ out_file_name = self.outputs[0].get_bld().abspath()#self.env)
+ header_files = [os.path.basename(node.abspath()) for node in self.inputs]
+ outfile = open(out_file_name, "w")
+ header_files.sort()
+
+ print("""
+#ifdef NS3_MODULE_COMPILATION
+# error "Do not include ns3 module aggregator headers from other modules; these are meant only for end user scripts."
+#endif
+
+#ifndef NS3_MODULE_%s
+ """ % (self.module.upper().replace('-', '_'),), file=outfile)
+
+ # if self.module_deps:
+ # print >> outfile, "// Module dependencies:"
+ # for dep in self.module_deps:
+ # print >> outfile, "#include \"%s-module.h\"" % dep
+
+ print(file=outfile)
+ print("// Module headers:", file=outfile)
+ for header in header_files:
+ print("#include \"%s\"" % (header,), file=outfile)
+
+ print("#endif", file=outfile)
+
+ outfile.close()
+ return 0
+
+ def sig_explicit_deps(self):
+ self.m.update('\n'.join(sorted([node.abspath() for node in self.inputs])).encode('utf-8'))
+ return self.m.digest()
+
+ def unique_id(self):
+ try:
+ return self.uid
+ except AttributeError:
+ "this is not a real hot zone, but we want to avoid surprizes here"
+ m = Utils.md5()
+ m.update("ns-3-module-header-%s" % self.module)
+ self.uid = m.digest()
+ return self.uid
+
+
+# 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_or_declare("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 sorted(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
+
+ try:
+ module_obj = self.bld.get_tgen_by_name("ns3-" + self.module)
+ except WafError: # maybe the module was disabled, and therefore removed
+ return
+
+ 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('${INCLUDEDIR}/%s%s/ns3' % (wutils.APPNAME, wutils.VERSION),
+ 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]
diff --git a/doc/doxygen.conf b/doc/doxygen.conf
index 047692378..20ecfba8a 100644
--- a/doc/doxygen.conf
+++ b/doc/doxygen.conf
@@ -788,7 +788,8 @@ INPUT = doc/modules \
doc/introspected-doxygen.h \
examples \
utils \
- src
+ src \
+ contrib
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/test.py b/test.py
index 484ca266e..15e08e467 100755
--- a/test.py
+++ b/test.py
@@ -46,6 +46,7 @@ except ImportError:
#
interesting_config_items = [
"NS3_ENABLED_MODULES",
+ "NS3_ENABLED_CONTRIBUTED_MODULES",
"NS3_MODULE_PATH",
"NSC_ENABLED",
"ENABLE_REAL_TIME",
@@ -1153,6 +1154,26 @@ def run_tests():
example_tests,
example_names_original,
python_tests)
+
+ for module in NS3_ENABLED_CONTRIBUTED_MODULES:
+ # Remove the "ns3-" from the module name.
+ module = module[len("ns3-"):]
+
+ # Set the directories and paths for this example.
+ module_directory = os.path.join("contrib", module)
+ example_directory = os.path.join(module_directory, "examples")
+ examples_to_run_path = os.path.join(module_directory, "test", "examples-to-run.py")
+ cpp_executable_dir = os.path.join(NS3_BUILDDIR, example_directory)
+ python_script_dir = os.path.join(example_directory)
+
+ # Parse this module's file.
+ parse_examples_to_run_file(
+ examples_to_run_path,
+ cpp_executable_dir,
+ python_script_dir,
+ example_tests,
+ example_names_original,
+ python_tests)
#
# If lots of logging is enabled, we can crash Python when it tries to
diff --git a/wscript b/wscript
index 1c32547a9..3452ee861 100644
--- a/wscript
+++ b/wscript
@@ -246,7 +246,7 @@ def options(opt):
opt.recurse('src')
opt.recurse('bindings/python')
opt.recurse('src/internet')
-
+ opt.recurse('contrib')
def _check_compilation_flag(conf, flag, mode='cxx', linkflags=None):
"""
@@ -457,21 +457,46 @@ def configure(conf):
conf.recurse('bindings/python')
conf.recurse('src')
+ conf.recurse('contrib')
# Set the list of enabled modules.
if Options.options.enable_modules:
# Use the modules explicitly enabled.
- conf.env['NS3_ENABLED_MODULES'] = ['ns3-'+mod for mod in
- Options.options.enable_modules.split(',')]
+ _enabled_mods = []
+ _enabled_contrib_mods = []
+ for mod in Options.options.enable_modules.split(','):
+ if mod in conf.env['NS3_MODULES'] and mod.startswith('ns3-'):
+ _enabled_mods.append(mod)
+ elif 'ns3-' + mod in conf.env['NS3_MODULES']:
+ _enabled_mods.append('ns3-' + mod)
+ elif mod in conf.env['NS3_CONTRIBUTED_MODULES'] and mod.startswith('ns3-'):
+ _enabled_contrib_mods.append(mod)
+ elif 'ns3-' + mod in conf.env['NS3_CONTRIBUTED_MODULES']:
+ _enabled_contrib_mods.append('ns3-' + mod)
+ conf.env['NS3_ENABLED_MODULES'] = _enabled_mods
+ conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = _enabled_contrib_mods
+
else:
# Use the enabled modules list from the ns3 configuration file.
if modules_enabled[0] == 'all_modules':
# Enable all modules if requested.
conf.env['NS3_ENABLED_MODULES'] = conf.env['NS3_MODULES']
+ conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = conf.env['NS3_CONTRIBUTED_MODULES']
else:
# Enable the modules from the list.
- conf.env['NS3_ENABLED_MODULES'] = ['ns3-'+mod for mod in
- modules_enabled]
+ _enabled_mods = []
+ _enabled_contrib_mods = []
+ for mod in modules_enabled:
+ if mod in conf.env['NS3_MODULES'] and mod.startswith('ns3-'):
+ _enabled_mods.append(mod)
+ elif 'ns3-' + mod in conf.env['NS3_MODULES']:
+ _enabled_mods.append('ns3-' + mod)
+ elif mod in conf.env['NS3_CONTRIBUTED_MODULES'] and mod.startswith('ns3-'):
+ _enabled_contrib_mods.append(mod)
+ elif 'ns3-' + mod in conf.env['NS3_CONTRIBUTED_MODULES']:
+ _enabled_contrib_mods.append('ns3-' + mod)
+ conf.env['NS3_ENABLED_MODULES'] = _enabled_mods
+ conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'] = _enabled_contrib_mods
# Add the template module to the list of enabled modules that
# should not be built if this is a static build on Darwin. They
@@ -487,6 +512,8 @@ def configure(conf):
conf.env['NS3_ENABLED_MODULES'].remove(not_built_name)
if not conf.env['NS3_ENABLED_MODULES']:
raise WafError('Exiting because the ' + not_built + ' module can not be built and it was the only one enabled.')
+ elif not_built_name in conf.env['NS3_ENABLED_CONTRIBUTED_MODULES']:
+ conf.env['NS3_ENABLED_CONTRIBUTED_MODULES'].remove(not_built_name)
conf.recurse('src/mpi')
@@ -719,7 +746,8 @@ def add_examples_programs(bld):
return
def add_scratch_programs(bld):
- all_modules = [mod[len("ns3-"):] for mod in bld.env['NS3_ENABLED_MODULES']]
+ all_modules = [mod[len("ns3-"):] for mod in bld.env['NS3_ENABLED_MODULES'] + bld.env['NS3_ENABLED_CONTRIBUTED_MODULES']]
+
try:
for filename in os.listdir("scratch"):
if filename.startswith('.') or filename == 'CVS':
@@ -835,18 +863,21 @@ def build(bld):
# process subfolders from here
bld.recurse('src')
+ bld.recurse('contrib')
# If modules have been enabled, then set lists of enabled modules
# and enabled module test libraries.
- if env['NS3_ENABLED_MODULES']:
+ if env['NS3_ENABLED_MODULES'] or env['NS3_ENABLED_CONTRIBUTED_MODULES']:
+
modules = env['NS3_ENABLED_MODULES']
+ contribModules = env['NS3_ENABLED_CONTRIBUTED_MODULES']
# Find out about additional modules that need to be enabled
# due to dependency constraints.
changed = True
while changed:
changed = False
- for module in modules:
+ for module in modules + contribModules:
module_obj = bld.get_tgen_by_name(module)
if module_obj is None:
raise ValueError("module %s not found" % module)
@@ -854,31 +885,35 @@ def build(bld):
for dep in module_obj.use:
if not dep.startswith('ns3-'):
continue
- if dep not in modules:
- modules.append(dep)
+ if dep not in modules and dep not in contribModules:
+ if dep in env['NS3_MODULES']: modules.append(dep)
+ elif dep in env['NS3_CONTRIBUTED_MODULES']: contribModules.append(dep)
changed = True
env['NS3_ENABLED_MODULES'] = modules
+ env['NS3_ENABLED_CONTRIBUTED_MODULES'] = contribModules
+
# If tests are being built, then set the list of the enabled
# module test libraries.
if env['ENABLE_TESTS']:
for (mod, testlib) in bld.env['NS3_MODULES_WITH_TEST_LIBRARIES']:
- if mod in bld.env['NS3_ENABLED_MODULES']:
+ if mod in bld.env['NS3_ENABLED_MODULES'] or mod in bld.env['NS3_ENABLED_CONTRIBUTED_MODULES']:
bld.env.append_value('NS3_ENABLED_MODULE_TEST_LIBRARIES', testlib)
add_examples_programs(bld)
add_scratch_programs(bld)
- if env['NS3_ENABLED_MODULES']:
+ if env['NS3_ENABLED_MODULES'] or env['NS3_ENABLED_CONTRIBUTED_MODULES']:
modules = env['NS3_ENABLED_MODULES']
+ contribModules = env['NS3_ENABLED_CONTRIBUTED_MODULES']
# Exclude the programs other misc task gens that depend on disabled modules
for obj in list(bld.all_task_gen):
# check for ns3moduleheader_taskgen
if 'ns3moduleheader' in getattr(obj, "features", []):
- if ("ns3-%s" % obj.module) not in modules:
+ if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules:
obj.mode = 'remove' # tell it to remove headers instead of installing
# check for programs
@@ -886,7 +921,7 @@ def build(bld):
# this is an NS-3 program (bld.create_ns3_program)
program_built = True
for dep in obj.ns3_module_dependencies:
- if dep not in modules: # prog. depends on a module that isn't enabled?
+ if dep not in modules and dep not in contribModules: # prog. depends on a module that isn't enabled?
bld.exclude_taskgen(obj)
program_built = False
break
@@ -907,38 +942,42 @@ def build(bld):
bld.env.append_value('NS3_RUNNABLE_PROGRAMS', object_relative_path)
# disable the modules themselves
- if hasattr(obj, "is_ns3_module") and obj.name not in modules:
+ if hasattr(obj, "is_ns3_module") and obj.name not in modules and obj.name not in contribModules:
bld.exclude_taskgen(obj) # kill the module
# disable the module test libraries
if hasattr(obj, "is_ns3_module_test_library"):
- if not env['ENABLE_TESTS'] or (obj.module_name not in modules):
+ if not env['ENABLE_TESTS'] or ((obj.module_name not in modules) and (obj.module_name not in contribModules)) :
bld.exclude_taskgen(obj) # kill the module test library
# disable the ns3header_taskgen
if 'ns3header' in getattr(obj, "features", []):
- if ("ns3-%s" % obj.module) not in modules:
+ if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules:
obj.mode = 'remove' # tell it to remove headers instead of installing
# disable the ns3privateheader_taskgen
if 'ns3privateheader' in getattr(obj, "features", []):
- if ("ns3-%s" % obj.module) not in modules:
+ if ("ns3-%s" % obj.module) not in modules and ("ns3-%s" % obj.module) not in contribModules:
+
obj.mode = 'remove' # tell it to remove headers instead of installing
# disable pcfile taskgens for disabled modules
if 'ns3pcfile' in getattr(obj, "features", []):
- if obj.module not in bld.env.NS3_ENABLED_MODULES:
+ if obj.module not in bld.env.NS3_ENABLED_MODULES and obj.module not in bld.env.NS3_ENABLED_CONTRIBUTED_MODULES:
bld.exclude_taskgen(obj)
if env['NS3_ENABLED_MODULES']:
env['NS3_ENABLED_MODULES'] = list(modules)
+ if env['NS3_ENABLED_CONTRIBUTED_MODULES']:
+ env['NS3_ENABLED_CONTRIBUTED_MODULES'] = list(contribModules)
+
# Determine which scripts will be runnable.
for (script, dependencies) in bld.env['NS3_SCRIPT_DEPENDENCIES']:
script_runnable = True
for dep in dependencies:
- if dep not in modules:
+ if dep not in modules and dep not in contribModules:
script_runnable = False
break
@@ -1018,7 +1057,7 @@ def shutdown(ctx):
print()
print('Modules built:')
names_without_prefix = []
- for name in env['NS3_ENABLED_MODULES']:
+ for name in env['NS3_ENABLED_MODULES'] + env['NS3_ENABLED_CONTRIBUTED_MODULES']:
name1 = name[len('ns3-'):]
if name not in env.MODULAR_BINDINGS_MODULES:
name1 += " (no Python)"