この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の12日目の記事です。
12日目 C++プロジェクトビルド用のラッパーを作ろう
前回、実際に実プロジェクトにRumy-Makeを適用して問題が把握できてきました。まず、C++ファイルをコンパイルするために記述しなければならないルールが多すぎます。いくらRubyの構文が使えるとはいえ、数10個のファイルをコンパイルするためにわざわざfor文を記述しなければならないのは面倒です。そう考えると、CMakeのようなうまく内部をラップしたビルドツールというのは上手くできていると実感できます。
まあ人のツールをほめたたえてもしょうがないので、自作のビルドツールにも同じようなラッパーを導入しましょう。そこで、rumy-cpp.rb
というラッパーを作成し、このファイルを呼べばC++用のビルドはある程度形式化できるようにしておきましょう。このC++用ラッパーでは、以下の機能を追加します。
- C++のファイルからライブラリを作るためのルール。C++のソースファイルを指定し、あとは外部の別のライブラリを指定すれば自動的にすべてのソースファイルをコンパイルしてライブラリを生成します。
- C++のファイルから実行ファイルを作るためのルール。C++のソースファイルを指定し、外部の依存する別のライブラリを指定すれば自動的にすべてのソースファイルをコンパイルして実行ファイルを生成します。
それぞれ、ライブラリを生成するルールをmake_library
、実行ファイルを生成するルールをmake_execute
として実装します。
実装としては全く難しくなくて、前回自力で書いたソースファイルのコンパイルのループと、最後の実行ファイル・ライブラリの作成部分を追加するだけです。
src/rumy-cpp.rb
def make_library(lib_name, cpp_file_list, compile_options, link_options, additional_depends = []) # 依存するターゲットをすべて実行するループ additional_depends.each {|dep| do_target dep } obj_file_list = cpp_file_list.map {|cpp| cpp + ".o"} # ソースファイルから生成したオブジェクトを使ってライブラリを生成する。 make_target lib_name do global depends obj_file_list executes ["ar qc #{link_options.join(' ').to_s} #{lib_name} #{obj_file_list.join(' ').to_s}"] end # 指定されたソースファイルをすべてコンパイルしていくループ。 cpp_file_list.zip(obj_file_list).each {|pair| cpp = pair[0] obj = pair[1] make_target obj do depends [cpp] executes ["g++ #{compile_options.join(' ').to_s} -c #{cpp} -o #{obj}"] end } end
このラッパーを使えば、前回一生懸命書いたオブジェクトをコンパイルするためのループは、以下のように表現できます。とても楽になりました。
cedar_cpp_lists = [ "../src/riscv_pe_thread.cpp", "../src/riscv_syscall.cpp", "../src/riscv_fds.cpp", ... "../src/mem_body.cpp", "../src/gdb_env.cpp" ] c_options = compile_options.join(' ').to_s l_options = link_options.join(' ').to_s make_library "libriscv_cedar.a", cedar_cpp_lists, compile_options, [], [:gen_riscv_arch_info, :gen_riscv_csr_info]
make_library
を使えば、ソースファイルのリストを追加するだけの1行でライブラリを生成できます。
実行ファイルの生成も同様です。ここではmake_execute
の実装は殆どmake_library
と一緒なので、ここでは省略します。ここでは、make_execute
の使用例だけを示します。
make_execute("swimmer_riscv", swimmer_cpp_lists, ["libriscv_cedar.a", "../vendor/softfloat/build/libsoftfloat.a"], compile_options, link_options, link_libs, [:config_hpp])
使い方はmake_library
と似ており、ソースファイル、依存するライブラリ、リンクオプション、外部リンクなどを指定すれば完成です。
これにより、前回のRumy-Makeファイルに対してどれだけ行数を節約できたかというと、
make_library
,make_execute
適用前 : 116行make_library
,make_execute
適用後 : 92行
多少は行数を削減することができました。まあ行数削減というか、目的としてはルールを見やすくするためだったので行数が減らなくても問題ないと思います。
また、make_library
, make_execute
はmake_target
の単純なラッパーなので、draw_target
も問題なく表示できます。