You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2023/09/22 07:25:16 UTC
[maven] branch master updated: [MNG-7836] Support alternative syntaxes for POMs (#1197)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new f24266eb64 [MNG-7836] Support alternative syntaxes for POMs (#1197)
f24266eb64 is described below
commit f24266eb64fbb4f23655fbc7d76dbf4be61045c2
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Sep 22 09:25:10 2023 +0200
[MNG-7836] Support alternative syntaxes for POMs (#1197)
The IT associated with this PR is using the Maven model to generate a hocon POM parser. This requires the maven-api-model module to attach the POM as an artifact, and the maven.yml change so that the model is present in the local repository.
---
.github/workflows/maven.yml | 16 ++---
api/maven-api-model/pom.xml | 20 ++++++
.../java/org/apache/maven/api/spi/ModelParser.java | 71 ++++++++++++++++++++++
.../apache/maven/api/spi/ModelParserException.java | 66 ++++++++++++++++++++
.../model/building/DefaultModelBuilderFactory.java | 7 ++-
.../model/building/DefaultModelProcessor.java | 59 ++++++++++++++++--
6 files changed, 226 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index ce78f7b5a7..26492a05c1 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -106,12 +106,6 @@ jobs:
ref: ${{ env.REPO_BRANCH }}
persist-credentials: false
- - name: Download built Maven
- uses: actions/download-artifact@v3
- with:
- name: built-maven
- path: built-maven/
-
- name: Set up JDK
uses: actions/setup-java@v3
with:
@@ -119,6 +113,14 @@ jobs:
distribution: 'temurin'
# cache: 'maven' - don't use cache for integration tests
+ - uses: actions/checkout@v3
+ with:
+ path: maven/
+ persist-credentials: false
+
+ - name: Build Maven
+ run: mvn install -e -B -V -DdistributionFileName=apache-maven -DskipTests -f maven/pom.xml
+
- name: Running integration tests
shell: bash
- run: mvn install -e -B -V -Prun-its,embedded -DmavenDistro="$GITHUB_WORKSPACE/built-maven/apache-maven-bin.zip" -f maven-integration-testing/pom.xml
+ run: mvn install -e -B -V -Prun-its,embedded -DmavenDistro="$GITHUB_WORKSPACE/maven/apache-maven/target/apache-maven-bin.zip" -f maven-integration-testing/pom.xml
diff --git a/api/maven-api-model/pom.xml b/api/maven-api-model/pom.xml
index 8c989d2988..47e5e2758a 100644
--- a/api/maven-api-model/pom.xml
+++ b/api/maven-api-model/pom.xml
@@ -76,6 +76,26 @@ under the License.
</excludes>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>3.4.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${basedir}/src/main/mdo/maven.mdo</file>
+ <type>mdo</type>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java
new file mode 100644
index 0000000000..68d8097ba1
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.maven.api.spi;
+
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.services.Source;
+
+/**
+ * The {@code ModelParser} interface is used to locate and read {@link Model}s from the file system.
+ * This allows plugging in additional syntaxes for the main model read by Maven when building a project.
+ */
+@Experimental
+public interface ModelParser {
+
+ /**
+ * Locates the pom in the given directory.
+ *
+ * @param dir the directory to locate the pom for, never {@code null}
+ * @return a {@code Source} pointing to the located pom or an empty {@code Optional} if none was found by this parser
+ */
+ @Nonnull
+ Optional<Source> locate(@Nonnull Path dir);
+
+ /**
+ * Parse the model obtained previously by a previous call to {@link #locate(Path)}.
+ *
+ * @param source the source to parse, never {@code null}
+ * @param options possible parsing options, may be {@code null}
+ * @return the parsed {@link Model}, never {@code null}
+ * @throws ModelParserException if the model cannot be parsed
+ */
+ @Nonnull
+ Model parse(@Nonnull Source source, @Nullable Map<String, ?> options) throws ModelParserException;
+
+ /**
+ * Locate and parse the model in the specified directory.
+ * This is equivalent to {@code locate(dir).map(s -> parse(s, options))}.
+ *
+ * @param dir the directory to locate the pom for, never {@code null}
+ * @param options possible parsing options, may be {@code null}
+ * @return an optional parsed {@link Model} or {@code null} if none could be found
+ * @throws ModelParserException if the located model cannot be parsed
+ */
+ default Optional<Model> locateAndParse(@Nonnull Path dir, @Nullable Map<String, ?> options)
+ throws ModelParserException {
+ return locate(dir).map(s -> parse(s, options));
+ }
+}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java
new file mode 100644
index 0000000000..4520f0650b
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.maven.api.spi;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.services.MavenException;
+
+@Experimental
+public class ModelParserException extends MavenException {
+
+ /**
+ * The one-based index of the line containing the error.
+ */
+ private final int lineNumber;
+
+ /**
+ * The one-based index of the column containing the error.
+ */
+ private final int columnNumber;
+
+ public ModelParserException() {
+ this(null, null);
+ }
+
+ public ModelParserException(String message) {
+ this(message, null);
+ }
+
+ public ModelParserException(String message, Throwable cause) {
+ this(message, -1, -1, cause);
+ }
+
+ public ModelParserException(String message, int lineNumber, int columnNumber, Throwable cause) {
+ super(message, cause);
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ }
+
+ public ModelParserException(Throwable cause) {
+ this(null, cause);
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+}
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
index a4bdaaab57..dc474cef22 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
@@ -20,6 +20,7 @@ package org.apache.maven.model.building;
import java.util.Arrays;
+import org.apache.maven.api.spi.ModelParser;
import org.apache.maven.model.Model;
import org.apache.maven.model.composition.DefaultDependencyManagementImporter;
import org.apache.maven.model.composition.DependencyManagementImporter;
@@ -215,7 +216,11 @@ public class DefaultModelBuilderFactory {
}
protected ModelProcessor newModelProcessor() {
- return new DefaultModelProcessor(newModelLocator(), newModelReader());
+ return new DefaultModelProcessor(Arrays.asList(newModelParsers()), newModelLocator(), newModelReader());
+ }
+
+ protected ModelParser[] newModelParsers() {
+ return new ModelParser[0];
}
protected ModelLocator newModelLocator() {
diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
index 0b4935318a..b3c459c0d0 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
@@ -27,10 +27,18 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.spi.ModelParser;
+import org.apache.maven.api.spi.ModelParserException;
import org.apache.maven.building.Source;
+import org.apache.maven.model.io.ModelParseException;
import org.apache.maven.model.io.ModelReader;
import org.apache.maven.model.locator.ModelLocator;
import org.eclipse.sisu.Typed;
@@ -65,11 +73,14 @@ import org.eclipse.sisu.Typed;
@Typed(ModelProcessor.class)
public class DefaultModelProcessor implements ModelProcessor {
+ private final Collection<ModelParser> modelParsers;
private final ModelLocator modelLocator;
private final ModelReader modelReader;
@Inject
- public DefaultModelProcessor(ModelLocator modelLocator, ModelReader modelReader) {
+ public DefaultModelProcessor(
+ Collection<ModelParser> modelParsers, ModelLocator modelLocator, ModelReader modelReader) {
+ this.modelParsers = modelParsers;
this.modelLocator = modelLocator;
this.modelReader = modelReader;
}
@@ -81,7 +92,15 @@ public class DefaultModelProcessor implements ModelProcessor {
public Path locatePom(Path projectDirectory) {
// Note that the ModelProcessor#locatePom never returns null
- Path pom = modelLocator.locatePom(projectDirectory.toFile()).toPath();
+ // while the ModelParser#locatePom needs to return an existing path!
+ Path pom = modelParsers.stream()
+ .map(m -> m.locate(projectDirectory)
+ .map(org.apache.maven.api.services.Source::getPath)
+ .orElse(null))
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElseGet(
+ () -> modelLocator.locatePom(projectDirectory.toFile()).toPath());
if (!pom.equals(projectDirectory) && !pom.getParent().equals(projectDirectory)) {
throw new IllegalArgumentException("The POM found does not belong to the given directory: " + pom);
}
@@ -95,8 +114,15 @@ public class DefaultModelProcessor implements ModelProcessor {
public Path locateExistingPom(Path projectDirectory) {
// Note that the ModelProcessor#locatePom never returns null
- File f = modelLocator.locateExistingPom(projectDirectory.toFile());
- Path pom = f != null ? f.toPath() : null;
+ // while the ModelParser#locatePom needs to return an existing path!
+ Path pom = modelParsers.stream()
+ .map(m -> m.locate(projectDirectory).map(s -> s.getPath()).orElse(null))
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElseGet(() -> {
+ File f = modelLocator.locateExistingPom(projectDirectory.toFile());
+ return f != null ? f.toPath() : null;
+ });
if (pom != null && !pom.equals(projectDirectory) && !pom.getParent().equals(projectDirectory)) {
throw new IllegalArgumentException("The POM found does not belong to the given directory: " + pom);
}
@@ -109,7 +135,28 @@ public class DefaultModelProcessor implements ModelProcessor {
if (pomFile == null && source instanceof org.apache.maven.building.FileSource) {
pomFile = ((org.apache.maven.building.FileSource) source).getFile().toPath();
}
- return readXmlModel(pomFile, input, reader, options);
+ if (pomFile != null) {
+ Path projectDirectory = pomFile.getParent();
+ List<ModelParserException> exceptions = new ArrayList<>();
+ for (ModelParser parser : modelParsers) {
+ try {
+ Optional<Model> model = parser.locateAndParse(projectDirectory, options);
+ if (model.isPresent()) {
+ return model.get().withPomFile(pomFile);
+ }
+ } catch (ModelParserException e) {
+ exceptions.add(e);
+ }
+ }
+ try {
+ return readXmlModel(pomFile, null, null, options);
+ } catch (IOException e) {
+ exceptions.forEach(e::addSuppressed);
+ throw e;
+ }
+ } else {
+ return readXmlModel(pomFile, input, reader, options);
+ }
}
private org.apache.maven.api.model.Model readXmlModel(
@@ -137,6 +184,8 @@ public class DefaultModelProcessor implements ModelProcessor {
try (InputStream in = input) {
org.apache.maven.api.model.Model model = read(null, in, null, options);
return new org.apache.maven.model.Model(model);
+ } catch (ModelParserException e) {
+ throw new ModelParseException("Unable to read model: " + e, e.getLineNumber(), e.getColumnNumber(), e);
}
}