10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

doxygenでIF仕様書を作る

Last updated at Posted at 2016-02-27

目的

昔書いた仕様書が更新されなくなって、仕様と実装が乖離する・・・なんてことが起きないようにしたい

手段

嫌でも目に入る場所に仕様を書いておくことで乖離した場合に気づくようにする。ヘッダファイルに使い方や仕様を書いて、doxygenでIF仕様を生成してみよう。

doxygenの使い方

出力フォーマットがいろいろあるらしい。

フォーマット 考察
html 見やすいけど、複数ファイルできてしまう。配布には向かない。
html→pdf pdf化することで、単ファイルに集約。ただ、diffをとりづらいので、前回からの変更点に注目したい場合ちょっとしんどい。
rtf 見づらい。
xml パースめんどくさい。
latex 試してない。
docbook privateメソッドも問答無用で出力される。

htmlかxmlかdocbookかで悩んだが、結局xml+xsltで実現することにしてみた。

手順

cygwin環境だとxsltprocをインストールしないといけない。
libxsltってパッケージを入れた気がする。

doxygenの設定ファイルを生成して、以下の場所だけ変更する

doxy_interface
GENERATE_XML           = YES
doxygen doxy_interface
xsltproc xml/combine.xslt xml/index.xml >xml/all.xml #xmlが複数ファイルに分割されているので統合する
sed -i -e "2i \<\?xml-stylesheet type=\"text\/xsl\" href=\"sample\.xsl\"\?\>" xml/all.xml #使用するstylesheetを追記

xsltを以下のような感じで作成する。

sample.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="html" encoding="UTF-8"/>

  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="doxygen">
    <HTML>
    <head>
    <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=9"/>
    <meta name="generator" content="Doxygen 1.8.11"/>
    <link href="tabs.css" rel="stylesheet" type="text/css"/>
    <link href="search/search.css" rel="stylesheet" type="text/css"/>
    <link href="doxygen.css" rel="stylesheet" type="text/css" />
    </head>
    <BODY>
    <xsl:apply-templates/>
    </BODY>
    </HTML>
  </xsl:template>

  <xsl:template match="compounddef[@kind='class'][@prot='public']">
    <div id="nav-path" class="navpath">
      <ul>
        <li class="navelem">
            <xsl:value-of select="compoundname"/>
        </li>
      </ul>
    </div>
    <xsl:apply-templates/>
  </xsl:template>
  <xsl:template match="text()">
    <p><xsl:value-of select="."/></p>
  </xsl:template>
  <xsl:template match="parameterlist[@kind='param']">
    <dl class="params">
        <dt>引数</dt>
        <dd>
        <table class="params">
            <xsl:apply-templates/>
        </table>
        </dd>
    </dl>
  </xsl:template>
  <xsl:template match="parameteritem">
    <tr>
        <td class="paramdir">[<xsl:value-of select="parameternamelist/parametername/@direction"/>]</td>
        <td class="paramname"><xsl:value-of select="parameternamelist/parametername"/></td>
        <td><xsl:value-of select="parameterdescription/para"/></td>
    </tr>
  </xsl:template>
  
  <xsl:template match="sectiondef[@kind='public-func']"><xsl:apply-templates/></xsl:template>
  <xsl:template match="sectiondef[@kind='public-attrib']"><xsl:apply-templates/></xsl:template>
  <xsl:template match="sectiondef[@kind='public-type']">
    <div class="memproto">
        <table class="memname">
        <tr>
            <td class="memname">
                <xsl:value-of select="memberdef/@kind"/>                 
                <xsl:value-of select="../compoundname"/>::<xsl:value-of select="memberdef/name"/>
            </td>
        </tr>
        </table>
    </div>
    <xsl:apply-templates/>
  </xsl:template>
  <xsl:template match="memberdef[@kind='function']|memberdef[@kind='variable']">
    <div class="memproto">
        <table class="memname">
        <tr>
        <td class="memname">
            <xsl:value-of select="definition"/>
            <xsl:value-of select="argsstring"/>
        </td>
        </tr>
    </table>
    </div>
    <div class="memdoc">
        <xsl:apply-templates/>
    </div>
  </xsl:template>
  <xsl:template match="memberdef[@kind='enum']">
    <div class="memdoc">
        <table class="fieldtable">
            <tr>
                <th colspan="2">列挙値</th>
            </tr>
            <xsl:apply-templates select="enumvalue"/>
        </table>
    </div>
  </xsl:template>
  <xsl:template match="enumvalue">
    <tr>
        <td class="fieldname"><xsl:value-of select="name"/></td>
        <td class="fielddoc"><p><xsl:value-of select="briefdescription/para"/> </p></td>
    </tr>
  </xsl:template>
  <xsl:template match="simplesect">
    <xsl:element name="dl">
        <xsl:attribute name="class">
            <xsl:value-of select="concat('section ', @kind)"/>
        </xsl:attribute>
        <dt><xsl:value-of select="@kind"/></dt>
        <dd><xsl:value-of select="para"/></dd>
    </xsl:element>
    
  </xsl:template>
  <xsl:template match="xrefsect">
    <xsl:element name="dl">
        <xsl:attribute name="class">
            <xsl:value-of select="substring-before(@id,'_')"/>
        </xsl:attribute>
        <dt><xsl:value-of select="xreftitle"/></dt>
        <dd><xsl:value-of select="xrefdescription"/></dd>
    </xsl:element>
  </xsl:template>
  <xsl:template match="briefdescription"><xsl:apply-templates/></xsl:template>
  <xsl:template match="detaileddescription"><xsl:apply-templates/></xsl:template>
  <xsl:template match="para"><xsl:apply-templates/></xsl:template>
  <!-- 無視する要素 -->
  <xsl:template match="*[@prot='private']"/>
  <xsl:template match="compoundname"/>
  <xsl:template match="listofallmembers"/>
  <xsl:template match="compounddef[@kind='file']"/>
  <xsl:template match="compounddef[@kind='page']"/>
  <xsl:template match="*[@kind='private-func']"/>
  <xsl:template match="*[@kind='private-attrib']"/>
  <xsl:template match="definition"/>
  <xsl:template match="argsstring"/>
  <xsl:template match="type"/>
  <xsl:template match="name"/>
  <xsl:template match="param"/>
  <xsl:template match="declname"/>
