この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の3日目の記事です。
3日目 ターゲットを実行する
前回、ターゲットを作ったので、次はターゲットを実行するためのルールを追加します。ターゲットを実行するために、exec_target関数を作りたいと思います。
作成したターゲットをHash関数に登録するための処理
ターゲットを指定して実行するためには、作成したターゲットを登録して、後で参照できるようにしておく必要があります。ターゲットはラベル名で識別するので、Hashを用いたテーブルを使用して、ラベル名から該当するターゲットを引き出せるようにしておきます。
グローバル領域に、@target_listというHashマップを定義します。キーはターゲットに付けているラベル名。コンテンツはターゲットルールそのもの(Targetクラスをインスタンス化したもの)です。
src/rumy-exec.rb
def exec_target (name) if @target_list.key?(name) then target = @target_list[name] result = `#{target.commands}` puts result else puts "Error: target #{name} not found." end end
@target_list = Hash.new
make_target実行時に、インスタンス化したターゲットを@target_listに登録しておきます。
def make_target (name, &block) target = Target.new(name) target.instance_eval(&block) target.show @target_list[name] = target # ターゲットを@target_listに登録する。 end
ターゲットを実行する
次に、exec_targetでは、同じようにラベル名を引数に指定し、ラベル名からターゲットを参照します。登録されていない(つまり作成されていない)ターゲットを参照しようとした場合はエラー、そうでない場合はターゲットから、コマンドを呼び出して実行します。
src/rumy-exec.rb
def exec_target (name) if @target_list.key?(name) then target = @target_list[name] result = `#{target.commands}` puts result else puts "Error: target #{name} not found." end end
テストを作成する
簡単なechoコマンドを呼び出すターゲットを実行する
では、実際にexec_targetを使用するテストを書いてみましょう。まずは、前回作成した:firstターゲットを実行してみます。exec_targetの引数に:firstを渡すだけで実行できます。
tests/exec_test.rb
#!/usr/bin/ruby load "rumy-exec.rb" make_target :first do depends [:second, :third] executes "echo Hello, First Target" end exec_target :first
$ ruby ../tests/exec_test.rb [DEBUG] : Target Created = first, Depends = [:second, :third], Commands = echo Hello, First Target Hello, First Target
実行できました!ルール(Rubyのラベル)を使用して、ターゲットを実行することができました。
Cソースコードをコンパイルするターゲットを実行する
次にもう少し進んで、以下のソースコードをコンパイルするターゲットを作ってみます。
tests/simple_main.cpp
#include <stdio.h> int main () { printf("Hello rumy-make!!\n"); return 0; }
以下のように、simple_main.cppをコンパイルするための:compileターゲットを作成します。
tests/exec_test.rb
make_target :compile_c do src = "../tests/simple_main.cpp" exe = src.sub(".cpp", "") executes "gcc #{src} -o #{exe}" end exec_target :compile_c
Rubyの内部DSLを使っているわけですから、Rubyの機能をフル活用しましょう。入力ソースコードをsimple_main.cppとし、実行バイナリをsimple_mainとしたいのですが、同じことを2回も書くのは面倒なので、Rubyの文字列に対するsub関数を使用して、ターゲットバイナリのファイル名を作っています。そしてgccに渡す引数も#{src}や#{exe}を活用することで、楽に書けるように工夫をしているわけです。
実行してみると、特にエラー無くコンパイルすることができます。simple_main.cppから、simple_mainがコンパイルされてバイナリが作られていることを確認しましょう。
ruby ../tests/exec_test.rb [DEBUG] : Target Created = compile_c, Depends = , Commands = gcc ../tests/simple_main.cpp -o ../tests/simple_main
これを実行するためのターゲットも作りたいのですが、これは:compileターゲットに対する依存関係を持たせたいので、次回に持ち越しです。