Replies: 3 comments
-
There's a substantial overhead of the derivation containing results of dependency build. Splitting builds of dependencies could make sense if you one could reliably partition the set of dependencies into ones that take a long time to build and change rarely, and ones that are quick to build and change more often. In practice however this would be extremely tedious and complex, and in many scenarios the changes in the project would invalidate all deps builds anyway.
This is not very practical/possible as compilation of each file in a crate require (potentially) all other files in a crate, and changes in one would invalidate builds in other. Rust compiles the crate as a whole. Building each crate separately is possible, but requires reproducing all the rules of crate building of |
Beta Was this translation helpful? Give feedback.
-
Hi @Ma27 thanks for the discussion! I do want to say that I definitely wouldn't want to discourage the exploration of new ideas or possibilities of improving caching. That said, I believe that granular level of caching becomes impossible in practice without either changing how cargo itself behaves, or moving entirely away from cargo. Let me elaborate further: Part of the complexity around cargo's caching has to do with the interaction of feature flags across crates. Adding a new crate to the dependency closure can result in rebuilding other, previously existing crates depending on how features are unified, meaning we can't just compile the new crate and its (new) dependencies and call it a day. Suppose we have the following:
Thus it is only possible to (naively) cache cargo artifacts as long as the entire dependency closure remains intact. The (Technically cargo doesn't rebuild everything like this when you add a new dependency and build outside of Nix because it happens to reuse whatever artifacts on disk end up being valid, but then you're out there in the non-functional-mutable-state world) Like @dpc mentioned above Obviously this comes with the benefit of being able to "observe" whether adding a new crate to the dependency closure influences having to rebuild other crates. If not, great, the artifacts can be reused! The really big downside is the additional complexity of having to understand all about cargo's behavior and re-implement that on top of Nix, especially if new features are added to cargo which change this completely. Granted I've never deeply used If you don't care about using cargo then It's worth mentioning that I do remember there being some proposals for having cargo generate and output build-plans: rather than have cargo do the actual build, it could spit out a plan of what it would do (e.g. compile these crates in this order, run that build script, etc.). The idea was that other build systems could consume this and do the actual orchestration and artifact management themselves. Unfortunately I haven't heard very much about it since then. If that does ever land I think it could lead to a whole bunch of optimizations and improvements! But until then we're back to the current status quo so long as we want to continue using cargo without reimplementing its behavior Now to answer your questions more specifically:
I personally think the maintenance burden of keeping up with cargo's internals would be too much for me (though if it could produce some kind of build plan with a stable format I'd change my mind). But if you are motivated enough to explore it I'd say go right ahead :)
Like @dpc mentioned I don't think this is possible. Rust's compilation unit is the whole crate and you cannot compile individual files even if you try to invoke |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
First of all, thanks a lot for this! This is a big step towards how I'd like to see Nix being used for development!
I have a question and I'd like to get some feedback on whether this is actually reasonable. If I have the time for that, I may file a PR on my own later on if that's the case.
When I e.g. add a dependency to my Cargo.toml and run
cargo build
, only the newly added (or existing, but updated) crates will be rebuilt. When I do anix build
instead, all of the deps are rebuilt in thepname-deps
drv. I'm wondering if it'd be somehow reasonable & possible to split thepname-deps
derivation into even more derivations, i.e. for each crate my project depends on. That means, we'd need to represent the entire dependency graph from cargo as graph of derivations.Benefits would be (1) faster rebuilds while developing and (2) being able to share built crates between projects (e.g. if both projects are using the exact same version of a dependency), IIRC even cargo can't do that currently (though I'm not sure if that'll actually be possible).
Downsides are most likely (1) more expensive evaluations and (2) higher maintenance workload in this project because we may need to adapt on newer Cargo releases if certain internals change.
Do you think that'd be a reasonable thing to do? As I said, this is for now a hypothetical question because I need a reality check whether that's actually a good idea or completely stupid, haven't done a deep-dive into Cargo internals yet :D
Follow-up question: do you think it'd be possible to actually build all files in their own derivations in
craneLib.buildPackage
? That'd be the holy grail (for me at least :p) considering that we'd have completely incremental Rust builds with Nix then.See for related work in Haskell e.g. https://github.com/matthewbauer/ghc-nix (I know that it's using a slightly different approach - i.e. "injecting" Nix builds into
ghc
, but the end-result is pretty similar to what I'd like to have).Beta Was this translation helpful? Give feedback.
All reactions