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.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
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, boolean reproducible )
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
73 p.println();
74 if ( reproducible )
75 {
76 p.println( "# build environment information (simplified for reproducibility)" );
77 p.println( "java.version=" + extractJavaMajorVersion( System.getProperty( "java.version" ) ) );
78 String ls = System.getProperty( "line.separator" );
79 p.println( "os.name=" + ( "\n".equals( ls ) ? "Unix" : "Windows" ) );
80 }
81 else
82 {
83 p.println( "# effective build environment information" );
84 p.println( "java.version=" + System.getProperty( "java.version" ) );
85 p.println( "java.vendor=" + System.getProperty( "java.vendor" ) );
86 p.println( "os.name=" + System.getProperty( "os.name" ) );
87 }
88 p.println();
89 p.println( "# Maven rebuild instructions and effective environment" );
90
91 if ( !reproducible )
92 {
93 p.println( "mvn.version=" + MavenVersion.createMavenVersionString() );
94 }
95 if ( ( project.getPrerequisites() != null ) && ( project.getPrerequisites().getMaven() != null ) )
96 {
97
98 p.println( "mvn.minimum.version=" + project.getPrerequisites().getMaven() );
99 }
100 if ( toolchain != null )
101 {
102 String javaVersion = JdkToolchainUtil.getJavaVersion( toolchain );
103 if ( reproducible )
104 {
105 javaVersion = extractJavaMajorVersion( javaVersion );
106 }
107 p.println( "mvn.toolchain.jdk=" + javaVersion );
108 }
109
110 if ( !mono && ( aggregate != null ) )
111 {
112 p.println( "mvn.aggregate.artifact-id=" + aggregate.getArtifactId() );
113 }
114
115 p.println();
116 p.println( "# " + ( mono ? "" : "aggregated " ) + "output" );
117 }
118
119 private static String extractJavaMajorVersion( String javaVersion )
120 {
121 if ( javaVersion.startsWith( "1." ) )
122 {
123 javaVersion = javaVersion.substring( 2 );
124 }
125 return javaVersion.substring( 0, javaVersion.indexOf( '.' ) );
126 }
127
128 private void printSourceInformation( MavenProject project )
129 {
130 boolean sourceAvailable = false;
131 p.println( "# source information" );
132
133 if ( project.getScm() != null )
134 {
135 sourceAvailable = true;
136 p.println( "source.scm.uri=" + project.getScm().getConnection() );
137 p.println( "source.scm.tag=" + project.getScm().getTag() );
138 if ( project.getArtifact().isSnapshot() )
139 {
140 log.warn( "SCM source tag in buildinfo source.scm.tag=" + project.getScm().getTag()
141 + " does not permit rebuilders reproducible source checkout" );
142
143 }
144 }
145 else
146 {
147 p.println( "# no scm configured in pom.xml" );
148 }
149
150 if ( !sourceAvailable )
151 {
152 log.warn( "No source information available in buildinfo for rebuilders..." );
153 }
154 }
155
156 void printArtifacts( MavenProject project )
157 throws MojoExecutionException
158 {
159 if ( project.getArtifact() == null )
160 {
161 return;
162 }
163
164 String prefix = "outputs.";
165 if ( !mono )
166 {
167
168 projectCount++;
169 prefix += projectCount + ".";
170 p.println();
171 p.println( prefix + "coordinates=" + project.getGroupId() + ':' + project.getArtifactId() );
172 }
173
174 int n = 0;
175 if ( project.getArtifact().getFile() != null )
176 {
177 printArtifact( prefix, n++, project.getArtifact() );
178 }
179
180 for ( Artifact attached : project.getAttachedArtifacts() )
181 {
182 if ( attached.getType().endsWith( ".asc" ) )
183 {
184
185 continue;
186 }
187 if ( ignoreJavadoc && "javadoc".equals( attached.getClassifier() ) )
188 {
189
190 continue;
191 }
192 if ( isIgnore( attached ) )
193 {
194 continue;
195 }
196 printArtifact( prefix, n++, attached );
197 }
198 }
199
200 private void printArtifact( String prefix, int i, Artifact artifact )
201 throws MojoExecutionException
202 {
203 prefix = prefix + i;
204 if ( artifact.getFile().isDirectory() )
205 {
206
207
208 throw new MojoExecutionException( "Artifact " + artifact.getId() + " points to a directory: "
209 + artifact.getFile() + ". Packaging should be 'pom'?" );
210 }
211 printFile( prefix, artifact.getFile(), getArtifactFilename( artifact ) );
212 artifacts.put( artifact, prefix );
213 }
214
215 private String getArtifactFilename( Artifact artifact )
216 {
217 StringBuilder path = new StringBuilder( 128 );
218
219 path.append( artifact.getArtifactId() ).append( '-' ).append( artifact.getBaseVersion() );
220
221 if ( artifact.hasClassifier() )
222 {
223 path.append( '-' ).append( artifact.getClassifier() );
224 }
225
226 ArtifactHandler artifactHandler = artifact.getArtifactHandler();
227
228 if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )
229 {
230 path.append( '.' ).append( artifactHandler.getExtension() );
231 }
232
233 return path.toString();
234 }
235
236 void printFile( String prefix, File file )
237 throws MojoExecutionException
238 {
239 printFile( prefix, file, file.getName() );
240 }
241
242 private void printFile( String prefix, File file, String filename )
243 throws MojoExecutionException
244 {
245 p.println();
246 p.println( prefix + ".filename=" + filename );
247 p.println( prefix + ".length=" + file.length() );
248 p.println( prefix + ".checksums.sha512=" + DigestHelper.calculateSha512( file ) );
249 }
250
251 Map<Artifact, String> getArtifacts()
252 {
253 return artifacts;
254 }
255
256
257
258
259
260
261
262
263 static Properties loadOutputProperties( File buildinfo )
264 throws MojoExecutionException
265 {
266 Properties prop = PropertyUtils.loadOptionalProperties( buildinfo );
267 for ( String name : prop.stringPropertyNames() )
268 {
269 if ( !name.startsWith( "outputs." ) || name.endsWith( ".coordinates" ) )
270 {
271 prop.remove( name );
272 }
273 }
274 return prop;
275 }
276
277 boolean getIgnoreJavadoc()
278 {
279 return ignoreJavadoc;
280 }
281
282 void setIgnoreJavadoc( boolean ignoreJavadoc )
283 {
284 this.ignoreJavadoc = ignoreJavadoc;
285 }
286
287 void setIgnore( Set<String> ignore )
288 {
289 this.ignore = ignore;
290 }
291
292 private boolean isIgnore( Artifact attached )
293 {
294 String classifier = attached.getClassifier();
295 String extension = attached.getType();
296 String search = ( classifier == null ) ? "" : ( classifier + '.' ) + extension;
297 return ignore.contains( search );
298 }
299
300 public void setToolchain( Toolchain toolchain )
301 {
302 this.toolchain = toolchain;
303 }
304 }