All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.maven.MavenMojoProjectParser Maven / Gradle / Ivy

/*
 * Copyright 2020 the original author or authors.
 * 

* Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://docs.moderne.io/licensing/moderne-source-available-license *

* 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.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; import org.apache.maven.model.Profile; import org.apache.maven.model.Repository; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.rtinfo.RuntimeInformation; import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; import org.apache.maven.settings.crypto.SettingsDecrypter; import org.apache.maven.settings.crypto.SettingsDecryptionRequest; import org.apache.maven.settings.crypto.SettingsDecryptionResult; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.ParseExceptionResult; import org.openrewrite.SourceFile; import org.openrewrite.internal.StringUtils; import org.openrewrite.java.JavaParser; import org.openrewrite.java.internal.JavaTypeCache; import org.openrewrite.java.marker.JavaProject; import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.marker.JavaVersion; import org.openrewrite.java.tree.J; import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.marker.*; import org.openrewrite.marker.ci.BuildEnvironment; import org.openrewrite.maven.cache.InMemoryMavenPomCache; import org.openrewrite.maven.cache.MavenPomCache; import org.openrewrite.maven.internal.RawPom; import org.openrewrite.maven.internal.RawRepositories; import org.openrewrite.maven.tree.Pom; import org.openrewrite.maven.tree.ProfileActivation; import org.openrewrite.maven.tree.ResolvedGroupArtifactVersion; import org.openrewrite.maven.utilities.MavenWrapper; import org.openrewrite.style.NamedStyles; import org.openrewrite.xml.tree.Xml; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Collections.*; import static java.util.stream.Collectors.toList; import static org.openrewrite.Tree.randomId; // ----------------------------------------------------------------------------------------------------------------- // Notes About Provenance Information: // // There are always three markers applied to each source file and there can potentially be up to five provenance markers // total: // // BuildTool - What build tool was used to compile the source file (This will always be Maven) // JavaVersion - What Java version/vendor was used when compiling the source file. // JavaProject - For each maven module/submodule, the same JavaProject will be associated with ALL source files belonging to that module. // // Optional: // // GitProvenance - If the project exists in the context of a git repository, all source files (for all modules) will have the same GitProvenance. // JavaSourceSet - All Java source files and all resource files that exist in src/main or src/test will have a JavaSourceSet marker assigned to them. // ----------------------------------------------------------------------------------------------------------------- public class MavenMojoProjectParser { private static final String MVN_JVM_CONFIG = ".mvn/jvm.config"; private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config"; private static final String MAVEN_COMPILER_PLUGIN = "org.apache.maven.plugins:maven-compiler-plugin"; @Nullable public static MavenPomCache POM_CACHE; private final Log logger; private final AtomicBoolean firstWarningLogged = new AtomicBoolean(false); private final Path baseDir; private final boolean pomCacheEnabled; @Nullable private final String pomCacheDirectory; private final boolean skipMavenParsing; private final BuildTool buildTool; private final Collection exclusions; private final Collection plainTextMasks; private final int sizeThresholdMb; private final MavenSession mavenSession; private final SettingsDecrypter settingsDecrypter; private final boolean runPerSubmodule; private final boolean parseAdditionalResources; @SuppressWarnings("BooleanParameter") public MavenMojoProjectParser(Log logger, Path baseDir, boolean pomCacheEnabled, @Nullable String pomCacheDirectory, RuntimeInformation runtime, boolean skipMavenParsing, Collection exclusions, Collection plainTextMasks, int sizeThresholdMb, MavenSession session, SettingsDecrypter settingsDecrypter, boolean runPerSubmodule, boolean parseAdditionalResources) { this.logger = logger; this.baseDir = baseDir; this.pomCacheEnabled = pomCacheEnabled; this.pomCacheDirectory = pomCacheDirectory; this.skipMavenParsing = skipMavenParsing; this.buildTool = new BuildTool(randomId(), BuildTool.Type.Maven, runtime.getMavenVersion()); this.exclusions = exclusions; this.plainTextMasks = plainTextMasks; this.sizeThresholdMb = sizeThresholdMb; this.mavenSession = session; this.settingsDecrypter = settingsDecrypter; this.runPerSubmodule = runPerSubmodule; this.parseAdditionalResources = parseAdditionalResources; } public Stream listSourceFiles(MavenProject mavenProject, List styles, ExecutionContext ctx) throws DependencyResolutionRequiredException, MojoExecutionException, MojoFailureException { if (runPerSubmodule) { //If running per submodule, parse the source files for only the current project. List projectProvenance = generateProvenance(mavenProject); Xml.Document maven = parseMaven(mavenProject, projectProvenance, ctx); return listSourceFiles(mavenProject, maven, projectProvenance, styles, ctx); } else { //If running across all projects, iterate and parse source files from each project Map> projectProvenances = mavenSession.getProjects().stream() .collect(Collectors.toMap(Function.identity(), this::generateProvenance)); Map projectMap = parseMaven(mavenSession.getProjects(), projectProvenances, ctx); return mavenSession.getProjects().stream() .flatMap(project -> { List projectProvenance = projectProvenances.get(project); try { return listSourceFiles(project, projectMap.get(project), projectProvenance, styles, ctx); } catch (DependencyResolutionRequiredException | MojoExecutionException e) { throw sneakyThrow(e); } }); } } public Stream listSourceFiles(MavenProject mavenProject, Xml.@Nullable Document maven, List projectProvenance, List styles, ExecutionContext ctx) throws DependencyResolutionRequiredException, MojoExecutionException { Stream sourceFiles = Stream.empty(); Set alreadyParsed = new HashSet<>(); if (maven != null) { sourceFiles = Stream.of(maven); alreadyParsed.add(baseDir.resolve(maven.getSourcePath())); } JavaParser.Builder javaParserBuilder = JavaParser.fromJavaVersion() .styles(styles) .logCompilationWarningsAndErrors(false); getCharset(mavenProject).ifPresent(javaParserBuilder::charset); // todo, add styles from autoDetect KotlinParser.Builder kotlinParserBuilder = KotlinParser.builder(); ResourceParser rp = new ResourceParser(baseDir, logger, exclusions, plainTextMasks, sizeThresholdMb, pathsToOtherMavenProjects(mavenProject), javaParserBuilder.clone(), kotlinParserBuilder.clone(), ctx); sourceFiles = Stream.concat(sourceFiles, processMainSources(mavenProject, javaParserBuilder.clone(), kotlinParserBuilder.clone(), rp, projectProvenance, alreadyParsed, ctx)); sourceFiles = Stream.concat(sourceFiles, processTestSources(mavenProject, javaParserBuilder.clone(), kotlinParserBuilder.clone(), rp, projectProvenance, alreadyParsed, ctx)); Collection exclusionMatchers = exclusions.stream() .map(pattern -> baseDir.getFileSystem().getPathMatcher("glob:" + pattern)) .collect(toList()); sourceFiles = sourceFiles.map(sourceFile -> { if (sourceFile instanceof J.CompilationUnit) { for (PathMatcher excluded : exclusionMatchers) { if (excluded.matches(sourceFile.getSourcePath())) { return null; } } } return sourceFile; }).filter(Objects::nonNull); // Collect any additional files that were not parsed above. int sourcesParsedBefore = alreadyParsed.size(); Stream parsedResourceFiles; if (parseAdditionalResources) { parsedResourceFiles = rp.parse(mavenProject.getBasedir().toPath(), alreadyParsed) .map(addProvenance(baseDir, projectProvenance, null)); logDebug(mavenProject, "Parsed " + (alreadyParsed.size() - sourcesParsedBefore) + " additional files found within the project."); } else { // Only parse Maven wrapper related files, such that UpdateMavenWrapper can use the version information. parsedResourceFiles = Stream.of( Paths.get(MVN_JVM_CONFIG), Paths.get(MVN_MAVEN_CONFIG), MavenWrapper.WRAPPER_BATCH_LOCATION, MavenWrapper.WRAPPER_JAR_LOCATION, MavenWrapper.WRAPPER_PROPERTIES_LOCATION, MavenWrapper.WRAPPER_SCRIPT_LOCATION) .flatMap(path -> rp.parse(mavenProject.getBasedir().toPath().resolve(path), alreadyParsed)) .map(addProvenance(baseDir, projectProvenance, null)); logDebug(mavenProject, "Parsed " + (alreadyParsed.size() - sourcesParsedBefore) + " Maven wrapper files found within the project."); } sourceFiles = Stream.concat(sourceFiles, parsedResourceFiles); // log parse errors here at the end, so that we don't log parse errors for files that were excluded return sourceFiles.map(this::logParseErrors); } private static Optional getCharset(MavenProject mavenProject) { String compilerPluginKey = MAVEN_COMPILER_PLUGIN; Plugin plugin = Optional.ofNullable(mavenProject.getPlugin(compilerPluginKey)) .orElseGet(() -> mavenProject.getPluginManagement().getPluginsAsMap().get(compilerPluginKey)); if (plugin != null && plugin.getConfiguration() instanceof Xpp3Dom) { Xpp3Dom encoding = ((Xpp3Dom) plugin.getConfiguration()).getChild("encoding"); if (encoding != null && StringUtils.isNotEmpty(encoding.getValue())) { return Optional.of(Charset.forName(encoding.getValue())); } } Object mavenSourceEncoding = mavenProject.getProperties().get("project.build.sourceEncoding"); if (mavenSourceEncoding != null) { return Optional.of(Charset.forName(mavenSourceEncoding.toString())); } return Optional.empty(); } private SourceFile logParseErrors(SourceFile source) { source.getMarkers().findFirst(ParseExceptionResult.class).ifPresent(e -> { if (firstWarningLogged.compareAndSet(false, true)) { logger.warn("There were problems parsing some source files" + (mavenSession.getRequest().isShowErrors() ? "" : ", run with --errors to see full stack traces")); } logger.warn("There were problems parsing " + source.getSourcePath()); if (mavenSession.getRequest().isShowErrors()) { logger.warn(e.getMessage()); } }); return source; } public List generateProvenance(MavenProject mavenProject) { BuildEnvironment buildEnvironment = BuildEnvironment.build(System::getenv); return Stream.of( buildEnvironment, gitProvenance(baseDir, buildEnvironment), OperatingSystemProvenance.current(), buildTool, new JavaProject(randomId(), mavenProject.getName(), new JavaProject.Publication( mavenProject.getGroupId(), mavenProject.getArtifactId(), mavenProject.getVersion() ))) .filter(Objects::nonNull) .collect(toList()); } private static JavaVersion getSrcMainJavaVersion(MavenProject mavenProject) { String sourceCompatibility = null; String targetCompatibility = null; Plugin compilerPlugin = mavenProject.getPlugin(MAVEN_COMPILER_PLUGIN); if (compilerPlugin != null && compilerPlugin.getConfiguration() instanceof Xpp3Dom) { Xpp3Dom dom = (Xpp3Dom) compilerPlugin.getConfiguration(); Xpp3Dom release = dom.getChild("release"); if (release != null && StringUtils.isNotEmpty(release.getValue()) && !release.getValue().contains("${")) { sourceCompatibility = release.getValue(); targetCompatibility = release.getValue(); } else { Xpp3Dom source = dom.getChild("source"); if (source != null && StringUtils.isNotEmpty(source.getValue()) && !source.getValue().contains("${")) { sourceCompatibility = source.getValue(); } Xpp3Dom target = dom.getChild("target"); if (target != null && StringUtils.isNotEmpty(target.getValue()) && !target.getValue().contains("${")) { targetCompatibility = target.getValue(); } } } if (sourceCompatibility == null || targetCompatibility == null) { String propertiesReleaseCompatibility = (String) mavenProject.getProperties().get("maven.compiler.release"); if (propertiesReleaseCompatibility != null) { sourceCompatibility = propertiesReleaseCompatibility; targetCompatibility = propertiesReleaseCompatibility; } else { String propertiesSourceCompatibility = (String) mavenProject.getProperties().get("maven.compiler.source"); if (sourceCompatibility == null && propertiesSourceCompatibility != null) { sourceCompatibility = propertiesSourceCompatibility; } String propertiesTargetCompatibility = (String) mavenProject.getProperties().get("maven.compiler.target"); if (targetCompatibility == null && propertiesTargetCompatibility != null) { targetCompatibility = propertiesTargetCompatibility; } } } return getJavaVersionMarker(sourceCompatibility, targetCompatibility); } static JavaVersion getSrcTestJavaVersion(MavenProject mavenProject) { String sourceCompatibility = null; String targetCompatibility = null; Plugin compilerPlugin = mavenProject.getPlugin(MAVEN_COMPILER_PLUGIN); if (compilerPlugin != null && compilerPlugin.getConfiguration() instanceof Xpp3Dom) { Xpp3Dom dom = (Xpp3Dom) compilerPlugin.getConfiguration(); Xpp3Dom release = dom.getChild("testRelease"); if (release != null && StringUtils.isNotEmpty(release.getValue()) && !release.getValue().contains("${")) { sourceCompatibility = release.getValue(); targetCompatibility = release.getValue(); } else { Xpp3Dom source = dom.getChild("testSource"); if (source != null && StringUtils.isNotEmpty(source.getValue()) && !source.getValue().contains("${")) { sourceCompatibility = source.getValue(); } Xpp3Dom target = dom.getChild("testTarget"); if (target != null && StringUtils.isNotEmpty(target.getValue()) && !target.getValue().contains("${")) { targetCompatibility = target.getValue(); } } } if (sourceCompatibility == null || targetCompatibility == null) { String propertiesReleaseCompatibility = (String) mavenProject.getProperties().get("maven.compiler.testRelease"); if (propertiesReleaseCompatibility != null) { sourceCompatibility = propertiesReleaseCompatibility; targetCompatibility = propertiesReleaseCompatibility; } else { String propertiesSourceCompatibility = (String) mavenProject.getProperties().get("maven.compiler.testSource"); if (sourceCompatibility == null && propertiesSourceCompatibility != null) { sourceCompatibility = propertiesSourceCompatibility; } String propertiesTargetCompatibility = (String) mavenProject.getProperties().get("maven.compiler.testTarget"); if (targetCompatibility == null && propertiesTargetCompatibility != null) { targetCompatibility = propertiesTargetCompatibility; } } } // Fall back to main source compatibility if test source compatibility is not set, or only partially set. if (sourceCompatibility == null || targetCompatibility == null) { JavaVersion srcMainJavaVersion = getSrcMainJavaVersion(mavenProject); if (sourceCompatibility == null && targetCompatibility == null) { return srcMainJavaVersion; } sourceCompatibility = sourceCompatibility == null ? srcMainJavaVersion.getSourceCompatibility() : sourceCompatibility; targetCompatibility = targetCompatibility == null ? srcMainJavaVersion.getTargetCompatibility() : targetCompatibility; } return getJavaVersionMarker(sourceCompatibility, targetCompatibility); } private static JavaVersion getJavaVersionMarker(@Nullable String sourceCompatibility, @Nullable String targetCompatibility) { String javaRuntimeVersion = System.getProperty("java.specification.version"); String javaVendor = System.getProperty("java.vm.vendor"); if (sourceCompatibility == null) { sourceCompatibility = javaRuntimeVersion; } if (targetCompatibility == null) { targetCompatibility = sourceCompatibility; } return new JavaVersion(randomId(), javaRuntimeVersion, javaVendor, sourceCompatibility, targetCompatibility); } private Stream processMainSources( MavenProject mavenProject, JavaParser.Builder javaParserBuilder, KotlinParser.Builder kotlinParserBuilder, ResourceParser resourceParser, List projectProvenance, Set alreadyParsed, ExecutionContext ctx) throws DependencyResolutionRequiredException, MojoExecutionException { // 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(mavenProject.getBasedir().toPath().resolve(mavenProject.getBuild().getDirectory())); List mainJavaSources = Stream.concat( generatedSourcePaths.stream(), listJavaSources(mavenProject.getBasedir().toPath().resolve(mavenProject.getBuild().getSourceDirectory())).stream() ).collect(toList()); alreadyParsed.addAll(mainJavaSources); // scan Kotlin files String kotlinSourceDir = getKotlinDirectory(mavenProject.getBuild().getSourceDirectory()); List mainKotlinSources = (kotlinSourceDir != null) ? listKotlinSources(mavenProject.getBasedir().toPath().resolve(kotlinSourceDir)) : Collections.emptyList(); alreadyParsed.addAll(mainKotlinSources); logInfo(mavenProject, "Parsing source files"); List dependencies = mavenProject.getCompileClasspathElements().stream() .distinct() .map(Paths::get) .collect(toList()); JavaTypeCache typeCache = new JavaTypeCache(); javaParserBuilder.classpath(dependencies).typeCache(typeCache); kotlinParserBuilder.classpath(dependencies).typeCache(new JavaTypeCache()); Stream parsedJava = Stream.empty(); if (!mainJavaSources.isEmpty()) { parsedJava = javaParserBuilder.build().parse(mainJavaSources, baseDir, ctx); logDebug(mavenProject, "Scanned " + mainJavaSources.size() + " java source files in main scope."); } Stream parsedKotlin = Stream.empty(); if (!mainKotlinSources.isEmpty()) { parsedKotlin = kotlinParserBuilder.build().parse(mainKotlinSources, baseDir, ctx); logDebug(mavenProject, "Scanned " + mainKotlinSources.size() + " kotlin source files in main scope."); } List mainProjectProvenance = new ArrayList<>(projectProvenance); mainProjectProvenance.add(JavaSourceSet.build("main", dependencies)); mainProjectProvenance.add(getSrcMainJavaVersion(mavenProject)); //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.getBuild().getDirectory())); Stream sourceFiles = Stream.concat(parsedJava, parsedKotlin) .filter(s -> !s.getSourcePath().startsWith(buildDirectory)) .map(addProvenance(baseDir, mainProjectProvenance, generatedSourcePaths)); // Any resources parsed from "main/resources" should also have the main source set added to them. int sourcesParsedBefore = alreadyParsed.size(); Stream parsedResourceFiles = resourceParser.parse(mavenProject.getBasedir().toPath().resolve("src/main/resources"), alreadyParsed) .map(addProvenance(baseDir, mainProjectProvenance, null)); logDebug(mavenProject, "Scanned " + (alreadyParsed.size() - sourcesParsedBefore) + " resource files in main scope."); return Stream.concat(sourceFiles, parsedResourceFiles); } private Stream processTestSources( MavenProject mavenProject, JavaParser.Builder javaParserBuilder, KotlinParser.Builder kotlinParserBuilder, ResourceParser resourceParser, List projectProvenance, Set alreadyParsed, ExecutionContext ctx) throws DependencyResolutionRequiredException, MojoExecutionException { List testDependencies = mavenProject.getTestClasspathElements().stream() .distinct() .map(Paths::get) .collect(toList()); JavaTypeCache typeCache = new JavaTypeCache(); javaParserBuilder.classpath(testDependencies).typeCache(typeCache); kotlinParserBuilder.classpath(testDependencies).typeCache(new JavaTypeCache()); List testJavaSources = new ArrayList<>(); for (String p : mavenProject.getTestCompileSourceRoots()) { testJavaSources.addAll(listJavaSources(mavenProject.getBasedir().toPath().resolve(p))); } alreadyParsed.addAll(testJavaSources); // scan Kotlin files String kotlinSourceDir = getKotlinDirectory(mavenProject.getBuild().getTestSourceDirectory()); List testKotlinSources = (kotlinSourceDir != null) ? listKotlinSources(mavenProject.getBasedir().toPath().resolve(kotlinSourceDir)) : Collections.emptyList(); alreadyParsed.addAll(testKotlinSources); Stream parsedJava = Stream.empty(); if (!testJavaSources.isEmpty()) { parsedJava = javaParserBuilder.build().parse(testJavaSources, baseDir, ctx); logDebug(mavenProject, "Scanned " + testJavaSources.size() + " java source files in test scope."); } Stream parsedKotlin = Stream.empty(); if (!testKotlinSources.isEmpty()) { parsedKotlin = kotlinParserBuilder.build().parse(testKotlinSources, baseDir, ctx); logDebug(mavenProject, "Scanned " + testKotlinSources.size() + " kotlin source files in main scope."); } List markers = new ArrayList<>(projectProvenance); markers.add(JavaSourceSet.build("test", testDependencies)); markers.add(getSrcTestJavaVersion(mavenProject)); // Any resources parsed from "test/resources" should also have the test source set added to them. int sourcesParsedBefore = alreadyParsed.size(); Stream parsedResourceFiles = resourceParser.parse(mavenProject.getBasedir().toPath().resolve("src/test/resources"), alreadyParsed); logDebug(mavenProject, "Scanned " + (alreadyParsed.size() - sourcesParsedBefore) + " resource files in test scope."); return Stream.concat(Stream.concat(parsedJava, parsedKotlin), parsedResourceFiles) .map(addProvenance(baseDir, markers, null)); } private @Nullable String getKotlinDirectory(@Nullable String sourceDirectory) { if (sourceDirectory == null) { return null; } File directory = new File(sourceDirectory); File parentDirectory = directory.getParentFile(); if (parentDirectory != null) { File[] subdirectories = parentDirectory.listFiles(File::isDirectory); if (subdirectories != null) { for (File subdirectory : subdirectories) { if (subdirectory.getName().equals("kotlin")) { return subdirectory.getAbsolutePath(); } } } } return null; } public Xml.@Nullable Document parseMaven(MavenProject mavenProject, List projectProvenance, ExecutionContext ctx) throws MojoFailureException { return parseMaven(singletonList(mavenProject), singletonMap(mavenProject, projectProvenance), ctx).get(mavenProject); } public Map parseMaven(List mavenProjects, Map> projectProvenances, ExecutionContext ctx) throws MojoFailureException { if (skipMavenParsing) { logger.info("Skipping Maven parsing..."); return emptyMap(); } MavenSettings settings = buildSettings(); MavenExecutionContextView mavenExecutionContext = MavenExecutionContextView.view(ctx); mavenExecutionContext.setMavenSettings(settings); mavenExecutionContext.setResolutionListener(new MavenLoggingResolutionEventListener(logger)); // The default pom cache is enabled as a two-layer cache L1 == in-memory and L2 == RocksDb // If the flag is set to false, only the default, in-memory cache is used. MavenPomCache pomCache = pomCacheEnabled ? getPomCache(pomCacheDirectory, logger) : mavenExecutionContext.getPomCache(); mavenExecutionContext.setPomCache(pomCache); MavenProject topLevelProject = mavenSession.getTopLevelProject(); logInfo(topLevelProject, "Resolving Poms..."); Set allPoms = new LinkedHashSet<>(); mavenProjects.forEach(p -> collectPoms(p, allPoms, mavenExecutionContext)); for (MavenProject mavenProject : mavenProjects) { mavenSession.getProjectDependencyGraph().getUpstreamProjects(mavenProject, true).forEach(p -> collectPoms(p, allPoms, mavenExecutionContext)); } MavenParser.Builder mavenParserBuilder = MavenParser.builder().mavenConfig(baseDir.resolve(MVN_MAVEN_CONFIG)); List activeProfiles = topLevelProject.getActiveProfiles().stream().map(Profile::getId).collect(toList()); if (!activeProfiles.isEmpty()) { mavenParserBuilder.activeProfiles(activeProfiles.toArray(new String[0])); } List mavens = mavenParserBuilder.build() .parse(allPoms, baseDir, ctx) .collect(toList()); if (logger.isDebugEnabled()) { logDebug(topLevelProject, "Base directory : '" + baseDir + "'"); if (allPoms.isEmpty()) { logDebug(topLevelProject, "There were no collected pom paths."); } else { for (Path path : allPoms) { logDebug(topLevelProject, " Collected Maven POM : '" + path + "'"); } } if (mavens.isEmpty()) { logDebug(topLevelProject, "There were no parsed maven source files."); } else { for (SourceFile source : mavens) { logDebug(topLevelProject, " Maven Source : '" + baseDir.resolve(source.getSourcePath()) + "'"); } } } Map projectsByPath = mavenProjects.stream().collect(Collectors.toMap(MavenMojoProjectParser::pomPath, Function.identity())); Map projectMap = new HashMap<>(); for (SourceFile document : mavens) { Path path = baseDir.resolve(document.getSourcePath()); MavenProject mavenProject = projectsByPath.get(path); if (mavenProject != null) { Optional parseExceptionResult = document.getMarkers().findFirst(ParseExceptionResult.class); if (parseExceptionResult.isPresent()) { throw new MojoFailureException( mavenProject, "Failed to parse or resolve the Maven POM file or one of its dependencies; " + "We can not reliably continue without this information.", parseExceptionResult.get().getMessage()); } projectMap.put(mavenProject, (Xml.Document) document); } } for (MavenProject mavenProject : mavenProjects) { if (projectMap.get(mavenProject) == null) { logError(mavenProject, "Parse resulted in no Maven source files. Maven Project File '" + mavenProject.getFile().toPath() + "'"); return emptyMap(); } } // assign provenance markers for (MavenProject mavenProject : mavenProjects) { Xml.Document document = projectMap.get(mavenProject); List provenance = projectProvenances.getOrDefault(mavenProject, emptyList()); Markers markers = document.getMarkers(); for (Marker marker : provenance) { markers = markers.addIfAbsent(marker); } projectMap.put(mavenProject, document.withMarkers(markers)); } return projectMap; } /** * Recursively navigate the maven project to collect any poms that are local (on disk) * * @param project A maven project to examine for any children/parent poms. * @param paths A list of paths to poms that have been collected so far. * @param ctx The execution context for the current project. */ private void collectPoms(MavenProject project, Set paths, MavenExecutionContextView ctx) { if (!paths.add(pomPath(project))) { return; } ResolvedGroupArtifactVersion gav = createResolvedGAV(project, ctx); ctx.getPomCache().putPom(gav, createPom(project)); // children if (project.getCollectedProjects() != null) { for (MavenProject child : project.getCollectedProjects()) { Path path = pomPath(child); if (!paths.contains(path)) { collectPoms(child, paths, ctx); } } } MavenProject parent = project.getParent(); while (parent != null && parent.getFile() != null) { Path path = pomPath(parent); if (!paths.contains(path)) { collectPoms(parent, paths, ctx); } parent = parent.getParent(); } } private static Path pomPath(MavenProject mavenProject) { Path pomPath = mavenProject.getFile().toPath(); if (pomPath.endsWith(".flattened-pom.xml") ||// org.codehaus.mojo:flatten-maven-plugin pomPath.endsWith("dependency-reduced-pom.xml") || // org.apache.maven.plugins:maven-shade-plugin pomPath.endsWith(".ci-friendly-pom.xml") || // com.outbrain.swinfra:ci-friendly-flatten-maven-plugin pomPath.endsWith(".tycho-consumer-pom.xml")) { // org.eclipse.tycho:tycho-packaging-plugin:update-consumer-pom Path normalPom = mavenProject.getBasedir().toPath().resolve("pom.xml"); // check for the existence of the POM, since Tycho can work pom-less if (Files.isReadable(normalPom) && Files.isRegularFile(normalPom)) { return normalPom; } } return pomPath; } private static MavenPomCache getPomCache(@Nullable String pomCacheDirectory, Log logger) { if (POM_CACHE == null) { POM_CACHE = new MavenPomCacheBuilder(logger).build(pomCacheDirectory); } if (POM_CACHE == null) { POM_CACHE = new InMemoryMavenPomCache(); } return POM_CACHE; } public MavenSettings buildSettings() { MavenExecutionRequest mer = mavenSession.getRequest(); MavenSettings.Profiles profiles = new MavenSettings.Profiles(); profiles.setProfiles( mer.getProfiles().stream().map(p -> new MavenSettings.Profile( p.getId(), p.getActivation() == null ? null : new ProfileActivation( p.getActivation().isActiveByDefault(), p.getActivation().getJdk(), p.getActivation().getProperty() == null ? null : new ProfileActivation.Property( p.getActivation().getProperty().getName(), p.getActivation().getProperty().getValue() ) ), buildRawRepositories(p.getRepositories()) ) ).collect(toList())); MavenSettings.ActiveProfiles activeProfiles = new MavenSettings.ActiveProfiles(); activeProfiles.setActiveProfiles(mer.getActiveProfiles()); MavenSettings.Mirrors mirrors = new MavenSettings.Mirrors(); mirrors.setMirrors( mer.getMirrors().stream().map(m -> new MavenSettings.Mirror( m.getId(), m.getUrl(), m.getMirrorOf(), null, null )).collect(toList()) ); MavenSettings.Servers servers = new MavenSettings.Servers(); servers.setServers(mer.getServers().stream().map(s -> { SettingsDecryptionRequest decryptionRequest = new DefaultSettingsDecryptionRequest(s); SettingsDecryptionResult decryptionResult = settingsDecrypter.decrypt(decryptionRequest); return new MavenSettings.Server( s.getId(), s.getUsername(), decryptionResult.getServer().getPassword(), null ); }).collect(toList())); return new MavenSettings(mer.getLocalRepositoryPath().toString(), profiles, activeProfiles, mirrors, servers); } private static @Nullable RawRepositories buildRawRepositories(@Nullable List repositoriesToMap) { if (repositoriesToMap == null) { return null; } RawRepositories rawRepositories = new RawRepositories(); List transformedRepositories = repositoriesToMap.stream().map(r -> new RawRepositories.Repository( r.getId(), r.getUrl(), r.getReleases() == null ? null : new RawRepositories.ArtifactPolicy(Boolean.toString(r.getReleases().isEnabled())), r.getSnapshots() == null ? null : new RawRepositories.ArtifactPolicy(Boolean.toString(r.getSnapshots().isEnabled())) )).collect(toList()); rawRepositories.setRepositories(transformedRepositories); return rawRepositories; } /** * Used to scope `Files.walkFileTree` to the current maven project by skipping the subtrees of other MavenProjects. */ private Set pathsToOtherMavenProjects(MavenProject mavenProject) { return mavenSession.getProjects().stream() .filter(o -> o != mavenProject) .map(o -> o.getBasedir().toPath()) .collect(Collectors.toSet()); } private 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 List listJavaSources(Path sourceDirectory) throws MojoExecutionException { return listSources(sourceDirectory, ".java"); } private static List listKotlinSources(Path sourceDirectory) throws MojoExecutionException { return listSources(sourceDirectory, ".kt"); } private static List listSources(Path sourceDirectory, String extension) throws MojoExecutionException { if (!Files.exists(sourceDirectory)) { return emptyList(); } try { List result = new ArrayList<>(); Files.walkFileTree(sourceDirectory, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { if (file.toString().endsWith(extension)) { result.add(file); } return FileVisitResult.CONTINUE; } }); return result; } catch (IOException e) { throw new MojoExecutionException("Unable to list source files of " + extension, e); } } private static final Map REPO_ROOT_TO_PROVENANCE = new HashMap<>(); private @Nullable GitProvenance gitProvenance(Path baseDir, @Nullable BuildEnvironment buildEnvironment) { try { // Computing git provenance can be expensive for repositories with many commits, ensure we do it only once return REPO_ROOT_TO_PROVENANCE.computeIfAbsent(baseDir, dir -> GitProvenance.fromProjectDirectory(dir, buildEnvironment)); } catch (Exception e) { // Logging at a low level as this is unlikely to happen except in non-git projects, where it is expected logger.debug("Unable to determine git provenance", e); } return null; } private void logError(MavenProject mavenProject, String message) { logger.error("Project [" + mavenProject.getName() + "] " + message); } private void logInfo(MavenProject mavenProject, String message) { logger.info("Project [" + mavenProject.getName() + "] " + message); } private void logDebug(MavenProject mavenProject, String message) { logger.debug("Project [" + mavenProject.getName() + "] " + message); } @SuppressWarnings({"RedundantThrows", "unchecked"}) private static E sneakyThrow(Throwable e) throws E { return (E) e; } private static ResolvedGroupArtifactVersion createResolvedGAV(MavenProject project, MavenExecutionContextView ctx) { return new ResolvedGroupArtifactVersion( ctx.getLocalRepository().getUri(), project.getGroupId(), project.getArtifactId(), project.getVersion(), project.getVersion().endsWith("-SNAPSHOT") ? null : project.getVersion() ); } private static @Nullable Pom createPom(MavenProject project) { Path pomPath = project.getFile().toPath(); try (InputStream is = Files.newInputStream(pomPath)) { RawPom rawPom = RawPom.parse(is, null); return rawPom.toPom(project.getBasedir().toPath().relativize(pomPath), null); } catch (IOException e) { return null; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy