Как программно составить список всех транзитивных зависимостей, в том числе переопределенных в Maven, используя DependencyGraphBuilder?

Это похоже на другие вопросы ( как этот), но я хочу быть в состоянии сделать это с последними API. Опция maven-dependency-plugin:tree verbose устарела и ничего не делает в последнем (2.5.1) коде, поэтому нет хорошего примера того, как это сделать.

2 ответа

Я верю Aether Утилита класса от jcabi-aether может помочь вам получить список всех зависимостей любого артефакта Maven, например:

File repo = this.session.getLocalRepository().getBasedir();
Collection<Artifact> deps = new Aether(this.getProject(), repo).resolve(
  new DefaultArtifact("junit", "junit-dep", "", "jar", "4.10"),
  JavaScopes.RUNTIME
);

Если вы находитесь за пределами плагина Maven:

File repo = new File("/tmp/local-repository");
MavenProject project = new MavenProject();
project.setRemoteProjectRepositories(
  Arrays.asList(
    new RemoteRepository(
      "maven-central",
      "default",
      "http://repo1.maven.org/maven2/"
    )
  )
);
Collection<Artifact> deps = new Aether(project, repo).resolve(
  new DefaultArtifact("junit", "junit-dep", "", "jar", "4.10"),
  "runtime"
);

Единственная зависимость, которая вам нужна:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-aether</artifactId>
  <version>0.7.5</version>
</dependency>

Включая мой подход здесь, поскольку дополнительные шаги могут стать частью вашего фактического варианта использования, особенно. при работе над составным или многомодульным проектом.

(Maven 3, моя среда выполнения была 3.6; прямой зависимости от эфира не было)

В моем случае я хотел разрешить дерево зависимостей определенного артефакта изнутри моего плагина; Однако,

  • некоторые версии зависимостей были доступны только в родительской версии. POM (т.е. отсутствует в собственном POM).
  • Родительский POM также имел дополнительные детали, такие как исключения для некоторых зависимостей - через .

Поэтому мне пришлось:

  • явно загрузить родительскую модель,
  • связать с ней дочернюю модель,
  • заполните отсутствующие номера версий дочерней модели (до сих пор не уверен, почему Maven не разрешил их автоматически после связывания родителя), а затем
  • запустить разрешение зависимостей для дочернего элемента.

Чтобы избежать построения модели с нуля, я вывел модель используя существующий артефакт (который в моем случае всегда гарантированно присутствует в строящемся проекте Maven). Все эти артефакты имеют одинаковый .

          @Component
    public LifecycleDependencyResolver resolver;

    // ...

    // `artifacts` contains all artifacts of current/reactor `MavenProject` obtained via `project.getArtifacts()`
    private Set<Artifact> resolveRuntimeDeps(Set<Artifact> artifacts) throws MojoExecutionException {

        // foo-api will always be present; use it to derive coordinates for foo-runtime
        Artifact fooApi = artifacts.stream().filter(artifact -> "foo-api".equals(artifact.getArtifactId()))
                .findFirst().orElseThrow(() -> new MojoExecutionException("Unable to find foo-api"));

        Collection<String> scopes = Arrays.asList("compile", "runtime");

        MavenProject fooRoot = deriveProject(fooApi, "foo-parent");
        Model fooRootPom = fooRoot.getModel();

        MavenProject fooSrv = deriveProject(fooApi, "foo-runtime");
        fooSrv.setParent(fooRoot);

        // some foo-runtime deps depend on versions declared on parent pom; merge them
        Map<String, Artifact> depMgt = fooRootPom.getDependencyManagement().getDependencies().stream()
                .collect(Collectors.toMap(dep -> dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getType(), this::toArtifact));
        for (Dependency d : fooSrv.getDependencies()) {
            if (d.getVersion() == null) {
                Artifact managed = depMgt.get(d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getType());
                if (managed != null) {
                    d.setVersion(managed.getVersion());
                }
            }
        }

        try {
            resolver.resolveProjectDependencies(fooSrv, scopes, scopes, session, false, Collections.emptySet());
            return fooSrv.getArtifacts();
        } catch (LifecycleExecutionException e) {
            throw new MojoExecutionException("Error resolving foo-runtime dependencies", e);
        }
    }

    // load POM for another artifact based on foo-api JAR available in current project
    private MavenProject deriveProject(Artifact fooApi, String artifactId) throws MojoExecutionException {
        Model pom;
        String pomPath = fooApi.getFile().getAbsolutePath().replaceAll("foo-api", artifactId).replaceAll("\\.jar$", ".pom");
        try (InputStream fooRootPomData = new FileInputStream(pomPath)) {
            pom = new MavenXpp3Reader().read(fooRootPomData);
            pom.setPomFile(new File(pomPath));
        } catch (IOException | XmlPullParserException e) {
            throw new MojoExecutionException("Error loading " + artifactId + " metadata", e);
        }

        // set these params to avoid skips/errors during resolution
        MavenProject proj = new MavenProject(pom);
        proj.setArtifact(toArtifact(pom));
        proj.setArtifactFilter(Objects::nonNull);
        proj.setRemoteArtifactRepositories(Collections.emptyList());
        return proj;
    }

    private Artifact toArtifact(Model model) {
        return new DefaultArtifact(
                Optional.ofNullable(model.getGroupId()).orElseGet(() -> model.getParent().getGroupId()), model.getArtifactId(),
                Optional.ofNullable(model.getVersion()).orElseGet(() -> model.getParent().getVersion()), "compile", model.getPackaging(), null,
                project.getArtifact().getArtifactHandler());
    }

    private Artifact toArtifact(Dependency dep) {
        return new DefaultArtifact(dep.getGroupId(), dep.getArtifactId(), dep.getVersion(), dep.getScope(), dep.getType(), dep.getClassifier(),
                project.getArtifact().getArtifactHandler());
    }

(Я пробовал почти все другие предложенные подходы, однако все они заканчивались той или иной ошибкой. Теперь, оглядываясь назад, я подозреваю, что многие из этих ошибок могли быть связаны с тем, что в моем листовом POM отсутствовали номера версий для некоторых артефакты. Кажется (приемлемо), что фаза «обогащения модели» — распространение родительских версий и т. д. — выполняется каким-то более ранним компонентом в потоке Maven, и вызывающая сторона должна позаботиться об этом, по крайней мере частично, при вызове зависимости резольвер с нуля.)

Другие вопросы по тегам