LoginSignup
6
6

More than 1 year has passed since last update.

DoxygenでXMLファイル出力(依存関係等の解析結果含む)

Last updated at Posted at 2017-08-19

導入

ソースコードからのドキュメント生成ツールDoxygen。多くのプログラミング言語と出力形式に対応しているため、使ったことのある方も多いんじゃないでしょうか。

この記事では、ドキュメントではなくコードの依存関係等をXML出力するためのDoxygen設定を紹介します。
出力のXSLT変換例も少し紹介します。

XML出力は、例えば以下のような独自のコード分析/解析やラッパーの自動生成等に活用できるかもしれません。

  • 改修時の変更影響箇所の検討
  • 関数呼び出し関係の見える化、コールグラフの作成
  • グローバル変数への参照箇所の洗い出し
  • 未使用関数や不要なincludeの洗い出し
  • メトリクス測定
  • 他言語バインディングの自動生成
  • テストコードひな形の自動生成
  • モックの自動生成

Doxygenの設定

Doxywizard(DoxygenのGUIフロントエンド)だと、Expertタブから以下のような感じです。

  • Buildで、出力に含めたい要素をチェック
  • Source Browserで、REFERENCED_BY_RELATION/REFERENCES_RELATIONにチェック
  • XMLで、GENERATE_XMLをチェック。(XML_PROGRAMLISTINGチェックは外してもよい)

Doxywizard-Build.png

Doxywizard-SourceBrowser.png

Doxywizard-XML.png

結果

前記の設定でdoxygenを実行すると、xmlディレクトリ以下にXMLファイルが出力されます。
Doxygen-xml.png

XML内容について

自然言語での公式ドキュメントは無さそうです。
スキーマ定義はxml出力先に含まれます(index.xsd, xml.xsd)。

定義や参照関係は出ますが、関数内のブロック構造は出ないようです。

xml抜粋
xml.xml
      <memberdef kind="define" id="aitc_8c_1a2512e9f94d9ab9e4c48b78400f9f2d44" prot="public" static="no">
        <name>FIPNDH</name>
        <param><defname>base</defname></param>
        <initializer>((base)+0x60)</initializer>
        <briefdescription>
        </briefdescription>
        <detaileddescription>
        </detaileddescription>
        <inbodydescription>
        </inbodydescription>
        <location file="aitc.c" line="79" column="9" bodyfile="aitc.c" bodystart="79" bodyend="-1" />
      </memberdef>
...
      <memberdef kind="function" id="aitc_8c_1a8353f48d9b6c62f1eaf43a0c0ac6ac9d" prot="public" static="yes" const="no" explicit="no" inline="no" virt="non-virtual">
        <type>void</type>
        <definition>static void int_source_change</definition>
        <argsstring>(SigNode *node, int value, void *clientData)</argsstring>
        <name>int_source_change</name>
        <param>
          <type><ref refid="struct_sig_node" kindref="compound">SigNode</ref> *</type>
          <declname>node</declname>
        </param>
        <param>
          <type>int</type>
          <declname>value</declname>
        </param>
        <param>
          <type>void *</type>
          <declname>clientData</declname>
        </param>
        <briefdescription>
        </briefdescription>
        <detaileddescription>
        </detaileddescription>
        <inbodydescription>
        </inbodydescription>
        <location file="aitc.c" line="172" column="1" bodyfile="aitc.c" bodystart="172" bodyend="183" />
        <references refid="struct_irq_trace_info_1ae85401bf0069a66b078436a712c5e550" compoundref="aitc_8c" startline="111">IrqTraceInfo::aitc</references>
        <references refid="struct_aitc_1a920cf4d7d13a06eb8944a7af53eeaa5f" compoundref="aitc_8c" startline="100">Aitc::intsrc</references>
        <references refid="struct_irq_trace_info_1af3d6abfd2d5ee95a490b9bb84230ed2a" compoundref="aitc_8c" startline="110">IrqTraceInfo::nr</references>
        <references refid="signode_8h_1a4893a579f5b29db39ff56a83d3d0622a" compoundref="signode_8h" startline="24">SIG_LOW</references>
        <references refid="aitc_8c_1a102505e8050a3b3e5066d02855ca1238" compoundref="aitc_8c" startline="115" endline="163">update_interrupts</references>
        <referencedby refid="aitc_8c_1a1190b3c9b1a45bbd79159dae993001f1" compoundref="aitc_8c" startline="587" endline="623">Aitc_New</referencedby>
      </memberdef>
