κeenのHappy Hacκing Blog | Lispエイリアンの狂想曲

AsciiDocを使ってお手軽manページ生成

コマンドラインツールを作ってるみなさん、man書いてますか?コマンドラインツールを使う人は時代の変化に取り残された遺物なのでWebにドキュメント置いても読んでくれませんよ。

いや、私のようにmanだけを頼りにツールを使う人もいるのでちゃんと書いて下さいね。面倒だって?AsciiDocを使えば簡単に書けますよ!

Manとは

man hoge

で見れるやつですね。

troff

manはtroffというフォーマットで書く必要があります。どんな感じかというと

.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
use the latest sbcl
.RE
.sp
.if n \{\
.RS 4
.\}

はい。人間が書くものではないですね。なので別の形式(AsciiDoc)から変換することを考えます。

セクションナンバー

コマンド(など)はセクションに分かれています。manを書くときに必要になる知識なので覚えておきましょう。コマンドラインから使うものの他、Cの関数やコンフィグファイルについての項目もあります。

Description
1 一般コマンド
2 システムコール
3 ライブラリ関数
4 デバイス
5 ファイルフォーマット(コンフィグファイル)
6 ゲーム
7 その他
8 システムメンテナンス(sudoとか)
9 カーネル

一般デベロッパが使うのは1、5、6、8あたりでしょうか。6のゲームには普通のゲームの他、phase of moon、盈虚(えいきょ、月齢のこと)を表示するpomコマンドなどがあります。

manを書くときにはmanを必ずどこかのセクションに割り当てます。

manを使う時はman hogeだと全てのセクションのhogeのmanを捜しますがman 1 hogeだとセクション1のみから捜します。割とセクション1と2で同名のmanがあることがあるので重宝します。また、そのような混同を防ぐためにコマンドやシステムコールの後には括弧書きでセクションナンバーを書くのが慣習です。manならman(1)のように。

因みにman n introとするとセクションナンバーnの説明が見れます。

AsciiDoc

AsciiDoc Home Page MarkdownとかrSTとかの類いです。リーダブルなAsciiなDocを書いて色々な形式に変換出来ます。HTMLやDocBookなどの形式に変換出来ます。普通にそのままのテキストでも十分リーダブルだと思います。拡張子は.txtを使います。そのままでも読めるんだぜオーラが出てますね。

有名どころだとgitが使ってるとか。バッククォートをあまり使わないので個人的にはリーダブルさが他より高い気がします。

ここにチートシートがあるのでだいたいはそこを見てもらえば良いのですが、例を出すと

TITLE
=====
keen mail@address
0.0.1, 2015-01-24

Header
------
[source, lisp]
(write-line "Hello, World")

な感じです。 タイトル、=で下線のあとにファイルの属性がきます。Author、Mail、Revision、Dateはよく使うのでこのようにリーダブルに書けます。他の属性は

:attr: value

のように書く必要があります。Authorなどもこのように書いても構いません。

独特なのがパラグラフ毎にマークアップしていくところですね。パラグラフの区切は空行です。上の

[source, lisp]
(write-line "Hello, World")

のようにパラグラフの前にマークアップコマンドを置けます。1パラグラフに収まらないものは例えばソースコードなら

----
(defun hello (name)
  (format t "Hello, ~a~%" name))

(hello "κeen")
----

のように-4つで囲む、などの記法もあります。

manのフォーマット

さて、話戻ってmanのフォーマットです。みなさん見慣れてるかと思いますが、manは最初はNAME、SYNOPSIS、DESCRIPTIONのセクションが並ぶことを要求します。

AsciiDocからman生成

manが一定のフォーマットを要求するのでAsciiDoc側も一定のフォーマットで書いてやる必要があります。

NAME、SYNOPSIS、DESCRIPTIONは勿論のこと、タイトルが「コマンド名(n)」となっている必要があります。コマンド名にはスペースが入ってはいけません。gitのようにサブコマンドに分かれているものはハイフンで繋ぐようです。nはセクションナンバーですね。

NAMEについて捕捉しておくと、NAMEの書式も決まっていて、

command-name - one line description

のフォーマットである必要があります。apropos(1)で表示するためでしょうね、きっと。

実際に私が書いたものを載せますね。

