FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

DSLでビルドツールを自作する (3日目 ターゲットを実行する)

この記事は「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ターゲットに対する依存関係を持たせたいので、次回に持ち越しです。

f:id:msyksphinz:20191119011946p:plain