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