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_1Exec.xml',
34 'build/xml/classsimgrid_1_1s4u_1_1Host.xml',
35 'build/xml/classsimgrid_1_1s4u_1_1Io.xml',
36 'build/xml/classsimgrid_1_1s4u_1_1Link.xml',
37 'build/xml/classsimgrid_1_1s4u_1_1Mailbox.xml',
38 'build/xml/classsimgrid_1_1s4u_1_1Mutex.xml',
39 'build/xml/classsimgrid_1_1s4u_1_1NetZone.xml',
40 'build/xml/classsimgrid_1_1s4u_1_1Semaphore.xml',
41 'build/xml/classsimgrid_1_1s4u_1_1VirtualMachine.xml',
42 'build/xml/namespacesimgrid_1_1s4u_1_1this__actor.xml',
43 'build/xml/actor_8h.xml',
44 'build/xml/barrier_8h.xml',
45 'build/xml/cond_8h.xml',
46 'build/xml/engine_8h.xml',
47 'build/xml/forward_8h.xml',
48 'build/xml/host_8h.xml',
49 'build/xml/link_8h.xml',
50 'build/xml/mailbox_8h.xml',
51 'build/xml/msg_8h.xml',
52 'build/xml/mutex_8h.xml',
53 'build/xml/semaphore_8h.xml',
54 'build/xml/vm_8h.xml',
55 'build/xml/zone_8h.xml'
66 ############ Search the python elements in the source, and report undocumented ones
69 # data structure in which we store the declaration we find
72 def handle_python_module(fullname, englobing, elm):
73 """Recursive function exploring the python modules."""
75 def found_decl(kind, obj):
76 """Helper function that add an object in the python_decl data structure"""
77 if kind not in python_decl:
78 python_decl[kind] = []
79 python_decl[kind].append(obj)
82 if fullname in python_ignore:
83 print ("Ignore Python symbol '{}' as requested.".format(fullname))
86 if inspect.isroutine(elm) and inspect.isclass(englobing):
87 found_decl("method", fullname)
88 # print('.. automethod:: {}'.format(fullname))
89 elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
90 found_decl("function", fullname)
91 # print('.. autofunction:: {}'.format(fullname))
92 elif inspect.isdatadescriptor(elm):
93 found_decl("attribute", fullname)
94 # print('.. autoattribute:: {}'.format(fullname))
95 elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
96 found_decl("data", fullname)
97 # print('.. autodata:: {}'.format(fullname))
98 elif inspect.ismodule(elm) or inspect.isclass(elm):
99 for name, data in inspect.getmembers(elm):
100 if name.startswith('__'):
102 # print("Recurse on {}.{}".format(fullname, name))
103 handle_python_module("{}.{}".format(fullname, name), elm, data)
105 print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
107 # Start the recursion on the provided Python modules
108 for name in python_modules:
110 module = __import__(name)
112 if os.path.exists("../lib") and "../lib" not in sys.path:
113 print("Adding ../lib to PYTHONPATH as {} cannot be imported".format(name))
114 sys.path.append("../lib")
116 module = __import__(name)
118 print("Cannot import {}, even with PYTHONPATH=../lib".format(name))
121 print("Cannot import {}".format(name))
123 for sub in dir(module):
126 handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
128 # Forget about the python declarations that were actually done in the RST
129 for kind in python_decl:
130 with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
131 for fullname in (l.strip() for l in pse):
132 if fullname not in python_decl[kind]:
133 print("Warning: {} documented but declaration not found in python.".format(fullname))
135 python_decl[kind].remove(fullname)
136 # Dump the missing ones
137 for kind in python_decl:
138 for fullname in python_decl[kind]:
139 print(" .. auto{}:: {}".format(kind, fullname))
141 ################ And now deal with Doxygen declarations
144 doxy_funs = {} # {classname: {func_name: [args]} }
145 doxy_vars = {} # {classname: [names]}
147 # find the declarations in the XML files
148 for arg in xml_files:
149 if arg[-4:] != '.xml':
150 print ("Argument '{}' does not end with '.xml'".format(arg))
152 #print("Parse file {}".format(arg))
154 for elem in tree.findall(".//compounddef"):
155 if elem.attrib["kind"] == "class":
156 if elem.attrib["prot"] != "public":
158 if "compoundname" in elem:
159 raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
160 compoundname = elem.find("compoundname").text
161 #print ("compoundname {}".format(compoundname))
162 elif elem.attrib["kind"] == "file":
164 elif elem.attrib["kind"] == "namespace":
165 compoundname = elem.find("compoundname").text
167 print("Element {} is of kind {}".format(elem.attrib["id"], elem.attrib["kind"]))
169 for member in elem.findall('.//memberdef'):
170 if member.attrib["prot"] != "public":
172 kind = member.attrib["kind"]
173 name = member.find("name").text
174 #print("kind:{} compoundname:{} name:{}".format( kind,compoundname, name))
175 if kind == "variable":
176 if compoundname not in doxy_vars:
177 doxy_vars[compoundname] = []
178 doxy_vars[compoundname].append(name)
179 elif kind == "function":
180 args = member.find('argsstring').text
181 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
183 if compoundname not in doxy_funs:
184 doxy_funs[compoundname] = {}
185 if name not in doxy_funs[compoundname]:
186 doxy_funs[compoundname][name] = []
187 doxy_funs[compoundname][name].append(args)
188 elif kind == "friend":
189 pass # Ignore friendship
191 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
193 # Forget about the declarations that are done in the RST
194 with os.popen('grep autodoxymethod:: find-missing.ignore source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
195 for line in (l.strip() for l in pse):
196 (klass, obj, args) = (None, None, None)
198 (line, args) = line.split('(', 1)
199 args = "({}".format(args)
201 (klass, obj) = line.rsplit('::', 1)
203 (klass, obj) = ("", line)
205 if klass not in doxy_funs:
206 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
208 if obj not in doxy_funs[klass]:
209 print("Warning: Object '{}' documented but not found in '{}'".format(line, klass))
210 # for obj in doxy_funs[klass]:
211 # print(" found: {}::{}".format(klass, obj))
212 elif len(doxy_funs[klass][obj])==1:
213 del doxy_funs[klass][obj]
214 elif args not in doxy_funs[klass][obj]:
215 print("Warning: Function {}{} not found in {}".format(obj, args, klass))
217 #print("Found {} in {}".format(line, klass))
218 doxy_funs[klass][obj].remove(args)
219 if len(doxy_funs[klass][obj]) == 0:
220 del doxy_funs[klass][obj]
221 with os.popen('grep autodoxyvar:: find-missing.ignore source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
222 for line in (l.strip() for l in pse):
223 (klass, var) = line.rsplit('::', 1)
225 if klass not in doxy_vars:
226 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
228 if var not in doxy_vars[klass]:
229 print("Warning: Object {} documented but not found in {}".format(line, klass))
231 # print("Found {} in {}".format(line, klass))
232 doxy_vars[klass].remove(var)
233 if len(doxy_vars[klass]) == 0:
236 # Dump the undocumented Doxygen declarations
237 for obj in sorted(doxy_funs):
238 for meth in sorted(doxy_funs[obj]):
239 for args in sorted(doxy_funs[obj][meth]):
240 print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
242 for obj in doxy_vars:
243 for meth in sorted(doxy_vars[obj]):
244 print(".. autodoxyvar:: {}::{}".format(obj, meth))