You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rm...@apache.org on 2025/09/17 16:14:04 UTC
(maven-dependency-plugin) branch dev/1522 created (now bd96d0a9)
This is an automated email from the ASF dual-hosted git repository.
rmannibucau pushed a change to branch dev/1522
in repository https://gitbox.apache.org/repos/asf/maven-dependency-plugin.git
at bd96d0a9 Fixes #1522, add render-dependencies mojo
This branch includes the following new commits:
new bd96d0a9 Fixes #1522, add render-dependencies mojo
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(maven-dependency-plugin) 01/01: Fixes #1522, add render-dependencies mojo
Posted by rm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rmannibucau pushed a commit to branch dev/1522
in repository https://gitbox.apache.org/repos/asf/maven-dependency-plugin.git
commit bd96d0a933bfcf7c4528d514b8989d40c374d2eb
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Wed Sep 17 18:13:49 2025 +0200
Fixes #1522, add render-dependencies mojo
---
.../fromDependencies/RenderDependenciesMojo.java | 240 +++++++++++++++++++++
.../TestRenderDependenciesMojo.java | 104 +++++++++
.../render-dependencies-test/plugin-config.xml | 37 ++++
3 files changed, 381 insertions(+)
diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/RenderDependenciesMojo.java b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/RenderDependenciesMojo.java
new file mode 100644
index 00000000..63ff132a
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/RenderDependenciesMojo.java
@@ -0,0 +1,240 @@
+/*
+ * 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.plugins.dependency.fromDependencies;
+
+import javax.inject.Inject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.plugins.dependency.utils.ResolverUtil;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.tools.generic.CollectionTool;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+import static java.util.Optional.ofNullable;
+
+/**
+ * This goal renders dependencies based on a velocity template.
+ *
+ * @since 3.8.2
+ */
+@Mojo(
+ name = "render-dependencies",
+ requiresDependencyResolution = ResolutionScope.TEST,
+ defaultPhase = LifecyclePhase.GENERATE_SOURCES,
+ threadSafe = true)
+public class RenderDependenciesMojo extends AbstractDependencyFilterMojo {
+ @Parameter(property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}")
+ private String outputEncoding;
+
+ /**
+ * The file to write the rendered template string. If undefined, it just prints the classpath as [INFO].
+ */
+ @Parameter(property = "mdep.outputFile")
+ private File outputFile;
+
+ /**
+ * If not null or empty it will attach the artifact with this classifier.
+ */
+ @Parameter(property = "mdep.classifier", defaultValue = "template")
+ private String classifier;
+
+ /**
+ * velocity template to use to render the output file.
+ */
+ @Parameter(property = "mdep.template", defaultValue = "<set the template>")
+ private String template;
+
+ private final MavenProjectHelper projectHelper;
+
+ @Inject
+ protected RenderDependenciesMojo(
+ MavenSession session,
+ BuildContext buildContext,
+ MavenProject project,
+ ResolverUtil resolverUtil,
+ ProjectBuilder projectBuilder,
+ ArtifactHandlerManager artifactHandlerManager,
+ MavenProjectHelper projectHelper) {
+ super(session, buildContext, project, resolverUtil, projectBuilder, artifactHandlerManager);
+ this.projectHelper = projectHelper;
+ }
+
+ /**
+ * Main entry into mojo.
+ *
+ * @throws MojoExecutionException with a message if an error occurs
+ */
+ @Override
+ protected void doExecute() throws MojoExecutionException {
+ // sort them to ease template work and ensure it is deterministic
+ final List<Artifact> artifacts =
+ ofNullable(getResolvedDependencies(true)).orElseGet(Collections::emptySet).stream()
+ .sorted(Comparator.comparing(Artifact::getGroupId)
+ .thenComparing(Artifact::getArtifactId)
+ .thenComparing(Artifact::getBaseVersion)
+ .thenComparing(orEmpty(Artifact::getClassifier))
+ .thenComparing(orEmpty(Artifact::getType)))
+ .collect(Collectors.toList());
+
+ if (artifacts.isEmpty()) {
+ getLog().warn("No dependencies found.");
+ }
+
+ final String rendered = render(artifacts);
+
+ if (outputFile == null) {
+ getLog().info(rendered);
+ } else {
+ store(rendered, outputFile);
+ }
+ if (classifier != null && !classifier.isEmpty()) {
+ attachFile(rendered);
+ }
+ }
+
+ /**
+ * Do render the template.
+ * @param artifacts input.
+ * @return the template rendered.
+ */
+ private String render(final List<Artifact> artifacts) {
+ final Properties props = new Properties();
+ props.setProperty("runtime.references.strict", "true");
+
+ final VelocityEngine ve = new VelocityEngine(props);
+ ve.init();
+
+ final VelocityContext context = new VelocityContext();
+ context.put("artifacts", artifacts);
+ context.put("sorter", new CollectionTool());
+
+ // Merge template + context
+ final StringWriter writer = new StringWriter();
+ try {
+ ve.evaluate(context, writer, "tpl-" + Math.abs(hashCode()), template);
+ } finally {
+ try {
+ writer.close();
+ } catch (final IOException e) {
+ // no-op, not possible
+ }
+ }
+
+ return writer.toString();
+ }
+
+ /**
+ * Trivial null protection impl for comparing callback.
+ * @param getter nominal getter.
+ * @return a comparer of getter defaulting on empty if getter value is null.
+ */
+ private Comparator<Artifact> orEmpty(final Function<Artifact, String> getter) {
+ return Comparator.comparing(a -> ofNullable(getter.apply(a)).orElse(""));
+ }
+
+ /**
+ * @param content the rendered template
+ * @throws MojoExecutionException in case of an error
+ */
+ protected void attachFile(final String content) throws MojoExecutionException {
+ final File attachedFile;
+ if (outputFile == null) {
+ attachedFile = new File(getProject().getBuild().getDirectory(), classifier);
+ store(content, attachedFile);
+ } else { // already written
+ attachedFile = outputFile;
+ }
+ store(content, attachedFile);
+ projectHelper.attachArtifact(getProject(), attachedFile, classifier);
+ }
+
+ /**
+ * Stores the specified string into that file.
+ *
+ * @param content the string to write into the file
+ */
+ private void store(final String content, final File out) throws MojoExecutionException {
+ // make sure the parent path exists.
+ final Path parent = out.toPath().getParent();
+ if (parent != null) {
+ try {
+ Files.createDirectories(parent);
+ } catch (final IOException e) {
+ throw new MojoExecutionException(e);
+ }
+ }
+
+ final String encoding = Objects.toString(outputEncoding, StandardCharsets.UTF_8.name());
+ try (Writer w = Files.newBufferedWriter(out.toPath(), Charset.forName(encoding))) {
+ w.write(content);
+ getLog().info("Wrote file '" + out + "'.");
+ } catch (final IOException ex) {
+ throw new MojoExecutionException("Error while writing to file '" + out, ex);
+ }
+ }
+
+ @Override
+ protected ArtifactsFilter getMarkedArtifactFilter() {
+ return null;
+ }
+
+ public void setOutputEncoding(final String outputEncoding) {
+ this.outputEncoding = outputEncoding;
+ }
+
+ public void setOutputFile(final File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ public void setClassifier(final String classifier) {
+ this.classifier = classifier;
+ }
+
+ public void setTemplate(final String template) {
+ this.template = template;
+ }
+}
diff --git a/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestRenderDependenciesMojo.java b/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestRenderDependenciesMojo.java
new file mode 100644
index 00000000..9db2d0fb
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestRenderDependenciesMojo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.plugins.dependency.fromDependencies;
+
+import java.io.File;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugins.dependency.AbstractDependencyMojoTestCase;
+import org.apache.maven.plugins.dependency.testUtils.stubs.DependencyProjectStub;
+import org.apache.maven.project.MavenProject;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestRenderDependenciesMojo extends AbstractDependencyMojoTestCase {
+ private RenderDependenciesMojo mojo;
+
+ @Override
+ protected String getTestDirectoryName() {
+ return "render-dependencies";
+ }
+
+ @Override
+ protected boolean shouldCreateFiles() {
+ return true;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ final MavenProject project = new DependencyProjectStub();
+ getContainer().addComponent(project, MavenProject.class.getName());
+
+ final MavenSession session = newMavenSession(project);
+ getContainer().addComponent(session, MavenSession.class.getName());
+
+ final File testPom = new File(
+ getBasedir(), "target/test-classes/unit/" + getTestDirectoryName() + "-test/plugin-config.xml");
+ mojo = (RenderDependenciesMojo) lookupMojo(getTestDirectoryName(), testPom);
+ }
+
+ /**
+ * Tests the rendering.
+ * Note that this is a real life example of using the mojo to generate a CRD for a SparkApplication.
+ * It is useful when combined with JIB for example since several versions of the CRD do not support wildcard for
+ * the classpath(s).
+ */
+ public void testRender() throws Exception {
+ final File rendered = new File(testDir, "render-dependencies.testRender.txt");
+
+ final MavenProject project = mojo.getProject();
+ final Set<Artifact> artifacts = stubFactory.getScopedArtifacts();
+ final Set<Artifact> directArtifacts = stubFactory.getReleaseAndSnapshotArtifacts();
+ artifacts.addAll(directArtifacts);
+ project.setArtifacts(artifacts);
+ project.setDependencyArtifacts(directArtifacts);
+
+ mojo.setTemplate("deps:\n"
+ + " jars:\n"
+ + "#foreach($dep in $sorter.sort($artifacts, [\"artifactId:asc\"]))\n"
+ + "#set($type = $dep.type)\n"
+ + "#if(!$type || $type.trim().isEmpty())\n"
+ + " #set($type = \"jar\")\n"
+ + "#end\n"
+ + "#set($classifierSuffix = \"\")\n"
+ + "#if($dep.classifier && !$dep.classifier.trim().isEmpty())\n"
+ + " #set($classifierSuffix = \"-$dep.classifier\")\n"
+ + "#end\n"
+ + " - local:///opt/test/libs/$dep.artifactId-$dep.baseVersion$classifierSuffix.$type\n"
+ + "#end");
+ mojo.setOutputFile(rendered);
+ mojo.execute();
+
+ assertThat(rendered)
+ .content()
+ .isEqualTo("deps:\n"
+ + " jars:\n"
+ + " - local:///opt/test/libs/compile-1.0.jar\n"
+ + " - local:///opt/test/libs/provided-1.0.jar\n"
+ + " - local:///opt/test/libs/release-1.0.jar\n"
+ + " - local:///opt/test/libs/runtime-1.0.jar\n"
+ + " - local:///opt/test/libs/snapshot-2.0-SNAPSHOT.jar\n"
+ + " - local:///opt/test/libs/system-1.0.jar\n"
+ + " - local:///opt/test/libs/test-1.0.jar\n");
+ }
+}
diff --git a/src/test/resources/unit/render-dependencies-test/plugin-config.xml b/src/test/resources/unit/render-dependencies-test/plugin-config.xml
new file mode 100644
index 00000000..1a046cae
--- /dev/null
+++ b/src/test/resources/unit/render-dependencies-test/plugin-config.xml
@@ -0,0 +1,37 @@
+<!--
+ * 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.
+ *
+-->
+<project>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <configuration>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-artifact</artifactId>
+ <version>2.0.4</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file