MakefileのドキュメンテーションツールをGoで書いた
この記事は Go (その2) Advent Calendar 2016 21日目の記事です。
開発時のタスクランナーがわりにMakefileをよく使います。
make build
したらビルドするとか make fmt
したら go fmt
するとか、 make server
したらサーバを起動するとかです。
中には make build target=linux
のようにオプションも渡したくなったりします。
しかしMakefileのタスクとその使い方をすぐに忘れてしまってMakefileの中を確認するということがよくあるので、
ドキュメントを表示してくれるようなコマンドをGoで書いてみました。
以下のように記述されたMakefileがあったとします。
.PHONY: command
command:
@## param1=value1 param2=value2
@#
@# document 1
@# document 2
@# document 3
echo "EXECUTE COMMANDS..."
unmakeを実行すると次のように表示されます。
$ unmake Makefile
* command param1=value1 param2=value2
document 1
document 2
document 3
動作を解説します。
makefilePath
はMakefileへのPATHです。
ファイルオープンし、中身を取得しています。
fp, err := os.OpenFile(makefilePath, os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer fp.Close()
buf, err := ioutil.ReadAll(fp)
if err != nil {
panic("")
}
実際にドキュメントの記述がある正規表現はこちらです。
regx, err := regexp.Compile(`(?m:^([^._][a-zA-Z0-9_\-]*):(.*)\n(^\t\@\##.*\n)?(^\t\@\#\s*\n)?((^\t\@\#.*\n)*))`)
if err != nil {
panic("")
}
見にくいですねえ。(?m)
はマルチラインモードです。
今回の場合複数行にわたってコメントを書くことが多かったので
コメントで続く塊を取得するために指定しています。
上記の正規表現を探し出して、出力のために整形しています。
for _, match := range regx.FindAllSubmatch(buf, -1) {
cmdName := strings.TrimSpace(string(match[1]))
paramLine := strings.TrimSpace(strings.Trim(string(match[3]), "\t@#"))
usage := strings.Replace(string(match[5]), "\t@#", " ", -1)
fmt.Printf("* %s %s\n\n%s\n", cmdName, paramLine, usage)
}
そんなに難しいことはしてないですね。
実は最初は同じ処理をPerlで実装していましたw
open (my $fp, $_[0]) or die "$!";
my $buf = do { local $/; <$fp> };
close $fp;
while ($buf =~ /^([^._][a-zA-Z0-9_\-]*):(.*)\n(^\t\@\##.*\n)?(^\t\@\#\s*\n)?((^\t\@\#.*\n)*)/gm ) {
my $cmd = $1;
my $synopsis = $3 || "";
my $msg = $5 || "";
$cmd =~ s/\A\s*(.*?)\s*\z/$1/;
$synopsis =~ s/@##//g;
$synopsis =~ s/\A\s*(.*?)\s*\z/$1/;
$msg =~ s/\t\@\#/ /g;
printf("* %-20s\n\n%s\n\n", $cmd . " " . $synopsis, $msg);
}
ただバイナリにコンパイルできるのが魅力的だったので実装し直しました。
Makefileのhelpには $(MAKEFILE_LIST)
を渡すことで
includeされたMakefileも引数に渡すことができます。
.DEFAULT_GOAL_NAME := help
.PHONY: help
help:
@# Display usage
unmake $(MAKEFILE_LIST)
リポジトリはこちら。