SkillAgentSearch skills...

Mabel

A Maven dependency graph generator for Bazel

Install / Use

/learn @menny/Mabel
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Mabel

Latest release codecov

Yet another Maven dependency graph generator for Bazel.

Mabel provides the mabel_rule rule and artifact macro, which automatically generate a set of targets that can be used as dependencies based on a given list of Maven coordinates. The rule outputs the dependency graph to a lockfile (similar to Yarn's lock file or npm's package-lock.json).

Features

  • Transitively resolves all dependencies from a given list of Maven dependencies and manages version conflicts, ensuring that only one version of each artifact is available in the dependency graph.
  • Generates repository rules for all remote artifacts.
  • Generates required Java rules with transitive dependencies.
  • Allows marking dependencies as test_only.
  • Automatically detects which rule type to create for a given dependency:
    • aar_import for Android artifacts.
    • java_plugin + java_library for annotation processors. More about this here.
    • jvm_import for anything else.
  • Allows implementation replacement for jvm_import and aar_import. These can be replaced with another rule or macro.
  • Supports custom Maven repository URLs and locking dependencies to a specific Maven repository.
  • Adds licenses data to jvm_import rules if a license is declared in the artifact's POM file. Also adds license metadata to the targets' tags attribute:
    • mabel_license_name - The name of the license as it appears in the pom.xml file.
    • mabel_license_url - The URL to the license file as it appears in the pom.xml file.
    • mabel_license_detected_type - The type of license (Apache, MIT, GPL, etc.) as detected by Mabel.
  • Adds srcjar if sources are available in the Maven repository.
  • Handles POM options:
    • Profiles and placeholders.
    • Version specifications.
    • Dependencies that do not have a POM.
    • Exports the Maven coordinate as a tag in the jvm_import rule. This can help with Bazel's pom_file rule.
  • Calculates sha256 for each remote artifact.
  • Produces a lockfile that describes the dependency graph. This file should be checked into your repository.

Why

Unlike other build systems, Bazel does not provide a dependency management service as part of the build and does not provide a way to specify a Maven dependency (which will be resolved transitively) and be available during compilation.

There are several attempts to solve this problem (such as sync-deps, gmaven, rules_jvm_external, migration-tooling, maven-rules, and bazel-deps), but some do not support Kotlin or Android, and some do not support customized Maven repositories.

Usage

Mabel uses a two-phase approach for dependency management:

  1. Phase 1: Lockfile Generation - Run the mabel_rule target to resolve dependencies and generate a JSON lockfile
  2. Phase 2: Module Extension - Configure the maven extension to read the lockfile and create repository rules

This approach ensures reproducible builds and allows you to review dependency changes before committing them.

Step 1: Add Mabel to your MODULE.bazel

Add mabel as dependencies:

bazel_dep(name = "mabel", version = "0.31.0")  # Check latest release

Step 2: Define Dependencies in BUILD.bazel

In your module's BUILD.bazel file (e.g., //third_party:BUILD.bazel), load the mabel_rule and artifact symbols, and define a mabel_rule target with the list of your dependencies:

load("@mabel//rules:mabel.bzl", "mabel_rule", "artifact")

mabel_rule(
    name = "maven_deps",
    lockfile_path = "third_party/maven_install.json",
    maven_deps = [
        artifact("com.google.guava:guava:33.0.0-jre"),
        artifact("org.apache.commons:commons-lang3:3.14.0"),
        artifact("com.google.code.findbugs:jsr305:3.0.2"),
    ],
)

Attributes:

  • name - A unique name for this target.

  • lockfile_path - Path to the output JSON lockfile (relative to workspace root). This file will be created/updated when you run the target.

  • maven_deps - List of artifact() macro invocations representing Maven coordinates to resolve.

  • version_conflict_resolver - (Optional) Default latest_version. Strategy for resolving version conflicts. Can be latest_version or breadth_first.

  • calculate_sha - (Optional) Default True. Calculates the sha256 value of each remote artifact.

  • fetch_srcjar - (Optional) Default False. Also tries to fetch sources jar for each dependency.

