2016-04-12 13 views
3

Ich schreibe ein Maven-Plugin, das die aufgelösten Abhängigkeiten erhält. Es funktioniert gut für ein einzelnes Modulprojekt/Pom, aber scheitert bei mehreren Modulprojekten.Wie lösen Sie Abhängigkeiten zwischen allen Modulen in einem Maven-Plugin auf?

Hier ist ein Code

@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 
     } 
    } 
    } 
} 

Der obige Code wird konsolidieren Snippet alle Abhängigkeiten aufgelöst über alle Module, aber es enthält einige zusätzliche diejenigen.

Hier ist ein Beispielprojekt, mit dem Sie arbeiten können. Bitte laden Sie this github repo

aus den Modulen Projekthauptordner, führen Sie bitte

mvn dependency:tree -Dverbose -Dincludes=commons-logging 

Sie sollten eine Ausgabe wie diese

[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] ------------------------------------------------------------------------ 

Hinweis sehen, dass das Modul/Projekt Kern auf commons hängt -logging 1.0.4 und commons-logging 1.0.3, aber 1.0.3 wird aufgrund eines Konflikts ausgelassen und 1.0.4 ist behoben. Dies bedeutet, dass wenn Sie Core allein erstellen sollten, sollten Sie nur Commons-Logging 1.0.4 bekommen.

Beachten Sie, dass das Modul/Projekt web auch von widersprüchlichen Versionen von Commons-Logging abhängig ist, aber zu 1.1.1 aufgelöst wird.

Wenn Sie jetzt das "gesamte Projekt" (Module-Projekt) mit dem Befehl "mvn package" erstellen, sollten Sie sehen, dass modules-project/web/target/myweb/WEB-INF/lib alle enthält aufgelöste Abhängigkeiten und es enthält ONLY commons-logging 1.1.1.

Hier ist das Problem mit dem Code

In dem obigen Code wird reactorProjects mit 3 MavenProject der instanziiert: Module-Projekt, Kern und Web.

Für Module-Projekt und Web, löst und gibt commons-logging 1.1.1 zurück. Für das Projekt core löst es jedoch commons-logging 1.0.4 auf und gibt es zurück.

Ich möchte mein Plugin-Code wissen, dass commons-logging 1.1.1 die Abhängigkeit ist, dass der Build zu produzieren, und nicht die commons-logging 1.0.4

Irgendwelche Gedanken?

+0

Was ist der Zweck dieses Plugins? Was möchtest du erreichen? – khmarbaise

+0

Ich möchte nur alle Abhängigkeiten erhalten, die "mvn package" – kane

+0

erzeugen würde. Sie sollten das maven-assembly-plugin verwenden, das bereits solche Dinge tun kann ... Abgesehen davon, dass Sie die Abhängigkeiten erhalten möchten, war klar basierend auf der Frage, aber was ist nicht klar ist, warum brauchen Sie sie? – khmarbaise

Antwort

4

Sie haben praktisch alles in Ihrer Frage. Das folgende Plugin wird die Artefakte des WAR-Projekt in den Reaktor in der Konsolenausgabe drucken:

@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"); 
    } 

} 

Was das bedeutet ist, dass es alle Projekte in den Reaktor mit dem injizierten Parameter reactorProjects erwirbt. Dann führt es eine Schleife, um zu finden, welche davon die "war" ist, indem sie ihre Verpackung vergleicht. Wenn es gefunden wird, gibt getArtifacts() alle aufgelösten Artefakte für dieses Projekt zurück.

Die Magie, die es Arbeit macht, ist die aggregator = true in der MOJO Definition:

Flags diese Mojo es in eine Multi-Modul Art und Weise ausgeführt werden, das heißt Aggregat des Build mit dem Satz von Projekten als Module aufgelistet.

Wann core POM hinzugefügt

<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> 

und führen Sie mit Ihrem Beispielprojekt, dieser Druck in der Konsole:

[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 

Das ist gut genug. Damit können wir fortfahren und zum Beispiel die aufgelösten Artefakte mit dem aktuellen Projekt und dem gepackten Projekt vergleichen. Wenn wir eine Methode

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); 
      } 
     } 
    } 
} 

genannt mit

printConflictingArtifacts(packagedProject.getArtifacts(), project.getArtifacts()); 

hinzufügen, die die aktuellen Artefakte mit den Artefakten des verpackten Projekt vergleicht, und nur diejenigen mit der gleichen Gruppe/Artefakt-ID aber andere Version behalten wir erhalten kann Ihr Beispiel in der Konsolenausgabe mit:

[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 

die oben angenommen, dass unsere endgültigen Verpackungsmodul war ein WAR-Modul. Wir könnten das generischer machen und den Benutzer spezifizieren lassen, welches Modul das Zielmodul ist (d. H. Das die tatsächliche Lieferung verpacken wird).

Dafür können wir einen Parameter in unseren MOJO

@Parameter(property = "packagingArtifact") 
private String packagingArtifact; 

Dieser Parameter wird der groupId:artifactId Form hinzufügen und die Koordinaten des Zielmoduls repräsentieren wird. Wir können dann eine Methode getPackagingProject hinzufügen, deren Ziel darin besteht, die mit diesen Koordinaten verknüpften MavenProject zurückzugeben.

Die Konfiguration des Plugins innerhalb core

wäre
<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> 

und die volle MOJO wäre:

@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"); 
    } 

} 

das die Idee von oben implementiert: wenn der Benutzer ein Zielmodul gegeben hat, Wir benutzen es als Referenz. Wenn dieser Parameter nicht vorhanden ist, suchen wir standardmäßig einen WAR im Reaktor.

+0

Dies ist eine geniale Lösung und ich glaube, dass es für das spezifische Beispiel funktioniert, das ich aufgelistet habe. (Ich habe es aufgewertet) Meine Frage ist, wie allgemein diese Lösung ist. z.B. Umfassen alle Multi-Modul-Projekte genau einen Krieg? Ich nehme an, dass, wenn es kein Kriegsprojekt gibt, das nicht funktionieren wird, aber ich bin nicht sicher, was ein Multi-Modul-Projekt ohne Kriegsverpackung bedeutet ... – kane

+0

@kane Nein, Multi-Modul-Projekte müssen nicht unbedingt ein Krieg. Die Frage hängt davon ab, welches Modul das Modul "Verpackung" oder "Ziel" ist. In diesem Fall wird mit dem Modul, das eine "Kriegsverpackung" hat, festgestellt. Es ist jedoch möglich, es generischer zu machen und ein Konfigurationselement hinzuzufügen, das das Zielmodul spezifiziert. Ich werde mit einer Lösung damit auch bearbeiten. – Tunaki

+0

Ich habe über den Reaktor gelesen. Offensichtlich garantiert es, dass die Module der Abhängigkeit entsprechend gebaut werden. Ich frage mich, ob es eine allgemeine Lösung gibt, bei der wir die reactorProjects-Reihenfolge nutzen. dh das letzte Projekt enthält immer den vollständigen Satz aufgelöster Abhängigkeiten; Ist das unser "Ziel" -Modul? – kane