如何解决maven插件中所有模块的依赖关系?

kan*_*ane 3 maven-plugin maven maven-assembly-plugin

我正在编写一个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
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将整合所有模块中所有已解析的依赖项,但它包含一些额外的依赖项.

这是一个可以使用的示例项目.请下载此github repo

从modules-project主文件夹,请运行

mvn dependency:tree -Dverbose -Dincludes=commons-logging
Run Code Online (Sandbox Code Playgroud)

你应该看到这样的输出

[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] ------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

请注意,模块/项目核心依赖于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

有什么想法吗?

Tun*_*aki 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");
    }

}
Run Code Online (Sandbox Code Playgroud)

这样做是因为它使用注入的参数获取反应堆中的所有项目reactorProjects.然后,它"war"通过比较它们的包装来循环找到那些中的哪一个.找到后,getArtifacts()将返回该项目的所有已解析工件.

使其有效的神奇之处在于aggregator = trueMOJO定义:

标记此Mojo以多模块方式运行它,即使用列为模块的项目集合来构建构建.

添加到corePOM时

<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>
Run Code Online (Sandbox Code Playgroud)

并在您的示例项目中运行,这将在控制台中打印:

[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
Run Code Online (Sandbox Code Playgroud)

这很好.有了它,我们可以继续前进,例如,比较正在构建的当前项目和打包项目的已解析工件.如果我们添加一个方法

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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

打电话给

printConflictingArtifacts(packagedProject.getArtifacts(), project.getArtifacts());
Run Code Online (Sandbox Code Playgroud)

将当前工件与打包项目的工件进行比较,并且仅保留具有相同组/工件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
Run Code Online (Sandbox Code Playgroud)

以上假设我们的最终包装模块是WAR模块.我们可以使它更通用,并让用户指定哪个模块是目标模块(即将打包实际交付).

为此,我们可以为MOJO添加一个参数

@Parameter(property = "packagingArtifact")
private String packagingArtifact;
Run Code Online (Sandbox Code Playgroud)

此参数将是表单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>
Run Code Online (Sandbox Code Playgroud)

完整的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");
    }

}
Run Code Online (Sandbox Code Playgroud)

这实现了上述思想:当用户给出目标模块时,我们将其用作参考.当此参数不存在时,我们默认在reactor中找到WAR.