1 package org.apache.maven.plugins.artifact.buildinfo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.DefaultArtifact;
31 import org.apache.maven.artifact.handler.ArtifactHandler;
32 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugin.logging.Log;
35 import org.apache.maven.project.MavenProject;
36 import org.apache.maven.shared.utils.PropertyUtils;
37 import org.apache.maven.toolchain.Toolchain;
38
39
40
41
42 class BuildInfoWriter
43 {
44 private final Log log;
45 private final PrintWriter p;
46 private final boolean mono;
47 private final ArtifactHandlerManager artifactHandlerManager;
48 private final Map<Artifact, String> artifacts = new LinkedHashMap<>();
49 private int projectCount = -1;
50 private boolean ignoreJavadoc = true;
51 private Set<String> ignore;
52 private Toolchain toolchain;
53
54 BuildInfoWriter( Log log, PrintWriter p, boolean mono, ArtifactHandlerManager artifactHandlerManager )
55 {
56 this.log = log;
57 this.p = p;
58 this.mono = mono;
59 this.artifactHandlerManager = artifactHandlerManager;
60 }
61
62 void printHeader( MavenProject project, MavenProject aggregate, boolean reproducible )
63 {
64 p.println( "# https://reproducible-builds.org/docs/jvm/" );
65 p.println( "buildinfo.version=1.0-SNAPSHOT" );
66 p.println();
67 p.println( "name=" + project.getName() );
68 p.println( "group-id=" + project.getGroupId() );
69 p.println( "artifact-id=" + project.getArtifactId() );
70 p.println( "version=" + project.getVersion() );
71 p.println();
72 printSourceInformation( project );
73 p.println();
74 p.println( "# build instructions" );
75 p.println( "build-tool=mvn" );
76
77 p.println();
78 if ( reproducible )
79 {
80 p.println( "# build environment information (simplified for reproducibility)" );
81 p.println( "java.version=" + extractJavaMajorVersion( System.getProperty( "java.version" ) ) );
82 String ls = System.getProperty( "line.separator" );
83 p.println( "os.name=" + ( "\n".equals( ls ) ? "Unix" : "Windows" ) );
84 }
85 else
86 {
87 p.println( "# effective build environment information" );
88 p.println( "java.version=" + System.getProperty( "java.version" ) );
89 p.println( "java.vendor=" + System.getProperty( "java.vendor" ) );
90 p.println( "os.name=" + System.getProperty( "os.name" ) );
91 }
92 p.println();
93 p.println( "# Maven rebuild instructions and effective environment" );
94 if ( !reproducible )
95 {
96 p.println( "mvn.version=" + MavenVersion.createMavenVersionString() );
97 }
98 if ( ( project.getPrerequisites() != null ) && ( project.getPrerequisites().getMaven() != null ) )
99 {
100
101 p.println( "mvn.minimum.version=" + project.getPrerequisites().getMaven() );
102 }
103 if ( toolchain != null )
104 {
105 String javaVersion = JdkToolchainUtil.getJavaVersion( toolchain );
106 if ( reproducible )
107 {
108 javaVersion = extractJavaMajorVersion( javaVersion );
109 }
110 p.println( "mvn.toolchain.jdk=" + javaVersion );
111 }
112
113 if ( !mono && ( aggregate != null ) )
114 {
115 p.println( "mvn.aggregate.artifact-id=" + aggregate.getArtifactId() );
116 }
117
118 p.println();
119 p.println( "# " + ( mono ? "" : "aggregated " ) + "output" );
120 }
121
122 private static String extractJavaMajorVersion( String javaVersion )
123 {
124 if ( javaVersion.startsWith( "1." ) )
125 {
126 javaVersion = javaVersion.substring( 2 );
127 }
128 int index = javaVersion.indexOf( '.' );
129 if ( index < 0 )
130 {
131 index = javaVersion.indexOf( '-' );
132 }
133 return ( index < 0 ) ? javaVersion : javaVersion.substring( 0, index );
134 }
135
136 private void printSourceInformation( MavenProject project )
137 {
138 boolean sourceAvailable = false;
139 p.println( "# source information" );
140
141 if ( project.getScm() != null )
142 {
143 sourceAvailable = true;
144 p.println( "source.scm.uri=" + project.getScm().getConnection() );
145 p.println( "source.scm.tag=" + project.getScm().getTag() );
146 if ( project.getArtifact().isSnapshot() )
147 {
148 log.warn( "SCM source tag in buildinfo source.scm.tag=" + project.getScm().getTag()
149 + " does not permit rebuilders reproducible source checkout" );
150
151 }
152 }
153 else
154 {
155 p.println( "# no scm configured in pom.xml" );
156 }
157
158 if ( !sourceAvailable )
159 {
160 log.warn( "No source information available in buildinfo for rebuilders..." );
161 }
162 }
163
164 void printArtifacts( MavenProject project )
165 throws MojoExecutionException
166 {
167 String prefix = "outputs.";
168 if ( !mono )
169 {
170
171 projectCount++;
172 prefix += projectCount + ".";
173 p.println();
174 p.println( prefix + "coordinates=" + project.getGroupId() + ':' + project.getArtifactId() );
175 }
176
177 int n = 0;
178 Artifact pomArtifact =
179 new DefaultArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null, "pom", "",
180 artifactHandlerManager.getArtifactHandler( "pom" ) );
181 pomArtifact.setFile( project.getFile() );
182
183 artifacts.put( pomArtifact, prefix + n );
184 printFile( prefix + n++, project.getFile(), project.getArtifactId() + '-' + project.getVersion() + ".pom" );
185
186 if ( project.getArtifact() == null )
187 {
188 return;
189 }
190
191 if ( project.getArtifact().getFile() != null )
192 {
193 printArtifact( prefix, n++, project.getArtifact() );
194 }
195
196 for ( Artifact attached : project.getAttachedArtifacts() )
197 {
198 if ( attached.getType().endsWith( ".asc" ) )
199 {
200
201 continue;
202 }
203 if ( ignoreJavadoc && "javadoc".equals( attached.getClassifier() ) )
204 {
205
206 continue;
207 }
208 if ( isIgnore( attached ) )
209 {
210 continue;
211 }
212 printArtifact( prefix, n++, attached );
213 }
214 }
215
216 private void printArtifact( String prefix, int i, Artifact artifact )
217 throws MojoExecutionException
218 {
219 prefix = prefix + i;
220 File artifactFile = artifact.getFile();
221 if ( artifactFile.isDirectory() )
222 {
223 if ( "pom".equals( artifact.getType() ) )
224 {
225
226 return;
227 }
228
229
230 throw new MojoExecutionException( "Artifact " + artifact.getId() + " points to a directory: "
231 + artifactFile + ". Packaging should be 'pom'?" );
232 }
233 if ( !artifactFile.isFile() )
234 {
235 log.warn( "Ignoring artifact " + artifact.getId() + " because it points to inexistent " + artifactFile );
236 return;
237 }
238
239 printFile( prefix, artifact.getFile(), getArtifactFilename( artifact ) );
240 artifacts.put( artifact, prefix );
241 }
242
243 private String getArtifactFilename( Artifact artifact )
244 {
245 StringBuilder path = new StringBuilder( 128 );
246
247 path.append( artifact.getArtifactId() ).append( '-' ).append( artifact.getBaseVersion() );
248
249 if ( artifact.hasClassifier() )
250 {
251 path.append( '-' ).append( artifact.getClassifier() );
252 }
253
254 ArtifactHandler artifactHandler = artifact.getArtifactHandler();
255
256 if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )
257 {
258 path.append( '.' ).append( artifactHandler.getExtension() );
259 }
260
261 return path.toString();
262 }
263
264 void printFile( String prefix, File file )
265 throws MojoExecutionException
266 {
267 printFile( prefix, file, file.getName() );
268 }
269
270 private void printFile( String prefix, File file, String filename )
271 throws MojoExecutionException
272 {
273 p.println();
274 p.println( prefix + ".filename=" + filename );
275 p.println( prefix + ".length=" + file.length() );
276 p.println( prefix + ".checksums.sha512=" + DigestHelper.calculateSha512( file ) );
277 }
278
279 Map<Artifact, String> getArtifacts()
280 {
281 return artifacts;
282 }
283
284
285
286
287
288
289
290
291 static Properties loadOutputProperties( File buildinfo )
292 throws MojoExecutionException
293 {
294 Properties prop = PropertyUtils.loadOptionalProperties( buildinfo );
295 for ( String name : prop.stringPropertyNames() )
296 {
297 if ( !name.startsWith( "outputs." ) || name.endsWith( ".coordinates" ) )
298 {
299 prop.remove( name );
300 }
301 }
302 return prop;
303 }
304
305 boolean getIgnoreJavadoc()
306 {
307 return ignoreJavadoc;
308 }
309
310 void setIgnoreJavadoc( boolean ignoreJavadoc )
311 {
312 this.ignoreJavadoc = ignoreJavadoc;
313 }
314
315 void setIgnore( Set<String> ignore )
316 {
317 this.ignore = ignore;
318 }
319
320 private boolean isIgnore( Artifact attached )
321 {
322 String classifier = attached.getClassifier();
323 String extension = attached.getType();
324 String search = ( classifier == null ) ? "" : ( classifier + '.' ) + extension;
325 return ignore.contains( search );
326 }
327
328 public void setToolchain( Toolchain toolchain )
329 {
330 this.toolchain = toolchain;
331 }
332 }