327 lines
12 KiB
Python
327 lines
12 KiB
Python
## -*- 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/<module>.py exists, it becomes the module frontend, and the C extension befomes _<module>
|
|
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_35=', 'NS3_DEPRECATED_H'])
|
|
defines.extend(['NS_DEPRECATED_3_34=', '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]
|