Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Order of parsed Maven modules is sometimes not correct #601

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 55 additions & 28 deletions src/main/java/org/openrewrite/maven/MavenMojoProjectParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,9 @@ public Map<MavenProject, Xml.Document> parseMaven(List<MavenProject> mavenProjec
MavenProject topLevelProject = mavenSession.getTopLevelProject();
logInfo(topLevelProject, "Resolving Poms...");

Set<Path> allPoms = new LinkedHashSet<>();
mavenProjects.forEach(p -> collectPoms(p, allPoms));
for (MavenProject mavenProject : mavenProjects) {
mavenSession.getProjectDependencyGraph().getUpstreamProjects(mavenProject, true).forEach(p -> collectPoms(p, allPoms));
}
List<Path> allPoms = collectLocalAndUpstreamPoms(mavenSession);


MavenParser.Builder mavenParserBuilder = MavenParser.builder().mavenConfig(baseDir.resolve(".mvn/maven.config"));

MavenSettings settings = buildSettings();
Expand Down Expand Up @@ -459,35 +457,64 @@ public Map<MavenProject, Xml.Document> parseMaven(List<MavenProject> mavenProjec
return projectMap;
}

/**
* Recursively navigate the maven project to collect any poms that are local (on disk)
*
* @param project A maven project to examine for any children/parent poms.
* @param paths A list of paths to poms that have been collected so far.
*/
private void collectPoms(MavenProject project, Set<Path> paths) {
paths.add(pomPath(project));

// children
if (project.getCollectedProjects() != null) {
for (MavenProject child : project.getCollectedProjects()) {
Path path = pomPath(child);
if (!paths.contains(path)) {
collectPoms(child, paths);
}
}
private List<Path> collectLocalAndUpstreamPoms(MavenSession mavenSession) {
List<Path> allPoms = new ArrayList<>();
// mavenSession.getProjects() or mavenSession.getAllProjects() ?
List<MavenProject> mavenProjects = mavenSession.getProjects();
mavenProjects.forEach(p -> collectProjectPoms(mavenSession, allPoms));
collectUpstreamPoms(mavenProjects, allPoms);
return allPoms;
}

private void collectUpstreamPoms(List<MavenProject> mavenProjects, List<Path> allPoms) {
for (MavenProject mavenProject : mavenProjects) {
mavenSession.getProjectDependencyGraph().getUpstreamProjects(mavenProject, true).forEach(p -> collectUpstreamPoms(mavenProject, allPoms));
}
}

MavenProject parent = project.getParent();
while (parent != null && parent.getFile() != null) {
Path path = pomPath(parent);
if (!paths.contains(path)) {
collectPoms(parent, paths);
private void collectUpstreamPoms(MavenProject mavenProject, List<Path> allPoms) {
if(mavenProject.getCollectedProjects() != null) {
for(MavenProject project : mavenProject.getCollectedProjects()) {
Path path = pomPath(project);
if (!allPoms.contains(path)) {
collectUpstreamPoms(project, allPoms);
}
}
parent = parent.getParent();
}
}

/**
* Collect Paths of all projects that belong to the current Maven reactor build.
*
* @param session The current Maven session
* @param paths A list of paths to poms that have been collected so far.
*/
private void collectProjectPoms(MavenSession session, List<Path> paths) {
List<Path> collect = session.getProjects().stream().map(p -> p.getFile().toPath()).collect(toList());
paths.addAll(collect);
//
// paths.add(pomPath(session.));
//
// // children
// if (session.getProjects() != null) {
// for (MavenProject child : /*project.getCollectedProjects()*/ session.getProjects()) {
// Path path = pomPath(child);
// if (!paths.contains(path)) {
// collectPoms(session, paths);
// }
// }
// }
//
// MavenProject parent = session.getCurrentProject().getParent();
// while (parent != null && parent.getFile() != null) {
// Path path = pomPath(parent);
// if (!paths.contains(path)) {
// collectPoms(parent, paths);
// }
// parent = parent.getParent();
// }
}

private static Path pomPath(MavenProject mavenProject) {
Path pomPath = mavenProject.getFile().toPath();
// org.codehaus.mojo:flatten-maven-plugin produces a synthetic pom unsuitable for our purposes, use the regular pom instead
Expand Down
126 changes: 122 additions & 4 deletions src/test/java/org/openrewrite/maven/MavenMojoProjectParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@
import org.apache.maven.plugin.logging.SystemStreamLog;
import org.apache.maven.project.MavenProject;
import org.apache.maven.rtinfo.internal.DefaultRuntimeInformation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Parser;
import org.openrewrite.SourceFile;
import org.openrewrite.java.marker.JavaVersion;
import org.openrewrite.marker.Marker;
import org.openrewrite.tree.ParsingEventListener;
import org.openrewrite.tree.ParsingExecutionContextView;
import org.openrewrite.xml.tree.Xml;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchIllegalArgumentException;
import static org.junit.jupiter.api.Assertions.*;

/**
Expand All @@ -31,4 +42,111 @@ void givenNoJavaVersionInformationExistsInMavenThenJavaSpecificationVersionShoul
assertThat(marker.getSourceCompatibility()).isEqualTo(System.getProperty("java.specification.version"));
assertThat(marker.getTargetCompatibility()).isEqualTo(System.getProperty("java.specification.version"));
}

@Test
@DisplayName("order of build files should reflect reactor build order")
void orderOfBuildFilesShouldReflectReactorBuildOrder(@TempDir Path tempDir) throws NoSuchMethodException, IOException {
MavenMojoProjectParser sut = new MavenMojoProjectParser(new SystemStreamLog(), null, false, null, new DefaultRuntimeInformation(), false, Collections.EMPTY_LIST, Collections.EMPTY_LIST, -1, null, null, false);
/*Method collectPoms = MavenMojoProjectParser.class.getDeclaredMethod("collectPoms", MavenProject.class, Set.class);
collectPoms.setAccessible(true);
Set<Path> allPoms = new LinkedHashSet<>();
Object mavenProjects;
collectPoms.invoke(sut, mavenProjects, allPoms);*/
String pomXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
" <modelVersion>4.0.0</modelVersion>\n" +
" <groupId>com.example</groupId>\n" +
" <artifactId>multi-module-1</artifactId>\n" +
" <version>1.0.0</version>\n" +
" <packaging>pom</packaging>\n" +
" <properties>\n" +
" <maven.compiler.target>17</maven.compiler.target>\n" +
" <maven.compiler.source>17</maven.compiler.source>\n" +
" <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n" +
" </properties>\n" +
" <dependencies>\n" +
" <dependency>\n" +
" <groupId>org.springframework.boot</groupId>\n" +
" <artifactId>spring-boot-starter</artifactId>\n" +
" <version>3.1.1</version>\n" +
" </dependency>\n" +
" </dependencies>\n" +
" <modules>\n" +
" <module>module-a</module>\n" +
" <module>module-b</module>\n" +
" </modules>\n" +
"</project>";

String moduleAPom = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
" <modelVersion>4.0.0</modelVersion>\n" +
" <parent>\n" +
" <groupId>com.example</groupId>\n" +
" <artifactId>multi-module-1</artifactId>\n" +
" <version>1.0.0</version>\n" +
" </parent>\n" +
" <artifactId>module-a</artifactId>\n" +
" <dependencies>\n" +
" <dependency>\n" +
" <groupId>com.example</groupId>\n" +
" <artifactId>module-b</artifactId>\n" +
" <version>${project.version}</version>\n" +
" </dependency>\n" +
" </dependencies>\n" +
"</project>";

String moduleBPom = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
" <modelVersion>4.0.0</modelVersion>\n" +
" <parent>\n" +
" <groupId>com.example</groupId>\n" +
" <artifactId>multi-module-1</artifactId>\n" +
" <version>1.0.0</version>\n" +
" </parent>\n" +
" <artifactId>module-b</artifactId>\n" +
"</project>";


Path rootPomFile = tempDir.resolve("pom.xml");
Files.write(rootPomFile, pomXml.getBytes());

Path aPath = tempDir.resolve("module-a");
aPath.toFile().mkdirs();
Path aFile = aPath.resolve("pom.xml");
Files.write(aFile, moduleAPom.getBytes());

Path bPath = tempDir.resolve("module-b");
Path bFile = bPath.resolve("pom.xml");
bPath.toFile().mkdirs();
Files.write(bFile, moduleBPom.getBytes());

MavenProject rootProject = new MavenProject();// mock(MavenProject.class);
MavenProject aProject = new MavenProject();
MavenProject bProject = new MavenProject();

List<MavenProject> mavenProjects = new ArrayList<>();
mavenProjects.add(bProject);
mavenProjects.add(rootProject);
mavenProjects.add(aProject);
Map<MavenProject, List<Marker>> markers = new LinkedHashMap<>();
ExecutionContext ctx = new InMemoryExecutionContext(Assertions::fail);
List<String> capturedResourcePaths = new ArrayList<>();
new ParsingExecutionContextView(ctx).setParsingListener((input, sourceFile) -> {
capturedResourcePaths.add(sourceFile.getSourcePath().toString());
});
// Map<MavenProject, Xml.Document> mavenProjectDocumentMap = sut.parseMaven(mavenProjects, markers, ctx);
MavenProject mavenProject = new MavenProject();
mavenProject.setFile(rootPomFile.toFile());
sut.parseMaven(mavenProject, new ArrayList<>(), ctx);

assertThat(capturedResourcePaths.get(0)).isEqualTo("pom.xml");
assertThat(capturedResourcePaths.get(1)).isEqualTo("module-b/pom.xml");
assertThat(capturedResourcePaths.get(2)).isEqualTo("module-a/pom.xml");
}
}
Loading