Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible transitive dependency bug? #50

Closed
paulirwin opened this issue Jan 26, 2024 · 4 comments
Closed

Possible transitive dependency bug? #50

paulirwin opened this issue Jan 26, 2024 · 4 comments

Comments

@paulirwin
Copy link

I have been testing transitive dependencies with MavenReferences to make sure that it will work for our case. I created an example repo here that speaks louder than words: https://github.com/paulirwin/ikvm-maven-transitive

The TL;DR is that with two different NuGet packages in a .NET dependency hierarchy that depend on MavenReferences that have different versions of the same compile-time Maven dependency (here, commons-lang3 v3.12 vs 3.11), the version that ends up in the consuming project is unexpected, and differs from the versions when the NuGet packages themselves are built.

  • PackageC depends on commons-configuration2 2.9.0, which has a compile dependency on commons-lang3 3.12.0.
  • PackageB depends on PackageC.
  • PackageA depends on PackageB and velocity-engine-core 2.3, which has a compile dependency on commons-lang3 3.11.0.
  • ConsoleApp depends on PackageA.

When you pack the packages in that order, you notice that all three packages use commons-lang3 3.12.0 in their bin folder. That makes sense for PackageB and PackageC intuitively, as they have not yet encountered 3.11.0. I could understand why PackageA uses 3.12.0 instead of its more-direct 3.11.0, as it is a newer package, maybe? Or maybe the POM resolution of the NuGet packages it depends on takes precedence over its own MavenReference? Either way, this is not yet a problem in this example.

But what is surprising is that ConsoleApp ends up with v3.11.0. Is this a bug or is there a good explanation for this behavior?

@wasabii
Copy link
Collaborator

wasabii commented Jan 26, 2024

Hmm. I think I have a theory.

We just feed a dependency list to Maven Resolver, and it comes back with the 'solution'. So, we rely on it for a lot. But, we also might be feeding it the wrong thing.

This might be a consequence of #44. I'll have to think more.

@wasabii
Copy link
Collaborator

wasabii commented Jan 26, 2024

So, after reading the Maven docs, I'm not sure this is a bug.

https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

With transitive dependencies, the graph of included libraries can quickly grow quite large. For this reason, there are additional features that limit which dependencies are included:

Dependency mediation - this determines what version of an artifact will be chosen when multiple versions are encountered as dependencies. Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, the first declaration wins.

So, Maven doesn't really pick the latest version. It picks the NEAREST version. This is contrary to how NuGet functions, where it tries to negotiate the highest version.

So, I don't think we're doing anything wrong here. If you want to ensure that a specific version is picked by ConsoleApp, then you should add a reference to that specific version to ConsoleApp. It'll be top in the tree, and thus nearest.

@wasabii
Copy link
Collaborator

wasabii commented Jan 26, 2024

Where we go wrong in #44 is that every NuGet package is considered "the same depth of the hierarchy". Basically we scrap the .pom files we injected into the NuGet package out, and just append them to the end of the dependency list. As opposed to giving Maven a hierarchy at all.

But, I don't think this is relevant to your use case, as the dependencies aren't in the Nuget pom files, they're in POM files referenced by those. So there is a hierarchy.

It's just the order and depth matters.

We have a POTENTIAL bug, which is that if two NuGet packages declare a direct dependency on different versions, we'd pick the first of those. And the "first" woul dbe determined by the order we enumerate POM files. Which is driven by the order in which we append to MavenReferenceItem. Which is set in the MSBuild targets to:

    <!-- PackageReferences come first, ProjectReferences come second, local MavenReferences come last. -->
    <PropertyGroup>
        <GetMavenReferenceItemsDependsOn>
            $(GetMavenReferenceItemsDependsOn);
            GetMavenReferenceItemsFromPackageReferences;
            PrepareProjectReferences;
            GetMavenReferenceItemsFromProjectReferences;
            GetMavenReferenceItemsFromMavenReferences;
            GetMavenReferenceItemsMetadata;
        </GetMavenReferenceItemsDependsOn>
    </PropertyGroup>

Which I think is backwards.

@wasabii
Copy link
Collaborator

wasabii commented Jan 27, 2024

Going to close this because I'm pretty confident at least the question in the topic isn't a bug.

@wasabii wasabii closed this as completed Jan 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants