1 #! /usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Copyright (c) 2019. The SimGrid Team.
7 # This program is free software; you can redistribute it and/or modify it
8 # under the terms of the license (GNU LGPL) which comes with this package.
11 Search for symbols documented in both the XML files produced by Doxygen and the python modules,
12 but not documented with autodoxy in the RST files.
14 This script is tailored to SimGrid own needs and should be made more generic for autodoxy.
21 import xml.etree.ElementTree as ET
25 'build/xml/classsimgrid_1_1s4u_1_1Activity.xml',
26 'build/xml/classsimgrid_1_1s4u_1_1Actor.xml',
27 'build/xml/classsimgrid_1_1s4u_1_1Barrier.xml',
28 'build/xml/classsimgrid_1_1s4u_1_1Comm.xml',
29 'build/xml/classsimgrid_1_1s4u_1_1ConditionVariable.xml',
30 'build/xml/classsimgrid_1_1s4u_1_1Disk.xml',
31 'build/xml/classsimgrid_1_1s4u_1_1Engine.xml',
32 'build/xml/classsimgrid_1_1s4u_1_1ExecPar.xml',
33 'build/xml/classsimgrid_1_1s4u_1_1ExecSeq.xml',
34 'build/xml/classsimgrid_1_1s4u_1_1Exec.xml',
35 'build/xml/classsimgrid_1_1s4u_1_1Host.xml',
36 'build/xml/classsimgrid_1_1s4u_1_1Io.xml',
37 'build/xml/classsimgrid_1_1s4u_1_1Link.xml',
38 'build/xml/classsimgrid_1_1s4u_1_1Mailbox.xml',
39 'build/xml/classsimgrid_1_1s4u_1_1Mutex.xml',
40 'build/xml/classsimgrid_1_1s4u_1_1NetZone.xml',
41 'build/xml/classsimgrid_1_1s4u_1_1Semaphore.xml',
42 'build/xml/classsimgrid_1_1s4u_1_1VirtualMachine.xml'
53 ############ Search the python elements in the source, and report undocumented ones
56 # data structure in which we store the declaration we find
59 def handle_python_module(fullname, englobing, elm):
60 """Recursive function exploring the python modules."""
62 def found_decl(kind, obj):
63 """Helper function that add an object in the python_decl data structure"""
64 if kind not in python_decl: python_decl[kind] = []
65 python_decl[kind].append(obj)
68 if fullname in python_ignore:
69 print ("Ignore Python symbol '{}' as requested.".format(fullname))
72 if inspect.isroutine(elm) and inspect.isclass(englobing):
73 found_decl("method", fullname)
74 # print('.. automethod:: {}'.format(fullname))
75 elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
76 found_decl("function", fullname)
77 # print('.. autofunction:: {}'.format(fullname))
78 elif inspect.isdatadescriptor(elm):
79 found_decl("attribute", fullname)
80 # print('.. autoattribute:: {}'.format(fullname))
81 elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
82 found_decl("data", fullname)
83 # print('.. autodata:: {}'.format(fullname))
84 elif inspect.ismodule(elm) or inspect.isclass(elm):
85 for name, data in inspect.getmembers(elm):
86 if name.startswith('__'):
88 # print("Recurse on {}.{}".format(fullname, name))
89 handle_python_module("{}.{}".format(fullname, name), elm, data)
91 print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
93 # Start the recursion on the provided Python modules
94 for name in python_modules:
96 module = __import__(name)
98 print("Cannot import {}. Did you set PYTHONPATH=../lib accordingly?".format(name))
100 for sub in dir(module):
103 handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
105 # Forget about the python declarations that were actually done in the RST
106 for kind in python_decl:
107 with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
108 for fullname in (l.strip() for l in pse):
109 if fullname not in python_decl[kind]:
110 print("Warning: {} documented but declaration not found in python.".format(fullname))
112 python_decl[kind].remove(fullname)
113 # Dump the missing ones
114 for kind in python_decl:
115 for fullname in python_decl[kind]:
116 print(" .. auto{}:: {}".format(kind, fullname))
118 ################ And now deal with Doxygen declarations
121 doxy_funs = {} # {classname: {func_name: [args]} }
122 doxy_vars = {} # {classname: [names]}
124 # find the declarations in the XML files
125 for arg in xml_files[:3]:
126 if arg[-4:] != '.xml':
127 print ("Argument '{}' does not end with '.xml'".format(arg))
129 print("Parse file {}".format(arg))
131 for elem in tree.findall(".//compounddef"):
132 if elem.attrib["prot"] != "public":
134 if "compoundname" in elem:
135 raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
136 compoundname = elem.find("compoundname").text
137 #print ("compoundname {}".format(compoundname))
138 for member in elem.findall('.//memberdef'):
139 if member.attrib["prot"] != "public":
141 kind = member.attrib["kind"]
142 name = member.find("name").text
143 if kind == "variable":
144 if compoundname not in doxy_vars: doxy_vars[compoundname] = []
145 doxy_vars[compoundname].append(name)
146 elif kind == "function":
147 args = member.find('argsstring').text
148 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
150 if compoundname not in doxy_funs: doxy_funs[compoundname] = {}
151 if name not in doxy_funs[compoundname]: doxy_funs[compoundname][name] = []
152 doxy_funs[compoundname][name].append(args)
154 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
156 # Forget about the declarations that are done in the RST
157 with os.popen('grep autodoxymethod:: source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
158 for line in (l.strip() for l in pse):
159 (klass, obj, args) = (None, None, None)
161 (line, args) = line.split('(', 1)
162 args = "({}".format(args)
163 (klass, obj) = line.rsplit('::', 1)
165 if klass not in doxy_funs:
166 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
168 if obj not in doxy_funs[klass]:
169 print("Warning: Object {} documented but not found in {}".format(line, klass))
170 elif len(doxy_funs[klass][obj])==1:
171 del doxy_funs[klass][obj]
172 elif args not in doxy_funs[klass][obj]:
173 print("Warning: Function {}{} not found in {}".format(obj, args, klass))
175 # print("Found {} in {}".format(line, klass))
176 doxy_funs[klass][obj].remove(args)
177 if len(doxy_funs[klass][obj]) == 0:
178 del doxy_funs[klass][obj]
179 with os.popen('grep autodoxyvar:: source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
180 for line in (l.strip() for l in pse):
181 (klass, var) = line.rsplit('::', 1)
183 if klass not in doxy_vars:
184 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
186 if var not in doxy_vars[klass]:
187 print("Warning: Object {} documented but not found in {}".format(line, klass))
189 # print("Found {} in {}".format(line, klass))
190 doxy_vars[klass].remove(var)
191 if len(doxy_vars[klass]) == 0:
194 # Dump the undocumented Doxygen declarations
195 for obj in sorted(doxy_funs):
196 for meth in sorted(doxy_funs[obj]):
197 for args in sorted(doxy_funs[obj][meth]):
198 print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
200 for obj in doxy_vars:
201 for meth in sorted(doxy_vars[obj]):
202 print(".. autodoxyvar:: {}::{}".format(obj, meth))