Skip to content

Commit

Permalink
[MNG-8210] Replace Maven "module" term by "subproject" (#1651)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
  • Loading branch information
gnodet and desruisseaux committed Aug 29, 2024
1 parent e11b475 commit b370e5e
Show file tree
Hide file tree
Showing 25 changed files with 231 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@
boolean projectRequired() default true;

/**
* if the Mojo uses the Maven project and its child modules.
* @return uses the Maven project and its child modules
* if the Mojo uses the Maven project and its subprojects.
* @return uses the Maven project and its subprojects
*/
boolean aggregator() default false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@

public interface ModelBuilder extends Service {

List<String> VALID_MODEL_VERSIONS = List.of("4.0.0", "4.1.0");
String MODEL_VERSION_4_0_0 = "4.0.0";

String MODEL_VERSION_4_1_0 = "4.1.0";

List<String> VALID_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0);

ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ enum Version {
V20,
V30,
V31,
V40
V40,
V41
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

/**
* A Source specific to load POMs. The {@link #resolve(ModelLocator, String)} method
* will be used to find POMs for children modules.
* will be used to find POMs for subprojects.
*
* @since 4.0.0
*/
Expand Down
30 changes: 22 additions & 8 deletions api/maven-api-model/src/main/mdo/maven.mdo
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@
<!-- SCM -->
<!-- ====================================================================== -->

<field xdoc.separator="blank" xml.insertParentFieldsUpTo="modules">
<field xdoc.separator="blank" xml.insertParentFieldsUpTo="subprojects">
<name>scm</name>
<version>4.0.0+</version>
<description>Specification for the SCM used by the project, such as CVS, Subversion, etc.</description>
Expand Down Expand Up @@ -527,11 +527,25 @@
<fields>
<field xdoc.separator="blank">
<name>modules</name>
<version>4.0.0+</version>
<description>The modules (sometimes called subprojects) to build as a part of this
project. Each module listed is a relative path to the directory containing the module.
To be consistent with the way default urls are calculated from parent, it is recommended
to have module names match artifact ids.</description>
<version>4.0.0/4.1.0</version>
<description>
@deprecated Use {@link #subprojects} instead.
</description>
<association>
<type>String</type>
<multiplicity>*</multiplicity>
</association>
<annotations>
<annotation>@Deprecated</annotation>
</annotations>
</field>
<field xdoc.separator="blank">
<name>subprojects</name>
<version>4.1.0</version>
<description>The subprojects (formerly called modules) to build as a part of this
project. Each subproject listed is a relative path to the directory containing the subproject.
To be consistent with the way default URLs are calculated from parent, it is recommended
to have subproject names match artifact ids.</description>
<association>
<type>String</type>
<multiplicity>*</multiplicity>
Expand Down Expand Up @@ -755,8 +769,8 @@
<name>defaultGoal</name>
<version>3.0.0+</version>
<description>The default goal (or phase in Maven 2) to execute when none is specified for
the project. Note that in case of a multi-module build, only the default goal of the top-level
project is relevant, i.e. the default goals of child modules are ignored. Since Maven 3,
the project. Note that in case of a build with subprojects, only the default goal of the top-level
project is relevant, i.e. the default goals of subprojects are ignored. Since Maven 3,
multiple goals/phases can be separated by whitespace.</description>
<type>String</type>
</field>
Expand Down
5 changes: 5 additions & 0 deletions maven-api-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ under the License.
<name>Maven API Implementation</name>
<description>Provides the implementation classes for the Maven API</description>

<properties>
<!-- in: DefaultModelValidator, DefaultModelBuilder -->
<checkstyle.violation.ignore>FileLength</checkstyle.violation.ignore>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -40,6 +41,7 @@
import java.util.stream.Stream;

import org.apache.maven.api.Session;
import org.apache.maven.api.Type;
import org.apache.maven.api.VersionRange;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.di.Inject;
Expand Down Expand Up @@ -305,7 +307,7 @@ private Model readEffectiveModel(
// Maven 3.x is always using 4.0.0 version to load the supermodel, so
// do the same when loading a dependency. The model validator will also
// check that field later.
superModelVersion = "4.0.0";
superModelVersion = MODEL_VERSION_4_0_0;
}
ModelData superData = new ModelData(null, getSuperModel(superModelVersion));

Expand Down Expand Up @@ -737,6 +739,32 @@ private Model doReadFileModel(

if (modelSource.getPath() != null) {
model = model.withPomFile(modelSource.getPath());

// subprojects discovery
if (model.getSubprojects().isEmpty()
&& model.getModules().isEmpty()
// only discover subprojects if POM > 4.0.0
&& !MODEL_VERSION_4_0_0.equals(model.getModelVersion())
// and if packaging is POM (we check type, but the session is not yet available,
// we would require the project realm if we want to support extensions
&& Type.POM.equals(model.getPackaging())) {
List<String> subprojects = new ArrayList<>();
try (Stream<Path> files = Files.list(model.getProjectDirectory())) {
for (Path f : files.toList()) {
if (Files.isDirectory(f)) {
Path subproject = modelProcessor.locateExistingPom(f);
if (subproject != null) {
subprojects.add(f.getFileName().toString());
}
}
}
if (!subprojects.isEmpty()) {
model = model.withSubprojects(subprojects);
}
} catch (IOException e) {
problems.add(Severity.FATAL, ModelProblem.Version.V41, "Error discovering subprojects", e);
}
}
}

problems.setSource(model);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,16 @@ private void doLoadFullReactor() {
ModelBuilderRequest gaBuildingRequest =
ModelBuilderRequest.build(request, ModelSource.fromPath(pom));
Model rawModel = defaultModelBuilder.readFileModel(gaBuildingRequest, problems);
for (String module : rawModel.getModules()) {
Path moduleFile = defaultModelBuilder
List<String> subprojects = rawModel.getSubprojects();
if (subprojects == null) {
subprojects = rawModel.getModules();
}
for (String subproject : subprojects) {
Path subprojectFile = defaultModelBuilder
.getModelProcessor()
.locateExistingPom(pom.getParent().resolve(module));
if (moduleFile != null) {
toLoad.add(moduleFile);
.locateExistingPom(pom.getParent().resolve(subproject));
if (subprojectFile != null) {
toLoad.add(subprojectFile);
}
}
} catch (ModelBuilderException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -65,6 +64,7 @@
import org.apache.maven.api.model.Repository;
import org.apache.maven.api.model.Resource;
import org.apache.maven.api.services.BuilderProblem.Severity;
import org.apache.maven.api.services.ModelBuilder;
import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelProblem.Version;
import org.apache.maven.api.services.ModelProblemCollector;
Expand All @@ -73,6 +73,8 @@
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.model.v4.MavenTransformer;

import static org.apache.maven.internal.impl.model.DefaultModelBuilder.NAMESPACE_PREFIX;

/**
*/
@Named
Expand All @@ -81,8 +83,7 @@ public class DefaultModelValidator implements ModelValidator {
public static final String BUILD_ALLOW_EXPRESSION_IN_EFFECTIVE_PROJECT_VERSION =
"maven.build.allowExpressionInEffectiveProjectVersion";

public static final List<String> VALID_MODEL_VERSIONS =
Collections.unmodifiableList(Arrays.asList("4.0.0", "4.1.0"));
public static final List<String> VALID_MODEL_VERSIONS = ModelBuilder.VALID_MODEL_VERSIONS;

private static final Pattern EXPRESSION_NAME_PATTERN = Pattern.compile("\\$\\{(.+?)}");
private static final Pattern EXPRESSION_PROJECT_NAME_PATTERN = Pattern.compile("\\$\\{(project.+?)}");
Expand Down Expand Up @@ -136,7 +137,6 @@ protected Activation.Builder transformActivation_ActiveByDefault(
protected Activation.Builder transformActivation_File(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("file", Activation::getFile));
Optional.ofNullable(target.getFile());
try {
return super.transformActivation_File(creator, builder, target);
} finally {
Expand Down Expand Up @@ -335,7 +335,7 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem
if (request.getValidationLevel() == ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL) {
// profiles: they are essential for proper model building (may contribute profiles, dependencies...)
HashSet<String> minProfileIds = new HashSet<>();
for (org.apache.maven.api.model.Profile profile : m.getProfiles()) {
for (Profile profile : m.getProfiles()) {
if (!minProfileIds.add(profile.getId())) {
addViolation(
problems,
Expand All @@ -362,6 +362,61 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem
m.getLocation("modules"));
}
}
String modelVersion = m.getModelVersion();
if (modelVersion == null) {
String namespace = m.getNamespaceUri();
if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
modelVersion = namespace.substring(NAMESPACE_PREFIX.length());
}
}
if (Objects.equals(modelVersion, ModelBuilder.MODEL_VERSION_4_0_0)) {
if (!m.getSubprojects().isEmpty()) {
addViolation(
problems,
Severity.ERROR,
Version.V40,
"subprojects",
null,
"unexpected subprojects element",
m.getLocation("subprojects"));
}
} else {
Set<String> subprojects = new HashSet<>();
for (int i = 0, n = m.getSubprojects().size(); i < n; i++) {
String subproject = m.getSubprojects().get(i);
if (!subprojects.add(subproject)) {
addViolation(
problems,
Severity.ERROR,
Version.V41,
"subprojects.subproject[" + i + "]",
null,
"specifies duplicate subproject " + subproject,
m.getLocation("subprojects"));
}
}
if (!modules.isEmpty()) {
if (subprojects.isEmpty()) {
addViolation(
problems,
Severity.WARNING,
Version.V41,
"modules",
null,
"deprecated modules element, use subprojects instead",
m.getLocation("modules"));
} else {
addViolation(
problems,
Severity.ERROR,
Version.V41,
"modules",
null,
"cannot use both modules and subprojects element",
m.getLocation("modules"));
}
}
}

Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0);

Expand Down Expand Up @@ -928,7 +983,7 @@ private void validate20RawDependencies(

private void validate20RawDependenciesSelfReferencing(
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Model m,
List<Dependency> dependencies,
String prefix,
ModelBuilderRequest request) {
Expand Down Expand Up @@ -959,7 +1014,7 @@ private void validate20RawDependenciesSelfReferencing(

private void validateEffectiveDependencies(
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Model m,
List<Dependency> dependencies,
boolean management,
ModelBuilderRequest request) {
Expand Down Expand Up @@ -1020,11 +1075,7 @@ private void validateEffectiveDependencies(
}

private void validateEffectiveModelAgainstDependency(
String prefix,
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Dependency d,
ModelBuilderRequest request) {
String prefix, ModelProblemCollector problems, Model m, Dependency d, ModelBuilderRequest request) {
String key = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion()
+ (d.getClassifier() != null ? ":" + d.getClassifier() : EMPTY);
String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ static Model transform(Model model, MavenProject project) {
String packaging = model.getPackaging();
if (POM_PACKAGING.equals(packaging)) {
// raw to consumer transform
model = model.withRoot(false).withModules(null);
model = model.withRoot(false).withModules(null).withSubprojects(null);
if (model.getParent() != null) {
model = model.withParent(model.getParent().withRelativePath(null));
}
Expand Down Expand Up @@ -294,6 +294,7 @@ private static boolean isEmpty(Profile profile) {
|| profile.getDependencyManagement().getDependencies().isEmpty())
&& profile.getDistributionManagement() == null
&& profile.getModules().isEmpty()
&& profile.getSubprojects().isEmpty()
&& profile.getProperties().isEmpty()
&& profile.getRepositories().isEmpty()
&& profile.getPluginRepositories().isEmpty()
Expand Down
Loading

0 comments on commit b370e5e

Please sign in to comment.