bindings/python/ns3modulescan.py
changeset 3408 2cc40b3e4fa5
child 3409 94ac3e381075
equal deleted inserted replaced
3396:0d83aa14b65d 3408:2cc40b3e4fa5
       
     1 #! /usr/bin/env python
       
     2 
       
     3 import sys
       
     4 import os.path
       
     5 
       
     6 import pybindgen.settings
       
     7 from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection, WrapperWarning
       
     8 from pybindgen.typehandlers.codesink import FileCodeSink
       
     9 from pygccxml.declarations import templates
       
    10 from pygccxml.declarations.class_declaration import class_t
       
    11 from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_t
       
    12 
       
    13 
       
    14 ## we need the smart pointer type transformation to be active even
       
    15 ## during gccxml scanning.
       
    16 import ns3modulegen_core_customizations
       
    17 
       
    18 
       
    19 ## silence gccxmlparser errors; we only want error handling in the
       
    20 ## generated python script, not while scanning.
       
    21 class ErrorHandler(pybindgen.settings.ErrorHandler):
       
    22     def handle_error(self, dummy_wrapper, dummy_exception, dummy_traceback_):
       
    23         return True
       
    24 pybindgen.settings.error_handler = ErrorHandler()
       
    25 import warnings
       
    26 warnings.filterwarnings(category=WrapperWarning, action='ignore')
       
    27 
       
    28 type_annotations = {
       
    29     '::ns3::RefCountBase': {
       
    30         'incref_method': 'Ref',
       
    31         'decref_method': 'Unref',
       
    32         'peekref_method': 'GetReferenceCount',
       
    33         'automatic_type_narrowing': 'true',
       
    34         },
       
    35     '::ns3::Object': {
       
    36         'incref_method': 'Ref',
       
    37         'decref_method': 'Unref',
       
    38         'peekref_method': 'GetReferenceCount',
       
    39         'automatic_type_narrowing': 'true',
       
    40         },
       
    41     '::ns3::Packet': {
       
    42         'incref_method': 'Ref',
       
    43         'decref_method': 'Unref',
       
    44         'peekref_method': 'GetReferenceCount',
       
    45         },
       
    46     '::ns3::CallbackImplBase': {
       
    47         'incref_method': 'Ref',
       
    48         'decref_method': 'Unref',
       
    49         'peekref_method': 'GetReferenceCount',
       
    50         },
       
    51     '::ns3::PacketMetadata': {
       
    52         'ignore': None,
       
    53         },
       
    54     '::ns3::AttributeChecker': {
       
    55         'incref_method': 'Ref',
       
    56         'decref_method': 'Unref',
       
    57         'automatic_type_narrowing': 'true',
       
    58         'allow_subclassing': 'false',
       
    59         },
       
    60     '::ns3::AttributeValue': {
       
    61         'incref_method': 'Ref',
       
    62         'decref_method': 'Unref',
       
    63         'automatic_type_narrowing': 'true',
       
    64         'allow_subclassing': 'false',
       
    65         },
       
    66     'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': {
       
    67         'ignore': None,
       
    68         },
       
    69     'ns3::RandomVariableBase * ns3::RandomVariable::Peek() const [member function]': {
       
    70         'ignore': None,
       
    71         },
       
    72     'void ns3::RandomVariable::GetSeed(uint32_t * seed) const [member function]': {
       
    73         'params': {'seed':{'direction':'out',
       
    74                            'array_length':'6'}}
       
    75         },
       
    76     'bool ns3::TypeId::LookupAttributeByName(std::string name, ns3::TypeId::AttributeInfo * info) const [member function]': {
       
    77         'params': {'info':{'transfer_ownership': 'false'}}
       
    78         },
       
    79     'static bool ns3::TypeId::LookupByNameFailSafe(std::string name, ns3::TypeId * tid) [member function]': {
       
    80         'params': {'tid': {'transfer_ownership': 'false'}}
       
    81         },
       
    82     'bool ns3::TraceSourceAccessor::ConnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
       
    83         'params': {'obj': {'transfer_ownership':'false'}}
       
    84         },
       
    85     'bool ns3::TraceSourceAccessor::Connect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
       
    86         'params': {'obj': {'transfer_ownership':'false'}}
       
    87         },
       
    88     'bool ns3::TraceSourceAccessor::DisconnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
       
    89         'params': {'obj': {'transfer_ownership':'false'}}
       
    90         },
       
    91     'bool ns3::TraceSourceAccessor::Disconnect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
       
    92         'params': {'obj': {'transfer_ownership':'false'}}
       
    93         },
       
    94     'bool ns3::AttributeAccessor::Set(ns3::ObjectBase * object, ns3::AttributeValue const & value) const [member function]': {
       
    95         'params': {'object': {'transfer_ownership':'false'}}
       
    96         },
       
    97     'ns3::EmpiricalVariable::EmpiricalVariable(ns3::RandomVariableBase const & variable) [constructor]': {
       
    98         'ignore': None
       
    99         },
       
   100     'static ns3::AttributeList * ns3::AttributeList::GetGlobal() [member function]': {
       
   101         'caller_owns_return': 'false'
       
   102         },
       
   103     'void ns3::CommandLine::Parse(int argc, char * * argv) const [member function]': {
       
   104         'ignore': None # manually wrapped
       
   105         },
       
   106     'extern void ns3::PythonCompleteConstruct(ns3::Ptr<ns3::Object> object, ns3::TypeId typeId, ns3::AttributeList const & attributes) [free function]': {
       
   107         'ignore': None # used transparently by, should not be wrapped
       
   108         },
       
   109     }
       
   110 
       
   111 def get_ns3_relative_path(path):
       
   112     l = []
       
   113     head = path
       
   114     while head:
       
   115         head, tail = os.path.split(head)
       
   116         if tail == 'ns3':
       
   117             return os.path.join(*l)
       
   118         l.insert(0, tail)
       
   119     raise AssertionError("is the path %r inside ns3?!" % path)
       
   120 
       
   121 
       
   122 def pre_scan_hook(dummy_module_parser,
       
   123                   pygccxml_definition,
       
   124                   global_annotations,
       
   125                   parameter_annotations):
       
   126     ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)
       
   127 
       
   128     ## Note: we don't include line numbers in the comments because
       
   129     ## those numbers are very likely to change frequently, which would
       
   130     ## cause needless changes, since the generated python files are
       
   131     ## kept under version control.
       
   132 
       
   133     #global_annotations['pygen_comment'] = "%s:%i: %s" % \
       
   134     #    (ns3_header, pygccxml_definition.location.line, pygccxml_definition)
       
   135     global_annotations['pygen_comment'] = "%s: %s" % \
       
   136         (ns3_header, pygccxml_definition)
       
   137 
       
   138 
       
   139     ## handle ns3::Object::GetObject (left to its own devices,
       
   140     ## pybindgen will generate a mangled name containing the template
       
   141     ## argument type name).
       
   142     if isinstance(pygccxml_definition, member_function_t) \
       
   143             and pygccxml_definition.parent.name == 'Object' \
       
   144             and pygccxml_definition.name == 'GetObject':
       
   145         template_args = templates.args(pygccxml_definition.demangled_name)
       
   146         if template_args == ['ns3::Object']:
       
   147             global_annotations['template_instance_names'] = 'ns3::Object=>GetObject'
       
   148 
       
   149     ## Don't wrap Simulator::Schedule* (manually wrapped)
       
   150     if isinstance(pygccxml_definition, member_function_t) \
       
   151             and pygccxml_definition.parent.name == 'Simulator' \
       
   152             and pygccxml_definition.name.startswith('Schedule'):
       
   153         global_annotations['ignore'] = None
       
   154 
       
   155     ## classes
       
   156     if isinstance(pygccxml_definition, class_t):
       
   157         # no need for helper classes to allow subclassing in Python, I think...
       
   158         if pygccxml_definition.name.endswith('Helper'):
       
   159             global_annotations['allow_subclassing'] = 'false'
       
   160 
       
   161         if pygccxml_definition.decl_string.startswith('::ns3::Callback<'):
       
   162             # manually handled in ns3modulegen_core_customizations.py
       
   163             global_annotations['ignore'] = None
       
   164             return
       
   165 
       
   166         if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'):
       
   167             # handled by pybindgen "type transformation"
       
   168             global_annotations['ignore'] = None
       
   169             return
       
   170 
       
   171         # table driven class customization
       
   172         try:
       
   173             annotations = type_annotations[pygccxml_definition.decl_string]
       
   174         except KeyError:
       
   175             pass
       
   176         else:
       
   177             global_annotations.update(annotations)
       
   178 
       
   179     ## free functions
       
   180     if isinstance(pygccxml_definition, free_function_t):
       
   181         if pygccxml_definition.name == 'PeekPointer':
       
   182             global_annotations['ignore'] = None
       
   183             return
       
   184 
       
   185     ## table driven methods/constructors/functions customization
       
   186     if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)):
       
   187         try:
       
   188             annotations = type_annotations[str(pygccxml_definition)]
       
   189         except KeyError:
       
   190             pass
       
   191         else:
       
   192             for key,value in annotations.items():
       
   193                 if key == 'params':
       
   194                     parameter_annotations.update (value)
       
   195                     del annotations['params']
       
   196             global_annotations.update(annotations)
       
   197 
       
   198 
       
   199 # def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper):
       
   200 #     ## classes
       
   201 #     if isinstance(pybindgen_wrapper, CppClass):
       
   202 #         if pybindgen_wrapper.name.endswith('Checker'):
       
   203 #             print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper
       
   204 #             #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function)
       
   205 
       
   206 
       
   207 def scan_callback_classes(module_parser, callback_classes_file):
       
   208     callback_classes_file.write("callback_classes = [\n")
       
   209     for cls in module_parser.module_namespace.classes(function=module_parser.location_filter,
       
   210                                                       recursive=False):
       
   211         if not cls.name.startswith("Callback<"):
       
   212             continue
       
   213         assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls
       
   214         dummy_cls_name, template_parameters = templates.split(cls.decl_string)
       
   215         callback_classes_file.write("    %r,\n" % template_parameters)
       
   216     callback_classes_file.write("]\n")
       
   217 
       
   218 
       
   219 class MyPygenClassifier(PygenClassifier):
       
   220     def __init__(self, headers_map):
       
   221         self.headers_map = headers_map
       
   222 
       
   223     def classify(self, pygccxml_definition):
       
   224         name = os.path.basename(pygccxml_definition.location.file_name)
       
   225         try:
       
   226             return self.headers_map[name]
       
   227         except KeyError:
       
   228             return '__main__'
       
   229 
       
   230 
       
   231 def ns3_module_scan(top_builddir, pygen_file_name):
       
   232 
       
   233     ns3_modules = eval(sys.stdin.read())
       
   234 
       
   235     ## do a topological sort on the modules graph
       
   236     from topsort import topsort
       
   237     graph = []
       
   238     for ns3_module_name, (ns3_module_deps, dummy) in ns3_modules.iteritems():
       
   239         for dep in ns3_module_deps:
       
   240             graph.append((dep, ns3_module_name))
       
   241     sorted_ns3_modules = topsort(graph)
       
   242     print >> sys.stderr, "******* topological sort: ", sorted_ns3_modules
       
   243 
       
   244     sections = [PygenSection('__main__', FileCodeSink(open(pygen_file_name, "wt")))]
       
   245     headers_map = {} # header_name -> section_name
       
   246     for ns3_module in sorted_ns3_modules:
       
   247         section_name = "ns3_module_%s" % ns3_module.replace('-', '_')
       
   248         file_name = os.path.join(os.path.dirname(pygen_file_name), "%s.py" % section_name)
       
   249         sections.append(PygenSection(section_name, FileCodeSink(open(file_name, "wt")),
       
   250                                      section_name + "__local"))
       
   251         for header in ns3_modules[ns3_module][1]:
       
   252             headers_map[header] = section_name
       
   253 
       
   254     module_parser = ModuleParser('ns3', 'ns3')
       
   255 
       
   256     module_parser.add_pre_scan_hook(pre_scan_hook)
       
   257     #module_parser.add_post_scan_hook(post_scan_hook)
       
   258     module_parser.parse_init([os.path.join(top_builddir, 'ns3', 'everything.h')],
       
   259                              include_paths=[top_builddir], whitelist_paths=[top_builddir],
       
   260                              #includes=['"ns3/everything.h"'],
       
   261                              pygen_sink=sections,
       
   262                              pygen_classifier=MyPygenClassifier(headers_map))
       
   263     module_parser.scan_types()
       
   264 
       
   265     callback_classes_file = open(os.path.join(os.path.dirname(pygen_file_name), "callbacks_list.py"), "wt")
       
   266     scan_callback_classes(module_parser, callback_classes_file)
       
   267     callback_classes_file.close()
       
   268 
       
   269 
       
   270     module_parser.scan_methods()
       
   271     module_parser.scan_functions()
       
   272     module_parser.parse_finalize()
       
   273 
       
   274     for section in sections:
       
   275         section.code_sink.file.close()
       
   276 
       
   277 
       
   278 
       
   279 if __name__ == '__main__':
       
   280     ns3_module_scan(sys.argv[1], sys.argv[2])
       
   281