ククログ

GTK-Docの使い方

GNOME Advent Calendar 2013 2日目の記事です。1日目はmyokoymさんの「Ruby/GTK3で作るカレンダーアプリ」でした。

C言語で書かれていて、Autotoolsでビルドシステムを構築しているプロジェクトがあるとします。このプロジェクトのドキュメントツールとしてGTK-Docを使う方法を説明します。

長いです。

GTK-Docについて

GTK-Docはその名の通りGTK+用のドキュメントツールです。GTK-Docは、公式サイトにも書いてある通り、一般的なドキュメントツールではなくGTK+に特化したドキュメントツールです。もう少し細かくいうと、GObjectに特化したドキュメントツールです。

「GObjectに特化している」というのは、次の機能を備えているからです。

  • GObjectのプロパティー専用のドキュメント機能
  • GObjectのシグナル1専用のドキュメント機能
  • GObjectのクラスの継承関係を表示する機能

これらは通常のドキュメントツールにはない機能です。

GTK-DocはDocBookをバックエンドに使ったドキュメントツールなので他のドキュメントツールと同じくらい表現力があります。そのため、GObjectを使ったプロジェクトなら、他のドキュメントツールではなくGTK-Docを使ったほうがユーザーにとって使いやすいドキュメントを提供できます。

ただし、GObjectを使っていないのであれば、別のドキュメントツールも検討したほうがよいでしょう。例えば、C++も使うなら確実に他のツールの方がよいです。GTK-DocはC++をサポートしていません。

なお、Cutterのリファレンスマニュアルmilter managerのリファレンスマニュアルはGTK-Docを使っています。これらのプロジェクトではGObjectを使っているからです2

GTK-Docの使い方

それでは、例を出しながらGTK-Docの使い方を説明します。ここで作るライブラリーはGitHubに置いてあります。ライセンスはCC0 (Public Domain)にするので、自由に再利用してください。

ここでは、「libsample」というライブラリーを作ります。このライブラリーの中にあるクラスはSampleGreeterだけです。

次の順に説明します。

  • GObjectを使ったプロジェクトを作成(GTK-Docはまだ使わない)
  • 作ったプロジェクトでGTK-Docを使う

GObjectを使ったプロジェクトを作成

それでは、GObjectを使ったプロジェクトを作成します。このセクションはそれほど大事なことではないので、興味のない人は飛ばしてもかまいません。

まずはこのライブラリーの実装を用意します。.cも.hも「sample/」以下に置きます。クラス定義について、GObjectの使い方を知っている人は「いつもの感じ」と思うかもしれませんが、知らない人は面倒だと思うことでしょう。

sample/greeter.h:

/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#ifndef SAMPLE_GREETER_H
#define SAMPLE_GREETER_H

#include <glib-object.h>

G_BEGIN_DECLS

#define SAMPLE_TYPE_GREETER \
  (sample_greeter_get_type())
#define SAMPLE_GREETER(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj), SAMPLE_TYPE_GREETER, SampleGreeter))
#define SAMPLE_GREETER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass), SAMPLE_TYPE_GREETER, SampleGreeterClass))
#define SAMPLE_IS_GREETER(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj), SAMPLE_TYPE_GREETER))
#define SAMPLE_IS_GREETER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE((klass), SAMPLE_TYPE_GREETER))
#define SAMPLE_GREETER_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS((obj), SAMPLE_TYPE_GREETER, SampleGreeterClass))

typedef struct _SampleGreeter         SampleGreeter;
typedef struct _SampleGreeterClass    SampleGreeterClass;

/**
 * SampleGreeter:
 *
 * A SampleGreeter greets.
 */
struct _SampleGreeter
{
    /*< private >*/
    GObject parent_instance;
};

struct _SampleGreeterClass
{
    GObjectClass parent_class;
};

GType          sample_greeter_get_type (void) G_GNUC_CONST;
SampleGreeter *sample_greeter_new      (void);
const gchar   *sample_greeter_greet    (SampleGreeter *greeter);

G_END_DECLS

#endif

sample/greeter.c:

/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "greeter.h"

/**
* SECTION: greeter
* @short_description: A greeter.
*
* The #SampleGreeter is a class to greet.
*/
G_DEFINE_TYPE(SampleGreeter, sample_greeter, G_TYPE_OBJECT)

static void
sample_greeter_init(SampleGreeter *object)
{
}

static void
sample_greeter_class_init(SampleGreeterClass *klass)
{
}

/**
 * sample_greeter_new:
 *
 * Allocates a new #SampleGreeter.
 *
 * Returns: a new #SampleGreeter.
 */
