我正在编写一个Maven插件来获取已解析的依赖项。它适用于单个模块项目/ pom,但在多个模块项目中失败。
这是一段代码片段
@Mojo(
name="scan",
aggregator = true,
defaultPhase = LifecyclePhase.COMPILE,
threadSafe = true,
requiresDependencyCollection = ResolutionScope.TEST,
requiresDependencyResolution = ResolutionScope.TEST,
requiresOnline = true
)
public class MyMojo extends AbstractMojo {
@Parameter(property = "project", required = true, readonly = true)
private MavenProject project;
@Parameter(property = "reactorProjects", required = true, readonly = true)
private List<MavenProject> reactorProjects;
@Override
public void execute() throws MojoExecutionException {
for(MavenProject p : reactorProjects) {
for(Artifact a : p.getArtifacts()) {
...consolidate artifacts
}
}
}
}
上述代码将整合所有模块中所有已解析的依赖项,但它包含一些额外的依赖项。
这是一个可以使用的示例项目。请下载this github repo
从modules-project主文件夹,请运行
mvn dependency:tree -Dverbose -Dincludes=commons-logging
你应该看到像这样的输出
[INFO] ------------------------------------------------------------------------
[INFO] Building core 0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ core ---
[INFO] com.github:core:jar:0.1-SNAPSHOT
[INFO] \- axis:axis:jar:1.4:compile
[INFO] +- commons-logging:commons-logging:jar:1.0.4:runtime
[INFO] \- commons-discovery:commons-discovery:jar:0.2:runtime
[INFO] \- (commons-logging:commons-logging:jar:1.0.3:runtime - omitted for conflict with 1.0.4)
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building web 0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ web ---
[INFO] com.github:web:war:0.1-SNAPSHOT
[INFO] +- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] \- com.github:core:jar:0.1-SNAPSHOT:compile
[INFO] \- axis:axis:jar:1.4:compile
[INFO] +- (commons-logging:commons-logging:jar:1.0.4:runtime - omitted for conflict with 1.1.1)
[INFO] \- commons-discovery:commons-discovery:jar:0.2:runtime
[INFO] \- (commons-logging:commons-logging:jar:1.0.3:runtime - omitted for conflict with 1.1.1)
[INFO] ------------------------------------------------------------------------
请注意,模块/项目核心依赖于commons-logging 1.0.4和commons-logging 1.0.3,但由于冲突而忽略了1.0.3并且解决了1.0.4。 这意味着如果你自己构建核心,你应该只获得commons-logging 1.0.4。
请注意,模块/项目 web 依赖于公共日志的冲突版本,但也会解析为1.1.1。
现在,如果您使用“mvn package”命令构建“整个项目”(modules-project),您应该看到modules-project / web / target / myweb / WEB-INF / lib包含所有已解析的依赖项,它包括ONLY commons-logging 1.1.1。
以下是代码
的问题在上面的代码中,reactorProjects实例化了3个MavenProject:modules-project,core和web。
对于modules-project和web,它解析并返回commons-logging 1.1.1。但是,对于核心项目,它会解析并返回commons-logging 1.0.4。
我希望我的插件代码知道commons-logging 1.1.1是构建将产生的依赖,而不是commons-logging 1.0.4
有什么想法吗?
答案 0 :(得分:5)
您几乎可以完成所有问题。以下插件将在控制台输出反应器中WAR项目的工件:
@Mojo(name = "foo", aggregator = true, requiresDependencyResolution = ResolutionScope.TEST)
public class MyMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@Parameter(property = "reactorProjects", required = true, readonly = true)
private List<MavenProject> reactorProjects;
public void execute() throws MojoExecutionException, MojoFailureException {
MavenProject packagedProject = getWarProject(reactorProjects);
for (Artifact artifact : packagedProject.getArtifacts()) {
getLog().info(artifact.toString());
}
}
private MavenProject getWarProject(List<MavenProject> list) throws MojoExecutionException {
for (MavenProject project : list) {
if ("war".equals(project.getPackaging())) {
return project;
}
}
throw new MojoExecutionException("No WAR project found in the reactor");
}
}
这样做是因为它使用注入的参数reactorProjects
获取了反应堆中的所有项目。然后,通过比较它们的包装,它循环查找哪一个是"war"
。找到后,getArtifacts()
将返回该项目的所有已解析工件。
使其有效的神奇之处在于MOJO定义中的aggregator = true
:
标记此Mojo以多模块方式运行它,即使用列为模块的项目集合聚合构建。
添加到core
POM
<plugin>
<groupId>sample.plugin</groupId>
<artifactId>test-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>test</id>
<phase>compile</phase>
<goals>
<goal>foo</goal>
</goals>
</execution>
</executions>
</plugin>
并使用您的示例项目运行,这将在控制台中打印:
[INFO] commons-logging:commons-logging:jar:1.1.1:compile
[INFO] com.github:core:jar:0.1-SNAPSHOT:compile
[INFO] axis:axis:jar:1.4:compile
[INFO] org.apache.axis:axis-jaxrpc:jar:1.4:compile
[INFO] org.apache.axis:axis-saaj:jar:1.4:compile
[INFO] axis:axis-wsdl4j:jar:1.5.1:runtime
[INFO] commons-discovery:commons-discovery:jar:0.2:runtime
这很好。有了它,我们可以继续前进,例如,比较正在构建的当前项目和打包项目的已解析工件。如果我们添加方法
private void printConflictingArtifacts(Set<Artifact> packaged, Set<Artifact> current) {
for (Artifact a1 : current) {
for (Artifact a2 : packaged) {
if (a1.getGroupId().equals(a2.getGroupId()) &&
a1.getArtifactId().equals(a2.getArtifactId()) &&
!a1.getVersion().equals(a2.getVersion())) {
getLog().warn("Conflicting dependency: " + a2 + " will be packaged and found " + a1);
}
}
}
}
用
调用printConflictingArtifacts(packagedProject.getArtifacts(), project.getArtifacts());
将当前工件与打包项目的工件进行比较,并且仅保留具有相同组/工件ID但版本不同的工件,我们可以使用您的示例进入控制台输出:
[WARNING] Conflicting dependency: commons-logging:commons-logging:jar:1.1.1:compile will be packaged and found commons-logging:commons-logging:jar:1.0.4:runtime
以上假设我们的最终包装模块是WAR模块。我们可以使它更通用,让用户指定哪个模块是目标模块(即将打包实际交付)。
为此,我们可以在MOJO中添加一个参数
@Parameter(property = "packagingArtifact")
private String packagingArtifact;
此参数的格式为groupId:artifactId
,表示目标模块的坐标。然后,我们可以添加方法getPackagingProject
,其目标是返回与这些坐标相关联的MavenProject
。
core
内的插件配置为
<plugin>
<groupId>sample.plugin</groupId>
<artifactId>test-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>test</id>
<phase>compile</phase>
<goals>
<goal>foo</goal>
</goals>
<configuration>
<packagingArtifact>com.github:web</packagingArtifact>
</configuration>
</execution>
</executions>
</plugin>
完整的MOJO将是:
@Mojo(name = "foo", aggregator = true, requiresDependencyResolution = ResolutionScope.TEST, defaultPhase = LifecyclePhase.COMPILE)
public class MyMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@Parameter(property = "reactorProjects", required = true, readonly = true)
private List<MavenProject> reactorProjects;
@Parameter(property = "packagingArtifact")
private String packagingArtifact;
public void execute() throws MojoExecutionException, MojoFailureException {
MavenProject packagedProject = getPackagingProject(reactorProjects, packagingArtifact);
printConflictingArtifacts(packagedProject.getArtifacts(), project.getArtifacts());
}
private void printConflictingArtifacts(Set<Artifact> packaged, Set<Artifact> current) {
for (Artifact a1 : current) {
for (Artifact a2 : packaged) {
if (a1.getGroupId().equals(a2.getGroupId()) && a1.getArtifactId().equals(a2.getArtifactId())
&& !a1.getVersion().equals(a2.getVersion())) {
getLog().warn("Conflicting dependency: " + a2 + " will be packaged and found " + a1);
}
}
}
}
private MavenProject getPackagingProject(List<MavenProject> list, String artifact) throws MojoExecutionException {
if (artifact == null) {
return getWarProject(list);
}
String[] tokens = artifact.split(":");
for (MavenProject project : list) {
if (project.getGroupId().equals(tokens[0]) && project.getArtifactId().equals(tokens[1])) {
return project;
}
}
throw new MojoExecutionException("No " + artifact + " project found in the reactor");
}
private MavenProject getWarProject(List<MavenProject> list) throws MojoExecutionException {
for (MavenProject project : list) {
if ("war".equals(project.getPackaging())) {
return project;
}
}
throw new MojoExecutionException("No WAR project found in the reactor");
}
}
这实现了上述思想:当用户给出目标模块时,我们将其用作参考。如果此参数不存在,我们默认在反应堆中找到WAR。