10

编辑:我分析了给定的答案。特别是我测试了 Till Brychcy 的假设,该假设似乎成立,但提出了更多问题。我将该分析添加到问题的末尾,在以下大分隔符之后:“------------Till Brychcy 的答案分析----------”

它以某种方式在 IJ 中编译和运行,但mvn clean install无法编译测试

2个模块的解释,以及失败的原因

我有一个带有 2 个模块的 java 9 maven 项目:apimodclientmod. 模块clientmod依赖于模块apimod(这些模块既是 Maven 模块又是 Java 9 模块)。

此外,我希望模块clientmod不仅能够重用来自 的生产代码apimod,还能够重用测试代码。这是一种常见的模式,我在 Java 8 中多次使用。使用 Java 9(与 Java 10 相同)它也可以正常工作,只要我不声明module-info.java(也就是说,只要我不声明)与模块系统一起运行)。

但是一旦我这样做,启用测试依赖项似乎会禁用生产依赖项:(api.Base模块src/mainapimod)不再从client.test.DerivedTestsrc/test模块类)可见clientmod。测试不再编译。

这是 Maven 还是 Java 9 中的错误?这是最新版本:Java 9.0.4(与 Java 10 相同)、Maven 3.5.3、maven-compiler-plugin 3.7.0

到目前为止我的分析

编码

源代码位于:

git clone https://github.com/vandekeiser/wires.git

我用分支中的失败测试“二分法”了这个问题:

git checkout MINIMIZE_ISSUE
`mvn clean install` 

