You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by hb...@apache.org on 2021/06/20 07:57:14 UTC
[maven-artifact-plugin] branch master updated: [MARTIFACT-20]
extract artifact:compare goal from artifact:buildinfo
This is an automated email from the ASF dual-hosted git repository.
hboutemy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-artifact-plugin.git
The following commit(s) were added to refs/heads/master by this push:
new 76926c1 [MARTIFACT-20] extract artifact:compare goal from artifact:buildinfo
76926c1 is described below
commit 76926c1dfe81add7b1ef6c732f5affea8dba5c11
Author: Hervé Boutemy <hb...@apache.org>
AuthorDate: Sun Jun 20 09:57:10 2021 +0200
[MARTIFACT-20] extract artifact:compare goal from artifact:buildinfo
---
pom.xml | 2 +-
src/it/{mono => buildinfo-mono}/pom.xml | 0
src/it/{mono => buildinfo-mono}/verify.groovy | 0
.../modA/pom.xml | 0
.../modB/pom.xml | 0
src/it/{multi => buildinfo-multi}/pom.xml | 0
src/it/{multi => buildinfo-multi}/verify.groovy | 0
.../modA/pom.xml | 0
.../modB/pom.xml | 0
.../modSkipAtEnd/pom.xml | 0
.../modSkipDeploy/pom.xml | 0
.../modSkipInstall/pom.xml | 0
.../pom.xml | 0
.../verify.groovy | 0
.../artifact/buildinfo/AbstractBuildinfoMojo.java | 272 +++++++++++
.../artifact/buildinfo/BuildInfoWriter.java | 1 -
.../plugins/artifact/buildinfo/BuildinfoMojo.java | 514 +--------------------
.../{BuildinfoMojo.java => CompareMojo.java} | 258 +----------
18 files changed, 290 insertions(+), 757 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0456932..0064326 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
</parent>
<artifactId>maven-artifact-plugin</artifactId>
- <version>3.1.1-SNAPSHOT</version>
+ <version>3.2.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>Apache Maven Artifact Plugin</name>
diff --git a/src/it/mono/pom.xml b/src/it/buildinfo-mono/pom.xml
similarity index 100%
rename from src/it/mono/pom.xml
rename to src/it/buildinfo-mono/pom.xml
diff --git a/src/it/mono/verify.groovy b/src/it/buildinfo-mono/verify.groovy
similarity index 100%
rename from src/it/mono/verify.groovy
rename to src/it/buildinfo-mono/verify.groovy
diff --git a/src/it/skip-install-deploy/modA/pom.xml b/src/it/buildinfo-multi/modA/pom.xml
similarity index 100%
rename from src/it/skip-install-deploy/modA/pom.xml
rename to src/it/buildinfo-multi/modA/pom.xml
diff --git a/src/it/skip-install-deploy/modB/pom.xml b/src/it/buildinfo-multi/modB/pom.xml
similarity index 100%
rename from src/it/skip-install-deploy/modB/pom.xml
rename to src/it/buildinfo-multi/modB/pom.xml
diff --git a/src/it/multi/pom.xml b/src/it/buildinfo-multi/pom.xml
similarity index 100%
rename from src/it/multi/pom.xml
rename to src/it/buildinfo-multi/pom.xml
diff --git a/src/it/multi/verify.groovy b/src/it/buildinfo-multi/verify.groovy
similarity index 100%
rename from src/it/multi/verify.groovy
rename to src/it/buildinfo-multi/verify.groovy
diff --git a/src/it/multi/modA/pom.xml b/src/it/buildinfo-skip-install-deploy/modA/pom.xml
similarity index 100%
rename from src/it/multi/modA/pom.xml
rename to src/it/buildinfo-skip-install-deploy/modA/pom.xml
diff --git a/src/it/multi/modB/pom.xml b/src/it/buildinfo-skip-install-deploy/modB/pom.xml
similarity index 100%
rename from src/it/multi/modB/pom.xml
rename to src/it/buildinfo-skip-install-deploy/modB/pom.xml
diff --git a/src/it/skip-install-deploy/modSkipAtEnd/pom.xml b/src/it/buildinfo-skip-install-deploy/modSkipAtEnd/pom.xml
similarity index 100%
rename from src/it/skip-install-deploy/modSkipAtEnd/pom.xml
rename to src/it/buildinfo-skip-install-deploy/modSkipAtEnd/pom.xml
diff --git a/src/it/skip-install-deploy/modSkipDeploy/pom.xml b/src/it/buildinfo-skip-install-deploy/modSkipDeploy/pom.xml
similarity index 100%
rename from src/it/skip-install-deploy/modSkipDeploy/pom.xml
rename to src/it/buildinfo-skip-install-deploy/modSkipDeploy/pom.xml
diff --git a/src/it/skip-install-deploy/modSkipInstall/pom.xml b/src/it/buildinfo-skip-install-deploy/modSkipInstall/pom.xml
similarity index 100%
rename from src/it/skip-install-deploy/modSkipInstall/pom.xml
rename to src/it/buildinfo-skip-install-deploy/modSkipInstall/pom.xml
diff --git a/src/it/skip-install-deploy/pom.xml b/src/it/buildinfo-skip-install-deploy/pom.xml
similarity index 100%
rename from src/it/skip-install-deploy/pom.xml
rename to src/it/buildinfo-skip-install-deploy/pom.xml
diff --git a/src/it/skip-install-deploy/verify.groovy b/src/it/buildinfo-skip-install-deploy/verify.groovy
similarity index 100%
rename from src/it/skip-install-deploy/verify.groovy
rename to src/it/buildinfo-skip-install-deploy/verify.groovy
diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java
new file mode 100644
index 0000000..63cd9c9
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java
@@ -0,0 +1,272 @@
+package org.apache.maven.plugins.artifact.buildinfo;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.utils.io.FileUtils;
+import org.apache.maven.toolchain.Toolchain;
+import org.apache.maven.toolchain.ToolchainManager;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Base buildinfo-generating class, for goals related to Reproducible Builds {@code .buildinfo} files.
+ *
+ * @since 3.2.0
+ */
+public abstract class AbstractBuildinfoMojo
+ extends AbstractMojo
+{
+ /**
+ * The Maven project.
+ */
+ @Parameter( defaultValue = "${project}", readonly = true )
+ protected MavenProject project;
+
+ /**
+ * The reactor projects.
+ */
+ @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
+ protected List<MavenProject> reactorProjects;
+
+ /**
+ * 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 <code>extension</code> or <code>classifier.extension</code>.
+ */
+ @Parameter( property = "buildinfo.ignore", defaultValue = "" )
+ private Set<String> ignore;
+
+ /**
+ * Detect projects/modules with install or deploy skipped: avoid taking fingerprints.
+ */
+ @Parameter( property = "buildinfo.detect.skip", defaultValue = "true" )
+ private boolean detectSkip;
+
+ /**
+ * 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;
+
+ /**
+ * The current build session instance. This is used for toolchain manager API calls.
+ */
+ @Parameter( defaultValue = "${session}", readonly = true, required = true )
+ private MavenSession session;
+
+ /**
+ * To obtain a toolchain if possible.
+ */
+ @Component
+ private ToolchainManager toolchainManager;
+
+ @Override
+ public void execute()
+ throws MojoExecutionException
+ {
+ boolean mono = reactorProjects.size() == 1;
+
+ 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 )
+ {
+ getLog().info( "Skipping intermediate goal run, aggregate will be " + last.getArtifactId() );
+ return;
+ }
+ }
+
+ // generate buildinfo
+ Map<Artifact, String> artifacts = generateBuildinfo( mono );
+ getLog().info( "Saved " + ( mono ? "" : "aggregate " ) + "info on build to " + buildinfoFile );
+
+ copyAggregateToRoot( buildinfoFile );
+
+ execute( artifacts );
+ }
+
+ /**
+ * 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
+ * (<code>outputs.[#module.].#artifact</code>)
+ */
+ abstract void execute( Map<Artifact, String> artifacts )
+ throws MojoExecutionException;
+
+ protected void copyAggregateToRoot( File aggregate )
+ throws MojoExecutionException
+ {
+ if ( reactorProjects.size() == 1 )
+ {
+ // mono-module, no aggregate buildinfo to deal with
+ return;
+ }
+
+ // copy aggregate buildinfo to root target directory
+ MavenProject root = getExecutionRoot();
+ String compare = aggregate.getName().endsWith( ".compare" ) ? ".compare" : "";
+ File rootCopy = new File( root.getBuild().getDirectory(),
+ root.getArtifactId() + '-' + root.getVersion() + ".buildinfo" + compare );
+ try
+ {
+ FileUtils.copyFile( aggregate, rootCopy );
+ getLog().info( "Aggregate buildinfo" + compare + " copied to " + rootCopy );
+ }
+ catch ( IOException ioe )
+ {
+ throw new MojoExecutionException( "Could not copy " + aggregate + "to " + rootCopy );
+ }
+ }
+
+ /**
+ * 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
+ * (<code>outputs.[#module.].#artifact</code>)
+ * @throws MojoExecutionException
+ */
+ private Map<Artifact, String> generateBuildinfo( boolean mono )
+ throws MojoExecutionException
+ {
+ MavenProject root = mono ? project : getExecutionRoot();
+
+ buildinfoFile.getParentFile().mkdirs();
+
+ try ( PrintWriter p = new PrintWriter( new BufferedWriter(
+ new OutputStreamWriter( new FileOutputStream( buildinfoFile ), StandardCharsets.UTF_8 ) ) ) )
+ {
+ BuildInfoWriter bi = new BuildInfoWriter( getLog(), p, mono );
+ bi.setIgnoreJavadoc( ignoreJavadoc );
+ bi.setIgnore( ignore );
+ bi.setToolchain( getToolchain() );
+
+ bi.printHeader( root, mono ? null : project, reproducible );
+
+ // artifact(s) fingerprints
+ if ( mono )
+ {
+ bi.printArtifacts( project );
+ }
+ else
+ {
+ for ( MavenProject project : reactorProjects )
+ {
+ 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 );
+ }
+ }
+
+ protected MavenProject getExecutionRoot()
+ {
+ for ( MavenProject p : reactorProjects )
+ {
+ if ( p.isExecutionRoot() )
+ {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ private MavenProject getLastProject()
+ {
+ int i = reactorProjects.size();
+ while ( i > 0 )
+ {
+ MavenProject project = reactorProjects.get( --i );
+ if ( !isSkip( project ) )
+ {
+ return project;
+ }
+ }
+ return null;
+ }
+
+ private boolean isSkip( MavenProject project )
+ {
+ return detectSkip && PluginUtil.isSkip( project );
+ }
+
+ private Toolchain getToolchain()
+ {
+ Toolchain tc = null;
+ if ( toolchainManager != null )
+ {
+ tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
+ }
+
+ return tc;
+ }
+}
diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildInfoWriter.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildInfoWriter.java
index 24b0651..1355f03 100644
--- a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildInfoWriter.java
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildInfoWriter.java
@@ -87,7 +87,6 @@ class BuildInfoWriter
}
p.println();
p.println( "# Maven rebuild instructions and effective environment" );
- //p.println( "mvn.rebuild-args=" + rebuildArgs );
if ( !reproducible )
{
p.println( "mvn.version=" + MavenVersion.createMavenVersionString() );
diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java
index ea187f5..0fc50b5 100644
--- a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java
@@ -19,202 +19,41 @@ package org.apache.maven.plugins.artifact.buildinfo;
* under the License.
*/
+import java.util.Map;
+
import org.apache.maven.artifact.Artifact;
-import org.apache.maven.artifact.factory.ArtifactFactory;
-import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
-
import org.apache.maven.plugins.annotations.Component;
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.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
-import org.apache.maven.shared.utils.io.FileUtils;
-import org.apache.maven.shared.utils.logging.MessageUtils;
-import org.apache.maven.toolchain.Toolchain;
-import org.apache.maven.toolchain.ToolchainManager;
-import org.apache.maven.shared.utils.PropertyUtils;
-import org.apache.maven.shared.utils.StringUtils;
-import org.eclipse.aether.RepositorySystem;
-import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.repository.RemoteRepository;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
/**
* Creates a buildinfo file recording build environment and output, as specified in
* <a href="https://reproducible-builds.org/docs/jvm/">Reproducible Builds for the JVM</a>
* for mono-module build, and extended for multi-module build.
- * Then if a remote repository is configured, check against reference content in it.
*/
@Mojo( name = "buildinfo", defaultPhase = LifecyclePhase.VERIFY )
public class BuildinfoMojo
- extends AbstractMojo
+ extends AbstractBuildinfoMojo
{
/**
- * The Maven project.
- */
- @Parameter( defaultValue = "${project}", readonly = true )
- private MavenProject project;
-
- /**
- * The reactor projects.
- */
- @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
- private List<MavenProject> reactorProjects;
-
- /**
- * Location of the generated buildinfo file.
- */
- @Parameter( defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}.buildinfo",
- required = true, readonly = true )
- private File buildinfoFile;
-
- /**
- * Ignore javadoc attached artifacts from buildinfo generation.
- */
- @Parameter( property = "buildinfo.ignoreJavadoc", defaultValue = "true" )
- private boolean ignoreJavadoc;
-
- /**
- * Artifacts to ignore, specified as <code>extension</code> or <code>classifier.extension</code>.
- */
- @Parameter( property = "buildinfo.ignore", defaultValue = "" )
- private Set<String> ignore;
-
- /**
* Specifies whether to attach the generated buildinfo file to the project.
*/
@Parameter( property = "buildinfo.attach", defaultValue = "true" )
private boolean attach;
/**
- * Rebuild arguments.
- */
- //@Parameter( property = "buildinfo.rebuild-args", defaultValue = "-DskipTests verify" )
- //private String rebuildArgs;
-
- /**
- * Repository for reference build, containing either reference buildinfo file or reference artifacts.<br/>
- * Format: <code>id</code> or <code>url</code> or <code>id::url</code>
- * <dl>
- * <dt>id</dt>
- * <dd>The repository id</dd>
- * <dt>url</dt>
- * <dd>The url of the repository</dd>
- * </dl>
- * @see <a href="https://maven.apache.org/ref/current/maven-model/maven.html#repository">repository definition</a>
- */
- @Parameter( property = "reference.repo" )
- private String referenceRepo;
-
- /**
- * Specifies if reference comparison output file should be saved.
- * This is expected to be a temporary feature to ease
- * <a href="https://github.com/jvm-repo-rebuild/reproducible-central">Central Repository rebuild</a>
- * results display.
- */
- @Parameter( property = "reference.compare.save", defaultValue = "false" )
- private boolean referenceCompareSave;
-
- /**
- * Detect projects/modules with install or deploy skipped: avoid taking fingerprinting.
- */
- @Parameter( property = "buildinfo.detect.skip", defaultValue = "true" )
- private boolean detectSkip;
-
- /**
- * Makes the generated {@code .buildinfo} file reproducible, by dropping detailed environment recording: OS will be
- * recorded as "Windows" or "Unix", JVM version only as major version.
- *
- * @since 3.1.0
- */
- @Parameter( property = "buildinfo.reproducible", defaultValue = "false" )
- private boolean reproducible;
-
- /**
* Used for attaching the buildinfo file in the project.
*/
@Component
private MavenProjectHelper projectHelper;
- @Component
- private ArtifactFactory artifactFactory;
-
- /**
- * The entry point to Maven Artifact Resolver, i.e. the component doing all the work.
- */
- @Component
- private RepositorySystem repoSystem;
-
- /**
- * The current repository/network configuration of Maven.
- */
- @Parameter( defaultValue = "${repositorySystemSession}", readonly = true )
- private RepositorySystemSession repoSession;
-
- /**
- * The project's remote repositories to use for the resolution.
- */
- @Parameter( defaultValue = "${project.remoteProjectRepositories}", readonly = true )
- private List<RemoteRepository> remoteRepos;
-
- @Component
- private ArtifactRepositoryLayout artifactRepositoryLayout;
-
- /**
- * The current build session instance. This is used for toolchain manager API calls.
- */
- @Parameter( defaultValue = "${session}", readonly = true, required = true )
- private MavenSession session;
-
- /**
- * To obtain a toolchain if possible.
- */
- @Component
- private ToolchainManager toolchainManager;
-
@Override
- public void execute()
+ public void execute( Map<Artifact, String> artifacts )
throws MojoExecutionException
{
- boolean mono = reactorProjects.size() == 1;
-
- if ( !mono )
- {
- // if module skips install and/or deploy
- if ( isSkip( project ) )
- {
- getLog().info( "Skipping buildinfo for module that skips install and/or deploy" );
- return;
- }
- // if multi-module build, generate (aggregate) buildinfo only in last module
- MavenProject last = getLastProject();
- if ( project != last )
- {
- getLog().info( "Skipping intermediate buildinfo, aggregate will be " + last.getArtifactId() );
- return;
- }
- }
-
- // generate buildinfo
- Map<Artifact, String> artifacts = generateBuildinfo( mono );
- getLog().info( "Saved " + ( mono ? "" : "aggregate " ) + "info on build to " + buildinfoFile );
-
// eventually attach
if ( attach )
{
@@ -225,350 +64,5 @@ public class BuildinfoMojo
{
getLog().info( "NOT adding buildinfo to the list of attached artifacts." );
}
-
- copyAggregateToRoot( buildinfoFile );
-
- // eventually check against reference
- if ( referenceRepo != null )
- {
- getLog().info( "Checking against reference build from " + referenceRepo + "..." );
- checkAgainstReference( mono, artifacts );
- }
- }
-
- private void copyAggregateToRoot( File aggregate )
- throws MojoExecutionException
- {
- if ( reactorProjects.size() == 1 )
- {
- // mono-module, no aggregate buildinfo to deal with
- return;
- }
-
- // copy aggregate buildinfo to root target directory
- MavenProject root = getExecutionRoot();
- String compare = aggregate.getName().endsWith( ".compare" ) ? ".compare" : "";
- File rootCopy = new File( root.getBuild().getDirectory(),
- root.getArtifactId() + '-' + root.getVersion() + ".buildinfo" + compare );
- try
- {
- FileUtils.copyFile( aggregate, rootCopy );
- getLog().info( "Aggregate buildinfo" + compare + " copied to " + rootCopy );
- }
- catch ( IOException ioe )
- {
- throw new MojoExecutionException( "Could not copy " + aggregate + "to " + rootCopy );
- }
- }
-
- /**
- * 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
- * (<code>outputs.[#module.].#artifact</code>)
- * @throws MojoExecutionException
- */
- private Map<Artifact, String> generateBuildinfo( boolean mono )
- throws MojoExecutionException
- {
- MavenProject root = mono ? project : getExecutionRoot();
-
- buildinfoFile.getParentFile().mkdirs();
-
- try ( PrintWriter p = new PrintWriter( new BufferedWriter(
- new OutputStreamWriter( new FileOutputStream( buildinfoFile ), StandardCharsets.UTF_8 ) ) ) )
- {
- BuildInfoWriter bi = new BuildInfoWriter( getLog(), p, mono );
- bi.setIgnoreJavadoc( ignoreJavadoc );
- bi.setIgnore( ignore );
- bi.setToolchain( getToolchain() );
-
- bi.printHeader( root, mono ? null : project, reproducible );
-
- // artifact(s) fingerprints
- if ( mono )
- {
- bi.printArtifacts( project );
- }
- else
- {
- for ( MavenProject project : reactorProjects )
- {
- 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 );
- }
- }
-
- /**
- * Check current build result with reference.
- *
- * @param mono is it a mono-module build?
- * @artifacts a Map of artifacts added to the build info with their associated property key prefix
- * (<code>outputs.[#module.].#artifact</code>)
- * @throws MojoExecutionException
- */
- private void checkAgainstReference( boolean mono, Map<Artifact, String> artifacts )
- throws MojoExecutionException
- {
- MavenProject root = mono ? project : getExecutionRoot();
- File referenceDir = new File( root.getBuild().getDirectory(), "reference" );
- referenceDir.mkdirs();
-
- // download or create reference buildinfo
- File referenceBuildinfo = downloadOrCreateReferenceBuildinfo( mono, artifacts, referenceDir );
-
- // compare outputs from reference buildinfo vs actual
- compareWithReference( artifacts, referenceBuildinfo );
- }
-
- private File downloadOrCreateReferenceBuildinfo( boolean mono, Map<Artifact, String> artifacts, File referenceDir )
- throws MojoExecutionException
- {
- RemoteRepository repo = createReferenceRepo();
-
- ReferenceBuildinfoUtil rmb = new ReferenceBuildinfoUtil( getLog(), referenceDir, artifacts,
- artifactFactory, repoSystem, repoSession );
-
- return rmb.downloadOrCreateReferenceBuildinfo( repo, project, buildinfoFile, mono );
- }
-
- private void compareWithReference( Map<Artifact, String> artifacts, File referenceBuildinfo )
- throws MojoExecutionException
- {
- Properties actual = BuildInfoWriter.loadOutputProperties( buildinfoFile );
- Properties reference = BuildInfoWriter.loadOutputProperties( referenceBuildinfo );
-
- int ok = 0;
- List<String> okFilenames = new ArrayList<>();
- List<String> koFilenames = new ArrayList<>();
- List<String> diffoscopes = new ArrayList<>();
- File referenceDir = referenceBuildinfo.getParentFile();
- for ( Map.Entry<Artifact, String> entry : artifacts.entrySet() )
- {
- Artifact artifact = entry.getKey();
- String prefix = entry.getValue();
-
- String diffoscope = checkArtifact( artifact, prefix, reference, actual, referenceDir );
- if ( diffoscope == null )
- {
- ok++;
- okFilenames.add( artifact.getFile().getName() );
- }
- else
- {
- koFilenames.add( artifact.getFile().getName() );
- diffoscopes.add( diffoscope );
- }
- }
-
- int ko = artifacts.size() - ok;
- int missing = reference.size() / 3 /* 3 property keys par file: filename, length and checksums.sha512 */;
-
- if ( ko + missing > 0 )
- {
- getLog().warn( "Reproducible Build output summary: " + MessageUtils.buffer().success( ok + " files ok" )
- + ", " + MessageUtils.buffer().failure( ko + " different" )
- + ( ( missing == 0 ) ? "" : ( ", " + MessageUtils.buffer().warning( missing + " missing" ) ) ) );
- getLog().warn( "see " + MessageUtils.buffer().project( "diff " + relative( referenceBuildinfo ) + " "
- + relative( buildinfoFile ) ).toString() );
- getLog().warn( "see also https://maven.apache.org/guides/mini/guide-reproducible-builds.html" );
- }
- else
- {
- getLog().info( "Reproducible Build output summary: " + MessageUtils.buffer().success( ok + " files ok" ) );
- }
-
- if ( referenceCompareSave )
- {
- File compare = new File( buildinfoFile.getParentFile(), buildinfoFile.getName() + ".compare" );
- try ( PrintWriter p =
- new PrintWriter( new BufferedWriter( new OutputStreamWriter( new FileOutputStream( compare ),
- StandardCharsets.UTF_8 ) ) ) )
- {
- p.println( "version=" + project.getVersion() );
- p.println( "ok=" + ok );
- p.println( "ko=" + ko );
- p.println( "okFiles=\"" + StringUtils.join( okFilenames.iterator(), " " ) + '"' );
- p.println( "koFiles=\"" + StringUtils.join( koFilenames.iterator(), " " ) + '"' );
- Properties ref = PropertyUtils.loadOptionalProperties( referenceBuildinfo );
- String v = ref.getProperty( "java.version" );
- if ( v != null )
- {
- p.println( "reference_java_version=\"" + v + '"' );
- }
- v = ref.getProperty( "os.name" );
- if ( v != null )
- {
- p.println( "reference_os_name=\"" + v + '"' );
- }
- for ( String diffoscope : diffoscopes )
- {
- p.print( "# " );
- p.println( diffoscope );
- }
- getLog().info( "Reproducible Build comparison saved to " + compare );
- }
- catch ( IOException e )
- {
- throw new MojoExecutionException( "Error creating file " + compare, e );
- }
-
- copyAggregateToRoot( compare );
- }
- }
-
- private String checkArtifact( Artifact artifact, String prefix, Properties reference, Properties actual,
- File referenceDir )
- {
- String actualFilename = (String) actual.remove( prefix + ".filename" );
- String actualLength = (String) actual.remove( prefix + ".length" );
- String actualSha512 = (String) actual.remove( prefix + ".checksums.sha512" );
-
- String referencePrefix = findPrefix( reference, actualFilename );
- String referenceLength = (String) reference.remove( referencePrefix + ".length" );
- String referenceSha512 = (String) reference.remove( referencePrefix + ".checksums.sha512" );
-
- String issue = null;
- if ( !actualLength.equals( referenceLength ) )
- {
- issue = "size";
- }
- else if ( !actualSha512.equals( referenceSha512 ) )
- {
- issue = "sha512";
- }
-
- if ( issue != null )
- {
- String diffoscope = diffoscope( artifact, referenceDir );
- getLog().warn( issue + " mismatch " + MessageUtils.buffer().strong( actualFilename ) + ": investigate with "
- + MessageUtils.buffer().project( diffoscope ) );
- return diffoscope;
- }
- return null;
- }
-
- private String diffoscope( Artifact a, File referenceDir )
- {
- File actual = a.getFile();
- // notice: actual file name may have been defined in pom
- // reference file name is taken from repository format
- File reference = new File( referenceDir, getRepositoryFilename( a ) );
- return "diffoscope " + relative( reference ) + " " + relative( actual );
- }
-
- private String getRepositoryFilename( Artifact a )
- {
- String path = artifactRepositoryLayout.pathOf( a );
- return path.substring( path.lastIndexOf( '/' ) );
- }
-
- private String relative( File file )
- {
- return file.getPath().substring( getExecutionRoot().getBasedir().getPath().length() + 1 );
- }
-
- private static String findPrefix( Properties reference, String actualFilename )
- {
- for ( String name : reference.stringPropertyNames() )
- {
- if ( name.endsWith( ".filename" ) && actualFilename.equals( reference.getProperty( name ) ) )
- {
- reference.remove( name );
- return name.substring( 0, name.length() - ".filename".length() );
- }
- }
- return null;
- }
-
- private RemoteRepository createReferenceRepo()
- throws MojoExecutionException
- {
- if ( referenceRepo.contains( "::" ) )
- {
- // id::url
- int index = referenceRepo.indexOf( "::" );
- String id = referenceRepo.substring( 0, index );
- String url = referenceRepo.substring( index + 2 );
- return createDeploymentArtifactRepository( id, url );
- }
- else if ( referenceRepo.contains( ":" ) )
- {
- // url, will use default "reference" id
- return createDeploymentArtifactRepository( "reference", referenceRepo );
- }
-
- // id
- for ( RemoteRepository repo : remoteRepos )
- {
- if ( referenceRepo.equals( repo.getId() ) )
- {
- return repo;
- }
- }
- throw new MojoExecutionException( "Could not find repository with id = " + referenceRepo );
- }
-
- private static RemoteRepository createDeploymentArtifactRepository( String id, String url )
- {
- return new RemoteRepository.Builder( id, "default", url ).build();
- }
-
- private MavenProject getExecutionRoot()
- {
- for ( MavenProject p : reactorProjects )
- {
- if ( p.isExecutionRoot() )
- {
- return p;
- }
- }
- return null;
- }
-
- private MavenProject getLastProject()
- {
- int i = reactorProjects.size();
- while ( i > 0 )
- {
- MavenProject project = reactorProjects.get( --i );
- if ( !isSkip( project ) )
- {
- return project;
- }
- }
- return null;
- }
-
- private boolean isSkip( MavenProject project )
- {
- return detectSkip && PluginUtil.isSkip( project );
- }
-
- private Toolchain getToolchain()
- {
- Toolchain tc = null;
- if ( toolchainManager != null )
- {
- tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
- }
-
- return tc;
}
}
diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CompareMojo.java
similarity index 61%
copy from src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java
copy to src/main/java/org/apache/maven/plugins/artifact/buildinfo/CompareMojo.java
index ea187f5..d69c0c4 100644
--- a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/BuildinfoMojo.java
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CompareMojo.java
@@ -23,18 +23,13 @@ import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.execution.MavenSession;
-import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
-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.project.MavenProject;
-import org.apache.maven.project.MavenProjectHelper;
-import org.apache.maven.shared.utils.io.FileUtils;
import org.apache.maven.shared.utils.logging.MessageUtils;
-import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.apache.maven.shared.utils.PropertyUtils;
import org.apache.maven.shared.utils.StringUtils;
@@ -53,62 +48,17 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.Set;
/**
- * Creates a buildinfo file recording build environment and output, as specified in
- * <a href="https://reproducible-builds.org/docs/jvm/">Reproducible Builds for the JVM</a>
- * for mono-module build, and extended for multi-module build.
- * Then if a remote repository is configured, check against reference content in it.
+ * Compare current build output with reference either previously installed or downloaded from a remote repository.
+ *
+ * @since 3.2.0
*/
-@Mojo( name = "buildinfo", defaultPhase = LifecyclePhase.VERIFY )
-public class BuildinfoMojo
- extends AbstractMojo
+@Mojo( name = "compare" )
+public class CompareMojo
+ extends AbstractBuildinfoMojo
{
/**
- * The Maven project.
- */
- @Parameter( defaultValue = "${project}", readonly = true )
- private MavenProject project;
-
- /**
- * The reactor projects.
- */
- @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
- private List<MavenProject> reactorProjects;
-
- /**
- * Location of the generated buildinfo file.
- */
- @Parameter( defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}.buildinfo",
- required = true, readonly = true )
- private File buildinfoFile;
-
- /**
- * Ignore javadoc attached artifacts from buildinfo generation.
- */
- @Parameter( property = "buildinfo.ignoreJavadoc", defaultValue = "true" )
- private boolean ignoreJavadoc;
-
- /**
- * Artifacts to ignore, specified as <code>extension</code> or <code>classifier.extension</code>.
- */
- @Parameter( property = "buildinfo.ignore", defaultValue = "" )
- private Set<String> ignore;
-
- /**
- * Specifies whether to attach the generated buildinfo file to the project.
- */
- @Parameter( property = "buildinfo.attach", defaultValue = "true" )
- private boolean attach;
-
- /**
- * Rebuild arguments.
- */
- //@Parameter( property = "buildinfo.rebuild-args", defaultValue = "-DskipTests verify" )
- //private String rebuildArgs;
-
- /**
* Repository for reference build, containing either reference buildinfo file or reference artifacts.<br/>
* Format: <code>id</code> or <code>url</code> or <code>id::url</code>
* <dl>
@@ -119,7 +69,7 @@ public class BuildinfoMojo
* </dl>
* @see <a href="https://maven.apache.org/ref/current/maven-model/maven.html#repository">repository definition</a>
*/
- @Parameter( property = "reference.repo" )
+ @Parameter( property = "reference.repo", defaultValue = "central" )
private String referenceRepo;
/**
@@ -131,27 +81,6 @@ public class BuildinfoMojo
@Parameter( property = "reference.compare.save", defaultValue = "false" )
private boolean referenceCompareSave;
- /**
- * Detect projects/modules with install or deploy skipped: avoid taking fingerprinting.
- */
- @Parameter( property = "buildinfo.detect.skip", defaultValue = "true" )
- private boolean detectSkip;
-
- /**
- * Makes the generated {@code .buildinfo} file reproducible, by dropping detailed environment recording: OS will be
- * recorded as "Windows" or "Unix", JVM version only as major version.
- *
- * @since 3.1.0
- */
- @Parameter( property = "buildinfo.reproducible", defaultValue = "false" )
- private boolean reproducible;
-
- /**
- * Used for attaching the buildinfo file in the project.
- */
- @Component
- private MavenProjectHelper projectHelper;
-
@Component
private ArtifactFactory artifactFactory;
@@ -189,144 +118,25 @@ public class BuildinfoMojo
private ToolchainManager toolchainManager;
@Override
- public void execute()
+ public void execute( Map<Artifact, String> artifacts )
throws MojoExecutionException
{
- boolean mono = reactorProjects.size() == 1;
-
- if ( !mono )
- {
- // if module skips install and/or deploy
- if ( isSkip( project ) )
- {
- getLog().info( "Skipping buildinfo for module that skips install and/or deploy" );
- return;
- }
- // if multi-module build, generate (aggregate) buildinfo only in last module
- MavenProject last = getLastProject();
- if ( project != last )
- {
- getLog().info( "Skipping intermediate buildinfo, aggregate will be " + last.getArtifactId() );
- return;
- }
- }
-
- // generate buildinfo
- Map<Artifact, String> artifacts = generateBuildinfo( mono );
- getLog().info( "Saved " + ( mono ? "" : "aggregate " ) + "info on build to " + buildinfoFile );
-
- // eventually attach
- if ( attach )
- {
- getLog().info( "Attaching buildinfo" );
- projectHelper.attachArtifact( project, "buildinfo", buildinfoFile );
- }
- else
- {
- getLog().info( "NOT adding buildinfo to the list of attached artifacts." );
- }
-
- copyAggregateToRoot( buildinfoFile );
-
- // eventually check against reference
- if ( referenceRepo != null )
- {
- getLog().info( "Checking against reference build from " + referenceRepo + "..." );
- checkAgainstReference( mono, artifacts );
- }
- }
-
- private void copyAggregateToRoot( File aggregate )
- throws MojoExecutionException
- {
- if ( reactorProjects.size() == 1 )
- {
- // mono-module, no aggregate buildinfo to deal with
- return;
- }
-
- // copy aggregate buildinfo to root target directory
- MavenProject root = getExecutionRoot();
- String compare = aggregate.getName().endsWith( ".compare" ) ? ".compare" : "";
- File rootCopy = new File( root.getBuild().getDirectory(),
- root.getArtifactId() + '-' + root.getVersion() + ".buildinfo" + compare );
- try
- {
- FileUtils.copyFile( aggregate, rootCopy );
- getLog().info( "Aggregate buildinfo" + compare + " copied to " + rootCopy );
- }
- catch ( IOException ioe )
- {
- throw new MojoExecutionException( "Could not copy " + aggregate + "to " + rootCopy );
- }
- }
-
- /**
- * 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
- * (<code>outputs.[#module.].#artifact</code>)
- * @throws MojoExecutionException
- */
- private Map<Artifact, String> generateBuildinfo( boolean mono )
- throws MojoExecutionException
- {
- MavenProject root = mono ? project : getExecutionRoot();
-
- buildinfoFile.getParentFile().mkdirs();
-
- try ( PrintWriter p = new PrintWriter( new BufferedWriter(
- new OutputStreamWriter( new FileOutputStream( buildinfoFile ), StandardCharsets.UTF_8 ) ) ) )
- {
- BuildInfoWriter bi = new BuildInfoWriter( getLog(), p, mono );
- bi.setIgnoreJavadoc( ignoreJavadoc );
- bi.setIgnore( ignore );
- bi.setToolchain( getToolchain() );
-
- bi.printHeader( root, mono ? null : project, reproducible );
-
- // artifact(s) fingerprints
- if ( mono )
- {
- bi.printArtifacts( project );
- }
- else
- {
- for ( MavenProject project : reactorProjects )
- {
- 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 );
- }
+ getLog().info( "Checking against reference build from " + referenceRepo + "..." );
+ checkAgainstReference( artifacts );
}
/**
* Check current build result with reference.
*
- * @param mono is it a mono-module build?
* @artifacts a Map of artifacts added to the build info with their associated property key prefix
* (<code>outputs.[#module.].#artifact</code>)
* @throws MojoExecutionException
*/
- private void checkAgainstReference( boolean mono, Map<Artifact, String> artifacts )
+ private void checkAgainstReference( Map<Artifact, String> artifacts )
throws MojoExecutionException
{
- MavenProject root = mono ? project : getExecutionRoot();
+ boolean mono = reactorProjects.size() == 1;
+ MavenProject root = mono ? project : getExecutionRoot();
File referenceDir = new File( root.getBuild().getDirectory(), "reference" );
referenceDir.mkdirs();
@@ -529,46 +339,4 @@ public class BuildinfoMojo
{
return new RemoteRepository.Builder( id, "default", url ).build();
}
-
- private MavenProject getExecutionRoot()
- {
- for ( MavenProject p : reactorProjects )
- {
- if ( p.isExecutionRoot() )
- {
- return p;
- }
- }
- return null;
- }
-
- private MavenProject getLastProject()
- {
- int i = reactorProjects.size();
- while ( i > 0 )
- {
- MavenProject project = reactorProjects.get( --i );
- if ( !isSkip( project ) )
- {
- return project;
- }
- }
- return null;
- }
-
- private boolean isSkip( MavenProject project )
- {
- return detectSkip && PluginUtil.isSkip( project );
- }
-
- private Toolchain getToolchain()
- {
- Toolchain tc = null;
- if ( toolchainManager != null )
- {
- tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
- }
-
- return tc;
- }
}