![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.maven.plugins.artifact.buildinfo.AbstractBuildinfoMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-artifact-plugin Show documentation
Show all versions of maven-artifact-plugin Show documentation
Plugin to manage artifacts tasks
The newest version!
/*
* 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.artifact.buildinfo;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.rtinfo.RuntimeInformation;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.eclipse.aether.artifact.Artifact;
/**
* Base buildinfo-generating class, for goals related to Reproducible Builds {@code .buildinfo} files.
*
* @since 3.2.0
*/
public abstract class AbstractBuildinfoMojo extends AbstractMojo {
/**
* Location of the generated buildinfo file.
*/
@Parameter(
defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}.buildinfo",
required = true,
readonly = true)
protected File buildinfoFile;
/**
* Ignore javadoc attached artifacts from buildinfo generation.
*/
@Parameter(property = "buildinfo.ignoreJavadoc", defaultValue = "true")
private boolean ignoreJavadoc;
/**
* Artifacts to ignore, specified as a glob matching against ${groupId}/${filename}
, for example
* *>/*.xml
.
*/
@Parameter(property = "buildinfo.ignore", defaultValue = "")
private List ignore;
/**
* Detect projects/modules with install or deploy skipped: avoid taking fingerprints.
*/
@Parameter(property = "buildinfo.detect.skip", defaultValue = "true")
private boolean detectSkip;
/**
* Avoid taking fingerprints for modules specified as glob matching against ${groupId}/${artifactId}
.
* @since 3.5.0
*/
@Parameter(property = "buildinfo.skipModules")
private List skipModules;
private List skipModulesMatcher = null;
/**
* Makes the generated {@code .buildinfo} file reproducible, by dropping detailed environment recording: OS will be
* recorded as "Windows" or "Unix", JVM version as major version only.
*
* @since 3.1.0
*/
@Parameter(property = "buildinfo.reproducible", defaultValue = "false")
private boolean reproducible;
/**
* Timestamp for reproducible output archive entries, either formatted as ISO 8601
* yyyy-MM-dd'T'HH:mm:ssXXX
or as an int representing seconds since the epoch (like
* SOURCE_DATE_EPOCH).
*
* @since 3.2.0
*/
@Parameter(defaultValue = "${project.build.outputTimestamp}")
protected String outputTimestamp;
/**
* Diagnose {@code outputTimestamp} effective value based on execution context.
*
* @since 3.5.2
*/
@Parameter(property = "diagnose", defaultValue = "false")
private boolean diagnose;
/**
* To obtain a toolchain if possible.
*/
private final ToolchainManager toolchainManager;
protected final RuntimeInformation rtInformation;
/**
* The Maven project.
*/
protected final MavenProject project;
/**
* The current build session instance. This is used for toolchain manager API calls.
*/
protected final MavenSession session;
protected AbstractBuildinfoMojo(
ToolchainManager toolchainManager,
RuntimeInformation rtInformation,
MavenProject project,
MavenSession session) {
this.toolchainManager = toolchainManager;
this.rtInformation = rtInformation;
this.project = project;
this.session = session;
}
@Override
public void execute() throws MojoExecutionException {
boolean mono = session.getProjects().size() == 1;
hasBadOutputTimestamp(outputTimestamp, getLog(), project, session, diagnose);
if (!mono) {
// if module skips install and/or deploy
if (isSkip(project)) {
getLog().info("Skipping goal because module skips install and/or deploy");
return;
}
// if multi-module build, generate (aggregate) buildinfo only in last module
MavenProject last = getLastProject();
if (project != last) {
skip(last);
return;
}
}
// generate buildinfo
Map artifacts = generateBuildinfo(mono);
getLog().info("Saved " + (mono ? "" : "aggregate ") + "info on build to " + buildinfoFile);
copyAggregateToRoot(buildinfoFile);
execute(artifacts);
}
static boolean hasBadOutputTimestamp(
String outputTimestamp, Log log, MavenProject project, MavenSession session, boolean diagnose) {
Instant timestamp =
MavenArchiver.parseBuildOutputTimestamp(outputTimestamp).orElse(null);
String effective = ((timestamp == null) ? "disabled" : DateTimeFormatter.ISO_INSTANT.format(timestamp));
if (diagnose) {
diagnose(outputTimestamp, log, project, session, effective);
}
if (timestamp == null) {
log.error("Reproducible Build not activated by project.build.outputTimestamp property: "
+ "see https://maven.apache.org/guides/mini/guide-reproducible-builds.html");
String projectProperty = project.getProperties().getProperty("project.build.outputTimestamp");
if (projectProperty != null && projectProperty.startsWith("${git.")) {
log.error("project.build.outputTimestamp = \"" + projectProperty + "\": isn't Git value set?");
log.error("Did validate phase run and Git plugin set the value?");
if (project.getPackaging().equals("pom")) {
log.error("if using git-commit-id-plugin, false may solve the issue.");
}
}
return true;
}
if (log.isDebugEnabled()) {
log.debug("project.build.outputTimestamp = \"" + outputTimestamp + "\" => "
+ (effective.equals(outputTimestamp) ? "" : (" => " + effective)));
}
// check if timestamp defined in a project from reactor: info if it is not the case
boolean parentInReactor = false;
MavenProject reactorParent = project;
while (session.getProjects().contains(reactorParent.getParent())) {
parentInReactor = true;
reactorParent = reactorParent.getParent();
}
String prop = reactorParent.getOriginalModel().getProperties().getProperty("project.build.outputTimestamp");
if (prop == null) {
log.info(" property (= " + outputTimestamp + ") is inherited"
+ (parentInReactor ? " from outside the reactor" : "") + ", you can override in "
+ (parentInReactor ? "parent POM from reactor " + reactorParent.getFile() : "pom.xml"));
return false;
}
return false;
}
static void diagnose(
String outputTimestamp, Log log, MavenProject project, MavenSession session, String effective) {
if (session.getProjects().size() > 1) {
MavenProject root = session.getTopLevelProject();
MavenProject first = session.getProjects().get(0);
Path firstRelative =
root.getBasedir().toPath().relativize(first.getFile().toPath());
MavenProject firstParent = first.getParent();
log.info("reactor executionRoot = " + root.getId() + (root.equals(project) ? " (current)" : "")
+ System.lineSeparator()
+ " reactor first = "
+ (first.equals(root) ? "executionRoot" : (first.getId() + " @ " + firstRelative))
+ System.lineSeparator()
+ " first.parent = " + ((firstParent == null) ? firstParent : firstParent.getId()));
if (firstParent != null && session.getProjects().contains(firstParent)) {
// should not happen...
log.warn("reactor first parent = " + firstParent.getId() + " is in reactor");
}
}
log.info("outputTimestamp = " + outputTimestamp
+ (effective.equals(outputTimestamp) ? "" : (" => " + effective)));
String projectProperty = project.getProperties().getProperty("project.build.outputTimestamp");
String modelProperty = project.getModel().getProperties().getProperty("project.build.outputTimestamp");
String originalModelProperty =
project.getOriginalModel().getProperties().getProperty("project.build.outputTimestamp");
log.info("plugin outputTimestamp parameter diagnostics:" + System.lineSeparator()
+ " - plugin outputTimestamp parameter (defaultValue=\"${project.build.outputTimestamp}\") = "
+ outputTimestamp + System.lineSeparator()
+ " - project.build.outputTimestamp property from project = " + projectProperty
+ System.lineSeparator()
+ " - project.build.outputTimestamp property from project model = " + modelProperty
+ System.lineSeparator()
+ " - project.build.outputTimestamp property from project original model = "
+ originalModelProperty);
if (outputTimestamp == null) {
return;
}
MavenProject parent = project.getParent();
if (parent != null) {
StringBuilder sb = new StringBuilder("Inheritance analysis for property:" + System.lineSeparator()
+ " - current " + project.getId() + " property = " + projectProperty);
while (parent != null) {
String parentProperty = parent.getProperties().getProperty("project.build.outputTimestamp");
sb.append(System.lineSeparator());
sb.append(" - " + (session.getProjects().contains(parent) ? "reactor" : "external") + " parent "
+ parent.getId() + " property = " + parentProperty);
if (!projectProperty.equals(parentProperty)) {
break;
}
parent = parent.getParent();
}
log.info(sb.toString());
}
}
/**
* Execute after buildinfo has been generated for current build (eventually aggregated).
*
* @param artifacts a Map of artifacts added to the build info with their associated property key prefix
* (outputs.[#module.].#artifact
)
*/
abstract void execute(Map artifacts) throws MojoExecutionException;
protected void skip(MavenProject last) throws MojoExecutionException {
getLog().info("Skipping intermediate goal run, aggregate will be " + last.getArtifactId());
}
protected void copyAggregateToRoot(File aggregate) throws MojoExecutionException {
if (session.getProjects().size() == 1) {
// mono-module, no aggregate file to deal with
return;
}
// copy aggregate file to root target directory
MavenProject root = session.getTopLevelProject();
String extension = aggregate.getName().substring(aggregate.getName().lastIndexOf('.'));
File rootCopy =
new File(root.getBuild().getDirectory(), root.getArtifactId() + '-' + root.getVersion() + extension);
try {
rootCopy.getParentFile().mkdirs();
Files.copy(
aggregate.toPath(),
rootCopy.toPath(),
LinkOption.NOFOLLOW_LINKS,
StandardCopyOption.REPLACE_EXISTING);
getLog().info("Aggregate " + extension.substring(1) + " copied to " + rootCopy);
} catch (IOException ioe) {
throw new MojoExecutionException("Could not copy " + aggregate + " to " + rootCopy, ioe);
}
}
protected BuildInfoWriter newBuildInfoWriter(PrintWriter p, boolean mono) {
BuildInfoWriter bi = new BuildInfoWriter(getLog(), p, mono, rtInformation);
bi.setIgnoreJavadoc(ignoreJavadoc);
bi.setIgnore(ignore);
bi.setToolchain(getToolchain());
return bi;
}
/**
* Generate buildinfo file.
*
* @param mono is it a mono-module build?
* @return a Map of artifacts added to the build info with their associated property key prefix
* (outputs.[#module.].#artifact
)
* @throws MojoExecutionException if anything goes wrong
*/
protected Map generateBuildinfo(boolean mono) throws MojoExecutionException {
MavenProject root = mono ? project : session.getTopLevelProject();
buildinfoFile.getParentFile().mkdirs();
try (PrintWriter p = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(Files.newOutputStream(buildinfoFile.toPath()), StandardCharsets.UTF_8)))) {
BuildInfoWriter bi = newBuildInfoWriter(p, mono);
bi.printHeader(root, mono ? null : project, reproducible);
// artifact(s) fingerprints
if (mono) {
bi.printArtifacts(project);
} else {
for (MavenProject project : session.getProjects()) {
if (!isSkip(project)) {
bi.printArtifacts(project);
}
}
}
if (p.checkError()) {
throw new MojoExecutionException("Write error to " + buildinfoFile);
}
return bi.getArtifacts();
} catch (IOException e) {
throw new MojoExecutionException("Error creating file " + buildinfoFile, e);
}
}
private MavenProject getLastProject() {
int i = session.getProjects().size();
while (i > 0) {
MavenProject project = session.getProjects().get(--i);
if (!isSkip(project)) {
return project;
}
}
return null;
}
protected boolean isSkip(MavenProject project) {
// manual/configured module skip
boolean skipModule = false;
if (skipModules != null && !skipModules.isEmpty()) {
if (skipModulesMatcher == null) {
FileSystem fs = FileSystems.getDefault();
skipModulesMatcher = skipModules.stream()
.map(i -> fs.getPathMatcher("glob:" + i))
.collect(Collectors.toList());
}
Path path = Paths.get(project.getGroupId() + '/' + project.getArtifactId());
skipModule = skipModulesMatcher.stream().anyMatch(m -> m.matches(path));
}
// detected skip
return skipModule || (detectSkip && PluginUtil.isSkip(project));
}
private Toolchain getToolchain() {
Toolchain tc = null;
if (toolchainManager != null) {
tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
}
return tc;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy