How to upgrade a Bazel-aware library's Maven dependency?

Viewed 836

My library supports Bazel builds and has a dependency from Maven Central. A user of my library wants to use a newer version of a dependency that has new transitive dependencies. How can that be done?

gRPC 1.17 depends on Guava 26. However, Guava 27 added a dependency on com.google.guava:failureaccess. Normally an application using gRPC would just make their own native.maven_jar() with the new version and disable gRPC's call to native.maven_jar(). This would then "upgrade" the @com_google_guava_guava repository that is then consumed by both gRPC and the application.

But @com_google_guava_guava does not include dependency information. That is commonly solved by having third_party java_library()s that stitch the transitive dependencies together. However, those java_library()s can't be changed by the application.

I believe that bind() would solve this problem, as gRPC could depend on //external:com_google_guava_guava which could be a java_library(). But bind() is discouraged.

3 Answers

Consider switching your library to use java_import_external instead of maven_jar.

java_import_external target includes dependency information and thus allow an application to supplant a target's version and its transitive dependencies.

Just remember to add if native.existing_rule(name) == None: before you define @com_google_guava_guava in order to allow a user of your library to define it herself with the newer version of guava that has updated dependencies.

Having thought about this for a little while, I feel like bind() might be the best way for grpc-java to offer this. I'm not aware of any features in the existing Maven transition tools that would make this easier.

However, if the user wants to do it without changing grpc-java, they could:

  1. In WORKSPACE, override com_google_guava_guava with a local_repository():

    grpc_java_repositories(
        omit_com_google_guava = True,
    )
    
    maven_jar(
        name = "com_google_guava_guava_real",
        artifact = "com.google.guava:guava:27.0.1-jre",
        sha1 = "bd41a290787b5301e63929676d792c507bbc00ae",
    )
    
    maven_jar(
        name = "com_google_guava_failureaccess",
        artifact = "com.google.guava:failureaccess:1.0.1",
        sha1 = "1dcf1de382a0bf95a3d8b0849546c88bac1292c9",
    )
    
    local_repository(
        name = "com_google_guava_guava",
        path = "guava_27",
    )
    
  2. Create a subrepo that present a compatible java_library():

    mkdir -p guava_27/jar
    echo > guava_27/WORKSPACE
    cat > guava_27/jar/BUILD.bazel << EOF
    java_library(
        name = "jar",
        visibility = ["//visibility:public"],
        exports = [
            "@com_google_guava_failureaccess//jar",
            "@com_google_guava_guava_real//jar",
        ],
    )
    EOF
    

An update: rules_jvm_external is a new ruleset by the Bazel team to fetch and resolve artifacts transitively.

In this case, the WORKSPACE file will contain something like this:

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

maven_install(
    artifacts = [
        "com.google.guava:guava:27.0.1-jre",
    ],
    repositories = [
        "https://jcenter.bintray.com",
    ]
)

This will automatically resolve and fetch the guava and failureaccess artifacts. Then in the BUILD file, you can directly depend on Guava like this:

java_library(
    name = "my_jar",
    srcs = # ...
    deps = [
        "@maven//com_google_guava_guava",
    ],
)
Related