如何配置Gradle的增量构建和注解处理器。

3
我希望在构建过程中使用QueryDSL注解处理器。我该如何做才能避免每次更改任何类时都进行不必要的注解处理器编译和运行?我希望只有在相关类发生更改时,QueryDSL才会生成Q-*类。
这种总是运行的注解处理器对我们的构建过程时间产生了负面影响,而且似乎增量构建无法正常工作,如果需要运行注解处理器。
谢谢。

所以它可以与我的简单注解处理器一起使用,监视一个硬编码目录中的文件。每次运行加速2-3倍!希望Gradle能够普遍解决所有注解处理器的问题 :) 谢谢。 - juraj00x
很高兴它对你有用。然而,我认为Gradle无法普遍解决它,因为它永远不知道AP需要哪些文件作为其输入。 - lukyer
3个回答

4

Gradle无法知道注解处理器使用哪些文件作为输入,因此每当监视目录(src)中的内容发生更改时,它必须触发完整的重新编译。

但是,您可以轻松地告诉Gradle哪些文件应该只触发注解处理。对其他文件的更改将不会触发注解处理器的使用,并且Gradle可以使用其全部功能(例如增量构建)。

我还添加了一个“force”任务buildWithAP,调用注解处理器,而不管提示(启发式)函数的结果如何。

我的解决方案:

ext.isTask = { name -> return project.gradle.startParameter.taskNames.contains(name) }

/**
 * Heuristic function allowing to build process guess if annotation processor run is necessary
 * Annotation processors will not be called during build task if this function returns FALSE
 */
ext.isApInvalidated = { -> return hasAnyFileRelatedToApChanged() }

dependencies {
  if (isTask("buildWithAP") || isApInvalidated()) {
    println "Going to run annotation processors ..."
    apt "com.querydsl:querydsl-apt:$queryDslVersion:jpa"
  ...
  } else {
    // just add generated classes to the classpath
    // must be in else branch or multiple AP calls will collide!
  sourceSets.main.java.srcDirs += projectDir.absolutePath + "/build/generated/apt"
  } 

}

task buildWithAP (dependsOn: build) {}

你可以使用任何注解处理器,例如自己编写的,不仅限于 QueryDSL。
希望我的表述清晰易懂。

很不幸,情况比那个更复杂,但Gradle很快就会发布一个解决方法。这在今年的Gradle峰会上已经宣布过了。你的解决方案并不理想,因为一旦需要进行注解处理,它就会触发完整的构建。在这种情况下,有价值的是增量注解处理。此外,用于确定是否重新编译使用注解的文件的启发式方法过于简单化,一些注解处理器可能具有高级处理功能,很难识别这些文件。更不用说在大多数情况下,你需要对所有文件进行注解搜索了。 - Snicolas
当然这不是最终解决方案,但它适用于我们的自定义AP和QueryDSL的一个。 我的hasAnyFileRelatedToApChanged()仅基于文件更改时间戳比较。如果AP使用的文件真的发生了变化,就会触发带有AP运行的完整构建。但这比每次都触发要好得多;)我期待官方解决方案,但现在这是我想出的最好的方法。 - lukyer
1
例如,我们遵循一种常见的做法,在实体文件上添加后缀*Entity。这些文件在大多数(或者可能是所有)情况下,你只需要关注它们是否发生了变化。如果有变化,则触发QueryDSL的AP。如果没有变化,则不管它(=快速)。因此,对于QueryDSL,我们监视:fileTree(dir: 'src', include: '**/*Entity.java') - lukyer
它可以工作,但高度取决于您的AP的性质。如果您的AP除了包含注释的文件之外不需要任何其他文件来执行其工作(即生成代码),那么是的,您肯定处于情况更简单的情况下,您的解决方法可能会提高构建时间。 - Snicolas
我发现构建缓存很容易被注解处理所混淆。例如,创建一个Dagger可注入类,在没有该类的情况下检出一个分支,重新构建将使用包含指向不存在类的生成类的缓存。 - enl8enmentnow
显示剩余2条评论

2
据我所知,目前还不能实现这一点:请参阅此博客文章的“使用注解处理器进行增量编译”部分:

...对于注解处理器,Gradle不知道它们将生成哪些文件。也不知道在哪里以及基于什么条件生成。因此,如果使用注解处理器,则Gradle会禁用Java增量编译器。

Gradle相关问题:在存在注解处理器的情况下使增量编译更有效 #1320 博客文章还提到了一个可能的解决方法:

但是,可以将其限制为实际使用注解处理器的类集。简而言之,您可以声明不同的源集,并使用不同的编译任务来使用注解处理器,而将其他编译任务保持没有任何类型的注解处理。

然而,这似乎需要相当多的工作,所以我尚未使用它。

谢谢,我也找到了那篇博客文章。很遗憾Gradle没有告诉开发人员因为注解处理器的影响而跳过增量处理。不过我已经想出了如何解决它。请看我的回答。 - lukyer

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接