Maven から Bazel への移行

7.3 · 7.2 · 7.1 · 7.0 · 6.5

このページでは、前提条件とインストール手順など、Maven から Bazel に移行する方法について説明します。Maven と Bazel の違いについて説明し、Guava プロジェクトを使用した移行例を示します。

ビルドツールから Bazel に移行する場合は、開発チーム、CI システム、その他の関連システムを完全に移行するまで、両方のビルドツールを並行して実行することをおすすめします。Maven と Bazel は同じリポジトリで実行できます。

始める前に

  • Bazel をインストールします(まだインストールしていない場合)。
  • Bazel を初めて使用する場合は、移行を開始する前に、Bazel の概要: Java のビルドのチュートリアルをご覧ください。このチュートリアルでは、Bazel のコンセプト、構造、ラベル構文について説明します。

Maven と Bazel の違い

  • Maven は最上位の pom.xml ファイルを使用します。Bazel は、複数のビルドファイルと BUILD ファイルあたりの複数のターゲットをサポートしているため、Maven よりも増分的なビルドが可能になります。
  • Maven がデプロイ プロセスの手順を管理します。Bazel はデプロイを自動化しません。
  • Bazel を使用すると、言語間の依存関係を表現できます。
  • プロジェクトに新しいセクションを追加した場合、Bazel で新しい BUILD ファイルの追加が必要になる場合があります。新しい Java パッケージごとに BUILD ファイルを追加することをおすすめします。

Maven から Bazel に移行する

プロジェクトを Bazel に移行する手順は次のとおりです。

  1. MODULE.bazel ファイルを作成する
  2. 1 つの BUILD ファイルを作成する
  3. BUILD ファイルをさらに作成する
  4. Bazel を使用してビルドする

以下の例は、Guava プロジェクトを Maven から Bazel に移行した場合のものです。使用される Guava プロジェクトはリリース v31.1 です。Guava を使用する例では、移行の各ステップについては説明しませんが、移行のために手動で生成または追加されたファイルとコンテンツは示しています。

$ git clone https://github.com/google/guava.git && cd guava
$ git checkout v31.1

1. MODULE.bazel ファイルを作成する

プロジェクトのルートに MODULE.bazel という名前のファイルを作成します。プロジェクトに外部依存関係がない場合、このファイルは空にできます。

プロジェクトがプロジェクトのディレクトリにないファイルまたはパッケージに依存している場合は、これらの外部依存関係を MODULE.bazel ファイルに指定します。rules_jvm_external を使用して、Maven からの依存関係を管理できます。このルールセットの使用方法については、README をご覧ください。

Guava プロジェクトの例: 外部依存関係

Guava プロジェクトの外部依存関係は、rules_jvm_external ルールセットを使用して一覧表示できます。

次のスニペットを MODULE.bazel ファイルに追加します。

bazel_dep(name = "rules_jvm_external", version = "6.2")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
    artifacts = [
        "com.google.code.findbugs:jsr305:3.0.2",
        "com.google.errorprone:error_prone_annotations:2.11.0",
        "com.google.j2objc:j2objc-annotations:1.3",
        "org.codehaus.mojo:animal-sniffer-annotations:1.20",
        "org.checkerframework:checker-qual:3.12.0",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)
use_repo(maven, "maven")

2. 1 つの BUILD ファイルを作成する

ワークスペースが定義され、外部依存関係(該当する場合)が一覧表示されたため、プロジェクトのビルド方法を記述する BUILD ファイルを作成する必要があります。1 つの pom.xml ファイルを持つ Maven とは異なり、Bazel では多くの BUILD ファイルを使用してプロジェクトをビルドできます。これらのファイルでは複数のビルドターゲットを指定し、Bazel で増分ビルドを生成できるようにします。

