この記事は「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.rbをRubyスクリプトとして実行するというのは、何となく格好悪いので、これらの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パッケージを使用します。RubyのCLIツール作成のための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. ...
コマンドラインツールを導入したことにより、Makefileがmakeを一発実行するだけでビルドできるように、Rumy-Makeもrumyコマンドを一発実行するだけでビルドできるようになりました。