## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- from __future__ import print_function from collections import deque import os, os.path import sys import shutil import types import warnings from waflib import TaskGen, Task, Options, Build, Utils, Context from waflib.Errors import WafError import wutils try: set except NameError: from sets import Set as set # Python 2.3 fallback # Allow mulitple modules to live in a single directory in contrib. # For example, a directory structure like: # contrib/package/module1 # /module2 # Useful for external projects that are building interdependent modules that # are logically packaged together. def find_contrib_modules(ctx, log=False): modules = [] entries = deque( (ctx.path, d) for d in ctx.path.listdir() ) while entries: parent, entry = entries.popleft() if not entry or entry[0] == '.' or entry.endswith('CVS'): continue node = parent.find_node(entry) if not node: continue if node.isdir(): #does this directory have a wscript file? wscript_node = node.find_node('wscript') if wscript_node: #found a wscript file, treat this directory as a module. #get the path relative to the context path module_path = node.path_from(ctx.path) modules.append(module_path) if log: ctx.msg("Found contrib module", module_path) else: #maybe this directory is a project, #add its children to the list of entries to process entries.extend( (node, d) for d in node.listdir() ) return sorted(modules) def get_required_boost_libs(conf): for module in find_contrib_modules(conf): conf.recurse (module, name="required_boost_libs", mandatory=False) def options(opt): for module in find_contrib_modules(opt): opt.recurse(module, mandatory=False) def configure(conf): all_contrib_modules = find_contrib_modules(conf, True) # Append blddir to the module path before recursing into modules # This is required for contrib modules with test suites blddir = os.path.abspath(os.path.join(conf.bldnode.abspath(), conf.variant)) conf.env.append_value('NS3_MODULE_PATH', blddir) # Remove duplicate path items conf.env['NS3_MODULE_PATH'] = wutils.uniquify_list(conf.env['NS3_MODULE_PATH']) for module in all_contrib_modules: conf.recurse(module, mandatory=False) ## 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_contrib_modules] # we need the 'ns3module' waf "feature" to be created because code # elsewhere looks for it to find the ns3 module objects. 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') target = '%s/lib/ns%s-%s%s' % (bld.srcnode.path_from(module.path), wutils.VERSION, name, bld.env.BUILD_SUFFIX) module.target = target 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", Context.out_dir) module.pcfilegen = bld(features='ns3pcfile') module.pcfilegen.module = module.name return module 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_CONTRIBUTED_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 = ['cxxprogram', 'cxxshlib', 'cxxstlib'] bindgen.after = 'gen_ns3_module_header' bindgen.name = "pybindgen(ns3 module %s)" % module bindgen.module = 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.module = module pymod.use = ["%s" % mod for mod in pymod.env['NS3_ENABLED_CONTRIBUTED_MODULES']] 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']) defines.extend(['NS_DEPRECATED_3_34=', 'NS3_DEPRECATED_H']) defines.extend(['NS_DEPRECATED_3_32=', 'NS3_DEPRECATED_H']) defines.extend(['NS_DEPRECATED_3_31=', 'NS3_DEPRECATED_H']) defines.extend(['NS_DEPRECATED_3_30=', 'NS3_DEPRECATED_H']) if Utils.unversioned_sys_platform() == 'win32': try: defines.remove('_DEBUG') # causes undefined symbols on win32 except ValueError: pass pymod.env['DEFINES'] = defines # The following string should lead to includes of # '-I.', '-Isrc/core/bindings' when compiling module_helpers.cc pymod.includes = Context.out_dir + ' ' + Context.out_dir + '/src/core/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) all_contrib_modules = find_contrib_modules(bld) # Remove these modules from the list of all modules. for not_built in bld.env['MODULES_NOT_BUILT']: if not_built in all_contrib_modules: all_contrib_modules.remove(not_built) bld.recurse(list(all_contrib_modules)) for module in all_contrib_modules: modheader = bld(features='ns3moduleheader') modheader.module = module.split('/')[-1]