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 javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.nio.file.StandardCopyOption;
30 import java.time.Instant;
31 import java.time.format.DateTimeFormatter;
32 import java.util.Comparator;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.stream.Collectors;
36
37 import org.apache.commons.codec.digest.DigestUtils;
38 import org.apache.maven.RepositoryUtils;
39 import org.apache.maven.archiver.MavenArchiver;
40 import org.apache.maven.execution.MavenSession;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugins.annotations.Mojo;
43 import org.apache.maven.project.MavenProject;
44 import org.apache.maven.rtinfo.RuntimeInformation;
45 import org.apache.maven.shared.utils.logging.MessageUtils;
46 import org.apache.maven.toolchain.ToolchainManager;
47 import org.eclipse.aether.artifact.Artifact;
48 import org.eclipse.aether.artifact.DefaultArtifact;
49
50
51
52
53
54
55
56 @Mojo(name = "describe-build-output", aggregator = true, threadSafe = true)
57 public class DescribeBuildOutputMojo extends AbstractBuildinfoMojo {
58
59 @Inject
60 public DescribeBuildOutputMojo(
61 ToolchainManager toolchainManager,
62 RuntimeInformation rtInformation,
63 MavenProject project,
64 MavenSession session) {
65 super(toolchainManager, rtInformation, project, session);
66 }
67
68 @Override
69 public void execute() throws MojoExecutionException {
70
71 Instant timestamp =
72 MavenArchiver.parseBuildOutputTimestamp(outputTimestamp).orElse(null);
73 String effective = ((timestamp == null) ? "disabled" : DateTimeFormatter.ISO_INSTANT.format(timestamp));
74
75 diagnose(outputTimestamp, getLog(), project, session, effective);
76 getLog().info("");
77 describeBuildOutput();
78 }
79
80 private Path rootPath;
81 private BuildInfoWriter bi;
82
83 private void describeBuildOutput() throws MojoExecutionException {
84 rootPath = session.getTopLevelProject().getBasedir().toPath();
85 bi = newBuildInfoWriter(null, false);
86
87 Map<MavenProject, Long> reactorParents = session.getProjects().stream()
88 .collect(Collectors.groupingBy(
89 p -> DescribeBuildOutputMojo.getReactorParent(session, p), Collectors.counting()));
90 reactorParents.entrySet().stream()
91 .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
92 .forEach(e -> getLog().info("parent in reactor: " + e.getKey().getGroupId() + ":"
93 + e.getKey().getArtifactId() + " @ "
94 + rootPath.relativize(e.getKey().getFile().toPath()) + " (" + e.getValue() + " module"
95 + ((e.getValue() > 1) ? "s" : "") + "), property = "
96 + e.getKey().getProperties().get("project.build.outputTimestamp")));
97
98 getLog().info("");
99
100 Map<String, Long> groupIds = session.getProjects().stream()
101 .collect(Collectors.groupingBy(MavenProject::getGroupId, Collectors.counting()));
102 groupIds.entrySet().stream()
103 .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
104 .forEach(e -> getLog().info("groupId: " + e.getKey() + " (" + e.getValue() + " artifactId"
105 + ((e.getValue() > 1) ? "s" : "") + ")"));
106
107 Map<String, Set<String>> artifactIds = session.getProjects().stream()
108 .collect(Collectors.groupingBy(
109 MavenProject::getArtifactId, Collectors.mapping(MavenProject::getGroupId, Collectors.toSet())));
110 artifactIds.entrySet().stream()
111 .sorted(Map.Entry.comparingByKey())
112 .filter(e -> e.getValue().size() > 1)
113 .forEach(e ->
114 getLog().info("artifactId: " + e.getKey() + " defined for multiple groupIds: " + e.getValue()));
115
116 getLog().info("");
117 getLog().info(MessageUtils.buffer()
118 .a("skip/ignore? artifactId")
119 .strong("[:classifier][:extension]")
120 .a(" = build-path repository-filename size [sha256]")
121 .build());
122
123 for (MavenProject p : session.getProjects()) {
124 boolean skipped = isSkip(p);
125 String s = skipped ? "not-deployed " : " ";
126
127
128
129 Artifact consumerPom = RepositoryUtils.toArtifacts(p.getAttachedArtifacts()).stream()
130 .filter(a -> "pom".equals(a.getExtension()) && "consumer".equals(a.getClassifier()))
131 .findAny()
132 .orElse(null);
133
134 Artifact pomArtifact = new DefaultArtifact(p.getGroupId(), p.getArtifactId(), null, "pom", p.getVersion());
135 if (consumerPom != null) {
136
137
138 try {
139 Path pomFile = Files.createTempFile(Paths.get(p.getBuild().getDirectory()), "consumer-", ".pom");
140 Files.copy(consumerPom.getFile().toPath(), pomFile, StandardCopyOption.REPLACE_EXISTING);
141 pomArtifact = pomArtifact.setFile(pomFile.toFile());
142 getLog().info(s + describeArtifact(pomArtifact));
143
144 pomArtifact =
145 new DefaultArtifact(p.getGroupId(), p.getArtifactId(), "build", "pom", p.getVersion());
146 } catch (IOException e) {
147 throw new MojoExecutionException("Error processing consumer POM", e);
148 }
149 }
150 pomArtifact = pomArtifact.setFile(p.getFile());
151 getLog().info(s + describeArtifact(pomArtifact, skipped));
152
153
154 if (p.getArtifact().getFile() != null) {
155 getLog().info(s + describeArtifact(RepositoryUtils.toArtifact(p.getArtifact()), skipped));
156 }
157
158
159 for (Artifact a : RepositoryUtils.toArtifacts(p.getAttachedArtifacts())) {
160 if ("pom".equals(a.getExtension()) && "consumer".equals(a.getClassifier())) {
161
162 continue;
163 }
164 boolean ignored = skipped ? false : isIgnore(a);
165 String i = skipped ? s : (ignored ? "RB-ignored " : " ");
166 getLog().info(i + describeArtifact(a, skipped || ignored));
167 }
168 }
169 }
170
171 private boolean isIgnore(Artifact a) {
172 if (a.getExtension().endsWith(".asc")) {
173 return true;
174 }
175 if (bi.getIgnoreJavadoc() && "javadoc".equals(a.getClassifier())) {
176 return true;
177 }
178 return bi.isIgnore(a);
179 }
180
181 private String describeArtifact(Artifact a) throws MojoExecutionException {
182 return describeArtifact(a, false);
183 }
184
185 private String describeArtifact(Artifact a, boolean skipped) throws MojoExecutionException {
186 String sha256 = skipped ? "" : (" " + sha256(a.getFile()));
187 String ce = ("".equals(a.getClassifier()) ? "" : (':' + a.getClassifier()))
188 + ("jar".equals(a.getExtension()) ? "" : (":" + a.getExtension()));
189 String path = rootPath.relativize(a.getFile().toPath()).toString();
190 int i = path.indexOf("target/");
191 if (i >= 0) {
192 path = MessageUtils.buffer().mojo(path.substring(0, i + 7)).build() + path.substring(i + 7);
193 }
194 String remoteFilename = BuildInfoWriter.getArtifactFilename(a);
195 return a.getArtifactId()
196 + MessageUtils.buffer().strong(ce) + " = "
197 + path + " "
198 + (path.endsWith(remoteFilename)
199 ? "-"
200 : MessageUtils.buffer().strong(remoteFilename).build())
201 + " " + a.getFile().length() + sha256;
202 }
203
204 private String sha256(File file) throws MojoExecutionException {
205 try (InputStream is = Files.newInputStream(file.toPath())) {
206 return DigestUtils.sha256Hex(is);
207 } catch (IOException ioe) {
208 throw new MojoExecutionException("cannot read " + file, ioe);
209 }
210 }
211
212 static MavenProject getReactorParent(MavenSession session, MavenProject project) {
213 MavenProject reactorParent = project;
214 while (session.getProjects().contains(reactorParent.getParent())) {
215 reactorParent = reactorParent.getParent();
216 }
217 return reactorParent;
218 }
219
220 @Override
221 public void execute(Map<org.eclipse.aether.artifact.Artifact, String> artifacts) throws MojoExecutionException {
222
223 }
224 }