bazelbuild / bazel

a fast, scalable, multi-language and extensible build system
https://bazel.build
Apache License 2.0
23.2k stars 4.06k forks source link

Java annotation processors do not have access to resources #12964

Open lgalfaso opened 3 years ago

lgalfaso commented 3 years ago

When executing a Java annotation processor, it does not have access to the resources referenced by the originating class.

java_library(
    name = "processor",
    srcs = ...,
)

java_plugin(
    name = "processor_plugin",
    deps = [":processor"],
#    generates_api = 1,  # tried with and without this.
)

java_library(
    name = "annotation_processor",
    exported_plugins = [":processor_plugin"],
    exports = [":processor"],
)

java_library(
    name = "res",
    resources = ...,
)

java_library(
    name = "target",
    srcs = ...,
    deps = [":res"],
)

Where target contains a class that has an annotation that makes reference to a resource in :res.

The resources are attempted to be loaded doing

processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", fileName);

and if that fails, with

processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", fileName);

At first, it looks like the issue was that in the classpath there were only java headers/interfaces present, so tried with

Using bazel release 4.0.0-homebrew

comius commented 3 years ago

@lgalfaso What does it fail with? It seems that you pasted wrong text into the description.

Could you provide also sample code for processor? Do I understand correctly that you're accessing downstream resources from the processor? If I understand this correctly, then this looks like a fishy design to me; could you support it with some use cases or an OSS annotation processor that does this?

whs commented 3 years ago

I believe I might be affected by this with pf4j

java_plugin(
    name="pf4j_plugin",
    processor_class = "org.pf4j.processor.ExtensionAnnotationProcessor",
    deps = [
        "@maven//:org_pf4j_pf4j",
    ],
)

java_library(
    name = "pf4j",
    exported_plugins = [":pf4j_plugin"],
    visibility = ["//:__subpackages__"],
    exports = [
        "@maven//:org_pf4j_pf4j",
    ],
)

// in app folder
kt_jvm_library(
    name = "kotlin",
    srcs = glob(["src/main/kotlin/**/*"]),
    deps = [
        "//:pf4j",
    ],
)

I expect to have META-INF/extensions.idx in the resulting jar but it does not show up

Zetten commented 3 years ago

I think this issue also affects the Spring Boot Configuration Processor.

I'm using a java_plugin with processor class org.springframework.boot.configurationprocessor.ConfigurationMetadataAnnotationProcessor (fromorg.springframework.boot:spring-boot-configuration-processor:2.5.3). The plugin correctly finds my @ConfigurationProperties and generates META-INF/spring-configuration-metadata.json in my jar.

According to https://docs.spring.io/spring-boot/docs/current/reference/html/configuration-metadata.html#configuration-metadata.annotation-processor.adding-additional-metadata we can add some custom hand-written properties in a resource resource META-INF/additional-spring-configuration-metadata.json, which should be merged into the main metadata file during compilation. But this doesn't happen - both files are in the resulting jar, unmodified.

The documentation suggests:

If you are using an additional-spring-configuration-metadata.json file, the compileJava task should be configured to depend on the processResources task, as shown in the following example:

compileJava.inputs.files(processResources)

This dependency ensures that the additional metadata is available when the annotation processor runs during compilation.

But it's not clear how to achieve this with Bazel. Perhaps a solution is to provide another java_plugin rule attribute like generates_api; something like process_java_resources?

I've created an example repo with both Gradle (passing) and Bazel (failing) configurations: https://github.com/Zetten/bazel-java-annotation-processor-resources. Simply ./gradlew check and bazel test //....

bdleitner commented 3 years ago

I ran into this as well. I was able to work around it by using:

String root = new File("").getAbsolutePath()

from within the processor, which got me the path into the source_output root (I think that's the one, anyway). Since in my particular case the resources were included in the java_library that used the plugin and the path to said resources directory was derivable from the package of the annotated class, I was able to find them and read them as normal files.

I did, however, run into staleness problems https://github.com/bazelbuild/bazel/issues/14095

danfabi commented 2 years ago

Ran into this issue as well. My current workaround is (1) to expose the resources in question to java_plugin via its "resource_jars" attribute and (2) to let the annotation processor search for the resources within StandardLocation.ANNOTATION_PROCESSOR_PATH. This, of course, requires a separate java_plugin declaration for each library that needs processing. Ugly workaround, but works for me.

bdleitner commented 2 years ago

Please reconsider. Needing a separate java_plugin declaration for every library that needs processing as opposed to being able to use exported_java_plugin on the build target for the annotation makes things unwieldy at best. As noted, it's an "ugly" workaround.

and my issue #14095 was duped to this only in the hope that this would actually be fixed.

cushon commented 2 years ago

There's some related history in internal issue b/198685781.

One of the options discussed is to do with with a separate attribute on library rules that would make files available to annotation processing, e.g. java_library.plugin_data.

sgowroji commented 2 years ago

Hello @bdleitner, Thank you for reaching out to us regarding the above issue. Please follow up on the internal issue as mentioned above for more updates and we have reopened the present issue.

softprops commented 2 years ago

does anyone have a working demo of how to make this possible available on github?

I have a usecase I'd like to scan a set of annotations at compile time collect the data into a resource which another library can then consume. the path to accomplish that with bazel semantics + sandboxing is still a bit unclear

github-actions[bot] commented 1 year ago

Thank you for contributing to the Bazel repository! This issue has been marked as stale since it has not had any activity in the last 1+ years. It will be closed in the next 90 days unless any other activity occurs or one of the following labels is added: "not stale", "awaiting-bazeler". Please reach out to the triage team (@bazelbuild/triage) if you think this issue is still relevant or you are interested in getting the issue resolved.

fmeum commented 1 year ago

@bazelbuild/triage not stale