1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.dependency.fromDependencies;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.Writer;
26 import java.nio.charset.Charset;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Files;
29 import java.util.ArrayList;
30 import java.util.Comparator;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Objects;
34 import java.util.Set;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37 import java.util.stream.Collectors;
38 import java.util.stream.Stream;
39
40 import org.apache.commons.lang3.StringUtils;
41 import org.apache.maven.artifact.Artifact;
42 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
43 import org.apache.maven.execution.MavenSession;
44 import org.apache.maven.plugin.MojoExecutionException;
45 import org.apache.maven.plugins.annotations.LifecyclePhase;
46 import org.apache.maven.plugins.annotations.Mojo;
47 import org.apache.maven.plugins.annotations.Parameter;
48 import org.apache.maven.plugins.annotations.ResolutionScope;
49 import org.apache.maven.plugins.dependency.utils.DependencyUtil;
50 import org.apache.maven.plugins.dependency.utils.ResolverUtil;
51 import org.apache.maven.project.MavenProject;
52 import org.apache.maven.project.MavenProjectHelper;
53 import org.apache.maven.project.ProjectBuilder;
54 import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
55 import org.sonatype.plexus.build.incremental.BuildContext;
56
57
58
59
60
61
62
63 @Mojo(
64 name = "build-classpath",
65 requiresDependencyResolution = ResolutionScope.TEST,
66 defaultPhase = LifecyclePhase.GENERATE_SOURCES,
67 threadSafe = true)
68 public class BuildClasspathMojo extends AbstractDependencyFilterMojo implements Comparator<Artifact> {
69
70 @Parameter(property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}")
71 private String outputEncoding;
72
73
74
75
76 @Parameter(property = "mdep.stripVersion", defaultValue = "false")
77 private boolean stripVersion = false;
78
79
80
81
82 @Parameter(property = "mdep.stripClassifier", defaultValue = "false")
83 private boolean stripClassifier = false;
84
85
86
87
88
89 @Parameter(property = "mdep.prefix")
90 private String prefix;
91
92
93
94
95
96 @Parameter(property = "mdep.outputProperty")
97 private String outputProperty;
98
99
100
101
102
103 @Parameter(property = "mdep.outputFile")
104 private File outputFile;
105
106
107
108
109 @Parameter(property = "mdep.regenerateFile", defaultValue = "false")
110 private boolean regenerateFile;
111
112
113
114
115
116
117
118
119 @Parameter(property = "mdep.fileSeparator", defaultValue = "")
120 private String fileSeparator;
121
122
123
124
125
126
127
128
129
130 @Parameter(property = "mdep.pathSeparator", defaultValue = "")
131 private String pathSeparator;
132
133
134
135
136
137
138
139 @Parameter(property = "mdep.localRepoProperty", defaultValue = "")
140 private String localRepoProperty;
141
142
143
144
145
146
147 @Parameter(defaultValue = "false")
148 private boolean attach;
149
150
151
152
153
154
155 @Parameter(property = "mdep.outputFilterFile", defaultValue = "false")
156 private boolean outputFilterFile;
157
158
159
160
161
162
163
164 @Parameter(property = "mdep.useBaseVersion", defaultValue = "true")
165 private boolean useBaseVersion = true;
166
167 private final MavenProjectHelper projectHelper;
168
169 @Inject
170 protected BuildClasspathMojo(
171 MavenSession session,
172 BuildContext buildContext,
173 MavenProject project,
174 ResolverUtil resolverUtil,
175 ProjectBuilder projectBuilder,
176 ArtifactHandlerManager artifactHandlerManager,
177 MavenProjectHelper projectHelper) {
178 super(session, buildContext, project, resolverUtil, projectBuilder, artifactHandlerManager);
179 this.projectHelper = projectHelper;
180 }
181
182
183
184
185
186
187
188 @Override
189 protected void doExecute() throws MojoExecutionException {
190
191 boolean isFileSepSet = fileSeparator != null && !fileSeparator.isEmpty();
192 boolean isPathSepSet = pathSeparator != null && !pathSeparator.isEmpty();
193
194
195 if (attach && (localRepoProperty == null || localRepoProperty.isEmpty())) {
196 localRepoProperty = "${M2_REPO}";
197 }
198
199 Set<Artifact> artifacts = getResolvedDependencies(true);
200
201 if (artifacts == null || artifacts.isEmpty()) {
202 getLog().info("No dependencies found.");
203 }
204
205 List<Artifact> artList = new ArrayList<>(artifacts);
206
207 StringBuilder sb = new StringBuilder();
208 Iterator<Artifact> i = artList.iterator();
209
210 if (i.hasNext()) {
211 appendArtifactPath(i.next(), sb);
212
213 while (i.hasNext()) {
214 sb.append(isPathSepSet ? this.pathSeparator : File.pathSeparator);
215 appendArtifactPath(i.next(), sb);
216 }
217 }
218
219 String cpString = sb.toString();
220
221
222
223 if (isFileSepSet) {
224
225 final String pattern = Pattern.quote(File.separator);
226 final String replacement = Matcher.quoteReplacement(fileSeparator);
227 cpString = cpString.replaceAll(pattern, replacement);
228 }
229
230
231 if (outputFilterFile) {
232 cpString = "classpath=" + cpString;
233 }
234
235 if (outputProperty != null) {
236 getProject().getProperties().setProperty(outputProperty, cpString);
237 if (getLog().isDebugEnabled()) {
238 getLog().debug(outputProperty + " = " + cpString);
239 }
240 }
241
242 if (outputFile != null) {
243 if (regenerateFile || !isUpToDate(cpString)) {
244 storeClasspathFile(cpString, outputFile);
245 } else {
246 this.getLog().info("Skipped writing classpath file '" + outputFile + "'. No changes found.");
247 }
248 }
249
250 if (outputProperty == null && outputFile == null) {
251 getLog().info("Dependencies classpath:" + System.lineSeparator() + cpString);
252 }
253
254 if (attach) {
255 attachFile(cpString);
256 }
257 }
258
259
260
261
262
263 protected void attachFile(String cpString) throws MojoExecutionException {
264 File attachedFile = new File(getProject().getBuild().getDirectory(), "classpath");
265 storeClasspathFile(cpString, attachedFile);
266
267 projectHelper.attachArtifact(getProject(), attachedFile, "classpath");
268 }
269
270
271
272
273
274
275
276 protected void appendArtifactPath(Artifact art, StringBuilder sb) {
277 if (prefix == null) {
278 String file = art.getFile().getPath();
279
280 if (localRepoProperty != null && !localRepoProperty.isEmpty()) {
281 File localBasedir =
282 session.getRepositorySession().getLocalRepository().getBasedir();
283 file = StringUtils.replace(file, localBasedir.getAbsolutePath(), localRepoProperty);
284 }
285 sb.append(file);
286 } else {
287 sb.append(prefix);
288 sb.append(File.separator);
289 sb.append(DependencyUtil.getFormattedFileName(
290 art, this.stripVersion, this.prependGroupId, this.useBaseVersion, this.stripClassifier));
291 }
292 }
293
294
295
296
297
298
299
300 private boolean isUpToDate(String cpString) {
301 try {
302 String oldCp = readClasspathFile();
303 return Objects.equals(cpString, oldCp);
304 } catch (IOException ex) {
305 this.getLog()
306 .warn("Error while reading old classpath file '" + outputFile + "' for up-to-date check: " + ex);
307
308 return false;
309 }
310 }
311
312
313
314
315
316
317 private void storeClasspathFile(String cpString, File out) throws MojoExecutionException {
318
319 out.getParentFile().mkdirs();
320
321 String encoding = Objects.toString(outputEncoding, StandardCharsets.UTF_8.name());
322 try (Writer w = Files.newBufferedWriter(out.toPath(), Charset.forName(encoding))) {
323 w.write(cpString);
324 getLog().info("Wrote classpath file '" + out + "'.");
325 } catch (IOException ex) {
326 throw new MojoExecutionException("Error while writing to classpath file '" + out, ex);
327 }
328 }
329
330
331
332
333
334
335
336
337 protected String readClasspathFile() throws IOException {
338 if (outputFile == null) {
339 throw new IllegalArgumentException(
340 "The outputFile parameter " + "cannot be null if the file is intended to be read.");
341 }
342
343 if (!outputFile.isFile()) {
344 return null;
345 }
346
347 String encoding = Objects.toString(outputEncoding, StandardCharsets.UTF_8.name());
348
349 try (Stream<String> lines = Files.lines(outputFile.toPath(), Charset.forName(encoding))) {
350 return lines.collect(Collectors.joining());
351 }
352 }
353
354
355
356
357
358
359
360
361
362
363 @Override
364 public int compare(Artifact art1, Artifact art2) {
365 if (art1 == art2) {
366 return 0;
367 } else if (art1 == null) {
368 return -1;
369 } else if (art2 == null) {
370 return +1;
371 }
372
373 String s1 = art1.getGroupId() + art1.getArtifactId() + art1.getVersion();
374 String s2 = art2.getGroupId() + art2.getArtifactId() + art2.getVersion();
375
376 return s1.compareTo(s2);
377 }
378
379 @Override
380 protected ArtifactsFilter getMarkedArtifactFilter() {
381 return null;
382 }
383
384
385
386
387 public void setOutputFile(File outputFile) {
388 this.outputFile = outputFile;
389 }
390
391
392
393
394 public void setOutputProperty(String theOutputProperty) {
395 this.outputProperty = theOutputProperty;
396 }
397
398
399
400
401 public void setFileSeparator(String theFileSeparator) {
402 this.fileSeparator = theFileSeparator;
403 }
404
405
406
407
408 public void setPathSeparator(String thePathSeparator) {
409 this.pathSeparator = thePathSeparator;
410 }
411
412
413
414
415 public void setPrefix(String thePrefix) {
416 this.prefix = thePrefix;
417 }
418
419
420
421
422 public void setRegenerateFile(boolean theRegenerateFile) {
423 this.regenerateFile = theRegenerateFile;
424 }
425
426
427
428
429 public boolean isStripVersion() {
430 return this.stripVersion;
431 }
432
433
434
435
436 public void setStripVersion(boolean theStripVersion) {
437 this.stripVersion = theStripVersion;
438 }
439
440
441
442
443 public void setLocalRepoProperty(String localRepoProperty) {
444 this.localRepoProperty = localRepoProperty;
445 }
446 }