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.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.nio.file.Files;
25 import java.util.HashSet;
26 import java.util.Map;
27 import java.util.Properties;
28 import java.util.Set;
29
30 import org.apache.maven.artifact.versioning.ArtifactVersion;
31 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
32 import org.apache.maven.execution.MavenSession;
33 import org.apache.maven.lifecycle.LifecycleExecutor;
34 import org.apache.maven.lifecycle.MavenExecutionPlan;
35 import org.apache.maven.model.Plugin;
36 import org.apache.maven.plugin.AbstractMojo;
37 import org.apache.maven.plugin.MojoExecution;
38 import org.apache.maven.plugin.MojoExecutionException;
39 import org.apache.maven.plugins.annotations.Component;
40 import org.apache.maven.plugins.annotations.Mojo;
41 import org.apache.maven.plugins.annotations.Parameter;
42 import org.apache.maven.project.MavenProject;
43
44
45
46
47
48
49 @Mojo(name = "check-buildplan", threadSafe = true, requiresProject = true)
50 public class CheckBuildPlanMojo extends AbstractMojo {
51 @Component
52 private MavenProject project;
53
54 @Component
55 private MavenSession session;
56
57 @Component
58 private LifecycleExecutor lifecycleExecutor;
59
60
61 @Parameter(property = "check.buildplan.tasks", defaultValue = "deploy")
62 private String[] tasks;
63
64
65
66
67
68
69 @Parameter(defaultValue = "${project.build.outputTimestamp}")
70 private String outputTimestamp;
71
72
73
74
75 @Parameter(property = "check.plugin-issues")
76 private File pluginIssues;
77
78
79
80
81 @Parameter(property = "check.failOnNonReproducible", defaultValue = "true")
82 private boolean failOnNonReproducible;
83
84 protected MavenExecutionPlan calculateExecutionPlan() throws MojoExecutionException {
85 try {
86 return lifecycleExecutor.calculateExecutionPlan(session, tasks);
87 } catch (Exception e) {
88 throw new MojoExecutionException("Cannot calculate Maven execution plan" + e.getMessage(), e);
89 }
90 }
91
92 @Override
93 public void execute() throws MojoExecutionException {
94 boolean fail =
95 AbstractBuildinfoMojo.hasBadOutputTimestamp(outputTimestamp, getLog(), project, session.getProjects());
96
97
98
99 Properties issues = loadIssues();
100
101 MavenExecutionPlan plan = calculateExecutionPlan();
102
103 Set<String> plugins = new HashSet<>();
104 for (MojoExecution exec : plan.getMojoExecutions()) {
105 Plugin plugin = exec.getPlugin();
106 String id = plugin.getId();
107
108 if (plugins.add(id)) {
109
110 String issue = issues.getProperty(plugin.getKey());
111 if (issue == null) {
112 getLog().info("no known issue with " + id);
113 } else if (issue.startsWith("fail:")) {
114 String logMessage = "plugin without solution " + id + ", see " + issue.substring(5);
115 if (failOnNonReproducible) {
116 getLog().error(logMessage);
117 } else {
118 getLog().warn(logMessage);
119 }
120 fail = true;
121
122 } else {
123 ArtifactVersion minimum = new DefaultArtifactVersion(issue);
124 ArtifactVersion version = new DefaultArtifactVersion(plugin.getVersion());
125 if (version.compareTo(minimum) < 0) {
126 String logMessage = "plugin with non-reproducible output: " + id + ", require minimum " + issue;
127 if (failOnNonReproducible) {
128 getLog().error(logMessage);
129 } else {
130 getLog().warn(logMessage);
131 }
132 fail = true;
133 } else {
134 getLog().info("no known issue with " + id + " (>= " + issue + ")");
135 }
136 }
137 }
138 }
139
140 if (fail) {
141 getLog().info("current module pom.xml is " + project.getBasedir() + "/pom.xml");
142 MavenProject parent = project;
143 while (true) {
144 parent = parent.getParent();
145 if ((parent == null) || !session.getProjects().contains(parent)) {
146 break;
147 }
148 getLog().info(" parent pom.xml is " + parent.getBasedir() + "/pom.xml");
149 }
150 String message = "non-reproducible plugin or configuration found with fix available";
151 if (failOnNonReproducible) {
152 throw new MojoExecutionException(message);
153 } else {
154 getLog().warn(message);
155 }
156 }
157 }
158
159 private Properties loadIssues() throws MojoExecutionException {
160 try (InputStream in = (pluginIssues == null)
161 ? getClass().getResourceAsStream("not-reproducible-plugins.properties")
162 : Files.newInputStream(pluginIssues.toPath())) {
163 Properties prop = new Properties();
164 prop.load(in);
165
166 Properties result = new Properties();
167 for (Map.Entry<Object, Object> entry : prop.entrySet()) {
168 String plugin = entry.getKey().toString().replace('+', ':');
169 if (!plugin.contains(":")) {
170 plugin = "org.apache.maven.plugins:" + plugin;
171 }
172 result.put(plugin, entry.getValue());
173 }
174 return result;
175 } catch (IOException ioe) {
176 throw new MojoExecutionException("Cannot load issues file", ioe);
177 }
178 }
179 }