以前Bazelの入門の記事をかきましたが、今回はC++をビルドする環境を作成してみます。 spring-mt.hatenablog.com
下記のチュートリアル通りにやっていきます。 Build Tutorial - C++ - Bazel
まずはexampleをcloneしてやっていくのですが、前回やった残りのプロジェクトもあるのでサクッと自分でやっていきます。
【1】単一パッケージ、単一ターゲットでビルド
workspaceのセットアップを行います。
workspaceにはソースファイルとビルド成果物が含まれます。
Bazelのowrkspaceとして、トップディレクトリにWORKSPACE
ファイルを置きます。
今回は依存関係がないので、空ファイルで置いておきます。
プロジェクトをビルドするために、全てのインプットと依存関係を同一workspaceに含める必要があります。
異なるワークスペースに存在するファイルは、リンクしていなければ互いに独立しています。
BUILD
ファイルはソースコードなどビルドに必要なファイルやビルド方法、出力されるファイルなどの設定を記述するファイルです。
BUILD
ファイルを含むワークスペース内のディレクトリはパッケージ
という単位になります。
BUILD
ファイルにはBazelのさまざまな種類の設定を記述しますが、一番重要な設定はビルドルールです。
ビルドルールは、実行可能なバイナリやライブラリなど、どのような成果物を作成するかを記述します。
BUILD
ファイル内のビルドルールのそれぞれの実体はターゲット
と呼ばれ、ソース・ファイルと依存関係の特定のセットを指します。ターゲット
は他のターゲット郡を指すこともできます。
最初に下記構成にしてみました。
. ├── WORKSPACE └── main ├── BUILD └── hello-world.cc
まずは単純なBUILD
ファイルを見ていきます。
cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
name
で指定したパラメータがターゲット
名になります。(name
の指定は必須です。)
hello-world
ターゲットはcc_binaryルールをインスタンス化します。
このルールでは依存関係のないhello-world.ccソースファイルから自己完結型の実行可能バイナリをビルドします。
ではビルドしてみます。
ビルドコマンドは
bazel build //main:hello-world
//main:
はworkspaseのルートディレクトリからのBUILD
ファイルが配置されてる相対パスを示しています。hello-world
はBUILD
ファイル内で指定したname
です。
% bazel build //main:hello-world ..................... INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 12.816s, Critical Path: 3.27s % bazel build //main:hello-world INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 0.165s, Critical Path: 0.00s
(二回目はキャッシュが使われて高速にビルドできています)
ビルドが完了すると、workspaceのルート直下のbazel-bin
に成果物ができています。
% bazel-bin/main/hello-world Hello world Fri Aug 4 01:11:51 2017
依存性の確認
BUILD
ファイルにはビルドの依存関係がすべて明示的に記述されています。
Bazelはこれらのステートメントを使用してプロジェクトの依存関係グラフを作成し、正確なインクリメンタルビルドを可能にします。
前述のプロジェクトの依存性のグラフは下記コマンドで生成できます。
% bazel query --nohost_deps --noimplicit_deps 'deps(//main:hello-world)' --output graph digraph mygraph { node [shape=box]; "//main:hello-world" "//main:hello-world" -> "//main:hello-world.cc" "//main:hello-world.cc" }
グラフ構造はGraphViz で可視化できます。
【2】単一のパッケージ、複数のターゲット
大きなプロジェクトを複数のターゲットとパッケージに分割することで、変更した部分の再ビルドだけの高速なインクリメンタルビルドができ、分割されたプロジェクトを一度にビルドするとことでビルド時間の短縮ができます。
【1】プロジェクトを2つのターゲットに分けます。
diff --git a/CppExamples/main/BUILD b/CppExamples/main/BUILD index 20c6f47..43b22a4 100644 --- a/CppExamples/main/BUILD +++ b/CppExamples/main/BUILD @@ -1,4 +1,13 @@ +cc_library( + name = "hello-greet", + srcs = ["hello-greet.cc"], + hdrs = ["hello-greet.h"], +) + cc_binary( name = "hello-world", srcs = ["hello-world.cc"], + deps = [ + ":hello-greet", + ], )
hello-world
のリンクしたいライブラリのラベルをdeps
に指定します。
こうすると、hello-world
をビルドする前に、hello-greet
のビルドが実行されます。
% bazel build //main:hello-world INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 5.813s, Critical Path: 1.16s % bazel-bin/main/hello-world Hello world Sun Aug 6 15:23:47 2017
hello-greet.cc
が修正されたら、hello-greet.cc
のみコンパイルが行われます。
【3】複数のパッケージ、複数のターゲット
BUILD
ファイルを含む新しいディレクトリを作成します。
(BUILD
ファイルを含むワークスペース内のディレクトリはパッケージ
という単位)
cc_library( name = "hello-time", srcs = ["hello-time.cc"], hdrs = ["hello-time.h"], visibility = ["//main:__pkg__"], )
srcs
でソースファイルを、hdrs
でヘッダーファイルを指定します。
lib/BUILD
のなかで指定した// lib:hello-time
ターゲットをmain/ BUILD
から明示的に見えるようにするためにvisibility
で//main:__pkg__
を指定しています。
デフォルトでは、ターゲットは同じBUILD
ファイル内の他のターゲットに対してのみだけに公開されています。
Bazelは、ターゲットの可視性を利用して、ライブラリの実装の詳細がパブリックAPIに漏れることを防いでいます。
ビルドをしてみます。
% bazel build //main:hello-world INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 0.365s, Critical Path: 0.00s % bazel-bin/main/hello-world Hello world Sun Aug 6 22:01:01 2017
ラベルの使い方
BUILD
ファイルやコマンドライン上では、ラベルからターゲットを参照します。
シンタックスは下記の通り。
//path/to/package:target-name
ターゲットがruleのターゲットの場合、path/to/package
はBUILDファイルを含むディレクトリへのパスで、target-nameはBUILDファイルのターゲット(name
)の名前です。
ターゲットがファイルである場合、path/to/package
はパッケージのroot
からのパスであり、target-nameはターゲットファイル名です。
同じパッケージ内のターゲットを参照するときは、パッケージパスを省略して//:target-name
とだけで使えます。
同じBUILD
ファイル内のターゲットを参照する場合は、//
のworkspaceのルート識別子をスキップして、:target-name
だけを使用することができます。