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..9e8f71285e7a 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,26 @@ 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 #module() module}/{@linkplain #scope() scope}/{@linkplain #language() language}
.
+ *
+ *
+ *
+ * The default value is relative.
+ * Implementation may override with absolute path instead.
*/
default Path directory() {
- return Path.of("src", scope().id(), language().id());
+ Path src = Path.of("src");
+ return module().map(src::resolve)
+ .orElse(src)
+ .resolve(scope().id())
+ .resolve(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..b447c9571f51 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/${module}/${scope}/${lang}} 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..d3a4e2bb673c 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,11 @@ 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) {
+ src = src.resolve(language.id());
+ }
+ directory = src.resolve(scope.id()).resolve(language.id());
}
value = nonBlank(source.getTargetVersion());