appledocでドキュメント生成

こんにちは、[iOS Advent Calendar / Aug.](http://qiita.com/advent-calendar/ios)28日目担当の[@mtgto](http://twitter.com/mtgto)です。
今日はObjective-Cのソースコードからドキュメントを生成するappledocというツールについて書こうと思います。Javadocや[doxygen](http://www.doxygen.jp/)のObjective-C版といったところでしょうか。
まずはappledocを見たことのない人のために、実例を見ていただきましょう。

[AFNetworkingのドキュメント](http://afnetworking.org/Documentation/)

一見するとApple謹製のドキュメントのようなデザインですが、実はこれ今回ご紹介するappledocによりソースコードのコメントから自動生成されたものなのです。

## サンプル
まずはどのように書けばいいのかということで、自分のプロジェクトの中からドキュメント化してあるメソッドのサンプルを持って来ました。
Javadocなどに親しみのある人にはほとんど調べることなく書くことができると思います。

```objective-c
/**
 * XMLノードから初期化を行います。
 *
 * @param node XMLノード
 * @return nodeが不正な場合はnilを返します。
 */
+ (Channel*)channelWithXMLNode:(GDataXMLNode*)node;
```

引数や戻り値の説明以外のところ、段落やリスト、引用などは[markdown風の記述を行うことができます](http://gentlebytes.com/appledoc-docs-comments/#layout)。

# インストール
appledocはCUIのプログラムです。ソースコードからビルドする場合は、githubからソースコードをcloneしてくる必要があります。
Xcodeのプロジェクトになっているので、自分でビルドしてもよいですが、ビルド＆インストールを行う便利なシェルスクリプトが用意されているので、そちらを使用したほうが簡単です。

```sh
git clone git://github.com/tomaz/appledoc.git
cd appledoc
sudo sh install-appledoc.sh
```

[Homebrew](http://mxcl.github.com/homebrew/)にもappledocのパッケージがあります。

```sh
brew install appledoc
```

[Jenkins](http://jenkins-ci.org/)などによる自動ドキュメント生成ができるとかっこいいんですが、appledoc自体がObjective-C + Cocoa Foundationを使って書かれているので、Mac以外で動くようにするのはちょっと大変そうです。

# 機能
appledocによってできることは大きく次の２つがあります。

- HTMLの生成
- Xcodeヘルプの作成

## HTMLの生成
先ほど見たAFNetworkingの例のように、HTMLを生成することができます。ブラウザから見れるのでOSSなどのドキュメントとしてウェブサイトで公開するとよさそうです。

```sh
appledoc --project-name プロジェクトの名前 --project-company プロジェクトの会社名 --create-html --no-create-docset --output HTMLを生成するディレクトリ ソースコードの場所
```

例として、`~/projects/TestProject`に置かれているプロジェクト"TestProject"のHTML版ドキュメントを~/docs/TestProject"に生成するなら次のようなコマンドになります。

```sh
appledoc --project-name TestProject --project-company TestCompany --create-html --no-create-docset --output ~/docs/TestProject ~/projects/TestProject
```

## Xcodeヘルプの作成
次にXcode内で使用できる形式のヘルプの作成を行う方法です。

```sh
appledoc --project-name プロジェクトの名前 --project-company プロジェクトの会社名 --no-create-html --create-docset --output docsetを生成するディレクトリ ソースコードの場所
```

成功すると`docset`という拡張子でヘルプが生成されます。

ここで`--install-docset`という引数を追加するとXcodeに`docset`がインストールされます。先ほどと同じように"TestProject"のヘルプをXcodeにインストールしてみましょう。
docsetを保存する必要はないので、作業用ディレクトリとして`/tmp`を指定しています。

```sh
appledoc --project-name TestProject --project-company TestCompany --no-create-html --create-docset --output /tmp ~/projects/TestProject
```

docsetのインストールに成功すると`~/Library/Developer/Shared/Documentation/DocSets`に*.docsetというファイルが生成されています。中身はパッケージになっており、Info.plistやHTMLが含まれていることがわかります。

インストール後、Xcodeのヘルプとして使用するためにはXcodeを再起動しなければならないようです（起動時にドキュメントのチェックをしている？）。また起動中に`~/Library/Developer/Shared/Documentation/DocSets`のファイルを削除するとXcodeが強制終了するようです。

再起動後にOrganizerのDocumentationタブを開くと、"TestProject Documentation"というドキュメントが追加されており、Organizerからのドキュメント検索やプロジェクト内でのQuick Helpからドキュメントを参照を行うことができます。やったね。

# plistによるコマンドの記述
先ほどのコマンドラインからの実行ではどうしても引数の数が多くなり、コマンドも長くなってしまいます。
appledocではAppledocSettings.plistというファイルがあればそこから実行引数を取得して実行してくれる機能がついています。
plistをサポートしている、このへんもCUIながらCocoaアプリである所以なのかもしれません。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>--project-name</key>
	<string>TestProject</string>
	<key>--project-company</key>
	<string>TestCompany</string>
	<key>--company-id</key>
	<string>com.example</string>
	<key>--output</key>
	<string>/tmp</string>
	<key>--input</key>
	<array>
		<string>TestProject</string>
	</array>
	<key>--create-docset</key>
	<true/>
	<key>--install-docset</key>
	<true/>
	<key>--keep-undocumented-objects</key>
	<true/>
	<key>--keep-undocumented-members</key>
	<true/>
	<key>--ignore</key>
	<array>
		<string>lib</string>
		<string>Test.m</string>
		<string>Test.h</string>
	</array>
	<key>--exit-threshold</key>
	<integer>1</integer>
</dict>
</plist>
```

## 無視ファイルの指定

`--ignore`に指定している文字列は各ファイル、各ディレクトリの名前の**接尾語**になっているかどうかだけを見ます。なので、上の例だと

- libフォルダ以下のすべてのファイル
- *Test.m
- *Test.h

がドキュメント化の対象から外されます。
詳しくは[Ignore pathsの説明 (英語)](http://gentlebytes.com/appledoc-docs-examples-basic/#ignore-paths)をご覧ください。

## 終了コード
appledocは未設定ですと警告が一つでも発生すると終了コードが1になります。
完全にドキュメントされてないファイルがあるとそれだけで警告が出てしまうため、Jenkinsやシェルスクリプトから実行しているときに警告だけで終了コードが0にならないと不便になる時があります。

上記のplistでも設定していますが、`--exit-threshold`で指定した値以下の終了コードは0に丸めることができます。

エラーコードの詳細については、[Controlling appledoc build resultsの説明 (英語)](http://gentlebytes.com/appledoc-docs-examples-xcode/#exit-code-threshold)をご覧ください。

# 注意
appledocを使っていて出くわした（回避不能な？）トラブルを２つ紹介します。


- Objective-Cには名前空間がないため、Xcodeは同じ名前のクラスが複数ドキュメントにあるとどれが選択されるかわからないようです（最初に見つかったものを選んでいるのでしょうか？）。
- たまに解析エラーになるファイルがあるようです。そのときにはappledocの無視ファイルに登録するなどして回避しましょう。

# 終わりに
３日前のコードは自分で書いたものであってもすぐには読めません。
githubなどに置かれる他の人の書いたコードであればもっと時間がかかるでしょう。

そんなときにプロジェクトのドキュメントがあれば、
- 「このメソッドはこういう仕様なんだな」
- 「このクラスはこういう目的で作られてるんだな」
というのが、まあ、少しはわかりやすくなるかもしれません;-p

ソースコードを腐らせないためにもドキュメント化をしてみませんか？

# 動作確認環境

- MacBook Air (2011)
- Mac OS X 10.8
- Xcode 4.4.1
- appledoc 2.0.5 (build 789)

# 参考にしたページ
- [github - tomaz/appledoc](https://github.com/tomaz/appledoc)
- [Gentle Bytes (appledocの作者のウェブサイト)](http://gentlebytes.com/)
  - [appledocの記法](http://gentlebytes.com/appledoc-docs-comments/)
- [Safx - appledocを使ってObjective-CソースをXcode Organizerで表示できるようにする](http://safx-dev.blogspot.jp/2012/06/appledocobjective-cxcode-organizer.html)
- [Safx - appledocのドキュメント作成を自動で行うようにする](http://safx-dev.blogspot.jp/2012/06/appledoc.html)