解决 JaCoCo 打包的类和执行文件不匹配

jacoco / java / maven

最近的项目里遇到了这么一个问题,打包时注意到 jacoco 有一个警告: 「打包的类和执行文件不匹配」,记录一下。

背景

jacoco 是一个检查代码覆盖率的工具,功能我们不做争论,规范要求使用,那我就使用, 同样我对 jacoco 的了解也仅限于规范和指引。这两天注意到打包时, jacoco 报了这么一个警告:

> Classes in bundle '...' do not match with execution data. 
> For report generation the same class files must be used as at runtime.

然后列出了四个明细的警告:

> Execution data for class ... does not match

不知道这个警告是什么时候出现的,但是以我对自己的了解, 引入 jacoco 时肯定是没有这个。所以我需要调查下这个警告是怎么来的, 不过由于我对 jacoco 的了解有限,怎么解警告自然是先搜索, 在翻了某个搜索引擎第一页以后,我觉得没有对我有帮助的内容, 于是我开始向 claude 求助。

分析和解决

今天运气还可以,Sonet 3.7 回答了我的问题,不过第一回合没有太多可用信息, 它跟我解释了这个警告是什么:

> 这个警告表明在生成代码覆盖率报告时,JaCoCo 发现类文件与执行数据不匹配。

并且建议我先 clean 再 package 重新打包看看,如果没有解决, 它需要我提供更具体的错误信息和 jacoco 配置。

很显然 clean 不能解决我的问题,于是我把上面两个告警, 以及我的 pom 相关配置贴给了它:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.6</version>
    <configuration>
        <skip>false</skip>
        <destFile>jacoco.exec</destFile>
        <dataFile>jacoco.exec</dataFile>
        <append>true</append>
    </configuration>
    <executions>
        <execution>
            <id>default-instrument</id>
            <goals>
                <goal>instrument</goal>
            </goals>
        </execution>
        <execution>
            <phase>test</phase>
            <id>default-restore-instrumented-classes</id>
            <goals>
                <goal>restore-instrumented-classes</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>prepare-packages</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Sonet 给了我四个方案:

  1. 更新 JaCoCo 配置
  2. 排除有问题的类
  3. 避免使用 instrument 目标
  4. 清理缓存和 .exec 文件

我们按顺序来,第一个方案,可能可以解决,不过它给出的方法和规范给出的指引并不一致, 所以我不打算最先试这个方案。第二个方案明显是在回避问题,下一个。 第三个方案其实和方案一雷同,那么看来值得一试的只有第四个方案。

Sonet 的建议是采用一或三,但其实第四个方案的试错成本是最低的, 也是我看起来最可行的,因为剩下三个我都排除了。而本地缓存造成版本不一致也确实可能, 不过出于安全第一的态度,我还是试探性问了 Sonet 是否可以安全删除 jacoco.exec, Sonet 给出的答复也让我更有把握一些:

> 是的,`jacoco.exec` 文件可以安全删除。这个文件只是 JaCoCo 
> 生成的代码覆盖率执行数据文件,删除它不会影响你的源代码或编译后的类文件。
> 
> `jacoco.exec` 文件包含了代码执行过程中收集的覆盖率信息,它是在测试运行时生成的。
> 删除它后,JaCoCo 会在下次运行测试时重新生成新的覆盖率数据文件。
> 
> 实际上,删除这个文件通常是解决 "Execution data for class ... does not match" 
> 这类问题的一个常见步骤,因为它可能包含了与当前类文件不匹配的旧数据。
> 

那就试一下吧,我甚至都没有备份这个文件,直接删除。

果然,问题解决。

总结

我的推测是某次打包打到一半的异常终止,或是频繁的代码文件切换和修改, 导致了文件内容出现了不一致的地方,而且配置中还有一个参数是 append = true, 可能与这次问题的出现有关,但这个解决方案也没有什么成本,如果你也遇到 jacoco 警告「类文件与执行数据不一致」的时候,不妨试试删掉 jacoco.exec 重新打包看看, 我觉得不会有什么损失。

这些天因为 jacoco 的糟心事实在是不少,不光是警告,还有报错: 有类文件不匹配的,有无法注入文件的,有报告生成失败的,非常让人心烦, 我开发机的 ssd 也不少年了,不知道是不是大限将至。目前还没有完全摸清问题, 临时的方案是先放另一台机器上打包试试,不过也是要先 mvnw clean, 再删 jacoco.exec,然后重新打包。