Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Paket need three files to support dependency management?

I am a Java developer used to Maven and Gradle, now coming into .NET and trying to understand Paket. From my understanding, Paket has three different files supporting a .NET solution's dependency management:

  1. a root paket.dependencies file where you list your direct dependencies and versions.
  2. a project specific paket.references file where you list a subset of the dependencies that you've already listed in paket.dependencies.
  3. a paket.lock auto-generated file that lists all direct and transitive dependencies along with their versions.

With Maven and Gradle, I am used to specifying my dependencies in one file. I can specify exact versions, and be sure that subsequent downloads of the dependencies will be the same. Why does Paket need three files? I would expect that a paket.references file in each project would be sufficient. Is there some problem or quirk in .NET world in how dependencies are managed that I am ignorant of that this three files are needed?

like image 523
jmrah Avatar asked Mar 27 '17 02:03

jmrah


2 Answers

  1. a root paket.dependencies file -- You have the possibility of grouping dependencies in this file, and thus access different versions of the same dependency.
  2. a project specific paket.references file -- The dependencies defined in paket.dependencies for a single project. Optionally reference a dependency in a group of the paket.dependencies file.
  3. a paket.lock auto-generated file -- Your build will work off of this file, and not the others to ensure referential transparency. You may, or may not, have included version-specifying instructions in paket.dependencies. The paket.lock file locks builds to the same specific versions every time. You must intentionally take some sort of action, usually with paket update or paket install to update the versions you want the next build to use.
like image 70
Jack Fox Avatar answered Sep 21 '22 03:09

Jack Fox


The issue is not that there is anything unique about dependency management in .NET. It's that Gradle (as great as it is) and Maven are missing some key features when it comes to dependency management.

  1. Each project specifies the version of each dependency separately. Consider these dependencies:

    • Project A: X 1.0
    • Project B: X 2.0, Project A

The two projects will build with two different versions of dependency X, even though they are ultimately destined for the same application. Arguably worse, the tests for each project will run with different versions of dependency X.

Paket solves this by specifying which project needs a dependency ('paket.references') and which version of a dependency should be used ('paket.dependencies') separately. This way multiple projects that use the same dependency are guaranteed to use the same version.

Because Gradle is very flexible, there are various ways to ensure that dependencies are declared with the same version in different projects. But none of them are terribly intuitive, and there is no standard way.

  1. While in Maven and Gradle you can specify exact versions of your direct dependencies, resolution of transitive dependencies is done at runtime, and is dependent on factors outside of the project (such as what dependency versions are available from the package source). This can result in two builds of the same source at different times or on different machines using different dependencies.

This is a common problem with dependency management on every platform, and the standard solution is a lock file ('paket.lock'), which saves the result of the dependency resolution to be used in future builds.

For years Gradle had no built-in support for lock files, although there was a Nebula plugin from Netflix that added locking capability. Recently Gradle has added built-in support for lock files.

like image 32
David Nelson Avatar answered Sep 19 '22 03:09

David Nelson