Skip to content

Feat: Support pluggable Error Prone plugins via java_package_configuration #1

Description

@JonathanPerry651

Description

Currently, there is no clean, public-facing API in Bazel or @bazel_tools to compile a custom JavaBuilder binary with extra Error Prone plugins/checkers (e.g., custom checks fetched from Maven) and use it in a custom java_toolchain. Extending the default JavaBuilder requires knowledge of internal Bazel targets, private dependencies (like @io_bazel//third_party), and complex packaging/merging logic.

This proposal introduces a clean, public-facing Starlark API exposed under @bazel_tools//tools/jdk:javabuilder.bzl that allows clients to easily define custom JavaBuilder binaries and Error Prone check suites.

Proposed API Changes

We introduce two new public-facing rules/macros in @bazel_tools//tools/jdk:javabuilder.bzl:

  1. default_javabuilder: A macro that packages a JavaBuilder executable. The default vanilla JavaBuilder is built by passing the default third-party Error Prone core libraries, while custom configurations can pass custom merged Error Prone engines.
  2. errorprone_with_custom_plugins: A rule that merges a base Error Prone library (e.g. core Error Prone fetched from Maven) with custom checkers/plugins.
load("@bazel_tools//tools/jdk:javabuilder.bzl", "default_javabuilder", "errorprone_with_custom_plugins")
load("@rules_java//toolchains:default_java_toolchain.bzl", "DEFAULT_TOOLCHAIN_CONFIGURATION", "default_java_toolchain")

# 1. Merge core Error Prone with a custom plugin
errorprone_with_custom_plugins(
    name = "my_errorprone",
    errorprone = "@maven//:com_google_errorprone_error_prone_core",
    plugins = [":my_plugin"],
)

# 2. Build the custom JavaBuilder deploy jar
default_javabuilder(
    name = "custom_javabuilder",
    errorprone = [":my_errorprone"],
)

# 3. Associate it with a custom java_toolchain
default_java_toolchain(
    name = "custom_toolchain",
    configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,
    javabuilder = ":custom_javabuilder_deploy.jar",
)

Note

Package-Level Configuration: Once a custom JavaBuilder is registered with the toolchain, users can configure the activation, severity, and options of these custom checkers on a package-by-package basis using the existing java_package_configuration API's javacopts attribute (e.g. passing "-Xep:CustomCheck:ERROR" for strict packages and "-Xep:CustomCheck:OFF" for legacy packages).

Implementation Overview & Expected Diffs

1. Bazel Core Diffs

  • API & Refactored JavaBuilder Targets (src/java_tools/buildjar/java/com/google/devtools/build/buildjar/BUILD):
    +java_library(
    +    name = "javabuilder_errorprone_api",
    +    srcs = ["javac/plugins/errorprone/ErrorProneInvoker.java"],
    +    visibility = ["//visibility:public"],
    +)
    +
     java_library(
    -    name = "buildjar",
    -    srcs = glob(["**/*.java"]),
    -    deps = ["//third_party:error_prone", ...],
    +    name = "javabuilder_without_errorprone",
    +    srcs = glob(
    +        ["**/*.java"],
    +        exclude = ["javac/plugins/errorprone/ErrorProneInvokerImpl.java"],
    +    ),
    +    deps = [
    +        ":javabuilder_errorprone_api",
    +        # other non-errorprone dependencies
    +    ],
     )
  • ServiceLoader Hook in JavaBuilder:
    +ServiceLoader<ErrorProneInvoker> loader = ServiceLoader.load(ErrorProneInvoker.class);
    +Iterator<ErrorProneInvoker> iterator = loader.iterator();
    +if (iterator.hasNext()) {
    +    ErrorProneInvoker invoker = iterator.next();
    +    invoker.init(context);
    +}
  • Export API & Base targets in @bazel_tools (tools/BUILD):
     filegroup(
         name = "embedded_tools_srcs",
         srcs = [
             ...
    +        "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar:javabuilder_errorprone_api",
    +        "//src/java_tools/buildjar/java/com/google/devtools/build/buildjar:javabuilder_without_errorprone",
             ...
         ],
     )

2. rules_java Diffs

  • Assemble JavaBuilder via default_javabuilder (toolchains/default_java_toolchain.bzl):
    +load("@bazel_tools//tools/jdk:javabuilder.bzl", "default_javabuilder")
     
     def default_java_toolchain(name, ...):
    +    default_javabuilder(
    +        name = name + "_javabuilder",
    +        errorprone = ["@rules_java//third_party:error_prone"],
    +    )
    +
         native.java_toolchain(
             name = name,
    -        javabuilder = ["@bazel_tools//tools/jdk:JavaBuilder_deploy.jar"],
    +        javabuilder = [":" + name + "_javabuilder_deploy.jar"],
             ...
         )

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions