Perl製ビルドツール "Daiku" 入門編

## はじめに

以前の投稿[^1]で紹介した Perl 製ビルドツールの [Daiku](https://metacpan.org/release/Daiku) ですが、2015年3月現在のところ、[GitHub で "Daikufile" と検索](https://github.com/search?utf8=%E2%9C%93&q=daikufile)してみてもヒット数は多くありません。
昨年5月に version 1.0 がリリース[^2]されて1年近くが経ちますが、まだまだ認知度が低い印象です。

[^1]: [Perlプロジェクトのドキュメントをmakeで一括生成](http://qiita.com/key-amb/items/791f78081018fd86d10d)
[^2]: [Daiku and Daikufile are now production ready | おそらくはそれさえも平凡な日々](http://www.songmu.jp/riji/entry/2014-05-12-daiku.html)

そこで、いくつかサンプルを書いてみながら Daiku の機能を試してみました。

なお、今回作成した Daikufile やソース一式は GitHub の [progrhyme/perl5-Daiku-examples](https://github.com/progrhyme/perl5-Daiku-examples) というリポジトリにアップしています。

## Daiku を使う準備

### インストール

cpanm[^3] を使っている場合、シェルから `cpanm Daiku` を実行してインストールして下さい。
Daiku パッケージには `daiku` コマンドが付属しています。
perl を perlbrew[^4] や plenv[^5] 環境で実行している場合、rehash が必要になります。

```bash
cpanm Daiku
plenv rehash # perlbrew なら perlbrew rehash
which daiku
# daiku コマンドのパスが表示されます
```

[^3]: https://metacpan.org/pod/App::cpanminus
[^4]: http://perlbrew.pl/
[^5]: https://github.com/tokuhirom/plenv

### daiku コマンドの使い方

最も簡単な使い方は単に `daiku` と打つだけです。
この場合、カレントディレクトリの **Daikufile** に従ってビルドを実行します。
`-f` オプションで、任意の Daikufile を指定することもできます。

また、`daiku タスク名` で、Daikufile に記述された任意のタスクを実行することもできます。

```bash
% daiku                      # Daikufile のビルドを実行
% daiku deploy               # タスク名を指定して実行
% daiku -f Daikufile.another # Daikufile.another のビルドを実行
```

また、`daiku -T` で Daikufile に記されたビルドタスクを一覧できます。

```bash
% daiku -T
daiku default  # run
daiku install  # install necessary modules
daiku run      # run app
daiku test     # run test
```

その他、詳しい使い方は `daiku --help` で確認して下さい。

## Daikufile とは

**Daikufile** は GNU make[^6] の Makefile に相当し、ビルドで実行するタスクを Daiku 独自の DSL (Domain Specific Language=ドメイン特化言語)で記述したものになります。

Daikufile の記法については、[GitHub](https://github.com/tokuhirom/Daiku) の README.md がドキュメントとなっています。

本稿でも、以降の例で詳しく見ていきます。

[^6]: http://www.gnu.org/software/make/

## Daiku で "Hello, world!"

最もシンプルな例として、Daiku で "Hello, world!" を出力する簡単なビルドを実行してみます。

### Daikufile

Daikufile は次のようになります。

```pl
desc 'hello'; # (1.1)
task default => sub { # (1.2)
    sh "echo 'Hello, world!'"; # (1.3)
};
```

以下、この Daikufile の中身を見ていきます。

**(1.1)** `desc <Str>` の構文で、下行に続くタスクの説明を表します。
省略することも可能ですが、書いておくと `daiku -T` で表示できるので、その方が親切でしょう。

**(1.2)** `task <Str> => <CodeRef>` の構文で、タスクの定義になります。
`<Str>` という名前のタスクで `<CodeRef>` のコードが実行されます。

この `task` 関数を使った構文は、ドキュメントに記載されている通り、他にもいくつか種類があります。

**(1.3)** `sh` 関数は Perl の `system` 関数と同義です。即ち、シェルコマンドを実行します。

### 実行結果

ビルドを実行すると、以下のように出力されます。

```
% daiku
[LOG] Building Task: default
Hello, world!
```

ここで、タスク名である *default* は既定値であるため、省略することができます。
上は `daiku default` と実行しても同じ結果になります。

## C言語のコードをビルドしてみる

わざわざ Daiku でやる必要はないかと思いますが、本家サンプルにもある C言語プログラムのビルドをやってみます。

ここでは、ソースファイル hello.c からオブジェクトファイル hello.o を経由して実行ファイル hello を生成します。

### Daikufile

```pl
desc 'build hello binary';
task 'default' => 'hello';

file 'hello' => 'hello.o' => sub { # (2.1)
    my $file = shift; # (2.2)
    sh sprintf('gcc -o %s %s', $file->dst, $file->deps->[0]); # (2.3)
};

rule '.o' => '.c' => sub { # (2.4)
    my ($rule, $dst, @srcs) = @_; # (2.5)
    sh sprintf('gcc -o %s -c %s', $dst, $srcs[0]); # (2.6)
};

desc 'clean up';
task 'clean' => sub {
    unlink $_ for ('hello', 'hello.o');
};
```

以下、この Daikufile の内容を解説します。

**(2.1)** `file <Str1> => <Str2> => <CodeRef>` という構文が登場しました。
`<Str1>` のファイルを生成するタスクの一種と見なすことができます。
このタスクでは `<CodeRef>` のコードが実行されます。`<CodeRef>` の実行によって、`<Str1>` のファイルが生成されることが期待されます。

また、このタスクは `<Str2>` のタスクに依存しています。即ち、`<Str1>` を生成するこのタスクよりも先に、依存関係によって `<Str2>` のビルドが実行されます。

**(2.2)** `$file` には [Daiku::File](https://metacpan.org/pod/Daiku::File) オブジェクトが渡されます。

**(2.3)** `$file->dst` は生成ファイル、`$file->deps` は依存タスクのリストです。
実行されるコマンドは `gcc -o hello hello.o` となります。

さて、ここで *hello* タスクが依存している *hello.o* という名前のタスクはこの Daikufile には明記されていません。
*hello.o* のタスクは続く `rule` 構文で表される **Suffix Rules** によって、解決されています。

**(2.4)** `rule <Str1> => <Str2> => <CodeRef>` 構文は **(2.1)** と似ていますが、`<Str1>`, `<Str2>` にはファイル拡張子が入ります。
この構文は GNU make の Suffix Rules[^7] と同様な働きをします。
Daiku はこの文によって *foo.o* を生成するには *foo.c* が必要で、`<CodeRef>` のコードを実行すればよいことを知ります。

[^7]: https://www.gnu.org/software/make/manual/html_node/Suffix-Rules.html

**(2.5)** `$rule`, `$dst`, `@srcs` にはそれぞれ *Daiku::SuffixRule* オブジェクト、出力ファイル名、ソースファイルリスト(この場合1つ)が格納されます。

**(2.6)** 実行されるコマンドは `gcc -o hello.o -c hello.c` となります。

### 実行結果

ビルドを実行すると、次のようになります。

```bash
% daiku
[LOG] Building Task: default
[LOG] Processing file: hello
[LOG] Building SuffixRule: hello.o
[LOG] Processing file: hello.c
[LOG]   Building file: hello(0)
% ls -l
-rw-r--r--  1 key-amb  key-amb   376  3 14 03:28 Daikufile
-rwxr-xr-x  1 key-amb  key-amb  8496  3 14 04:06 hello
-rw-r--r--  1 key-amb  key-amb    73  3 14 01:41 hello.c
-rw-r--r--  1 key-amb  key-amb   836  3 14 04:06 hello.o
% ./hello
Hello, world!
```

`daiku clean` を実行すると、実行ファイル *hello* とオブジェクトファイル *hello.o* を削除できます。

```bash
% daiku clean
[LOG] Building Task: clean
% ls -l
-rw-r--r--  1 key-amb  key-amb   376  3 14 03:28 Daikufile
-rw-r--r--  1 key-amb  key-amb    73  3 14 01:41 hello.c
```

## まとめ

以上、簡単な例でしたが、Daiku の基本的な機能を解説しました。
他にも、Ruby rake 相当の namespace 分割機能もあるようです。

プロジェクトにおいて定形作業が煩雑になっている場合や、一連のジョブ実行を定型化したいような場合、Daiku の導入を検討する価値はあると思います。

## 補遺

### ".PHONY task" について

`task` 構文について、README.md には *.PHONY task* という記載があります。
これは、GNU make 由来[^8]の表現で、「ビルドによってファイルを生成せず、指定された際に必ず実行されるタスク」のことです。

make に慣れている人はわかると思いますが、慣れてないと「何のこっちゃ」と思う人もいるかな、ということで触れておきました。

[^8]: https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html

## 脚注