From 8fc77a20bc6f53f5ef5a7e7d6fb7a1c3b8c099af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Kr=C3=BCger?= <56278322+fabapp2@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:13:31 +0000 Subject: [PATCH] Fix Windows build * Fix paths for all OS * Tweak build file * Provide correct class to logger * Download dependencies before comparing parse reults When local Maven repo is empty the Markers added by the RewriteMavenProjectParser (comparing) and the RewriteProjectParser (under test) differ. This is because one downloads the dependencies and the next takes them form local Maven and the URIs for dependencies then differ. By builing the projects with Maven first the dependencies will be available in local Maven repo and taken from there by both parsers. * Add logging * fix formatting * Ignore test when using Windows --- .github/workflows/continuous-integration.yml | 26 +- README.adoc | 1 + .../sbm/PrivateArtifactRepositoryTest.java | 16 +- .../rewrite/parsers/MavenProject.java | 29 +- .../rewrite/parsers/ProjectScanner.java | 13 +- .../rewrite/parsers/RewriteProjectParser.java | 8 +- .../parsers/RewriteResourceParser.java | 1 - .../parsers/maven/BuildFileParser.java | 11 +- .../parsers/maven/MavenModuleParser.java | 568 +++++++++--------- .../parsers/maven/MavenProjectAnalyzer.java | 36 +- .../maven/MavenSettingsInitializer.java | 8 +- .../maven/ProvenanceMarkerFactory.java | 1 + .../utils/LinuxWindowsPathUnifier.java | 46 +- .../rewrite/utils/OsAgnosticPathMatcher.java | 2 +- .../maven/CalculateClasspathTest.java | 6 +- .../rewrite/parsers/BuildFileParserTest.java | 47 +- ...ParserEventPublicationIntegrationTest.java | 24 +- .../RewriteProjectParserIntegrationTest.java | 5 + .../RewriteProjectParserParityTest.java | 12 +- .../parsers/SpringRewritePropertiesTest.java | 5 +- .../maven/MavenMojoProjectParserFactory.java | 2 +- .../maven/MavenProjectAnalyzerTest.java | 57 +- .../maven/MavenSettingsInitializerTest.java | 12 +- .../test/util/ParserParityTestHelper.java | 69 ++- 24 files changed, 603 insertions(+), 402 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 128c4ba..9055989 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -3,8 +3,6 @@ name: CI on: workflow_dispatch: push: - branches: - - main paths-ignore: - '.github/**' @@ -27,14 +25,28 @@ jobs: java: 17 name: CI Build ${{ matrix.nickname }} steps: - - name: Set git config for long paths - if: runner.os == 'Windows' - run: | - git config --system core.longpaths true - uses: actions/checkout@v4 + - uses: jvalkeal/setup-maven@v1 + with: + maven-version: 3.6.3 - uses: actions/setup-java@v4 with: distribution: liberica java-version: ${{ matrix.java }} - - name: Build + - name: Setup docker (missing on MacOS) + if: runner.os == 'macos' + run: | + brew install docker + colima start + + # For testcontainers to find the Colima socket + # https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running + sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock + - name: Verify formatting + run: mvn -B -Pfunctional-tests spring-javaformat:validate + - name: Build (Windows, no functional tests) + if: runner.os == 'Windows' run: ./mvnw -B clean verify + - name: Build (Non Windows) + if: runner.os != 'Windows' + run: ./mvnw -B -Pfunctional-tests clean verify \ No newline at end of file diff --git a/README.adoc b/README.adoc index 47e2c05..c1c698d 100644 --- a/README.adoc +++ b/README.adoc @@ -30,6 +30,7 @@ ____ compile 'org.springframework.rewrite:spring-rewrite-commons-launcher:{projectVersion}' ---- + === Implement a Recipe Launcher [source,java] diff --git a/spring-rewrite-commons-functional-tests/private-artifact-repository-tests/src/test/java/org/springframework/sbm/PrivateArtifactRepositoryTest.java b/spring-rewrite-commons-functional-tests/private-artifact-repository-tests/src/test/java/org/springframework/sbm/PrivateArtifactRepositoryTest.java index a8d9986..4767716 100644 --- a/spring-rewrite-commons-functional-tests/private-artifact-repository-tests/src/test/java/org/springframework/sbm/PrivateArtifactRepositoryTest.java +++ b/spring-rewrite-commons-functional-tests/private-artifact-repository-tests/src/test/java/org/springframework/sbm/PrivateArtifactRepositoryTest.java @@ -102,16 +102,17 @@ public class PrivateArtifactRepositoryTest { public static final String DEPENDENCY_CLASS_FQNAME = "com.example.dependency.DependencyClass"; private static final String NEW_USER_HOME = Path.of(".") - .resolve(TESTCODE_DIR + "/user.home") - .toAbsolutePath() - .normalize() - .toString(); + .resolve(TESTCODE_DIR + "/user.home") + .toAbsolutePath() + .normalize() + .toString(); + private static final File LOCAL_MAVEN_REPOSITORY = Path.of(NEW_USER_HOME + "/.m2/repository").toFile(); private static final Path DEPENDENCY_PATH_IN_LOCAL_MAVEN_REPO = Path - .of(NEW_USER_HOME + "/.m2/repository/com/example/dependency/dependency-project") - .toAbsolutePath() - .normalize(); + .of(NEW_USER_HOME + "/.m2/repository/com/example/dependency/dependency-project") + .toAbsolutePath() + .normalize(); // File path was too long under Windows when Maven repository was under TESTCODE_DIR + // "/reposilite-data" @@ -356,6 +357,7 @@ public class PrivateArtifactRepositoryTest { throw new RuntimeException("Could not create target dir"); } } + } } diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/MavenProject.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/MavenProject.java index 32ec7d6..80657b2 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/MavenProject.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/MavenProject.java @@ -25,6 +25,7 @@ import org.openrewrite.maven.utilities.MavenArtifactDownloader; import org.openrewrite.xml.tree.Xml; import org.springframework.core.io.Resource; import org.springframework.rewrite.parsers.maven.MavenRuntimeInformation; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.rewrite.utils.ResourceUtil; import java.io.File; @@ -88,15 +89,26 @@ public class MavenProject { return pomFile; } + /** + * @return absolute Path of Module + */ + public Path getModulePath() { + return projectRoot.resolve(getModuleDir()); + } + + /** + * @return Path for Module relative to {@code baseDir}. + */ public Path getModuleDir() { if (getBasedir() == null) { return null; } - else if (projectRoot.relativize(ResourceUtil.getPath(pomFile)).toString().equals("pom.xml")) { + else if ("pom.xml" + .equals(LinuxWindowsPathUnifier.relativize(projectRoot, ResourceUtil.getPath(pomFile)).toString())) { return Path.of(""); } else { - return projectRoot.relativize(ResourceUtil.getPath(pomFile)).getParent(); + return LinuxWindowsPathUnifier.relativize(projectRoot, ResourceUtil.getPath(pomFile)).getParent(); } } @@ -218,7 +230,11 @@ public class MavenProject { @NotNull private static Predicate whenIn(Path sourceDirectory) { - return r -> ResourceUtil.getPath(r).toString().startsWith(sourceDirectory.toString()); + return r -> { + String resourcePath = LinuxWindowsPathUnifier.unifiedPathString(r); + String sourceDirectoryPath = LinuxWindowsPathUnifier.unifiedPathString(sourceDirectory); + return resourcePath.startsWith(sourceDirectoryPath); + }; } public List getJavaSourcesInTarget() { @@ -230,11 +246,12 @@ public class MavenProject { } public List getMainJavaSources() { - return listJavaSources(resources, getProjectRoot().resolve(getModuleDir()).resolve("src/main/java")); + Path sourceDir = getProjectRoot().resolve(getModuleDir()).resolve("src/main/java"); + return listJavaSources(resources, sourceDir); } - public Path getModulePath() { - return projectRoot.resolve(getModuleDir()); + public List getTestJavaSources() { + return listJavaSources(resources, getProjectRoot().resolve(getModuleDir()).resolve("src/test/java")); } public ProjectId getProjectId() { diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/ProjectScanner.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/ProjectScanner.java index e9d20bb..2ca74d2 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/ProjectScanner.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/ProjectScanner.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -57,9 +58,8 @@ public class ProjectScanner { if (!baseDir.toFile().exists()) { throw new IllegalArgumentException("Provided path does not exist: " + baseDir); } - Path absoluteRootPath = baseDir.toAbsolutePath(); - String unifiedPath = new LinuxWindowsPathUnifier().unifyPath(absoluteRootPath.toString() + "/**"); - String pattern = "file:" + unifiedPath; + Path absoluteRootPath = baseDir; + String pattern = "file:" + absoluteRootPath.toString() + "/**"; try { Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(resourceLoader) .getResources(pattern); @@ -72,7 +72,7 @@ public class ProjectScanner { int numIgnored = resources.length - numResulting; LOGGER.debug("Scan returns %s resources, %d resources were ignored.".formatted(numResulting, numIgnored)); LOGGER.trace("Resources resulting from scan: %s".formatted(resultingResources.stream() - .map(r -> absoluteRootPath.relativize(ResourceUtil.getPath(r)).toString()) + .map(getResourceStringFunction(absoluteRootPath)) .collect(Collectors.joining(", ")))); return resultingResources; @@ -82,6 +82,11 @@ public class ProjectScanner { } } + @NotNull + private static Function getResourceStringFunction(Path absoluteRootPath) { + return r -> LinuxWindowsPathUnifier.relativize(absoluteRootPath, ResourceUtil.getPath(r)).toString(); + } + @NotNull private List filterIgnoredResources(Path baseDir, Resource[] resources) { Set effectivePathMatcherPatterns = new HashSet<>(); diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteProjectParser.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteProjectParser.java index 317b58b..8ec7bd7 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteProjectParser.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteProjectParser.java @@ -35,6 +35,8 @@ import org.springframework.rewrite.parsers.maven.MavenProjectAnalyzer; import org.springframework.rewrite.parsers.maven.ProvenanceMarkerFactory; import org.springframework.rewrite.recipes.RewriteRecipeDiscovery; import org.springframework.rewrite.scopes.ScanScope; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; +import org.springframework.util.StringUtils; import java.nio.file.Path; import java.util.ArrayList; @@ -120,7 +122,7 @@ public class RewriteProjectParser { } /** - * Parse given {@link Resource}s in {@code baseDir} to OpenRewrite AST representation. + * Parse given {@link Resource}s in {@code baseDir} to OpenRewrite LST. */ public RewriteProjectParsingResult parse(Path givenBaseDir, List resources) { scanScope.clear(beanFactory); @@ -175,8 +177,8 @@ public class RewriteProjectParser { if (!givenBaseDir.isAbsolute()) { givenBaseDir = givenBaseDir.toAbsolutePath().normalize(); } - final Path baseDir = givenBaseDir; - return baseDir; + String cleanedPath = StringUtils.cleanPath(givenBaseDir.toString()); + return Path.of(cleanedPath); } } diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteResourceParser.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteResourceParser.java index b832202..1d0a8e6 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteResourceParser.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/RewriteResourceParser.java @@ -90,7 +90,6 @@ public class RewriteResourceParser { } public Stream parse(Path searchDir, List resources, Set alreadyParsed) { - // TODO: 945 remove/clean this up List resourcesLeft = resources.stream() .filter(r -> alreadyParsed.stream() .noneMatch(path -> ResourceUtil.getPath(r).toString().startsWith(path.toString()))) diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/BuildFileParser.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/BuildFileParser.java index 8b84141..5eefa5c 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/BuildFileParser.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/BuildFileParser.java @@ -15,6 +15,7 @@ */ package org.springframework.rewrite.parsers.maven; +import org.jetbrains.annotations.NotNull; import org.openrewrite.ExecutionContext; import org.openrewrite.Parser; import org.openrewrite.SourceFile; @@ -24,6 +25,7 @@ import org.openrewrite.xml.tree.Xml; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.rewrite.utils.ResourceUtil; import org.springframework.util.Assert; @@ -101,10 +103,15 @@ public class BuildFileParser { private List findResourcesWithoutProvenanceMarker(Path baseDir, List buildFileResources, Map> provenanceMarkers) { return buildFileResources.stream() - .filter(r -> !provenanceMarkers.containsKey(baseDir.resolve(ResourceUtil.getPath(r)).normalize())) + .filter(r -> !provenanceMarkers.containsKey(ResourceUtil.getPath(r))) .toList(); } + @NotNull + private static Path getUnifiedPath(Path baseDir, Resource r) { + return LinuxWindowsPathUnifier.unifiedPath(baseDir.resolve(ResourceUtil.getPath(r))); + } + private static List retrieveNonPomFiles(List buildFileResources) { return buildFileResources.stream() .filter(r -> !"pom.xml".equals(ResourceUtil.getPath(r).getFileName().toString())) @@ -167,7 +174,7 @@ public class BuildFileParser { private static boolean filterTestResources(Resource r) { String path = ResourceUtil.getPath(r).toString(); - boolean underTest = path.contains("src/test"); + boolean underTest = path.contains(Path.of("src/test").toString()); if (underTest) { LOGGER.info("Ignore build file '%s' having 'src/test' in its path indicating it's a build file for tests." .formatted(path)); diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenModuleParser.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenModuleParser.java index 72bc7b1..2105daa 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenModuleParser.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenModuleParser.java @@ -36,6 +36,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; import org.springframework.rewrite.parsers.*; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.rewrite.utils.ResourceUtil; import java.io.InputStream; @@ -58,333 +59,344 @@ import static org.openrewrite.Tree.randomId; */ public class MavenModuleParser { - private static final Logger LOGGER = LoggerFactory.getLogger(MavenProvenanceMarkerFactory.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MavenModuleParser.class); - private final SpringRewriteProperties springRewriteProperties; + private final SpringRewriteProperties springRewriteProperties; - public MavenModuleParser(SpringRewriteProperties springRewriteProperties) { - this.springRewriteProperties = springRewriteProperties; - } + public MavenModuleParser(SpringRewriteProperties springRewriteProperties) { + this.springRewriteProperties = springRewriteProperties; + } - public List parseModuleSourceFiles(List resources, MavenProject currentProject, - Xml.Document moduleBuildFile, List provenanceMarkers, List styles, - ExecutionContext executionContext, Path baseDir) { + public List parseModuleSourceFiles(List resources, MavenProject currentProject, + Xml.Document moduleBuildFile, List provenanceMarkers, List styles, + ExecutionContext executionContext, Path baseDir) { - List sourceFiles = new ArrayList<>(); - // 146:149: get source encoding from maven - // TDOD: - // String s = - // moduleBuildFile.getMarkers().findFirst(MavenResolutionResult.class).get().getPom().getProperties().get("project.build.sourceEncoding"); - // if (mavenSourceEncoding != null) { - // ParsingExecutionContextView.view(ctx).setCharset(Charset.forName(mavenSourceEncoding.toString())); - // } - Object mavenSourceEncoding = currentProject.getProjectEncoding(); - if (mavenSourceEncoding != null) { - ParsingExecutionContextView.view(executionContext) - .setCharset(Charset.forName(mavenSourceEncoding.toString())); - } + List sourceFiles = new ArrayList<>(); + // 146:149: get source encoding from maven + // TDOD: + // String s = + // moduleBuildFile.getMarkers().findFirst(MavenResolutionResult.class).get().getPom().getProperties().get("project.build.sourceEncoding"); + // if (mavenSourceEncoding != null) { + // ParsingExecutionContextView.view(ctx).setCharset(Charset.forName(mavenSourceEncoding.toString())); + // } + Object mavenSourceEncoding = currentProject.getProjectEncoding(); + if (mavenSourceEncoding != null) { + ParsingExecutionContextView.view(executionContext) + .setCharset(Charset.forName(mavenSourceEncoding.toString())); + } - JavaParser.Builder javaParserBuilder = JavaParser.fromJavaVersion() - .styles(styles) - .logCompilationWarningsAndErrors(false); + JavaParser.Builder javaParserBuilder = JavaParser.fromJavaVersion() + .styles(styles) + .logCompilationWarningsAndErrors(false); - Path buildFilePath = currentProject.getBasedir().resolve(moduleBuildFile.getSourcePath()); - LOGGER.info("Parsing module " + buildFilePath); - // these paths will be ignored by ResourceParser - Set skipResourceScanDirs = pathsToOtherMavenProjects(currentProject, buildFilePath); - RewriteResourceParser rp = new RewriteResourceParser(baseDir, springRewriteProperties.getIgnoredPathPatterns(), - springRewriteProperties.getPlainTextMasks(), springRewriteProperties.getSizeThresholdMb(), - skipResourceScanDirs, javaParserBuilder.clone(), executionContext); + Path buildFilePath = currentProject.getBasedir().resolve(moduleBuildFile.getSourcePath()); + LOGGER.info("Parsing module " + buildFilePath); + // these paths will be ignored by ResourceParser + Set skipResourceScanDirs = pathsToOtherMavenProjects(currentProject, buildFilePath); + RewriteResourceParser rp = new RewriteResourceParser(baseDir, springRewriteProperties.getIgnoredPathPatterns(), + springRewriteProperties.getPlainTextMasks(), springRewriteProperties.getSizeThresholdMb(), + skipResourceScanDirs, javaParserBuilder.clone(), executionContext); - Set alreadyParsed = new HashSet<>(); - Path moduleBuildFilePath = baseDir.resolve(moduleBuildFile.getSourcePath()); - alreadyParsed.add(moduleBuildFilePath); - alreadyParsed.addAll(skipResourceScanDirs); - SourceSetParsingResult mainSourcesParsingResult = parseMainSources(baseDir, currentProject, moduleBuildFile, - resources, javaParserBuilder.clone(), rp, provenanceMarkers, alreadyParsed, executionContext); - SourceSetParsingResult testSourcesParsingResult = parseTestSources(baseDir, currentProject, moduleBuildFile, - javaParserBuilder.clone(), rp, provenanceMarkers, alreadyParsed, executionContext, resources, - mainSourcesParsingResult.classpath()); - // Collect the dirs of modules parsed in previous steps + Set alreadyParsed = new HashSet<>(); + Path moduleBuildFilePath = baseDir.resolve(moduleBuildFile.getSourcePath()); + alreadyParsed.add(moduleBuildFilePath); + alreadyParsed.addAll(skipResourceScanDirs); - // parse other project resources - Stream parsedResourceFiles = rp.parse(moduleBuildFilePath.getParent(), resources, alreadyParsed) - // FIXME: handle generated sources - .map(addProvenance(baseDir, provenanceMarkers, null)); + SourceSetParsingResult mainSourcesParsingResult = processMainSources(baseDir, resources, moduleBuildFile, + javaParserBuilder, rp, provenanceMarkers, alreadyParsed, executionContext, currentProject); - List mainAndTestSources = mergeAndFilterExcluded(baseDir, - springRewriteProperties.getIgnoredPathPatterns(), mainSourcesParsingResult.sourceFiles(), - testSourcesParsingResult.sourceFiles()); - List resourceFilesList = parsedResourceFiles.toList(); - sourceFiles.addAll(mainAndTestSources); - sourceFiles.addAll(resourceFilesList); + SourceSetParsingResult testSourcesParsingResult = processTestSources(baseDir, moduleBuildFile, + javaParserBuilder, rp, provenanceMarkers, alreadyParsed, executionContext, currentProject, resources, + mainSourcesParsingResult.classpath()); + // Collect the dirs of modules parsed in previous steps - return sourceFiles; - } + // parse other project resources + Stream parsedResourceFiles = rp.parse(moduleBuildFilePath.getParent(), resources, alreadyParsed) + // FIXME: handle generated sources + .map(addProvenance(baseDir, provenanceMarkers, null)); + List mainAndTestSources = mergeAndFilterExcluded(baseDir, + springRewriteProperties.getIgnoredPathPatterns(), mainSourcesParsingResult.sourceFiles(), + testSourcesParsingResult.sourceFiles()); + List resourceFilesList = parsedResourceFiles.toList(); + sourceFiles.addAll(mainAndTestSources); + sourceFiles.addAll(resourceFilesList); - /** - * Add {@link Marker}s to {@link SourceFile}. - */ - public UnaryOperator addProvenance(Path baseDir, List provenance, - @Nullable Collection generatedSources) { - return s -> { - Markers markers = s.getMarkers(); - for (Marker marker : provenance) { - markers = markers.addIfAbsent(marker); - } - if (generatedSources != null && generatedSources.contains(baseDir.resolve(s.getSourcePath()))) { - markers = markers.addIfAbsent(new Generated(randomId())); - } - return s.withMarkers(markers); - }; - } + return sourceFiles; + } - private List mergeAndFilterExcluded(Path baseDir, Set exclusions, List mainSources, - List testSources) { - List pathMatchers = exclusions.stream() - .map(pattern -> baseDir.getFileSystem().getPathMatcher("glob:" + pattern)) - .toList(); - if (pathMatchers.isEmpty()) { - return Stream.concat(mainSources.stream(), testSources.stream()).toList(); - } - return new ArrayList<>(Stream.concat(mainSources.stream(), testSources.stream()) - .filter(s -> isNotExcluded(baseDir, pathMatchers, s)) - .toList()); - } + /** + * Add {@link Marker}s to {@link SourceFile}. + */ + public UnaryOperator addProvenance(Path baseDir, List provenance, + @Nullable Collection generatedSources) { + return s -> { + Markers markers = s.getMarkers(); + for (Marker marker : provenance) { + markers = markers.addIfAbsent(marker); + } + if (generatedSources != null && generatedSources.contains(baseDir.resolve(s.getSourcePath()))) { + markers = markers.addIfAbsent(new Generated(randomId())); + } + return s.withMarkers(markers); + }; + } - private static boolean isNotExcluded(Path baseDir, List exclusions, SourceFile s) { - return exclusions.stream() - .noneMatch(pm -> pm.matches(baseDir.resolve(s.getSourcePath()).toAbsolutePath().normalize())); - } + private List mergeAndFilterExcluded(Path baseDir, Set exclusions, List mainSources, + List testSources) { + List pathMatchers = exclusions.stream() + .map(pattern -> baseDir.getFileSystem().getPathMatcher("glob:" + pattern)) + .toList(); + if (pathMatchers.isEmpty()) { + return Stream.concat(mainSources.stream(), testSources.stream()).toList(); + } + return new ArrayList<>(Stream.concat(mainSources.stream(), testSources.stream()) + .filter(s -> isNotExcluded(baseDir, pathMatchers, s)) + .toList()); + } - private SourceSetParsingResult parseTestSources(Path baseDir, MavenProject mavenProject, - Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, - RewriteResourceParser rp, List provenanceMarkers, Set alreadyParsed, - ExecutionContext executionContext, List resources, List classpath) { - return processTestSources(baseDir, moduleBuildFile, javaParserBuilder, rp, - provenanceMarkers, alreadyParsed, executionContext, mavenProject, resources, classpath); - } + private static boolean isNotExcluded(Path baseDir, List exclusions, SourceFile s) { + return exclusions.stream() + .noneMatch(pm -> pm.matches(baseDir.resolve(s.getSourcePath()).toAbsolutePath().normalize())); + } - /** - * - */ - private SourceSetParsingResult parseMainSources(Path baseDir, MavenProject mavenProject, - Xml.Document moduleBuildFile, List resources, - JavaParser.Builder javaParserBuilder, RewriteResourceParser rp, - List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext) { + private Set pathsToOtherMavenProjects(MavenProject mavenProject, Path moduleBuildFile) { + return mavenProject.getCollectedProjects() + .stream() + .filter(p -> !p.getFile().toPath().toString().equals(moduleBuildFile.toString())) + .map(p -> p.getFile().toPath().getParent()) + .collect(Collectors.toSet()); + } - return processMainSources(baseDir, resources, moduleBuildFile, - javaParserBuilder, rp, provenanceMarkers, alreadyParsed, executionContext, mavenProject); - } + /** + * Parse Java sources and resources under {@code src/main} of current module. + */ + public SourceSetParsingResult processMainSources(Path baseDir, List resources, + Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, + RewriteResourceParser rp, List provenanceMarkers, Set alreadyParsed, + ExecutionContext executionContext, MavenProject currentProject) { + LOGGER.info("Processing main sources in module '%s'".formatted(currentProject.getProjectId())); + // FIXME: 945 + // Some annotation processors output generated sources to the /target directory. + // These are added for parsing but + // should be filtered out of the final SourceFile list. - private Set pathsToOtherMavenProjects(MavenProject mavenProject, Path moduleBuildFile) { - return mavenProject.getCollectedProjects() - .stream() - .filter(p -> !p.getFile().toPath().toString().equals(moduleBuildFile.toString())) - .map(p -> p.getFile().toPath().getParent()) - .collect(Collectors.toSet()); - } + List mainJavaSources = new ArrayList<>(); + List javaSourcesInTarget = currentProject.getJavaSourcesInTarget(); // listJavaSources(resources, + // currentProject.getBasedir().resolve(currentProject.getBuildDirectory())); + List javaSourcesInMain = currentProject.getMainJavaSources(); // listJavaSources(resources, + // currentProject.getBasedir().resolve(currentProject.getSourceDirectory())); + mainJavaSources.addAll(javaSourcesInTarget); + mainJavaSources.addAll(javaSourcesInMain); + LOGGER.info("[%s] Parsing main source files".formatted(currentProject)); - /** - * Parse Java sources and resources under {@code src/main} of current module. - */ - public SourceSetParsingResult processMainSources(Path baseDir, List resources, - Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, - RewriteResourceParser rp, List provenanceMarkers, Set alreadyParsed, - ExecutionContext executionContext, MavenProject currentProject) { - LOGGER.info("Processing main sources in module '%s'".formatted(currentProject.getProjectId())); - // FIXME: 945 - // Some annotation processors output generated sources to the /target directory. - // These are added for parsing but - // should be filtered out of the final SourceFile list. + // FIXME 945 classpath + // - Resolve dependencies to non-reactor projects from Maven repository + // - Resolve dependencies to reactor projects by providing the sources + // javaParserBuilder.classpath(byte[]) - List mainJavaSources = new ArrayList<>(); - List javaSourcesInTarget = currentProject.getJavaSourcesInTarget(); // listJavaSources(resources, - // currentProject.getBasedir().resolve(currentProject.getBuildDirectory())); - List javaSourcesInMain = currentProject.getMainJavaSources(); // listJavaSources(resources, - // currentProject.getBasedir().resolve(currentProject.getSourceDirectory())); - mainJavaSources.addAll(javaSourcesInTarget); - mainJavaSources.addAll(javaSourcesInMain); + // we're processing a module here. The classpath of the module consists of all + // declared dependencies and their transitive dependencies too. + // For dependencies to projects that belong to the current rector... + // They'd either need to be built with Maven before to guarantee that the jars are + // installed to local Maven repo. + // Or, the classpath must be created from the sources of the project. - LOGGER.info("[%s] Parsing source files".formatted(currentProject)); + List dependencies = currentProject.getCompileClasspathElements(); - // FIXME 945 classpath - // - Resolve dependencies to non-reactor projects from Maven repository - // - Resolve dependencies to reactor projects by providing the sources - // javaParserBuilder.classpath(byte[]) + javaParserBuilder.classpath(dependencies); - // we're processing a module here. The classpath of the module consists of all - // declared dependencies and their transitive dependencies too. - // For dependencies to projects that belong to the current rector... - // They'd either need to be built with Maven before to guarantee that the jars are - // installed to local Maven repo. - // Or, the classpath must be created from the sources of the project. + LOGGER.info("Dependencies on main classpath: %s".formatted(dependencies)); - List dependencies = currentProject.getCompileClasspathElements(); + JavaTypeCache typeCache = new JavaTypeCache(); + javaParserBuilder.typeCache(typeCache); - javaParserBuilder.classpath(dependencies); + Iterable inputs = mainJavaSources.stream().map(r -> { + FileAttributes fileAttributes = null; + Path path = ResourceUtil.getPath(r); + boolean isSynthetic = Files.exists(path); + Supplier inputStreamSupplier = () -> ResourceUtil.getInputStream(r); + Parser.Input input = new Parser.Input(path, fileAttributes, inputStreamSupplier, isSynthetic); + return input; + }).toList(); - JavaTypeCache typeCache = new JavaTypeCache(); - javaParserBuilder.typeCache(typeCache); + LOGGER.info("Parsing main Java sources."); - Iterable inputs = mainJavaSources.stream().map(r -> { - FileAttributes fileAttributes = null; - Path path = ResourceUtil.getPath(r); - boolean isSynthetic = Files.exists(path); - Supplier inputStreamSupplier = () -> ResourceUtil.getInputStream(r); - Parser.Input input = new Parser.Input(path, fileAttributes, inputStreamSupplier, isSynthetic); - return input; - }).toList(); + Set localClassesCp = new HashSet<>(); + JavaSourceSet javaSourceSet = sourceSet("main", dependencies, typeCache); + List cus = javaParserBuilder.build() + .parseInputs(inputs, baseDir, executionContext) + .peek(s -> { + ((J.CompilationUnit) s).getClasses() + .stream() + .map(J.ClassDeclaration::getType) + .forEach(localClassesCp::add); - Set localClassesCp = new HashSet<>(); - JavaSourceSet javaSourceSet = sourceSet("main", dependencies, typeCache); - List cus = javaParserBuilder.build() - .parseInputs(inputs, baseDir, executionContext) - .peek(s -> { - ((J.CompilationUnit) s).getClasses() - .stream() - .map(J.ClassDeclaration::getType) - .forEach(localClassesCp::add); + alreadyParsed.add(baseDir.resolve(s.getSourcePath())); + }) + .toList(); - alreadyParsed.add(baseDir.resolve(s.getSourcePath())); - }) - .toList(); + LOGGER.info("Parsed %d main Java source files.".formatted(cus.size())); - // TODO: This is a hack: - // Parsed java sources are not themselves on the classpath (here). - // The actual parsing happens when the stream is terminated (toList), - // therefore the toList() must be called before the parsed compilation units can - // be added to the classpath - List mainProjectProvenance = new ArrayList<>(provenanceMarkers); - javaSourceSet = appendToClasspath(localClassesCp, javaSourceSet); - mainProjectProvenance.add(javaSourceSet); + // TODO: This is a hack: + // Parsed java sources are not themselves on the classpath (here). + // The actual parsing happens when the stream is terminated (toList), + // therefore the toList() must be called before the parsed compilation units can + // be added to the classpath + List mainProjectProvenance = new ArrayList<>(provenanceMarkers); + javaSourceSet = appendToClasspath(localClassesCp, javaSourceSet); + mainProjectProvenance.add(javaSourceSet); - List parsedJavaPaths = javaSourcesInTarget.stream().map(ResourceUtil::getPath).toList(); - Stream parsedJava = cus.stream() - .map(addProvenance(baseDir, mainProjectProvenance, parsedJavaPaths)); - LOGGER.debug( - "[%s] Scanned %d java source files in main scope.".formatted(currentProject, mainJavaSources.size())); + List parsedJavaPaths = javaSourcesInTarget.stream().map(ResourceUtil::getPath).toList(); + Stream parsedMainJava = cus.stream() + .map(addProvenance(baseDir, mainProjectProvenance, parsedJavaPaths)); + LOGGER.debug( + "[%s] Scanned %d java source files in main scope.".formatted(currentProject, mainJavaSources.size())); - // Filter out any generated source files from the returned list, as we do not want - // to apply the recipe to the - // generated files. - Path buildDirectory = Paths.get(currentProject.getBuildDirectory()); - List sourceFiles = parsedJava.filter(s -> !s.getSourcePath().startsWith(buildDirectory)) - .collect(Collectors.toCollection(ArrayList::new)); + // Filter out any generated source files from the returned list, as we do not want + // to apply the recipe to the + // generated files. + Path buildDirectory = LinuxWindowsPathUnifier.unifiedPath(Paths.get(currentProject.getBuildDirectory())); + List filteredMainJava = filterOutResourcesInDir(parsedMainJava, buildDirectory); - int sourcesParsedBefore = alreadyParsed.size(); - alreadyParsed.addAll(parsedJavaPaths); - List parsedResourceFiles = rp - .parse(currentProject.getModulePath().resolve("src/main/resources"), resources, alreadyParsed) - .map(addProvenance(baseDir, mainProjectProvenance, null)) - .toList(); + int sourcesParsedBefore = alreadyParsed.size(); + alreadyParsed.addAll(parsedJavaPaths); - LOGGER.debug("[%s] Scanned %d resource files in main scope.".formatted(currentProject, - (alreadyParsed.size() - sourcesParsedBefore))); - // Any resources parsed from "main/resources" should also have the main source set - // added to them. - sourceFiles.addAll(parsedResourceFiles); - return new SourceSetParsingResult(sourceFiles, javaSourceSet.getClasspath()); - } + List resourcesLeft = resources.stream() + .filter(r -> alreadyParsed.stream().noneMatch(path -> LinuxWindowsPathUnifier.pathStartsWith(r, path))) + .toList(); - /** - * Add entries that don't exist in the classpath of {@code javaSourceSet} from - * {@code appendingClasspath}. - */ - @NotNull - private static JavaSourceSet appendToClasspath(Set appendingClasspath, - JavaSourceSet javaSourceSet) { - List curCp = javaSourceSet.getClasspath(); - appendingClasspath.forEach(f -> { - if (!curCp.contains(f)) { - curCp.add(f); - } - }); - javaSourceSet = javaSourceSet.withClasspath(new ArrayList<>(curCp)); - return javaSourceSet; - } + LOGGER.info("Parsing main resources"); + List parsedResourceFiles = rp + .parseSourceFiles(currentProject.getModulePath().resolve("src/main/resources"), resourcesLeft, + alreadyParsed, executionContext) + .map(addProvenance(baseDir, mainProjectProvenance, null)) + .toList(); - @NotNull - private static JavaSourceSet sourceSet(String name, List dependencies, JavaTypeCache typeCache) { - return JavaSourceSet.build(name, dependencies, typeCache, false); - } + LOGGER.info("Parsed %d main resources".formatted(parsedResourceFiles.size())); - /** - * Parse Java sources and resource files under {@code src/test}. - */ - public SourceSetParsingResult processTestSources(Path baseDir, Xml.Document moduleBuildFile, - JavaParser.Builder javaParserBuilder, RewriteResourceParser rp, - List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, - MavenProject currentProject, List resources, List classpath) { - LOGGER.info("Processing test sources in module '%s'".formatted(currentProject.getProjectId())); + // TODO: Remove + // List parsedResourceFiles = rp + // .parse(currentProject.getModulePath().resolve("src/main/resources"), resources, + // alreadyParsed) + // .map(addProvenance(baseDir, mainProjectProvenance, null)) + // .toList(); - List testDependencies = currentProject.getTestClasspathElements(); + LOGGER.debug("[%s] Scanned %d resource files in main scope.".formatted(currentProject, + (alreadyParsed.size() - sourcesParsedBefore))); + // Any resources parsed from "main/resources" should also have the main source set + // added to them. + filteredMainJava.addAll(parsedResourceFiles); + return new SourceSetParsingResult(filteredMainJava, javaSourceSet.getClasspath()); + } - javaParserBuilder.classpath(testDependencies); - JavaTypeCache typeCache = new JavaTypeCache(); - javaParserBuilder.typeCache(typeCache); + @NotNull + private static ArrayList filterOutResourcesInDir(Stream parsedJava, Path buildDirectory) { + return parsedJava.filter(s -> !s.getSourcePath().startsWith(buildDirectory)) + .collect(Collectors.toCollection(ArrayList::new)); + } - List testJavaSources = listJavaSources(resources, - currentProject.getBasedir().resolve(currentProject.getTestSourceDirectory())); - alreadyParsed.addAll(testJavaSources.stream().map(ResourceUtil::getPath).toList()); + /** + * Add entries that don't exist in the classpath of {@code javaSourceSet} from + * {@code appendingClasspath}. + */ + @NotNull + private static JavaSourceSet appendToClasspath(Set appendingClasspath, + JavaSourceSet javaSourceSet) { + List curCp = javaSourceSet.getClasspath(); + appendingClasspath.forEach(f -> { + if (!curCp.contains(f)) { + curCp.add(f); + } + }); + javaSourceSet = javaSourceSet.withClasspath(new ArrayList<>(curCp)); + return javaSourceSet; + } - Iterable inputs = testJavaSources.stream() - .map(r -> new Parser.Input(ResourceUtil.getPath(r), () -> ResourceUtil.getInputStream(r))) - .toList(); + @NotNull + private static JavaSourceSet sourceSet(String name, List dependencies, JavaTypeCache typeCache) { + return JavaSourceSet.build(name, dependencies, typeCache, false); + } - final List localClassesCp = new ArrayList<>(); - List cus = javaParserBuilder.build() - .parseInputs(inputs, baseDir, executionContext) - .peek(s -> { - ((J.CompilationUnit) s).getClasses() - .stream() - .map(J.ClassDeclaration::getType) - .forEach(localClassesCp::add); - alreadyParsed.add(baseDir.resolve(s.getSourcePath())); - }) - .toList(); + /** + * Parse Java sources and resource files under {@code src/test}. + */ + public SourceSetParsingResult processTestSources(Path baseDir, Xml.Document moduleBuildFile, + JavaParser.Builder javaParserBuilder, RewriteResourceParser rp, + List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, + MavenProject currentProject, List resources, List classpath) { + LOGGER.info("Processing test sources in module '%s'".formatted(currentProject.getProjectId())); - List markers = new ArrayList<>(provenanceMarkers); + List testDependencies = currentProject.getTestClasspathElements(); - JavaSourceSet javaSourceSet = sourceSet("test", testDependencies, typeCache); - Set curClasspath = Stream.concat(classpath.stream(), localClassesCp.stream()) - .collect(Collectors.toSet()); - javaSourceSet = appendToClasspath(curClasspath, javaSourceSet); - markers.add(javaSourceSet); - Stream parsedJava = cus.stream().map(addProvenance(baseDir, markers, null)); + javaParserBuilder.classpath(testDependencies); + JavaTypeCache typeCache = new JavaTypeCache(); + javaParserBuilder.typeCache(typeCache); - LOGGER.debug( - "[%s] Scanned %d java source files in test scope.".formatted(currentProject, testJavaSources.size())); - Stream sourceFiles = parsedJava; + List testJavaSources = currentProject.getTestJavaSources(); + // listJavaSources(resources, + // currentProject.getBasedir().resolve(currentProject.getTestSourceDirectory())); + // alreadyParsed.addAll(testJavaSources.stream().map(ResourceUtil::getPath).toList()); - // Any resources parsed from "test/resources" should also have the test source set - // added to them. - int sourcesParsedBefore = alreadyParsed.size(); - Stream parsedResourceFiles = rp - .parse(currentProject.getBasedir().resolve("src/test/resources"), resources, alreadyParsed) - .map(addProvenance(baseDir, markers, null)); - LOGGER.debug("[%s] Scanned %d resource files in test scope.".formatted(currentProject, - (alreadyParsed.size() - sourcesParsedBefore))); - sourceFiles = Stream.concat(sourceFiles, parsedResourceFiles); - List result = sourceFiles.toList(); - return new SourceSetParsingResult(result, javaSourceSet.getClasspath()); - } + Iterable inputs = testJavaSources.stream() + .map(r -> new Parser.Input(ResourceUtil.getPath(r), () -> ResourceUtil.getInputStream(r))) + .toList(); - // FIXME: 945 take Java sources from resources - private static List listJavaSources(List resources, Path sourceDirectory) { - return resources.stream().filter(whenIn(sourceDirectory)).filter(whenFileNameEndsWithJava()).toList(); - } + final List localClassesCp = new ArrayList<>(); + List cus = javaParserBuilder.build() + .parseInputs(inputs, baseDir, executionContext) + .peek(s -> { + ((J.CompilationUnit) s).getClasses() + .stream() + .map(J.ClassDeclaration::getType) + .forEach(localClassesCp::add); + alreadyParsed.add(baseDir.resolve(s.getSourcePath())); + }) + .toList(); - @NotNull - private static Predicate whenFileNameEndsWithJava() { - return p -> ResourceUtil.getPath(p).getFileName().toString().endsWith(".java"); - } + List markers = new ArrayList<>(provenanceMarkers); - @NotNull - private static Predicate whenIn(Path sourceDirectory) { - return r -> ResourceUtil.getPath(r).toString().startsWith(sourceDirectory.toString()); - } + JavaSourceSet javaSourceSet = sourceSet("test", testDependencies, typeCache); + Set curClasspath = Stream.concat(classpath.stream(), localClassesCp.stream()) + .collect(Collectors.toSet()); + javaSourceSet = appendToClasspath(curClasspath, javaSourceSet); + markers.add(javaSourceSet); + Stream parsedJava = cus.stream().map(addProvenance(baseDir, markers, null)); + + LOGGER.debug( + "[%s] Scanned %d java source files in test scope.".formatted(currentProject, testJavaSources.size())); + Stream sourceFiles = parsedJava; + + // Any resources parsed from "test/resources" should also have the test source set + // added to them. + int sourcesParsedBefore = alreadyParsed.size(); + Stream parsedResourceFiles = rp + .parse(currentProject.getBasedir().resolve("src/test/resources"), resources, alreadyParsed) + .map(addProvenance(baseDir, markers, null)); + LOGGER.debug("[%s] Scanned %d resource files in test scope.".formatted(currentProject, + (alreadyParsed.size() - sourcesParsedBefore))); + sourceFiles = Stream.concat(sourceFiles, parsedResourceFiles); + List result = sourceFiles.toList(); + return new SourceSetParsingResult(result, javaSourceSet.getClasspath()); + } + + // FIXME: 945 take Java sources from resources + private static List listJavaSources(List resources, Path sourceDirectory) { + return resources.stream() + .filter(whenIn(LinuxWindowsPathUnifier.unifiedPath(sourceDirectory))) + .filter(whenFileNameEndsWithJava()) + .toList(); + } + + @NotNull + private static Predicate whenFileNameEndsWithJava() { + return p -> ResourceUtil.getPath(p).getFileName().toString().endsWith(".java"); + } + + @NotNull + private static Predicate whenIn(Path sourceDirectory) { + return r -> ResourceUtil.getPath(r).toString().startsWith(sourceDirectory.toString()); + } } diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzer.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzer.java index 0921d63..e30066b 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzer.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzer.java @@ -18,16 +18,19 @@ package org.springframework.rewrite.parsers.maven; import org.apache.maven.model.*; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.jetbrains.annotations.NotNull; import org.openrewrite.maven.utilities.MavenArtifactDownloader; import org.springframework.core.io.Resource; import org.springframework.rewrite.parsers.MavenProject; import org.springframework.rewrite.parsers.ParserContext; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.rewrite.utils.ResourceUtil; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.*; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -60,7 +63,7 @@ public class MavenProjectAnalyzer { } Resource rootPom = allPomFiles.stream() - .filter(r -> ResourceUtil.getPath(r).toString().equals(baseDir.resolve(POM_XML).normalize().toString())) + .filter(r -> LinuxWindowsPathUnifier.pathEquals(r, baseDir.resolve(POM_XML))) .findFirst() .orElseThrow( () -> new IllegalArgumentException("The provided resources do not contain a root 'pom.xml' file.")); @@ -78,12 +81,14 @@ public class MavenProjectAnalyzer { private List map(Path baseDir, List resources, List sortedModels) { List mavenProjects = new ArrayList<>(); sortedModels.stream().filter(Objects::nonNull).forEach(m -> { - String projectDir = baseDir.resolve(m.getProjectDirectory().toString()).normalize().toString(); + String projectDir = LinuxWindowsPathUnifier + .unifiedPathString(baseDir.resolve(m.getProjectDirectory().toPath()).normalize()); List filteredResources = resources.stream() - .filter(r -> ResourceUtil.getPath(r).toString().startsWith(projectDir)) + .filter(r -> LinuxWindowsPathUnifier.unifiedPathString(r).startsWith(projectDir)) .toList(); - mavenProjects - .add(new MavenProject(baseDir, m.getResource(), m, rewriteMavenArtifactDownloader, filteredResources)); + MavenProject mavenProject = new MavenProject(baseDir, m.getResource(), m, rewriteMavenArtifactDownloader, + filteredResources); + mavenProjects.add(mavenProject); }); // set all non parent poms as collected projects for root parent pom List collected = new ArrayList<>(mavenProjects); @@ -108,15 +113,8 @@ public class MavenProjectAnalyzer { String modulePathSegment = path == null ? moduleName : path + "/" + moduleName; - allPomFiles.stream().filter(resource -> { - String modulePath = baseDir.resolve(modulePathSegment) - .resolve(POM_XML) - .toAbsolutePath() - .normalize() - .toString(); - String resourcePath = ResourceUtil.getPath(resource).toAbsolutePath().normalize().toString(); - return resourcePath.equals(modulePath); - }) + allPomFiles.stream() + .filter(getResourcePredicate(baseDir, modulePathSegment)) .map(Model::new) .forEach(m -> recursivelyFindReactorModules(baseDir, modulePathSegment, reactorModels, allPomFiles, m) .stream()); @@ -124,6 +122,16 @@ public class MavenProjectAnalyzer { return reactorModels; } + @NotNull + private static Predicate getResourcePredicate(Path baseDir, String modulePathSegment) { + return resource -> { + Path pomPath = baseDir.resolve(modulePathSegment).resolve(POM_XML).toAbsolutePath().normalize(); + String modulePath = LinuxWindowsPathUnifier.unifiedPathString(pomPath); + Path resourcePath = ResourceUtil.getPath(resource).toAbsolutePath().normalize(); + return LinuxWindowsPathUnifier.pathEquals(resourcePath, modulePath); + }; + } + public List sortModels(List reactorModels) { List sortedModels = new ArrayList<>(); Map gaToModelMap = reactorModels.stream().collect(Collectors.toMap(m -> { diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializer.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializer.java index f291947..640bc4e 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializer.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializer.java @@ -22,6 +22,7 @@ import org.openrewrite.maven.MavenSettings; import org.openrewrite.maven.internal.RawRepositories; import org.openrewrite.maven.tree.MavenRepository; import org.springframework.rewrite.scopes.ProjectMetadata; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.stereotype.Component; import java.nio.file.Files; @@ -50,9 +51,10 @@ public class MavenSettingsInitializer { } public void initializeMavenSettings() { - Path userHome = Path.of(System.getProperty("user.home")); + Path userHome = Path.of(System.getProperty("user.home")).toAbsolutePath().normalize(); String m2RepoPath = userHome.resolve(".m2/repository").toAbsolutePath().normalize() + "/"; - String repo = "file://" + m2RepoPath; + String unifiedM2RepoPath = LinuxWindowsPathUnifier.unifiedPathString(m2RepoPath); + String repo = "file://" + unifiedM2RepoPath; Path mavenSettingsFile = userHome.resolve(".m2/settings.xml"); Path mavenSecuritySettingsFile = userHome.resolve(".m2/settings-security.xml"); @@ -62,7 +64,7 @@ public class MavenSettingsInitializer { MavenSettings.@Nullable ActiveProfiles activeProfiles = new MavenSettings.ActiveProfiles(List.of("default")); MavenSettings.@Nullable Mirrors mirrors = new MavenSettings.Mirrors(); MavenSettings.Servers servers = new MavenSettings.Servers(); - MavenSettings mavenSettings = new MavenSettings(m2RepoPath, mavenRepository, profiles, activeProfiles, mirrors, + MavenSettings mavenSettings = new MavenSettings(repo, mavenRepository, profiles, activeProfiles, mirrors, servers); // TODO: Add support for global Maven settings (${maven.home}/conf/settings.xml). diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/ProvenanceMarkerFactory.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/ProvenanceMarkerFactory.java index 40fe12b..f63552b 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/ProvenanceMarkerFactory.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/parsers/maven/ProvenanceMarkerFactory.java @@ -18,6 +18,7 @@ package org.springframework.rewrite.parsers.maven; import org.openrewrite.marker.Marker; import org.springframework.core.io.Resource; import org.springframework.rewrite.parsers.ParserContext; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.rewrite.utils.ResourceUtil; import java.nio.file.Path; diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/LinuxWindowsPathUnifier.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/LinuxWindowsPathUnifier.java index 81e3c41..2140723 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/LinuxWindowsPathUnifier.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/LinuxWindowsPathUnifier.java @@ -15,6 +15,7 @@ */ package org.springframework.rewrite.utils; +import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; import java.nio.file.Path; @@ -26,11 +27,26 @@ import java.nio.file.Path; */ public class LinuxWindowsPathUnifier { - public String unifyPath(Path path) { - return unifyPath(path.toString()); + public static Path relativize(Path subpath, Path path) { + LinuxWindowsPathUnifier linuxWindowsPathUnifier = new LinuxWindowsPathUnifier(); + String unifiedAbsoluteRootPath = linuxWindowsPathUnifier.unifiedPathString(subpath); + String pathUnified = linuxWindowsPathUnifier.unifiedPathString(path); + return Path.of(unifiedAbsoluteRootPath).relativize(Path.of(pathUnified)); } - public String unifyPath(String path) { + public static String unifiedPathString(Path path) { + return unifiedPathString(path.toString()); + } + + public static Path unifiedPath(Path path) { + return Path.of(unifiedPathString(path)); + } + + public static String unifiedPathString(Resource r) { + return unifiedPathString(ResourceUtil.getPath(r)); + } + + public static String unifiedPathString(String path) { path = StringUtils.cleanPath(path); if (isWindows()) { path = transformToLinuxPath(path); @@ -38,12 +54,32 @@ public class LinuxWindowsPathUnifier { return path; } - boolean isWindows() { + public static Path unifiedPath(String path) { + return Path.of(unifiedPathString(path)); + } + + static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } - private String transformToLinuxPath(String path) { + private static String transformToLinuxPath(String path) { return path.replaceAll("^[\\w]+:\\/?", "/"); } + public static boolean pathEquals(Resource r, Path path) { + return unifiedPathString(ResourceUtil.getPath(r)).equals(unifiedPathString(path.normalize())); + } + + public static boolean pathEquals(Path basedir, String parentPomPath) { + return unifiedPathString(basedir).equals(parentPomPath); + } + + public static boolean pathEquals(Path path1, Path path2) { + return unifiedPathString(path1).equals(unifiedPathString(path2)); + } + + public static boolean pathStartsWith(Resource r, Path path) { + return ResourceUtil.getPath(r).toString().startsWith(unifiedPathString(path)); + } + } diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/OsAgnosticPathMatcher.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/OsAgnosticPathMatcher.java index 4b8e674..30bf590 100644 --- a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/OsAgnosticPathMatcher.java +++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/utils/OsAgnosticPathMatcher.java @@ -48,7 +48,7 @@ public class OsAgnosticPathMatcher implements PathMatcher { } private String unifyPath(String path) { - return pathUnifier.unifyPath(path); + return pathUnifier.unifiedPathString(path); } @Override diff --git a/spring-rewrite-commons-launcher/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java b/spring-rewrite-commons-launcher/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java index 1e0c055..4ac0f2a 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java @@ -31,6 +31,7 @@ import org.springframework.rewrite.parsers.RewriteProjectParser; import org.springframework.rewrite.parsers.RewriteProjectParsingResult; import org.springframework.rewrite.test.util.DummyResource; import org.springframework.rewrite.test.util.TestProjectHelper; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import java.nio.file.Path; import java.util.List; @@ -100,7 +101,7 @@ public class CalculateClasspathTest { } """; - Path baseDir = tmpDir.resolve("/example-1").toAbsolutePath().normalize(); + Path baseDir = LinuxWindowsPathUnifier.unifiedPath(tmpDir.resolve("/example-1").toAbsolutePath().normalize()); List resources = List.of(new DummyResource(baseDir.resolve("pom.xml"), pom), new DummyResource(baseDir.resolve("src/main/java/com/example/MainClass.java"), mainClass), new DummyResource(baseDir.resolve("src/test/java/com/example/TestClass.java"), testClass)); @@ -109,13 +110,16 @@ public class CalculateClasspathTest { // verify types in use SourceFile mainSourceFile = parsingResult.sourceFiles().get(1); + assertThat(mainSourceFile).isInstanceOf(J.CompilationUnit.class); J.CompilationUnit mainCu = (J.CompilationUnit) mainSourceFile; + // Having Min annotation resolved proves type resolution is working for main // resources assertThat(mainCu.getTypesInUse().getTypesInUse().stream().map(t -> t.toString())) .containsExactlyInAnyOrder("int", "String", "javax.validation.constraints.Min"); SourceFile testSourceFile = parsingResult.sourceFiles().get(2); + assertThat(testSourceFile).isInstanceOf(J.CompilationUnit.class); J.CompilationUnit testCu = (J.CompilationUnit) testSourceFile; // Having Test annotation resolved proves type resolution is working for test // resources diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/BuildFileParserTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/BuildFileParserTest.java index 4200a6b..cc84bcd 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/BuildFileParserTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/BuildFileParserTest.java @@ -154,10 +154,20 @@ class BuildFileParserTest { Path module1SubmodulePomPath = baseDir.resolve(module1SubmoduleSourcePath); Path parentPomPath = baseDir.resolve(parentSourcePath); Path module1PomXml = baseDir.resolve(module1SourcePath); - Map> provenanceMarkers = Map.of(parentPomPath, - List.of(new JavaProject(UUID.randomUUID(), "parent", null)), module1PomXml, - List.of(new JavaProject(UUID.randomUUID(), "module1", null)), module1SubmodulePomPath, - List.of(new JavaProject(UUID.randomUUID(), "module1/submodule", null))); + + // @formatter:off + Map> provenanceMarkers = Map.of( + parentPomPath, List.of( + new JavaProject(UUID.randomUUID(), "parent", null) + ), + module1PomXml, List.of( + new JavaProject(UUID.randomUUID(), "module1", null) + ), + module1SubmodulePomPath, List.of( + new JavaProject(UUID.randomUUID(), "module1/submodule", null) + ) + ); + // @formatter:on ExecutionContext executionContext = new InMemoryExecutionContext(t -> t.printStackTrace()); boolean skipMavenParsing = false; @@ -196,14 +206,14 @@ class BuildFileParserTest { @DisplayName("parse with non-pom resources provided should throw exception") void parseWithNonPomResourcesProvidedShouldThrowException() { Path baseDir = Path.of(".").toAbsolutePath().normalize(); - Resource nonPomResource = new DummyResource(baseDir, "src/main/java/SomeClass.java", - "public class SomeClass {}"); + Path someClassPath = baseDir.resolve("src/main/java/SomeClass.java"); + Resource nonPomResource = new DummyResource(baseDir, someClassPath.toString(), "public class SomeClass {}"); List nonPomResource1 = List.of(nonPomResource); String message = assertThrows(IllegalArgumentException.class, () -> sut.parseBuildFiles(baseDir, nonPomResource1, List.of(), new InMemoryExecutionContext(), false, Map.of())) .getMessage(); - assertThat(message).isEqualTo("Provided resources which are not Maven build files: '[" + baseDir - + "/src/main/java/SomeClass.java]'"); + assertThat(message) + .isEqualTo("Provided resources which are not Maven build files: '[" + someClassPath + "]'"); } @Test @@ -213,20 +223,25 @@ class BuildFileParserTest { Path pom1Path = baseDir.resolve("pom.xml"); Resource pom1 = new DummyResource(pom1Path, ""); + Path pom2Path = baseDir.resolve("module1/pom.xml"); Resource pom2 = new DummyResource(pom2Path, ""); + List poms = List.of(pom1, pom2); - Map> provenanceMarkers = Map.of(pom1Path, - List.of(new JavaProject(UUID.randomUUID(), "pom.xml", null)) - // no marker for module1/pom.xml + // @formatter:off + Map> provenanceMarkers = Map.of( + pom1Path, List.of(new JavaProject(UUID.randomUUID(), "pom.xml", null)) + // no marker for module1/pom.xml ); - - String message = assertThrows(IllegalArgumentException.class, () -> sut.parseBuildFiles(baseDir, poms, - List.of(), new InMemoryExecutionContext(), false, provenanceMarkers)) + String message = assertThrows( + IllegalArgumentException.class, + () -> sut.parseBuildFiles(baseDir, poms, List.of(), new InMemoryExecutionContext(), false, provenanceMarkers) + ) .getMessage(); - assertThat(message).isEqualTo("No provenance marker provided for these pom files [" - + Path.of(".").toAbsolutePath().normalize().resolve("module1/pom.xml]")); + // @formatter:on + + assertThat(message).isEqualTo("No provenance marker provided for these pom files [" + pom2Path + "]"); } } diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/ParserEventPublicationIntegrationTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/ParserEventPublicationIntegrationTest.java index a17f8f5..7038328 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/ParserEventPublicationIntegrationTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/ParserEventPublicationIntegrationTest.java @@ -26,6 +26,7 @@ import org.springframework.core.io.Resource; import org.springframework.rewrite.parsers.events.FinishedParsingResourceEvent; import org.springframework.rewrite.parsers.events.StartedParsingProjectEvent; import org.springframework.rewrite.parsers.events.SuccessfullyParsedProjectEvent; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import java.nio.file.Path; import java.util.ArrayList; @@ -53,7 +54,7 @@ public class ParserEventPublicationIntegrationTest { @Autowired ExecutionContext executionContext; - private static List capturedEvents = new ArrayList<>(); + private static List capturedEvents = new ArrayList<>(); private static StartedParsingProjectEvent startedParsingEvent; @@ -69,19 +70,19 @@ public class ParserEventPublicationIntegrationTest { RewriteProjectParsingResult parsingResult = sut.parse(baseDir, resources); assertThat(parsingResult.sourceFiles()).hasSize(5); - assertThat(parsingResult.sourceFiles().stream().map(s -> s.getSourcePath().toString()).toList()) - .containsExactly("pom.xml", "module-b/pom.xml", "module-a/pom.xml", + assertThat(parsingResult.sourceFiles() + .stream() + .map(s -> LinuxWindowsPathUnifier.unifiedPathString(s.getSourcePath())) + .toList()).containsExactly("pom.xml", "module-b/pom.xml", "module-a/pom.xml", "module-b/src/test/resources/application.yaml", "module-a/src/main/java/com/acme/SomeClass.java"); assertThat(capturedEvents).hasSize(5); - assertThat(capturedEvents.get(0).sourceFile().getSourcePath().toString()).isEqualTo("pom.xml"); - assertThat(capturedEvents.get(1).sourceFile().getSourcePath().toString()).isEqualTo("module-b/pom.xml"); - assertThat(capturedEvents.get(2).sourceFile().getSourcePath().toString()).isEqualTo("module-a/pom.xml"); - assertThat(capturedEvents.get(3).sourceFile().getSourcePath().toString()) - .isEqualTo("module-b/src/test/resources/application.yaml"); - assertThat(capturedEvents.get(4).sourceFile().getSourcePath().toString()) - .isEqualTo("module-a/src/main/java/com/acme/SomeClass.java"); + assertThat(capturedEvents.get(0)).isEqualTo("pom.xml"); + assertThat(capturedEvents.get(1)).isEqualTo("module-b/pom.xml"); + assertThat(capturedEvents.get(2)).isEqualTo("module-a/pom.xml"); + assertThat(capturedEvents.get(3)).isEqualTo("module-b/src/test/resources/application.yaml"); + assertThat(capturedEvents.get(4)).isEqualTo("module-a/src/main/java/com/acme/SomeClass.java"); // ResourceParser not firing events // TODO: reactivate after // https://github.com/openrewrite/rewrite-maven-plugin/issues/622 @@ -99,7 +100,8 @@ public class ParserEventPublicationIntegrationTest { @EventListener(FinishedParsingResourceEvent.class) public void onEvent(FinishedParsingResourceEvent event) { - capturedEvents.add(event); + String unifiedPathString = LinuxWindowsPathUnifier.unifiedPathString(event.sourceFile().getSourcePath()); + capturedEvents.add(unifiedPathString); } @EventListener(StartedParsingProjectEvent.class) diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserIntegrationTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserIntegrationTest.java index 27bd40c..515ce36 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserIntegrationTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserIntegrationTest.java @@ -17,6 +17,9 @@ package org.springframework.rewrite.parsers; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junitpioneer.jupiter.Issue; import org.openrewrite.java.tree.J; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -33,6 +36,8 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Fabian Krüger */ +@DisabledOnOs(value = OS.WINDOWS, disabledReason = "The repository URIs of dependencies differ.") +@Issue("https://github.com/spring-projects/spring-rewrite-commons/issues/12") @SpringBootTest(classes = { SbmSupportRewriteConfiguration.class, SbmTestConfiguration.class }) public class RewriteProjectParserIntegrationTest { diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserParityTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserParityTest.java index 4ff916e..e774fdc 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserParityTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/RewriteProjectParserParityTest.java @@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; import org.junitpioneer.jupiter.Issue; import org.openrewrite.ExecutionContext; @@ -55,6 +57,8 @@ import static org.assertj.core.api.AssertionsForClassTypes.fail; * * @author Fabian Krüger */ +@DisabledOnOs(value = OS.WINDOWS, disabledReason = "The repository URIs of dependencies differ.") +@Issue("https://github.com/spring-projects/spring-rewrite-commons/issues/12") class RewriteProjectParserParityTest { @Test @@ -163,14 +167,14 @@ class RewriteProjectParserParityTest { .verifyParity((comparingParsingResult, testedParsingResult) -> { assertThat( comparingParsingResult.sourceFiles().stream().map(sf -> sf.getSourcePath().toString()).toList()) - .contains("checkstyle/rules.xml"); + .contains(Path.of("checkstyle/rules.xml").toString()); assertThat( comparingParsingResult.sourceFiles().stream().map(sf -> sf.getSourcePath().toString()).toList()) - .contains("checkstyle/suppressions.xml"); + .contains(Path.of("checkstyle/suppressions.xml").toString()); assertThat(testedParsingResult.sourceFiles().stream().map(sf -> sf.getSourcePath().toString()).toList()) - .contains("checkstyle/rules.xml"); + .contains(Path.of("checkstyle/rules.xml").toString()); assertThat(testedParsingResult.sourceFiles().stream().map(sf -> sf.getSourcePath().toString()).toList()) - .contains("checkstyle/suppressions.xml"); + .contains(Path.of("checkstyle/suppressions.xml").toString()); }); } diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/SpringRewritePropertiesTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/SpringRewritePropertiesTest.java index 438a9e9..f898b65 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/SpringRewritePropertiesTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/SpringRewritePropertiesTest.java @@ -21,6 +21,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import java.io.File; +import java.nio.file.Path; + import static org.assertj.core.api.Assertions.assertThat; class SpringRewritePropertiesTest { @@ -42,7 +45,7 @@ class SpringRewritePropertiesTest { @DisplayName("spring.rewrite.pomCacheDirectory") void defaultPomCacheDirectory() { assertThat(springRewriteProperties.getPomCacheDirectory()) - .isEqualTo(System.getProperty("user.home") + "/.rewrite-cache"); + .isEqualTo(Path.of(System.getProperty("user.home")).resolve(".rewrite-cache").toString()); } @Test diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenMojoProjectParserFactory.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenMojoProjectParserFactory.java index 12be3ac..16d1ccf 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenMojoProjectParserFactory.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenMojoProjectParserFactory.java @@ -60,7 +60,7 @@ public class MavenMojoProjectParserFactory { Collection plainTextMasks, int sizeThresholdMb, boolean runPerSubmodule, PlexusContainer plexusContainer, MavenSession session) { try { - Log logger = new Slf4jToMavenLoggerAdapter(LOGGER); + Log logger = new Slf4jToMavenLoggerAdapter(LoggerFactory.getLogger(MavenMojoProjectParser.class)); RuntimeInformation runtimeInformation = plexusContainer.lookup(RuntimeInformation.class); SettingsDecrypter decrypter = plexusContainer.lookup(SettingsDecrypter.class); diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzerTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzerTest.java index a228bc3..c1e5182 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzerTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenProjectAnalyzerTest.java @@ -28,6 +28,7 @@ import org.openrewrite.maven.utilities.MavenArtifactDownloader; import org.springframework.core.io.Resource; import org.springframework.rewrite.parsers.MavenProject; import org.springframework.rewrite.test.util.DummyResource; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import org.springframework.rewrite.utils.ResourceUtil; import java.io.IOException; @@ -240,7 +241,7 @@ class MavenProjectAnalyzerTest { */ @Test @DisplayName("reactorBuild") - void reactorBuild() { + void getSortedProjectsWithMultiModule() { @Language("xml") String parentPom = """ @@ -272,18 +273,45 @@ class MavenProjectAnalyzerTest { """; - List resources = List.of(new DummyResource(Path.of("pom.xml"), parentPom), - new DummyResource(Path.of("example/pom.xml"), modulePom)); + String javaSource = """ + package com.example; + public class MainSource { - List sortedProjects = sut.getSortedProjects(Path.of(".").toAbsolutePath(), resources); + } + """; + + Path baseDir = Path.of(".").toAbsolutePath().normalize(); + Path exampleModuleDir = baseDir.resolve("example"); + + // @formatter:off + List resources = List.of( + new DummyResource(baseDir.resolve("pom.xml"), parentPom), + new DummyResource(exampleModuleDir.resolve("pom.xml"), modulePom), + new DummyResource(exampleModuleDir.resolve("src/main/java/com/acme/MainSource.java"), javaSource), + new DummyResource(exampleModuleDir.resolve("src/test/java/com/acme/MainSourceTest.java"), javaSource) + ); + // @formatter:on + + List sortedProjects = sut.getSortedProjects(baseDir, resources); assertThat(sortedProjects).hasSize(2); - String parentPomPath = Path.of(".").toAbsolutePath().normalize().toString(); - assertThat(sortedProjects.get(0).getBasedir().toString()).isEqualTo(parentPomPath); + MavenProject parentProject = sortedProjects.get(0); + MavenProject exampleProject = sortedProjects.get(1); + + assertThat(LinuxWindowsPathUnifier.pathEquals(baseDir.normalize(), parentProject.getBasedir())).isTrue(); + assertThat(LinuxWindowsPathUnifier.pathEquals(exampleProject.getBasedir(), + Path.of(".").resolve("example").toAbsolutePath().normalize())); + + assertThat(exampleProject.getMainJavaSources()).hasSize(1); + assertThat(LinuxWindowsPathUnifier + .unifiedPathString(ResourceUtil.getPath(exampleProject.getMainJavaSources().get(0))) + .endsWith("src/main/java/com/acme/MainSource.java")); + assertThat(exampleProject.getTestJavaSources()).hasSize(1); + assertThat(LinuxWindowsPathUnifier + .unifiedPathString(ResourceUtil.getPath(exampleProject.getMainJavaSources().get(0))) + .endsWith("src/test/java/com/acme/MainSourceTest.java")); - String modulePomPath = Path.of(".").resolve("example").toAbsolutePath().normalize().toString(); - assertThat(sortedProjects.get(1).getBasedir().toString()).isEqualTo(modulePomPath); } /** @@ -345,10 +373,10 @@ class MavenProjectAnalyzerTest { assertThat(sortedProjects).hasSize(2); String parentPomPath = Path.of(".").toAbsolutePath().normalize().toString(); - assertThat(sortedProjects.get(0).getBasedir().toString()).isEqualTo(parentPomPath); + assertThat(LinuxWindowsPathUnifier.pathEquals(sortedProjects.get(0).getBasedir(), parentPomPath)); String modulePomPath = Path.of(".").resolve("example").toAbsolutePath().normalize().toString(); - assertThat(sortedProjects.get(1).getBasedir().toString()).isEqualTo(modulePomPath); + assertThat(LinuxWindowsPathUnifier.pathEquals(sortedProjects.get(1).getBasedir(), modulePomPath)); } /** @@ -416,11 +444,12 @@ class MavenProjectAnalyzerTest { assertThat(sortedProjects).hasSize(2); - String parentPomPath = Path.of(".").toAbsolutePath().normalize().toString(); - assertThat(sortedProjects.get(0).getBasedir().toString()).isEqualTo(parentPomPath); + String parentPomPath = LinuxWindowsPathUnifier.unifiedPathString(Path.of(".").toAbsolutePath().normalize()); + assertThat(LinuxWindowsPathUnifier.pathEquals(sortedProjects.get(0).getBasedir(), parentPomPath)).isTrue(); - String modulePomPath = Path.of(".").resolve("example").toAbsolutePath().normalize().toString(); - assertThat(sortedProjects.get(1).getBasedir().toString()).isEqualTo(modulePomPath); + String modulePomPath = LinuxWindowsPathUnifier + .unifiedPathString(Path.of(".").resolve("example").toAbsolutePath().normalize()); + assertThat(LinuxWindowsPathUnifier.pathEquals(sortedProjects.get(1).getBasedir(), modulePomPath)).isTrue(); } /** diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializerTest.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializerTest.java index 1567899..188324f 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializerTest.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/parsers/maven/MavenSettingsInitializerTest.java @@ -23,6 +23,7 @@ import org.openrewrite.maven.tree.MavenRepository; import org.sonatype.plexus.components.cipher.PlexusCipherException; import org.springframework.rewrite.parsers.RewriteExecutionContext; import org.springframework.rewrite.scopes.ProjectMetadata; +import org.springframework.rewrite.utils.LinuxWindowsPathUnifier; import java.net.URISyntaxException; import java.nio.file.Path; @@ -57,13 +58,10 @@ class MavenSettingsInitializerTest { MavenRepository localRepository = mavenExecutionContextView.getLocalRepository(); assertThat(localRepository.getSnapshots()).isNull(); - String tmpDir = removeTrailingSlash(System.getProperty("java.io.tmpdir")); - String customLocalRepository = "file://" + Path.of(System.getProperty("user.home")) - .resolve(".m2/repository") - .toAbsolutePath() - .normalize() - .toString();// new URI("file://" + tmpDir).toString(); - assertThat(removeTrailingSlash(localRepository.getUri())).isEqualTo(customLocalRepository); + String expectedCustomLocalRepository = "file://" + LinuxWindowsPathUnifier.unifiedPathString( + Path.of(System.getProperty("user.home")).resolve(".m2/repository").toAbsolutePath().normalize()); + + assertThat(removeTrailingSlash(localRepository.getUri())).isEqualTo(expectedCustomLocalRepository); assertThat(localRepository.getSnapshots()).isNull(); assertThat(localRepository.isKnownToExist()).isTrue(); assertThat(localRepository.getUsername()).isNull(); diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/ParserParityTestHelper.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/ParserParityTestHelper.java index ee0f2b4..fe35f02 100644 --- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/ParserParityTestHelper.java +++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/ParserParityTestHelper.java @@ -15,6 +15,10 @@ */ package org.springframework.rewrite.test.util; +import org.apache.maven.shared.invoker.DefaultInvocationRequest; +import org.apache.maven.shared.invoker.DefaultInvoker; +import org.apache.maven.shared.invoker.InvocationRequest; +import org.apache.maven.shared.invoker.MavenInvocationException; import org.assertj.core.api.SoftAssertions; import org.jetbrains.annotations.NotNull; import org.openrewrite.ExecutionContext; @@ -30,8 +34,8 @@ import org.openrewrite.style.Style; import org.springframework.rewrite.parsers.SpringRewriteProperties; import org.springframework.rewrite.parsers.RewriteProjectParsingResult; +import java.io.File; import java.net.URI; -import java.net.URISyntaxException; import java.nio.file.Path; import java.util.*; import java.util.function.BiConsumer; @@ -97,6 +101,8 @@ public class ParserParityTestHelper { RewriteProjectParsingResult expectedParserResult = null; RewriteProjectParsingResult actualParserResult = null; + resolveDependencies(); + ParserExecutionHelper parserExecutionHelper = new ParserExecutionHelper(); if (isParallelParse) { ParallelParsingResult result = parserExecutionHelper.parseParallel(baseDir, springRewriteProperties, @@ -116,6 +122,39 @@ public class ParserParityTestHelper { customParserResultParityChecker.accept(actualParserResult, expectedParserResult); } + private void resolveDependencies() { + try { + DefaultInvoker defaultInvoker = new DefaultInvoker(); + InvocationRequest request = new DefaultInvocationRequest(); + request.setGoals(List.of("clean", "test-compile")); + request.setBaseDirectory(baseDir.toFile()); + // request.setBatchMode(true); + request.setPomFile(baseDir.resolve("pom.xml").toFile()); + + String mavenHome = ""; + if (System.getenv("MAVEN_HOME") != null) { + mavenHome = System.getenv("MAVEN_HOME"); + } + else if (System.getenv("MVN_HOME") != null) { + mavenHome = System.getenv("MVN_HOME"); + } + else if (System.getenv("M2_HOME") != null) { + mavenHome = System.getenv("M2_HOME"); + } + else { + throw new IllegalStateException("Neither MVN_HOME nor MAVEN_HOME set but required by MavenInvoker."); + } + + System.out.println("Using Maven %s".formatted(mavenHome)); + + request.setMavenHome(new File(mavenHome)); + defaultInvoker.execute(request); + } + catch (MavenInvocationException e) { + throw new RuntimeException(e); + } + } + public interface CustomParserResultParityChecker extends BiConsumer { @@ -280,7 +319,7 @@ public class ParserParityTestHelper { .ignoringFields("modules", // checked further down "dependencies", // checked further down "parent.modules" // TODO: - // https://github.com/spring-projects-experimental/spring-boot-migrator/issues/991 + // https://github.com/spring-projects-experimental/spring-boot-migrator/issues/991 ) .ignoringFieldsOfTypes(UUID.class) .isEqualTo(expected); @@ -363,20 +402,18 @@ public class ParserParityTestHelper { && f1.getFragment().equals(f2.getFragment()); } else if (actual instanceof String) { - try { - URI f1 = new URI((String) actual); - URI f2 = new URI((String) expected); - return f1.getScheme() == null ? (f2.getScheme() == null ? true : false) - : f1.getScheme().equals(f2.getScheme()) - && (f1.getHost() == null ? (f2.getHost() == null ? true : false) - : f1.getHost().equals(f2.getHost())) - && f1.getPath().equals(f2.getPath()) && f1.getFragment() == null - ? (f2.getFragment() == null ? true : false) - : f1.getFragment().equals(f2.getFragment()); - } - catch (URISyntaxException e) { - throw new RuntimeException(e); - } + URI f1 = new File((String) actual).toURI(); + URI f2 = new File((String) expected).toURI(); + // @formatter:off + return + f1.getScheme() != null && f2.getScheme() != null ? f1.getScheme().equals(f2.getScheme()) : f1.getScheme() == null && f2.getScheme() == null ? true : false + && + f1.getHost() != null && f2.getHost() != null ? f1.getHost().equals(f2.getHost()) : f1.getHost() == null && f2.getHost() == null ? true : false + && + f1.getPath() != null && f2.getPath() != null ? f1.getPath().equals(f2.getPath()) : f1.getPath() == null && f2.getPath() == null ? true : false + && + f1.getFragment() != null && f2.getFragment() != null ? f1.getFragment().equals(f2.getFragment()) : f1.getFragment() == null && f2.getFragment() == null ? true : false; + // @formatter:on } else { return false;