はじめに
Bazel を初めて使用する場合このページでその方法を確認できます。Bazel の使用方法の簡単な概要については、この最初のビルドのチュートリアルをご覧ください。このチュートリアルでは、Bazel のコンテキストで使用される重要な用語を定義し、Bazel ワークフローの基本について説明します。必要なツールから始めて、複雑さを増していく 3 つのプロジェクトを構築して実行し、それらがどのように、なぜ複雑になっていくのかを学びます。
Bazel は多言語ビルドをサポートするビルドシステムですが、このチュートリアルでは C++ プロジェクトを例として使用し、ほとんどの言語に適用される一般的なガイドラインとフローを提供します。
推定所要時間: 30 分。
前提条件
まだインストールしていない場合は、まず Bazel をインストールします。このチュートリアルではソース管理に Git を使用するため、最良の結果を得るには Git もインストールしてください。
次に、任意のコマンドライン ツールで次のコマンドを実行して、Bazel の GitHub リポジトリからサンプル プロジェクトを取得します。
git clone https://github.com/bazelbuild/examples
このチュートリアルのサンプル プロジェクトは examples/cpp-tutorial
ディレクトリにあります。
構造は次のとおりです。
examples
└── cpp-tutorial
├──stage1
│ ├── main
│ │ ├── BUILD
│ │ └── hello-world.cc
│ └── MODULE.bazel
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── MODULE.bazel
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── MODULE.bazel
ファイルは 3 つのセットに分かれており、各セットはこのチュートリアルのステージを表しています。最初のステージでは、単一のパッケージに存在する単一のターゲットをビルドします。第 2 段階では、単一のパッケージからバイナリとライブラリの両方をビルドします。第 3 段階(最終段階)では、複数のパッケージを含むプロジェクトを構築し、複数のターゲットでビルドします。
概要: はじめに
Bazel(および Git)をインストールし、このチュートリアルのリポジトリのクローンを作成することで、Bazel を使用した最初のビルドの基盤が整いました。次のセクションに進んで、いくつかの用語を定義し、ワークスペースを設定します。
スタートガイド
プロジェクトをビルドする前に、ワークスペースを設定する必要があります。ワークスペースは、プロジェクトのソースファイルと Bazel のビルド出力を保持するディレクトリです。また、次の重要なファイルも含まれています。
MODULE.bazel
ファイル。ディレクトリとそのコンテンツを Bazel ワークスペースとして識別し、プロジェクトのディレクトリ構造のルートに存在します。外部依存関係を指定する場所でもあります。- 1 つ以上の
BUILD
ファイル。プロジェクトのさまざまな部分をビルドする方法を Bazel に指示します。BUILD
ファイルを含むワークスペース内のディレクトリは、パッケージです。(パッケージについては、このチュートリアルの後半で詳しく説明します)。
今後のプロジェクトで、ディレクトリを Bazel ワークスペースとして指定するには、そのディレクトリに MODULE.bazel
という名前の空のファイルを作成します。このチュートリアルでは、各ステージに MODULE.bazel
ファイルがすでに存在します。
BUILD ファイルを理解する
BUILD
ファイルには、Bazel のさまざまな種類の命令が含まれています。各 BUILD
ファイルには、実行可能バイナリやライブラリなど、必要な出力を Bazel に構築方法を指示する一連の命令として、少なくとも 1 つのルールが必要です。BUILD
ファイルのビルドルールの各インスタンスはターゲットと呼ばれ、特定のソースファイルと依存関係のセットを指します。ターゲットは他のターゲットを指すこともできます。
cpp-tutorial/stage1/main
ディレクトリの BUILD
ファイルを見てください。
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
この例では、hello-world
ターゲットは Bazel の組み込みの cc_binary
ルールをインスタンス化します。このルールは、依存関係のない hello-world.cc
> ソースファイルから自己完結型の実行可能バイナリをビルドするように Bazel に指示します。
概要: スタートガイド
これで、いくつかの重要な用語と、このプロジェクトと Bazel の一般的なコンテキストにおけるそれらの意味について理解できました。次のセクションでは、プロジェクトのステージ 1 をビルドしてテストします。
ステージ 1: 単一のターゲット、単一のパッケージ
プロジェクトの最初の部分を構築しましょう。視覚的な参考資料として、プロジェクトのステージ 1 セクションの構造は次のとおりです。
examples
└── cpp-tutorial
└──stage1
├── main
│ ├── BUILD
│ └── hello-world.cc
└── MODULE.bazel
次のコマンドを実行して、cpp-tutorial/stage1
ディレクトリに移動します。
cd cpp-tutorial/stage1
続いて、次のコマンドを実行します。
bazel build //main:hello-world
ターゲット ラベルの //main:
部分は、ワークスペースのルートに対する BUILD
ファイルの場所です。hello-world
は、BUILD
ファイル内のターゲット名です。
Bazel は次のような出力を生成します。
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s
最初の Bazel ターゲットがビルドされました。Bazel は、ビルド出力をワークスペースのルートにある bazel-bin
ディレクトリに配置します。
新しくビルドしたバイナリをテストします。
bazel-bin/main/hello-world
これにより、「Hello world
」というメッセージが印刷されます。
ステージ 1 の依存関係グラフは次のとおりです。
概要: ステージ 1
最初のビルドが完了したので、ビルドの構造について基本的な考え方を理解できたはずです。次のステージでは、別のターゲットを追加して複雑さを増します。
ステージ 2: 複数のビルド ターゲット
小規模なプロジェクトでは 1 つのターゲットで十分ですが、大規模なプロジェクトでは複数のターゲットとパッケージに分割することをおすすめします。これにより、高速な増分ビルド(Bazel は変更されたものだけを再ビルドします)が可能になり、プロジェクトの複数の部分を一度にビルドすることでビルドが高速化されます。このチュートリアルのこのステージではターゲットを追加し、次のステージではパッケージを追加します。
ステージ 2 で使用するディレクトリは次のとおりです。
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── MODULE.bazel
cpp-tutorial/stage2/main
ディレクトリの BUILD
ファイルを見てください。
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",
],
)
この BUILD
ファイルを使用すると、Bazel はまず hello-greet
ライブラリ(Bazel の組み込みの cc_library
ルールを使用)をビルドし、次に hello-world
バイナリをビルドします。hello-world
ターゲットの deps
属性は、hello-world
バイナリのビルドに hello-greet
ライブラリが必要であることを Bazel に伝えます。
この新しいバージョンのプロジェクトをビルドする前に、ディレクトリを変更する必要があります。次のコマンドを実行して、cpp-tutorial/stage2
ディレクトリに切り替えます。
cd ../stage2
次のコマンドを使用して、新しいバイナリをビルドできます。
bazel build //main:hello-world
Bazel は、次のような出力を生成します。
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s
これで、新しくビルドしたバイナリをテストできます。別の「Hello
world
」が返されます。
bazel-bin/main/hello-world
ここで hello-greet.cc
を変更してプロジェクトを再ビルドすると、Bazel はそのファイルのみを再コンパイルします。
依存関係グラフを見ると、hello-world
が hello-greet
という名前の追加の入力に依存していることがわかります。
概要: ステージ 2
これで、2 つのターゲットを含むプロジェクトが作成されました。hello-world
ターゲットは 1 つのソースファイルをビルドし、2 つの追加ソースファイルをビルドする別のターゲット(//main:hello-greet
)に依存します。次のセクションでは、さらに一歩進んで別のパッケージを追加します。
ステージ 3: 複数のパッケージ
次の段階では、さらに複雑なレイヤを追加し、複数のパッケージを含むプロジェクトを構築します。cpp-tutorial/stage3
ディレクトリの構造と内容を確認します。
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── MODULE.bazel
2 つのサブディレクトリがあり、それぞれに BUILD
ファイルが含まれていることがわかります。したがって、Bazel から見ると、ワークスペースには lib
と main
の 2 つのパッケージが含まれています。
lib/BUILD
ファイルを確認します。
cc_library(
name = "hello-time",
srcs = ["hello-time.cc"],
hdrs = ["hello-time.h"],
visibility = ["//main:__pkg__"],
)
main/BUILD
ファイルでは、次のようにします。
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",
"//lib:hello-time",
],
)
メイン パッケージの hello-world
ターゲットは、lib
パッケージの hello-time
ターゲットに依存します(ターゲット ラベル //lib:hello-time
)。Bazel は、deps
属性を通じてこれを認識します。依存関係グラフに反映されている様子は次のとおりです。
ビルドを成功させるには、可視性属性を使用して、lib/BUILD
の //lib:hello-time
ターゲットを main/BUILD
のターゲットに明示的に表示します。これは、デフォルトでは、ターゲットは同じ BUILD
ファイル内の他のターゲットにのみ表示されるためです。Bazel はターゲットの可視性を使用して、実装の詳細を含むライブラリが公開 API にリークするなどの問題を回避します。
このプロジェクトの最終バージョンをビルドします。次のコマンドを実行して、cpp-tutorial/stage3
ディレクトリに切り替えます。
cd ../stage3
再度、次のコマンドを実行します。
bazel build //main:hello-world
Bazel は次のような出力を生成します。
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s
このチュートリアルの最後のバイナリをテストして、最終的な Hello world
メッセージを確認します。
bazel-bin/main/hello-world
概要: ステージ 3
これで、プロジェクトを 3 つのターゲットを含む 2 つのパッケージとしてビルドし、それらの間の依存関係を理解しました。これにより、Bazel を使用して今後のプロジェクトをビルドする準備が整いました。次のセクションでは、Bazel の学習を継続する方法について説明します。
次のステップ
これで、Bazel を使用した最初の基本的なビルドが完了しましたが、これはほんの始まりにすぎません。Bazel の学習を続けるためのリソースをいくつかご紹介します。
- C++ に焦点を当て続けるには、一般的な C++ ビルドのユースケースをご覧ください。
- Bazel を使用して他のアプリケーションのビルドを開始するには、Java、Android アプリケーション、iOS アプリケーションのチュートリアルをご覧ください。
- ローカル リポジトリとリモート リポジトリの操作について詳しくは、外部依存関係をご覧ください。
- Bazel のその他のルールの詳細については、こちらのリファレンス ガイドをご覧ください。
Happy building!