FPGA開発日記

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

DSLでビルドツールを自作する (18日目 コマンドラインの整備)

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

18日目 コマンドラインの整備

これまで、Rumy-Makeを起動するために基本的に以下のようなコマンドを実行していました。

RUBYLIB=${HOME}/work/rumy-make/src ./build.rb clean

このコマンドを実下するために、build.rbに以下のようなコードを追加する必要がありました。要するに、デフォルトの実行オプションや、cleanコマンドの挙動、デフォルトの動作などについてbuild.rbに追加する必要がありました。

しかし、RUBYLIBをわざわざ指定したり、デフォルトの動作を指定するためにいちいちbuild.rbを指定するのは面倒です。しかも、Rumyルールファイルであるbuild.rbRubyスクリプトとして実行するというのは、何となく格好悪いので、これらのCLIオプションを制御することのできる機能を追加します。

RubyのLIB環境変数を定義する

まずは、コマンドラインで毎回指定しているRUBYLIBを外します。私はコマンドラインツールの管理をEnvironmental Modulesで管理しているので、Rumy-Makeを使用するためのRumyモジュールを新規作成し、Rumyをロードした場合にはRUBYLIB環境変数が定義されるようにします。

  • ~/dotfiles/modules/rumy/rumy
#%Module1.0
##
proc ModulesHelp { } {
    puts stderr "Rumy Makefile\n"
}

module-whatis   "Rumy Makefile Environment"

# append pathes
prepend-path PATH            $::env(HOME)/work/rumy-make/src
prepend-path RUBYLIB         $::env(HOME)/work/rumy-make/src

Rumyモジュールをロードすると、RUBYLIBが定義され、コマンド実行時のRUBYLIBの指定を省略できます。

module load rumy/rumy
./build.rb clean

これでコマンドライン環境変数を制御できるようになりました。

Rubyコマンドライン管理パッケージ thorでコマンドラインを制御する

次に、コマンドラインツールとしてもう少し使いやすくするようにします。実現したいことは、

  • 以下の3つのコマンドを用意する。
rumy build   # ビルドの実行
rumy clean   # クリーンの実行
rumy deps_draw # 依存関係グラフを印刷
rumy build --file buildfile.rb  # 依存関係ルールファイルとしてbuildfile.rbを指定する(デフォルトはbuild.rb)

これを実現するために、Rubyのthorパッケージを使用します。RubyCLIツール作成のためのGemパッケージです。

https://qiita.com/tbpgr/items/10a5c236cfb528c76ef5

https://qiita.com/akif999/items/67fabf49c3d64274898f

thorパッケージについては初めて知ったのですが、Rubyのツールのコマンドラインオプションなどの管理を行ってくれます。早速、このパッケージを使用してみます。

  • src/rumy
#!/usr/bin/ruby

require 'thor'
require 'rumy-main.rb'

class RumyCLI < Thor
  default_command :build

  desc "rumy build", "Execute Build"
  method_option "file", type: :string
  def build()
    rumyfile = 'build.rb'
    if options.key?("file") then
      rumyfile = options["file"]
    end
    load rumyfile
    exec_target :all
  end

  desc "rumy clean", "Execute Clean"
  method_option "file", type: :string
  def clean()
    rumyfile = 'build.rb'
    if options.key?("file") then
      rumyfile = options["file"]
    end
    load rumyfile
    clean_target :all
  end

  desc "rumy deps_draw", "Draw dependence Graph"
  def deps_draw()
    load 'build.rb'
    draw_target :all
  end

end

RumyCLI.start(ARGV)

rumy build, build clean, build deps_drawの3つのコマンドを作成しました。すべてデフォルトではターゲットディレクトリに存在しているbuild.rbを読み込み、最初のターゲットは:allに記述してあるものとします。rumy-mainパッケージをロードして、コマンドを実行します。

また、rumy buildコマンドとrumy cleanコマンドについては、--fileオプションによってデフォルトのRumyルールファイルを指定できるようにしました。これにより、build.rb以外のRumy-Makefileを記述できるようになります(makeの-f)のようなものです。

$ rumy help
Commands:
  rumy help [COMMAND]  # Describe available commands or one specific command
  rumy rumy build      # Execute Build
  rumy rumy clean      # Execute Clean
  rumy rumy deps_draw  # Draw dependence Graph

さらにルールファイルについて、これまではswimmer_riscvなどの実行ファイル名をトップのターゲットに指定していましたが、統一したいので:allに設定します。

  • build.rb
...
make_target :all do
  global
  depends ["swimmer_riscv"]
end

さて、これでSwimmer-RISCVをビルドできます。

rumy
$ rumy
key = PATH, data = /usr/bin/:/bin/:/usr/local/bin/
key = RUBYLIB, data = /home/msyksphinz/work/rumy-make/src
fatal: Needed a single revision
[DEBUG] : Target Created  = gen_riscv_arch_info, Depends = , Commands = ["cd ../src && ruby -I../script/ ../script/gen_arch_table.rb riscv"]
[DEBUG] : Target Created  = gen_riscv_csr_info, Depends = , Commands = ["cd ../src && ruby -I../script/ ../script/gen_sysreg_table.rb riscv"]
[DEBUG] : Depend Tareget "../script/gen_arch_table.rb" is skip because it's file.
[DEBUG] : Depend Tareget "../script/gen_decode_table.rb" is skip because it's file.
[DEBUG] : Depend Tareget "../script/gen_operand_table.rb" is skip because it's file.
[DEBUG] : Depend Tareget "riscv_arch_table.rb" is skip because it's file.
[DEBUG] : Depend Tareget "../script/gen_inst_mnemonic.rb" is skip because it's file.
...

コマンドラインツールを導入したことにより、Makefilemakeを一発実行するだけでビルドできるように、Rumy-Makeもrumyコマンドを一発実行するだけでビルドできるようになりました。