From 18b343b8ddfd3d243afc6a9a4a8c20da5b9a83b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 16 Feb 2024 07:16:08 +0100 Subject: [PATCH] Consider properties defined in .mvn/maven.config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently lemminx does not consider properties that are defined in the .mvn/maven.config and therefore gives errors or otherwhise wrong results when parsing a project. This adds support for detecting and reading these properties from the .mvn/maven.config Fix https://github.com/eclipse/lemminx-maven/issues/549 Signed-off-by: Christoph Läubrich --- .../maven/project/MavenProjectCache.java | 107 +++++++++++++++--- 1 file changed, 94 insertions(+), 13 deletions(-) diff --git a/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/project/MavenProjectCache.java b/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/project/MavenProjectCache.java index 4fc8a792..3c50161d 100644 --- a/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/project/MavenProjectCache.java +++ b/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/project/MavenProjectCache.java @@ -15,6 +15,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -23,6 +27,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Properties; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -32,11 +37,14 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.ParseException; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; import org.apache.maven.artifact.repository.MavenArtifactRepository; import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; -import org.apache.maven.artifact.repository.metadata.Plugin; +import org.apache.maven.cli.CLIManager; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Build; @@ -69,6 +77,9 @@ public class MavenProjectCache { + private static final String MVN_FOLDER = ".mvn"; + private static final String MAVEN_CONFIG = "maven.config"; + private static final Logger LOGGER = Logger.getLogger(MavenProjectCache.class.getName()); private MavenLemminxExtension plugin; private final Map projectCache; @@ -162,8 +173,9 @@ public LoadedMavenProject build(FileModelSource source, CancelChecker cancelChec MavenProject project = null; File file = source.getFile(); try { - ProjectBuildingResult buildResult = projectBuilder.build( - source, newProjectBuildingRequest()); + ProjectBuildingRequest request = newProjectBuildingRequest(file); + + ProjectBuildingResult buildResult = projectBuilder.build(source, request); cancelChecker.checkCanceled(); project = buildResult.getProject(); problems.addAll(buildResult.getProblems()); @@ -337,7 +349,7 @@ public Optional getSnapshotProject(File file) { } try { - return Optional.of(projectBuilder.build(file, newProjectBuildingRequest()).getProject()); + return Optional.of(projectBuilder.build(file, newProjectBuildingRequest(file)).getProject()); } catch (ProjectBuildingException e) { List result = e.getResults(); if (result != null && result.size() == 1 && result.get(0).getProject() != null) { @@ -364,7 +376,7 @@ public Optional getSnapshotProject(File file) { */ public MavenProject getSnapshotProject(DOMDocument document, String profileId, boolean resolve) { // it would be nice to directly rebuild from Model instead of re-parsing text - ProjectBuildingRequest request = newProjectBuildingRequest(resolve); + ProjectBuildingRequest request = newProjectBuildingRequest(resolve, getFileForDocument(document)); if (profileId != null) { request.setActiveProfileIds(List.of(profileId)); } @@ -386,24 +398,28 @@ public MavenProject getSnapshotProject(DOMDocument document, String profileId, b } /** - * Creates a new default Maven Project Building request (with dependency - * resolve enabled) + * Creates a new default Maven Project Building request (with dependency resolve + * enabled) + * + * @param projectFile the project file or base directory of the building request * * @return A ProjectBuildingRequest object */ - public ProjectBuildingRequest newProjectBuildingRequest() { - return newProjectBuildingRequest(true); + public ProjectBuildingRequest newProjectBuildingRequest(File projectFile) { + return newProjectBuildingRequest(true, projectFile); } /** * Creates a new default Maven Project Building request * - * @param resolveDependencies a boolean indicating if the dependency - * resolve is to be enabled on the request. + * @param resolveDependencies a boolean indicating if the dependency resolve is + * to be enabled on the request. + * @param projectFile the project file or base directory of the building + * request * * @return A ProjectBuildingRequest object */ - public ProjectBuildingRequest newProjectBuildingRequest(boolean resolveDependencies) { + public ProjectBuildingRequest newProjectBuildingRequest(boolean resolveDependencies, File projectFile) { ProjectBuildingRequest request = new DefaultProjectBuildingRequest(); MavenExecutionRequest mavenRequest = mavenSession.getRequest(); request.setSystemProperties(mavenRequest.getSystemProperties()); @@ -415,7 +431,38 @@ public ProjectBuildingRequest newProjectBuildingRequest(boolean resolveDependenc request.setResolveDependencies(resolveDependencies); // See: https://issues.apache.org/jira/browse/MRESOLVER-374 - request.getUserProperties().setProperty("aether.syncContext.named.factory", "noop"); + Properties userProperties = request.getUserProperties(); + userProperties.setProperty("aether.syncContext.named.factory", "noop"); + File multiModuleProjectDirectory = computeMultiModuleProjectDirectory(projectFile); + if (multiModuleProjectDirectory != null) { + File mavenConfig = new File(multiModuleProjectDirectory, MAVEN_CONFIG); + if (mavenConfig.isFile()) { + try { + CLIManager manager = new CLIManager(); + String[] args; + try (Stream lines = Files.lines(mavenConfig.toPath(), Charset.defaultCharset())) { + args = lines.filter(arg -> !arg.isEmpty()).toArray(String[]::new); + } + CommandLine commandline = manager.parse(args); + if (commandline.hasOption(CLIManager.SET_USER_PROPERTY)) { + String[] configUserProperties = commandline.getOptionValues(CLIManager.SET_USER_PROPERTY); + if (configUserProperties != null) { + for (String property : configUserProperties) { + int index = property.indexOf('='); + if (index <= 0) { + userProperties.setProperty(property.trim(), "true"); + } else { + userProperties.setProperty(property.substring(0, index).trim(), + property.substring(index + 1).trim()); + } + } + } + } + } catch (IOException | ParseException e) { + // TODO how to best propagate this?!? + } + } + } return request; } } @@ -573,4 +620,38 @@ public CompletableFuture getLoadedMavenProject(String uriStr } return provider.getLoadedMavenProject(); } + + /** + * @param file a base file or directory, may be null + * @return the value for `maven.multiModuleProjectDirectory` as defined in Maven + * launcher + */ + public static File computeMultiModuleProjectDirectory(File file) { + if (file == null) { + return null; + } + final File basedir = file.isDirectory() ? file : file.getParentFile(); + for (File root = basedir; root != null; root = root.getParentFile()) { + if (new File(root, MVN_FOLDER).isDirectory()) { + return root; + } + } + return null; + } + + private static File getFileForDocument(DOMDocument document) { + String documentURI = document.getDocumentURI(); + if (documentURI == null) { + return null; + } + if (documentURI.toLowerCase().startsWith("file:")) { + try { + return new File(new URI(documentURI)); + } catch (URISyntaxException e) { + return null; + } + } + return new File(documentURI); + } + }