Java と Bazel

問題を報告 ソースを表示

このページには、Java プロジェクトで Bazel を使用する場合に役立つリソースが記載されています。このページには、Bazel を使用した Java プロジェクトのビルドに固有のチュートリアル、ビルドルールなど、さまざまな情報へのリンクがあります。

Bazel の使用

Java プロジェクトで Bazel を使用する際は、次のリソースをご覧ください。

Bazel への移行

現在 Maven を使用して Java プロジェクトをビルドしている場合は、移行ガイドの手順に沿って、Bazel を使用した Maven プロジェクトのビルドを開始してください。

Java のバージョン

構成フラグが設定された Java には、次の 2 つの関連バージョンがあります。

  • リポジトリ内のソースファイルのバージョン
  • コードの実行とテストに使用される Java ランタイムのバージョン

リポジトリ内のソースコードのバージョンを構成する

Bazel では、追加の構成がない場合、リポジトリ内のすべての Java ソースファイルが単一の Java バージョンで記述されていると想定されます。リポジトリ内のソースのバージョンを指定するには、build --java_language_version={ver}.bazelrc ファイルに追加します。ここで、{ver}11 などになります。Bazel リポジトリのオーナーは、Bazel とそのユーザーがソースコードの Java バージョン番号を参照できるように、このフラグを設定する必要があります。詳細については、Java 言語バージョン フラグをご覧ください。

コードの実行とテストに使用する JVM を構成する

Bazel は、コンパイルに JDK を使用し、コードの実行とテストに別の JVM を使用します。

デフォルトでは、Bazel はダウンロードした JDK を使用してコードをコンパイルし、ローカルマシンにインストールされた JVM でコードを実行し、テストします。Bazel は、JAVA_HOME またはパスを使用して JVM を検索します。

生成されたバイナリは、システム ライブラリのローカルにインストールされた JVM と互換性があります。つまり、生成されるバイナリは、マシンにインストールされているものによって異なります。

実行とテストに使用する JVM を構成するには、--java_runtime_version フラグを使用します。デフォルト値は local_jdk です。

密閉型のテストとコンパイル

密閉コンパイルを作成するには、コマンドライン フラグ --java_runtime_version=remotejdk_11 を使用します。コードは、リモート リポジトリからダウンロードした JVM でコンパイル、実行、テストされます。詳細については、Java ランタイム バージョン フラグをご覧ください。

Java でのビルドツールのコンパイルと実行の設定

ツールのビルドと実行に使用される JDK と JVM のもう一つのペアがあります。これらはビルドプロセスでは使用されますが、ビルド結果には含まれません。この JDK と JVM は、--tool_java_language_version--tool_java_runtime_version を使用して制御されます。デフォルト値はそれぞれ 11remotejdk_11 です。

ローカルにインストールされた JDK を使用したコンパイル

Bazel は JDK の内部をオーバーライドするため、デフォルトではリモート JDK を使用してコンパイルされます。ローカルにインストールされた JDK を使用するコンパイル ツールチェーンは構成されていますが、使用されません。

ローカルにインストールされた JDK を使用してコンパイルする場合、つまりローカル JDK のコンパイル ツールチェーンを使用する場合は、追加のフラグ --extra_toolchains=@local_jdk//:all を使用します。ただし、任意のベンダーの JDK では動作しない場合があります。

詳細については、Java ツールチェーンの構成をご覧ください。

ベスト プラクティス

Bazel の一般的なベスト プラクティスに加えて、Java プロジェクトに固有のベスト プラクティスを以下に示します。

ディレクトリ構造

Maven の標準ディレクトリ レイアウト(src/main/java にあるソース、src/test/java にあるテスト)を優先します。

BUILD ファイル

BUILD ファイルを作成する際は、次のガイドラインに従ってください。

  • ビルドのパフォーマンスが向上するため、Java ソースを含むディレクトリごとに 1 つの BUILD ファイルを使用します。

  • すべての BUILD ファイルには、次のような java_library ルールが 1 つ含まれている必要があります。

    java_library(
        name = "directory-name",
        srcs = glob(["*.java"]),
        deps = [...],
    )
    
  • ライブラリの名前は、BUILD ファイルを含むディレクトリの名前にする必要があります。これにより、ライブラリのラベルが短くなり、"//package:package" ではなく "//package" が使用されます。

  • ソースは、ディレクトリ内のすべての Java ファイルの非再帰的 glob である必要があります。

  • テストは src/test の下の一致するディレクトリに配置し、このライブラリに依存する必要があります。

