Maven から Bazel への移行

問題を報告する ソースを表示

このページでは、前提条件とインストール手順を含め、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. WORKSPACE ファイルを作成する
  2. BUILD ファイルを 1 つ作成する
  3. その他の BUILD ファイルを作成する
  4. Bazel を使用したビルド

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

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

1. WORKSPACE ファイルを作成する

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

プロジェクトがプロジェクトのどのディレクトリにもないファイルやパッケージに依存している場合は、ワークスペース ファイルでこれらの外部依存関係を指定します。ワークスペース ファイルの外部依存関係のリスティングを自動化するには、rules_jvm_external を使用します。このルールセットの使用方法については、README をご覧ください。

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

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

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

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

RULES_JVM_EXTERNAL_TAG = "4.3"
RULES_JVM_EXTERNAL_SHA = "6274687f6fc5783b589f56a2f1ed60de3ce1f99bc4e8f9edef3de43bdf7c6e74"

http_archive(
    name = "rules_jvm_external",
    sha256 = RULES_JVM_EXTERNAL_SHA,
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:defs.bzl", "maven_install")

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",
    ],
)

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

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

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

  1. WORKSPACE ファイルと同じディレクトリにテキスト ファイルを作成し、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: ターゲットにわかりやすい名前を付けます。上記の例では、ターゲットは「すべて」と呼ばれます。
      • srcs: globbing を使用して、プロジェクト内のすべての .java ファイルを一覧表示します。
      • resources: globbing を使用して、プロジェクト内のすべてのリソースを一覧表示します。
      • deps: プロジェクトに必要な外部依存関係を決定する必要があります。たとえば、generate_workspace ツールを使用して外部依存関係のリストを生成した場合、java_library の依存関係は、generated_java_libraries マクロにリストされているライブラリになります。
    • 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",
    ]),
    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 ディレクトリに追加する場合は、必ず BUILD ファイルを対応する test ディレクトリに追加してください。
  • パッケージ間での公開設定を適切に制限してください。
  • BUILD ファイルのセットアップ時のエラーのトラブルシューティングを簡素化するには、各ビルドファイルを追加する際に、Bazel で引き続きプロジェクトがビルドされるようにしてください。bazel build //... を実行して、すべてのターゲットがまだビルドされていることを確認します。

4. Bazel を使用したビルド

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

目的の粒度の BUILD ファイルがある場合は、Bazel を使用してすべてのビルドを生成できます。