Skip to content

Dagger compiler breaks on classloader issue using bazel rules_kotlin/kt_ksp_plugin and jvm_rules_external #4649

@cgruber

Description

@cgruber

I am working on a project (dagger-grpc) which provides a different approach to dagger binding, with a focus on direct examples of integration with microservice containers (armeria in particular). I am building out the examples, and running into a difficulty wherein Dagger, when run as a KSP processor, blows up with an IllegalAccessException from a module violation, in attempting to reference a class in com.google.common.graph from another which has already been loaded in the outer container. (see trace below).

I referenced this in this comment

Environment:

  • bazel_dep(name = "rules_kotlin", version = "2.1.0", repo_name = "io_bazel_rules_kotlin")
  • Bazel 8.1.1
  • OS: Mac/OS 15.3.1
  • Dagger 2.55 (via maven_install / rules_jvm_external)

All bazel-isms are set up locally for the project, and use maven-downloaded deps. Notably, I set up a plugin declaration for dagger-compiler like so in my root BUILD.bazel file:

load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_ksp_plugin")

kt_ksp_plugin(
    name = "dagger-compiler",
    processor_class = "dagger.internal.codegen.ComponentProcessor",
    visibility = ["//visibility:public"],
    deps = [
        "@maven//:com_google_dagger_dagger_compiler",
    ],
)

And then my target is like so:

kt_jvm_library(
    name = "lib",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    plugins = [
        "@dagger-grpc//io_grpc/compiler/ksp:plugin",
        "//:dagger-compiler",
    ],
    visibility = ["//visibility:public"],
    runtime_deps = [
        "@maven//:org_slf4j_slf4j_jdk14",
    ],
    deps = [
        "//proto:grpc",
        "@com_google_protobuf//java/core",
        "@dagger-grpc//api",
        "@dagger-grpc//third_party/dagger",  # Use your own dagger mapping in your repo
        "@dagger-grpc//util/armeria",
        "@maven//:com_google_guava_guava",
        "@maven//:com_linecorp_armeria_armeria_grpc",
        "@maven//:io_github_oshai_kotlin_logging_jvm",
        "@maven//:io_grpc_grpc_protobuf_lite",
    ],
)

Expected behavior

Normal generation of dagger factories and component implementations.

Actual Outcome

Stack trace:

exception: java.lang.IllegalAccessError: failed to access class com.google.common.graph.BaseGraph from class com.google.common.graph.Traverser (com.google.common.graph.BaseGraph is in unnamed module of loader org.jetbrains.kotlin.preloading.MemoryBasedClassLoader @50b494a6; com.google.common.graph.Traverser is in unnamed module of loader java.net.URLClassLoader @268e3a1d)
	at com.google.common.graph.Traverser.forTree(Traverser.java:182)
	at dagger.internal.codegen.validation.ValidationReport.<clinit>(ValidationReport.java:43)
	at dagger.internal.codegen.validation.InjectValidator$InternalValidator.validateUncached(InjectValidator.java:159)
	at dagger.internal.codegen.base.Util.reentrantComputeIfAbsent(Util.java:33)
	at dagger.internal.codegen.validation.InjectValidator$InternalValidator.validate(InjectValidator.java:155)
	at dagger.internal.codegen.validation.InjectValidator.validate(InjectValidator.java:113)
	at dagger.internal.codegen.validation.InjectBindingRegistryImpl.tryRegisterConstructor(InjectBindingRegistryImpl.java:257)
	at dagger.internal.codegen.validation.InjectBindingRegistryImpl.tryRegisterInjectConstructor(InjectBindingRegistryImpl.java:243)
	at dagger.internal.codegen.processingstep.InjectProcessingStep.process(InjectProcessingStep.java:73)
	at dagger.internal.codegen.processingstep.TypeCheckingProcessingStep.lambda$process$0(TypeCheckingProcessingStep.java:94)
	at com.google.common.collect.RegularImmutableMap.forEach(RegularImmutableMap.java:297)
	at dagger.internal.codegen.processingstep.TypeCheckingProcessingStep.process(TypeCheckingProcessingStep.java:72)
	at dagger.internal.codegen.processingstep.TypeCheckingProcessingStep.process(TypeCheckingProcessingStep.java:49)
	at dagger.spi.internal.shaded.androidx.room.compiler.processing.XProcessingStep.process(XProcessingStep.kt:57)
	at dagger.spi.internal.shaded.androidx.room.compiler.processing.CommonProcessorDelegate.processRound(XBasicAnnotationProcessor.kt:134)
	at dagger.spi.internal.shaded.androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor.process(KspBasicAnnotationProcessor.kt:62)
...

Note: The presence of the other ksp plugin is immaterial - the issue reproduces irrespective of other plugins being present.

Reproduction Steps

I built a repro PR at geekinasuit/dagger-grpc#8

Analysis

The KSP environment provided by rules_kotlin is insufficiently isolated in its classloading. Something inside the ksp infrastructure itself uses parts of com.google.common.graph, but does not load all of it. Some provisional attempts to do a low-hanging-fruit fix on that end have failed (e.g. bazelbuild/rules_kotlin#1284).

Regardless of isolation on the containing end, dagger currently seems to be leaning into com.google.common.graph from within its own validation code (ValidationReport.java:43 based on the stack trace). This suggests com.google.common.graph is not "shaded" in the compiler maven artifact, leaving it exposed to collision with other usages of Guava in the containing environment. One fix of this, for Dagger, at least, would be to ensure that com.google.common.graph is package-rewritten to avoid the collision and class-loading module conflict. That was always best-practice for annotation processors anyway, back when I was involved with the project, but it may have changed since.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions