diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java b/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java
index 8db3be1c2863..5d63495093e1 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java
@@ -37,11 +37,23 @@ public interface SourceRoot {
* The path is relative to the POM file.
*
*
Default implementation
- * The default value is src/{@linkplain #scope() scope}/{@linkplain #language() language}
- * as a relative path. Implementation classes may override this default with an absolute path instead.
+ * The default value depends on whether a {@linkplain #module() module name} is specified in this source root:
+ *
+ * -
+ * If no module name, then the default directory is
+ *
src/{@linkplain #scope() scope}/{@linkplain #language() language}
.
+ * -
+ * If a module name is present, then the default directory is
+ *
src/{@linkplain #language() language}/{@linkplain #module() module}/{@linkplain #scope() scope}
.
+ *
+ *
+ *
+ * These default values are relative directories.
+ * Implementation classes may override this default with absolute paths instead.
*/
default Path directory() {
- return Path.of("src", scope().id(), language().id());
+ return module().map((module) -> Path.of("src", language().id(), module, scope().id()))
+ .orElseGet(() -> Path.of("src", scope().id(), language().id()));
}
/**
diff --git a/api/maven-api-model/src/main/mdo/maven.mdo b/api/maven-api-model/src/main/mdo/maven.mdo
index f0e9f7ca9f5f..94db81076c61 100644
--- a/api/maven-api-model/src/main/mdo/maven.mdo
+++ b/api/maven-api-model/src/main/mdo/maven.mdo
@@ -2011,9 +2011,15 @@
usage (main code, tests, etc.) is specified by the {@code scope} element.
Default source directories
- If no source directories are specified, the defaults are {@code src/${scope}/${lang}} where
- {@code ${scope}} is the value of the {@link #scope} element (typically {@code main} or {@code test}) and
- {@code ${lang}} is the value of the {@link #lang} element (typically {@code java} or {@code resources}).
+ If no source directories are specified, the default values depend on whether module names are specified:
+
+ - {@code src/${scope}/${lang}} if no module is specified
+ - {@code src/${lang}/${module}/${scope}} if a module is specified
+
+ where
+ {@code ${scope}} is the value of the {@link #scope} element (typically {@code main} or {@code test}),
+ {@code ${lang}} is the value of the {@link #lang} element (typically {@code java} or {@code resources}),
+ and {@code ${module}} is the optional {@link #module} element.
]]>
4.1.0+
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSourceRoot.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSourceRoot.java
index 0c5f9e54e3ce..dc22b95a394c 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSourceRoot.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSourceRoot.java
@@ -79,7 +79,12 @@ public DefaultSourceRoot(final Session session, final Path baseDir, final Source
if (value != null) {
directory = baseDir.resolve(value);
} else {
- directory = baseDir.resolve("src").resolve(scope.id()).resolve(language.id());
+ Path src = baseDir.resolve("src");
+ if (moduleName != null) {
+ directory = src.resolve(language.id()).resolve(moduleName).resolve(scope.id());
+ } else {
+ directory = src.resolve(scope.id()).resolve(language.id());
+ }
}
value = nonBlank(source.getTargetVersion());