-
-
Notifications
You must be signed in to change notification settings - Fork 162
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
[RFC 0092] Computed derivations #92
Changes from 37 commits
3a8338a
3b8422a
4da9193
800b5f3
f708983
6a87c1b
36193e5
ffb9203
5564fdb
22f8322
7f5f854
5c9f1fb
5e56f21
ba7dcce
8bcb4e6
baae1e6
9448a2a
37a643e
14b134d
1b0a6a1
3fe1c3d
a38f245
4022058
6c01e4d
6d97de1
2079528
fd7bb41
ec622cd
ad74b68
0eaa6bb
49070db
c50ee43
98ea32a
c01f07c
3f187a3
7d18780
5e92cc9
1e2012c
5836c02
970ff43
d6d6b17
6ab3338
5f7d4da
ea388e7
f6741c4
b5aa21d
866dc5e
098fe68
fed8991
854fd9b
f853a6f
1abaf30
ef1d9aa
5115ebb
fdbf778
2388cbe
404ad6d
a906a7c
4d579ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,261 @@ | ||||||||||||||
--- | ||||||||||||||
feature: plan-dynamism | ||||||||||||||
start-date: 2019-02-01 | ||||||||||||||
author: John Ericson (@Ericson2314) | ||||||||||||||
co-authors: (find a buddy later to help out with the RFC) | ||||||||||||||
shepherd-team: (names, to be nominated and accepted by RFC steering committee) | ||||||||||||||
shepherd-leader: (name to be appointed by RFC steering committee) | ||||||||||||||
related-issues: (will contain links to implementation PRs) | ||||||||||||||
--- | ||||||||||||||
|
||||||||||||||
# Summary | ||||||||||||||
[summary]: #summary | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this section could use a paragraph actually describing the benefit. You list some problems that are hard to solve in the Motivation but a succinct summary would be useful. Possibly something like: Plan Dynamism would allow making Nix derivations available for large numbers of packages from language-specific package repositories such as NPM or crates.io with very little per-package effort |
||||||||||||||
|
||||||||||||||
We need build plan dynamism -- interleaved building and planning -- to cope with the ever-growing world of language-specific package managers. | ||||||||||||||
Propose to allow derivations to build derivations, and depend on those built derivations, as the core primitive for this. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
Additionally, introduce a new primop to leverage this in making "import from derivation" (IFD), still the gold standard for ease of use, more efficient and compatible with `hydra.nixos.org`'s queue runner. | ||||||||||||||
|
||||||||||||||
# Motivation | ||||||||||||||
[motivation]: #motivation | ||||||||||||||
|
||||||||||||||
Nix's design encourages a separation of build *planning* from build *execution*: | ||||||||||||||
evaluation of the Nix language produces derivations, and then then those derivations are built. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
This usually a great thing. | ||||||||||||||
It's enforced the separation of the more complex Nix expression language from the simpler derivation language. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which do you mean?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
("has" is usually only contracted to "'s" when part of "has got".) |
||||||||||||||
It's also encouraged Nixpkgs to take the "birds eye" view and successful grapple a ton of complexity that would have overwhelmed a more traditional package repository. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
|
||||||||||||||
Nixpkgs, along with every other distro, also faces a looming crisis: new open source software is increasingly not intended to be packaged by distros at all. | ||||||||||||||
Many languages now support very large library ecosystems, with dependencies expressed in a language-specific package manager. | ||||||||||||||
To this new generation of developers, the distro (or homebrew) is a crufty relic from an earlier age to bootstrap modernity, and then be forgotten about. | ||||||||||||||
|
||||||||||||||
Right now, to deal with these packages, we either convert by hand, or commit lots of generated code into Nixpkgs. | ||||||||||||||
But I don't think either of those options is healthy or sustainable. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Nit: I would recommend trying to avoid "think" and similar and just state as fact. |
||||||||||||||
The problem with the first is sheer effort; we'll never be able to keep up. | ||||||||||||||
The problem with the second is bloating Nixpkgs but more importantly reproducability: If someone wants to update that generated code it is unclear how. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it may be worth expanding on this slightly. Basically each language system will have it's own script or command to update these generated files. This inconsistency is difficult to learn and hard to tool in a generic way. |
||||||||||||||
All these mean that potential users coming from this new model of development find Nix / Nixpkgs cumbersome and unsuited to their needs. | ||||||||||||||
|
||||||||||||||
The core feature here, derivations that build derivations, is the best fundamental primitive for this problem. | ||||||||||||||
It's very performant, being well-adapted for Nix's current scheduler. | ||||||||||||||
Unlike recursive Nix, there's is no potential for half-built dependencies to sit around waiting for other builds, wasting resources. | ||||||||||||||
Each build step (derivation) always runs start to finish blocking on nothing. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
comma for clarity |
||||||||||||||
It's very efficient, because it doesn't obligate the use of the Nix expression language. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The direct object of "obligate" should be the person(s) obligated to do something, not the thing to be done. Maybe
Suggested change
or
Suggested change
or
Suggested change
|
||||||||||||||
Finally, it's quite compatible with `--dry-run`. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just "quite" compatible? What does that mean? |
||||||||||||||
|
||||||||||||||
However, "import from derivation" is still far and away the easiest method to use, and the one that existing tools to convert to Nix use. | ||||||||||||||
Ericson2314 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
\[Actually it's not just `import` which is notable, one can `builtins.readFile` a not-yet-buit path and it will also block today, and probably other such primops. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider splitting this sentence into two:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are "primops"? |
||||||||||||||
The exact primop doesn't matter --- all are noticeably more ergonomic than alternatives, and our proposal here is agnostic to the prim-op which would trigger the blocking. | ||||||||||||||
I will continue to use "IFD" as an umbrella acronym despite its deficiencies because it is best known.\] | ||||||||||||||
We should continue to support it, finding a way for `hydra.nixos.org` to allow it, so those tools with IFD can be used in Nixpkgs and become first-class members of the Nix ecosystem. | ||||||||||||||
We have a fairly straightforward mechanism, only slightly more cumbersome than IFD today, to allow evaluation to be deferred after imported things are built. | ||||||||||||||
This frees up the Hydra evaluator to finish before building, and also meshes well with any plan to build more than one eval-realized path at a time. | ||||||||||||||
This should allow us to drop the `hydra.nixos.org` restriction. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it generally known what restriction this refers to? |
||||||||||||||
|
||||||||||||||
With these steps, I think we will be able to successfully convert to Nix a bunch of developers that mainly work in one language, and didn't even think they were in need of a better distro. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
In turn, I hope these upstream packages and ecosystems might even care about packaging and integration of the sort that we do. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Packages and ecosystems can't care. Their developers and maintainers can and might. |
||||||||||||||
This would create a virtuous cycle where Nix is easier to use by more people, and Nixpkgs is easier to maintain as upstream packages better match our values. | ||||||||||||||
|
||||||||||||||
# Detailed design | ||||||||||||||
[design]: #detailed-design | ||||||||||||||
|
||||||||||||||
We can break this down nicely into steps. | ||||||||||||||
|
||||||||||||||
## Dynamic derivations | ||||||||||||||
|
||||||||||||||
*This is implemented in https://github.com/NixOS/nix/pull/4628.* | ||||||||||||||
|
||||||||||||||
1. Derivation outputs can be valid derivations. | ||||||||||||||
\[If one tries to output a drv file today, they will find Nix doesn't accept the output as such because these small paper cuts. | ||||||||||||||
This list item and its children should be thought of as "lifting artificial restrictions".\] | ||||||||||||||
|
||||||||||||||
1. Allow derivation outputs to be content addressed in the same manner as drv files. | ||||||||||||||
(The little-exposed name for this is "text" content addressing). | ||||||||||||||
|
||||||||||||||
2. Lift the (perhaps not yet documented) restriction barring derivations output paths from ending in `.drv`, but only for derivation outputs that are so content-addressed. | ||||||||||||||
\[There are probably other ways to make store paths that end in `.drv` that aren't valid derivations, so we could make the simpler change of lifting this restriction entirely without breaking invariants. But I'm fine keeping it for the wrong sorts of derivations as a useful guard rail.\] | ||||||||||||||
|
||||||||||||||
2. Extend the CLI to take advantage of such derivations: | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specifying the CLI here feels a bit odd. Shouldn't this feature, at first, be transparent to the CLI? If someone uses the new hydra-safe IFD that shouldn't matter to the CLI. In general overloading the "installables" attribute more and more (first flakes with Can't we be explicit about the intentions?
This would at least keep a clear separation of attributes and expression/derivation to build without adding more and more complexity into "installables". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So Flake's Here's another way to think about it: what would # build the out output
$ nix path-info /nix/store/something-....drv out mean with your proposal? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even more concretely I hope that this example shows how There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the topic of |
||||||||||||||
|
||||||||||||||
We hopefully will soon allow CLI "installable" args in the form | ||||||||||||||
``` | ||||||||||||||
single-installable ::= <path> ! <output-name> | ||||||||||||||
``` | ||||||||||||||
Ericson2314 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
where the first path is a derivation, and the second is the output we want to build. | ||||||||||||||
|
||||||||||||||
We should generalize the grammar like so: | ||||||||||||||
``` | ||||||||||||||
single-installable ::= <single-installable> ! <output-name> | ||||||||||||||
| <path> | ||||||||||||||
|
||||||||||||||
multi-installable ::= <single-installable> | ||||||||||||||
| <single-installable> ! * | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
Plain paths just mean that path itself is the goal, while `!` indexing indicates one more outputs of the derivation to the left of the `!` is the goal. | ||||||||||||||
|
||||||||||||||
> For example, | ||||||||||||||
> ``` | ||||||||||||||
> nix build /nix/store/…foo.drv | ||||||||||||||
> ``` | ||||||||||||||
> would just obtain `/nix/store/…foo.drv` and not build it, while | ||||||||||||||
> ``` | ||||||||||||||
> nix build /nix/store/…foo.drv!* | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I picked There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops maybe I replied to the wrong one. |
||||||||||||||
> ``` | ||||||||||||||
> would obtain (by building or substituting) all its outputs. | ||||||||||||||
> ``` | ||||||||||||||
> nix build /nix/store/…foo.drv!out!out | ||||||||||||||
> ``` | ||||||||||||||
> would obtain the `out` output of whatever derivation `/nix/store/…foo.drv!out` produces. | ||||||||||||||
|
||||||||||||||
Now that we have `path` vs `path!*`, we also don't need `--derivation` as a disambiguator, and so that should be removed along with all the complexity that goes with it. | ||||||||||||||
(`toDerivedPathsWithHints` in the the nix commands should always be pure functions and not consult the store.) | ||||||||||||||
|
||||||||||||||
3. Extend the scheduler and derivation dependencies similarly: | ||||||||||||||
|
||||||||||||||
- Derivations can depend on the outputs of derivations that are themselves derivation outputs. | ||||||||||||||
The scheduler will substitute derivations to simplify dependencies as computed derivations are built, just like how floating content addressed derivations are realized. | ||||||||||||||
|
||||||||||||||
- Missing derivations get their own full fledged goals so they can be built, not just fetched from substituters. | ||||||||||||||
|
||||||||||||||
4. Add a new `outputOf` primop: | ||||||||||||||
|
||||||||||||||
`builtins.outputOf drv outputName` produces a placeholder string with the appropriate string context to access the output of that name produced by that derivation. | ||||||||||||||
The placeholder string is quite analogous to that used for floating content addressed derivation outputs. | ||||||||||||||
\[With just floating content-addressed derivations but no computed derivations, derivations are always known statically but their outputs aren't. | ||||||||||||||
With this RFC, since derivations themselves can floating CA derivation outputs, we also might not know them statically, so we need "deep" placeholders to account for arbitrary layers of dynamism. | ||||||||||||||
Ericson2314 marked this conversation as resolved.
Show resolved
Hide resolved
Ericson2314 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
This also corresponds to the use of arbitrary many `!` in the CLI.\] | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also makes it so that you don't need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How will a package definition look when it uses this functionality? We'll want to provide certain package attributes "statically", so they don't depend on anything in the derivation, so as not to necessitate IFD for Perhaps we could define "package" as an attrset with outputs, # mkPackage: tie the knot, return the `public` attrset, add `overridePackage` function
mkPackage (self: {
public = self.finalOutputs // {
# other public attrs we care about
version = "1.2.3";
tests = f self.public;
meta = {
description = /* ... */;
};
};
# An arbitrary internal attribute to allow overriding a lockfile
lockFile = /* ... */;
# Generate the derivation
drvDrv = stdenv.mkDerivation {
inherit (self.public) version;
inherit (self) lockFile;
outputHashMethodThingy = "text"; # output is a derivation (using wrong syntax, sorry)
/* ... */
};
finalOutputs =
# lib.packageOutputsOf wraps builtins.outputOf returning outputs in the usual format
lib.packageOutputsOf ["out"] self.drvDrv;
})
It probably needs some refinement, but I feel like this could be a good change for Nixpkgs. UPDATE: I don't think it should replace #119942, as a pragmatic choice to solve problems for the current packages without having to switch them over to |
||||||||||||||
|
||||||||||||||
## Deferred import from derivation. | ||||||||||||||
|
||||||||||||||
Create a new primop `assumeDerivation` that takes a single expression. | ||||||||||||||
It's semantics are as follows: | ||||||||||||||
|
||||||||||||||
- If the underlying expression evaluates (shallowly) without needing to build derivations (for `import`, `readFile`, etc.), and is a derivation, simply return that. | ||||||||||||||
``` | ||||||||||||||
e ⇓ e' | ||||||||||||||
e' is derivation | ||||||||||||||
------ | ||||||||||||||
builtins.assumeDerivation e ⇓ e' | ||||||||||||||
``` | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a section explaining this syntax? I assume it's familiar for PLT folks, but it's very cryptic to me :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are inference rules expressed in operational semantics. You can read that as I agree with asymmetric, we're not a PLT-focused symposium, prose is fine here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a more accessible presentation would be better. This is pseudo-formal, because (AFAIK) the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Unfortunately not everyone has that background. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A "translation" would suffice. Those compaxt bits of formalisms are great, once one has learned to parse them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well.....I'll put it this way:
So I am fine writing some sort of prose explanation, but I rather do it once the RFC is further along. P.S. whether you love our hate this stuff, https://www.youtube.com/watch?v=dCuZkaaou0Q is a fun talk. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could include a link to a short introduction to natural deduction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is gone we can remove this. |
||||||||||||||
|
||||||||||||||
- If the underlying expression cannot evaluate (shallowly) without building one or more paths, defer evaluation into a derivation-producing-derivation, and take it's output with `builtins.outputOf`: | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is "shallow evaluation" defined anywhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The phrase defines it on spot in the surrounding words. I had no difficulty parsing this. "Shallow: without building" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually mean weak head normal form. On analogy to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is removed from the RFC. |
||||||||||||||
``` | ||||||||||||||
e gets stuck on builds | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is "stuck"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "synchronous waiting" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. stuck term, term that cannot finish evaluating. Traditionally that would be an open term with a free variable "blocking" a ~~regex~ redex. I consider an unbuilt path that is to be inspected quite analogous as it also a "dangling" reference of sorts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
regex -> redex (reducible expression) |
||||||||||||||
defer = derivation { | ||||||||||||||
inputDrvs = builds; | ||||||||||||||
buildCommand = "nix-instantiate ${reified e} > $out" | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One issue that is somewhat independent of this RFC, but relevant for it to be useful: We also need a “standalone” There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure of that? Evaluation is current designed so that like it or not, writing drv files is a side effect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that at the time of writing I was not aware of recursive nix being possible in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup. I would want to limit recursive nix so one can just add paths to store (including DRVs), not build things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I think that should at least be mentioned for clarity. If I understand it correctly, then maybe the following derivation would be clearer: derivation {
buildCommand = "cp $(nix-instantiate ${reifed e}) $out";
} In this case, the inner (recursive) instantiation would add the derivation file to the outer store, but due to content addressability of derivations, its There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reification is rather hand-wavy. The language implementation can't serialize arbitrary closures. Such a feature isn't impossible but has consequences:
Compared to a somewhat naive serialization, another partial solution is to do "event sourcing" in the spirit of transient (Haskell). To implement that, the evaluator must only be accessible through a facade interface that tracks exactly how an evaluation came to be: which files were provided to it and which external traversals and function calls were made, etc. This method, if it even applies here, seems to solve the thunk dilemma, but doesn't solve the other problems and requires significant changes to the evaluator. The other problems remain unsolved. Is not serializing the closure an option? We could suspend the evaluation by just retaining the reference to the thunk, assigning it a random id and wait for the daemon to get back to us. This defeats the desired separation between the evaluation and build processes. It does let a derivation like I hope I've just misunderstood something, because I'd like this to succeed. I just don't see a good way to implement this. |
||||||||||||||
} | ||||||||||||||
------ | ||||||||||||||
builtins.assumeDerivation e ⇓ builtins.outputOf defer "out" | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
This allows downstream non-dynamic derivations to be evaluated without getting stuck on their dynamic upstream ones. | ||||||||||||||
They just depend on the derivation computed by the deferred eval derivations, and those substitutions are performed by the scheduler. | ||||||||||||||
|
||||||||||||||
# Examples and Interactions | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/hackage-packages.nix concretely change? |
||||||||||||||
[examples-and-interactions]: #examples-and-interactions | ||||||||||||||
|
||||||||||||||
Here is everything put together with a Haskell and `cabal2nix` example. | ||||||||||||||
|
||||||||||||||
Given the following code, and assuming `bar` will depend on `foo` | ||||||||||||||
```nix | ||||||||||||||
mkScope (self: { | ||||||||||||||
foo = builtins.assumeDerivation (self.callCabal2nix "foo" ./foo); | ||||||||||||||
bar = builtins.assumeDerivation (self.callCabal2nix "bar" ./bar); | ||||||||||||||
}) | ||||||||||||||
``` | ||||||||||||||
After some evaluation, we get something like the following derivations with dependencies: | ||||||||||||||
``` | ||||||||||||||
(cabal2nix foo)!out ----> deFoo = deferred eval (fooNix: self.callPackage fooNix) | ||||||||||||||
(cabal2nix bar)!out ----> deBar = deferred eval (barNix: self.callPackage barNix) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's only used to disambiguate this text and tell drv apart from deferred drv. However, I was wondering if this (or something similar) could be made a convention of assumeDerivation to more easily idententify asyncly built derivations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this example is a mess of unclear notation. (I am disgruntled by this in complete contrast to my standing by the sequent notation I like a lot.) |
||||||||||||||
``` | ||||||||||||||
and evaluated nix | ||||||||||||||
```nix | ||||||||||||||
mkScope (self: { | ||||||||||||||
foo = builtins.outputOf /nix/store/deFoo.drv "out"; | ||||||||||||||
bar = builtins.outputOf /nix/store/deBar.drv "out"; | ||||||||||||||
}) | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
If we then build `bar` we will get something steps like: | ||||||||||||||
|
||||||||||||||
1. ``` | ||||||||||||||
deBar!out | ||||||||||||||
``` | ||||||||||||||
2. Expand `deBar` in to see dep | ||||||||||||||
``` | ||||||||||||||
((cabal2nix bar)!out ----> (deferred eval (fooNix: self.callPackage barNix {}))!out | ||||||||||||||
``` | ||||||||||||||
3. Build cabal2nix and inline path for simplicity (or if cabal2nix job is floating CA derivation) | ||||||||||||||
``` | ||||||||||||||
(deferred eval self.callPackage built-barNix)!out | ||||||||||||||
``` | ||||||||||||||
4. Build deferred eval job and substitute | ||||||||||||||
``` | ||||||||||||||
deFoo!out ----> bar.drv!out | ||||||||||||||
``` | ||||||||||||||
5. Expand `deFoo` in to see dep | ||||||||||||||
``` | ||||||||||||||
((cabal2nix foo)!out ----> (deferred eval (fooNix: self.callPackage fooNix {}))!out ----> bar.drv!out | ||||||||||||||
``` | ||||||||||||||
6. Build cabal2nix and inline path for simplicity (or if cabal2nix job is floating CA derivation) | ||||||||||||||
``` | ||||||||||||||
(deferred eval self.callPackage built-fooNix)!out ----> bar.drv!out | ||||||||||||||
``` | ||||||||||||||
7. Build deferred eval job and substitute | ||||||||||||||
``` | ||||||||||||||
foo.drv!out ----> bar.drv!out | ||||||||||||||
``` | ||||||||||||||
8. Build foo and realize bar | ||||||||||||||
``` | ||||||||||||||
bar.drv[foo-path/foo!out]!out | ||||||||||||||
``` | ||||||||||||||
9. Build bar | ||||||||||||||
``` | ||||||||||||||
bar-path | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
The above is no doubt hard to read -- I am sorry about that --- but here are a few things to note: | ||||||||||||||
|
||||||||||||||
- The scheduler "substitutes on demand" giving us a lazy evaluation of sorts. | ||||||||||||||
This means that in the extreme case where we to make to, e.g., make a derivation for every C compiler invocation, we can avoid storing a very large completely static graph all at once. | ||||||||||||||
|
||||||||||||||
- At the same time, the derivations can be built in many different orders, so one can intentionally build all the `cabal2nix` derivations first and try to accumulate up the biggest static graph with `--dry-run`. | ||||||||||||||
This approximates what would happen in the "infinitely parallel" case when the scheduler will try to dole out work to do as fast as it can. | ||||||||||||||
|
||||||||||||||
# Drawbacks | ||||||||||||||
[drawbacks]: #drawbacks | ||||||||||||||
|
||||||||||||||
The main drawback is that these stub expressions are *only* "pure" derivations --- placeholder strings (with the proper string context) and not attrsets with all the niceties we are used to getting from `mkDerivation`. | ||||||||||||||
This is true even when the deferred evaluation in fact *does* use `mkDerivation` and would provide those niceties. | ||||||||||||||
For other sort of values, we have no choice but wait; that would require a fully incremental / deferral evaluation which is a completely separate feature not an extension of this. | ||||||||||||||
Concretely, our design means we cannot defer the `pname` `meta` etc. fields: either make do with the bare string `builtins.outputOf` provides, or *statically* add a fake `name` and `meta` etc. that must be manually synced with the deferred eval derivation if it is to match. | ||||||||||||||
|
||||||||||||||
# Alternatives | ||||||||||||||
[alternatives]: #alternatives | ||||||||||||||
|
||||||||||||||
- Do nothing, and continue to have no good answer for language-specific package managers. | ||||||||||||||
|
||||||||||||||
- Instead of deferring only certain portions of the evaluation with `builtins.asssumeDerivation`, simply restart the entire eval. | ||||||||||||||
Quite simple, and no existing IFD code today needs to change. | ||||||||||||||
|
||||||||||||||
- Abandon IFD and just let users use the underlying dynamic derivation feature. | ||||||||||||||
This was my first idea, but I became worried that this was too hard to use for simple experiments. | ||||||||||||||
|
||||||||||||||
# Unresolved questions | ||||||||||||||
[unresolved]: #unresolved-questions | ||||||||||||||
|
||||||||||||||
The exact way the outputs refer to the replacement derivations / their outputs is subject to bikeshedding. | ||||||||||||||
|
||||||||||||||
# Future work | ||||||||||||||
[future]: #future-work | ||||||||||||||
|
||||||||||||||
1. Actually use this stuff in Nixpkgs with modification to the existing "lang2nix" tools. | ||||||||||||||
This is the lowest hanging fruit and most import thing. | ||||||||||||||
|
||||||||||||||
2. Try to breach the build system package manager divide. | ||||||||||||||
Just as there are foreign packages graphs to convert to Nix, there are Ninja and Make graphs we can also convert to Nix. | ||||||||||||||
This might really help with big builds like Chromium and LLVM. | ||||||||||||||
|
||||||||||||||
3. Try to convince upstream tools to use Nix like CMake, Meson, etc. use Ninja. | ||||||||||||||
Rather than converting Ninja plans, we might convince those tools to have purpose-built Nix backends. | ||||||||||||||
Language-specific package mangers that don't use Ninja today might also be modified to "let Nix do that actual building". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.