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/engine_8h.xml',
57 ############ Search the python elements in the source, and report undocumented ones
60 # data structure in which we store the declaration we find
63 def handle_python_module(fullname, englobing, elm):
64 """Recursive function exploring the python modules."""
66 def found_decl(kind, obj):
67 """Helper function that add an object in the python_decl data structure"""
68 if kind not in python_decl:
69 python_decl[kind] = []
70 python_decl[kind].append(obj)
73 if fullname in python_ignore:
74 print ("Ignore Python symbol '{}' as requested.".format(fullname))
77 if inspect.isroutine(elm) and inspect.isclass(englobing):
78 found_decl("method", fullname)
79 # print('.. automethod:: {}'.format(fullname))
80 elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
81 found_decl("function", fullname)
82 # print('.. autofunction:: {}'.format(fullname))
83 elif inspect.isdatadescriptor(elm):
84 found_decl("attribute", fullname)
85 # print('.. autoattribute:: {}'.format(fullname))
86 elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
87 found_decl("data", fullname)
88 # print('.. autodata:: {}'.format(fullname))
89 elif inspect.ismodule(elm) or inspect.isclass(elm):
90 for name, data in inspect.getmembers(elm):
91 if name.startswith('__'):
93 # print("Recurse on {}.{}".format(fullname, name))
94 handle_python_module("{}.{}".format(fullname, name), elm, data)
96 print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
98 # Start the recursion on the provided Python modules
99 for name in python_modules:
101 module = __import__(name)
103 print("Cannot import {}. Did you set PYTHONPATH=../lib accordingly?".format(name))
105 for sub in dir(module):
108 handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
110 # Forget about the python declarations that were actually done in the RST
111 for kind in python_decl:
112 with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
113 for fullname in (l.strip() for l in pse):
114 if fullname not in python_decl[kind]:
115 print("Warning: {} documented but declaration not found in python.".format(fullname))
117 python_decl[kind].remove(fullname)
118 # Dump the missing ones
119 for kind in python_decl:
120 for fullname in python_decl[kind]:
121 print(" .. auto{}:: {}".format(kind, fullname))
123 ################ And now deal with Doxygen declarations
126 doxy_funs = {} # {classname: {func_name: [args]} }
127 doxy_vars = {} # {classname: [names]}
129 # find the declarations in the XML files
130 for arg in xml_files:
131 if arg[-4:] != '.xml':
132 print ("Argument '{}' does not end with '.xml'".format(arg))
134 #print("Parse file {}".format(arg))
136 for elem in tree.findall(".//compounddef"):
137 if elem.attrib["kind"] == "class":
138 if elem.attrib["prot"] != "public":
140 if "compoundname" in elem:
141 raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
142 compoundname = elem.find("compoundname").text
143 #print ("compoundname {}".format(compoundname))
144 elif elem.attrib["kind"] == "file":
147 print("Element {} is of kind {}".format(elem.attrib["id"], elem.attrib["kind"]))
149 for member in elem.findall('.//memberdef'):
150 if member.attrib["prot"] != "public":
152 kind = member.attrib["kind"]
153 name = member.find("name").text
154 #print("kind:{} compoundname:{} name:{}".format( kind,compoundname, name))
155 if kind == "variable":
156 if compoundname not in doxy_vars:
157 doxy_vars[compoundname] = []
158 doxy_vars[compoundname].append(name)
159 elif kind == "function":
160 args = member.find('argsstring').text
161 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
163 if compoundname not in doxy_funs:
164 doxy_funs[compoundname] = {}
165 if name not in doxy_funs[compoundname]:
166 doxy_funs[compoundname][name] = []
167 doxy_funs[compoundname][name].append(args)
168 elif kind == "friend":
169 pass # Ignore friendship
171 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
173 # Forget about the declarations that are done in the RST
174 with os.popen('grep autodoxymethod:: source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
175 for line in (l.strip() for l in pse):
176 (klass, obj, args) = (None, None, None)
178 (line, args) = line.split('(', 1)
179 args = "({}".format(args)
181 (klass, obj) = line.rsplit('::', 1)
183 (klass, obj) = ("", line)
185 if klass not in doxy_funs:
186 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
188 if obj not in doxy_funs[klass]:
189 print("Warning: Object '{}' documented but not found in '{}'".format(line, klass))
190 # for obj in doxy_funs[klass]:
191 # print(" found: {}::{}".format(klass, obj))
192 elif len(doxy_funs[klass][obj])==1:
193 del doxy_funs[klass][obj]
194 elif args not in doxy_funs[klass][obj]:
195 print("Warning: Function {}{} not found in {}".format(obj, args, klass))
197 #print("Found {} in {}".format(line, klass))
198 doxy_funs[klass][obj].remove(args)
199 if len(doxy_funs[klass][obj]) == 0:
200 del doxy_funs[klass][obj]
201 with os.popen('grep autodoxyvar:: source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
202 for line in (l.strip() for l in pse):
203 (klass, var) = line.rsplit('::', 1)
205 if klass not in doxy_vars:
206 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
208 if var not in doxy_vars[klass]:
209 print("Warning: Object {} documented but not found in {}".format(line, klass))
211 # print("Found {} in {}".format(line, klass))
212 doxy_vars[klass].remove(var)
213 if len(doxy_vars[klass]) == 0:
216 # Dump the undocumented Doxygen declarations
217 for obj in sorted(doxy_funs):
218 for meth in sorted(doxy_funs[obj]):
219 for args in sorted(doxy_funs[obj][meth]):
220 print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
222 for obj in doxy_vars:
223 for meth in sorted(doxy_vars[obj]):
224 print(".. autodoxyvar:: {}::{}".format(obj, meth))