【问题标题】:How can Java build tools stop rebuilding everything?Java 构建工具如何停止重新构建一切?
【发布时间】:2014-05-31 10:06:09
【问题描述】:
  • tl;dr 版本: 有没有办法告诉 ant/maven/gradle 跟踪文件依赖关系,并且只重建过时的文件,即比它们的依赖关系更旧的文件?

  • 以下长版:

我是如何在 C/C++ 中做到的

我多年来一直使用make 来开发 C/C++。虽然有时它可能很麻烦,但它至少为我做了一件最重要的事情:它跟踪文件依赖关系,并且只有在其任何依赖关系比文件本身更新时才重建文件。这很棒,它大大减少了构建时间,尤其是在执行大量代码生成工具时。

我在我的项目中使用了大量的代码生成。但是,代码生成需要一些时间,并且生成的代码必须在之后进行编译,因此浪费了更多时间。大多数生成的代码仅依赖于少量文件,例如生成解析器时的语法规范。使用make,我只需将依赖项设置为规范文件,并且仅在规范更改时重新构建解析器。在我的一些项目中,生成代码需要完全重建所需时间的 80-90%。因此,无需重新生成生成的代码,我的构建过程几乎可以加快 10 倍。

现在到 java

人们不会将make 用于java,所以即使我喜欢make,我也会为我的java 项目使用常用的java 构建工具。我尝试的构建工具(ant、gradle、maven)似乎不提供这种依赖跟踪。相反,它们依赖于调用的应用程序或任务来执行此操作(例如,ant copy 任务仅复制过时的文件)。但是,我使用的代码生成程序根本不提供此类检查。无论输入规范是否过时,它们总是执行完整的代码生成。

因此,我在 java 中的构建时间变得越来越长。它们的时间太长了,以至于我将代码生成从通常的构建周期中取出到一个单独的目标中,并且仅在更改任何规范文件后手动执行该目标。但是,当然,这很容易出错,因为我有时会忘记它。我的构建过程现在感觉完全有缺陷。

最后,我的问题

  1. 有没有办法在我提到的任何我不知道的构建工具(ant、gradle、maven)中执行依赖项跟踪和仅重建过时的文件?

  2. 如果不是,那为什么?为什么java社区似乎不关心这个特性?尤其是在涉及大量代码生成的情况下,这样的功能非常重要,尤其是在所使用的代码生成工具本身不提供该功能的情况下。

  3. 如果没有,那么是否还有其他具有此类跟踪功能的 java 构建工具?

【问题讨论】:

    标签: java maven ant build gradle


    【解决方案1】:

    一些以 Gradle 为中心的答案:

    Gradle 只会在任务的输入与之前的运行相比发生更改,或者其输出已被删除或修改时才会重新运行任务。这是一个通用 功能,只要它声明了它的输入和输出,它就适用于任何 任务。 (内置任务类型声明其输入和输出开箱即用。)此功能通常称为增量构建 - 仅执行任务子集的构建。

    Gradle 还(非常)对增量 Java 编译提供了实验性支持。这是一个 增量任务 的示例,即根据与上一次运行相比哪些 输入发生了变化,仅执行部分工作的任务。实现增量任务需要了解任务的领域(例如 Java 编译)。因此,此功能不如增量构建 功能通用,并且需要任务实施者付出更多努力。

    举个具体的例子,如果你的代码生成任务声明了它的输入和输出(这很容易做到),它只会在语法文件发生变化或生成的文件被删除或修改时重新运行。 (出于本示例的目的,我们假设可以有多个语法文件。)此外,您的任务实现可能会询问 Gradle 自上次运行以来哪些语法文件已更改,并使用此信息仅重新生成文件的子集。显然,这需要您努力实现,并且可能无法实现,具体取决于您的任务使用的底层工具的性质。

    关于增量编译的更多话:由于几个原因,增量编译在 Java 构建工具中不太普遍。一方面,Java 的编译速度比 C++ 快得多。此外,代码库通常被模块化为许多较小的编译单元,每个编译单元只有在其输入之一发生变化时才会重新编译(至少在 Gradle 中)。尽管如此,我确信随着时间的推移,更多的 Java 构建工具将支持增量编译。有些今天已经做到了(例如 Pants、sbt 和 Eclipse 和 IntelliJ 等 IDE),Gradle 即将迎头赶上。

    【讨论】:

    • 有趣的答案,谢谢!您能否举个例子或提供一个链接来说明如何在 gradle 中为任务声明输入和输出?反正我喜欢 gradle,因为我讨厌 XML。
    【解决方案2】:

    Maven 可以用于此。 How does maven compile only the modified java files? 是您问题的答案:

    我只是想知道这一点,当我没有做 mvn install 时 'clean',maven只编译修改后的java文件

    问题是您重新生成所有文件。然后您将丢失未修改文件的跟踪(所有迭代工具都会比较 .class 和 .java 之间的最后修改时间戳)。你能把代码生成限制在真正修改过的文件上吗?

    如果你不能,一个想法可能是在另一个目录中生成文件并编写一个脚本来检查和替换编译“区域”中的修改文件(通过比较内容)。之后,您可以运行 mvn install。

    【讨论】:

    • 我知道它适用于 java 到类编译。但是,这正是我的问题。不是 Maven 跟踪这个,而是编译器插件(ant 中的javac 任务也这样做)。因此,这适用于编译 java 但 not 用于生成代码。当然,我可以使用脚本,但这就像完全放弃并自己进行所有跟踪一样。
    • 不,各种第三方应用程序,例如解析器生成器等。
    • 问题是你没有过时的文件。对编译器来说,一切都是崭新的(所有时间戳都较新)。要与它合作,你必须遵守它过时的含义。添加只复制修改文件的脚本真的很难吗?如果你这样做,你可以轻松地重建项目。示例:generateTool -generate && copyScript && cd ../compileDirectory && mvn install。 copyScript 应该比较文件内容并仅在它们被更改时复制。
    • 我发现 ant 有一个任务叫做依赖于你的场景。问题是它不是很可靠,因为它可能会错过一些应该触发重新编译的更改。链接stackoverflow.com/questions/627868/do-you-use-ants-depend-task
    • Maven 中的增量编译是完全不可靠的。除非使用depend 任务,否则Ant 不会进行增量编译,这也是不可靠的。与普遍看法相反,JDK 的 javac 命令不支持增量编译(它只是重新编译您告诉它的文件)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-15
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多