高度な Java ビルド用の新しいルールの作成

: 新しいルールの作成は、高度なビルドとテストのシナリオを対象とします。Bazel の使用を開始する場合は不要です。

以下のモジュール、構成フラグメント、プロバイダは、Java プロジェクトのビルド時に Bazel の機能を拡張するのに役立ちます。

Java ツールチェーンの構成

Bazel では、次の 2 種類の Java ツールチェーンを使用します。 - 実行(Java バイナリの実行とテストに使用され、--java_runtime_version フラグで制御) - コンパイル(Java ソースのコンパイルに使用され、 --java_language_version フラグで制御)

追加の実行ツールチェーンの構成

実行ツールチェーンはローカルまたはリポジトリの JVM であり、そのバージョン、オペレーティング システム、CPU アーキテクチャに関する追加情報があります。

Java 実行ツールチェーンは、WORKSPACE ファイルで local_java_repository ルールまたは remote_java_repository ルールを使用して追加できます。ルールを追加すると、フラグを使用して JVM が使用可能になります。同じオペレーティング システムと CPU アーキテクチャの定義が複数ある場合は、最初の定義が使用されます。

ローカル JVM の構成例:

load("@bazel_tools//tools/jdk:local_java_repository.bzl", "local_java_repository")

local_java_repository(
  name = "additionaljdk",          # Can be used with --java_runtime_version=additionaljdk, --java_runtime_version=11 or --java_runtime_version=additionaljdk_11
  version = 11,                    # Optional, if not set it is autodetected
  java_home = "/usr/lib/jdk-15/",  # Path to directory containing bin/java
)

リモート JVM の構成例:

load("@bazel_tools//tools/jdk:remote_java_repository.bzl", "remote_java_repository")

remote_java_repository(
  name = "openjdk_canary_linux_arm",
  prefix = "openjdk_canary", # Can be used with --java_runtime_version=openjdk_canary_11
  version = "11",            # or --java_runtime_version=11
  target_compatible_with = [ # Specifies constraints this JVM is compatible with
    "@platforms//cpu:arm",
    "@platforms//os:linux",
  ],
  urls = ...,               # Other parameters are from http_repository rule.
  sha256 = ...,
  strip_prefix = ...
)

追加のコンパイル ツールチェーンの構成

コンパイル ツールチェーンは、Bazel がコンパイル時に使用する JDK と複数のツールで構成され、Error Prone、厳格な Java 依存関係、ヘッダー コンパイル、Android の desugar、カバレッジ インストルメンテーション、IDE の genclass 処理などの追加機能を提供します。

JavaBuilder は、コンパイルを実行する Bazel にバンドルされたツールで、前述の機能を提供します。実際のコンパイルは、JDK の内部コンパイラを使用して実行されます。コンパイルに使用される JDK は、ツールチェーンの java_runtime 属性で指定されます。

Bazel は一部の JDK 内部をオーバーライドします。JDK バージョン 9 より後の場合、java.compiler モジュールと jdk.compiler モジュールには、JDK のフラグ --patch_module を使用してパッチが適用されます。JDK バージョン 8 の場合、Java コンパイラには -Xbootclasspath フラグを使用してパッチが適用されます。

VanillaJavaBuilder は JavaBuilder の 2 番目の実装です。JDK の内部コンパイラを変更せず、追加機能も備えていません。VanillaJavaBuilder は、組み込みツールチェーンでは使用されません。

Bazel は、JavaBuilder 以外にもコンパイル時に複数のツールを使用します。

ijar ツールは、jar ファイルを処理して呼び出し署名を除くすべてを削除します。生成される JAR はヘッダー JAR と呼ばれます。関数の本体が変更されたときにダウンストリームの依存先のみを再コンパイルすることで、コンパイルのインクリメンタリティを改善するために使用されます。

singlejar ツールは、複数の jar ファイルを 1 つにまとめます。

genclass ツールは、Java コンパイルの出力を後処理し、アノテーション プロセッサによって生成されたソースのクラスファイルのみを含む jar を生成します。

