FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

DSLでビルドツールを自作する (17日目 ビルドルールをYAMLに書きたい 2. YAMLルールをもう少し便利に)

この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の17日目の記事です。

17日目 ビルドルールをYAMLに書きたい 2. YAMLルールをもう少し便利に

前回、ビルドルールにYAMLルールを導入してみたのですが、いろいろと実装が途中までになっており、しかもRubyの便利な機能を使えないので結局表現が煩雑になってしまいました。

これはYAMLの制約なのでもうどうしようもないのですが、せっかく作ったので、もう少し強化してまともにビルド処理ができるようにしたいです。今回は、YAMLルールにカスタム命令を実装します。

YAMLにカスタムルールを追加してファイルの自動生成などの処理に対応する

Rumy-MakeのRubyで記述していたルールでは、ルール記法は一般化されていたので、例えばスクリプトを使用してファイルを自動生成する場合には以下のように記述していました。

make_target :gen_riscv_csr_info do
  depends ["../script/gen_sysreg_table.rb",  "./riscv_spr_table.rb"]
  executes ["cd ../src && ruby -I../script/ ../script/gen_sysreg_table.rb riscv"]
end

しかし、YAMLではここまで自由な書き方はできません。いろいろ考えたのですが、YAMLのライブラリビルド、実行ファイルビルドと一緒で、カスタムビルド用のルールを定義して同じように記述できるようにしました。つまり、YAMLの記法で、

- "libswimmer.a" :  # ライブラリのビルドルール
  - type :
    - "lib"         # ビルドタイプを"lib"に設定
- "swimmer_riscv" : # 実行ファイルのビルドルール
  - type :
    - "bin"         # ビルドタイプをバイナリに設定
- "config.hpp" :    # ファイル生成などの、カスタムルール
  - type :
    - "custom"      # ビルドタイプをカスタムに設定

このように先頭に記述すれば、カスタムルールを定義できるようにしました。これに基づいて、rumy-yaml.rbの実装を追加します。

  • src/rumy-yaml.rb
def obtain_rule (rule_array)
  # assert(rule_array.kind_of?(Array))
  rule_array.each {|rule|
...
        elsif value[0].key?("type") then
          if value[0]["type"][0] == "lib" then
            make_library_rule(key, value)
          elsif value[0]["type"][0] == "bin" then
            make_execute_rule(key, value)
          elsif value[0]["type"][0] == "custom" then
            make_custom_rule(key, value)  # customでmake_custom_ruleに飛ぶ
...

make_custom_rule()は以下のような実装になっています。

def make_custom_rule(rule_name, option_hash)
  rule_option_hash = Hash.new
  rule_option_hash['executes'] = []
  rule_option_hash['depends' ] = []

  option_hash.each {|elem|
    elem.each{|key, value|
      rule_option_hash[key] = value
    }
  }
  make_target rule_name do
    depends rule_option_hash['depends']
    executes rule_option_hash['executes']
  end
end

とても単純です。make_targetでルールを定義するようにしました。

これを使って、YAMLにルールを追加します。スクリプトを使って、C++のヘッダファイルを生成するルールなどです。

# RISC-Vのアーキテクチャテーブルを生成するスクリプトを実行するためのカスタムルール。
- gen_riscv_arch_info :
  - type :
    - "custom"
  - depends :
    - "../script/gen_arch_table.rb"
    - "../script/gen_decode_table.rb"
    - "../script/gen_operand_table.rb"
    - "../script/gen_inst_mnemonic.rb"
    - "riscv_arch_table.rb"
  - executes :
    - "cd ../src && ruby -I../script/ ../script/gen_arch_table.rb riscv"

# シミュレータのコンフィグファイルを生成するためのカスタムルール。
- config_hpp :
  - type :
    - "custom"
  - executes :
    - "sed 's/@VERSION@/#{build_date}/g' config.h.in | sed 's/@REVISION@/#{build_version}/g' > config.hpp"

# CSRテーブルを生成するためのカスタムルール。
- gen_riscv_csr_info :
  - type :
    - "custom"
  - depends :
    - "../script/gen_sysreg_table.rb"
    - "./riscv_spr_table.rb"
  - executes :
    - "cd ../src && ruby -I../script/ ../script/gen_sysreg_table.rb riscv"

このような実装により、YAMLによりビルドルールを記法することができました。

ただし、作っておいて何ですが結局Rumyルールよりも複雑で表現能力も低いので、あまり使わないと思います。YAMLの呼び出しと構文解析の勉強だけで、この機能はお蔵入りかな...