xsd
index.xsd
<?xml version='1.0' encoding='utf-8' ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>

  <xsd:element name="doxygenindex" type="DoxygenType"/>

  <xsd:complexType name="DoxygenType">
    <xsd:sequence>
      <xsd:element name="compound" type="CompoundType" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
    <xsd:attribute name="version" type="xsd:string" use="required"/>
    <xsd:attribute ref="xml:lang" use="required"/>
  </xsd:complexType>

  <xsd:complexType name="CompoundType">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="member" type="MemberType" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
    <xsd:attribute name="refid" type="xsd:string" use="required"/>
    <xsd:attribute name="kind" type="CompoundKind" use="required"/>
  </xsd:complexType>

  <xsd:complexType name="MemberType">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
    </xsd:sequence>
    <xsd:attribute name="refid" type="xsd:string" use="required"/>
    <xsd:attribute name="kind" type="MemberKind" use="required"/>
  </xsd:complexType>
  
  <xsd:simpleType name="CompoundKind">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="class"/>
      <xsd:enumeration value="struct"/>
      <xsd:enumeration value="union"/>
      <xsd:enumeration value="interface"/>
      <xsd:enumeration value="protocol"/>
      <xsd:enumeration value="category"/>
      <xsd:enumeration value="exception"/>
      <xsd:enumeration value="file"/>
      <xsd:enumeration value="namespace"/>
      <xsd:enumeration value="group"/>
      <xsd:enumeration value="page"/>
      <xsd:enumeration value="example"/>
      <xsd:enumeration value="dir"/>
      <xsd:enumeration value="type"/>
      <xsd:enumeration value="concept"/>
    </xsd:restriction>
  </xsd:simpleType>

  <xsd:simpleType name="MemberKind">
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="define"/>
      <xsd:enumeration value="property"/>
      <xsd:enumeration value="event"/>
      <xsd:enumeration value="variable"/>
      <xsd:enumeration value="typedef"/>
      <xsd:enumeration value="enum"/>
      <xsd:enumeration value="enumvalue"/>
      <xsd:enumeration value="function"/>
      <xsd:enumeration value="signal"/>
      <xsd:enumeration value="prototype"/>
      <xsd:enumeration value="friend"/>
      <xsd:enumeration value="dcop"/>
      <xsd:enumeration value="slot"/>
    </xsd:restriction>
  </xsd:simpleType>

</xsd:schema>
xml.xsd
<?xml version='1.0'?>
<xsd:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xml:lang="en">

  <xsd:attribute name="lang" type="xsd:language">
  </xsd:attribute>

  <xsd:attribute name="space" default="preserve">
    <xsd:simpleType>
      <xsd:restriction base="xsd:NCName">
        <xsd:enumeration value="default"/>
        <xsd:enumeration value="preserve"/>
      </xsd:restriction>
    </xsd:simpleType>
  </xsd:attribute>

  <xsd:attributeGroup name="specialAttrs">
    <xsd:attribute ref="xml:lang"/>
    <xsd:attribute ref="xml:space"/>
  </xsd:attributeGroup>

</xsd:schema>

おまけ

XMLファイル結合用PowerShellバッチ

XMLファイルはソースファイルごとに生成されるため、そのままだと扱いづらいです。
1ファイルにまとめる用のXSLT/XML(combine.xslt/index.xml)も出力されるため、これを使ってまとめると扱いやすくなります。
Linuxだとxlstprocを使うのが手軽です。xsltproc -o combined.xml xml/combine.xslt xml/index.xml

WindowsだとXSLT処理させるのが面倒なため、以下のようなPowerShellバッチを用意しておくとお手軽便利です。(.NETのXSLT処理系を使うため、別途XSLT処理系の準備が不要となります)

XMLファイル結合用PowerShellバッチ

Doxygen結果のxml格納フォルダを引数で渡すと、xml格納フォルダの隣にxml.xmlファイルを出力します。

doxygenxml_combine.ps1
 # DoxygenのXMLを結合する
 # 引数: Doxygen結果のxml格納フォルダ
 [CmdletBinding()]
 param(
     [Parameter(Mandatory=$True)]
     [string]$xml_dir
 )
 
 # お約束
 Set-StrictMode -Version Latest
 $ErrorActionPreference = "Stop"
 $WarningPreference = "Continue"
 $VerbosePreference = "Continue"
 $DebugPreference = "Continue"
 trap {
     $Error | foreach {
         Write-Debug $_.Exception.Message
         Write-Debug $_.InvocationInfo.PositionMessage
     }
     Exit -1
 }
 
 
 #### 処理
 
 # パス準備
 $xml_dir = (Convert-Path $xml_dir)
 [string]$xslt_path = (Join-Path $xml_dir combine.xslt)
 [string]$index_path = (Join-Path $xml_dir index.xml)
 [string]$combined_path = ($xml_dir + ".xml")
 
 # 存在チェック
 if (-not (Test-Path $xslt_path)) {
     Write-Host ("Error File Not Found: " + $xslt_path) -ForegroundColor red
     Exit -1
 }
 if (-not (Test-Path $index_path)) {
     Write-Host ("Error File Not Found: " + $index_path) -ForegroundColor red
     Exit -1
 }
 
 # 結合
 # XSLT読み込み
 $xslt = New-Object System.Xml.Xsl.XslCompiledTransform
 $xslt_setting = New-Object System.Xml.Xsl.XsltSettings
 $xslt_setting.EnableDocumentFunction = $true
 $xslt_setting.EnableScript = $true
 $xslt.Load($xslt_path, $xslt_setting, $null)
 # 出力準備
 $writer_setting = New-Object System.Xml.XmlWriterSettings
 $writer_setting.Indent = $true
 $writer = [System.Xml.XmlWriter]::Create($combined_path, $writer_setting)
 # 変換
 $xslt.Transform($index_path, $writer)
 # 終了
 $writer.Close()
 Write-Host ("Complete Combine: " + $combined_path) -ForegroundColor green

