计时每个文件用 ant 编译需要多长时间

Timing how long each file takes to compile with ant

偶尔对 Java 源文件进行轻微修改,例如一些额外的显式转换,以帮助编译器将单个 java 文件的编译时间从 4 分钟缩短到 3 秒(特别是在 Java 8).

问题是:在大型 java 项目中,您如何找到哪些特定的 .java 文件编译缓慢?

有没有办法让 Ant 计算编译每个单独的 .java 文件需要多长时间?

我认为这是可能的。这是我发现的:

如果您使用的是 Java 8,您可以向编译器注册一个 Plugin 以在编译期间添加一些额外的功能。该文档对插件有这样的说法:

It is expected that a typical plug-in will simply register a TaskListener to be informed of events during the execution of the compilation, and that the rest of the work will be done by the task listener.

因此您可以设置一个插件来使用 TaskListener,并在生成 class 时让任务监听器记录时间戳。

package xyz;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;

public class TimestampPlugin implements Plugin {


    @Override
    public String getName() {
        return "Timestamp_Plugin";
    }

    @Override
    public void init(JavacTask task, String... strings) {
        task.setTaskListener(new FileTimestampListener());
    }
}

Documentation for TaskListener. A task listener is passed a TaskEvent, which has a Kind。就您而言,听起来您对生成感兴趣。

package xyz;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;

import java.util.HashMap;

public class FileTimestampListener implements TaskListener {
    HashMap<String, Long> timeStampMap = new HashMap<>();

    @Override
    public void started(TaskEvent taskEvent) {
        if(TaskEvent.Kind.GENERATE.equals(taskEvent.getKind())) {
            String name = taskEvent.getSourceFile().getName();
            timeStampMap.put(name, System.currentTimeMillis());
        }
    }

    @Override
    public void finished(TaskEvent taskEvent) {
        if(TaskEvent.Kind.GENERATE.equals(taskEvent.getKind())) {
            String name = taskEvent.getSourceFile().getName();
            System.out.println("Generated " + name + " over " + (System.currentTimeMillis() - timeStampMap.get(name)) + " milliseconds");
        }
    }
}

这是一个简单的例子,但从这里开始设置日志文件之类的东西来存储收集到的信息应该很简单。正如您在插件的 init 函数中看到的那样,参数可以从命令行传递给插件。

插件通过使用 -Xplugin 编译器参数指定来配置。我不确定为什么,但似乎没有关于 this page 的任何文档,但可以通过设置一个名为 com.sun.source.util.Plugin 的文件来使用它(FQ class 的名称要实现的接口)在您的 META-INF/services 目录中。所以:

META-INF
|-- services
    |-- com.sun.source.util.Plugin

并在该文件中列出您实现此 class 的 FQ class 名称。所以文件内容将是:

xyz.TimestampPlugin

在您的 Ant 任务中,您只需要指定一个编译器标志 -Xplugin:Timestamp_Plugin(注意这是插件的 getName() 提供的名称功能)。您还需要在 class 路径或注释处理器路径(如果已指定)上提供已编译的插件和运行时依赖项。