DoxygenのXML出力
ひさびさに、仕事で(C++ではない)C言語を触っている。コーディングのお約束として、基本はグローバル変数、関数パラメータは使わない、構造体も使っちゃダメ、ということになってる。特殊用途なので仕方ないらしい。
ただ、こうなると、動作の解析が一筋縄ではいかないので、グローバル変数の一覧、参照範囲等をまとめた資料がほしくなる。という訳で、DoxygenのXML出力をちょこっと整形出力してみた。
出力はこんな感じになる。
Excelに読込むと、関数の上下関係の概略と、どの関数がどの変数を参照しているのか、多少なりとも分かりやすくなる。
ファイル名,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,参照変数名,説明 foo.c,hoge_sch,,,,,,,,,,,1000 XXXスケジューラ foo.c,,hoge_cnt,,,,,,,,,,1100 XXX管理 foo.c,,,hoge_ini,,,,,,,,,1200 XXX入力管理 foo.c,,,,hoge_xxx,,,,,,,, ,,,,,,,,,,,v_yyy,○○○ ,,,,,,,,,,,v_zzz,○○○ ・ ・ ・ foo.c,,,,hoge_yyy,,,,,,,,1300 XXXパラメータ設定 ,,,,,,,,,,,v_yyy,○○○ ,,,,,,,,,,,v_zzz,○○○ ・ ・ ・
# -*- coding: utf-8 -*- import argparse import os import sys import xml.etree.ElementTree as ET Doxygen = None def SetupDoxygen(directory, entry): global Doxygen Doxygen = DoxygenXml(directory, entry) class Member(object): """DoxygenのXML出力のmember要素に相当するクラス """ __members = {} @classmethod def get(klass, id, c_ref=None): if not klass.__members.has_key(id): klass.__members[id] = None #循環参照を回避するため、仮エントリ登録 aMember = Member(c_ref, id) klass.__members[aMember.id] = aMember return klass.__members[id] def __init__(self, c_ref, id): self.c_ref = c_ref self.id = id self.filename = Doxygen.getCompound(c_ref).find('name').text element = self.getMember() #----- self.kind = element.attrib['kind'] self.name = element.find('name').text self.para = Doxygen.text(element.find('./detaileddescription/para')) if self.kind == 'function': self.funcs = {} self.vars = {} for ref in element.findall("./references"): c_ref = ref.attrib['compoundref'] m_ref = ref.attrib['refid'] ref = Member.get(m_ref, c_ref) if ref.kind == 'variable': self.vars[ref.id] = ref if ref.kind == 'function': self.funcs[ref.id] = ref def getMember(self): return Doxygen.getMember(self.c_ref, self.id) class DoxygenXml(object): """DoxygenのXML出力からデータ取得を行うクラス """ def __init__(self, directory, entry): self.directory = directory self.entry = entry self.index_xml = os.path.join(self.directory, 'index.xml') self.doxygenindex = ET.parse(self.index_xml).getroot() def getEntry(self): for c in self.doxygenindex.findall("./compound[@kind='file']"): for f in c.findall("./member[@kind='function']"): name = f.find('name') if name.text == self.entry: return Member.get(f.attrib['refid'], c.attrib['refid']) return None def getCompound(self, c_refid): return self.doxygenindex.find("./compound[@refid='%s']" % c_refid) def getMember(self, c_refid, f_refid): #print os.path.join(self.directory, c_refid+os.extsep+'xml') tree = ET.parse(os.path.join(self.directory, c_refid+os.extsep+'xml')) root = tree.getroot() return root.find("./compounddef/sectiondef/memberdef[@id='%s']" % f_refid) def getAllFiles(self): return self.doxygenindex.findall("./compound[@kind='file']") def getMembers(self, kind=None): refs = [] for compound in self.getAllFiles(): name = compound.find('name') c_refid = compound.attrib['refid'] if kind is None: xpath = "./member" else: xpath = "./member[@kind='%s']" % kind for member in compound.findall(xpath): ref = Member.get(member.attrib['refid'], c_refid) refs.append(ref) return refs @staticmethod def text(element): if element is None: return "" else: return element.text class Process(object): """DocygenのXMLをCSVに変換する処理 """ def __init__(self): #self.encode = 'sjis' #sjisだと、'〜'、'‖'、'−'、'¢'、'£'、'¬'などの一部の記号類が文字化けする self.encode = 'cp932' def output(self, msg): print msg.encode(self.encode) def do(self): entry = Doxygen.getEntry() if entry is None: self.processWithoutEntry() else: self.processWithEntry(entry) def processWithEntry(self, entry, level=0): max_level = 10 if level==0: self.output(u"ファイル名,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,参照変数名,説明") if entry is None: return try: self.output((u"%s,"+u","*level+u"%s"+u","*(max_level-level)+u",%s") % (entry.filename, entry.name, entry.para)) except Exception, e: self.output((u"%s,"+u","*level+u"%s"+u","*(max_level-level)+u",%s") % (entry.filename, entry.name, u'<変換エラー:おそらくエンコードが違います>')) raise e #参照変数 for v in entry.vars: m = Member.get(v) self.output((u","*(1+max_level)+u"%s,%s") % (m.name, m.para)) #参照関数 for f in entry.funcs: self.processWithEntry(Member.get(f), level+1) def processWithoutEntry(self): self.output(u"ファイル名, 関数名, 参照変数名") for f in Doxygen.getMembers('function'): print "%s, %s, %s" % (f.filename, f.name, '') for v in f.vars: e = Member.get(v) print "%s, %s, %s" % ('', '', e.name) def main(): parser = argparse.ArgumentParser(description="""Doxygen-XML出力解析""") parser.add_argument('-d', '--directory',default="."+os.sep+'generated'+os.sep+'xml') parser.add_argument('-e', '--entry',default="hoge_sch") args = parser.parse_args() SetupDoxygen(args.directory, args.entry) Process().do() if __name__ == '__main__': main()
簡単なスクリプトでこんなことができるのは、Doxygenのおかげ。Doxygenエラい。各関数のソースコードも特定できるので、ちょっと手を加えると、フローチャートの自動生成もできるかもしれない。