SampleGreeter *
sample_greeter_new(void)
{
    SampleGreeter *greeter;
    greeter = g_object_new(SAMPLE_TYPE_GREETER, NULL);
    return greeter;
}

/**
 * sample_greeter_greet:
 * @greeter: A #SampleGreeter.
 *
 * Greets.
 *
 * Returns: Message from a greeter.
 */
const gchar *
sample_greeter_greet(SampleGreeter *greeter)
{
    return "Hello!";
}

コード中の次のようなコメントの中に書かれているドキュメントがGTK-Doc用のドキュメントです。ドキュメントを.cに書くドキュメントツールと.hに書くドキュメントツールがありますが、GTK-Docは主に.cです。ざっくり言うと、実装のそばにドキュメントを書くスタイルということです。

/**
 ...
 */

次に、これらのソースをビルドする部分を作ります。次のようなディレクトリー構成にします。

.
|-- Makefile.am
|-- autogen.sh
|-- configure.ac
`-- sample
    |-- Makefile.am
    |-- greeter.c
    `-- greeter.h

まず、configure.acを作ります。コメントを入れておいたのでなんとなくわかるでしょう。

# Autoconf 2.65以上を必須にする
# これは、Automake 1.13がAutoconf 2.65以上が必須だから
AC_PREREQ(2.65)

# Autoconfを初期化
AC_INIT([gtk-doc-sample], [1.0.0], [kou@clear-code.com])
# 補助スクリプトをconfig/以下に置く
AC_CONFIG_AUX_DIR([config])
# M4マクロをm4/以下に置く
AC_CONFIG_MACRO_DIR([m4])

# ソースディレクトリーを検出するために、
# ソースディレクトリーに必ずあるファイルを指定
AC_CONFIG_SRCDIR([sample/greeter.h])
# AC_DEFINEで定義した変数を出力するヘッダーファイルを指定
AC_CONFIG_HEADERS([config.h])

# 1.13:    Automake 1.13以降必須
# foreign: NEWSなどのファイルを必須としない
AM_INIT_AUTOMAKE([1.13 foreign])
# ビルドする時にコマンドラインではなく
# どんな役割のコマンドを実行しているかを出力するだけにする
AM_SILENT_RULES([yes])

# Cコンパイラーを検出
AC_PROG_CC
# Libtoolsを初期化
LT_INIT

# GLib 2.32.4以降必須
# GLibの中のgobjectモジュールを使う
AM_PATH_GLIB_2_0([2.32.4], [], [], [gobject])

# .inから生成するファイルを指定
AC_CONFIG_FILES([
  Makefile
  sample/Makefile
])

# AC_CONFIG_FILESで指定したファイルなどを生成
AC_OUTPUT

続いてMakefile.amです。「aclocal」用の設定ぐらいのものです。

Makefile.am:

# configure.acでm4/以下にM4マクロを置くようにしたので
# m4/以下もパスに加える。
# また、ユーザーがACLOCAL_FLAGS環境変数でオプションを
# 渡せるようにしている。
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}

# 再帰的にsample/以下もmakeする。
SUBDIRS =					\
	sample

最後にsample/Makefile.amです。

sample/Makefile.am:

# top_builddirはconfig.hを探すため
# top_srcdirはプロジェクトのヘッダーファイルを探すため
AM_CPPFLAGS =					\
	 -I$(top_builddir)			\
	 -I$(top_srcdir)

# .cからGLibを使うためのコンパイラーオプションを指定
AM_CFLAGS =					\
	$(GLIB_CFLAGS)

# 作成する共有ライブラリー(libsample)を指定
lib_LTLIBRARIES =				\
	libsample.la

# libsampleで使うライブラリー(GLib)を指定
libsample_la_LIBADD =				\
	$(GLIB_LIBS)

# libsampleのソースファイルを指定
libsample_la_SOURCES =				\
	greeter.c				\
	greeter.h

autogen.shを用意すればビルドできます。

autogen.sh:

#!/bin/sh

set -u
set -e

mkdir -p m4
autoreconf --install

それではビルドしてみましょう。

% ./autogen.sh
% ./configure --prefix=/tmp/local
% make
% make install

これで/tmp/local/lib/libsample.soができます。

GTK-Docを使う

このプロジェクト用のドキュメントをGTK-Docで生成する仕組みを作ります。

ポイントは次の4つです。

  • autogen.shでgtkdocizeを使う
  • configure.acでGTK-Docを検出する
  • GTK-Doc用のファイルを作る

ファイルを作る前に、GTK-Docを使うようになった後のディレクトリー構造を示します。

