ascheman commented on code in PR #3392:
URL: https://github.com/apache/maven-surefire/pull/3392#discussion_r3620939632
##########
maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java:
##########
@@ -224,4 +240,76 @@ File createArgsFile(
return surefireArgs;
}
}
+
+ /**
+ * Searches for module-info-patch.args generated by maven-compiler-plugin
4.x.
+ * The file is expected at {@code
target/test-classes/META-INF/maven/module-info-patch.args}
+ * or within a module subdirectory at
+ * {@code
target/test-classes/<module>/META-INF/maven/module-info-patch.args}.
+ *
+ * @param patchFile the test classes directory (may be the module
subdirectory)
+ * @return the module-info-patch.args file, or null if not found
+ */
+ @Nullable
+ private static File findModuleInfoPatchArgs(File patchFile) {
Review Comment:
Done in 8527e26b2: `findModuleInfoPatchArgs` now returns `Path` (converting
the incoming `File` once) and the reader consumes it directly.
##########
maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java:
##########
@@ -224,4 +240,76 @@ File createArgsFile(
return surefireArgs;
}
}
+
+ /**
+ * Searches for module-info-patch.args generated by maven-compiler-plugin
4.x.
+ * The file is expected at {@code
target/test-classes/META-INF/maven/module-info-patch.args}
+ * or within a module subdirectory at
+ * {@code
target/test-classes/<module>/META-INF/maven/module-info-patch.args}.
+ *
+ * @param patchFile the test classes directory (may be the module
subdirectory)
+ * @return the module-info-patch.args file, or null if not found
+ */
+ @Nullable
+ private static File findModuleInfoPatchArgs(File patchFile) {
+ // Direct location:
target/test-classes/<module>/META-INF/maven/module-info-patch.args
+ File argsFile = new File(patchFile,
"META-INF/maven/module-info-patch.args");
+ if (argsFile.isFile()) {
+ return argsFile;
+ }
+ // Parent location:
target/test-classes/META-INF/maven/module-info-patch.args
+ File parent = patchFile.getParentFile();
+ if (parent != null) {
+ argsFile = new File(parent,
"META-INF/maven/module-info-patch.args");
+ if (argsFile.isFile()) {
+ return argsFile;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Reads the module-info-patch.args file and appends its directives (e.g.
--add-exports,
+ * --add-opens) to the args builder. Exceptions are --add-reads and
--add-modules, which
+ * are skipped: they may reference named modules that surefire places on
the classpath
+ * rather than the module path. Surefire appends its own
+ * {@code --add-reads <module>=ALL-UNNAMED} instead, and {@code
--add-modules
+ * ALL-MODULE-PATH} is generated by the caller.
+ *
+ * @param args the args builder to append to
+ * @param patchArgs the module-info-patch.args file
+ * @param moduleName the module name for surefire's own --add-reads
+ * @throws IOException if the file cannot be read
+ */
+ private static void appendModuleInfoPatchArgs(StringBuilder args, File
patchArgs, String moduleName)
+ throws IOException {
+ // explicit charset: the compiler writes the args file as UTF-8, the
platform default may differ
+ try (BufferedReader reader =
Files.newBufferedReader(patchArgs.toPath(), StandardCharsets.UTF_8)) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // Skip --add-reads and --add-modules from the file — surefire
manages these itself.
+ // The compiler-generated args may reference named modules
that surefire places on
+ // the classpath rather than the module-path, causing boot
layer errors.
+ if (line.startsWith("--add-reads") ||
line.startsWith("--add-modules")) {
Review Comment:
Yes — removing these skips once the test-scope dependencies move to the
module path is tracked as part of #3090.
##########
maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java:
##########
@@ -224,4 +240,76 @@ File createArgsFile(
return surefireArgs;
}
}
+
+ /**
+ * Searches for module-info-patch.args generated by maven-compiler-plugin
4.x.
+ * The file is expected at {@code
target/test-classes/META-INF/maven/module-info-patch.args}
+ * or within a module subdirectory at
+ * {@code
target/test-classes/<module>/META-INF/maven/module-info-patch.args}.
+ *
+ * @param patchFile the test classes directory (may be the module
subdirectory)
+ * @return the module-info-patch.args file, or null if not found
+ */
+ @Nullable
+ private static File findModuleInfoPatchArgs(File patchFile) {
+ // Direct location:
target/test-classes/<module>/META-INF/maven/module-info-patch.args
+ File argsFile = new File(patchFile,
"META-INF/maven/module-info-patch.args");
+ if (argsFile.isFile()) {
+ return argsFile;
+ }
+ // Parent location:
target/test-classes/META-INF/maven/module-info-patch.args
+ File parent = patchFile.getParentFile();
+ if (parent != null) {
+ argsFile = new File(parent,
"META-INF/maven/module-info-patch.args");
+ if (argsFile.isFile()) {
+ return argsFile;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Reads the module-info-patch.args file and appends its directives (e.g.
--add-exports,
+ * --add-opens) to the args builder. Exceptions are --add-reads and
--add-modules, which
+ * are skipped: they may reference named modules that surefire places on
the classpath
+ * rather than the module path. Surefire appends its own
+ * {@code --add-reads <module>=ALL-UNNAMED} instead, and {@code
--add-modules
+ * ALL-MODULE-PATH} is generated by the caller.
+ *
+ * @param args the args builder to append to
+ * @param patchArgs the module-info-patch.args file
+ * @param moduleName the module name for surefire's own --add-reads
+ * @throws IOException if the file cannot be read
+ */
+ private static void appendModuleInfoPatchArgs(StringBuilder args, File
patchArgs, String moduleName)
+ throws IOException {
+ // explicit charset: the compiler writes the args file as UTF-8, the
platform default may differ
+ try (BufferedReader reader =
Files.newBufferedReader(patchArgs.toPath(), StandardCharsets.UTF_8)) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // Skip --add-reads and --add-modules from the file — surefire
manages these itself.
+ // The compiler-generated args may reference named modules
that surefire places on
+ // the classpath rather than the module-path, causing boot
layer errors.
+ if (line.startsWith("--add-reads") ||
line.startsWith("--add-modules")) {
+ if (line.equals("--add-reads") ||
line.equals("--add-modules")) {
+ // two-line form: the value is on the following line
+ reader.readLine();
+ }
+ continue;
+ }
+ args.append(line).append(NL);
+ }
+ }
+ // Surefire always needs --add-reads <module>=ALL-UNNAMED for its
classpath-based runner
+ args.append("--add-reads")
Review Comment:
Yes — removing these skips once the test-scope dependencies move to the
module path is tracked as part of #3090.
##########
maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java:
##########
@@ -1457,6 +1477,45 @@ private ResolvePathResultWrapper
findModuleDescriptor(File jdkHome, File buildPa
}
}
+ /**
+ * Whether the main build output uses the Maven 4 Module Source Hierarchy
layout:
+ * no module-info.class at the root of the build output directory, but at
least one
+ * immediate subdirectory containing one ({@code
target/classes/<module>/}).
+ * A classic modular build (module-info.class at the root) is never
nested, even if
+ * a subdirectory happens to share the module's name.
+ *
+ * @param buildPath the build output directory (e.g., target/classes)
+ * @return {@code true} for the nested module source hierarchy layout
+ */
+ private static boolean isNestedModuleLayout(File buildPath) {
+ return buildPath != null
+ && buildPath.isDirectory()
+ && !new File(buildPath, "module-info.class").exists()
+ && findNestedModuleDescriptor(buildPath) != null;
+ }
+
+ /**
+ * Searches for a module-info.class in immediate subdirectories of the
given directory.
+ * This supports Maven 4 Module Source Hierarchy where compiled classes
are placed
+ * in {@code target/classes/<module>/} instead of directly in {@code
target/classes/}.
+ *
+ * @param buildPath the build output directory (e.g., target/classes)
+ * @return the subdirectory containing module-info.class, or null if not
found
+ */
+ private static File findNestedModuleDescriptor(File buildPath) {
+ File[] subdirs = buildPath.listFiles(File::isDirectory);
+ if (subdirs != null) {
+ // deterministic pick independent of filesystem iteration order
+ Arrays.sort(subdirs);
Review Comment:
Since the multi-module support (#3393) is merged into this PR, no module is
dropped or picked arbitrarily anymore: all module directories are resolved
(sorted by name for determinism), each contributes its
`--patch-module`/resolution roots, and the primary descriptor is the first
module that has a nested test output directory. Throwing on more than one
module would reject exactly the multi-module builds this PR now supports.
##########
maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java:
##########
@@ -1064,7 +1065,18 @@ private DefaultScanResult scanForTestClasses() throws
MojoFailureException {
}
private DefaultScanResult scanDirectories() throws MojoFailureException {
- DirectoryScanner scanner = new
DirectoryScanner(getTestClassesDirectory(), getIncludedAndExcludedTests());
+ File scanDir = getTestClassesDirectory();
+ // Maven 4 Module Source Hierarchy: test classes may be nested under
<module>/
+ if (scanDir != null && isNestedModuleLayout(getMainBuildPath())) {
Review Comment:
Done in 8527e26b2: the nested module directories are now computed once
(`nestedModuleDirectories`) and passed down; `isNestedModuleLayout` and the
single-result `findNestedModuleDescriptor` are gone.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]