2007-02-15

複数のプロジェクトのコードからドキュメントを自動生成する

昨日やっていたことを振り返りながら今日の日付で日記を書きます。

やってることは管理しているコード全体について、プロジェクトをまたいでドキュメントの生成を自動化する、というものです。

ドキュメント生成ツールの実行を自動化してこその自動生成

「ドキュメント 自動生成」などで検索すると phpdoc, rdoc などのツールについての紹介ページがたくさん引っかかります。しかしこれらのツールはコードからドキュメントとして使えるようにマークされた部分を引っこ抜いてきて HTML に整形する作業を自動化しているだけで、コード中にちゃんとドキュメントを書いておかないといけないという意味では別に自動生成でもなんでもないわけです1。ただ、コードとドキュメントが物理的に分離していないため、それぞれのバージョンの食い違いが発生しにくいというとても大きなメリットがあることも事実で、だからこそこれらのツールを使うわけです2。が、

どうせならツールの実行も全部自動化してこその自動生成じゃないのか?

と思いますわな。ということでやってる人は当然のようにやっているので表に出てきにくいんじゃないかなネタ第?弾。

前提条件

今回は以下の条件でドキュメントを生成します。しかし条件に合わなくても工夫すればなんとかなるかもしれません。

  • WebDAV経由の Subversion リポジトリがあること
  • Apache の設定をある程度自由に読み書きできること
  • phpdoc, pdoc, jsdoc, rdoc など利用するドキュメント生成ツールのインストールや設定、起動を行えること
  • cron, タスクスケジューラなどの設定方法が分かっていること

あと Apache 用語とか CVS 用語とか Subversion 用語とかが説明抜きにいきなり出てきますのでめげないでください。

目指す形

Web サーバ上のある位置に定期的に自動でドキュメントが生成される。

ドキュメントは Web ブラウザで読みます。

一応今回は Perl, Ruby, PHP, JavaScript に対応したツールを扱いますが、それ以外のツール(言語)でも同様に応用可能だと思います。

shスクリプトの例(Pdoc編)

ではいきなりですがスクリプトを晒します。これは pdoc の例です。

#! /bin/sh
umask 002

BASE=/dat/www/devel/apidoc/pdoc
SRC=$BASE/svn_co

SVN_LOG=$BASE/log/svn.log
cat /dev/null > $SVN_LOG
PDOC_LOG=$BASE/log/pdoc.log
cat /dev/null > $PDOC_LOG

cd $SRC
for proj in *
do
  if [ -d $SRC/$proj ]; then
      echo "= Project $proj ="
      echo "= Project $proj =" >> $SVN_LOG 2>&1
      cd $SRC/$proj
      svn update >> $SVN_LOG

      echo "= Project $proj =" >> $PDOC_LOG 2>&1
      if [ ! -d $BASE/$proj ]; then
          mkdir $BASE/$proj
      fi
      $BASE/perlmod2www.pl -skip .svn -source $SRC/$proj -target $BASE/$proj >> $PDOC_LOG 2>&1
  fi
done

umask はあってもなくてもいいです。単に手元の作業の都合上の問題です。

何をやっているか?

  1. まず作業対象のディレクトリを指定します
  2. そこを基準に working copy を作成するディレクトリ
  3. およびログファイルを作成するディレクトリを決定します
  4. ログファイルをクリアします
  5. working copy を展開しているディレクトリに入って
  6. そこですべての中身をリストアップ
  7. ディレクトリだったらそれがプロジェクトだと判断してそこに入って svn update
  8. ドキュメント生成先がなかったらそれを作成して、
  9. HTML を生成する perlmod2www.pl に必要な引き数を与えて実行
  10. 7 に戻ってくり返す

ポイントは

  • BASE の設定以外はすべて相対で設定してあるので、一カ所書き換えるだけでどこにでも置くことができる
  • ディレクトリの中身を丸ごと対象にしているので、subversion で checkout するプロジェクト(CVS風に言えばモジュール)が増えたら生成されるドキュメントも自動で増える
    • 処理対象はディレクトリのみなので、working copy を置くためのディレクトリにもメモ用のファイルなどは置いておける
    • ドキュメント生成先のディレクトリが存在しない場合は勝手に作る

辺りでしょうか。できるだけメンテしなくても動くようにしたつもりですが、まだ甘いかもしれません。

ディレクトリ構成

ということで今回のサンプルのディレクトリ構成はこんな感じです。

DOCROOT
 |-- A_proj/           生成された A_proj のドキュメント
 |-- log/              ログ(単なる動作確認用)
 |-- makepdoc*         呼び出すshスクリプト
 |-- perlmod2www.pl*   pdoc スクリプト本体
 |-- svn_co/           コードのチェックアウト先
 |   |-- A_proj/       A_proj の working copy
 |   `-- W_proj/       W_proj の working copy
 `-- W_proj/           生成された W_proj のドキュメント

