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:
65 python_decl[kind] = []
66 python_decl[kind].append(obj)
69 if fullname in python_ignore:
70 print ("Ignore Python symbol '{}' as requested.".format(fullname))
73 if inspect.isroutine(elm) and inspect.isclass(englobing):
74 found_decl("method", fullname)
75 # print('.. automethod:: {}'.format(fullname))
76 elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
77 found_decl("function", fullname)
78 # print('.. autofunction:: {}'.format(fullname))
79 elif inspect.isdatadescriptor(elm):
80 found_decl("attribute", fullname)
81 # print('.. autoattribute:: {}'.format(fullname))
82 elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
83 found_decl("data", fullname)
84 # print('.. autodata:: {}'.format(fullname))
85 elif inspect.ismodule(elm) or inspect.isclass(elm):
86 for name, data in inspect.getmembers(elm):
87 if name.startswith('__'):
89 # print("Recurse on {}.{}".format(fullname, name))
90 handle_python_module("{}.{}".format(fullname, name), elm, data)
92 print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
94 # Start the recursion on the provided Python modules
95 for name in python_modules:
97 module = __import__(name)
99 print("Cannot import {}. Did you set PYTHONPATH=../lib accordingly?".format(name))
101 for sub in dir(module):
104 handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
106 # Forget about the python declarations that were actually done in the RST
107 for kind in python_decl:
108 with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
109 for fullname in (l.strip() for l in pse):
110 if fullname not in python_decl[kind]:
111 print("Warning: {} documented but declaration not found in python.".format(fullname))
113 python_decl[kind].remove(fullname)
114 # Dump the missing ones
115 for kind in python_decl:
116 for fullname in python_decl[kind]:
117 print(" .. auto{}:: {}".format(kind, fullname))
119 ################ And now deal with Doxygen declarations
122 doxy_funs = {} # {classname: {func_name: [args]} }
123 doxy_vars = {} # {classname: [names]}
125 # find the declarations in the XML files
126 for arg in xml_files[:3]:
127 if arg[-4:] != '.xml':
128 print ("Argument '{}' does not end with '.xml'".format(arg))
130 print("Parse file {}".format(arg))
132 for elem in tree.findall(".//compounddef"):
133 if elem.attrib["prot"] != "public":
135 if "compoundname" in elem:
136 raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
137 compoundname = elem.find("compoundname").text
138 #print ("compoundname {}".format(compoundname))
139 for member in elem.findall('.//memberdef'):
140 if member.attrib["prot"] != "public":
142 kind = member.attrib["kind"]
143 name = member.find("name").text
144 if kind == "variable":
145 if compoundname not in doxy_vars:
146 doxy_vars[compoundname] = []
147 doxy_vars[compoundname].append(name)
148 elif kind == "function":
149 args = member.find('argsstring').text
150 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
152 if compoundname not in doxy_funs:
153 doxy_funs[compoundname] = {}
154 if name not in doxy_funs[compoundname]:
155 doxy_funs[compoundname][name] = []
156 doxy_funs[compoundname][name].append(args)
158 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
160 # Forget about the declarations that are done in the RST
161 with os.popen('grep autodoxymethod:: source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
162 for line in (l.strip() for l in pse):
163 (klass, obj, args) = (None, None, None)
165 (line, args) = line.split('(', 1)
166 args = "({}".format(args)
167 (klass, obj) = line.rsplit('::', 1)
169 if klass not in doxy_funs:
170 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
172 if obj not in doxy_funs[klass]:
173 print("Warning: Object {} documented but not found in {}".format(line, klass))
174 elif len(doxy_funs[klass][obj])==1:
175 del doxy_funs[klass][obj]
176 elif args not in doxy_funs[klass][obj]:
177 print("Warning: Function {}{} not found in {}".format(obj, args, klass))
179 # print("Found {} in {}".format(line, klass))
180 doxy_funs[klass][obj].remove(args)
181 if len(doxy_funs[klass][obj]) == 0:
182 del doxy_funs[klass][obj]
183 with os.popen('grep autodoxyvar:: source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
184 for line in (l.strip() for l in pse):
185 (klass, var) = line.rsplit('::', 1)
187 if klass not in doxy_vars:
188 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
190 if var not in doxy_vars[klass]:
191 print("Warning: Object {} documented but not found in {}".format(line, klass))
193 # print("Found {} in {}".format(line, klass))
194 doxy_vars[klass].remove(var)
195 if len(doxy_vars[klass]) == 0:
198 # Dump the undocumented Doxygen declarations
199 for obj in sorted(doxy_funs):
200 for meth in sorted(doxy_funs[obj]):
201 for args in sorted(doxy_funs[obj][meth]):
202 print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
204 for obj in doxy_vars:
205 for meth in sorted(doxy_vars[obj]):
206 print(".. autodoxyvar:: {}::{}".format(obj, meth))