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 )
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 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
81 p.println( "mvn.version=" + MavenVersion.createMavenVersionString() );
82 if ( ( project.getPrerequisites() != null ) && ( project.getPrerequisites().getMaven() != null ) )
83 {
84
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
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
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
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
158 continue;
159 }
160 if ( ignoreJavadoc && "javadoc".equals( attached.getClassifier() ) )
161 {
162
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
180
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
231
232
233
234
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 }