.
|-- Makefile.am                 変更!
|-- autogen.sh                  変更!
|-- configure.ac                変更!
|-- doc                         新規!
|   |-- Makefile.am             新規!
|   `-- reference               新規!
|       |-- Makefile.am         新規!
|       |-- sample-docs.sgml    新規!
|       `-- version.xml.in      新規!
`-- sample
    |-- Makefile.am
    |-- greeter.c
    `-- greeter.h

doc/以下を新しく作り、それに伴いトップディレクトリーにある次のファイルを変更します。

  • Makefile.am
  • autogen.sh
  • configure.ac

それでは、autogen.shからいきましょう。

GTK-Docを使うための補助的なファイルを生成するgtkdocizeというコマンドがあります。これは、autoreconfを実行するために必要なファイルも生成するので、autoreconfの前に実行します。gtkdocizeもautoreconfのようにm4/ディレクトリー以下にファイルをコピーするのでmkdirよりは後にします。

autogen.sh:

diff --git a/autogen.sh b/autogen.sh
index 41bf970..2275409 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -4,4 +4,6 @@ set -u
 set -e

 mkdir -p m4
+
+gtkdocize --copy --docdir doc/reference
 autoreconf --install

configure.acでは、GTK-Docを検出するようにします。

configure.ac:

diff --git a/configure.ac b/configure.ac
index 3df3497..1164668 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,6 +31,9 @@ LT_INIT
 # GLibの中のgobjectモジュールを使う
 AM_PATH_GLIB_2_0([2.32.4], [], [], [gobject])

