1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.artifact.buildinfo;
20
21 import java.io.BufferedWriter;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStreamWriter;
26 import java.io.PrintWriter;
27 import java.nio.charset.StandardCharsets;
28 import java.text.SimpleDateFormat;
29 import java.util.Date;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33
34 import org.apache.maven.archiver.MavenArchiver;
35 import org.apache.maven.artifact.Artifact;
36 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
37 import org.apache.maven.execution.MavenSession;
38 import org.apache.maven.plugin.AbstractMojo;
39 import org.apache.maven.plugin.MojoExecutionException;
40 import org.apache.maven.plugins.annotations.Component;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.project.MavenProject;
43 import org.apache.maven.rtinfo.RuntimeInformation;
44 import org.apache.maven.shared.utils.io.FileUtils;
45 import org.apache.maven.toolchain.Toolchain;
46 import org.apache.maven.toolchain.ToolchainManager;
47
48
49
50
51
52
53 public abstract class AbstractBuildinfoMojo extends AbstractMojo {
54
55
56
57 @Parameter(defaultValue = "${project}", readonly = true)
58 protected MavenProject project;
59
60
61
62
63 @Parameter(defaultValue = "${reactorProjects}", required = true, readonly = true)
64 protected List<MavenProject> reactorProjects;
65
66
67
68
69 @Parameter(
70 defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}.buildinfo",
71 required = true,
72 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 @Component
128 protected RuntimeInformation rtInformation;
129
130 @Override
131 public void execute() throws MojoExecutionException {
132 boolean mono = reactorProjects.size() == 1;
133
134 MavenArchiver archiver = new MavenArchiver();
135 Date timestamp = archiver.parseOutputTimestamp(outputTimestamp);
136 if (timestamp == null) {
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 } else {
140 if (getLog().isDebugEnabled()) {
141 getLog().debug("project.build.outputTimestamp = \"" + outputTimestamp + "\" => "
142 + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(timestamp));
143 }
144
145
146 boolean parentInReactor = false;
147 MavenProject reactorParent = project;
148 while (reactorProjects.contains(reactorParent.getParent())) {
149 parentInReactor = true;
150 reactorParent = reactorParent.getParent();
151 }
152 String prop = reactorParent.getOriginalModel().getProperties().getProperty("project.build.outputTimestamp");
153 if (prop == null) {
154 getLog().error("project.build.outputTimestamp property should not be inherited but defined in "
155 + (parentInReactor ? "parent POM from reactor " : "POM ") + reactorParent.getFile());
156 }
157 }
158
159 if (!mono) {
160
161 if (isSkip(project)) {
162 getLog().info("Skipping goal because module skips install and/or deploy");
163 return;
164 }
165
166 MavenProject last = getLastProject();
167 if (project != last) {
168 skip(last);
169 return;
170 }
171 }
172
173
174 Map<Artifact, String> artifacts = generateBuildinfo(mono);
175 getLog().info("Saved " + (mono ? "" : "aggregate ") + "info on build to " + buildinfoFile);
176
177 copyAggregateToRoot(buildinfoFile);
178
179 execute(artifacts);
180 }
181
182
183
184
185
186
187
188 abstract void execute(Map<Artifact, String> artifacts) throws MojoExecutionException;
189
190 protected void skip(MavenProject last) throws MojoExecutionException {
191 getLog().info("Skipping intermediate goal run, aggregate will be " + last.getArtifactId());
192 }
193
194 protected void copyAggregateToRoot(File aggregate) throws MojoExecutionException {
195 if (reactorProjects.size() == 1) {
196
197 return;
198 }
199
200
201 MavenProject root = getExecutionRoot();
202 String extension = aggregate.getName().substring(aggregate.getName().lastIndexOf('.'));
203 File rootCopy =
204 new File(root.getBuild().getDirectory(), root.getArtifactId() + '-' + root.getVersion() + extension);
205 try {
206 FileUtils.copyFile(aggregate, rootCopy);
207 getLog().info("Aggregate " + extension.substring(1) + " copied to " + rootCopy);
208 } catch (IOException ioe) {
209 throw new MojoExecutionException("Could not copy " + aggregate + "to " + rootCopy);
210 }
211 }
212
213
214
215
216
217
218
219
220
221 protected Map<Artifact, String> generateBuildinfo(boolean mono) throws MojoExecutionException {
222 MavenProject root = mono ? project : getExecutionRoot();
223
224 buildinfoFile.getParentFile().mkdirs();
225
226 try (PrintWriter p = new PrintWriter(new BufferedWriter(
227 new OutputStreamWriter(new FileOutputStream(buildinfoFile), StandardCharsets.UTF_8)))) {
228 BuildInfoWriter bi = new BuildInfoWriter(getLog(), p, mono, artifactHandlerManager, rtInformation);
229 bi.setIgnoreJavadoc(ignoreJavadoc);
230 bi.setIgnore(ignore);
231 bi.setToolchain(getToolchain());
232
233 bi.printHeader(root, mono ? null : project, reproducible);
234
235
236 if (mono) {
237 bi.printArtifacts(project);
238 } else {
239 for (MavenProject project : reactorProjects) {
240 if (!isSkip(project)) {
241 bi.printArtifacts(project);
242 }
243 }
244 }
245
246 if (p.checkError()) {
247 throw new MojoExecutionException("Write error to " + buildinfoFile);
248 }
249
250 return bi.getArtifacts();
251 } catch (IOException e) {
252 throw new MojoExecutionException("Error creating file " + buildinfoFile, e);
253 }
254 }
255
256 protected MavenProject getExecutionRoot() {
257 for (MavenProject p : reactorProjects) {
258 if (p.isExecutionRoot()) {
259 return p;
260 }
261 }
262 return null;
263 }
264
265 private MavenProject getLastProject() {
266 int i = reactorProjects.size();
267 while (i > 0) {
268 MavenProject project = reactorProjects.get(--i);
269 if (!isSkip(project)) {
270 return project;
271 }
272 }
273 return null;
274 }
275
276 private boolean isSkip(MavenProject project) {
277 return detectSkip && PluginUtil.isSkip(project);
278 }
279
280 private Toolchain getToolchain() {
281 Toolchain tc = null;
282 if (toolchainManager != null) {
283 tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
284 }
285
286 return tc;
287 }
288 }