このチュートリアルでは、Bazel
を使用して Java アプリケーションをビルドする際の基本について説明します。ワークスペースを設定し、Bazel の重要なコンセプトを示す簡単な Java プロジェクトをビルドします。たとえば、ターゲットや BUILD ファイルなどです。
所要時間の目安: 30 分
学習内容
このチュートリアルでは、以下の方法について学習します。
- ターゲットをビルドする
- プロジェクトの依存関係を可視化する
- プロジェクトを複数のターゲットとパッケージに分割する
- パッケージ間でターゲットの公開設定を制御する
- ラベルを使用してターゲットを参照する
- ターゲットをデプロイする
始める前に
Bazel をインストールする
チュートリアルを開始する前に、Bazel がまだインストールされていない場合は、Bazel をインストールします。
JDK をインストールする
Java JDK をインストールします(推奨バージョンは 11 ですが、8 ~ 15 のバージョンがサポートされています)。
JAVA_HOME 環境変数を設定して JDK をポイントします。
Linux/macOS の場合:
export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"Windows の場合:
- コントロール パネルを開きます。
- [システムとセキュリティ] > [システム] > [システムの詳細設定] > [詳細設定] タブ > [環境変数...] に移動します。。
- [ユーザー環境変数] リスト(上部)で [新規...] をクリックします。
- ["Variable name"] フィールドに
JAVA_HOMEと入力します。 - [ディレクトリの参照...] をクリックします。
- JDK ディレクトリ(
C:\Program Files\Java\jdk1.8.0_152など)に移動します。 - すべてのダイアログ ウィンドウで [OK] をクリックします。
サンプル プロジェクトを取得する
Bazel の GitHub リポジトリからサンプル プロジェクトを取得します。
git clone https://github.com/bazelbuild/examplesこのチュートリアルのサンプル プロジェクトは examples/java-tutorial
ディレクトリにあり、次のような構造になっています。
java-tutorial
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── MODULE.bazel
Bazel を使用したビルド
ワークスペースをセットアップする
プロジェクトをビルドする前に、ワークスペースを設定する必要があります。ワークスペースは プロジェクトのソースファイルと Bazel のビルド出力が格納されるディレクトリです。また、Bazel が特別なファイルとして認識するファイルも含まれています。
MODULE.bazelファイル。このファイルは、ディレクトリとその内容を Bazel ワークスペースとして識別し、プロジェクトのディレクトリ構造のルートに配置されます。1 つ以上の
BUILDファイル。このファイルは、プロジェクトのさまざまな部分を ビルドする方法を Bazel に指示します。(`BUILD` ファイル を含むワークスペース内のディレクトリは、パッケージです。パッケージについては、このチュートリアルの後半で説明します)。
ディレクトリを Bazel ワークスペースとして指定するには、そのディレクトリに
MODULE.bazel という名前の空のファイルを作成します。
Bazel がプロジェクトをビルドする場合、すべての入力と依存関係が同じ ワークスペースに存在する必要があります。異なるワークスペースにあるファイルは、リンクされていない限り相互に独立しています。リンクについては、このチュートリアルの範囲外です。
BUILD ファイルについて
BUILD ファイルには、Bazel 用のさまざまな種類の命令が含まれています。
最も重要なタイプはビルドルールです。
このルールは、実行可能バイナリやライブラリなど、必要な出力をビルドする方法を Bazel に指示します。`BUILD` ファイル内のビルドルールの各インスタンスは ターゲット と呼ばれ、
特定のソースファイルと依存関係のセットを指します。ターゲットは他の
ターゲットを指すこともできます。
java-tutorial/BUILD ファイルを見てみましょう。
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
この例では、ProjectRunner ターゲットは Bazel の組み込み
java_binary ルールをインスタンス化します。このルールは、Bazel に
.jar ファイルとラッパー シェル スクリプト(どちらもターゲットと同じ名前)をビルドするように指示します。
ターゲットの属性は、その依存関係とオプションを明示的に示します。
name 属性は必須ですが、多くは省略可能です。たとえば、
ProjectRunner ルール ターゲットでは、name はターゲットの名前、srcs は Bazel がターゲットのビルドに使用するソースファイル、main_class は main メソッドを含むクラスを指定します。(この例では、ソースファイルを 1 つずつリストするのではなく、glob を使用してソースファイルのセットを Bazel に渡しています)。
プロジェクトをビルドする
サンプル プロジェクトをビルドするには、java-tutorial ディレクトリ
に移動して、次のコマンドを実行します。
bazel build //:ProjectRunnerターゲット ラベルでは、// 部分はワークスペースのルートに対する BUILD ファイルの場所(この場合はルート自体)、
および ProjectRunner は BUILD ファイル内のターゲット名です。(ターゲット ラベルの詳細については、このチュートリアルの最後で
説明します)。
Bazel は次のような出力を生成します。
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 1.021s, Critical Path: 0.83s
これで最初の Bazel ターゲットがビルドされました。Bazel は、ビルド
出力をワークスペースのルートにある bazel-bin ディレクトリに配置します。その内容を参照して、Bazel の出力構造を確認してください。
新しくビルドしたバイナリをテストします。
bazel-bin/ProjectRunner依存関係グラフを確認する
Bazel では、ビルドの依存関係を BUILD ファイルで明示的に宣言する必要があります。 Bazel はこれらのステートメントを使用してプロジェクトの依存関係グラフを作成し、これによって正確な増分ビルドが可能になります。
サンプル プロジェクトの依存関係を可視化するには、ワークスペースのルートで次のコマンドを実行して、依存関係グラフのテキスト 表現を生成します。
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph上記のコマンドは、ターゲット
//:ProjectRunner のすべての依存関係(ホストと暗黙的な依存関係を除く)を検索し、
出力をグラフとしてフォーマットするように Bazel に指示します。
次に、テキストを GraphViz に貼り付けます。
ご覧のとおり、このプロジェクトには、追加の依存関係なしで 2 つのソースファイルをビルドする単一のターゲットがあります。
ワークスペースを設定し、プロジェクトをビルドして、その 依存関係を確認したら、複雑さを追加できます。
Bazel ビルドを改善する
小規模なプロジェクトでは単一のターゲットで十分ですが、大規模なプロジェクトでは、増分ビルドを高速化(変更された部分のみを再ビルド)し、プロジェクトの複数の部分を同時にビルドしてビルドを高速化するために、複数のターゲットとパッケージに分割することをおすすめします。
複数のビルド ターゲットを指定する
サンプル プロジェクトのビルドを 2 つのターゲットに分割できます。java-tutorial/BUILD ファイルの内容を次のように置き換えます。
java_binary(
name = "ProjectRunner",
srcs = ["src/main/java/com/example/ProjectRunner.java"],
main_class = "com.example.ProjectRunner",
deps = [":greeter"],
)
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
)
この構成では、Bazel は最初に greeter ライブラリをビルドし、次に
ProjectRunner バイナリをビルドします。java_binary の deps 属性は、ProjectRunner バイナリをビルドするには
greeter ライブラリが必要であることを Bazel に伝えます。
この新しいバージョンのプロジェクトをビルドするには、次のコマンドを実行します。
bazel build //:ProjectRunnerBazel は次のような出力を生成します。
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s
新しくビルドしたバイナリをテストします。
bazel-bin/ProjectRunnerここで ProjectRunner.java を変更してプロジェクトを再ビルドすると、Bazel は
そのファイルのみを再コンパイルします。
依存関係グラフを見ると、ProjectRunner は
以前と同じ入力に依存していますが、ビルドの構造が異なります。
これで、2 つのターゲットでプロジェクトをビルドしました。ProjectRunner ターゲットは 1 つのソースファイルをビルドし、もう 1 つのターゲット(:greeter)に依存します。このターゲットは 1 つの追加のソースファイルをビルドします。
複数のパッケージを使用する
次に、プロジェクトを複数のパッケージに分割します。
src/main/java/com/example/cmdline ディレクトリを見ると、
a BUILD ファイルといくつかのソースファイルが含まれています。したがって、Bazel にとって、ワークスペースには
2 つのパッケージ(//src/main/java/com/example/cmdline と //)が含まれています(ワークスペースのルートに
BUILD ファイルがあるため)。
src/main/java/com/example/cmdline/BUILD ファイルを見てみましょう。
java_binary(
name = "runner",
srcs = ["Runner.java"],
main_class = "com.example.cmdline.Runner",
deps = ["//:greeter"],
)
runner ターゲットは、// パッケージの greeter ターゲットに依存します(
ターゲット ラベル //:greeter)。Bazel は deps 属性を通じてこれを認識します。
依存関係グラフを見てみましょう。
ただし、ビルドを成功させるには、runner ターゲット
に //src/main/java/com/example/cmdline/BUILD のターゲットの公開設定を
//BUILD を使用して明示的に付与する必要があります。visibilityこれは、デフォルトでは、ターゲット
は同じ BUILD ファイル内の他のターゲットにのみ表示されるためです。(Bazel はターゲット
の公開設定を使用して、実装の詳細を含むライブラリが公開 API に漏洩するなどの問題を回避します)。
これを行うには、次のように
java-tutorial/BUILDのgreeterターゲットにvisibility属性を追加します。
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)
ワークスペースのルート で次のコマンドを実行して、新しいパッケージをビルドできます。
bazel build //src/main/java/com/example/cmdline:runnerBazel は次のような出力を生成します。
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner.jar
bazel-bin/src/main/java/com/example/cmdline/runner
INFO: Elapsed time: 1.576s, Critical Path: 0.81s
新しくビルドしたバイナリをテストします。
./bazel-bin/src/main/java/com/example/cmdline/runnerこれで、プロジェクトを変更して、それぞれに 1 つの ターゲットを含む 2 つのパッケージとしてビルドし、それらの間の依存関係を理解しました。
ラベルを使用してターゲットを参照する
BUILD ファイルとコマンドラインで、Bazel はターゲット ラベルを使用して
ターゲットを参照します(//:ProjectRunner や
//src/main/java/com/example/cmdline:runner など)。構文は次のとおりです。
//path/to/package:target-name
ターゲットがルール ターゲットの場合、path/to/package は BUILD ファイルを含む
ディレクトリのパス、target-name は BUILD ファイルでターゲットに付けた名前(name 属性)です。ターゲットがファイル
ターゲットの場合、path/to/package はパッケージのルートへのパス、および
target-name はターゲット ファイルの名前(フルパスを含む)です。
リポジトリのルートでターゲットを参照する場合、パッケージ パスは空です。
//:target-name を使用します。同じ BUILD
ファイル内のターゲットを参照する場合は、// ワークスペース ルート識別子を省略して、
:target-name のみを使用することもできます。
たとえば、java-tutorial/BUILD ファイル内のターゲットの場合、ワークスペースのルート自体がパッケージ(//)であり、
2 つのターゲット ラベルが単に //:ProjectRunner と //:greeter であったため、
パッケージ パスを指定する必要はありませんでした。
ただし、//src/main/java/com/example/cmdline/BUILD ファイル内のターゲットの場合は、//src/main/java/com/example/cmdline の完全なパッケージ パスを指定する必要があり、ターゲット ラベルは //src/main/java/com/example/cmdline:runner でした。
デプロイ用に Java ターゲットをパッケージ化する
次に、すべての ランタイム依存関係を含むバイナリをビルドして、デプロイ用に Java ターゲットをパッケージ化します。これにより、開発環境の外部でバイナリを実行できます。
ご存じのとおり、java_binary ビルドルール
は .jar とラッパー シェル スクリプトを生成します。次のコマンドを使用して、
runner.jar の内容を確認します。
jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar内容は次のとおりです。
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
ご覧のとおり、runner.jar には Runner.class が含まれていますが、その依存関係である
Greeting.class は含まれていません。Bazel が生成する runner スクリプトは、greeter.jar
をクラスパスに追加します。そのため、このままにしておくと、ローカルでは実行されますが、別のマシンでスタンドアロンで実行することはできません。幸いなことに、java_binary ルール
を使用すると、自己完結型のデプロイ可能なバイナリをビルドできます。ビルドするには、ターゲット名に
_deploy.jar を追加します。
bazel build //src/main/java/com/example/cmdline:runner_deploy.jarBazel は次のような出力を生成します。
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s
これで runner_deploy.jar がビルドされました。これには必要なランタイム依存関係が含まれているため、開発環境から離れてスタンドアロンで実行できます。前のコマンドと同じコマンドを使用して、このスタンドアロン JAR の内容を確認します。
jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar実行に必要なすべてのクラスが含まれています。
META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class
関連情報
詳しくは、以下をご覧ください。
推移的な Maven 依存関係を管理する ルールについては、rules_jvm_external をご覧ください。
ローカル リポジトリとリモート リポジトリの操作について詳しくは、 外部依存関係をご覧ください。
Bazel の詳細については、その他のルールをご覧ください。
Bazel を使用した C++ プロジェクトのビルドを開始するには、C++ ビルドのチュートリアルをご覧ください。
Bazel を使用して Android と iOS 用のモバイル アプリケーションのビルドを開始するには、Android アプリケーションのチュートリアルと iOS アプリケーションのチュートリアルをご覧ください。
ビルドをお楽しみください。