The artifact() macro accepts:

  • coordinate - Maven coordinate in the form group-id:artifact-id:version (e.g., "com.google.guava:guava:33.0.0-jre").
  • type - (Optional) Target type to create. Default is inherit. Can be jar, aar, naive, processor, inherit, or auto.
  • test_only - (Optional) Marks this dependency as test-only.
  • maven_exclude_deps - (Optional) List of Maven dependencies to exclude from resolution.
  • repositories - (Optional) List of URLs pointing to Maven servers. Defaults to Maven Central.
  • exports_generation_type - (Optional) Default inherit. Override exports generation for this artifact. Can be inherit, all, requested_deps, none.
  • debug_logs - (Optional) Default False. Prints debug logs for this artifact.

Step 3: Generate the Lockfile

Run the mabel_rule target to resolve all transitive dependencies and generate the lockfile:

bazel run //third_party:maven_deps

This will:

  1. Resolve all transitive dependencies from the specified Maven coordinates
  2. Apply version conflict resolution (using the configured strategy)
  3. Download and calculate SHA256 hashes for all artifacts
  4. Generate third_party/maven_install.json containing all resolved dependency metadata

The lockfile is a JSON file with the following structure:

{
  "version": "1.0",
  "artifacts": {
    "com.google.guava:guava:33.0.0-jre": {
      "repo_name": "com_google_guava__guava__33_0_0_jre",
      "url": "https://repo1.maven.org/maven2/...",
      "sha256": "...",
      "dependencies": ["com_google_code_findbugs__jsr305__3_0_2", ...],
      "exports": [...],
      "runtime_deps": [...],
      "test_only": false,
      "target_type": "jar",
      "licenses": [...]
    }
  }
}

Important: Commit this lockfile to your version control system. It should be treated like package-lock.json in npm or Cargo.lock in Rust.

Step 4: Configure the Module Extension

In your MODULE.bazel, configure the mabel extension to read the lockfile:

mabel = use_extension("@mabel//rules:extensions.bzl", "mabel")
mabel.install(
    lockfile = "//third_party:maven_install.json",
    aliases_repo = "maven",
)

# Import the maven alias repository for convenient access
use_repo(mabel, "maven")

# Optionally (but discouraged) import specific versioned repositories if needed
use_repo(mabel,
    "com_google_guava__guava__33_0_0_jre",
    "org_apache_commons__commons_lang3__3_14_0",
    "com_google_code_findbugs__jsr305__3_0_2",
)

The mabel.install() tag accepts:

  • lockfile - Label pointing to the JSON lockfile generated in Step 3.
  • aliases_repo - Name of the alias repository to create (e.g., "maven"). This repository provides clean, version-free paths to your dependencies.

The use_repo() call imports the repository rules created by the extension. The "maven" repository is an alias repository that provides convenient access to all dependencies. Repository names follow the {group_id}__{artifact_id}__{version} pattern, where dots, hyphens, and other special characters are replaced with underscores.

How to find repository names:

You can find the exact repository names in the lockfile under the repo_name field for each artifact, or use this command:

cat third_party/maven_install.json | grep -o '"repo_name": "[^"]*"' | cut -d'"' -f4 | sort

Step 5: Use in Your Targets

Reference the dependencies in your Bazel targets using the @maven alias repository:

java_library(
    name = "mylib",
    srcs = ["MyLib.java"],
    deps = [
        "@maven//com/google/guava/guava",
        "@maven//org/apache/commons/commons-lang3",
    ],
)

The @maven repository provides clean, version-free paths to your dependencies using the pattern @maven//group/id/artifact-id.

Updating Dependencies

When you need to update dependencies:

  1. Modify the maven_deps list in your mabel_rule (add, remove, or change versions)
  2. Run bazel run //third_party:maven_deps to regenerate the lockfile
  3. Review the changes to maven_install.json (use git diff)
  4. Update the use_repo() call in MODULE.bazel if you added or removed dependencies
  5. Commit the updated lockfile

Working with Multiple Dependency Sets

You can define multiple mabel_rule targets for different dependency sets (e.g., separate sets for main code, tests, or different modules):

mabel_rule(
    name = "main_deps",
    lockfile_path = "third_party/main_install.json",
    maven_deps = [
        artifact("com.google.guava:guava:33.0.0-jre"),
    ],
)

mabel_rule(
    name = "test_deps",
    lockfile_path = "third_party/test_install.json",
    maven_deps = [
        artifact("junit:junit:4.13.2", test_only = True),
        artifact("org.mockito:mockito-core:5.0.0", test_
View on GitHub
GitHub Stars17
CategoryDevelopment
Updated5d ago
Forks2

Languages

Java

Security Score

95/100

Audited on Apr 4, 2026

No findings