1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
# An example showing how to use the python doxmlparser module to extract some metrics from
# the XML output generated by doxygen for a project.
import sys
import doxmlparser
from doxmlparser.compound import DoxCompoundKind, DoxMemberKind, DoxSectionKind, MixedContainer
class Metrics:
def __init__(self):
self.numClasses = 0
self.numDocClasses = 0
self.numStructs = 0
self.numUnions = 0
self.numInterfaces = 0
self.numExceptions = 0
self.numNamespaces = 0
self.numFiles = 0
self.numDocFiles = 0
self.numGroups = 0
self.numPages = 0
self.numPubMethods = 0
self.numDocPubMethods = 0
self.numProMethods = 0
self.numDocProMethods = 0
self.numPriMethods = 0
self.numDocPriMethods = 0
self.numAttributes = 0
self.numDocAttributes = 0
self.numFunctions = 0
self.numDocFunctions = 0
self.numVariables = 0
self.numDocVariables = 0
self.numParams = 0
def print(self):
numMethods = self.numPubMethods + self.numProMethods + self.numPriMethods
numDocMethods = self.numDocPubMethods + self.numDocProMethods + self.numDocPriMethods
print("Metrics:");
print("-----------------------------------");
if self.numClasses>0:
print("Classes: {:=10} ({} documented)".format(self.numClasses,self.numDocClasses))
if self.numStructs>0:
print("Structs: {:=10}".format(self.numStructs))
if self.numUnions>0:
print("Unions: {:=10}".format(self.numUnions))
if self.numExceptions>0:
print("Exceptions: {:=10}".format(self.numExceptions))
if self.numNamespaces>0:
print("Namespaces: {:=10}".format(self.numNamespaces))
if self.numFiles>0:
print("Files: {:=10} ({} documented)".format(self.numFiles,self.numDocFiles))
if self.numGroups>0:
print("Groups: {:=10}".format(self.numGroups))
if self.numPages>0:
print("Pages: {:=10}".format(self.numPages))
if numMethods>0:
print("Methods: {:=10} ({} documented)".format(numMethods,numDocMethods))
if self.numPubMethods>0:
print(" Public: {:=10} ({} documented)".format(self.numPubMethods,self.numDocPubMethods))
if self.numProMethods>0:
print(" Protected: {:=10} ({} documented)".format(self.numProMethods,self.numDocProMethods))
if self.numPriMethods>0:
print(" Private: {:=10} ({} documented)".format(self.numPriMethods,self.numDocPriMethods))
if self.numFunctions>0:
print("Functions: {:=10} ({} documented)".format(self.numFunctions,self.numDocFunctions))
if self.numAttributes>0:
print("Attributes: {:=10} ({} documented)".format(self.numAttributes,self.numDocAttributes))
if self.numVariables>0:
print("Variables: {:=10} ({} documented)".format(self.numVariables,self.numDocVariables))
if self.numParams>0:
print("Params: {:=10}".format(self.numParams))
print("-----------------------------------");
if self.numClasses>0:
print("Avg. #methods/compound: {:=10}".format(float(numMethods)/float(self.numClasses)))
if numMethods>0:
print("Avg. #params/method: {:=10}".format(float(self.numParams)/float(numMethods)))
print("-----------------------------------");
def description_is_empty(description):
for content in description.content_:
if content.getCategory()==MixedContainer.CategoryText:
if not content.getValue().isspace():
return False # non space-only text
elif not content.getCategory()==MixedContainer.CategoryNone:
return False # some internal object like a paragraph
return True
def is_documented(definition):
return not description_is_empty(definition.get_briefdescription()) or \
not description_is_empty(definition.get_detaileddescription())
def section_is_protected(sectionkind):
return sectionkind in [DoxSectionKind.PROTECTEDTYPE,
DoxSectionKind.PROTECTEDFUNC,
DoxSectionKind.PROTECTEDATTRIB,
DoxSectionKind.PROTECTEDSLOT,
DoxSectionKind.PROTECTEDSTATICFUNC,
DoxSectionKind.PROTECTEDSTATICATTRIB]
def section_is_private(sectionkind):
return sectionkind in [DoxSectionKind.PRIVATETYPE,
DoxSectionKind.PRIVATEFUNC,
DoxSectionKind.PRIVATEATTRIB,
DoxSectionKind.PRIVATESLOT,
DoxSectionKind.PRIVATESTATICFUNC,
DoxSectionKind.PRIVATESTATICATTRIB]
def section_is_public(sectionkind):
return not section_is_protected(sectionkind) and not section_is_private(sectionkind)
def linked_text_to_string(linkedtext):
str=''
if linkedtext:
for text_or_ref in linkedtext.content_:
if text_or_ref.getCategory()==MixedContainer.CategoryText:
str+=text_or_ref.getValue()
else:
str+=text_or_ref.getValue().get_valueOf_()
return str
def parse_members(compounddef,sectiondef,metrics):
functionLikeKind = [DoxMemberKind.FUNCTION,
DoxMemberKind.PROTOTYPE,
DoxMemberKind.SIGNAL,
DoxMemberKind.SLOT,
DoxMemberKind.DCOP]
variableLikeKind = [DoxMemberKind.VARIABLE, DoxMemberKind.PROPERTY]
for memberdef in sectiondef.get_memberdef():
if compounddef.get_kind() in [DoxCompoundKind.CLASS, DoxCompoundKind.STRUCT, DoxCompoundKind.INTERFACE]:
if memberdef.get_kind() in functionLikeKind:
if section_is_public(sectiondef.get_kind()):
metrics.numPubMethods+=1
if is_documented(memberdef):
metrics.numDocPubMethods+=1
elif section_is_protected(sectiondef.get_kind()):
metrics.numProMethods+=1
if is_documented(memberdef):
metrics.numDocProMethods+=1
elif section_is_private(sectiondef.get_kind()):
metrics.numPriMethods+=1
if is_documented(memberdef):
metrics.numDocPriMethods+=1
elif memberdef.get_kind() in variableLikeKind:
metrics.numAttributes+=1
if is_documented(memberdef):
metrics.numDocAttributes+=1
elif compounddef.get_kind() in [DoxCompoundKind.FILE, DoxCompoundKind.NAMESPACE]:
if memberdef.get_kind() in functionLikeKind:
metrics.numFunctions+=1
if is_documented(memberdef):
metrics.numDocFunctions+=1
elif memberdef.get_kind() in variableLikeKind:
metrics.numVariables+=1
if is_documented(memberdef):
metrics.numDocVariables+=1
#for param in memberdef.get_param():
# name = ''
# if param.get_defname():
# name = param.get_defname()
# if param.get_declname():
# name = param.get_declname()
# print("param '{}':'{}'".format(linked_text_to_string(param.get_type()),name))
metrics.numParams+=len(memberdef.get_param())
if memberdef.get_type() and memberdef.get_type()!="void" and linked_text_to_string(memberdef.get_type()):
metrics.numParams+=1 # count non-void return types as well
#print("returns '{}'".format(linked_text_to_string(memberdef.get_type())))
def parse_sections(compounddef,metrics):
for sectiondef in compounddef.get_sectiondef():
parse_members(compounddef,sectiondef,metrics)
def parse_compound(inDirName,baseName,metrics):
rootObj = doxmlparser.compound.parse(inDirName+"/"+baseName+".xml",True)
for compounddef in rootObj.get_compounddef():
kind = compounddef.get_kind()
if kind==DoxCompoundKind.CLASS:
metrics.numClasses+=1
if is_documented(compounddef):
metrics.numDocClasses+=1
elif kind==DoxCompoundKind.STRUCT:
metrics.numStructs+=1
elif kind==DoxCompoundKind.UNION:
metrics.numUnions+=1
elif kind==DoxCompoundKind.INTERFACE:
metrics.numInterfaces+=1
elif kind==DoxCompoundKind.EXCEPTION:
metrics.numExceptions+=1
elif kind==DoxCompoundKind.NAMESPACE:
metrics.numNamespaces+=1
elif kind==DoxCompoundKind.FILE:
metrics.numFiles+=1
if is_documented(compounddef):
metrics.numDocFiles+=1
elif kind==DoxCompoundKind.GROUP:
metrics.numGroups+=1
elif kind==DoxCompoundKind.PAGE:
metrics.numPages+=1
else:
continue
parse_sections(compounddef,metrics)
def parse_index(inDirName):
metrics = Metrics()
rootObj = doxmlparser.index.parse(inDirName+"/index.xml",True)
for compound in rootObj.get_compound(): # for each compound defined in the index
print("Processing {0}...".format(compound.get_name()))
parse_compound(inDirName,compound.get_refid(),metrics)
metrics.print()
def usage():
print("Usage {0} <xml_output_dir>".format(sys.argv[0]))
sys.exit(1)
def main():
args = sys.argv[1:]
if len(args)==1:
parse_index(args[0])
else:
usage()
if __name__ == '__main__':
main()
|