Dependency scopes and conflict resolution: Possible bug?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Dependency scopes and conflict resolution: Possible bug?

Andreas Sewe-2
Hi Maven developers,

I a trying to wrap my head around Maven's handling of dependency scopes
and was wondering the following: Is the test-classpath always a
super-sequence of the compile-classpath?

AFAICT, this is the case. However, my experiments (using Maven 3.5.2)
left me wondering whether I may have stumbled upon a bug.

My (somewhat contrived) scenario is this:

>   <dependencies>
>     <dependency>
>       <groupId>junit</groupId>
>       <artifactId>junit</artifactId>
>       <version>4.9</version>
>       <scope>compile</scope>
>       <!-- compile-depends on org.hamcrest:hamcrest-core:1.1 -->
>     </dependency>
>     <dependency>
>       <groupId>org.hamcrest</groupId>
>       <artifactId>hamcrest-core</artifactId>
>       <version>1.2</version>
>       <scope>test</scope>
>     </dependency>
>   </dependencies>
The *test* classpath that compiler:testCompile sees is junit:junit:4.9 +
org.hamcrest:hamcrest-core:1.2. This is as expected, as the direct
hamcrest-core dependency beats the transitive one; hence, version 1.2
gets picked.

Now, the *compile* classpath that compile:compile sees is just
junit:junit:4.9. While this affirms my super-sequence hypothesis, it
seems like a bug: Without the direct, test-scoped
org.hamcrest:hamcrest-core:1.2 dependency the compile classpath would
have been junit:junit:4.9 + org.hamcrest:hamcrest-core:1.1, but with it
hamcrest-core has vanished completely.

Do you consider this a bug as well?

If so, what's the desired behavior here?

1. A compile classpath of junit:junit:4.9 +
org.hamcrest:hamcrest-core:1.1 would be confusing, as the super-sequence
assumption seems very natural.

2. But a compile classpath of junit:junit:4.9 +
org.hamcrest:hamcrest-core:1.2 seems harder to implement (to my as a
maven-resolver layman, at least), because a pruned part of the
dependency graph may now still exert influence on the versions in other
parts of the graph.

Best wishes,

Andreas


signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Dependency scopes and conflict resolution: Possible bug?

Jonathan Haber
As a workaround you can check out the dependency-scope-maven-plugin we
wrote:
https://github.com/HubSpot/dependency-scope-maven-plugin

The initial motivation was avoiding NoClassDefFoundError when someone
accidentally puts a dep at test scope that is transitively required at
runtime. I made a pom with the example dependencies you gave:
https://gist.github.com/jhaber/19d0b0a0454a6f05fde3375b21b8447e

And the plugin correctly failed the build:

[INFO] --- dependency-scope-maven-plugin:0.8:check (default) @ test ---

[ERROR] Found a problem with test-scoped dependency
org.hamcrest:hamcrest-core

  Scope compile was expected by artifact: junit:junit:4.9

[INFO]
------------------------------------------------------------------------

[INFO] BUILD FAILURE

[INFO]
------------------------------------------------------------------------

On Thu, Nov 23, 2017 at 2:16 PM Robert Scholte <[hidden email]> wrote:

