Bazelをひたすら試行して、Bazelの小難しい環境がなんとなく分かってきた。特に注意しなければならないのは、Bazelに含まれるBUILDファイルとWORKSPACEファイルの関係性だ。ここを理解するのには、かなり時間がかかった。
例として、以下のようなプロジェクトを作ってテストしてみた。
Bazelのプロジェクト内に、2つのアプリケーション(app1:実行ファイル、app2: ライブラリ)を用意した。 app1はapp2を使用するが、まずはapp2をライブラリとして作成し、それをリンクする。
. # bazel_test ├── app1 │ ├── app1.cpp │ └── BUILD ├── app2 │ ├── app2.cpp │ ├── app2.hpp │ └── BUILD └── WORKSPACE
このとき、WORKSPACEファイルは、各アプリケーションのディレクトリに配置されるのではなく、app1とapp2の上位ディレクトリに存在する。
これにより、コンパイルが開始されるときのルートディレクトリがどこになるのかが決定する。ちょっと見てみよう。少しエラーを仕込んで、Bazelが何をやっているのかを確認する。
$ cd app1 $ bazel build :app1 --verbose_failures INFO: Found 1 target... ERROR: /home/vagrant/work/sicp_exercise/bazel_test/app1/BUILD:1:1: C++ compilation of rule '//app1:app1' failed: namespace-sandbox failed: error executing command (cd /home/vagrant/.cache/bazel/_bazel_vagrant/bf6184562b9de4efbf42ada266fc0899/bazel_test && \ exec env - \
ふむ。app1のディレクトリにいるはずなのに、Bazelが移動したのはそれより上のディレクトリ、bazel_testディレクトリだ。つまり、ビルドを開始するときは、BazelはBUILDディレクトリの存在する場所ではなく、WORKSPACEディレクトリの存在する場所に移動する。
したがって、BUILDファイルの書き方も少しトリッキーだ。
- app1/BUILD
cc_binary ( name = "app1", srcs = ["app1.cpp"], deps = ["//app2"], copts = ["-Iapp2"], )
ソースファイルは、BUILDファイルの存在するapp1ディレクトリからの相対パスで指定してよい(厳密にはファイル名ではなくラベル名だが)。 depsで記述している依存パッケージはWORKSPACEディレクトリが存在する場所からの相対ディレクトリ、さらにcoptsでg++に渡されるオプションも、WORKSPACEディレクトリが存在する場所からの相対パスを記述すべきだ。
- app2/BUILD
cc_library ( name = "app2", srcs = ["app2.cpp"], hdrs = ["app2.hpp"], visibility = ["//visibility:public"], )
ビルドには成功する。
$ bazel build :app1 --verbose_failures INFO: Found 1 target... Target //app1:app1 up-to-date: bazel-bin/app1/app1 INFO: Elapsed time: 0.237s, Critical Path: 0.03s