※ 末尾の / はそれがディレクトリであることを、* は実行可能であることを表しています。

絶対に必要な作業として、svn_co/ ディレクトリの中に、ドキュメントを生成したいプロジェクトを svn co で作成しておくというものがあります。これが今回のキモというかほぼすべてです。

すぐに気づいたと思いますが、内部向けしか考えていないので割と大胆なことになっています。今回は

Web サーバで公開されている領域に working copy もログも生成されたドキュメントも全部置いちゃう

形にしてしまいました。これは、

  • ログを落とす場所
  • working copy を作成する場所

  • ドキュメントを置く場所

と別に確保するのが面倒だったからに他なりません。考えるのが面倒でなければこのレイアウトにこだわる必要はまったくありません。

また、上の例を見るとプロジェクト名を proj_XXX という形に整えたくなるかもしれませんが、これはあくまで例示用の名前であって、proj が prefix になるか suffix になるかなんてことはまったく問題にならないのでお間違えなきよう。log とかチェックアウト先の場所と名前がかぶりさえしなければなんだっていいですし、たぶんその方がブラウザで見たときには自然でしょう。だってこの場合、

proj_XXX なんて名前でなくたって何らかのプロジェクトについてのドキュメントであることは自明

なんですから。

permission に注意

内部向けなのでなんだっていいっちゃなんだっていいんですが、

  • ドキュメント生成のためのスクリプト
  • (今回の例の場合は perlmod2www.pl)

に、Web サーバプロセスのユーザーでの実行権限があるとブラウザからドキュメントの自動生成ができます。便利なように見えなくもないですが、今回自分はそういうことが起きないように権限を分離しておきました。でないと誰かが閲覧中にいきなり裏でドキュメントの生成が走っちゃって、思いのほかサーバが重くなったりするかもしれないので。

ブラウザからいい感じに見えるようにWebサーバを設定する

すいません、問答無用で Apache でいきます。まず、ブラウザからドキュメントのリストが見えるように

+Options Indexes

します。

しかしこのままではブラウザで見たときにかっこ悪いなと思いませんか? そこで

IndexIgnore log svn_co makepdoc perlmod2www.pl

と言った具合に見えてほしくないものを除外していきます。

ま、だいたいこんなもんでしょう。直アクセスすれば当然丸見えですが、細かいことは気にしちゃいけません。どうしても気になるならアクセス権限を制限すればいいんですけど、そういう人は素直に、見せたくないものは document root の外に置くようにした方がいいです。

他の言語の場合

RDoc編

RDoc の場合は以下を削除してください。

      if [ ! -d $BASE/$proj ]; then
          mkdir $BASE/$proj
      fi

RDoc はドキュメント生成先に指定されたディレクトリが RDoc 自身の作成したディレクトリかどうかを判断できますので、勝手にディレクトリを作ってもその中にドキュメントは吐き出してくれません。

実際に rdoc コマンドを呼ぶところは

     /usr/bin/rdoc -c eucj -o $BASE/$proj $SRC/$proj >> $RDOC_LOG 2>&1

こんな感じでしょうか。残念ながら charset を指定してやらないとできあがったドキュメントが文字化けしてしまうのですが、これは

  1. 判別用のファイルをリポジトリ内で決めておいて、それには明示的に svn:mime-type で charset を propset しておく(何語だ)
  2. rdoc を呼ぶ前に svn proplist -v からエンコーディングを取得して
  3. rdoc のオプションに与える

という処理を入れてやるのがよさげな気がします。1プロジェクト内でエンコーディングがぐちゃぐちゃに混ざってるなんてことはないでしょうから。

自分とこではまだそこまでやってないのでとりあえず決め打ちですけど。

rdoc のパスは環境に応じて適当に直してください。

PhpDocumentor編

phpdoc コマンドを呼ぶ部分が以下のような感じになるはずです。(バージョン 1.3 以降じゃないとこの書き方はできないかも。)

     /usr/bin/phpdoc -pp on -o HTML:frames:DOM/default.i18n -ti $proj -d $SRC/$proj -t $BASE/$proj >> $PHPDOC_LOG 2>&1

これは自分が手を加えた(文字化けしないように iso-8859-1 を埋め込んである部分を片っ端から削った)テンプレートが default.i18n というディレクトリに置いてあるからこういう指定になっていますが、ここら辺は環境によって設定をいじってください。

phpdoc コマンドのオプションについては phpdoc -h とかマニュアルとか参照してください。phpdoc のパスは環境に応じて適当に直してください。

JSDoc編

