はじめに
以前の投稿1で紹介した Perl 製ビルドツールの Daiku ですが、2015年3月現在のところ、GitHub で "Daikufile" と検索してみてもヒット数は多くありません。
昨年5月に version 1.0 がリリース2されて1年近くが経ちますが、まだまだ認知度が低い印象です。
そこで、いくつかサンプルを書いてみながら Daiku の機能を試してみました。
なお、今回作成した Daikufile やソース一式は GitHub の progrhyme/perl5-Daiku-examples というリポジトリにアップしています。
Daiku を使う準備
インストール
cpanm3 を使っている場合、シェルから cpanm Daiku
を実行してインストールして下さい。
Daiku パッケージには daiku
コマンドが付属しています。
perl を perlbrew4 や plenv5 環境で実行している場合、rehash が必要になります。
cpanm Daiku
plenv rehash # perlbrew なら perlbrew rehash
which daiku
# daiku コマンドのパスが表示されます
daiku コマンドの使い方
最も簡単な使い方は単に daiku
と打つだけです。
この場合、カレントディレクトリの Daikufile に従ってビルドを実行します。
-f
オプションで、任意の Daikufile を指定することもできます。
また、daiku タスク名
で、Daikufile に記述された任意のタスクを実行することもできます。
% daiku # Daikufile のビルドを実行
% daiku deploy # タスク名を指定して実行
% daiku -f Daikufile.another # Daikufile.another のビルドを実行
また、daiku -T
で Daikufile に記されたビルドタスクを一覧できます。
% daiku -T
daiku default # run
daiku install # install necessary modules
daiku run # run app
daiku test # run test
その他、詳しい使い方は daiku --help
で確認して下さい。
Daikufile とは
Daikufile は GNU make6 の Makefile に相当し、ビルドで実行するタスクを Daiku 独自の DSL (Domain Specific Language=ドメイン特化言語)で記述したものになります。
Daikufile の記法については、GitHub の README.md がドキュメントとなっています。
本稿でも、以降の例で詳しく見ていきます。
Daiku で "Hello, world!"
最もシンプルな例として、Daiku で "Hello, world!" を出力する簡単なビルドを実行してみます。
Daikufile
Daikufile は次のようになります。
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
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 オブジェクトが渡されます。
(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 Rules7 と同様な働きをします。
Daiku はこの文によって foo.o を生成するには foo.c が必要で、<CodeRef>
のコードを実行すればよいことを知ります。
(2.5) $rule
, $dst
, @srcs
にはそれぞれ Daiku::SuffixRule オブジェクト、出力ファイル名、ソースファイルリスト(この場合1つ)が格納されます。
(2.6) 実行されるコマンドは gcc -o hello.o -c hello.c
となります。
実行結果
ビルドを実行すると、次のようになります。
% 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 を削除できます。
% 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 に慣れている人はわかると思いますが、慣れてないと「何のこっちゃ」と思う人もいるかな、ということで触れておきました。