XSLT

XMLファイルはXSLTで変換/整形できます。
いくつかXSLT例を紹介します。

要素一覧CSV
member_dump.xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" encoding="Shift_JIS" />

  <xsl:param name="delim" select="'&#x09;'" />
  <xsl:param name="break" select="'&#x0D;&#x0A;'" />
  
  <xsl:template match="/doxygen">
    <xsl:text>#id</xsl:text><xsl:value-of select="$delim" />
    <xsl:text>kind</xsl:text><xsl:value-of select="$delim" />
    <xsl:text>static</xsl:text><xsl:value-of select="$delim" />
    <xsl:text>name</xsl:text><xsl:value-of select="$delim" />
    <xsl:text>file</xsl:text><xsl:value-of select="$delim" />
    <xsl:text>line</xsl:text><xsl:value-of select="$break" />
    <xsl:for-each select="//memberdef">
      <xsl:value-of select="./@id" /><xsl:value-of select="$delim" />
      <xsl:value-of select="./@kind" /><xsl:value-of select="$delim" />
      <xsl:value-of select="./@static" /><xsl:value-of select="$delim" />
      <xsl:value-of select="./name" /><xsl:value-of select="$delim" />
      <xsl:value-of select="./location/@file" /><xsl:value-of select="$delim" />
      <xsl:value-of select="./location/@line" /><xsl:value-of select="$break" />
    </xsl:for-each>
  </xsl:template>
  
</xsl:stylesheet>
関数呼び出し一覧CSV
ref_dump.xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" encoding="Shift_JIS" />

  <xsl:param name="delim" select="'&#x09;'" />
  <xsl:param name="break" select="'&#x0D;&#x0A;'" />
  
  <xsl:template match="/doxygen">
    <xsl:text>#src_id</xsl:text><xsl:value-of select="$delim" />
    <xsl:text>dst_id</xsl:text><xsl:value-of select="$break" />
    <xsl:for-each select="//memberdef[@kind=&quot;function&quot;]/references">
      <xsl:value-of select="../@id" /><xsl:value-of select="$delim" />
      <xsl:value-of select="./@refid" /><xsl:value-of select="$break" />
    </xsl:for-each>
    <xsl:for-each select="//memberdef[@kind=&quot;function&quot;]/referencedby">
      <xsl:value-of select="./@refid" /><xsl:value-of select="$delim" />
      <xsl:value-of select="../@id" /><xsl:value-of select="$break" />
    </xsl:for-each>
  </xsl:template>
  
</xsl:stylesheet>

XSLT処理用Windowsバッチ

XSLT処理用PowerShellバッチ

結合済みのXMLファイルを引数で渡すと、バッチと同名のXSLT(XSLT処理.ps1ならXSLT処理.xslt)を適用して、結果をout.csvに出力します。

XSLT処理.ps1
# 引数: Doxygenの結合XML
# 出力: out.csv
[CmdletBinding()]
param(
    [Parameter(Mandatory=$True)]
    [string]$xml_path
)

# お約束
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$WarningPreference = "Continue"
$VerbosePreference = "Continue"
$DebugPreference = "Continue"
trap {
    $Error | foreach {
        Write-Debug $_.Exception.Message
        Write-Debug $_.InvocationInfo.PositionMessage
    }
    Exit -1
}


#### 処理

# パス準備
$xml_path = (Convert-Path $xml_path)
[string]$xslt_path = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, ".xslt")
[string]$out_path = [System.IO.Path]::ChangeExtension($xml_path, "out.tsv")

# 存在チェック
if (-not (Test-Path $xslt_path)) {
    Write-Host ("Error File Not Found: " + $xslt_path) -ForegroundColor red
    Exit -1
}

# 結合
# XSLT読み込み
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform
$xslt_setting = New-Object System.Xml.Xsl.XsltSettings
$xslt_setting.EnableDocumentFunction = $true
$xslt_setting.EnableScript = $true
$xslt.Load($xslt_path, $xslt_setting, $null)
# 変換
$xslt.Transform($xml_path, $out_path)
# 終了
Write-Host ("Complete: " + $out_path) -ForegroundColor green

もっと知りたい方向け資料

以下のような資料を見つけました。
私の興味と異なるためあまり読み込んでいませんが、皆さんの参考にはなるかもしれません。

doxygen/addon/doxmlparser at master · doxygen/doxygen
doxygenのXML出力の解析サンプル(python)

XML/XSLT examples : Doxygen: Helper tools and scripts
XML/XSLTのサンプル紹介

C言語の構造体を構造分析したい〜静的解析としての doxygen
XMLから、C言語構造体の構造を JSON で吐き出すスクリプト(ruby)

DoxygenのXML出力 - unyaunyaの日記
XMLから、CSVで関数・変数の参照を出力(python)

なお、もっとしっかり解析したい方には、フリーだとclang/LLVM系ツールが おすすめです。
https://clang.llvm.org/docs/Tooling.html

6
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6