</xsl:stylesheet>

cssはhtml生成したときに吐き出されていたものをコピーして利用している。
後はブラウザでall.xmlを開けば表示されるはず。

diffをとりたければxmlでdiffとればよろし。

enum、remark、warning、todo、deprecatedに対応してみた。
structはC++においてIFにしてたら設計がおかしいはずなんで対応しないことにしよう。

実際使ってみたらドキュメント付けされていない場所もxmlに出力されていることに気が付いた。
不要な部分は削る必要がありそう。C#はnoobなのでぐちゃぐちゃだが、不要な部分を削るプログラムをC#で書いてみた。

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace doxyparser
{
    class Program
    {
        static bool isRemove(XmlNode node)
        {
            if (node.LocalName == "location" || node.LocalName == "listofallmembers")
            {
                return true;
            }
            else if (node.LocalName == "xrefsect")
            {
                return false;
            }
            else if (node.Attributes != null)
            {
                for (int i = 0; i < node.Attributes.Count; i++)
                {
                    XmlAttribute attr = node.Attributes[i];
                    Console.Write(attr.LocalName + ": ");
                    Console.WriteLine(attr.Value);
                    if (attr.Value.Contains("private"))
                    {
                        Console.WriteLine(" ->delete");
                        return true;
                    }
                    else if (attr.LocalName == "refid")
                    {
                        node.Attributes.Remove(attr);
                        --i;
                    }
                    else if (attr.LocalName == "id")
                    {
                        if (attr.Value == "todo" || attr.Value == "deprecated")
                        {
                            return true;
                        }
                        else
                        {
                            node.Attributes.Remove(attr);
                            --i;
                        }
                    }
                }
            }
            return false;
        }
        static bool search(XmlNode node)
        {
            bool result = isRemove(node);
            if (result)
            {
                node.ParentNode.RemoveChild(node);
            }
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode child = node.ChildNodes[i];
                if (search(child))
                {
                    --i;
                }
            }
            return result;
        }
        static void Main(string[] args)
        {
          XmlDocument xmlDocument = new XmlDocument();
          xmlDocument.Load(args[0]);
          elem_ = xmlDocument.DocumentElement;

          for (int i = 0; i < elem_.ChildNodes.Count; i++)
          {
              XmlNode child = elem_.ChildNodes[i];
             if( search(child))
             {
                --i;
             }
          }
          xmlDocument.Save("modified.xml");
            
        }
        static XmlElement elem_;
    }
}
10
12
0

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
10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?