JacocoRunner ツールは、インストゥルメント化されたファイルに対して Jacoco を実行し、結果を LCOV 形式で出力します。

TestRunner ツールは、制御された環境で JUnit 4 テストを実行します。

コンパイルを再構成するには、default_java_toolchain マクロを BUILD ファイルに追加し、register_toolchains ルールを WORKSPACE ファイルに追加するか、--extra_toolchains フラグを使用して登録します。

ツールチェーンは、source_version 属性が --java_language_version フラグで指定された値と一致する場合にのみ使用されます。

ツールチェーン構成の例:

load(
  "@bazel_tools//tools/jdk:default_java_toolchain.bzl",
  "default_java_toolchain", "DEFAULT_TOOLCHAIN_CONFIGURATION", "BASE_JDK9_JVM_OPTS", "DEFAULT_JAVACOPTS"
)

default_java_toolchain(
  name = "repository_default_toolchain",
  configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,        # One of predefined configurations
                                                          # Other parameters are from java_toolchain rule:
  java_runtime = "@bazel_tools//tools/jdk:remote_jdk11", # JDK to use for compilation and toolchain's tools execution
  jvm_opts = BASE_JDK9_JVM_OPTS + ["--enable_preview"],   # Additional JDK options
  javacopts = DEFAULT_JAVACOPTS + ["--enable_preview"],   # Additional javac options
  source_version = "9",
)

これは、--extra_toolchains=//:repository_default_toolchain_definition を使用するか、ワークスペースに register_toolchains("//:repository_default_toolchain_definition") を追加することで使用できます。

事前定義された構成:

  • DEFAULT_TOOLCHAIN_CONFIGURATION: すべての機能、9 以上の JDK バージョンをサポート
  • VANILLA_TOOLCHAIN_CONFIGURATION: 追加機能なし。任意のベンダーの JDK をサポートします。
  • PREBUILT_TOOLCHAIN_CONFIGURATION: デフォルトと同じですが、ビルド済みツールのみを使用します(ijarsinglejar
  • NONPREBUILT_TOOLCHAIN_CONFIGURATION: デフォルトと同じですが、すべてのツールはソースからビルドされます(これは、libc が異なるオペレーティング システムで有用です)。

JVM コンパイラ フラグと Java コンパイラ フラグの構成

JVM フラグと javac フラグは、フラグまたは default_java_toolchain 属性で構成できます。

関連するフラグは、--jvmopt--host_jvmopt--javacopt--host_javacopt です。

関連する default_java_toolchain 属性は、javacoptsjvm_optsjavabuilder_jvm_optsturbine_jvm_opts です。

パッケージ固有の Java コンパイラ フラグの構成

default_java_toolchainpackage_configuration 属性を使用すると、特定のソースファイルにさまざまな Java コンパイラ フラグを構成できます。以下の例を参照してください。

load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain")

# This is a convenience macro that inherits values from Bazel's default java_toolchain
default_java_toolchain(
    name = "toolchain",
    package_configuration = [
        ":error_prone",
    ],
    visibility = ["//visibility:public"],
)

# This associates a set of javac flags with a set of packages
java_package_configuration(
    name = "error_prone",
    javacopts = [
        "-Xep:MissingOverride:ERROR",
    ],
    packages = ["error_prone_packages"],
)

# This is a regular package_group, which is used to specify a set of packages to apply flags to
package_group(
    name = "error_prone_packages",
    packages = [
        "//foo/...",
        "-//foo/bar/...", # this is an exclusion
    ],
)

単一のリポジトリ内に複数のバージョンの Java ソースコード

Bazel は、ビルド内での Java ソースの単一バージョンのコンパイルのみをサポートしています。つまり、Java のテストまたはアプリケーションのビルドでは、すべての依存関係が同じ Java バージョンに対してビルドされます。

ただし、異なるフラグを使用して別々のビルドを実行することもできます。

さまざまなフラグを使用するタスクを簡単にするために、特定のバージョンのフラグのセットを .bazelrc 構成ファイルでグループ化できます。

build:java8 --java_language_version=8
build:java8 --java_runtime_version=local_jdk_8
build:java11 --java_language_version=11
build:java11 --java_runtime_version=remotejdk_11

これらの構成ファイルは、--config フラグとともに使用できます(例: bazel test --config=java11 //:java11_test)。