現在、自作ISSのビルド環境としてはCMakeを利用している。
CMakeの良いところは、
- マルチプラットフォーム (ただしISSの開発においては特にこの利点が生かされているわけではない)
- テキストファイルによるビルド環境管理 (オプション、ファイル一覧などをテキストファイルの形式で管理できる)
- 依存関係の自動抽出
などがある。欠点といえば、CMakeは基本的にC++での開発プロジェクトを前提としており、それ以外のプログラミング言語では管理が少し難しい。 Verilogの開発環境などでも以前導入したことがあるが、かなり苦労させられた(というかほとんど依存関係を自分で記述する羽目になった)
いろいろ調べていたのだが、Tensorflowをインストールするにあたり、Bazelというビルドツールをよく導入していた。 調査の結果、BazelはCMakeよりも多くの言語をサポートしており、マルチプラットフォームで依存関係も抽出できる。これは良さそうだ。 現状のサポート言語としてはVerilogは含まれていないが、将来性を見込んで試行してみよう。
サポートされている言語とコマンド
http://bazel.io/docs/be/overview.html より抜粋
まずは簡単なプロジェクトでBazelの使い方を把握する
ググった結果、以下の記事がとても良さそうだった。
同じような環境を構築し、試行してみた。
BUILDの中身
cc_binary ( name = "main", srcs = ["main.cpp"], )
Makefileの中身
.PHONY: all all: run BAZEL=$(shell which bazel) .PHONY: run run: $(BAZEL) run //src:main .PHONY: build build : $(BAZEL) build //src:main .PHONY: clean clean : $(BAZEL) clean
実行結果
$ make /usr/local/bin/bazel run //src:main INFO: Found 1 target... Target //src:main up-to-date: bazel-bin/src/main INFO: Elapsed time: 6.113s, Critical Path: 0.47s INFO: Running command line: bazel-bin/src/main Hello World
確かに、シンプルなプロジェクト構成であれば、簡単にビルドおよび実行が可能だ。
欠点としては、異常に遅いことだと思う。一つのC++ソースをビルドして実行するのにおよそ5秒はかかっている。CMakeだと一瞬なのに...
実際のプロジェクトはここまで単純なものでは無い
シンプルなプロジェクトならこれで問題ないが、実際に僕のプロジェクトはここまで単純なものでは無い。
ライブラリの生成方法はBazelでもとても簡単だ。上記のBUILD記述ではcc_binaryとしていたが、cc_libraryとして同様の記述をするだけでライブラリを作成できる。
しかし問題は別の言語を用いて、ファイルを自動生成したいときにどのように記述するか、という事だ。まず、僕の環境では、アーキテクチャを記述したrubyのテーブルファイルから、複数のC++コードとヘッダを生成する。
CMakeだと以下のように記述している。
add_custom_command (OUTPUT ../src/inst_decoder.cpp ../src/inst_operand.cpp ../src/inst_mnemonic.cpp ../src/inst_riscv_init.cpp COMMAND ruby ./../src/gen_arch_table.rb riscv WORKING_DIRECTORY ./../src/ DEPENDS ./../src/gen_arch_table.rb ../src/riscv_arch_table.rb )
カスタムコマンドの記述だ。カスタムコマンドgen_arch_table.rbに対して入力ファイル、出力ファイルを記述すれば自動的に依存関係を抽出してくれる。
一方でBazelで同様の記述をするためにはどのようにすれば良いのだろう?
Bazel でカスタムコマンドを使用する
残念ながら、Bazelは現在rubyでのビルドには対応していないらしい。
そこで、カスタムルールを生成するコマンドを使用してみたい。Bazelにもカスタムルールを生成するコマンドが存在する。genruleだ。
http://bazel.io/docs/be/general.html#genrule
genruleにより、C++ファイルを自動生成するコマンドを記述してみた。
genrule( name = "inst_decoder", outs = ["inst_decoder.cpp", "inst_decoder.hpp", "inst_operand.cpp", "inst_mnemonic.cpp", "inst_riscv_init.cpp", "hogelog", ], cmd = "./$(location gen_arch_table.rb) riscv", tools = ["gen_arch_table.rb"], visibility = ["//visibility:public"], srcs = ["gen_arch_table.rb", "gen_inst_list.rb", "gen_decode_table.rb", "gen_function_table.rb", "gen_inst_mnemonic.rb", "gen_operand_table.rb", "riscv_arch_table.rb", ], )
依存関係を記述し、出力としてはさまざまなデコードファイルが生成される、という訳だ。これをBazelでビルドしてみたのだが、以下のようなエラーが出てしまった。
$ make /usr/local/bin/bazel build :inst_decoder --verbose_failures INFO: Found 1 target... ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: declared output 'inst_decoder.cpp' was not created by genrule. This is probably because the genrule actually didn't create this output, or because the output was a directory and the genrule was run remotely (note that only the contents of declared file outputs are copied from genrules run remotely). ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: declared output 'inst_decoder.hpp' was not created by genrule. This is probably because the genrule actually didn't create this output, or because the output was a directory and the genrule was run remotely (note that only the contents of declared file outputs are copied from genrules run remotely). ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: declared output 'inst_operand.cpp' was not created by genrule. This is probably because the genrule actually didn't create this output, or because the output was a directory and the genrule was run remotely (note that only the contents of declared file outputs are copied from genrules run remotely). ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: declared output 'inst_mnemonic.cpp' was not created by genrule. This is probably because the genrule actually didn't create this output, or because the output was a directory and the genrule was run remotely (note that only the contents of declared file outputs are copied from genrules run remotely). ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: declared output 'inst_riscv_init.cpp' was not created by genrule. This is probably because the genrule actually didn't create this output, or because the output was a directory and the genrule was run remotely (note that only the contents of declared file outputs are copied from genrules run remotely). ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: declared output 'hogelog' was not created by genrule. This is probably because the genrule actually didn't create this output, or because the output was a directory and the genrule was run remotely (note that only the contents of declared file outputs are copied from genrules run remotely). ERROR: /home/vagrant/swimmer_iss/src/BUILD:1:1: not all outputs were created. Target //:inst_decoder failed to build INFO: Elapsed time: 4.076s, Critical Path: 0.09s make: *** [build] Error 1
うーん、実際にはスクリプトでファイルが生成されなかったって。
冷静に考えてみると、Bazelのビルドの仕組みがよくわかっていない。そもそもBazelは、ビルド時にビルド用のディレクトリを生成するようで、環境を移動してファイルを作成したらならば、生成したファイル群をどのように取り扱えばよいのか、そのあたりが全く理解できてない。
Bazel buildを実行すると、実際にはBazelにより生成された以下のディレクトリでビルド処理が行われるという訳だ。
:~/swimmer_iss/src$ ls -lt total 1020 lrwxrwxrwx 1 vagrant vagrant 108 May 8 03:45 bazel-bin -> /home/vagrant/.cache/bazel/_bazel_vagrant/b2de25cdaad74e7085e250210e26a5eb/src/bazel-out/local-fastbuild/bin lrwxrwxrwx 1 vagrant vagrant 113 May 8 03:45 bazel-genfiles -> /home/vagrant/.cache/bazel/_bazel_vagrant/b2de25cdaad74e7085e250210e26a5eb/src/bazel-out/local-fastbuild/genfiles lrwxrwxrwx 1 vagrant vagrant 88 May 8 03:45 bazel-out -> /home/vagrant/.cache/bazel/_bazel_vagrant/b2de25cdaad74e7085e250210e26a5eb/src/bazel-out lrwxrwxrwx 1 vagrant vagrant 78 May 8 03:45 bazel-src -> /home/vagrant/.cache/bazel/_bazel_vagrant/b2de25cdaad74e7085e250210e26a5eb/src lrwxrwxrwx 1 vagrant vagrant 113 May 8 03:45 bazel-testlogs -> /home/vagrant/.cache/bazel/_bazel_vagrant/b2de25cdaad74e7085e250210e26a5eb/src/bazel-out/local-fastbuild/testlogs -rw-rw-r-- 1 vagrant vagrant 581 May 7 17:38 BUILD
そう考えると、どのようにして自動生成ファイルを処理すればよいのか。これを解決するためにはlocationという記述子を活用すれば良さそうなのだが、これもいまいちよく分からない。
以下はBazelのマニュアルに書いてある例なのだが、リダイレクトを使うのならば、カスタムルール一つにつき一つしかファイル作れないよね?
http://bazel.io/docs/be/general.html#genrule
genrule( name = "foo", srcs = [], outs = ["foo.h"], cmd = "./$(location create_foo.pl) > \"$@\"", tools = ["create_foo.pl"], )
このあたりはまだ勉強する必要がある。実際にBazelを導入する必要があるかどうかも含めて。