+# GTK-Docを検出
+GTK_DOC_CHECK([1.18-2])
+
 # .inから生成するファイルを指定
 AC_CONFIG_FILES([
   Makefile

これでGTK-Docを下準備はできました。それでは、GTK-Docの設定にいきます。

GTK-Docの設定はdoc/reference/以下で行います。まずは、トップディレクトリーでmakeしてもdoc/reference/まで再帰的に降りていってくれるようにしましょう。次のファイルを編集します。

  • Makefile.am
  • doc/Makefile.am
  • configure.ac

Makefile.am:

diff --git a/Makefile.am b/Makefile.am
index 870d996..73ee162 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,7 @@
 # 渡せるようにしている。
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}

-# 再帰的にsample/以下もmakeする。
+# 再帰的にsample/、doc/以下もmakeする。
 SUBDIRS =					\
-	sample
+	sample					\
+	doc

doc/Makefile.am:

# 再帰的にreference/以下もmakeする。
SUBDIRS =					\
	reference

configure.ac:

diff --git a/configure.ac b/configure.ac
index 1164668..abb481c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,6 +38,8 @@ GTK_DOC_CHECK([1.18-2])
 AC_CONFIG_FILES([
   Makefile
   sample/Makefile
+  doc/Makefile
+  doc/reference/Makefile
 ])

 # AC_CONFIG_FILESで指定したファイルなどを生成

これでdoc/reference/までの道ができました。doc/reference/Makefile.amがGTK-Docの設定をする場所です。意外とパラメーターが多いなぁと思うことでしょう。

doc/reference/Makefile.am

# モジュール名
DOC_MODULE = sample

# エントリーポイントになるファイル名
# 「${モジュール名}-docs.sgml」という名前付け規則が広く使われている
# のでそのまま従うのがよい。
DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.sgml

# ソースコードがあるディレクトリー
DOC_SOURCE_DIR =				\
	$(top_srcdir)/sample

# 非推奨になったAPIを検出するためのオプションを指定
# 今回は使っていないが開発を続けていくライブラリーなら
# きっと必要になる
SCAN_OPTIONS =						\
	--deprecated-guards="SAMPLE_DISABLE_DEPRECATED"

# ライブラリー内で関数名の最初に使う名前空間用プレフィックスを指定
MKDB_OPTIONS =					\
	--name-space=sample

# ヘッダーファイルを指定するグロブパターン
HFILE_GLOB =					\
	$(top_srcdir)/sample/*.h

# ソースファイルを指定するグロブパターン
CFILE_GLOB =					\
	$(top_srcdir)/sample/*.c

# ソースコードをビルドするために必要なCプリプロセッサー用のオプション
# $(top_srcdir)/sample/Makefile.amで指定したものと同じものを指定すればよい。
# ただし、ビルドするディレクトリーが違うのでそこは考慮する必要がある。
AM_CPPFLAGS =					\
	-I$(top_builddir)			\
	-I$(top_srcdir)

# ソースコードをビルドするために必要なCコンパイラー用のオプション
# $(top_srcdir)/sample/Makefile.amで指定したものと同じものを指定すればよい。
# ただし、ビルドするディレクトリーが違うのでそこは考慮する必要がある。
AM_CFLAGS =					\
	$(GLIB_CFLAGS)

# ライブラリーをリンクするためのオプション
GTKDOC_LIBS =					\
	$(top_builddir)/sample/libsample.la	\
	$(GLIB_LIBS)

# GTK-Docが用意したMakefileを読み込む
include $(srcdir)/gtk-doc.make

# make cleanでsample-sections.txtとsample.typesを削除する。
# (詳細は本文で。)
CLEANFILES +=					\
	$(DOC_MODULE)-sections.txt		\
	$(DOC_MODULE).types

どのパラメーターも悩まずに設定できるはずです。新しく決めることはなく、事実を埋めていくだけです。

次の2点を補足します。

  • ドキュメントを生成するのにどうしてビルドオプションを指定するのか
  • CLEANFILESに指定しているものはなにか

まず、ビルドオプションについてです。GTK-Docはドキュメントを生成するために、ドキュメント対象のライブラリーを組み込んだ実行ファイルを作成します。その実行ファイルを実行してライブラリーのメタデータを動的に取得します。メタデータとはクラス階層やどんなプロパティーを持っているかなどです。GTK-DocがGObjectに特化したドキュメントツールなのでここまでやっているのです。

次に、CLEANFILESに指定している次のファイルについてです。

  • sample-sections.txt
  • sample.types

GLibやGTK+での使い方をみると、どちらもリポジトリーに入れて管理することを期待しているファイルのようにみえます。しかし、ここではリポジトリーに入れずに自動で生成するようにします。理由は、どちらも手動で管理することが面倒だからです。

sections.txtはドキュメント対象とする関数や構造体を列挙するファイルで、.typesは自分で作ったオブジェクト3を列挙するファイルです。開発が進むにつれ、ここに列挙しなければいけないことが増えますが、それをいちいちやっていられません。これらのファイルはなければ前述したメタデータなどから自動的に作成されます。多少ゴミもまじりますが、手動で管理する手間を考えれば割にあいます。そのため、CLEANFILESに入れています。

最後にドキュメントのエントリーポイントとなるXMLファイルとそこで使うファイルを作成して仕組みは完成です。ポイントは<xi:include>のところです。他は一度作ったら後はほとんど使いまわせます。

doc/reference/sample-docs.sgml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
<!ENTITY version SYSTEM "version.xml">
]>
<book xmlns:xi="http://www.w3.org/2003/XInclude" id="index" lang="en">
  <bookinfo>
    <title>Sample Reference Manual</title>
    <releaseinfo>for Sample &version;</releaseinfo>
  </bookinfo>

  <part id="reference">
    <title>Reference</title>
    <chapter id="libsample">
      <title>libsample</title>
      <xi:include href="xml/greeter.xml"/>
    </chapter>
  </part>

  <index id="index-all">
    <title>Index of all symbols</title>
  </index>
  <index id="index-deprecated" role="deprecated">
    <title>Index of deprecated symbols</title>
  </index>
  <index id="index-1-0-0" role="1.0.0">
    <title>Index of new symbols in 1.0.0</title>
  </index>
</book>

新しいクラスができたら対応する<xi:include>を増やしていきます。そうするとドキュメントに組み込まれます。

version.xmlを参照しているので作ります。ここにはバージョン番号だけが入るので、.inからconfigureで作ります。

sample/doc/reference/version.xml.in:

@VERSION@

.inを作ったのでconfigure.acも変更します。

diff --git a/configure.ac b/configure.ac
index abb481c..9a925d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,6 +40,7 @@ AC_CONFIG_FILES([
   sample/Makefile
   doc/Makefile
   doc/reference/Makefile
+  doc/reference/version.xml
 ])

 # AC_CONFIG_FILESで指定したファイルなどを生成

これで仕組みができました。GTK-Docを使ったドキュメントを作ってみましょう。configureで--enable-gtk-docを指定することがポイントです。

% ./autogen.sh
% ./configure --prefix=/tmp/local --enable-gtk-doc
% make
% make install

これで/tmp/local/share/gtk-doc/html/sample/index.htmlにHTMLができているはずです。

まとめ

実例を用いてプロジェクトのビルドシステムにGTK-Docを組み込む方法を説明しました。GTK+などGObjectベースのライブラリーを作っている場合はドキュメントはGTK-Docで提供することをオススメします。GObjectに特化した情報のドキュメントの作成が容易ですし、わりと小ぎれいなドキュメントができます。ぜひ、GTK-Docを使ってみてください。

ここで作った例はGitHubに置いてあります。ライセンスはCC0 (Public Domain)なので自由に再利用してください。

  1. kill(1)で送るシグナルとは違う。

  2. CutterではGTK+を少し使っていますが、milter managerではGTK+を使っていません。GTK+がベースとしているライブラリーであるGLibやGObjectの機能だけを使っています。

  3. もう少し言うとGType