【问题标题】:Choosing Gradle incremental annotation processor category when processor depends on a class in annotation value当处理器依赖于注释值中的类时,选择 Gradle 增量注释处理器类别
【发布时间】:2019-09-09 23:04:38
【问题描述】:

我有一个简单的annotation processor,应用如下:

@DiffElement(diffReceiver = Renderer.class)
class ViewState {
  String getHello();
  int getWorld();
}

class Renderer {
  void renderHello(String hello);
  void renderWorld(int world);
}

要使此处理器正常工作,get-functions 和 Renderer-interface 函数参数的名称必须匹配。它会检查这一点,并使用注解的参数来查看提供的类并在此基础上生成一些代码。

它生成一个文件。

我已阅读 Incremental annotation processing 上的文档,但我无法决定将哪个类别应用于此处理器。以下是我的考虑:

  • 它不能是 isolating,因为它不会从带注释元素的 AST 派生所有内容,因为它还检查来自注释参数的类
  • 不可能是aggregating,因为它在Renderer类上没有任何注释,所以根据上面的文档,每当Renderer类发生变化时,处理器都不会被调用,因为处理器没有' t 注册来处理这个文件,所以这会导致生成的结果出错。

问题:

  • 我是否正确理解文档?或者某些类别仍然可以应用于此处理器
  • 如果它不属于任何一个类别,我怎么能告诉 Gradle 它不是增量的,所以像“kotlin kapt”这样的工具不会向用户抱怨我的处理器不是增量的

【问题讨论】:

    标签: java gradle annotation-processing


    【解决方案1】:

    我是否正确理解文档?或者某些类别仍然可以应用于此处理器

    关于处理器类别的文档非常短,在我看来,缺乏示例。我花了很多时间弄清楚这些文档,构建了一些简单的实验项目。因此,据我所知,如果我最终正确地计算出它们,则可以应用一个类别:)

    你这么说

    它不能被隔离,因为它不会从带注释元素的 AST 派生所有内容,因为它还从注释参数中检查一个类

    这并不完全正确,我会解释原因。 正如documentation 中提到的,隔离处理器

    必须根据可从其 AST 获得的信息为带注释的类型做出所有决定(代码生成、验证消息)。这意味着您可以分析类型的超类、方法返回类型、注释等,甚至可以传递。

    "Even transitively" 短语在这里非常重要——这意味着,您不仅可以分析带注释类型的 AST,还可以分析其中一些方法的返回类型的 AST,然后,比如说,一个超类那种……

    据我了解,您可以通过 AST 从带注释的元素中发现的每种类型都是带注释的类型(或一般元素)的依赖项。如果一个依赖改变了,那么依赖的类型需要重新编译。因此,当Renderer 类发生变化时,ViewState 将被重新编译并因此被重新处理,因为 它引用 Renderer 作为它的注释参数。超类型、超接口、方法的返回类型、方法参数的类型、注释类参数……——所有这些都被认为是依赖类型。

    因此,您的注释处理器实际上可以隔离


    P.S.如果隔离不起作用,请确保注释保留为 CLASS 或更高,无论出于何种原因。

    P.P.S. 我发现注释处理中的增量是一个非常阴暗的话题,充满了惊喜和水下岩石。我为自己发现的经验法则是,几乎每一个经过一些调整的处理器都可以被隔离,除非它真的需要根据许多输入生成一个实体。而且,重要的是,只有这些输入在引用方面彼此完全不相关,甚至位于不同的库中。

    【讨论】:

    • 感谢您如此详细的分析!我已将接受的答案更改为您的答案,因为它直接回答了我的问题(而 Colin 之前的回答也很有帮助)。
    【解决方案2】:

    它不能聚合,因为它在 Renderer 类上没有任何注释,所以根据上面的文档,只要 Renderer 类发生变化,处理器就不会被调用,因为处理器还没有注册来处理这个文件,所以这会导致生成的结果出错。

    这不会是一个直接的答案,但我认为它仍然可以回答您的问题:据我了解,这句话是您问题的实际根源。任何温和的增量系统都会让你失望,不仅仅是 Gradle 的“聚合”模式(例如,尝试在一个简单的 Eclipse 项目中使用你的处理器,甚至可能是 IntelliJ,尽管我在那里得到了不同的结果)。

    如果您确实想要强制对未注释类型的更改将导致您的注释处理器再次运行,您不能将您的注释处理器限制为该注释,而必须从 getSupportedAnnotationTypes() 返回神奇的 "*" 值,表示任何更改的类都必须触发处理器重新运行。 From the Javadoc for this method:

    最后,“*”本身代表了所有注解类型的集合,包括空集。请注意,处理器不应声明“*”,除非它实际上正在处理所有文件;在某些环境中声明不必要的注释可能会导致性能下降。

    理想情况下,您只需创建第二个(或第三个等)注释来提供此提示,但这并不总是可行的,这就是允许您使用此通配符选项的原因

    【讨论】:

    • 我怀疑我将不得不重组我的处理器并添加一个额外的注释来正确跟踪对注释类的更改。我不知道"*" 选项,但我担心这会有点矫枉过正。我会尝试重新考虑我想的注释结构。谢谢!
    • 当然有点矫枉过正,但如果你有办法为这些类型提供一个 gradle 项目,那么它仍然是值得的,因此重新运行处理器以进行任何更改是很有意义的。跨度>
    猜你喜欢
    • 2017-01-05
    • 1970-01-01
    • 2017-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 2020-05-13
    • 2013-03-13
    相关资源
    最近更新 更多