> You're hitting MNG-5739
>
> Within a dependency-tree a groupId+artifactId is unique (nearest wins).
> Such a dependency has 1 version, but also 1 scope.
> By setting it explicit to test, you reduce the scope and it is not
> available during compile anymore.
>
> Is this correct?
> Well, this concept ensures that you test with exactly the same set of
> dependencies as the ones available at runtime.
> On the other hand, reducing the scope implies that you'll be missing one
> or more dependencies at runtime.
>
> We could think of making dependencies *always* scope-context-aware.
> Using your case one could suggest that at compile-time hamcrest-core:1.1
> is used, but at test-time hamcrest-core:1.2
>
> To me this makes more sense, but at the same time it is hard to predict
> the impact of such a change.
>
> thanks,
> Robert
>
> [1] https://issues.apache.org/jira/browse/MNG-5739
>
> On Thu, 23 Nov 2017 09:49:50 +0100, Andreas Sewe
> <[hidden email]> wrote:
>
> > Hi Maven developers,
> >
> > I a trying to wrap my head around Maven's handling of dependency scopes
> > and was wondering the following: Is the test-classpath always a
> > super-sequence of the compile-classpath?
> >
> > AFAICT, this is the case. However, my experiments (using Maven 3.5.2)
> > left me wondering whether I may have stumbled upon a bug.
> >
> > My (somewhat contrived) scenario is this:
> >
> >>   <dependencies>
> >>     <dependency>
> >>       <groupId>junit</groupId>
> >>       <artifactId>junit</artifactId>
> >>       <version>4.9</version>
> >>       <scope>compile</scope>
> >>       <!-- compile-depends on org.hamcrest:hamcrest-core:1.1 -->
> >>     </dependency>
> >>     <dependency>
> >>       <groupId>org.hamcrest</groupId>
> >>       <artifactId>hamcrest-core</artifactId>
> >>       <version>1.2</version>
> >>       <scope>test</scope>
> >>     </dependency>
> >>   </dependencies>
> >
> > The *test* classpath that compiler:testCompile sees is junit:junit:4.9 +
> > org.hamcrest:hamcrest-core:1.2. This is as expected, as the direct
> > hamcrest-core dependency beats the transitive one; hence, version 1.2
> > gets picked.
> >
> > Now, the *compile* classpath that compile:compile sees is just
> > junit:junit:4.9. While this affirms my super-sequence hypothesis, it
> > seems like a bug: Without the direct, test-scoped
> > org.hamcrest:hamcrest-core:1.2 dependency the compile classpath would
> > have been junit:junit:4.9 + org.hamcrest:hamcrest-core:1.1, but with it
> > hamcrest-core has vanished completely.
> >
> > Do you consider this a bug as well?
> >
> > If so, what's the desired behavior here?
> >
> > 1. A compile classpath of junit:junit:4.9 +
> > org.hamcrest:hamcrest-core:1.1 would be confusing, as the super-sequence
> > assumption seems very natural.
> >
> > 2. But a compile classpath of junit:junit:4.9 +
> > org.hamcrest:hamcrest-core:1.2 seems harder to implement (to my as a
> > maven-resolver layman, at least), because a pruned part of the
> > dependency graph may now still exert influence on the versions in other
> > parts of the graph.
> >
> > Best wishes,
> >
> > Andreas
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Dependency scopes and conflict resolution: Possible bug?

Andreas Sewe-2
In reply to this post by Andreas Sewe-2
Robert Scholte wrote:
> You're hitting MNG-5739
>
> Within a dependency-tree a groupId+artifactId is unique (nearest wins).
> Such a dependency has 1 version, but also 1 scope.
> By setting it explicit to test, you reduce the scope and it is not
> available during compile anymore.
>
> Is this correct?

Yes, this is what I observe.

> Well, this concept ensures that you test with exactly the same set of
> dependencies as the ones available at runtime.

That's a reasonable goal; you wouldn't want to use different versions
between in compile and test classpath.

> On the other hand, reducing the scope implies that you'll be missing one
> or more dependencies at runtime.
>
> We could think of making dependencies *always* scope-context-aware.
> Using your case one could suggest that at compile-time hamcrest-core:1.1
> is used, but at test-time hamcrest-core:1.2

I don't think that's a good solution, as it misses the goal of using the
*same* version of a library in compile and test classpaths.

IMHO, the better solution would be if the direct, test-scoped
hamcrest-core:1.2 dependency promoted the transitive, compile-scoped
hamcrest-core:1.1 to version 1.2 -- even if it itself were excluded from
the compile classpath.

Granted, this would mean that one part of the dependency graph overrides
versions in another part of the graph, which seems unexpected, but with
dependencies being "omitted for conflict" we already have something similar.

> To me this makes more sense, but at the same time it is hard to predict
> the impact of such a change.

That being said, I can understand when you don't want to change this
behavior, as that would potentially change the meaning of existing POMs.
However, it would only *add* compile-scoped dependencies that weren't
there previously, so I think the change wouldn't break anything but
merely add dependencies to the classpath that logically should have been
there all along -- but whose absence just didn't cause any compilation
or runtime error.

Best wishes,

Andreas


signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Dependency scopes and conflict resolution: Possible bug?

Andreas Sewe-2
In reply to this post by Jonathan Haber
Jonathan Haber wrote:
> As a workaround you can check out the dependency-scope-maven-plugin we
> wrote:
> https://github.com/HubSpot/dependency-scope-maven-plugin

Thanks for the pointer; this indeed looks like a useful plugin.

That being said, I wasn't experiencing this bug/misfeature myself.
Rather, I'm simply trying to understand how Maven works in this
corner-case to ensure that a research project of mine, a large scale
analysis of third-party projects, has correct input data (classpaths).

Best wishes,

Andreas


signature.asc (836 bytes) Download Attachment