LoginSignup
14
9

More than 5 years have passed since last update.

MakefileのドキュメンテーションツールをGoで書いた

Posted at

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)

リポジトリはこちら

14
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9