-
Notifications
You must be signed in to change notification settings - Fork 5
/
FlattenMojo.java
executable file
·370 lines (322 loc) · 10.9 KB
/
FlattenMojo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
package com.carrotgarden.maven.flatten;
import static org.codehaus.plexus.util.StringUtils.isEmpty;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.sonatype.plexus.build.incremental.BuildContext;
/**
* Simplify Maven project pom.xml descriptor for publication.
*/
@Mojo( //
name = "flatten", //
defaultPhase = LifecyclePhase.PREPARE_PACKAGE, //
requiresDependencyResolution = ResolutionScope.TEST, //
requiresProject = true)
public class FlattenMojo extends AbstractMojo implements Context {
/**
* Current Maven project.
*/
@Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
/**
* Current Maven session.
*/
@Parameter(defaultValue = "${session}", required = true, readonly = true)
MavenSession mavenSession;
/**
* Maven build plugin manager component.
*/
@Component()
BuildPluginManager buildManager;
/**
* Eclipse integration context.
*/
@Component()
BuildContext buildContext;
/**
* This plugin descriptor.
*/
@Parameter(defaultValue = "${plugin}", required = true, readonly = true)
PluginDescriptor pluginMeta;
/**
* Flag to skip this goal execution.
*/
@Parameter(property = "flatten.skip", defaultValue = "false")
boolean skip;
/**
* Propagate dependency exclusions during {@link #performDependencyResolve}.
*/
@Parameter(property = "flatten.resolveExclusions", defaultValue = "true")
boolean resolveExclusions;
/**
* List of pom.xml model members to remove. Used by
* {@link #performRemoveMembers}.
*/
@Parameter(property = "flatten.memberRemoveList")
String[] memberRemoveList = new String[] {};
/**
* Erase dependencies based on artifact scope. Used by
* {@link #performEraseScopes}.
*/
@Parameter(property = "flatten.scopeEraseList", defaultValue = "test")
String[] scopeEraseList = new String[] {};
/**
* List of project packaging types that trigger pom.xml switch for publication.
* Used by {@link #performSwitchPomXml}.
*/
@Parameter(property = "flatten.packagingSwitchList", defaultValue = "jar,war,ear,bundle,maven-plugin")
String[] packagingSwitchList = new String[] {};
/**
* Absolute file path for the generated flattened pom.xml. Main output produced
* by this goal.
*/
@Parameter(property = "flatten.targetPomFile", defaultValue = "${project.build.directory}/flatten/pom.xml.flatten")
File targetPomFile;
/**
* Execution step 1. Invoke {@code maven-dependency-plugin:resolve} to resolve
* and filter project dependencies with {@link #includeScope},
* {@link #ecludeTransitive}. Alternative to step 2.
*
* @see <a hfef=
* "https://maven.apache.org/plugins/maven-dependency-plugin/resolve-mojo.html">maven-dependency-plugin:resolve</a
*/
@Parameter(property = "flatten.performDependencyResolve", defaultValue = "true")
boolean performDependencyResolve;
/**
* Execution step 2. Erase dependency by scope names defined in
* {@link #scopeEraseList}. Alternative to step 1.
*/
@Parameter(property = "flatten.performEraseScopes", defaultValue = "false")
boolean performEraseScopes;
/**
* Execution step 3. Remove pom.xml members matched by xml tag names defined in
* the {@link #memberRemoveList}.
*/
@Parameter(property = "flatten.performRemoveMembers", defaultValue = "true")
boolean performRemoveMembers;
/**
* Execution step 4. Override project maven identity with:
* {@link #overrideGroupId} {@link #overrideArtifactId}
*/
@Parameter(property = "flatten.performOverrideIdentity", defaultValue = "false")
boolean performOverrideIdentity;
/**
* Execution step 5. Replace project pom.xml with generated flattened pom.xml
* for publication. Actual switch depends on condition
* {@link #packagingSwitchList}.
*/
@Parameter(property = "flatten.performSwitchPomXml", defaultValue = "true")
boolean performSwitchPomXml;
/**
* Default pom.xml encoding java charset name for text operations. Used when not
* defined in pom.xml.
*/
@Parameter(property = "flatten.encoding", defaultValue = "UTF-8")
String encoding;
/**
* Override project group id via {@link #performOverrideIdentity}.
*/
@Parameter(property = "flatten.overrideGroupId", defaultValue = "${project.groupId}")
String overrideGroupId;
/**
* Override project artifact id via {@link #performOverrideIdentity}.
*/
@Parameter(property = "flatten.overrideArtifactId", defaultValue = "${project.artifactId}")
String overrideArtifactId;
/**
* Use model char set with fall back to {@link #encoding}
*/
Charset modelCharset(Model model) {
String encoding = model.getModelEncoding();
if (isEmpty(encoding)) {
encoding = this.encoding;
}
return Charset.forName(encoding);
}
/**
* Determine pom.xml switch behaviour based on project packaging type.
*/
boolean hasPackagingSwitch() {
for (String packaging : packagingSwitchList) {
if (packaging.equals(project.getPackaging())) {
return true;
}
}
return false;
}
/**
* Invoke another goal from this plugin.
*/
void executeSelfMojo(String goal) throws Exception {
Support.executePluginMojo(pluginMeta, buildManager, mavenSession, goal);
}
/**
* Replace dependencies with result of goal="dependency:resolve".
*/
void resolveReplaceDependency(Model baseModel, Model flatModel) throws Exception {
Set<Artifact> resolvedArtifactList = contextResolvedExtract();
List<Dependency> baseDependencyList = baseModel.getDependencies();
List<Dependency> flatDependencyList = new ArrayList<Dependency>();
for (Artifact resolvedArtifact : resolvedArtifactList) {
Dependency flatDependency = new Dependency();
Support.resolveApplyDeclared(resolvedArtifact, flatDependency);
// TODO improve on this hack.
boolean hasSystem = "system".equals(resolvedArtifact.getScope());
if (hasSystem) {
// FIXME may not match iterpolated artifact vs non-iterpolated dependency.
Dependency baseDependency = Support.resolveLocateMatching(baseDependencyList, resolvedArtifact);
if (baseDependency == null) {
String artifactKey = ArtifactUtils.key(resolvedArtifact);
getLog().warn("no matching artifact: " + artifactKey);
} else {
flatDependency.setSystemPath(baseDependency.getSystemPath());
}
}
if (resolveExclusions) {
Support.resolveApplyExclusions(baseModel, resolvedArtifact, flatDependency);
}
flatDependencyList.add(flatDependency);
}
flatModel.setDependencies(flatDependencyList);
}
/**
* Remove artifacts in scopes configured via {@link #scopeEraseList}.
*/
void eraseScopes(Model model) throws Exception {
for (String scope : scopeEraseList) {
Support.removeScope(model, scope);
}
}
/**
* Remove model members configured in {@link #memberRemoveList}.
*/
void removeMembers(Model model) throws Exception {
for (String member : memberRemoveList) {
Support.removeMember(model, member);
}
}
/**
* Serialize generated maven model into a target pom.xml.
*/
void persistModel(Model model) throws Exception {
File file = targetPomFile;
Charset charset = modelCharset(model);
Support.ensureParent(file);
Support.modelWrite(model, file, charset);
buildContext.refresh(file);
}
/**
* Replace model identity: inside pom.xml.
*/
void overrideIdentity(Model model) {
model.setGroupId(overrideGroupId);
model.setArtifactId(overrideArtifactId);
}
/**
* Replace artifact identity: for the repository/${...}.jar.
*/
void overrideIdentity(Artifact artifact) {
artifact.setGroupId(overrideGroupId);
artifact.setArtifactId(overrideArtifactId);
}
/**
* Replace project build identity: for the ./target/${...}.jar.
*/
void overrideIdentity(Build build) {
// Use maven convention.
String finalName = overrideArtifactId + "-" + project.getVersion();
build.setFinalName(finalName);
}
/**
* Replace project identity for multiple targets.
*/
void overrideIdentity(MavenProject project) {
overrideIdentity(project.getModel());
overrideIdentity(project.getArtifact());
overrideIdentity(project.getBuild());
}
/**
* Replace attached project pom.xml with generated target pom.xml.
*/
void switchProjectPomXml() throws Exception {
File file = targetPomFile;
project.setPomFile(file);
}
/**
* Invoke execution steps in order.
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (buildContext.isIncremental()) {
getLog().info("Skipping incremental execution.");
return;
}
if (skip) {
getLog().info("Skipping plugin goal execution.");
return;
}
// Original model location.
File sourcePomFile = project.getModel().getPomFile();
try {
// Original read-only model, not interpolated.
Model baseModel = Support.modelRead(sourcePomFile, modelCharset(project.getModel()));
// Generated updatable model, already interpolated.
final Model flatModel = project.getModel().clone();
// Clear error markers in IDE.
buildContext.removeMessages(sourcePomFile);
// Change pom.xml.flatten.
if (performDependencyResolve) {
getLog().info("Resolving dependencies.");
executeSelfMojo("resolve");
resolveReplaceDependency(baseModel, flatModel);
}
if (performEraseScopes) {
getLog().info("Erasing dependency scopes.");
eraseScopes(flatModel);
}
if (performRemoveMembers) {
getLog().info("Removing pom.xml model members.");
removeMembers(flatModel);
}
// Change pom.xml.flatten and active project.
if (performOverrideIdentity) {
getLog().info("Overriding project maven identity.");
// Change model clone, affects pom.xml.flatten.
overrideIdentity(flatModel);
// Change active project, affects following phases.
overrideIdentity(project);
}
// Persist pom.xml.flatten.
persistModel(flatModel);
// Switch pom.xml -> pom.xml.flatten.
if (performSwitchPomXml && hasPackagingSwitch()) {
getLog().info("Switching project to flattened pom.xml.");
switchProjectPomXml();
}
} catch (Throwable e) {
String message = "Flatten failure";
// Show error markers in IDE.
buildContext.addMessage(sourcePomFile, 1, 1, message, BuildContext.SEVERITY_ERROR, e);
throw new MojoFailureException(message, e);
}
}
}