-> BUILD FAIL(测试中的编译错误clientmod

Maven 测试范围的依赖

我希望模块clientmod不仅能够重用生产代码,apimod而且能够重用测试代码。使用 Maven,您可以这样做 ( clientmod/pom.xml):

<dependency>
    <groupId>fr.cla</groupId>
    <artifactId>apimod</artifactId>
    <version>${project.version}</version>
    <classifier>tests</classifier>
    <scope>test</scope>
</dependency>

Java 9 模块

module apimod {
    exports api;
}

module clientmod {
    requires apimod;
}

尝试启用两个模块系统时失败

对于 Java 9,如果我同时声明了测试范围的依赖项和 Java 9 模块,则测试不再编译(mvn clean install输出):

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:testCompile (default-testCompile) on project clientmod: Compilation failure
[ERROR] /G:/projets/wires/wires/wires/clientmod/src/test/java/client/test/DerivedTest.java:[8,22] cannot access api.Base
[ERROR]   class file for api.Base not found

使用 javac 重现问题:模块修补中的错误?

就好像启用测试依赖项 ( src/test) 禁用生产依赖项 ( src/main)。我知道在这种情况下 Maven 应该使用 javac--patch-module标志。所以我只使用 javac 重现了这个问题(使用调试输出mvn -X):

相同的编译,跳过 Maven:

javac "G:\projets\wires\wires\wires\clientmod\src\test\java\client\test\DerivedTest.java" \
-d "G:\projets\wires\wires\wires\clientmod\target\test-classes" \
-classpath "G:\projets\wires\wires\wires\clientmod\target\test-classes;" \
--module-path "G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT-tests.jar;G:\projets\wires\wires\wires\clientmod\target\classes;G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT.jar;" \
-sourcepath "G:\projets\wires\wires\wires\clientmod\src\test\java;" \
--release 9 \
-Xlint:all \
--patch-module clientmod="G:\projets\wires\wires\wires\clientmod\target\classes;G:\projets\wires\wires\wires\clientmod\src\test\java;"

相同的编译错误:

G:\projets\wires\wires\wires\clientmod\src\test\java\client test\DerivedTest.java:8: error: cannot access Base
new Derived().equals(null);                         ^
class file for api.Base not found
1 error

我尝试使用应该禁用模块系统的 javac 标志,但它们似乎不存在于我的 64 位 Windows Oracle JVM 中?(Java HotSpot(TM) 64 位服务器 VM(内部版本 9.0.4+11,混合模式):

javac --illegal-access=warn
javac: invalid flag: --permit-illegal-access
javac --permit-illegal-access
javac: invalid flag: --illegal-access=warn

添加(逻辑上不需要,绝望地完成)导出或读取也不会改变任何内容:

--add-reads apimod=ALL-UNNAMED \
--add-reads clientmod=ALL-UNNAMED \
--add-exports apimod/api=ALL-UNNAMED \
--add-exports clientmod/client=ALL-UNNAMED \

mvn -version 的输出:

Apache Maven 3.5.3 (3383c37e1f9e9b3bc3df5050c29c8aff9f295297; 2018-02-24T20:49:05+01:00)
Maven home: G:\software\apache-maven-3.5.3
Java version: 9.0.4, vendor: Oracle Corporation
Java home: C:\Program Files\Java\jdk-9.0.4
Default locale: fr_FR, platform encoding: Cp1252
OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows" 

分析 Till Brychcy 的答案

感谢您的详细回答,本质上是“看起来 maven 似乎还不支持这个用例”-->所以让我们尝试在没有 maven 的情况下重现该问题。我TRY_ADAPT_khmarbaise-MINIMIZE_ISSUE为这些试验创建了分支(对不起,令人困惑的分支名称)。

  1. 调整我以前由 maven 记录的命令行,即:

    javac "G:\projets\wires\wires\wires\clientmod\src\test\java\client\test\DerivedTest.java"
    -d "G:\projets\wires\wires\wires\clientmod\target\test-classes"
    -classpath "G:\projets\wires\wires\wires\clientmod\target\test-classes;"
    --module-path "G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT-tests.jar;G:\projets\wires\wires\wires\clientmod\target\classes;G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT.jar;"
    -sourcepath "G:\projets\wires\wires\wires\clientmod\src\test\java;"
    --release 9
    -Xlint:all
    --patch-module clientmod="G:\projets\wires\wires\wires\clientmod\target\classes;G:\projets\wires\wires\wires\clientmod\src\test\java;"
    --add-reads apimod=ALL-UNNAMED
    --add-reads clientmod=ALL-UNNAMED
    --add-exports apimod/api=ALL-UNNAMED
    --add-exports clientmod/client=ALL-UNNAMED
    --add-modules apimod
    
  2. G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT-tests.jar;--module-path

  3. 我将相同的内容添加到--patch-module clientmod,给我:

    javac "G:\projets\wires\wires\wires\clientmod\src\test\java\client\test\DerivedTest.java" \
    -d "G:\projets\wires\wires\wires\clientmod\target\test-classes" \
    -classpath "G:\projets\wires\wires\wires\clientmod\target\test-classes;" \
    --module-path "G:\projets\wires\wires\wires\clientmod\target\classes;G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT.jar;" \
    -sourcepath "G:\projets\wires\wires\wires\clientmod\src\test\java;" \
    --release 9 \
    -Xlint:all \
    --patch-module clientmod="G:\projets\wires\wires\wires\clientmod\target\classes;G:\projets\wires\wires\wires\clientmod\src\test\java;G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT-tests.jar;" \
    --add-reads apimod=ALL-UNNAMED \
    --add-reads clientmod=ALL-UNNAMED \
    --add-exports apimod/api=ALL-UNNAMED \
    --add-exports clientmod/client=ALL-UNNAMED \
    --add-modules apimod
    

-->好的,现在可以编译了!因此,您认为 maven-compiler-plugin 或 maven 不支持这一点的假设似乎得到了验证。 我认为它应该在我使用的版本中得到支持,这是最新的。我想知道从哪里开始检查..

无论如何,与此同时,我尝试显式配置 maven-compiler-plugin 但无济于事。我尝试的一般结构是(maven-compiler-plugin.version = 3.7.0):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <release>${java.version}</release>
        <compilerArgs>
            [...]
        </compilerArgs>
    </configuration>
</plugin>

我尝试了以下compilerArgs(对应的 xml 注释中的 mvn clean install 错误):

1:

<!--1. Syntaxically OK, but:-->
<!--[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project apimod: Compilation failure: Compilation failure:-->
<!--[ERROR] /G:/projets/wires/wires/wires/apimod/src/main/java/api/Base.java:[1,1]-->
<!--file should be on source path, or on patch path for module-->
<!--[ERROR] /G:/projets/wires/wires/wires/apimod/src/main/java/module-info.java:[1,1]-->
<!--file should be on source path, or on patch path for module-->
<compilerArgs>
    <arg>--class-path=/G/projets/wires/wires/wires/clientmod/target/test-classes;</arg>
    <arg>
        --module-path=/G/projets/wires/wires/wires/clientmod/target/classes;/G/projets/wires/wires/wires/apimod/target/apimod-1.0-SNAPSHOT.jar;
    </arg>
    <arg>--source-path=/G/projets/wires/wires/wires/clientmod/src/test/java;</arg>
    <arg>-Xlint:all</arg>
    <arg>
        --patch-module=clientmod=/G/projets/wires/wires/wires/clientmod/target/classes;/G/projets/wires/wires/wires/clientmod/src/test/java;/G/projets/wires/wires/wires/apimod/target/apimod-1.0-SNAPSHOT-tests.jar;
    </arg>
    <arg>--add-reads=apimod=ALL-UNNAMED</arg>
    <arg>--add-reads=clientmod=ALL-UNNAMED</arg>
    <arg>--add-exports=apimod/api=ALL-UNNAMED</arg>
    <arg>--add-exports=clientmod/client=ALL-UNNAMED</arg>
    <arg>--add-modules=apimod</arg>
</compilerArgs>

2:

<!--2.-->
<!--[ERROR] Please refer to dump files (if any exist) [date]-jvmRun[N].dump, [date].dumpstream and [date]-jvmRun[N].dumpstream.-->
<!--[ERROR] There was an error in the forked process-->
<!--[ERROR] api/foo/BaseTest (wrong name: apimod/api/foo/BaseTest)-->
<!--[ERROR] org.apache.maven.surefire.booter.SurefireBooterForkException: There was an error in the forked process-->
<!--[ERROR] api/foo/BaseTest (wrong name: apimod/api/foo/BaseTest)-->
<!--[ERROR]         at org.apache.maven.plugin.surefire.booterclient.ForkStarter.fork(ForkStarter.java:673)-->
<compilerArgs>
    <arg>--module-source-path=./*/src/main/java;./*/src/test/java/;</arg>
    <arg>
        --source-path=/G/projets/wires/wires/wires/apimod/src/main/java;/G/projets/wires/wires/wires/apimod/src/test/java;/G/projets/wires/wires/wires/clientmod/src/test/java;/G/projets/wires/wires/wires/clientmod/src/main/java;
    </arg>
    <arg>-Xlint:all</arg>
    <arg>
        --patch-module=clientmod=/G/projets/wires/wires/wires/clientmod/target/classes;/G/projets/wires/wires/wires/clientmod/src/test/java;/G/projets/wires/wires/wires/apimod/target/apimod-1.0-SNAPSHOT.jar;/G/projets/wires/wires/wires/apimod/target/apimod-1.0-SNAPSHOT-tests.jar;
    </arg>
    <arg>--add-reads=apimod=ALL-UNNAMED</arg>
    <arg>--add-reads=clientmod=ALL-UNNAMED</arg>
    <arg>--add-exports=apimod/api=ALL-UNNAMED</arg>
    <arg>--add-exports=clientmod/client=ALL-UNNAMED</arg>
    <arg>--add-modules=apimod</arg>
</compilerArgs>    
4

2 回答 2

2

看起来 maven 还不支持这个用例。

问题是它apimod-1.0-SNAPSHOT-tests.jar被视为自动模块,其自动模块名称(从文件名派生)是“ apimod”,因此实际模块(稍后出现在模块路径中)被忽略apimodapimod-1.0-SNAPSHOT.jar

Maven 应该检测apimod-1.0-SNAPSHOT-tests.jar属于apimod并使用--patch-module apimod=G:\projets\wires\wires\wires\apimod\target\apimod-1.0-SNAPSHOT-tests.jar而不是使其成为--module-path

于 2018-05-02T12:44:13.207 回答
2

现在可以了!这一切都在那里解释:https ://issues.apache.org/jira/browse/MCOMPILER-348

问题是名称冲突:在问题减少分支和 main-dev 分支中将 maven 模块的名称更改为与修复它的 java 模块不同的名称。

我仍然有一个警告(见链接),现在我忽略它,但它需要去。

------------链接内容的副本以防JIRA消失-----------

Robert Scholte:我的猜测是 apimod-1.0-SNAPSHOT-tests.jar 应该在类路径上,而不是在模块路径上。我能想到它被添加到模块路径的唯一原因是它的自动模块名称也是 apimod。将名称更改为不与自动模块名称冲突的名称可以解决问题(例如 info.example.apimod ) 一般来说,请选择一个更独特的模块名称,它必须是全局唯一的(就像类路径上的类一样)。添加 reverseDNS 或共享包。固定链接 编辑 删除

:是的,就是这样,非常感谢!我认为这是一个错误(但是,可能还有一些更小的错误?我还没有看更多,但仍然有一个 javac 警告 - 请参阅本评论结束)。

它修复了二分法分支和主分支: -在二分法分支中,我为 java 模块添加了一个前缀 -当我回到主分支时,我将 Maven 模块重命名为wires.core --> wires-core 等等on,和java模块共享根包,也避免了名字冲突

用“Java 谜题”的术语来说,这里的教训可能是双重的: - 对于 API (Maven) 的用户:遵循命名约定:我不应该用 '.' 命名 Maven 模块。分隔器。我会不假思索地避免名称冲突。-对于 API 的设计者:如果你能检测到这种冲突,也许会记录一条有意义的消息,因为很难看到它何时发生

最后,我仍然收到 javac 警告。所以现在我必须忽略出口警告。

我还没有调查它,但这里有:

[WARNING] COMPILATION WARNING :
[INFO] -------------------------------------------------------------
[WARNING] /G:/projets/wires/wires/wires/wires-core/src/test/java/fr/cla/wires/core/MavenVsJavaModulesReproduceTest.java:[9,54] class fr.cla.wires.support.oo.ddd.AbstractValueObjectTest in module  is not exported
[INFO] 1 warning
[INFO] -------------------------------------------------------------
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /G:/projets/wires/wires/wires/wires-core/src/test/java/fr/cla/wires/core/MavenVsJavaModulesReproduceTest.java: warnings found and -Werror specified
[INFO] 1 error

要重现它,请在主分支 ( https://github.com/vandekeiser/wires.git ) 中签出标签 WARN_IF_UNCOMMENT 并删除以下导出异常:-Xlint:all,-serial,-exports

我不能(也不应该)添加出口 fr.cla.wires.support.oo.ddd;进入 module-info.java 因为它指的是 src/test 中的包

这就是它现在的位置

于 2018-07-17T21:57:12.523 回答