BUILD ファイルを段階的に追加します。まず、プロジェクトのルートに BUILD ファイルを 1 つ追加し、それを使用して Bazel を使用して初期ビルドを行います。次に、より詳細なターゲットを含む BUILD ファイルを追加して、ビルドを絞り込みます。

  1. MODULE.bazel ファイルと同じディレクトリにテキスト ファイルを作成し、BUILD という名前を付けます。

  2. この BUILD ファイルで、適切なルールを使用して、プロジェクトをビルドするターゲットを 1 つ作成します。次のヒントを参考にしてください。

    • 適切なルールを使用します。

      • 単一の Maven モジュールを使用してプロジェクトをビルドするには、次のように java_library ルールを使用します。

        java_library(
           name = "everything",
           srcs = glob(["src/main/java/**/*.java"]),
           resources = glob(["src/main/resources/**"]),
           deps = ["//:all-external-targets"],
        )
        
      • 複数の Maven モジュールを含むプロジェクトをビルドするには、次のように java_library ルールを使用します。

        java_library(
           name = "everything",
           srcs = glob([
                 "Module1/src/main/java/**/*.java",
                 "Module2/src/main/java/**/*.java",
                 ...
           ]),
           resources = glob([
                 "Module1/src/main/resources/**",
                 "Module2/src/main/resources/**",
                 ...
           ]),
           deps = ["//:all-external-targets"],
        )
        
      • バイナリをビルドするには、java_binary ルールを使用します。

        java_binary(
           name = "everything",
           srcs = glob(["src/main/java/**/*.java"]),
           resources = glob(["src/main/resources/**"]),
           deps = ["//:all-external-targets"],
           main_class = "com.example.Main"
        )
        
      • 属性を指定します。

        • name: ターゲットにわかりやすい名前を付けます。上記の例では、ターゲットは「Everything」と呼ばれます。
        • srcs: グロブを使用して、プロジェクト内のすべての .java ファイルを一覧表示します。
        • resources: グロブを使用して、プロジェクト内のすべてのリソースを一覧表示します。
        • deps: プロジェクトに必要な外部依存関係を決定する必要があります。
      • Guava プロジェクトの移行から、このトップレベルの BUILD ファイルの例をご覧ください。

  3. プロジェクトのルートに BUILD ファイルが作成されたので、プロジェクトをビルドして動作を確認します。コマンドラインから、ワークスペース ディレクトリで bazel build //:everything を使用して、Bazel でプロジェクトをビルドします。

    これで、Bazel によるプロジェクトのビルドが完了しました。プロジェクトのインクリメンタル ビルドを許可するには、さらに BUILD ファイルを追加する必要があります。

Guava プロジェクトの例: 1 つの BUILD ファイルから開始する

Guava プロジェクトを Bazel に移行する場合、最初は 1 つの BUILD ファイルを使用してプロジェクト全体をビルドします。ワークスペース ディレクトリにあるこの最初の BUILD ファイルの内容は次のとおりです。

java_library(
    name = "everything",
    srcs = glob([
        "guava/src/**/*.java",
        "futures/failureaccess/src/**/*.java",
    ]),
    javacopts = ["-XepDisableAllChecks"],
    deps = [
        "@maven//:com_google_code_findbugs_jsr305",
        "@maven//:com_google_errorprone_error_prone_annotations",
        "@maven//:com_google_j2objc_j2objc_annotations",
        "@maven//:org_checkerframework_checker_qual",
        "@maven//:org_codehaus_mojo_animal_sniffer_annotations",
    ],
)

3. 他の BUILD ファイルを作成する(省略可)

最初のビルドの完了後に確認したように、Bazel は 1 つの BUILD file で動作します。それでも、詳細なターゲットを持つ BUILD ファイルを追加して、ビルドを小さなチャンクに分割することを検討する必要があります。

複数のターゲットを含む複数の BUILD ファイルを使用すると、ビルドの粒度が細かくなり、次のことが可能になります。

  • プロジェクトの増分ビルドの増加、
  • ビルドの並列実行が増えたため
  • 将来のユーザー向けのビルドの保守性を高める。
  • パッケージ間のターゲットの可視性を制御できるため、実装の詳細を含むライブラリが公開 API に漏洩するなどの問題を防ぐことができます。

BUILD ファイルを追加する際のヒント:

  • まず、各 Java パッケージに BUILD ファイルを追加します。依存関係が最も少ない Java パッケージから、依存関係が最も多いパッケージまで順に移行します。
  • BUILD ファイルを追加してターゲットを指定する際は、これらの新しいターゲットを、それらに依存するターゲットの deps セクションに追加します。glob() 関数はパッケージの境界を越えないため、パッケージ数が増えると、glob() で一致するファイルは減少します。
  • BUILD ファイルを main ディレクトリに追加するときは、対応する test ディレクトリにも BUILD ファイルを追加してください。
  • パッケージ間の公開設定を適切に制限してください。
  • BUILD ファイルの設定で発生したエラーをトラブルシューティングしやすくするため、各ビルドファイルを追加する際に、Bazel を使用したプロジェクトのビルドを続行できるようにしてください。bazel build //... を実行して、すべてのターゲットが引き続きビルドされることを確認します。

4. Bazel を使用してビルドする

Bazel を使用してビルドし、BUILD ファイルを追加してビルドの設定を検証しています。

必要な粒度の BUILD ファイルが作成されたら、Bazel を使用してすべてのビルドを生成できます。