bindings/python/ns3modulescan.py
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sat, 04 Jul 2009 08:15:48 +0200
changeset 4654 2eaebe77d66b
parent 3929 909b0a724ed3
permissions -rw-r--r--
Added tag ns-3.5 for changeset c975274c9707
     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::AttributeChecker': {
    52         'automatic_type_narrowing': 'true',
    53         'allow_subclassing': 'false',
    54         },
    55     '::ns3::AttributeValue': {
    56         'automatic_type_narrowing': 'true',
    57         'allow_subclassing': 'false',
    58         },
    59 
    60     '::ns3::CommandLine': {
    61         'allow_subclassing': 'true', # needed so that AddValue is able to set attributes on the object
    62         },
    63 
    64     'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': {
    65         'ignore': None,
    66         },
    67     'ns3::RandomVariableBase * ns3::RandomVariable::Peek() const [member function]': {
    68         'ignore': None,
    69         },
    70     'void ns3::RandomVariable::GetSeed(uint32_t * seed) const [member function]': {
    71         'params': {'seed':{'direction':'out',
    72                            'array_length':'6'}}
    73         },
    74     'bool ns3::TypeId::LookupAttributeByName(std::string name, ns3::TypeId::AttributeInfo * info) const [member function]': {
    75         'params': {'info':{'transfer_ownership': 'false'}}
    76         },
    77     'static bool ns3::TypeId::LookupByNameFailSafe(std::string name, ns3::TypeId * tid) [member function]': {
    78         'ignore': None, # manually wrapped in 
    79         },
    80     'bool ns3::TraceSourceAccessor::ConnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
    81         'params': {'obj': {'transfer_ownership':'false'}}
    82         },
    83     'bool ns3::TraceSourceAccessor::Connect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
    84         'params': {'obj': {'transfer_ownership':'false'}}
    85         },
    86     'bool ns3::TraceSourceAccessor::DisconnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
    87         'params': {'obj': {'transfer_ownership':'false'}}
    88         },
    89     'bool ns3::TraceSourceAccessor::Disconnect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
    90         'params': {'obj': {'transfer_ownership':'false'}}
    91         },
    92     'bool ns3::AttributeAccessor::Set(ns3::ObjectBase * object, ns3::AttributeValue const & value) const [member function]': {
    93         'params': {'object': {'transfer_ownership':'false'}}
    94         },
    95     'ns3::EmpiricalVariable::EmpiricalVariable(ns3::RandomVariableBase const & variable) [constructor]': {
    96         'ignore': None
    97         },
    98     'static ns3::AttributeList * ns3::AttributeList::GetGlobal() [member function]': {
    99         'caller_owns_return': 'false'
   100         },
   101     'void ns3::CommandLine::Parse(int argc, char * * argv) const [member function]': {
   102         'ignore': None # manually wrapped
   103         },
   104     'extern void ns3::PythonCompleteConstruct(ns3::Ptr<ns3::Object> object, ns3::TypeId typeId, ns3::AttributeList const & attributes) [free function]': {
   105         'ignore': None # used transparently by, should not be wrapped
   106         },
   107     }
   108 
   109 def get_ns3_relative_path(path):
   110     l = []
   111     head = path
   112     while head:
   113         head, tail = os.path.split(head)
   114         if tail == 'ns3':
   115             return os.path.join(*l)
   116         l.insert(0, tail)
   117     raise AssertionError("is the path %r inside ns3?!" % path)
   118 
   119 
   120 def pre_scan_hook(dummy_module_parser,
   121                   pygccxml_definition,
   122                   global_annotations,
   123                   parameter_annotations):
   124     ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)
   125 
   126     ## Note: we don't include line numbers in the comments because
   127     ## those numbers are very likely to change frequently, which would
   128     ## cause needless changes, since the generated python files are
   129     ## kept under version control.
   130 
   131     #global_annotations['pygen_comment'] = "%s:%i: %s" % \
   132     #    (ns3_header, pygccxml_definition.location.line, pygccxml_definition)
   133     global_annotations['pygen_comment'] = "%s: %s" % \
   134         (ns3_header, pygccxml_definition)
   135 
   136 
   137     ## handle ns3::Object::GetObject (left to its own devices,
   138     ## pybindgen will generate a mangled name containing the template
   139     ## argument type name).
   140     if isinstance(pygccxml_definition, member_function_t) \
   141             and pygccxml_definition.parent.name == 'Object' \
   142             and pygccxml_definition.name == 'GetObject':
   143         template_args = templates.args(pygccxml_definition.demangled_name)
   144         if template_args == ['ns3::Object']:
   145             global_annotations['template_instance_names'] = 'ns3::Object=>GetObject'
   146 
   147     ## Don't wrap Simulator::Schedule* (manually wrapped)
   148     if isinstance(pygccxml_definition, member_function_t) \
   149             and pygccxml_definition.parent.name == 'Simulator' \
   150             and pygccxml_definition.name.startswith('Schedule'):
   151         global_annotations['ignore'] = None
   152 
   153     # manually wrapped
   154     if isinstance(pygccxml_definition, member_function_t) \
   155             and pygccxml_definition.parent.name == 'Simulator' \
   156             and pygccxml_definition.name == 'Run':
   157         global_annotations['ignore'] = True
   158 
   159 
   160     ## classes
   161     if isinstance(pygccxml_definition, class_t):
   162         # no need for helper classes to allow subclassing in Python, I think...
   163         if pygccxml_definition.name.endswith('Helper'):
   164             global_annotations['allow_subclassing'] = 'false'
   165 
   166         if pygccxml_definition.decl_string.startswith('::ns3::Callback<'):
   167             # manually handled in ns3modulegen_core_customizations.py
   168             global_annotations['ignore'] = None
   169             return
   170 
   171         if pygccxml_definition.decl_string.startswith('::ns3::TracedCallback<'):
   172             global_annotations['ignore'] = None
   173             return
   174 
   175         if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'):
   176             # handled by pybindgen "type transformation"
   177             global_annotations['ignore'] = None
   178             return
   179 
   180         # table driven class customization
   181         try:
   182             annotations = type_annotations[pygccxml_definition.decl_string]
   183         except KeyError:
   184             pass
   185         else:
   186             global_annotations.update(annotations)
   187 
   188     ## free functions
   189     if isinstance(pygccxml_definition, free_function_t):
   190         if pygccxml_definition.name == 'PeekPointer':
   191             global_annotations['ignore'] = None
   192             return
   193 
   194     ## table driven methods/constructors/functions customization
   195     if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)):
   196         try:
   197             annotations = type_annotations[str(pygccxml_definition)]
   198         except KeyError:
   199             pass
   200         else:
   201             for key,value in annotations.items():
   202                 if key == 'params':
   203                     parameter_annotations.update (value)
   204                     del annotations['params']
   205             global_annotations.update(annotations)
   206 
   207 
   208 # def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper):
   209 #     ## classes
   210 #     if isinstance(pybindgen_wrapper, CppClass):
   211 #         if pybindgen_wrapper.name.endswith('Checker'):
   212 #             print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper
   213 #             #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function)
   214 
   215 
   216 def scan_callback_classes(module_parser, callback_classes_file):
   217     callback_classes_file.write("callback_classes = [\n")
   218     for cls in module_parser.module_namespace.classes(function=module_parser.location_filter,
   219                                                       recursive=False):
   220         if not cls.name.startswith("Callback<"):
   221             continue
   222         assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls
   223         dummy_cls_name, template_parameters = templates.split(cls.decl_string)
   224         callback_classes_file.write("    %r,\n" % template_parameters)
   225     callback_classes_file.write("]\n")
   226 
   227 
   228 class MyPygenClassifier(PygenClassifier):
   229     def __init__(self, headers_map):
   230         self.headers_map = headers_map
   231 
   232     def classify(self, pygccxml_definition):
   233         name = os.path.basename(pygccxml_definition.location.file_name)
   234         try:
   235             return self.headers_map[name]
   236         except KeyError:
   237             return '__main__'
   238 
   239 
   240 def ns3_module_scan(top_builddir, pygen_file_name, everything_h):
   241 
   242     ns3_modules = eval(sys.stdin.read())
   243 
   244     ## do a topological sort on the modules graph
   245     from topsort import topsort
   246     graph = []
   247     for ns3_module_name, (ns3_module_deps, dummy) in ns3_modules.iteritems():
   248         for dep in ns3_module_deps:
   249             graph.append((dep, ns3_module_name))
   250     sorted_ns3_modules = topsort(graph)
   251     #print >> sys.stderr, "******* topological sort: ", sorted_ns3_modules
   252 
   253     sections = [PygenSection('__main__', FileCodeSink(open(pygen_file_name, "wt")))]
   254     headers_map = {} # header_name -> section_name
   255     for ns3_module in sorted_ns3_modules:
   256         section_name = "ns3_module_%s" % ns3_module.replace('-', '_')
   257         file_name = os.path.join(os.path.dirname(pygen_file_name), "%s.py" % section_name)
   258         sections.append(PygenSection(section_name, FileCodeSink(open(file_name, "wt")),
   259                                      section_name + "__local"))
   260         for header in ns3_modules[ns3_module][1]:
   261             headers_map[header] = section_name
   262 
   263     module_parser = ModuleParser('ns3', 'ns3')
   264 
   265     module_parser.add_pre_scan_hook(pre_scan_hook)
   266     #module_parser.add_post_scan_hook(post_scan_hook)
   267 
   268     gccxml_options = dict(
   269         include_paths=[top_builddir],
   270          define_symbols={
   271             #'NS3_ASSERT_ENABLE': None,
   272             #'NS3_LOG_ENABLE': None,
   273             }
   274         )
   275 
   276     module_parser.parse_init([everything_h],
   277                              None, whitelist_paths=[top_builddir, os.path.dirname(everything_h)],
   278                              #includes=['"ns3/everything.h"'],
   279                              pygen_sink=sections,
   280                              pygen_classifier=MyPygenClassifier(headers_map),
   281                              gccxml_options=gccxml_options)
   282     module_parser.scan_types()
   283 
   284     callback_classes_file = open(os.path.join(os.path.dirname(pygen_file_name), "callbacks_list.py"), "wt")
   285     scan_callback_classes(module_parser, callback_classes_file)
   286     callback_classes_file.close()
   287 
   288 
   289     module_parser.scan_methods()
   290     module_parser.scan_functions()
   291     module_parser.parse_finalize()
   292 
   293     for section in sections:
   294         section.code_sink.file.close()
   295 
   296 
   297 
   298 if __name__ == '__main__':
   299     ns3_module_scan(sys.argv[1], sys.argv[3], sys.argv[2])
   300