siom79 / japicmp

Comparison of two versions of a jar archive
https://siom79.github.io/japicmp
Apache License 2.0
712 stars 107 forks source link

[maven-plugin] Provided dependencies cannot be loaded #317

Closed zentol closed 2 years ago

zentol commented 2 years ago

When a module being verified has a provided dependency on some other module that is required at compile time, then japicmp cannot load the classes from the first module because the provided dependency is not put on the classpath.

Steps to reproduce:

I can provide such a project as a zip if desired.

This happens because to generate the classpath The JapiCmpMojo is determining the set of dependencies via RepositorySystem#resolveDependencies in setUpClassPathUsingMavenProject(). However this does not give you all dependencies that the module needs to compile, but only those dependencies that another module depending on that module would need, and since provided (and optional) dependencies are not transitive they are not put on the classpath.

I couldn't find a nice way to solve this via the RepositorySystem API; the only alternative I could come up with is to use a custom DependencySelector that properly selects all dependencies that are required at compile time.

This selector would be used like this:

final DefaultRepositorySystemSession repositorySystemSession =
                new DefaultRepositorySystemSession(mavenParameters.getRepoSession());
            repositorySystemSession.setDependencySelector(new CompileScopeDependencySelector());

            DependencyResult dependencyResult = mavenParameters.getRepoSystem().resolveDependencies(
                repositorySystemSession,
                new DependencyRequest(
                    request, (node, parents) -> true));

I'm still prototyping such a selector, but it should be something along the lines of:

class CompileScopeDependencySelector implements DependencySelector {

  private final boolean isTopLevel;

  CompileScopeDependencySelector() {
        this(true);
  }

  private CompileScopeDependencySelector(boolean isTopLevel) {
        this.isTopLevel = isTopLevel;
  }

  @Override
  public boolean selectDependency(Dependency dependency) {
        return isNoTestDependency(dependency);
  }

  @Override
  public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
        return isTopLevel
            ? new CompileScopeDependencySelector(false)
            : new CompileScopeTransitiveDependencySelector();
  }

  private static class CompileScopeTransitiveDependencySelector implements DependencySelector {

        @Override
        public boolean selectDependency(Dependency dependency) {
          return isNoTestDependency(dependency) && !isProvidedDependency(dependency) && !dependency.isOptional();
        }

        @Override
        public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
          return this;
        }
  }

  private static boolean isNoTestDependency(Dependency dependency) {
        return !"test".equalsIgnoreCase(dependency.getScope());
  }

  private static boolean isProvidedDependency(Dependency dependency) {
        return "provided".equalsIgnoreCase(dependency.getScope());
  }
}

Alternatively, it might be possible to get the original set of dependencies from the Maven model, and create a CollectRequest for those dependencies instead (i.e., pretend to be the module).

cstamas commented 2 years ago

Somewhat related to https://github.com/siom79/japicmp/issues/327

zentol commented 2 years ago

No longer occurs with 1.16.0; considering it fixed.