Erlangソースファイルのコメントからドキュメント自動生成するEDoc

表題の通りです。例によってVoluntasさんの記事「Erlangに興味を持った人へ」を参考にさせて頂きつつ、オライリーの「Erlangプログラミング」も見つつ、まとめてみます。

これはこの度EDocで作ったHTML形式のドキュメンテーションです。

ソースへのEDoc形式でのコメント記述
RubyだとRDocとかありますね。あれのErlang版です。各関数の直前に以下のような形式でコメントを書いておけば、ドキュメントとなるHTMLを自動的に生成してくれます。

注意:これは古い書き方です。最新のErlangでは、この下に書いた-spec(〜)に対応していますので、そちらが良いと思います。
>|erlang|
%%
%% @doc get message record.
%% @spec get_message(int()) -> {ok, #message{}} | {error, not_found}
%%
get_message(MessageId) ->
spawn_call(get_message, [MessageId]).

<
%%
%% @doc get message record.
%%
-spec(get_message(int()) -> {ok, #message{}} | {error, not_found}).

get_message(MessageId) ->
    spawn_call(get_message, [MessageId]).

実際のソースコードはこちら。

上記の@docの後ろには直後の関数の説明を、@specの後ろには型情報を記します。specに関してはコメント中ではなく、モジュール定義などと同じような感じで、-spec(型情報).と書くことを上記のサイトのVoluntasさんに教えて頂きました。
一応コメント中の@specもドキュメント化されますが、-spec()を使う事でTypErによる実際の型チェックに役立てる事ができます。


Erlangは動的型言語なので、戻り値の型が一定していません。ソースが少ないうちはなんとかやれますが、そのうち段々と戻り値がどんなものだったかわからなくなってきたりするので、たとえHTMLのドキュメントが不要でも、こうして関数の直前に型情報と説明を添えておくとぐっと見やすくなると思います。

あと、ファイルの先頭に以下のような説明を書いておくと、それもHTMLドキュメントに反映されます。

%%
%% @author HIROE Shin <twitter: http://twitter.com/#!/hiroe_orz17>
%% @doc MessageBox is Twitter clone using Erlang (for plactice).
%% @copyright 2011 HIROE Shin
%%
-module(message_box).
...

ドキュメントの生成
これを書いておいて erlコマンドでノードを立ち上げた上で

1> edoc:files(["src/message_box.erl", "src/user_db.erl"])

のようにするとmessage_box.erlとuser_db.erlのドキュメントができます。
しかしVoluntusさんのページに在る通り、rebarを使えば1コマンドで全モジュールのドキュメントが作れますし、Makefileに記述しておけば更にラクチンです。

rebar.confに以下の行を追加します。

{edoc_opts, [{dialyzer_specs, all}, {report_missing_type, true},
             {report_type_mismatch, true}, {pretty_print, erl_pp},
             {preprocess, true}]}.

さらにMakefileに以下を追加。

edoc:
	@$(REBAR) doc

この時点での私のrebar.configとMakefileは以下のようになっています。

rebar.config

%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et

{erl_opts, [fail_on_warning,                                                    
            warn_export_all]}.
{xref_checks, [undefined_function_calls]}.
{cover_enabled, true}.
{clean_files, ["ebin/*", ".eunit/*"]}.

{edoc_opts, [{dialyzer_specs, all}, {report_missing_type, true},
             {report_type_mismatch, true}, {pretty_print, erl_pp},
             {preprocess, true}]}.

{deps_dir, ["deps"]}.
{deps, [{sqlite3, 
         "1.*",
         {git, "git://github.com/alexeyr/erlang-sqlite3.git", 
          {branch, "master"}}}]}.

Makefile

ERLC=erlc
ERL=erl
ERLCFLAGS=-o
SRCDIR=src
LOGDIR=./log
BEAMDIR=./ebin ./deps/sqlite3/ebin
DBDIR=./db
APP_NAME=message_box
HOST_NAME=127.0.0.1
REBAR=./rebar

all: clean compile xref

compile:
	@$(REBAR) get-deps compile

xref:
	@$(REBAR) xref

clean: 
	@ $(REBAR) clean

check:
	@rm -rf .eunit
	@mkdir -p .eunit
	@$(REBAR) skip_deps=true eunit 

edoc:
	@$(REBAR) doc

cleardata:
	@ rm -rf $(DBDIR)

boot:
	@ $(ERL) -pa $(BEAMDIR) -name $(APP_NAME)@$(HOST_NAME) -s $(APP_NAME) start

あとはシェルで

$ make edoc

とすれば(構文に問題が無ければ)自動的にdocディレクトリが作られて、HTML形式のドキュメントが生成されます。