bindings/python/ns3modulescan.py
author Florian Westphal <fw@strlen.de>
Wed, 03 Sep 2008 23:24:59 +0200
changeset 3595 693faf7f4e9b
parent 3537 ab1673dcdf58
child 3753 a84a48233eb3
permissions -rw-r--r--
nsc: Fix build problem if gtk config store is disabled gtk config store pulled in libdl.so for us, so things fail to link of the config store isn't enabled. This makes nsc pull in libdl itself when its enabled.

#! /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.class_declaration import class_t
from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_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::RefCountBase': {
        'incref_method': 'Ref',
        'decref_method': 'Unref',
        'peekref_method': 'GetReferenceCount',
        'automatic_type_narrowing': 'true',
        },
    '::ns3::Object': {
        'incref_method': 'Ref',
        'decref_method': 'Unref',
        'peekref_method': 'GetReferenceCount',
        'automatic_type_narrowing': 'true',
        },
    '::ns3::Packet': {
        'incref_method': 'Ref',
        'decref_method': 'Unref',
        'peekref_method': 'GetReferenceCount',
        },
    '::ns3::CallbackImplBase': {
        'incref_method': 'Ref',
        'decref_method': 'Unref',
        'peekref_method': 'GetReferenceCount',
        },
    '::ns3::PacketMetadata': {
        'ignore': None,
        },
    '::ns3::AttributeChecker': {
        'automatic_type_narrowing': 'true',
        'allow_subclassing': 'false',
        },
    '::ns3::AttributeValue': {
        'automatic_type_narrowing': 'true',
        'allow_subclassing': 'false',
        },
    '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]': {
        'params': {'tid': {'transfer_ownership': 'false'}}
        },
    '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<ns3::Object> object, ns3::TypeId typeId, ns3::AttributeList const & attributes) [free function]': {
        'ignore': None # used transparently by, should not be wrapped
        },
    }

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)


def pre_scan_hook(dummy_module_parser,
                  pygccxml_definition,
                  global_annotations,
                  parameter_annotations):
    ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)

    ## 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: %s" % \
        (ns3_header, 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

    # unblock python threads for Simulator::Run
    if isinstance(pygccxml_definition, member_function_t) \
            and pygccxml_definition.parent.name == 'Simulator' \
            and pygccxml_definition.name == 'Run':
        global_annotations['unblock_threads'] = True


    ## 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 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)

    ## free functions
    if isinstance(pygccxml_definition, free_function_t):
        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")


class MyPygenClassifier(PygenClassifier):
    def __init__(self, headers_map):
        self.headers_map = headers_map

    def classify(self, pygccxml_definition):
        name = os.path.basename(pygccxml_definition.location.file_name)
        try:
            return self.headers_map[name]
        except KeyError:
            return '__main__'


def ns3_module_scan(top_builddir, pygen_file_name, everything_h):

    ns3_modules = eval(sys.stdin.read())

    ## do a topological sort on the modules graph
    from topsort import topsort
    graph = []
    for ns3_module_name, (ns3_module_deps, dummy) in ns3_modules.iteritems():
        for dep in ns3_module_deps:
            graph.append((dep, ns3_module_name))
    sorted_ns3_modules = topsort(graph)
    #print >> sys.stderr, "******* topological sort: ", sorted_ns3_modules

    sections = [PygenSection('__main__', FileCodeSink(open(pygen_file_name, "wt")))]
    headers_map = {} # header_name -> section_name
    for ns3_module in sorted_ns3_modules:
        section_name = "ns3_module_%s" % ns3_module.replace('-', '_')
        file_name = os.path.join(os.path.dirname(pygen_file_name), "%s.py" % section_name)
        sections.append(PygenSection(section_name, FileCodeSink(open(file_name, "wt")),
                                     section_name + "__local"))
        for header in ns3_modules[ns3_module][1]:
            headers_map[header] = section_name

    module_parser = ModuleParser('ns3', 'ns3')

    module_parser.add_pre_scan_hook(pre_scan_hook)
    #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,
            }
        )

    module_parser.parse_init([everything_h],
                             None, whitelist_paths=[top_builddir, os.path.dirname(everything_h)],
                             #includes=['"ns3/everything.h"'],
                             pygen_sink=sections,
                             pygen_classifier=MyPygenClassifier(headers_map),
                             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()

    for section in sections:
        section.code_sink.file.close()



if __name__ == '__main__':
    ns3_module_scan(sys.argv[1], sys.argv[3], sys.argv[2])