rules_jvm_external

repository·master·Indexed 18 days ago

https://github.com/bazel-contrib/rules_jvm_external

Bazel rules providing transitive Maven artifact resolution and publishing. It enables management of Java and Android dependencies using standard Maven coordinates, integrating with Bazel's caching and downloading mechanisms. Key features include Bzlmod support, dependency pinning via JSON lock files, Maven BOM support, and tools for handling private repositories, version conflicts, and artifact exclusions.

Tokens
15.4K
Snippets
47
Records
60
Agent score
61%

What's inside rules_jvm_external

  1. Understand Bzlmod module dependency layering

    master

    The rules_jvm_external Bzlmod extension collects artifacts from all tags with the same name attribute (defaulting to maven) across different Bzlmod modules and resolves them together.

    Key Behaviors:

    • Namespace Contributions: If multiple modules contribute to the same maven repository, a message will inform you which modules are involved. You can suppress this warning by adding the contributing modules to the known_contributing_modules attribute of the install tag.
    • Version Conflicts: If different modules request different versions of the same artifact, the resolver uses the highest version found in the root and sub-modules.
    • Resolution Warnings: If the root module's version is not the highest, you will see a warning during repinning. To fix this, either update the version in your root MODULE.bazel to the highest version or set force_version = True in the root module.
  2. Configure the dependency resolver in maven_install

    master

    The maven_install rule supports different resolvers via the resolver attribute.

    • Coursier (Default): Fast, but cannot handle resolutions requiring Maven BOMs.
    • Maven: Requires a lock file. Uses $HOME/.m2/repository as a source and $HOME/.netrc for credentials.
    • Gradle (Experimental): Requires a lock file. Uses $HOME/.gradle caches.
  3. Manage multiple Maven installations

    master

    If your project requires multiple distinct sets of Maven dependencies, you can declare multiple maven.install blocks. Each must have a unique name and its own lock_file to avoid conflicts.

    maven.install(
        name = "foo",
        lock_file = "//:foo_maven_install.json",
        # ...
    )
    
    maven.install(
        name = "bar",
        lock_file = "//:bar_maven_install.json",
        # ...
    )
  4. Create isolated artifact version trees with multiple maven_install declarations

    master

    If different components of your project require different versions of the same artifact (e.g., a JRE version of Guava for a server and an Android version for an app), you can declare multiple maven.install blocks in MODULE.bazel using unique repository names.

    # In MODULE.bazel
    maven.install(
        name = "server_app",
        artifacts = ["com.google.guava:guava:27.0-jre"],
    )
    
    maven.install(
        name = "android_app",
        artifacts = ["com.google.guava:guava:27.0-android"],
    )
    
    # In BUILD files
    java_binary(
        name = "my_server_app",
        deps = ["@server_app//:com_google_guava_guava"],
    )
    
    android_binary(
        name = "my_android_app",
        deps = ["@android_app//:com_google_guava_guava_android"],
    )
  5. Use the `maven` Bzlmod extension

    master

    To manage Maven dependencies in a Bzlmod-enabled Bazel project, use the maven extension from @rules_jvm_external. You must first declare the extension in your MODULE.bazel file and then call its various tag classes to configure your dependencies.

    Available tag classes include:

    • maven.install: The primary configuration point for repositories, lock files, and artifact lists.
    • maven.artifact: Defines individual artifacts with specific metadata.
    • maven.amend_artifact: Modifies existing artifacts (e.g., adding exclusions or forcing versions).
    • maven.from_toml: Imports dependencies from a Gradle libs.versions.toml file.
    • maven.override: Redirects specific Maven coordinates to a different Bazel target.
    maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
  6. Exclude Maven artifacts in Bzlmod

    master

    In Bzlmod, you cannot 'inline' exclusions within a maven.install(artifacts = [...]) list. Attempting to do so will cause a type error. Instead, use one of the following two methods:

    Method 1: Use maven.artifact

    Split the specific artifact into its own maven.artifact declaration to apply exclusions.

    maven.artifact(
        artifact = "grpc-core",
        exclusions = ["io.grpc:grpc-util"],
        group = "io.grpc",
        version = "1.58.0",  # Must stay in sync with the version used in maven.install
    )
    
    maven.install(
        artifacts = [
            "junit:junit:4.13.2",
            # ...
        ],
    )

    Method 2: Use maven.amend_artifact

    Use amend_artifact to modify an artifact that was declared elsewhere (e.g., in an install or from_toml tag). Matching is performed using the group:artifact tuple.

    # Modifying an existing declaration
    maven.amend_artifact(
        coordinates = "io.grpc:grpc-core",
        exclusions = ["io.grpc:grpc-util"],
    )
  7. Create a deployable Spring Boot jar

    master

    The default Bazel build for Spring Boot launches the application by constructing a classpath from all dependency jars. This is suitable for development but not for production environments where the Bazel workspace is unavailable.

    To package the Spring Boot application as a single, self-contained, deployable .jar file for production use, use the Spring Boot rule for Bazel instead of the standard Bazel build process.

  8. Install rules_jvm_external with Bzlmod

    master

    To use rules_jvm_external with Bzlmod (required for Bazel 7+), add the dependency and the maven extension to your MODULE.bazel file. You can define dependencies using maven.install and provide specific overrides or additional options using maven.artifact. The maven.install and maven.artifact tags are merged automatically.

    After installation, you must generate a lockfile to pin transitive dependencies.

    Note on pinning: Due to a known issue, the generated lockfile name is long and non-standard. You must manually rename it to maven_install.json before referencing it in your MODULE.bazel.

    bazel_dep(name = "rules_jvm_external", version = "...")
    maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
    
    maven.install(
        artifacts = [
            "org.seleniumhq.selenium:selenium-java:4.4.0",
        ],
    )
    
    # Individual artifact overrides
    maven.artifact(
        artifact = "javapoet",
        group = "com.squareup",
        neverlink = True,
        version = "1.11.1",
    )
    
    use_repo(maven, "maven")

    Pinning workflow:

    1. Run the pin program:
    $ bazel run @maven//:pin
    1. Rename the resulting file:
    $ mv rules_jvm_external~4.5~maven~maven_install.json maven_install.json
    1. Update MODULE.bazel to use the lockfile:
    maven.install(
        ...
        lock_file = "//:maven_install.json",
    )
  9. Declare dependencies from a Gradle Version Catalog

    master

    You can import dependencies from a Gradle libs.versions.toml file using the maven.from_toml tag. This allows you to share dependency definitions between Gradle and Bazel.

    rules_jvm_external supports several extended fields in the TOML file that are not part of the standard Gradle format. These must be provided as quoted strings within the inline table.

    # In MODULE.bazel
    maven.from_toml(
        libs_versions_toml = "//gradle:libs.versions.toml",
    )

    Example libs.versions.toml with extended fields:

    [versions]
    junitJupiter = "5.12.2"
    
    [libraries]
    guava = { module = "com.google.guava:guava" }
    guavaBom = { module = "com.google.guava:guava-bom", version = "33.4.8-jre", is_bom = "true" }
    junitApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junitJupiter" }
    clickhouse = { module = "com.clickhouse:clickhouse-jdbc", version = "0.9.2", classifier = "all", force_version = "true" }
    misk = { module = "com.squareup.misk:misk-core", version = "1.0.0", exclusions = "['*:*']" }
  10. Enable IPv6 support for dependency downloads

    master

    If your environment requires IPv6, you must configure both Bazel's native downloader and the Coursier downloader.

    1. For Bazel's native downloader, add the following to your .bazelrc: startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

    2. For Coursier, set the COURSIER_OPTS environment variable: COURSIER_OPTS="-Djava.net.preferIPv6Addresses=true"

  11. Pin Maven dependencies with a lock file

    master

    To ensure repeatable builds and faster resolution, you should "pin" your dependencies into a JSON lock file. This allows Bazel to use its downloader and cache via SHA-256 checksums, enabling offline builds.

    Follow these steps:

    1. Add the lock_file attribute to your maven.install call in MODULE.bazel (e.g., lock_file = "//:maven_install.json").
    2. Create the required files in your workspace root:
      touch maven_install.json BUILD.bazel
    3. Generate the initial lock file:
      bazel run @maven//:pin

    Updating the lock file: Whenever you modify artifacts or repositories in MODULE.bazel, you must re-pin to update the lock file. If you have set fail_if_repin_required = True, the build will fail until you run:

    REPIN=1 bazel run @maven//:pin
    $ REPIN=1 bazel run @maven//:pin