Transitive Maven artifact resolver as a repository rule.
If you're looking for documentation on gmaven_rules, go here.
- WORKSPACE configuration
- JAR, AAR, source JARs
- Custom Maven repositories
- Private Maven repositories with HTTP Basic Authentication
- Artifact version resolution with Coursier
- Reuse artifacts from a central cache
- Versionless target labels for simpler dependency management
- Ability to declare multiple sets of versioned artifacts
- Supported on Windows, macOS, Linux
Get the latest release here.
List the top-level Maven artifacts and servers in the WORKSPACE:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
RULES_JVM_EXTERNAL_TAG = "1.2"
RULES_JVM_EXTERNAL_SHA = "e5c68b87f750309a79f59c2b69ead5c3221ffa54ff9496306937bfa1c9c8c86b"
http_archive(
name = "rules_jvm_external",
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
sha256 = RULES_JVM_EXTERNAL_SHA,
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 = [
"junit:junit:4.12",
"androidx.test.espresso:espresso-core:3.1.1",
],
repositories = [
# Private repositories are supported through HTTP Basic auth
"http://username:password@localhost:8081/artifactory/my-repository",
"https://jcenter.bintray.com/",
"https://maven.google.com",
"https://repo1.maven.org/maven2",
],
# Fetch srcjars. Defaults to False.
fetch_sources = True,
)
and use them directly in the BUILD file by specifying the versionless target alias label:
android_library(
name = "test_deps",
exports = [
"@maven//:androidx_test_espresso_espresso_core",
"@maven//:junit_junit",
],
)
For the junit:junit
example, using bazel query @maven//:all --output=build
, we can see that the rule generated these targets:
alias(
name = "junit_junit",
actual = "@maven//:junit_junit_4_12",
)
java_import(
name = "junit_junit_4_12",
jars = ["@maven//:https/repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar"],
srcjar = "@maven//:https/repo1.maven.org/maven2/junit/junit/4.12/junit-4.12-sources.jar",
deps = ["@maven//:org_hamcrest_hamcrest_core_1_3"],
tags = ["maven_coordinates=junit:junit:4.12"],
)
java_import(
name = "org_hamcrest_hamcrest_core_1_3",
jars = ["@maven//:https/repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar"],
srcjar = "@maven//:https/repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar",
deps = [],
tags = ["maven_coordinates=org.hamcrest:hamcrest.library:1.3"],
)
The generated tags
attribute value also contains the original coordinates of
the artifact, which integrates with rules like bazel-common's
pom_file
for generating POM files. See the pom_file_generation
example for more information.
You can find the complete API reference at docs/api.md.
To download artifacts into a shared and persistent directory in your home
directory, specify use_unsafe_shared_cache = True
in maven_install
:
maven_install(
name = "maven",
artifacts = [
# ...
],
repositories = [
# ...
],
use_unsafe_shared_cache = True,
)
This is not safe as Bazel is currently not able to detect changes in the shared cache. For example, if an artifact is deleted from the shared cache, Bazel will not re-run the repository rule automatically.
The default value of use_unsafe_shared_cache
is False
. This means that Bazel
will create independent caches for each maven_install
repository, located at
$(bazel info output_base)/external/@repository_name/v1
.
The artifact
macro translates the artifact's group:artifact
coordinates to
the label of the versionless target. This target is an
alias that
points to the java_import
/aar_import
target in the @maven
repository,
which includes the transitive dependencies specified in the top level artifact's
POM file.
For example, @maven//:junit_junit
is equivalent to artifact("junit:junit")
.
To use it, add the load statement to the top of your BUILD file:
load("@rules_jvm_external//:defs.bzl", "artifact")
Note that usage of this macro makes BUILD file refactoring with tools like
buildozer
more difficult, because the macro hides the actual target label at
the syntax level.
If your WORKSPACE contains several projects that use different versions of the
same artifact, you can specify multiple maven_install
declarations in the
WORKSPACE, with a unique repository name for each of them.
For example, if you want to use the JRE version of Guava for a server app, and
the Android version for an Android app, you can specify two maven_install
declarations:
maven_install(
name = "server_app",
artifacts = [
"com.google.guava:guava:27.0-jre",
],
repositories = [
"https://repo1.maven.org/maven2",
],
)
maven_install(
name = "android_app",
artifacts = [
"com.google.guava:guava:27.0-android",
],
repositories = [
"https://repo1.maven.org/maven2",
],
)
This way, rules_jvm_external
will invoke coursier to resolve artifact versions for
both repositories independent of each other. Coursier will fail if it encounters
version conflicts that it cannot resolve. The two Guava targets can then be used
in BUILD files like so:
java_binary(
name = "my_server_app",
srcs = ...
deps = [
# a versionless alias to @server_app//:com_google_guava_guava_27_0_jre
"@server_app//:com_google_guava_guava",
]
)
android_binary(
name = "my_android_app",
srcs = ...
deps = [
# a versionless alias to @android_app//:com_google_guava_guava_27_0_android
"@android_app//:com_google_guava_guava",
]
)
Although you can always give a dependency as a Maven coordinate string, occasionally special handling is required in the form of additional directives to properly situate the artifact in the dependency tree. For example, a given artifact may need to have one of its dependencies excluded to prevent a conflict.
This situation is provided for by allowing the artifact to be specified as a map
containing all of the required information. This map can express more
information than the coordinate strings can, so internally the coordinate
strings are parsed into the artifact map with default values for the additional
items. To assist in generating the maps, you can pull in the file specs.bzl
alongside defs.bzl
and import the maven
struct, which provides several
helper functions to assist in creating these maps. An example:
load("@rules_jvm_external//:defs.bzl", "artifact")
load("@rules_jvm_external//:specs.bzl", "maven")
maven_install(
artifacts = [
maven.artifact(
group = "com.google.guava",
artifact = "guava",
version = "27.0-android",
exclusions = [
...
]
),
"junit:junit:4.12",
...
],
repositories = [
maven.repository(
"https://some.private.maven.re/po",
user = "johndoe",
password = "example-password"
),
"https://repo1.maven.org/maven2",
...
],
)
If you want to exclude an artifact from the transitive closure of a top level
artifact, specify its group-id:artifact-id
in the exclusions
attribute of
the maven.artifact
helper:
load("@rules_jvm_external//:specs.bzl", "maven")
maven_install(
artifacts = [
maven.artifact(
group = "com.google.guava",
artifact = "guava",
version = "27.0-jre",
exclusions = [
maven.exclusion(
group = "org.codehaus.mojo",
artifact = "animal-sniffer-annotations"
),
"com.google.j2objc:j2objc-annotations",
]
),
# ...
],
repositories = [
# ...
],
)
You can specify the exclusion using either the maven.exclusion
helper or the
group-id:artifact-id
string directly.
As with other Bazel repository rules, the standard http_proxy
, https_proxy
and no_proxy
environment variables (and their uppercase counterparts) are
supported.
You can find demos in the examples/
directory.
This repository also hosts the previous implementation of gmaven_rules, a set of repository rules to provide support for easily depending on common Android libraries in Bazel. The previous implementation is now deprecated.
If you were previously depending on gmaven_rules
using an http_archive
in
the WORKSPACE function, you may see this error:
ERROR: error loading package '': Encountered error while reading extension file 'gmaven.bzl': no such package '@gmaven_rules//':
java.io.IOException: Prefix gmaven_rules-20180625-1 was given, but not found in the archive
To fix this, replace this snippet:
# Google Maven Repository
GMAVEN_TAG = "20181212-2"
http_archive(
name = "gmaven_rules",
strip_prefix = "gmaven_rules-%s" % GMAVEN_TAG,
url = "https://github.com/bazelbuild/gmaven_rules/archive/%s.tar.gz" % GMAVEN_TAG,
)
with this snippet:
RULES_JVM_EXTERNAL_TAG = "1.0"
RULES_JVM_EXTERNAL_SHA = "48e0f1aab74fabba98feb8825459ef08dcc75618d381dff63ec9d4dd9860deaa"
http_archive(
name = "gmaven_rules",
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
sha256 = RULES_JVM_EXTERNAL_SHA,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)
The core of it is gmaven.bzl
, a file containing external repository targets
for all artifacts in Google Maven Repository plus
their dependencies, and the supporting tools for generating it.
We no longer recommend using the previous implementation due to its limitations, such as not having support for cross-repository dependency resolution. Some of the artifacts depend on other artifacts that are not present on Google Maven, and these missing dependencies are silently ignored and may cause failures at runtime.
However, if you are still depending on it, please see the releases page for instructions on using the latest snapshot.
To update gmaven.bzl
, run the following command. It will take about 3 minutes.
bazel run //:gmaven_to_bazel && cp bazel-bin/gmaven_to_bazel.runfiles/__main__/gmaven.bzl .