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
|
#! /usr/bin/python
from xml.dom import minidom
from sys import argv
helptext = \
"""This script processes the XML generated by "make doc" and produces summary information
on symbols that Mir intends to make public.
To use:
1. Go to your build folder and run "make doc"
2. Create summary by running "../tools/process_doxygen_xml.py doc/xml/*.xml > raw-output"
3. For each symbol map (e.g. mirserver)
3.1. cat raw-output | grep "^mirserver public" | sed "s/mirserver public: / /" | sort > mirserver-public-symbols
3.2. vi -d mirserver-public-symbols ../src/server/symbols.map"""
debug = False
def get_text(node):
rc = []
for node in node.childNodes:
if node.nodeType == node.TEXT_NODE:
rc.append(node.data)
elif node.nodeType == node.ELEMENT_NODE:
rc.append(get_text(node))
return ''.join(rc)
def get_text_for_element(parent, tagname):
rc = []
nodes = parent.getElementsByTagName(tagname);
for node in nodes : rc.append(get_text(node))
return ''.join(rc)
def get_file_location(node):
for node in node.childNodes:
if node.nodeType == node.ELEMENT_NODE and node.tagName == 'location':
return node.attributes['file'].value
if debug: print 'no location in:', node
return None
def has_element(node, tagname):
for node in node.childNodes:
if node.nodeType == node.ELEMENT_NODE and node.tagName in tagname:
return True
return False
def print_attribs(node, attribs):
for attrib in attribs : print ' ', attrib, '=', node.attributes[attrib].value
def concat_text_from_tags(parent, tagnames):
rc = []
for tag in tagnames : rc.append(get_text_for_element(parent, tag))
return ''.join(rc)
def print_location(node):
print ' ', 'location', '=', get_file_location(node)
def get_attribs(node):
kind = node.attributes['kind'].value
static = node.attributes['static'].value
prot = node.attributes['prot'].value
return (kind, static, prot)
def is_file_publishable(file_location):
return file_location.startswith('include/') \
or 'build/src' in file_location
# Special cases for publishing anyway:
publish_special_cases = {
# Although private this is called by a template wrapper function that instantiates
# in client code
'mir::SharedLibrary::load_symbol*',
}
component_map = {}
def report(component, publish, symbol):
symbol = symbol.replace('~', '?')
if symbol in publish_special_cases: publish = True
symbols = component_map.get(component, {'public' : set(), 'private' : set()})
if publish: symbols['public'].add(symbol)
else: symbols['private'].add(symbol)
component_map[component] = symbols
if not debug: return
if publish: print ' PUBLISH in {}: {}'.format(component, symbol)
else : print 'NOPUBLISH in {}: {}'.format(component, symbol)
def print_report():
format = '{} {}: {};'
for component, symbols in component_map.iteritems():
print 'COMPONENT:', component
for key in symbols.keys():
for symbol in symbols[key]: print format.format(component, key, symbol)
print
def print_debug_info(node, attributes):
if not debug: return
print
print_attribs(node, attributes)
print_location(node)
def find_physical_component(location_file):
path_elements = location_file.split('/')
found = False
for element in path_elements:
if found: return element
found = element in ['include', 'src']
if debug: print 'no component in:', location_file
return None
def mapped_physical_component(location_file):
location = find_physical_component(location_file)
return 'mir' + location
def parse_member_def(context_name, node, is_class):
library = mapped_physical_component(get_file_location(node))
(kind, static, prot) = get_attribs(node)
if kind in ['enum', 'typedef']: return
if has_element(node, ['templateparamlist']): return
if kind in ['function'] and node.attributes['inline'].value == 'yes': return
name = concat_text_from_tags(node, ['name'])
if name in ['__attribute__']:
if debug: print ' ignoring doxygen mis-parsing:', concat_text_from_tags(node, ['argsstring'])
return
if name.startswith('operator'): name = 'operator'
if not context_name == None: symbol = context_name + '::' + name
else: symbol = name
file_location = get_file_location(node)
publish = is_file_publishable(file_location)
is_function = kind == 'function'
if publish: publish = kind != 'define'
if publish and is_class: publish = is_function or static == 'yes'
if publish and prot == 'private':
if is_function: publish = node.attributes['virt'].value == 'virtual'
else: publish = False
if publish and has_element(node, ['argsstring']):
publish = not get_text_for_element(node, 'argsstring').endswith('=0')
if is_function: print_debug_info(node, ['kind', 'prot', 'static', 'virt'])
else: print_debug_info(node, ['kind', 'prot', 'static'])
if debug: print ' is_class:', is_class
report(library, publish, symbol+'*')
if is_function and node.attributes['virt'].value == 'virtual': report(library, publish, 'non-virtual?thunk?to?'+symbol+'*')
def parse_compound_defs(xmldoc):
compounddefs = xmldoc.getElementsByTagName('compounddef')
for node in compounddefs:
kind = node.attributes['kind'].value
if kind in ['page', 'file', 'example', 'union']: continue
if kind in ['group']:
for member in node.getElementsByTagName('memberdef') :
parse_member_def(None, member, False)
continue
if kind in ['namespace']:
symbol = concat_text_from_tags(node, ['compoundname'])
for member in node.getElementsByTagName('memberdef') :
parse_member_def(symbol, member, False)
continue
file = get_file_location(node)
if debug: print ' from file:', file
if '/examples/' in file or '/test/' in file or '[generated]' in file or '[STL]' in file:
continue
if has_element(node, ['templateparamlist']): continue
library = mapped_physical_component(file)
symbol = concat_text_from_tags(node, ['compoundname'])
file_location = get_file_location(node)
publish = is_file_publishable(file_location)
if publish:
if kind in ['class', 'struct']:
prot = node.attributes['prot'].value
publish = prot != 'private'
print_debug_info(node, ['kind', 'prot'])
report(library, publish, 'vtable?for?' + symbol)
report(library, publish, 'typeinfo?for?' + symbol)
if publish:
for member in node.getElementsByTagName('memberdef') :
parse_member_def(symbol, member, kind in ['class', 'struct'])
if __name__ == "__main__":
if len(argv) == 1 or '-h' in argv or '--help' in argv:
print helptext
exit()
for arg in argv[1:]:
try:
if debug: print 'Processing:', arg
xmldoc = minidom.parse(arg)
parse_compound_defs(xmldoc)
except Exception as error:
print 'Error:', arg, error
print_report()
|