-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Investigate packaging Rust crates separately #333702
Description
Right now, every Rust package is an ecosystem unto itself, with dependency versions being selected from each package’s upstream Cargo.lock file (or a vendored one if none is present). This stands in contrast to how many language ecosystems in Nixpkgs work, and has caused us problems:
-
Cargo.lockconsidered harmful #327063 – keepingCargo.lockfiles in the repository bloats its size, but gives us more static insight into the dependencies used by packages and avoids hacky FOD duplication. -
Rust 1.80.0 breaks some packages #332957 – in an ideal world, we could bump
timeonce to the fixed version, rather than playing whack‐a‐mole with all the broken packages. -
This hasn’t happened yet, but I’m reading dealing with all the pinned
ffmpeg-sys-nextdependencies when I upgrade the default FFmpeg to 7. In general, it’s just pretty painful for us to patch Rust dependencies in a way that it isn’t for many other language ecosystems.
I don’t think it’s practical for us to manually maintain a Rust package set like the Python one. However, I think we could do better here. The idea is that we could essentially have one big Cargo.lock file that pins versions of all crates used across the tree, and abandon both cargoHash and vendored Cargo.lock files. The hope is that this would give us the static advantages of vendored Cargo.lock files, let us reduce the number of versions of the same packages that we ship, and do treewide bumps of package versions with much less pain, while (I hope) still taking up less space in the repository than the status quo.
It wouldn’t be feasible to maintain exactly one version of every package. Many popular packages have incompatible major versions, and we may not want to keep a package exclusively on an older version just for some outdated software that pins an older version than most software is compatible with. However, I suspect we could vastly reduce the proliferation of alternate crate versions across the tree.
A downside would be that we would no longer be using the “known good” versions of packages from our various upstreams. For some packages with incompletely‐declared dependency ranges, this could result in broken builds or functionality. In those cases, we would still have the option to vendor a package‐specific Cargo.lock file. Note that this is how it works in most Linux distributions, so although we might package more Rust software than the average Linux distribution, these challenges aren’t unique to us.
This would not necessarily have to literally be one huge Cargo.lock file; we just need something we can turn into Cargo.lock files or a Cargo source to replace crates.io, as suggested in #327063 (comment). As @alyssais pointed out in the issue I linked, we don’t need the dependencies array, and as I pointed out, one single file is conflict‐prone. I suspect we would want something like a JSON or TOML‐based format with one file per package (or package version). That should be comparable in size and organization to our other language package sets, and minimize conflicts.
There are unsolved problems here, e.g.:
-
We probably don’t want every bump of any Rust library to rebuild every Rust application. We’d need to figure out some way to narrow down the rebuilds to what’s required by each package. The best option I can currently think of is adapting the
cargoHash‐style FOD stuff to the task of selecting the subset of our One Big Lock File that is present in the upstreamCargo.lock/Cargo.tomlsomehow. For instance, it may be acceptable if every crate version bump rebuilds Rust derivations across the tree that check against thesrc’sCargo.tomland either succeed because the applicable versions remained constant, or fail because the hash of those versions no longer matches. We just need to be able to short‐circuit the actual builds. -
We’d need automation to manage the One Big Lock File. In particular, we’d want to be able to tell when a dependency bump is compatible with the version bounds in various packages so that we can decide between bumping vs. adding a new available version, and keep a set of crates that is consistent with the things we package. This could probably be as simple as just automating and unconditionally accepting SemVer‐compatible bumps, dealing with any fallout by hand, and trying SemVer‐incompatible bumps when we feel brave.
Ultimately, though, I think that the current status quo is causing a lot of problems, and that if we can successfully pursue this proposal, we’ll hopefully make all the groups here happier: the people who maintain Rust packages, the people who worry about the repository size and evaluation performance of Nixpkgs, and the people who worry about losing Nix’s static guarantees.
cc @matklad who suggested the source approach
cc @alyssais who said that we used to do this but stopped
cc @Atemu who opened the issue about Cargo.lock