View Javadoc
1   package org.apache.maven.plugins.artifact.buildinfo;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.PrintWriter;
24  import java.util.LinkedHashMap;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.Set;
28  
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.artifact.handler.ArtifactHandler;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.logging.Log;
33  import org.apache.maven.project.MavenProject;
34  import org.apache.maven.shared.utils.PropertyUtils;
35  import org.apache.maven.toolchain.Toolchain;
36  
37  /**
38   * Buildinfo content writer.
39   */
40  class BuildInfoWriter
41  {
42      private final Log log;
43      private final PrintWriter p;
44      private final boolean mono;
45      private final Map<Artifact, String> artifacts = new LinkedHashMap<>();
46      private int projectCount = -1;
47      private boolean ignoreJavadoc = true;
48      private Set<String> ignore;
49      private Toolchain toolchain;
50  
51      BuildInfoWriter( Log log, PrintWriter p, boolean mono )
52      {
53          this.log = log;
54          this.p = p;
55          this.mono = mono;
56      }
57  
58      void printHeader( MavenProject project, MavenProject aggregate )
59      {
60          p.println( "# https://reproducible-builds.org/docs/jvm/" );
61          p.println( "buildinfo.version=1.0-SNAPSHOT" );
62          p.println();
63          p.println( "name=" + project.getName() );
64          p.println( "group-id=" + project.getGroupId() );
65          p.println( "artifact-id=" + project.getArtifactId() );
66          p.println( "version=" + project.getVersion() );
67          p.println();
68          printSourceInformation( project );
69          p.println();
70          p.println( "# build instructions" );
71          p.println( "build-tool=mvn" );
72          //p.println( "# optional build setup url, as plugin parameter" );
73          p.println();
74          p.println( "# effective build environment information" );
75          p.println( "java.version=" + System.getProperty( "java.version" ) );
76          p.println( "java.vendor=" + System.getProperty( "java.vendor" ) );
77          p.println( "os.name=" + System.getProperty( "os.name" ) );
78          p.println();
79          p.println( "# Maven rebuild instructions and effective environment" );
80          //p.println( "mvn.rebuild-args=" + rebuildArgs );
81          p.println( "mvn.version=" + MavenVersion.createMavenVersionString() );
82          if ( ( project.getPrerequisites() != null ) && ( project.getPrerequisites().getMaven() != null ) )
83          {
84              // TODO wrong algorithm, should reuse algorithm written in versions-maven-plugin
85              p.println( "mvn.minimum.version=" + project.getPrerequisites().getMaven() );
86          }
87          if ( toolchain != null )
88          {
89              p.println( "mvn.toolchain.jdk=" + JdkToolchainUtil.getJavaVersion( toolchain ) );
90          }
91  
92          if ( !mono && ( aggregate != null ) )
93          {
94              p.println( "mvn.aggregate.artifact-id=" + aggregate.getArtifactId() );
95          }
96  
97          p.println();
98          p.println( "# " + ( mono ? "" : "aggregated " ) + "output" );
99      }
100 
101     private void printSourceInformation( MavenProject project )
102     {
103         boolean sourceAvailable = false;
104         p.println( "# source information" );
105         //p.println( "# TBD source.* artifact, url should be parameters" );
106         if ( project.getScm() != null )
107         {
108             sourceAvailable = true;
109             p.println( "source.scm.uri=" + project.getScm().getConnection() );
110             p.println( "source.scm.tag=" + project.getScm().getTag() );
111             if ( project.getArtifact().isSnapshot() )
112             {
113                 log.warn( "SCM source tag in buildinfo source.scm.tag=" + project.getScm().getTag()
114                     + " does not permit rebuilders reproducible source checkout" );
115                 // TODO is it possible to use Scm API to get SCM version?
116             }
117         }
118         else
119         {
120             p.println( "# no scm configured in pom.xml" );
121         }
122 
123         if ( !sourceAvailable )
124         {
125             log.warn( "No source information available in buildinfo for rebuilders..." );
126         }
127     }
128 
129     void printArtifacts( MavenProject project )
130         throws MojoExecutionException
131     {
132         if ( project.getArtifact() == null )
133         {
134             return;
135         }
136 
137         String prefix = "outputs.";
138         if ( !mono )
139         {
140             // aggregated buildinfo output
141             projectCount++;
142             prefix += projectCount + ".";
143             p.println();
144             p.println( prefix + "coordinates=" + project.getGroupId() + ':' + project.getArtifactId() );
145         }
146 
147         int n = 0;
148         if ( project.getArtifact().getFile() != null )
149         {
150             printArtifact( prefix, n++, project.getArtifact() );
151         }
152 
153         for ( Artifact attached : project.getAttachedArtifacts() )
154         {
155             if ( attached.getType().endsWith( ".asc" ) )
156             {
157                 // ignore pgp signatures
158                 continue;
159             }
160             if ( ignoreJavadoc && "javadoc".equals( attached.getClassifier() ) )
161             {
162                 // TEMPORARY ignore javadoc, waiting for MJAVADOC-627 in m-javadoc-p 3.2.0
163                 continue;
164             }
165             if ( isIgnore( attached ) )
166             {
167                 continue;
168             }
169             printArtifact( prefix, n++, attached );
170         }
171     }
172 
173     private void printArtifact( String prefix, int i, Artifact artifact )
174         throws MojoExecutionException
175     {
176         prefix = prefix + i;
177         if ( artifact.getFile().isDirectory() )
178         {
179             // edge case found in a distribution module with default packaging and skip set for
180             // m-jar-p: should use pom packaging instead
181             throw new MojoExecutionException( "Artifact " + artifact.getId() + " points to a directory: "
182                 + artifact.getFile() + ". Packaging should be 'pom'?" );
183         }
184         printFile( prefix, artifact.getFile(), getArtifactFilename( artifact ) );
185         artifacts.put( artifact, prefix );
186     }
187 
188     private String getArtifactFilename( Artifact artifact )
189     {
190         StringBuilder path = new StringBuilder( 128 );
191 
192         path.append( artifact.getArtifactId() ).append( '-' ).append( artifact.getBaseVersion() );
193 
194         if ( artifact.hasClassifier() )
195         {
196             path.append( '-' ).append( artifact.getClassifier() );
197         }
198 
199         ArtifactHandler artifactHandler = artifact.getArtifactHandler();
200 
201         if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )
202         {
203             path.append( '.' ).append( artifactHandler.getExtension() );
204         }
205 
206         return path.toString();
207     }
208 
209     void printFile( String prefix, File file )
210         throws MojoExecutionException
211     {
212         printFile( prefix, file, file.getName() );
213     }
214 
215     private void printFile( String prefix, File file, String filename )
216         throws MojoExecutionException
217     {
218         p.println();
219         p.println( prefix + ".filename=" + filename );
220         p.println( prefix + ".length=" + file.length() );
221         p.println( prefix + ".checksums.sha512=" + DigestHelper.calculateSha512( file ) );
222     }
223 
224     Map<Artifact, String> getArtifacts()
225     {
226         return artifacts;
227     }
228 
229     /**
230      * Load buildinfo file and extracts properties on output files.
231      *
232      * @param buildinfo the build info file
233      * @return output properties
234      * @throws MojoExecutionException
235      */
236     static Properties loadOutputProperties( File buildinfo )
237         throws MojoExecutionException
238     {
239         Properties prop = PropertyUtils.loadOptionalProperties( buildinfo );
240         for ( String name : prop.stringPropertyNames() )
241         {
242             if ( !name.startsWith( "outputs." ) || name.endsWith( ".coordinates" ) )
243             {
244                 prop.remove( name );
245             }
246         }
247         return prop;
248     }
249 
250     boolean getIgnoreJavadoc()
251     {
252         return ignoreJavadoc;
253     }
254 
255     void setIgnoreJavadoc( boolean ignoreJavadoc )
256     {
257         this.ignoreJavadoc = ignoreJavadoc;
258     }
259 
260     void setIgnore( Set<String> ignore )
261     {
262         this.ignore = ignore;
263     }
264 
265     private boolean isIgnore( Artifact attached )
266     {
267         String classifier = attached.getClassifier();
268         String extension = attached.getType();
269         String search = ( classifier == null ) ? "" : ( classifier + '.' ) + extension;
270         return ignore.contains( search );
271     }
272 
273     public void setToolchain( Toolchain toolchain )
274     {
275         this.toolchain = toolchain;
276     }
277 }