From 54b20047fdcbb07f5d7ce0f094a4c6b5b476c882 Mon Sep 17 00:00:00 2001 From: Lauri Sormunen Date: Thu, 6 Apr 2017 20:32:04 -0700 Subject: [PATCH] build: (fixes #2630) Add contrib directory in parallel to src --- CHANGES.html | 8 +- contrib/wscript | 752 +++++++++++++++++++++++++++++++++++++++++++++++ doc/doxygen.conf | 3 +- test.py | 21 ++ wscript | 81 +++-- 5 files changed, 840 insertions(+), 25 deletions(-) create mode 100644 contrib/wscript 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:

    - +

    Changes to build system:

    + 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)"