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;
-    }
 }