1 #! /usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Copyright (c) 2019-2020. The SimGrid Team. All rights reserved.
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the license (GNU LGPL) which comes with this package.
10 Search for symbols documented in both the XML files produced by Doxygen and the python modules,
11 but not documented with autodoxy in the RST files.
13 This script is tailored to SimGrid own needs and should be made more generic for autodoxy.
15 If you are missing some dependencies, try: pip3 install --requirement docs/requirements.txt
22 import xml.etree.ElementTree as ET
26 'build/xml/classsimgrid_1_1s4u_1_1Activity.xml',
27 'build/xml/classsimgrid_1_1s4u_1_1Actor.xml',
28 'build/xml/classsimgrid_1_1s4u_1_1Barrier.xml',
29 'build/xml/classsimgrid_1_1s4u_1_1Comm.xml',
30 'build/xml/classsimgrid_1_1s4u_1_1ConditionVariable.xml',
31 'build/xml/classsimgrid_1_1s4u_1_1Disk.xml',
32 'build/xml/classsimgrid_1_1s4u_1_1Engine.xml',
33 'build/xml/classsimgrid_1_1s4u_1_1ExecPar.xml',
34 'build/xml/classsimgrid_1_1s4u_1_1ExecSeq.xml',
35 'build/xml/classsimgrid_1_1s4u_1_1Exec.xml',
36 'build/xml/classsimgrid_1_1s4u_1_1Host.xml',
37 'build/xml/classsimgrid_1_1s4u_1_1Io.xml',
38 'build/xml/classsimgrid_1_1s4u_1_1Link.xml',
39 'build/xml/classsimgrid_1_1s4u_1_1Mailbox.xml',
40 'build/xml/classsimgrid_1_1s4u_1_1Mutex.xml',
41 'build/xml/classsimgrid_1_1s4u_1_1NetZone.xml',
42 'build/xml/classsimgrid_1_1s4u_1_1Semaphore.xml',
43 'build/xml/classsimgrid_1_1s4u_1_1VirtualMachine.xml',
44 'build/xml/actor_8h.xml',
45 'build/xml/barrier_8h.xml',
46 'build/xml/cond_8h.xml',
47 'build/xml/engine_8h.xml',
48 'build/xml/forward_8h.xml',
49 'build/xml/host_8h.xml',
50 'build/xml/link_8h.xml',
51 'build/xml/mailbox_8h.xml',
52 'build/xml/msg_8h.xml',
53 'build/xml/mutex_8h.xml',
54 'build/xml/semaphore_8h.xml',
55 'build/xml/vm_8h.xml',
56 'build/xml/zone_8h.xml'
67 ############ Search the python elements in the source, and report undocumented ones
70 # data structure in which we store the declaration we find
73 def handle_python_module(fullname, englobing, elm):
74 """Recursive function exploring the python modules."""
76 def found_decl(kind, obj):
77 """Helper function that add an object in the python_decl data structure"""
78 if kind not in python_decl:
79 python_decl[kind] = []
80 python_decl[kind].append(obj)
83 if fullname in python_ignore:
84 print ("Ignore Python symbol '{}' as requested.".format(fullname))
87 if inspect.isroutine(elm) and inspect.isclass(englobing):
88 found_decl("method", fullname)
89 # print('.. automethod:: {}'.format(fullname))
90 elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
91 found_decl("function", fullname)
92 # print('.. autofunction:: {}'.format(fullname))
93 elif inspect.isdatadescriptor(elm):
94 found_decl("attribute", fullname)
95 # print('.. autoattribute:: {}'.format(fullname))
96 elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
97 found_decl("data", fullname)
98 # print('.. autodata:: {}'.format(fullname))
99 elif inspect.ismodule(elm) or inspect.isclass(elm):
100 for name, data in inspect.getmembers(elm):
101 if name.startswith('__'):
103 # print("Recurse on {}.{}".format(fullname, name))
104 handle_python_module("{}.{}".format(fullname, name), elm, data)
106 print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
108 # Start the recursion on the provided Python modules
109 for name in python_modules:
111 module = __import__(name)
113 print("Cannot import {}. Did you set PYTHONPATH=../lib accordingly?".format(name))
115 for sub in dir(module):
118 handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
120 # Forget about the python declarations that were actually done in the RST
121 for kind in python_decl:
122 with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
123 for fullname in (l.strip() for l in pse):
124 if fullname not in python_decl[kind]:
125 print("Warning: {} documented but declaration not found in python.".format(fullname))
127 python_decl[kind].remove(fullname)
128 # Dump the missing ones
129 for kind in python_decl:
130 for fullname in python_decl[kind]:
131 print(" .. auto{}:: {}".format(kind, fullname))
133 ################ And now deal with Doxygen declarations
136 doxy_funs = {} # {classname: {func_name: [args]} }
137 doxy_vars = {} # {classname: [names]}
139 # find the declarations in the XML files
140 for arg in xml_files:
141 if arg[-4:] != '.xml':
142 print ("Argument '{}' does not end with '.xml'".format(arg))
144 #print("Parse file {}".format(arg))
146 for elem in tree.findall(".//compounddef"):
147 if elem.attrib["kind"] == "class":
148 if elem.attrib["prot"] != "public":
150 if "compoundname" in elem:
151 raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
152 compoundname = elem.find("compoundname").text
153 #print ("compoundname {}".format(compoundname))
154 elif elem.attrib["kind"] == "file":
157 print("Element {} is of kind {}".format(elem.attrib["id"], elem.attrib["kind"]))
159 for member in elem.findall('.//memberdef'):
160 if member.attrib["prot"] != "public":
162 kind = member.attrib["kind"]
163 name = member.find("name").text
164 #print("kind:{} compoundname:{} name:{}".format( kind,compoundname, name))
165 if kind == "variable":
166 if compoundname not in doxy_vars:
167 doxy_vars[compoundname] = []
168 doxy_vars[compoundname].append(name)
169 elif kind == "function":
170 args = member.find('argsstring').text
171 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
173 if compoundname not in doxy_funs:
174 doxy_funs[compoundname] = {}
175 if name not in doxy_funs[compoundname]:
176 doxy_funs[compoundname][name] = []
177 doxy_funs[compoundname][name].append(args)
178 elif kind == "friend":
179 pass # Ignore friendship
181 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
183 # Forget about the declarations that are done in the RST
184 with os.popen('grep autodoxymethod:: source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
185 for line in (l.strip() for l in pse):
186 (klass, obj, args) = (None, None, None)
188 (line, args) = line.split('(', 1)
189 args = "({}".format(args)
191 (klass, obj) = line.rsplit('::', 1)
193 (klass, obj) = ("", line)
195 if klass not in doxy_funs:
196 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
198 if obj not in doxy_funs[klass]:
199 print("Warning: Object '{}' documented but not found in '{}'".format(line, klass))
200 # for obj in doxy_funs[klass]:
201 # print(" found: {}::{}".format(klass, obj))
202 elif len(doxy_funs[klass][obj])==1:
203 del doxy_funs[klass][obj]
204 elif args not in doxy_funs[klass][obj]:
205 print("Warning: Function {}{} not found in {}".format(obj, args, klass))
207 #print("Found {} in {}".format(line, klass))
208 doxy_funs[klass][obj].remove(args)
209 if len(doxy_funs[klass][obj]) == 0:
210 del doxy_funs[klass][obj]
211 with os.popen('grep autodoxyvar:: source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
212 for line in (l.strip() for l in pse):
213 (klass, var) = line.rsplit('::', 1)
215 if klass not in doxy_vars:
216 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
218 if var not in doxy_vars[klass]:
219 print("Warning: Object {} documented but not found in {}".format(line, klass))
221 # print("Found {} in {}".format(line, klass))
222 doxy_vars[klass].remove(var)
223 if len(doxy_vars[klass]) == 0:
226 # Dump the undocumented Doxygen declarations
227 for obj in sorted(doxy_funs):
228 for meth in sorted(doxy_funs[obj]):
229 for args in sorted(doxy_funs[obj][meth]):
230 print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
232 for obj in doxy_vars:
233 for meth in sorted(doxy_vars[obj]):
234 print(".. autodoxyvar:: {}::{}".format(obj, meth))