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 org.apache.maven.archiver.MavenArchiver;
23 import org.apache.maven.artifact.Artifact;
24 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
25 import org.apache.maven.execution.MavenSession;
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugins.annotations.Component;
29 import org.apache.maven.plugins.annotations.Parameter;
30 import org.apache.maven.project.MavenProject;
31 import org.apache.maven.shared.utils.io.FileUtils;
32 import org.apache.maven.toolchain.Toolchain;
33 import org.apache.maven.toolchain.ToolchainManager;
34
35 import java.io.BufferedWriter;
36 import java.io.File;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.OutputStreamWriter;
40 import java.io.PrintWriter;
41 import java.nio.charset.StandardCharsets;
42 import java.text.SimpleDateFormat;
43 import java.util.Date;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47
48
49
50
51
52
53 public abstract class AbstractBuildinfoMojo
54 extends AbstractMojo
55 {
56
57
58
59 @Parameter( defaultValue = "${project}", readonly = true )
60 protected MavenProject project;
61
62
63
64
65 @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
66 protected List<MavenProject> reactorProjects;
67
68
69
70
71 @Parameter( defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}.buildinfo",
72 required = true, readonly = true )
73 protected File buildinfoFile;
74
75
76
77
78 @Parameter( property = "buildinfo.ignoreJavadoc", defaultValue = "true" )
79 private boolean ignoreJavadoc;
80
81
82
83
84 @Parameter( property = "buildinfo.ignore", defaultValue = "" )
85 private Set<String> ignore;
86
87
88
89
90 @Parameter( property = "buildinfo.detect.skip", defaultValue = "true" )
91 private boolean detectSkip;
92
93
94
95
96
97
98
99 @Parameter( property = "buildinfo.reproducible", defaultValue = "false" )
100 private boolean reproducible;
101
102
103
104
105 @Parameter( defaultValue = "${session}", readonly = true, required = true )
106 private MavenSession session;
107
108
109
110
111
112
113
114
115 @Parameter( defaultValue = "${project.build.outputTimestamp}" )
116 private String outputTimestamp;
117
118
119
120
121 @Component
122 private ToolchainManager toolchainManager;
123
124 @Component
125 protected ArtifactHandlerManager artifactHandlerManager;
126
127 @Override
128 public void execute()
129 throws MojoExecutionException
130 {
131 boolean mono = reactorProjects.size() == 1;
132
133 MavenArchiver archiver = new MavenArchiver();
134 Date timestamp = archiver.parseOutputTimestamp( outputTimestamp );
135 if ( timestamp == null )
136 {
137 getLog().warn( "Reproducible Build not activated by project.build.outputTimestamp property: "
138 + "see https://maven.apache.org/guides/mini/guide-reproducible-builds.html" );
139 }
140 else
141 {
142 if ( getLog().isDebugEnabled() )
143 {
144 getLog().debug( "project.build.outputTimestamp = \"" + outputTimestamp + "\" => "
145 + new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssXXX" ).format( timestamp ) );
146 }
147
148
149 MavenProject reactorParent = project;
150 while ( reactorProjects.contains( reactorParent.getParent() ) )
151 {
152 reactorParent = reactorParent.getParent();
153 }
154 String prop =
155 reactorParent.getOriginalModel().getProperties().getProperty( "project.build.outputTimestamp" );
156 if ( prop == null )
157 {
158 getLog().warn( "project.build.outputTimestamp property should not be inherited "
159 + "but defined in parent POM from reactor " + reactorParent.getFile() );
160 }
161 }
162
163 if ( !mono )
164 {
165
166 if ( isSkip( project ) )
167 {
168 getLog().info( "Skipping goal because module skips install and/or deploy" );
169 return;
170 }
171
172 MavenProject last = getLastProject();
173 if ( project != last )
174 {
175 skip( last );
176 return;
177 }
178 }
179
180
181 Map<Artifact, String> artifacts = generateBuildinfo( mono );
182 getLog().info( "Saved " + ( mono ? "" : "aggregate " ) + "info on build to " + buildinfoFile );
183
184 copyAggregateToRoot( buildinfoFile );
185
186 execute( artifacts );
187 }
188
189
190
191
192
193
194
195 abstract void execute( Map<Artifact, String> artifacts )
196 throws MojoExecutionException;
197
198 protected void skip( MavenProject last )
199 throws MojoExecutionException
200 {
201 getLog().info( "Skipping intermediate goal run, aggregate will be " + last.getArtifactId() );
202 }
203
204 protected void copyAggregateToRoot( File aggregate )
205 throws MojoExecutionException
206 {
207 if ( reactorProjects.size() == 1 )
208 {
209
210 return;
211 }
212
213
214 MavenProject root = getExecutionRoot();
215 String extension = aggregate.getName().substring( aggregate.getName().lastIndexOf( '.' ) );
216 File rootCopy = new File( root.getBuild().getDirectory(),
217 root.getArtifactId() + '-' + root.getVersion() + extension );
218 try
219 {
220 FileUtils.copyFile( aggregate, rootCopy );
221 getLog().info( "Aggregate " + extension.substring( 1 ) + " copied to " + rootCopy );
222 }
223 catch ( IOException ioe )
224 {
225 throw new MojoExecutionException( "Could not copy " + aggregate + "to " + rootCopy );
226 }
227 }
228
229
230
231
232
233
234
235
236
237 protected Map<Artifact, String> generateBuildinfo( boolean mono )
238 throws MojoExecutionException
239 {
240 MavenProject root = mono ? project : getExecutionRoot();
241
242 buildinfoFile.getParentFile().mkdirs();
243
244 try ( PrintWriter p = new PrintWriter( new BufferedWriter(
245 new OutputStreamWriter( new FileOutputStream( buildinfoFile ), StandardCharsets.UTF_8 ) ) ) )
246 {
247 BuildInfoWriter bi = new BuildInfoWriter( getLog(), p, mono, artifactHandlerManager );
248 bi.setIgnoreJavadoc( ignoreJavadoc );
249 bi.setIgnore( ignore );
250 bi.setToolchain( getToolchain() );
251
252 bi.printHeader( root, mono ? null : project, reproducible );
253
254
255 if ( mono )
256 {
257 bi.printArtifacts( project );
258 }
259 else
260 {
261 for ( MavenProject project : reactorProjects )
262 {
263 if ( !isSkip( project ) )
264 {
265 bi.printArtifacts( project );
266 }
267 }
268 }
269
270 if ( p.checkError() )
271 {
272 throw new MojoExecutionException( "Write error to " + buildinfoFile );
273 }
274
275 return bi.getArtifacts();
276 }
277 catch ( IOException e )
278 {
279 throw new MojoExecutionException( "Error creating file " + buildinfoFile, e );
280 }
281 }
282
283 protected MavenProject getExecutionRoot()
284 {
285 for ( MavenProject p : reactorProjects )
286 {
287 if ( p.isExecutionRoot() )
288 {
289 return p;
290 }
291 }
292 return null;
293 }
294
295 private MavenProject getLastProject()
296 {
297 int i = reactorProjects.size();
298 while ( i > 0 )
299 {
300 MavenProject project = reactorProjects.get( --i );
301 if ( !isSkip( project ) )
302 {
303 return project;
304 }
305 }
306 return null;
307 }
308
309 private boolean isSkip( MavenProject project )
310 {
311 return detectSkip && PluginUtil.isSkip( project );
312 }
313
314 private Toolchain getToolchain()
315 {
316 Toolchain tc = null;
317 if ( toolchainManager != null )
318 {
319 tc = toolchainManager.getToolchainFromBuildContext( "jdk", session );
320 }
321
322 return tc;
323 }
324 }