これまでと同様、jsdoc コマンドの呼び出し部分を以下のような感じにします。

     $BASE/JSDoc-1.10.1/jsdoc.pl -r -d $BASE/$proj $SRC/$proj >> $JSDOC_LOG 2>&1

これは BASE の中に JSDoc 一式を丸ごと放り込むという大胆な設定の場合なので、どこに置いたかに応じて書き換えて使ってください。

なぜ WebDAV経由の Subversion で update なのか

subversion を前提にしちゃってますが、これは以下の理由によります。

  1. 何らかのバージョン管理システムを使っている方がドキュメント生成用に最新のコードを準備する処理が楽ちんだから
  2. WebDAV 経由だと Apache の LimitExcept ディレクティブを使うことで、読み出しに関しては認証なしにする設定を簡単にできるから
  3. svn export の場合、.svn ディレクトリが作成されず、その working copy はどこのリポジトリと結びついているのかを sh スクリプト側で管理しないといけなくなるから

2 については別に認証なしにする必要はないのですが、認証ありの場合

  • 認証情報をキャッシュしておかないと自動化できない
  • 誰の認証情報をキャッシュさせる?

という問題が発生します。

誰でもええやんと思うかもしれないけど、仮に誰かのアカウントにした場合、この自動生成のシステムが、いなくなった人のアカウントで動き続けようとするケースが考えられます。今一生懸命やっている人がいつまでもいるとは限りませんので、できるだけアカウント情報は残したくありません。Subversion 管理用のアカウントにするという手もありますが、簡単に無認証にできるならそっちの方がいいかなと思いました。

また、WebDAV 経由にしておけばリポジトリの場所が移動しても rewrite や redirect などのサーバ側の設定だけで、クライアント側であるこのスクリプトを置く機械の方では何もしなくていいかもしれません。

3 については書いたまんまです。.svn ディレクトリが作成された方が楽なんです。どのディレクトリがどのリポジトリにアクセスしに行くのかがその中に保存されますので、あとは無造作に svn update するだけで最新の状態になりますし、プロジェクトが増えても svn co でディレクトリを増やしていくだけで sh スクリプトの方には修正を加える必要がなくなります。

Subversion などのリポジトリがない場合、例えばどこかから Windows の共有などを利用して丸ごとコード全体をコピーしてくる場合は、コピー元をスクリプト側、あるいは他のツールで管理する必要が出てきます。Subversion を利用する場合でも sh スクリプト以外のツールで管理していると言えば管理しているのですが、この情報はディレクトリを移動してもそのまま生きますし、何らかの設定ファイルなどを書く必要がないというところがポイントです。

逆に言えばこうした管理が苦でないなら、Subversion を利用していなくても自動化は可能です。(自分はやりたくないですが。)

個々のツールの注意点

PhpDocumentor

まずインストールの段階で結構メモリを食います。php-cli.ini で利用できるメモリを増やしておきましょう。

ドキュメント作成時にも実行時間、メモリの消費量とも大きいので、この設定はちょこちょこ見直す必要があるかもしれません。

Pdoc

インストールには make が必要です。どうにかして make を調達しましょう。Windows には入れたことないのでどうやるのかは分かりません。

JSDoc

HTML::Template が必要です。パッケージが用意されているならそれを、ないなら CPAN からインストールします。

メモリがちょっとでも厳しいと正規表現の再帰処理が深すぎるらしくて落ちることがあります。もしかすると最新の Perl にすればいいのかもしれませんが、多くの Linux ディストリビューションなどで Perl はベースシステムに食い込んでいておいそれとバージョンアップできなかったりします。

そこでスクリプトの設定の方を直します。具体的には

JSDoc.pm の

# Recursion limit for recursive regexes
use constant RECURSION  => 10;

ここです。これをとりあえず半分の 5 とかにすると割と動くと思います。

また、-extensions オプションがうまく動きません。これは jsdoc.pl の方を以下のように直します。

                         relname => $OPTIONS{NESTEDFILENAMING}
                                 ? substr($_, length($arg))
                                 : (fileparse($_))[0]
-                    } if ((-f and -r and /.+\.$ext_re$/oi) &&
+                    } if ((-f and -r and /.+\.($ext_re)$/oi) &&
                                 (/^\Q$arg\E[^\/]+$/ || $OPTIONS{RECURSIVE}))
                 },
                 no_chdir => 1 }, $arg);

これで例えば

-extensions js,jsx

などのようにして InDesign 用の JavaScript を解析対象に入れることができるようになります。

あとは定期実行を設定するだけ

ここはお使いのシステムに応じて cron, タスクスケジューラなどの設定をしてください。

  1. 逆の、コードの自動生成については、ツールによってレベルの違いはあるでしょうがある程度実現できていますな。 

  2. Excel で書いてる? 御愁傷様;-) 

About

例によって個人のなんちゃらです