CubicLouve

Spring_MTの技術ブログ

Bazel入門

Bazelとは

Googleが内部で利用していたビルドツールのオープンソース版として提供されているものです。

Bazel - a fast, scalable, multi-language and extensible build system" - Bazel

FAQ - Bazel

ビルドやテストの高速化を目指して作られています。

C++Javaだけでなく、iOSAndroidアプリのビルドにも対応しています。

今後のロードマップにはAndroid Studioとの統合も入っています。

Bazelを導入する理由

  • Gradleよりも構造的な設計になっていて、各アクションが何をするかが正確に理解しやすい構成になっている(らしい)。
  • すでに、ローカルでのキャッシュ、並列実行、依存性解析の最適化などが行われており高速に動作する。 さらに、分散キャッシュの仕組みも今絶賛入ろうとしていることろのなので、ビルドの高速化という意味では期待ができそう。

Bazelの公式ページも参照ください。

FAQ - Bazel

Bazelのinstall

Macであればhomebrew経由でインストールできます。

Installing Bazel on macOS - Bazel

homebrewでinstallすると最後に補完スクリプトのパスが表示されるのでそれを利用してzshの補完の設定をします。

Bash completion has been installed to:
  /usr/local/etc/bash_completion.d

zsh completions have been installed to:
  /usr/local/share/zsh/site-functions

Installing Bazel - Bazel

fpath[1,0]=~/.zsh/completion/
mkdir -p ~/.zsh/completion/
cp scripts/zsh_completion/_bazel ~/.zsh/completion

rm -f ~/.zcompdump; compinit

# This way the completion script does not have to parse Bazel's options
# repeatedly.  The directory in cache-path must be created manually.
zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path ~/.zsh/cache

Bazelでビルド

まずは簡単なチュートリアルを試してみます。

Getting Started - Bazel

WORKSPACEファイル

Bazelは、ビルドしたいソースコードとビルド成果物を含むworkspaceというディレクトリでビルドを行います。

必要に応じて、1つのworkspaceを複数のプロジェクトで共有することができます。

workspaceはどこでもよいのですが、トップディレクトリにWORKSPACEファイルを必ず置く必要があります。

このWORKSPACEファイルに外部の依存性の定義を行います。

依存がなければ空ファイルでも大丈夫です。

touch WORKSPACE

BUILDファイル

Bazelは、プロジェクト内でのビルドターゲットを知るために、BUILDファイルを使います。

BUILDファイルはPythonに似たBazelのビルド用言語で記述します。

BUILDファイルにいろいろなルールを書いていきます。

各ルールは、入力、出力、および入力からの出力を計算する方法を指定します。

genruleについて

シェルコマンドを呼び出すgenruleは、Makefileを使っている人たちにおなじみのルールです。

genruleの詳細は下記ページにまとまっています。

General Rules - Bazel

今回は単純にechoコマンドで標準出力に出した内容をリダイレクトしてファイルに書き込むルールを作ってみます。

genruleでは、 cmdで指定したコマンドが実行されます。

genrule(
  name = "hello",
  outs = ["hello_world.txt"],
  cmd = "echo Hello World > $@",
)

今回、cmdの中で使われている$@はBazelの中で使える変数で、outsが一つの場合において、outsの内容が展開されます。

利用可能な変数の一覧は Make variables にまとまっています。

ターゲットは、ルールの中で設定しているnameによって指定されるlabelになります。

また、Bazelの成果物はソースツリーを汚染しないようにするために、ソースツリーとは別のbazel-genfilesディレクトリに格納されます。

1回目

% bazel build :hello
....................
INFO: Found 1 target...
Target //:hello up-to-date:
  bazel-genfiles/hello_world.txt
INFO: Elapsed time: 7.322s, Critical Path: 0.01s

2回目

% bazel build :hello
INFO: Found 1 target...
Target //:hello up-to-date:
  bazel-genfiles/hello_world.txt
INFO: Elapsed time: 0.155s, Critical Path: 0.00s

% cat bazel-genfiles/hello_world.txt
Hello World

BUILDファイルにoutsを複数して$@を利用するとビルドエラーになります。

genrule(
  name = "hello",
  outs = ["hello_world.txt", "hello_world2.txt"],
  cmd = "echo Hello World > $@",
)
% bazel build :hello
ERROR: /Users/hoge/BazelTest/SampleEcho/BUILD:4:9: in cmd attribute of genrule rule //:hello: variable '$@' : more than one output file.
ERROR: Analysis of target '//:hello' failed; build aborted.
INFO: Elapsed time: 0.161s

ルールは、他のルールの出力を入力として利用できます。

生成したsrcはlabelから参照できます。

$<srcのファイルの変数です。

genrule(
  name = "hello",
  outs = ["hello_world.txt"],
  cmd = "echo Hello World > $@",
)

genrule(
  name = "double",
  srcs = [":hello"],
  outs = ["double_hello.txt"],
  cmd = "cat $< $< > $@",                                                                                                                               
)
% bazel build :double
INFO: Found 1 target...
Target //:double up-to-date:
  bazel-genfiles/double_hello.txt
INFO: Elapsed time: 0.396s, Critical Path: 0.04s
% cat bazel-genfiles/double_hello.txt
Hello World
Hello World

genruleですが、通常利用するためのものではないです。

それぞれの言語に特化したルールがあるので、それを使ったほうがよいです。

Build Encyclopedia - Bazel

参考にした記事

knowledge.sakura.ad.jp

blog.matsuokah.jp

Common C++ Build Use Cases - Bazel

github.com

github.com