1 #! /usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Copyright (c) 2019. 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'
54 ############ Search the python elements in the source, and report undocumented ones
57 # data structure in which we store the declaration we find
60 def handle_python_module(fullname, englobing, elm):
61 """Recursive function exploring the python modules."""
63 def found_decl(kind, obj):
64 """Helper function that add an object in the python_decl data structure"""
65 if kind not in python_decl:
66 python_decl[kind] = []
67 python_decl[kind].append(obj)
70 if fullname in python_ignore:
71 print ("Ignore Python symbol '{}' as requested.".format(fullname))
74 if inspect.isroutine(elm) and inspect.isclass(englobing):
75 found_decl("method", fullname)
76 # print('.. automethod:: {}'.format(fullname))
77 elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
78 found_decl("function", fullname)
79 # print('.. autofunction:: {}'.format(fullname))
80 elif inspect.isdatadescriptor(elm):
81 found_decl("attribute", fullname)
82 # print('.. autoattribute:: {}'.format(fullname))
83 elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
84 found_decl("data", fullname)
85 # print('.. autodata:: {}'.format(fullname))
86 elif inspect.ismodule(elm) or inspect.isclass(elm):
87 for name, data in inspect.getmembers(elm):
88 if name.startswith('__'):
90 # print("Recurse on {}.{}".format(fullname, name))
91 handle_python_module("{}.{}".format(fullname, name), elm, data)
93 print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
95 # Start the recursion on the provided Python modules
96 for name in python_modules:
98 module = __import__(name)
100 print("Cannot import {}. Did you set PYTHONPATH=../lib accordingly?".format(name))
102 for sub in dir(module):
105 handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
107 # Forget about the python declarations that were actually done in the RST
108 for kind in python_decl:
109 with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
110 for fullname in (l.strip() for l in pse):
111 if fullname not in python_decl[kind]:
112 print("Warning: {} documented but declaration not found in python.".format(fullname))
114 python_decl[kind].remove(fullname)
115 # Dump the missing ones
116 for kind in python_decl:
117 for fullname in python_decl[kind]:
118 print(" .. auto{}:: {}".format(kind, fullname))
120 ################ And now deal with Doxygen declarations
123 doxy_funs = {} # {classname: {func_name: [args]} }
124 doxy_vars = {} # {classname: [names]}
126 # find the declarations in the XML files
127 for arg in xml_files[:3]:
128 if arg[-4:] != '.xml':
129 print ("Argument '{}' does not end with '.xml'".format(arg))
131 print("Parse file {}".format(arg))
133 for elem in tree.findall(".//compounddef"):
134 if elem.attrib["prot"] != "public":
136 if "compoundname" in elem:
137 raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
138 compoundname = elem.find("compoundname").text
139 #print ("compoundname {}".format(compoundname))
140 for member in elem.findall('.//memberdef'):
141 if member.attrib["prot"] != "public":
143 kind = member.attrib["kind"]
144 name = member.find("name").text
145 if kind == "variable":
146 if compoundname not in doxy_vars:
147 doxy_vars[compoundname] = []
148 doxy_vars[compoundname].append(name)
149 elif kind == "function":
150 args = member.find('argsstring').text
151 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
153 if compoundname not in doxy_funs:
154 doxy_funs[compoundname] = {}
155 if name not in doxy_funs[compoundname]:
156 doxy_funs[compoundname][name] = []
157 doxy_funs[compoundname][name].append(args)
159 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
161 # Forget about the declarations that are done in the RST
162 with os.popen('grep autodoxymethod:: source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
163 for line in (l.strip() for l in pse):
164 (klass, obj, args) = (None, None, None)
166 (line, args) = line.split('(', 1)
167 args = "({}".format(args)
168 (klass, obj) = line.rsplit('::', 1)
170 if klass not in doxy_funs:
171 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
173 if obj not in doxy_funs[klass]:
174 print("Warning: Object {} documented but not found in {}".format(line, klass))
175 elif len(doxy_funs[klass][obj])==1:
176 del doxy_funs[klass][obj]
177 elif args not in doxy_funs[klass][obj]:
178 print("Warning: Function {}{} not found in {}".format(obj, args, klass))
180 # print("Found {} in {}".format(line, klass))
181 doxy_funs[klass][obj].remove(args)
182 if len(doxy_funs[klass][obj]) == 0:
183 del doxy_funs[klass][obj]
184 with os.popen('grep autodoxyvar:: source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
185 for line in (l.strip() for l in pse):
186 (klass, var) = line.rsplit('::', 1)
188 if klass not in doxy_vars:
189 print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
191 if var not in doxy_vars[klass]:
192 print("Warning: Object {} documented but not found in {}".format(line, klass))
194 # print("Found {} in {}".format(line, klass))
195 doxy_vars[klass].remove(var)
196 if len(doxy_vars[klass]) == 0:
199 # Dump the undocumented Doxygen declarations
200 for obj in sorted(doxy_funs):
201 for meth in sorted(doxy_funs[obj]):
202 for args in sorted(doxy_funs[obj][meth]):
203 print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
205 for obj in doxy_vars:
206 for meth in sorted(doxy_vars[obj]):
207 print(".. autodoxyvar:: {}::{}".format(obj, meth))