The Java compiler does not allow usage of an annotation with `Source` retention from a `compileOnly` module for a project using JPMS

huangapple go评论89阅读模式
英文:

The Java compiler does not allow usage of an annotation with `Source` retention from a `compileOnly` module for a project using JPMS

问题

注解处理器和编译器插件通常使用sourceclass保留策略定义注解。这些注解在运行时不可见,因此无需将它们包含在运行时类路径中;可以在Gradle构建文件中通过compileOnly来使用它们。此外,在存在module-info文件的情况下,Java编译器要求将注解类包含在运行时类路径中 - 必须在module-info中声明它们,这意味着它们必须从Gradle中使用implementation而不是compileOnly。这似乎是编译器对JPMS的支持存在的一个问题。或者,对于这种奇怪的行为有没有一个合理的解释?

这里有一个示例。

package com.example;
...

@Retention(RetentionPolicy.SOURCE)
public @interface Example {
...
}

com.example.Example 注解在依赖项 my-annotation-proc 中定义。

dependencies {
    compileOnly 'com.example:my-annotation-proc:0.1-SNAPSHOT'
    annotationProcessor 'com.example:my-annotation-proc:0.1-SNAPSHOT'
}

Foo.java 中使用 ExampleAnnotation

package abc;

public class Foo {
  
  @com.example.Example
  public void something() {
    ...
  }
}

module-info.java 文件不应该需要为注解的使用添加 requires

module MyProject {
  // 不应该需要这个。
  // 而且,添加它需要在Gradle中添加 `implementation` 依赖项,将其引入到不应该存在的运行时中。
  //requires my.annotation.proc;
}

编译项目会产生一个编译错误,指示 com.example 不可见等问题。

英文:

Annotation processors and compiler plugins often define annotations with source or class retention. These annotations are not exposed at runtime, thus there is no need to include them in the runtime classpath; they can be used via compileOnly in a Gradle build file. Additionally, there is no need to declare their use in the module-info file. Yet, in the presence of a module-info file, the Java compiler requires the annotation classes to be included in the runtime classpath -- they must be declared in the module-info, which means they must be accessed from Gradle with implementation instead of compileOnly. This appears to be a hole in the compiler's support for JPMS. Or, is there a good explanation for this odd behavior?

Here's an example.

package com.example;
...

@Retention(RetentionPolicy.SOURCE)
public @interface Example {
...
}

The com.example.Example annotation is defined in dependency my-annotation-proc.

dependencies {
    compileOnly 'com.example:my-annotation-proc:0.1-SNAPSHOT'
    annotationProcessor 'com.example:my-annotation-proc:0.1-SNAPSHOT'
}

Usage of ExampleAnnotation in Foo.java.

package abc;

public class Foo {
  
  @com.example.Example
  public void something() {
    ...
  }
}

The module-info.java file should not need a requires for usage of the annotation.

module MyProject {
  // Should be no need for this. 
  // Plus, adding it requires an `implementation` dependency in Gradle, which brings it into runtime where it does not belong.
  //requires my.annotation.proc;
}

Compiling the project produces a compile error indicating the com.example is not visible etc.

答案1

得分: 4

requires 指令对于任何依赖项都是强制的,不仅限于运行时依赖。

JLS,§7.7.1进行比较:

7.7.1. 依赖

requires 指令指定当前模块所依赖的模块的名称。

requires 关键字后面可以跟着修饰符 static。这表示依赖在编译时是强制的,但在运行时是可选的。

我不知道在Gradle方面是否使用 requires static my.annotation.proc; 能够解决你的问题,但这是语言层面上应该处理的方式。

英文:

A requires directive is mandatory for any dependency, not just runtime dependencies.

Compare with JLS, §7.7.1:

> ### 7.7.1. Dependences
> The requires directive specifies the name of a module on which the current module has a dependence.
>
> …
>
> The requires keyword may be followed by the modifier static. This specifies that the dependence, while mandatory at compile time, is optional at run time.

I don’t know whether using requires static my.annotation.proc; solves your problem with Gradle, but that’s how it is supposed to be handled at the language level.

huangapple
  • 本文由 发表于 2020年6月29日 15:38:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/62633349.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定