diff --git a/bindings/python/ns/__init__.py b/bindings/python/ns/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/bindings/python/ns/__init__.py @@ -0,0 +1 @@ + diff --git a/bindings/python/ns3modulegen-modular.py b/bindings/python/ns3modulegen-modular.py new file mode 100644 index 000000000..49a1fe735 --- /dev/null +++ b/bindings/python/ns3modulegen-modular.py @@ -0,0 +1,54 @@ +import warnings +import sys +import os +import pybindgen.settings +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers +import ns3modulegen_core_customizations + +class ErrorHandler(pybindgen.settings.ErrorHandler): + def handle_error(self, wrapper, exception, traceback_): + warnings.warn("exception %r in wrapper %s" % (exception, wrapper)) + return True +pybindgen.settings.error_handler = ErrorHandler() + + + + +def main(argv): + assert len(argv) == 3 + module_abs_src_path, target = argv[1], argv[2] + module_name = os.path.basename(module_abs_src_path) + + sys.path.insert(0, os.path.join(module_abs_src_path, "bindings")) + try: + module_apidefs = __import__("modulegen__%s" % target) + del sys.modules["modulegen__%s" % target] + try: + module_customization = __import__("modulegen_local") + del sys.modules["modulegen_local"] + except ImportError: + module_customization = None + finally: + sys.path.pop(0) + + out = FileCodeSink(sys.stdout) + root_module = module_apidefs.module_init() + root_module.add_include('"ns3/%s-module.h"' % module_name) + module_apidefs.register_types(root_module) + + module_apidefs.register_methods(root_module) + module_apidefs.register_functions(root_module) + + ns3modulegen_core_customizations.Object_customizations(root_module) + ns3modulegen_core_customizations.Attribute_customizations(root_module) + + if module_customization is not None: + module_customization.customize(root_module) + + root_module.generate(out) + +if __name__ == '__main__': + import sys + main(sys.argv) + + diff --git a/bindings/python/ns3modulescan-modular.py b/bindings/python/ns3modulescan-modular.py new file mode 100644 index 000000000..76d7ffaa0 --- /dev/null +++ b/bindings/python/ns3modulescan-modular.py @@ -0,0 +1,301 @@ +#! /usr/bin/env python + +import sys +import os.path + +import pybindgen.settings +from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection, WrapperWarning +from pybindgen.typehandlers.codesink import FileCodeSink +from pygccxml.declarations import templates +from pygccxml.declarations.enumeration import enumeration_t +from pygccxml.declarations.class_declaration import class_t +from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_t, calldef_t + + +## we need the smart pointer type transformation to be active even +## during gccxml scanning. +import ns3modulegen_core_customizations + + +## silence gccxmlparser errors; we only want error handling in the +## generated python script, not while scanning. +class ErrorHandler(pybindgen.settings.ErrorHandler): + def handle_error(self, dummy_wrapper, dummy_exception, dummy_traceback_): + return True +pybindgen.settings.error_handler = ErrorHandler() +import warnings +warnings.filterwarnings(category=WrapperWarning, action='ignore') + +type_annotations = { + '::ns3::AttributeChecker': { + 'automatic_type_narrowing': 'true', + 'allow_subclassing': 'false', + }, + '::ns3::AttributeValue': { + 'automatic_type_narrowing': 'true', + 'allow_subclassing': 'false', + }, + + '::ns3::CommandLine': { + 'allow_subclassing': 'true', # needed so that AddValue is able to set attributes on the object + }, + + '::ns3::NscTcpL4Protocol': { + 'ignore': 'true', # this class is implementation detail + }, + + + 'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': { + 'ignore': None, + }, + 'ns3::RandomVariableBase * ns3::RandomVariable::Peek() const [member function]': { + 'ignore': None, + }, + 'void ns3::RandomVariable::GetSeed(uint32_t * seed) const [member function]': { + 'params': {'seed':{'direction':'out', + 'array_length':'6'}} + }, + 'bool ns3::TypeId::LookupAttributeByName(std::string name, ns3::TypeId::AttributeInfo * info) const [member function]': { + 'params': {'info':{'transfer_ownership': 'false'}} + }, + 'static bool ns3::TypeId::LookupByNameFailSafe(std::string name, ns3::TypeId * tid) [member function]': { + 'ignore': None, # manually wrapped in + }, + 'bool ns3::TraceSourceAccessor::ConnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': { + 'params': {'obj': {'transfer_ownership':'false'}} + }, + 'bool ns3::TraceSourceAccessor::Connect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': { + 'params': {'obj': {'transfer_ownership':'false'}} + }, + 'bool ns3::TraceSourceAccessor::DisconnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': { + 'params': {'obj': {'transfer_ownership':'false'}} + }, + 'bool ns3::TraceSourceAccessor::Disconnect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': { + 'params': {'obj': {'transfer_ownership':'false'}} + }, + 'bool ns3::AttributeAccessor::Set(ns3::ObjectBase * object, ns3::AttributeValue const & value) const [member function]': { + 'params': {'object': {'transfer_ownership':'false'}} + }, + 'ns3::EmpiricalVariable::EmpiricalVariable(ns3::RandomVariableBase const & variable) [constructor]': { + 'ignore': None + }, + 'static ns3::AttributeList * ns3::AttributeList::GetGlobal() [member function]': { + 'caller_owns_return': 'false' + }, + 'void ns3::CommandLine::Parse(int argc, char * * argv) const [member function]': { + 'ignore': None # manually wrapped + }, + 'extern void ns3::PythonCompleteConstruct(ns3::Ptr object, ns3::TypeId typeId, ns3::AttributeList const & attributes) [free function]': { + 'ignore': None # used transparently by, should not be wrapped + }, + + 'ns3::Ptr ns3::Ipv4ListRouting::GetRoutingProtocol(uint32_t index, int16_t & priority) const [member function]': { + 'params': {'priority':{'direction':'out'}} + }, + 'ns3::Ipv4RoutingTableEntry * ns3::GlobalRouter::GetInjectedRoute(uint32_t i) [member function]': { + 'params': {'return': { 'caller_owns_return': 'false',}}, + }, + 'ns3::Ipv4RoutingTableEntry * ns3::Ipv4GlobalRouting::GetRoute(uint32_t i) [member function]': { + 'params': {'return': { 'caller_owns_return': 'false',}}, + }, + + } + +def get_ns3_relative_path(path): + l = [] + head = path + while head: + head, tail = os.path.split(head) + if tail == 'ns3': + return os.path.join(*l) + l.insert(0, tail) + raise AssertionError("is the path %r inside ns3?!" % path) + +class PreScanHook: + + def __init__(self, headers_map, module): + self.headers_map = headers_map + self.module = module + + def __call__(self, dummy_module_parser, + pygccxml_definition, + global_annotations, + parameter_annotations): + ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name) + definition_module = self.headers_map[ns3_header] + + ## Note: we don't include line numbers in the comments because + ## those numbers are very likely to change frequently, which would + ## cause needless changes, since the generated python files are + ## kept under version control. + + #global_annotations['pygen_comment'] = "%s:%i: %s" % \ + # (ns3_header, pygccxml_definition.location.line, pygccxml_definition) + global_annotations['pygen_comment'] = "%s (module %r): %s" % \ + (ns3_header, definition_module, pygccxml_definition) + + + ## handle ns3::Object::GetObject (left to its own devices, + ## pybindgen will generate a mangled name containing the template + ## argument type name). + if isinstance(pygccxml_definition, member_function_t) \ + and pygccxml_definition.parent.name == 'Object' \ + and pygccxml_definition.name == 'GetObject': + template_args = templates.args(pygccxml_definition.demangled_name) + if template_args == ['ns3::Object']: + global_annotations['template_instance_names'] = 'ns3::Object=>GetObject' + + ## Don't wrap Simulator::Schedule* (manually wrapped) + if isinstance(pygccxml_definition, member_function_t) \ + and pygccxml_definition.parent.name == 'Simulator' \ + and pygccxml_definition.name.startswith('Schedule'): + global_annotations['ignore'] = None + + # manually wrapped + if isinstance(pygccxml_definition, member_function_t) \ + and pygccxml_definition.parent.name == 'Simulator' \ + and pygccxml_definition.name == 'Run': + global_annotations['ignore'] = True + + ## http://www.gccxml.org/Bug/view.php?id=9915 + if isinstance(pygccxml_definition, calldef_t): + for arg in pygccxml_definition.arguments: + if arg.default_value is None: + continue + if "ns3::MilliSeconds( )" == arg.default_value: + arg.default_value = "ns3::MilliSeconds(0)" + + ## classes + if isinstance(pygccxml_definition, class_t): + # no need for helper classes to allow subclassing in Python, I think... + #if pygccxml_definition.name.endswith('Helper'): + # global_annotations['allow_subclassing'] = 'false' + + if definition_module != self.module: + global_annotations['import_from_module'] = 'ns.%s' % definition_module + + if pygccxml_definition.decl_string.startswith('::ns3::SimpleRefCount<'): + global_annotations['incref_method'] = 'Ref' + global_annotations['decref_method'] = 'Unref' + global_annotations['peekref_method'] = 'GetReferenceCount' + global_annotations['automatic_type_narrowing'] = 'true' + return + + if pygccxml_definition.decl_string.startswith('::ns3::Callback<'): + # manually handled in ns3modulegen_core_customizations.py + global_annotations['ignore'] = None + return + + if pygccxml_definition.decl_string.startswith('::ns3::TracedCallback<'): + global_annotations['ignore'] = None + return + + if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'): + # handled by pybindgen "type transformation" + global_annotations['ignore'] = None + return + + # table driven class customization + try: + annotations = type_annotations[pygccxml_definition.decl_string] + except KeyError: + pass + else: + global_annotations.update(annotations) + + ## enums + if isinstance(pygccxml_definition, enumeration_t): + if definition_module != self.module: + global_annotations['import_from_module'] = 'ns.%s' % definition_module + + ## free functions + if isinstance(pygccxml_definition, free_function_t): + + if definition_module != self.module: + global_annotations['ignore'] = None + return + + if pygccxml_definition.name == 'PeekPointer': + global_annotations['ignore'] = None + return + + ## table driven methods/constructors/functions customization + if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)): + try: + annotations = type_annotations[str(pygccxml_definition)] + except KeyError: + pass + else: + for key,value in annotations.items(): + if key == 'params': + parameter_annotations.update (value) + del annotations['params'] + global_annotations.update(annotations) + + +# def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper): +# ## classes +# if isinstance(pybindgen_wrapper, CppClass): +# if pybindgen_wrapper.name.endswith('Checker'): +# print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper +# #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function) + + +def scan_callback_classes(module_parser, callback_classes_file): + callback_classes_file.write("callback_classes = [\n") + for cls in module_parser.module_namespace.classes(function=module_parser.location_filter, + recursive=False): + if not cls.name.startswith("Callback<"): + continue + assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls + dummy_cls_name, template_parameters = templates.split(cls.decl_string) + callback_classes_file.write(" %r,\n" % template_parameters) + callback_classes_file.write("]\n") + + +def ns3_module_scan(top_builddir, module_name, headers_map, output_file_name, cflags): + module_parser = ModuleParser('ns.%s' % module_name, 'ns3') + module_parser.add_pre_scan_hook(PreScanHook(headers_map, module_name)) + #module_parser.add_post_scan_hook(post_scan_hook) + + gccxml_options = dict( + include_paths=[top_builddir], + define_symbols={ + #'NS3_ASSERT_ENABLE': None, + #'NS3_LOG_ENABLE': None, + }, + cflags=('--gccxml-cxxflags %r' % (cflags,)) + ) + + try: + os.unlink(output_file_name) + except OSError: + pass + output_file = open(output_file_name, "wt") + output_sink = FileCodeSink(output_file) + module_parser.parse_init([os.path.join(top_builddir, "ns3", "%s-module.h" % module_name)], + None, whitelist_paths=[top_builddir], + #includes=['"ns3/everything.h"'], + pygen_sink=output_sink, + gccxml_options=gccxml_options) + module_parser.scan_types() + + #callback_classes_file = open(os.path.join(os.path.dirname(pygen_file_name), "callbacks_list.py"), "wt") + #scan_callback_classes(module_parser, callback_classes_file) + #callback_classes_file.close() + + + module_parser.scan_methods() + module_parser.scan_functions() + module_parser.parse_finalize() + + output_file.close() + os.chmod(output_file_name, 0400) + + +if __name__ == '__main__': + if len(sys.argv) != 6: + print "ns3modulescan2.py top_builddir module_path module_headers output_file_name cflags" + sys.exit(1) + ns3_module_scan(sys.argv[1], sys.argv[2], eval(sys.argv[3]), sys.argv[4], sys.argv[5]) + sys.exit(0) diff --git a/bindings/python/wscript b/bindings/python/wscript index 8afd1823d..96dc7a8ba 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -1,5 +1,5 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- - +import types import re import os import pproc as subprocess @@ -15,7 +15,7 @@ import Build import Utils ## https://launchpad.net/pybindgen/ -REQUIRED_PYBINDGEN_VERSION = (0, 15, 0) +REQUIRED_PYBINDGEN_VERSION = (0, 15, 0, 775) REQUIRED_PYGCCXML_VERSION = (0, 9, 5) @@ -52,6 +52,11 @@ def set_pybindgen_pythonpath(env): def set_options(opt): opt.tool_options('python') + opt.add_option('--bindings-type', type="choice", + choices=("monolithic", "modular", "both"), + help=("Type of Python bindings to build"), + default="monolithic", + dest='bindings_type') opt.add_option('--disable-python', help=("Don't build Python bindings."), action="store_true", default=False, @@ -60,6 +65,9 @@ def set_options(opt): help=("Rescan Python bindings. Needs working GCCXML / pygccxml environment."), action="store_true", default=False, dest='python_scan') + opt.add_option('--apiscan', + help=("EXPERIMENTAL: Rescan module API, for Python bindings. Needs working GCCXML / pygccxml environment."), + default=None, dest='apiscan', metavar="MODULE[,MODULE...]") opt.add_option('--with-pybindgen', help=('Path to an existing pybindgen source tree to use.'), default=None, @@ -198,7 +206,12 @@ int main () conf.env['ENABLE_PYTHON_BINDINGS'] = True conf.report_optional_feature("python", "Python Bindings", True, None) - + conf.env['BINDINGS_TYPE'] = Options.options.bindings_type + if conf.env['BINDINGS_TYPE'] == 'both': + msg = "monolithic and modular" + else: + msg = conf.env['BINDINGS_TYPE'] + conf.check_message_custom('type of bindings to build', '', msg) ## Check for pygccxml try: @@ -244,12 +257,73 @@ int main () conf.report_optional_feature("pygccxml", "Python API Scanning Support", False, "gccxml too old") return - + ## If we reached conf.env['ENABLE_PYTHON_SCANNING'] = True conf.report_optional_feature("pygccxml", "Python API Scanning Support", True, None) + + +# --------------------- + +def get_headers_map(bld): + headers_map = {} # header => module + for ns3headers in bld.all_task_gen: + if type(ns3headers).__name__ == 'ns3header_taskgen': # XXX: find less hackish way to compare + for h in ns3headers.to_list(ns3headers.source): + headers_map[os.path.basename(h)] = ns3headers.module + return headers_map + +def get_module_path(bld, module): + for ns3headers in bld.all_task_gen: + if type(ns3headers).__name__ == 'ns3header_taskgen': # XXX: find less hackish way to compare + if ns3headers.module == module: + break + + else: + raise ValueError("Module %r not found" % module) + return ns3headers.path.abspath() + +class apiscan_task(Task.TaskBase): + """Uses gccxml to scan the file 'everything.h' and extract API definitions. + """ + after = 'gen_everything_h_task' + before = 'cc cxx gchx' + color = "BLUE" + def __init__(self, curdirnode, env, bld, target, cflags, module): + self.bld = bld + super(apiscan_task, self).__init__(generator=self) + self.curdirnode = curdirnode + self.env = env + self.target = target + self.cflags = cflags + self.module = module + + def display(self): + return 'api-scan-%s\n' % (self.target,) + + def run(self): + top_builddir = self.curdirnode.find_dir('../..').abspath(self.env) + module_path = get_module_path(self.bld, self.module) + print module_path + headers_map = get_headers_map(self.bld) + argv = [ + self.env['PYTHON'], + os.path.join(self.curdirnode.abspath(), 'ns3modulescan-modular.py'), # scanning script + top_builddir, + self.module, + repr(get_headers_map(self.bld)), + os.path.join(module_path, "bindings", 'modulegen__%s.py' % (self.target)), # output file + self.cflags, + ] + scan = subprocess.Popen(argv, stdin=subprocess.PIPE) + retval = scan.wait() + return retval + + + +# -------------- prio_headers = { -2: ( "string.h", # work around http://www.gccxml.org/Bug/view.php?id=6682 @@ -452,7 +526,7 @@ class python_scan_task(Task.TaskBase): class python_scan_task_collector(Task.TaskBase): """Tasks that waits for the python-scan-* tasks to complete and then signals WAF to exit """ - after = 'python_scan_task' + after = 'python_scan_task apiscan_task' before = 'cc cxx' color = "BLUE" def __init__(self, curdirnode, env, bld): @@ -481,7 +555,28 @@ def build(bld): set_pybindgen_pythonpath(env) - if env['ENABLE_PYTHON_BINDINGS']: + + + # copy the __init__.py file to the build dir. waf can't handle + # this, it's against waf's principles to have build dir files + # with the same name as source dir files, apparently. + dirnode = bld.path.find_dir('ns') + src = os.path.join(dirnode.abspath(), '__init__.py') + dst = os.path.join(dirnode.abspath(env), '__init__.py') + try: + need_copy = os.stat(src).st_mtime > os.stat(dst).st_mtime + except OSError: + need_copy = True + if need_copy: + try: + os.mkdir(os.path.dirname(dst)) + except OSError: + pass + print "%r -> %r" % (src, dst) + shutil.copy2(src, dst) + + + if env['ENABLE_PYTHON_BINDINGS'] and env['BINDINGS_TYPE'] in ('monolithic', 'both'): obj = bld.new_task_gen('all_ns3_headers') if Options.options.python_scan: @@ -503,7 +598,28 @@ def build(bld): python_scan_task_collector(bld.path, env, bld) return - if env['ENABLE_PYTHON_BINDINGS']: + if Options.options.apiscan: + if not env['ENABLE_PYTHON_SCANNING']: + raise Utils.WafError("Cannot re-scan python bindings: (py)gccxml not available") + scan_targets = [] + if sys.platform == 'cygwin': + scan_targets.append(('gcc_cygwin', '')) + else: + import struct + if struct.calcsize('I') == 4 and struct.calcsize('L') == 8 and struct.calcsize('P') == 8: + scan_targets.extend([('gcc_ILP32', '-m32'), ('gcc_LP64', '-m64')]) + elif struct.calcsize('I') == 4 and struct.calcsize('L') == 4 and struct.calcsize('P') == 4: + scan_targets.append(('gcc_ILP32', '')) + else: + raise Utils.WafError("Cannot scan python bindings for unsupported data model") + for target, cflags in scan_targets: + for module in Options.options.apiscan.split(','): + apiscan_task(bld.path, env, bld, target, cflags, module) + python_scan_task_collector(bld.path, env, bld) + return + + + if env['ENABLE_PYTHON_BINDINGS'] and env['BINDINGS_TYPE'] in ('monolithic', 'both'): apidefs = env['PYTHON_BINDINGS_APIDEFS'] ## Get a list of scanned modules; the set of scanned modules @@ -518,7 +634,7 @@ def build(bld): if name.endswith("__local"): continue scanned_modules.append(name) - + print "scanned_modules:", scanned_modules debug = ('PYBINDGEN_DEBUG' in os.environ) source = [ 'ns3modulegen.py', @@ -610,3 +726,5 @@ def build(bld): pass print "%r -> %r" % (src, dst) shutil.copy2(src, dst) + + diff --git a/src/core/wscript b/src/core/wscript index a9b0c44e3..e68a09e34 100644 --- a/src/core/wscript +++ b/src/core/wscript @@ -271,3 +271,5 @@ def build(bld): if env['ENABLE_GSL']: core.uselib = core.uselib + ' GSL GSLCBLAS M' core.source.extend(['test/rng-test-suite.cc']) + + bld.ns3_python_bindings() diff --git a/src/network/wscript b/src/network/wscript index 9c633541b..3c4f43295 100644 --- a/src/network/wscript +++ b/src/network/wscript @@ -117,3 +117,5 @@ def build(bld): 'helper/packet-socket-helper.h', 'helper/trace-helper.h', ] + + bld.ns3_python_bindings() diff --git a/src/wscript b/src/wscript index 592c46e10..7d644fe5a 100644 --- a/src/wscript +++ b/src/wscript @@ -120,9 +120,79 @@ def create_obj(bld, *args): DeprecationWarning, stacklevel=2) return bld.new_task_gen(*args) + +def ns3_python_bindings(bld): + env = bld.env + if not env['ENABLE_PYTHON_BINDINGS']: + return + if env['BINDINGS_TYPE'] not in ('modular', 'both'): + return + + # this method is called from a module wscript, so remember bld.path is not bindings/python! + module_abs_src_path = bld.path.abspath() + module = os.path.basename(module_abs_src_path) + apidefs = env['PYTHON_BINDINGS_APIDEFS'].replace("-", "_") + + #debug = ('PYBINDGEN_DEBUG' in os.environ) + debug = True # XXX + source = [bld.srcnode.find_resource('bindings/python/ns3modulegen-modular.py').relpath_gen(bld.path), + "bindings/modulegen__%s.py" % apidefs] + + # the local customization file may or not exist + if bld.path.find_resource("bindings/modulegen_local.py"): + source.append("bindings/modulegen_local.py") + + target = ['bindings/module.cc'] + #if not debug: + # target.append('ns3modulegen.log') + + argv = ['NS3_ENABLED_FEATURES=${FEATURES}', '${PYTHON}'] + #if debug: + # argv.extend(["-m", "pdb"]) + + argv.extend(['${SRC[0]}', module_abs_src_path, apidefs, '>', '${TGT[0]}']) + #if not debug: + # 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.new_task_gen('command', source=source, target=target, command=argv) + bindgen.env['FEATURES'] = ','.join(features) + bindgen.dep_vars = ['FEATURES'] + bindgen.before = 'cxx' + bindgen.after = 'gen_ns3_module_header_task' + bindgen.name = "pybindgen(ns3 module %s)" % module + + pymod = bld.new_task_gen(features='cxx cshlib pyext') + pymod.source = ['bindings/module.cc'] + pymod.target = '%s/%s' % (bld.srcnode.find_dir("bindings/python/ns").relpath_gen(bld.path), module) + pymod.name = 'ns3module_%s' % module + pymod.uselib_local = "ns3" + if pymod.env['ENABLE_STATIC_NS3']: + if sys.platform == 'darwin': + pymod.env.append_value('LINKFLAGS', '-Wl,-all_load') + pymod.env.append_value('LINKFLAGS', '-lns3') + else: + pymod.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic') + pymod.env.append_value('LINKFLAGS', '-lns3') + pymod.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive') + defines = list(pymod.env['CXXDEFINES']) + defines.extend(['NS_DEPRECATED=', 'NS3_DEPRECATED_H']) + if Options.platform == 'win32': + try: + defines.remove('_DEBUG') # causes undefined symbols on win32 + except ValueError: + pass + pymod.env['CXXDEFINES'] = defines + + def build(bld): bld.create_ns3_module = types.MethodType(create_ns3_module, bld) bld.create_obj = types.MethodType(create_obj, bld) + bld.ns3_python_bindings = types.MethodType(ns3_python_bindings, bld) bld.add_subdirs(list(all_modules))