From a8cdc8aae88f5bad87136efc2614bd158d40b451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Kr=C3=BCger?= Date: Wed, 27 Sep 2023 14:31:44 +0200 Subject: [PATCH] WIP * Parsing single module project works * Classpath resolved --- .../MavenMojoProjectParserPrivateMethods.java | 149 +++++------------- .../sbm/parsers/MavenProjectAnalyzer.java | 14 +- .../parsers/RewriteParserConfiguration.java | 4 +- .../sbm/parsers/RewriteProjectParser.java | 13 +- .../sbm/parsers/SbmMavenProject.java | 47 ++++-- .../sbm/parsers/SourceFileParser.java | 21 ++- .../maven/CalculateClasspathTest.java | 148 +++++++++++++++++ .../sbm/parsers/MavenProjectAnalyzerTest.java | 27 ++-- .../RewriteMavenProjectParserTest.java | 2 +- .../RewriteProjectParserIntegrationTest.java | 11 +- .../sbm/parsers/RewriteProjectParserTest.java | 2 +- .../sbm/test/util/TestProjectHelper.java | 27 ++++ .../classpath-test/example-1/module-a/pom.xml | 28 ++++ .../src/main/java/com/example/ModuleA.java | 4 + .../classpath-test/example-1/module-b/pom.xml | 15 ++ .../src/main/java/com/example/ModuleB.java | 5 + .../classpath-test/example-1/pom.xml | 18 +++ 17 files changed, 380 insertions(+), 155 deletions(-) create mode 100644 sbm-support-rewrite/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java create mode 100644 sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/TestProjectHelper.java create mode 100644 sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/pom.xml create mode 100644 sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/src/main/java/com/example/ModuleA.java create mode 100644 sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/pom.xml create mode 100644 sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/src/main/java/com/example/ModuleB.java create mode 100644 sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/pom.xml diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenMojoProjectParserPrivateMethods.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenMojoProjectParserPrivateMethods.java index 8228da47c..8f9e29772 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenMojoProjectParserPrivateMethods.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenMojoProjectParserPrivateMethods.java @@ -21,6 +21,7 @@ import org.apache.maven.settings.crypto.DefaultSettingsDecrypter; import org.jetbrains.annotations.NotNull; import org.openrewrite.ExecutionContext; +import org.openrewrite.Parser; import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.internal.lang.Nullable; @@ -64,64 +65,51 @@ class MavenMojoProjectParserPrivateMethods { /** * process sources in src/main/java of current module. */ - public List processMainSources(Path baseDir, List resources, Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, SbmMavenProject mavenProject) { + public List processMainSources(Path baseDir, List resources, Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, SbmMavenProject mavenProject) { + log.info("Processing main sources in module '%s'".formatted(mavenProject.getModuleDir())); // 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. - List javaSourcesInTarget = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getBuildDirectory())); - List javaSourcesInMain = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getSourceDirectory())); - List mainJavaSources = Stream.concat(javaSourcesInTarget.stream(),javaSourcesInMain.stream()).toList(); + List javaSourcesInTarget = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getBuildDirectory())); + List javaSourcesInMain = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getSourceDirectory())); + List mainJavaSources = Stream.concat(javaSourcesInTarget.stream(),javaSourcesInMain.stream()).toList(); alreadyParsed.addAll(mainJavaSources); log.info("[%s] Parsing source files".formatted(mavenProject)); + // FIXME 945 classpath + // - Resolve dependencies to non-reactor projects from Maven repository + // - Resolve dependencies to reactor projects by providing the sources + // javaParserBuilder.classpath(byte[]) - /* - @Override - public Set getClasspath(Scope scope) { - Map> resolvedDependenciesMap = getResolvedDependenciesMap(); - Set classpath = resolvedDependenciesMap.entrySet().stream() - .filter(e -> e.getKey().isInClasspathOf(scope)) - .map(e -> e.getValue()) - .flatMap(Set::stream) - .collect(Collectors.toSet()); - return classpath; - } + // 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. - @Override - public Map> getResolvedDependenciesMap() { - Map> dependenciesMap = new HashMap<>(); - Arrays.stream(Scope.values()).forEach(scope -> { - List resolvedDependencies = getPom().getDependencies().get(scope); - if(resolvedDependencies != null) { - Set paths = resolvedDependencies - .stream() - .map(rd -> rewriteMavenArtifactDownloader.downloadArtifact(rd)) - .collect(Collectors.toSet()); - dependenciesMap.put(scope, paths); - } - }); - return dependenciesMap; - } - */ - List dependencies = mavenProject.getCompileClasspathElements().stream() - .distinct() - .map(Paths::get) - .collect(toList()); + List dependencies = mavenProject.getCompileClasspathElements(); + javaParserBuilder.classpath(dependencies); + JavaTypeCache typeCache = new JavaTypeCache(); javaParserBuilder.typeCache(typeCache); + Iterable inputs = mainJavaSources.stream() + .map(r -> new Parser.Input(ResourceUtil.getPath(r), () -> ResourceUtil.getInputStream(r))) + .toList(); + Stream cus = Stream.of(javaParserBuilder) .map(JavaParser.Builder::build) - .flatMap(parser -> parser.parse(mainJavaSources, baseDir, executionContext)); + .flatMap(parser -> parser.parseInputs(inputs, baseDir, executionContext)); List mainProjectProvenance = new ArrayList<>(provenanceMarkers); mainProjectProvenance.add(sourceSet("main", dependencies, typeCache)); - Stream parsedJava = cus.map(addProvenance(baseDir, mainProjectProvenance, javaSourcesInTarget)); + // FIXME: 945 Why target and not all main? + List parsedJavaPaths = javaSourcesInTarget.stream().map(ResourceUtil::getPath).toList(); + Stream parsedJava = cus.map(addProvenance(baseDir, mainProjectProvenance, parsedJavaPaths)); log.debug("[%s] Scanned %d java source files in main scope.".formatted(mavenProject, mainJavaSources.size())); //Filter out any generated source files from the returned list, as we do not want to apply the recipe to the @@ -130,7 +118,7 @@ public Map> getResolvedDependenciesMap() { Stream sourceFiles = parsedJava.filter(s -> !s.getSourcePath().startsWith(buildDirectory)); int sourcesParsedBefore = alreadyParsed.size(); - Stream parsedResourceFiles = rp.parse(mavenProject.getBasedir().resolve("src/main/resources"), alreadyParsed) + Stream parsedResourceFiles = rp.parse(mavenProject.getBasedir().resolve("src/main/resources"), alreadyParsed.stream().map(ResourceUtil::getPath).toList()) .map(addProvenance(baseDir, mainProjectProvenance, null)); log.debug("[%s] Scanned %d resource files in main scope.".formatted(mavenProject, (alreadyParsed.size() - sourcesParsedBefore))); @@ -142,19 +130,23 @@ public Map> getResolvedDependenciesMap() { /** * Calls {@link MavenMojoProjectParser#processTestSources(SbmMavenProject, JavaParser.Builder, ResourceParser, List, Set, ExecutionContext)} */ - public List processTestSources(Path baseDir, Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, SbmMavenProject mavenProject, List resources) { + public List processTestSources(Path baseDir, Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, SbmMavenProject mavenProject, List resources) { List testDependencies = mavenProject.getTestClasspathElements(); javaParserBuilder.classpath(testDependencies); JavaTypeCache typeCache = new JavaTypeCache(); javaParserBuilder.typeCache(typeCache); - List testJavaSources = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getTestSourceDirectory())); + List testJavaSources = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getTestSourceDirectory())); alreadyParsed.addAll(testJavaSources); + Iterable inputs = testJavaSources.stream() + .map(r -> new Parser.Input(ResourceUtil.getPath(r), () -> ResourceUtil.getInputStream(r))) + .toList(); + Stream cus = Stream.of(javaParserBuilder) .map(JavaParser.Builder::build) - .flatMap(parser -> parser.parse(testJavaSources, baseDir, executionContext)); + .flatMap(parser -> parser.parseInputs(inputs, baseDir, executionContext)); List markers = new ArrayList<>(provenanceMarkers); markers.add(sourceSet("test", testDependencies, typeCache)); @@ -166,70 +158,12 @@ public List processTestSources(Path baseDir, Xml.Document moduleBuil // Any resources parsed from "test/resources" should also have the test source set added to them. int sourcesParsedBefore = alreadyParsed.size(); - Stream parsedResourceFiles = rp.parse(mavenProject.getBasedir().resolve("src/test/resources"), alreadyParsed) + Stream parsedResourceFiles = rp.parse(mavenProject.getBasedir().resolve("src/test/resources"), alreadyParsed.stream().map(ResourceUtil::getPath).toList()) .map(addProvenance(baseDir, markers, null)); log.debug("[%s] Scanned %d resource files in test scope.".formatted(mavenProject, (alreadyParsed.size() - sourcesParsedBefore))); sourceFiles = Stream.concat(sourceFiles, parsedResourceFiles); - return sourceFiles.toList(); - } - - /** - * See {@link MavenMojoProjectParser#processMainSources(SbmMavenProject, JavaParser.Builder, ResourceParser, List, Set, ExecutionContext)} - */ - @NotNull - private List invokeProcessMethod( - Path baseDir, - SbmMavenProject mavenProject, - Xml.Document moduleBuildFile, - JavaParser.Builder javaParserBuilder, - ResourceParser resourceParser, - List provenanceMarkers, - Set alreadyParsed, - ExecutionContext executionContext, - String methodName, - List resources) { - // 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. - List generatedSourcePaths = listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getBuildDirectory())); - List mainJavaSources = Stream.concat( - generatedSourcePaths.stream(), - listJavaSources(resources, mavenProject.getBasedir().resolve(mavenProject.getSourceDirectory())).stream() - ).collect(toList()); - - alreadyParsed.addAll(mainJavaSources); - - List dependencies = mavenProject.getCompileClasspathElements().stream() - .distinct() - .map(Paths::get) - .collect(toList()); - javaParserBuilder.classpath(dependencies); - JavaTypeCache typeCache = new JavaTypeCache(); - javaParserBuilder.typeCache(typeCache); - - Stream cus = Stream.of(javaParserBuilder) - .map(JavaParser.Builder::build) - .flatMap(parser -> parser.parse(mainJavaSources, baseDir, executionContext)); - - List mainProjectProvenance = new ArrayList<>(provenanceMarkers); - mainProjectProvenance.add(sourceSet("main", dependencies, typeCache)); - - Stream parsedJava = cus.map(addProvenance(baseDir, mainProjectProvenance, generatedSourcePaths)); - - log.debug("[%s] Scanned %d java source files in main scope.".formatted(mavenProject.getArtifactId(), 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 = baseDir.relativize(Paths.get(mavenProject.getBuildDirectory())); - Stream sourceFiles = parsedJava.filter(s -> !s.getSourcePath().startsWith(buildDirectory)); - - int sourcesParsedBefore = alreadyParsed.size(); - Stream parsedResourceFiles = resourceParser.parse(mavenProject.getBasedir().resolve("src/main/resources"), alreadyParsed) - .map(addProvenance(baseDir, mainProjectProvenance, null)); - - log.debug("[%s] Scanned %s resource files in main scope.".formatted(mavenProject.getArtifactId(), (alreadyParsed.size() - sourcesParsedBefore))); - // Any resources parsed from "main/resources" should also have the main source set added to them. - sourceFiles = Stream.concat(sourceFiles, parsedResourceFiles); - return sourceFiles.toList(); + List result = sourceFiles.toList(); + return result; } @NotNull @@ -239,22 +173,21 @@ private static JavaSourceSet sourceSet(String name, List dependencies, Jav // FIXME: 945 take Java sources from resources - private static List listJavaSources(List resources, Path sourceDirectory) { + private static List listJavaSources(List resources, Path sourceDirectory) { return resources.stream() - .map(ResourceUtil::getPath) .filter(whenIn(sourceDirectory)) .filter(whenFileNameEndsWithJava()) .toList(); } @NotNull - private static Predicate whenFileNameEndsWithJava() { - return p -> p.getFileName().toString().endsWith(".java"); + private static Predicate whenFileNameEndsWithJava() { + return p -> ResourceUtil.getPath(p).getFileName().toString().endsWith(".java"); } @NotNull - private static Predicate whenIn(Path sourceDirectory) { - return p -> p.toString().startsWith(sourceDirectory.toString()); + private static Predicate whenIn(Path sourceDirectory) { + return r -> ResourceUtil.getPath(r).toString().startsWith(sourceDirectory.toString()); } diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenProjectAnalyzer.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenProjectAnalyzer.java index d9b8b2c3a..fce2fc3dc 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenProjectAnalyzer.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/MavenProjectAnalyzer.java @@ -18,6 +18,7 @@ import org.apache.maven.model.*; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.openrewrite.maven.utilities.MavenArtifactDownloader; import org.springframework.core.io.Resource; import org.springframework.sbm.utils.ResourceUtil; @@ -36,8 +37,13 @@ */ public class MavenProjectAnalyzer { - public static final String POM_XML = "pom.xml"; - public static final MavenXpp3Reader XPP_3_READER = new MavenXpp3Reader(); + private static final String POM_XML = "pom.xml"; + private static final MavenXpp3Reader XPP_3_READER = new MavenXpp3Reader(); + private final MavenArtifactDownloader rewriteMavenArtifactDownloader; + + public MavenProjectAnalyzer(MavenArtifactDownloader rewriteMavenArtifactDownloader) { + this.rewriteMavenArtifactDownloader = rewriteMavenArtifactDownloader; + } public List getSortedProjects(Path baseDir, List resources) { @@ -51,7 +57,7 @@ public List getSortedProjects(Path baseDir, List reso Model rootPomModel = new Model(rootPom); if (isSingleModuleProject(rootPomModel)) { - return List.of(new SbmMavenProject(baseDir, rootPom, rootPomModel)); + return List.of(new SbmMavenProject(baseDir, rootPom, rootPomModel, rewriteMavenArtifactDownloader)); } List reactorModels = new ArrayList<>(); recursivelyFindReactorModules(baseDir, null, reactorModels, allPomFiles, rootPomModel); @@ -62,7 +68,7 @@ public List getSortedProjects(Path baseDir, List reso private List map(Path baseDir, List sortedModels) { List sbmMavenProjects = new ArrayList<>(); sortedModels.forEach(m -> { - sbmMavenProjects.add(new SbmMavenProject(baseDir, m.getResource(), m)); + sbmMavenProjects.add(new SbmMavenProject(baseDir, m.getResource(), m, rewriteMavenArtifactDownloader)); }); // set all non parent poms as collected projects for root parent pom List collected = new ArrayList<>(sbmMavenProjects); diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteParserConfiguration.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteParserConfiguration.java index ab029a72a..4e155f21e 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteParserConfiguration.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteParserConfiguration.java @@ -146,8 +146,8 @@ RewriteMavenProjectParser rewriteMavenProjectParser(MavenPlexusContainer plexusC } @Bean - MavenProjectAnalyzer mavenProjectAnalyzer() { - return new MavenProjectAnalyzer(); + MavenProjectAnalyzer mavenProjectAnalyzer(MavenArtifactDownloader artifactDownloader) { + return new MavenProjectAnalyzer(artifactDownloader); } @Bean diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteProjectParser.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteProjectParser.java index 57ba66ce4..6466999bd 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteProjectParser.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/RewriteProjectParser.java @@ -163,6 +163,7 @@ public RewriteProjectParsingResult parse(Path givenBaseDir, List resou List parsedAndSortedBuildFileDocuments = mavenInfos.getResources().stream() .map(r -> resourceToDocumentMap.get(ResourceUtil.getPath(r))) .map(SourceFile.class::cast) + .peek(sourceFile -> addSourceFileToModel(baseDir, sortedProjectsList, sourceFile)) .toList(); // FIXME: 945 - classpath required @@ -171,8 +172,7 @@ public RewriteProjectParsingResult parse(Path givenBaseDir, List resou k -> k.getKey(), k -> k.getValue().getMarkers().findFirst(MavenResolutionResult.class).get().getPom().getRequested() )); - try { - List resolvedDependencies = parsedAndSortedBuildFileDocuments.get(0).getMarkers().findFirst(MavenResolutionResult.class).get().getPom().resolveDependencies(Scope.Compile, new MavenPomDownloader(projectPoms, executionContext), executionContext); +// List resolvedDependencies = parsedAndSortedBuildFileDocuments.get(0).getMarkers().findFirst(MavenResolutionResult.class).get().getPom().resolveDependencies(Scope.Compile, new MavenPomDownloader(projectPoms, executionContext), executionContext); // 128 : 131 @@ -188,13 +188,16 @@ public RewriteProjectParsingResult parse(Path givenBaseDir, List resou eventPublisher.publishEvent(new SuccessfullyParsedProjectEvent(sourceFiles)); return new RewriteProjectParsingResult(sourceFiles, executionContext); - } catch (MavenDownloadingExceptions e) { - throw new RuntimeException(e); - } // }); } + private static void addSourceFileToModel(Path baseDir, List sortedProjectsList, SourceFile s) { + sortedProjectsList.stream() + .filter(p -> ResourceUtil.getPath(p.getPomFile()).toString().equals(baseDir.resolve(s.getSourcePath()).toString())) + .forEach(p -> p.setSourceFile(s)); + } + private void withMavenSession(Path baseDir, Consumer consumer) { List goals = List.of("clean", "package"); log.debug("Successfully finished goals %s".formatted(goals)); diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SbmMavenProject.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SbmMavenProject.java index 187e5d6cc..e88d2f32b 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SbmMavenProject.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SbmMavenProject.java @@ -17,17 +17,19 @@ import lombok.Getter; import lombok.Setter; -import org.apache.maven.Maven; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.model.Build; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; +import org.jetbrains.annotations.NotNull; +import org.openrewrite.SourceFile; +import org.openrewrite.maven.tree.*; +import org.openrewrite.maven.utilities.MavenArtifactDownloader; import org.springframework.core.io.Resource; import org.springframework.sbm.utils.ResourceUtil; import java.io.File; import java.nio.file.Path; import java.util.*; +import java.util.stream.Collectors; @Getter @@ -42,11 +44,14 @@ public class SbmMavenProject { // FIXME: 945 temporary method, model should nopt come from Maven private final Model pomModel; private List collectedProjects = new ArrayList<>(); + private SourceFile sourceFile; + private final MavenArtifactDownloader rewriteMavenArtifactDownloader; - public SbmMavenProject(Path projectRoot, Resource pomFile, Model pomModel) { + public SbmMavenProject(Path projectRoot, Resource pomFile, Model pomModel, MavenArtifactDownloader rewriteMavenArtifactDownloader) { this.projectRoot = projectRoot; this.pomFile = pomFile; this.pomModel = pomModel; + this.rewriteMavenArtifactDownloader = rewriteMavenArtifactDownloader; } public File getFile() { @@ -119,7 +124,8 @@ public String getVersion() { @Override public String toString() { - return pomModel.getGroupId() == null ? pomModel.getParent().getGroupId() : pomModel.getGroupId() + ":" + pomModel.getArtifactId(); + String groupId = pomModel.getGroupId() == null ? pomModel.getParent().getGroupId() : pomModel.getGroupId(); + return groupId + ":" + pomModel.getArtifactId(); } public String getBuildDirectory() { @@ -132,18 +138,39 @@ public String getSourceDirectory() { return s == null ? ResourceUtil.getPath(pomFile).getParent().resolve("src/main/java").toAbsolutePath().normalize().toString() : s; } - public List getCompileClasspathElements() { - // FIXME: 945 - implement method - return List.of(); + public List getCompileClasspathElements() { + Scope scope = Scope.Compile; + return getClasspathElements(scope); } public List getTestClasspathElements() { - // FIXME: 945 - implement method - return List.of(); + return getClasspathElements(Scope.Test); + } + + @NotNull + private List getClasspathElements(Scope scope) { + MavenResolutionResult pom = getSourceFile().getMarkers().findFirst(MavenResolutionResult.class).get(); + List resolvedDependencies = pom.getDependencies().get(scope); + if(resolvedDependencies != null) { + return resolvedDependencies + // FIXME: 945 - deal with dependencies to projects in reactor + // + .stream() + .filter(rd -> rd.getRepository() != null) + .map(rd -> rewriteMavenArtifactDownloader.downloadArtifact(rd)) + .distinct() + .toList(); + } else { + return new ArrayList<>(); + } } public String getTestSourceDirectory() { String s = pomModel.getBuild() != null ? pomModel.getBuild().getSourceDirectory() : null; return s == null ? ResourceUtil.getPath(pomFile).getParent().resolve("src/test/java").toAbsolutePath().normalize().toString() : s; } + + public void setSourceFile(SourceFile sourceFile) { + this.sourceFile = sourceFile; + } } diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SourceFileParser.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SourceFileParser.java index dc2a5d408..6cc17dcf3 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SourceFileParser.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/parsers/SourceFileParser.java @@ -24,6 +24,7 @@ import org.openrewrite.maven.ResourceParser; import org.openrewrite.style.NamedStyles; import org.openrewrite.xml.tree.Xml; +import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.sbm.utils.ResourceUtil; @@ -63,7 +64,7 @@ public List parseOtherSourceFiles( if(markers == null || markers.isEmpty()) { log.warn("Could not find provenance markers for resource '%s'".formatted(mavenProject.getMatchingBuildFileResource(currentMavenProject))); } - List sourceFiles = parseModuleSourceFiles(resources, currentMavenProject, moduleBuildFile, markers, styles, executionContext, baseDir); + List sourceFiles = parseModuleSourceFiles(resources, mavenProject, currentMavenProject, moduleBuildFile, markers, styles, executionContext, baseDir); parsedSourceFiles.addAll(sourceFiles); }); @@ -75,6 +76,7 @@ public List parseOtherSourceFiles( */ private List parseModuleSourceFiles( List resources, + SortedProjects mavenProject, SbmMavenProject sbmMavenProject, Xml.Document moduleBuildFile, List provenanceMarkers, @@ -98,6 +100,7 @@ private List parseModuleSourceFiles( Path buildFilePath = sbmMavenProject.getBasedir().resolve(moduleBuildFile.getSourcePath()); // these paths will be ignored by ResourceParser Set skipResourceScanDirs = pathsToOtherMavenProjects(sbmMavenProject, buildFilePath); + // FIXME: Why is skipResourceScanDirs required at all? Shouldn't the module know it's resources ResourceParser rp = new ResourceParser( baseDir, new Slf4jToMavenLoggerAdapter(log), @@ -109,14 +112,16 @@ private List parseModuleSourceFiles( ); // 155:156: parse main and test sources - Set alreadyParsed = new HashSet<>(); - alreadyParsed.add(baseDir.resolve(moduleBuildFile.getSourcePath())); + Set alreadyParsed = new HashSet<>(); + Path moduleBuildFilePath = baseDir.resolve(moduleBuildFile.getSourcePath()); + alreadyParsed.add(new FileSystemResource(moduleBuildFilePath)); List mainSources = parseMainSources(baseDir, sbmMavenProject, moduleBuildFile, resources, javaParserBuilder.clone(), rp, provenanceMarkers, alreadyParsed, executionContext); List testSources = parseTestSources(baseDir, sbmMavenProject, moduleBuildFile, javaParserBuilder.clone(), rp, provenanceMarkers, alreadyParsed, executionContext, resources); - alreadyParsed.addAll(skipResourceScanDirs); +// FIXME: 945 alreadyParsed.addAll(skipResourceScanDirs); + // 171:175 - Stream parsedResourceFiles = rp.parse(baseDir.resolve(moduleBuildFile.getSourcePath()).getParent(), alreadyParsed ) + Stream parsedResourceFiles = rp.parse(moduleBuildFilePath.getParent(), alreadyParsed.stream().map(ResourceUtil::getPath).toList()) // FIXME: handle generated sources .map(mavenMojoProjectParserPrivateMethods.addProvenance(baseDir, provenanceMarkers, null)); @@ -125,6 +130,8 @@ private List parseModuleSourceFiles( List resourceFilesList = parsedResourceFiles.toList(); sourceFiles.addAll(resourceFilesList); sourceFiles.addAll(resourceSourceFiles); + sourceFiles.addAll(mainSources); + sourceFiles.addAll(testSources); return sourceFiles; } @@ -147,13 +154,13 @@ private static boolean isNotExcluded(Path baseDir, List exclusions, .noneMatch(pm -> pm.matches(baseDir.resolve(s.getSourcePath()).toAbsolutePath().normalize())); } - private List parseTestSources(Path baseDir, SbmMavenProject sbmMavenProject, Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, List resources) { + private List parseTestSources(Path baseDir, SbmMavenProject sbmMavenProject, Xml.Document moduleBuildFile, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext, List resources) { return mavenMojoProjectParserPrivateMethods.processTestSources(baseDir, moduleBuildFile, javaParserBuilder, rp, provenanceMarkers, alreadyParsed, executionContext, sbmMavenProject, resources); } /** */ - private List parseMainSources(Path baseDir, SbmMavenProject sbmMavenProject, Xml.Document moduleBuildFile, List resources, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext) { + private List parseMainSources(Path baseDir, SbmMavenProject sbmMavenProject, Xml.Document moduleBuildFile, List resources, JavaParser.Builder javaParserBuilder, ResourceParser rp, List provenanceMarkers, Set alreadyParsed, ExecutionContext executionContext) { // MavenMojoProjectParser#processMainSources(..) takes SbmMavenProject // it reads from it: // - sbmMavenProject.getBuild().getDirectory() diff --git a/sbm-support-rewrite/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java b/sbm-support-rewrite/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java new file mode 100644 index 000000000..e369d5589 --- /dev/null +++ b/sbm-support-rewrite/src/test/java/org/openrewrite/maven/CalculateClasspathTest.java @@ -0,0 +1,148 @@ +/* + * Copyright 2021 - 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.maven; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openrewrite.SourceFile; +import org.openrewrite.java.marker.JavaSourceSet; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.Resource; +import org.springframework.sbm.boot.autoconfigure.ScannerConfiguration; +import org.springframework.sbm.parsers.RewriteExecutionContext; +import org.springframework.sbm.parsers.RewriteProjectParser; +import org.springframework.sbm.parsers.RewriteProjectParsingResult; +import org.springframework.sbm.test.util.DummyResource; +import org.springframework.sbm.test.util.TestProjectHelper; + +import java.nio.file.Path; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Fabian Krüger + */ +@SpringBootTest(classes = {ScannerConfiguration.class}) +public class CalculateClasspathTest { + + @Autowired + RewriteProjectParser parser; + + @Test + @DisplayName("classpath for single-module project") + void classpathForSingleModuleProject() { + @Language("xml") + String pom = """ + + + 4.0.0 + com.example + example-1 + 0.1.0-SNAPSHOT + + 17 + 17 + + + + javax.validation + validation-api + 2.0.1.Final + + + org.junit.jupiter + junit-jupiter-api + 5.9.3 + test + + + + """; + + @Language("java") + String mainClass = """ + package com.example; + import javax.validation.constraints.Min; + + public class MainClass { + @Min("10") + private int value; + } + """; + + @Language("java") + String testClass = """ + package com.example; + import org.junit.jupiter.api.Test; + + public class TestClass { + @Test + void someTest() {} + } + """; + + Path baseDir = Path.of("./target/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) + ); + + RewriteProjectParsingResult parsingResult = parser.parse(baseDir, resources, new RewriteExecutionContext()); + + // verify types in use + SourceFile mainSourceFile = parsingResult.sourceFiles().get(1); + 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); + J.CompilationUnit testCu = (J.CompilationUnit) testSourceFile; + // Having Test annotation resolved proves type resolution is working for test resources + assertThat(testCu.getTypesInUse().getTypesInUse().stream().map(t -> t.toString())).containsExactlyInAnyOrder("void", "org.junit.jupiter.api.Test"); + + // verify classpath + List mainClasspath = mainCu.getMarkers().findFirst(JavaSourceSet.class).get().getClasspath().stream().map(JavaType.FullyQualified::getFullyQualifiedName).toList(); + // Min is on main classpath + assertThat(mainClasspath).contains("javax.validation.constraints.Min"); + // Test is not + assertThat(mainClasspath).doesNotContain("org.junit.jupiter.api.Test"); + + List testClasspath = testCu.getMarkers().findFirst(JavaSourceSet.class).get().getClasspath().stream().map(JavaType.FullyQualified::getFullyQualifiedName).toList(); + // all main classes on test classpath + assertThat(testClasspath).containsAll(mainClasspath); + // plus the classes from test dependencies + assertThat(testClasspath).contains("org.junit.jupiter.api.Test"); + } + + /** + * Given a multi-module Maven reactor project. + * - Where module A depends on B and both inherit from same parent. + * - Module A has a + */ + @Test + @DisplayName("classpath for reactor build") + void classpathForReactorBuild() { + Path mavenProject = TestProjectHelper.getMavenProject("classpath-test/example-1"); + RewriteProjectParsingResult parsingResult = parser.parse(mavenProject); + } +} diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/MavenProjectAnalyzerTest.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/MavenProjectAnalyzerTest.java index b0a840374..638de9420 100644 --- a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/MavenProjectAnalyzerTest.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/MavenProjectAnalyzerTest.java @@ -16,14 +16,15 @@ package org.springframework.sbm.parsers; -import org.apache.commons.io.FileUtils; import org.apache.maven.execution.MavenSession; import org.apache.maven.project.MavenProject; import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.openrewrite.maven.utilities.MavenArtifactDownloader; import org.springframework.core.io.Resource; import org.springframework.sbm.test.util.DummyResource; import org.springframework.sbm.utils.ResourceUtil; @@ -33,15 +34,23 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * @author Fabian Krüger */ class MavenProjectAnalyzerTest { + private MavenProjectAnalyzer sut; + + @BeforeEach + void beforeEach() { + MavenArtifactDownloader rewriteMavenArtifactDownloader = mock(RewriteMavenArtifactDownloader.class); + sut = new MavenProjectAnalyzer(rewriteMavenArtifactDownloader); + } + @Nested class CompareWithMaven { @Test @@ -151,7 +160,6 @@ void compareMavenProjectGetCollectedProjects(@TempDir Path tmpDir) { new DummyResource(baseDir.resolve("parent-b/module-1/pom.xml"), module1Pom) ); - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); writeToDisk(baseDir, resources); MavenSession mavenSession = startMavenSession(baseDir); @@ -223,7 +231,7 @@ void projectWithSinglePom() { """; List resources = List.of(new DummyResource(Path.of("pom.xml"), singlePom)); - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); + Path baseDir = Path.of(".").toAbsolutePath().normalize(); List sortedProjects = sut.getSortedProjects(baseDir, resources); assertThat(sortedProjects).hasSize(1); @@ -273,7 +281,7 @@ void reactorBuild() { new DummyResource(Path.of("example/pom.xml"), modulePom) ); - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); + List sortedProjects = sut.getSortedProjects(Path.of(".").toAbsolutePath(), resources); assertThat(sortedProjects).hasSize(2); @@ -344,7 +352,7 @@ void reactorBuildWithDanglingPom() { new DummyResource(Path.of("dangling/pom.xml"), danglingPom) ); - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); + List sortedProjects = sut.getSortedProjects(Path.of(".").toAbsolutePath(), resources); assertThat(sortedProjects).hasSize(2); @@ -423,7 +431,7 @@ void reactorBuildWithDanglingPomWhichAReactorModuleDependsOn() { new DummyResource(Path.of("dangling/pom.xml"), danglingPom) ); - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); + List sortedProjects = sut.getSortedProjects(Path.of(".").toAbsolutePath(), resources); assertThat(sortedProjects).hasSize(2); @@ -513,7 +521,7 @@ void theReactorBuildOrderIsReturned() { new DummyResource(Path.of("pom.xml"), parentPom) ); - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); + List sortedProjects = sut.getSortedProjects(Path.of(".").toAbsolutePath(), resources); // Returned ordered @@ -613,7 +621,7 @@ void moreComplex() { """; - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); + List resources = List.of( new DummyResource(Path.of("module-b/pom.xml"), moduleBPom), @@ -721,7 +729,6 @@ void sortModels() { """; // Provided unordered - MavenProjectAnalyzer sut = new MavenProjectAnalyzer(); List models = List.of( new MavenProjectAnalyzer.Model(new DummyResource(Path.of("module-b/pom.xml"), moduleBPom)), new MavenProjectAnalyzer.Model(new DummyResource(Path.of("module-a/pom.xml"), moduleAPom)), diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteMavenProjectParserTest.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteMavenProjectParserTest.java index 418fcf87c..36917ee34 100644 --- a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteMavenProjectParserTest.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteMavenProjectParserTest.java @@ -292,7 +292,7 @@ void parseMultiModule1_WithCustomParser() { beanFactory, new ProjectScanner(new DefaultResourceLoader(), parserProperties), ctx, - new MavenProjectAnalyzer() + new MavenProjectAnalyzer(mock(RewriteMavenArtifactDownloader.class)) ); Set ignoredPatters = Set.of(); diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserIntegrationTest.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserIntegrationTest.java index a68e06b54..565e33124 100644 --- a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserIntegrationTest.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserIntegrationTest.java @@ -28,6 +28,7 @@ import org.springframework.sbm.parsers.events.StartedParsingProjectEvent; import org.springframework.sbm.parsers.events.SuccessfullyParsedProjectEvent; import org.springframework.sbm.boot.autoconfigure.ScannerConfiguration; +import org.springframework.sbm.test.util.TestProjectHelper; import java.nio.file.Path; import java.util.ArrayList; @@ -53,7 +54,7 @@ public class RewriteProjectParserIntegrationTest { @Test @DisplayName("parseCheckstyle") void parseCheckstyle() { - Path baseDir = getMavenProject("checkstyle"); + Path baseDir = TestProjectHelper.getMavenProject("checkstyle"); List resources = projectScanner.scan(baseDir); RewriteProjectParsingResult parsingResult = sut.parse(baseDir, resources, new InMemoryExecutionContext(t -> {throw new RuntimeException(t);})); assertThat(parsingResult.sourceFiles().stream().map(sf -> sf.getSourcePath().toString()).toList()).contains("checkstyle/rules.xml"); @@ -74,7 +75,7 @@ void testFailingProject() { @Test @DisplayName("parseResources") void parseResources() { - Path baseDir = getMavenProject("resources"); + Path baseDir = TestProjectHelper.getMavenProject("resources"); List resources = projectScanner.scan(baseDir); RewriteProjectParsingResult parsingResult1 = mavenProjectParser.parse(baseDir); @@ -89,7 +90,7 @@ void parseResources() { @Test @DisplayName("parse4Modules") void parse4Modules() { - Path baseDir = getMavenProject("4-modules"); + Path baseDir = TestProjectHelper.getMavenProject("4-modules"); List resources = projectScanner.scan(baseDir); assertThat(resources).hasSize(4); @@ -98,8 +99,4 @@ void parse4Modules() { assertThat(parsingResult.sourceFiles()).hasSize(4); } - private Path getMavenProject(String s) { - return Path.of("./testcode/maven-projects/").resolve(s).toAbsolutePath().normalize(); - } - } diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserTest.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserTest.java index 28f907163..08197e3c9 100644 --- a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserTest.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteProjectParserTest.java @@ -116,7 +116,7 @@ void parseSimpleMavenProject(@TempDir Path tempDir) { mock(ConfigurableListableBeanFactory.class), new ProjectScanner(new DefaultResourceLoader(), parserProperties), executionContext, - new MavenProjectAnalyzer() + new MavenProjectAnalyzer(mock(RewriteMavenArtifactDownloader.class)) ); List parsedFiles = new ArrayList<>(); diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/TestProjectHelper.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/TestProjectHelper.java new file mode 100644 index 000000000..fa323de37 --- /dev/null +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/TestProjectHelper.java @@ -0,0 +1,27 @@ +/* + * Copyright 2021 - 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.sbm.test.util; + +import java.nio.file.Path; + +/** + * @author Fabian Krüger + */ +public class TestProjectHelper { + public static Path getMavenProject(String s) { + return Path.of("./testcode/maven-projects/").resolve(s).toAbsolutePath().normalize(); + } +} diff --git a/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/pom.xml b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/pom.xml new file mode 100644 index 000000000..d78510fc7 --- /dev/null +++ b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + com.example + example-1-parent + 0.1.0-SNAPSHOT + + module-a + + 17 + 17 + + + + com.example + module-b + ${project.version} + + + javax.validation + validation-api + 2.0.1.Final + + + + diff --git a/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/src/main/java/com/example/ModuleA.java b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/src/main/java/com/example/ModuleA.java new file mode 100644 index 000000000..2f0222c8f --- /dev/null +++ b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-a/src/main/java/com/example/ModuleA.java @@ -0,0 +1,4 @@ +package com.example; +public class ModuleA extends ModuleB { + +} \ No newline at end of file diff --git a/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/pom.xml b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/pom.xml new file mode 100644 index 000000000..c02acadc7 --- /dev/null +++ b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + com.example + example-1-parent + 0.1.0-SNAPSHOT + + module-b + + 17 + 17 + + diff --git a/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/src/main/java/com/example/ModuleB.java b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/src/main/java/com/example/ModuleB.java new file mode 100644 index 000000000..eb093f173 --- /dev/null +++ b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/module-b/src/main/java/com/example/ModuleB.java @@ -0,0 +1,5 @@ +package com.example; + +public class ModuleB { + +} \ No newline at end of file diff --git a/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/pom.xml b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/pom.xml new file mode 100644 index 000000000..403606457 --- /dev/null +++ b/sbm-support-rewrite/testcode/maven-projects/classpath-test/example-1/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.example + example-1-parent + 0.1.0-SNAPSHOT + pom + + 17 + 17 + + + module-a + module-b + + +