cim-use(1)
==========
keen(@blackenedgold) 3han5chou7@gmail.com
:man manual: CIM Manual

NAME
----
cim-use -  Use specified impl as 'cl' command's backend.

SYNOPSIS
--------
[verse]
cim use <impl[-version]> [--default]

DESCRIPTION
-----------

Use <impl> as background for cl command. It also affects bare lisp command.
If version is not given, use latest version.
If --default is given, use the specified implementation at initial state.

EXAMPLES
--------
* use the latest sbcl
----
$ cim use sbcl
$ sbcl --version
SBCL 1.1.14
----

* use old sbcl
----
$ cim use sbcl-1.1.10
$ sbcl --version
SBCL 1.1.10
----

* use ccl-1.9 and set it default
----
$ cim use ccl-1.9 --default
----

:man manual:の属性はヘッダに表示されるものです。

んで変換は

a2x  --doctype manpage --format manpage filename.txt

これ、ファイル名に関らずcim-use.1というファイルを生成します。

んでファイルの内容は

man ./cim-use.1

で見れます。

CIM-USE(1)			  CIM Manual			    CIM-USE(1)



NAME
       cim-use - Use specified impl as 'cl' command's backend.

SYNOPSIS
       cim use <impl[-version]>	[--default]

DESCRIPTION
       Use <impl> as background	for cl command.	It also	affects	bare lisp
       command.	If version is not given, use latest version. If	--default is
       given, use the specified	implementation at initial state.

EXAMPLES
       o   use the latest sbcl

	   $ cim use sbcl
	   $ sbcl --version
	   SBCL	1.1.14

       o   use old sbcl

	   $ cim use sbcl-1.1.10
	   $ sbcl --version
	   SBCL	1.1.10

       o   use ccl-1.9 and set it default

	   $ cim use ccl-1.9 --default

AUTHOR
       keen(@blackenedgold) 3han5chou7@gmail.com
	   Author.



				  01/21/2015			    CIM-USE(1)

他の例だと、gitのドキュメントを見るとよさそうです。あそこはWEB用にもビルドしてるのでAsciiDocのコンフィグ書いてWEBとmanで条件分岐するマクロとかも書いてます。変態ですね。

ビルド

私は以下のようなスクリプトを書いてビルドしてます。並列ビルド、タイムスタンプセンシティブビルド対応。DOC_ROOTはデファルトでそのスクリプトが置いてあるディレクトリです。環境変数で制御出来ます。make.shって名前で保存したなら

$ DOC_ROOT=your/root ./make.sh

のように。その他AUTO_POLLなども設定出来ます。必要ならお使い下さい。CIMの配布物に含まれるのでBSDライセンスです。

#!/bin/sh

: ${DOC_ROOT:=$(cd $(dirname $0); pwd)}
: ${MAN_DIR:=${DOC_ROOT}/man/man1}
: ${AUTO_POLL:=false}
: ${POLL_INTERVAL:=5}

txt2man(){
    if [ ! -e "${MAN_DIR}/$(basename $1 .txt).1" ] || [ "$1" -nt "${MAN_DIR}/$(basename $1 .txt).1" ]; then
        echo "Building $1"
        if a2x -v  --doctype manpage --format manpage -D "${MAN_DIR}" "$1" > "log/$(basename $1 .txt).log" 2>&1
        then
            echo "O Built $1"
        else
            echo "X Bulid failed: $1. See log/$(basename $1 .txt).log"
            return 1
        fi
    fi
}

cd "${DOC_ROOT}"
while "${AUTO_POLL}"; do
      for f in *.txt; do
          txt2man "$f" &
      done

      wait
      if  "${AUTO_POLL}"; then
          sleep "${POLL_INTERVAL}"
      fi
done

運用

manはMANPATH環境変数を元にmanを捜します。捜すのはMANPATH直下ではなく、セクションナンバー1なら/man/path/man1/name.1を捜します。それっぽいところに置きましょう。

因みにFreeBSDではちょっと面倒です。詳しくはmanpath(1)を見て下さい。

まとめ

  • manpageは重要
  • だけど手で書くものではない
  • AsciiDoc使うと便利!
  • 便利なスクリプト用意しといたよ
Written by κeen