-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
WIP: deterministic evaluation of expressions/derivations #709
Conversation
Ah, I saw the original proposal but didn't understand it until now. "source closure" is the perfect word for this, is it just the analog of "run closure" and "build closure" for an earlier phase. Here's a few thoughts:
I'm thinking just as we store
Note the intensional store ought to automatically de-dup and rehash the contents of directories, so this will become a non-issue
Hmm that might be a feature not a bug :). Certainly we can skip that for fixed output derivations, and certainly we need to do that for @copumpkin's proposed non-determinstic derivations [I view this + that is key to both succeeding]. Normal derivations are kinda a middle ground, and I'd be OK making that configurable. I'd like to decouple this from surface UIs as much as possible. In that vain, can we make the following equivalent? nix-build '<nixpkgs>' -A vim --record
nix-build --record -E '(import <nixpkgs> {}).vim'
nix-build --record -E <(echo '(import <nixpkgs> {}).vim')
nix-build $(nix-instantiate --record -E '(import <nixpkgs> {}).vim') Besides exporting, our notion of boot generations absolutely needs to use this. Can be used to restore channel version when booting (though long term I hope to replace channels with things related to this) so that rebuilding of old boot generation yields the same thing. Down the road, It would be great if these were composable. I.e. |
Yes, that's exactly what happens. Currently, I already instantiate the source derivation in
Yes, storing source clojure->drv sounds good (this should probably be stored somewhere in the nix metadata, but I'm not familiar with that part). Then we still only get an n-to-1 map of possible sources for this derivation. With an other indirection we would be able to point back to the source 1-to-1.
hm, I don't see your argument here. Currently, every .nix-file gets copied into its own store path and we save a mapping to all of them. I think we want to instead save a mapping to the nixpkgs directory. Then we also automatically link related metadata like the git revision of the nixpkgs file.
Yes, that's possible. Something similar already happens for
Sounds like a good idea! |
Sorry, what do you mean?
How do we recover a 1-1 map? If you mean tracking source closure -> drv directly, I think that's a bit of a false mapping because the source closure should not influence the final build except through the drv.
Ah good point. IMO however nix treats the the path keys should be reified into the nix expression language so that we can do |
I currently generate a derivation for 'build source closure' and then build it. For the 1-1 mapping, the idea is to create a new derivation which consists of symlinks to the normal derivation and the source closure. |
corepkgs/reproducable-derivation.nix
Outdated
args = ["-e" (__toFile "name" '' | ||
#!/bin/bash | ||
${coreutils}/mkdir -p $out/nix-support | ||
${coreutils}/cat << EOF > $out/nix-support/source |
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.
''
${coreutils}/cp ${__toFile "result" result}$ $out/nix-support/source
''
Would this be more resilient to escaping problems?
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.
done
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.
Sweet!
Well, that's not 1-1 either as the same derivation could be symlinked in different places. |
@lethalman This would provide a way to do imperative package management on top declarative package management, as I brought up in the UX issue. |
I just realized that the current user name is a further impurity which isn't handled yet by this patch:
|
…ange because the result included in the primop argument)
…opied to the store. Well, they're still not inverse because a path gets converted to a string, but that doesn't seem to be a problem so far
…ayed currently only supported for nix-instantiate (and thus probably also nix-build), not yet nix-env add --playback option to play back the exact command So the interface is now: "NIX_RECORDING=filename nix-instantiate ..." to record the nix-instantiate command and "nix-instantiate --replay filename" to play it back commands in file
fix json encoding
In combination with nix channels (or the newer http download), the whole channel is treated as one input
we write 3 files to the nix closure: -> default.nix: just imports the other two and can be imported to get the exact reproducable output we want to have -> nix-support/recording.nix: recordings of all impurities i. e. function calls and imported files -> nix-support/expressions.nix: the expression that will be executed for the expression, a primop __findAlongAttrPath that does the supplies the autobindings along the attrpath. for the playback, a new primop prim_playback has been introduced. It's first parameter is a description of all impurities (as written to expression.nix). After this function is invoked, all subsequent calls can use the functions introduced here. In general, we allow multiple prim_playback when they don't conflict (not in all cases conflicts are found yet) It would also be cool to make the prim_playback more local, e. g. it should only apply to a specific term, but it's unclear to me how this can be achieved in a lazy language.
5ca5dda
to
eca9dec
Compare
…k to recorded source closure (not registered in garbage collection)
After more experience with nix, I think now, that there's a better way to implement this with much less intrusion inside nix. With |
I was thinking about that a while ago but doesn't scopedImport not
|
@copumpkin yes, but you can also overwrite import |
Oh, I see
|
(in fact, you can overwrite almost any builtin things in nix, also operators like < and so on) |
@copumpkin you won't get the caching of imports this way, I don't know how big a performance deal this is. But doing anything about that would be premature optimation. |
Caching of imports actually seems like a biggish deal, given how they seem like the sort of thing that can grow really quickly. Do you have any objection to our current approach or does this just seem simpler to you?
|
@copumpkin |
I look forward to seeing it 😄 what primops would you need to add, then? |
I suppose |
OOO, easy hashing experimentation would be really useful for #859 (comment) |
For me, I was mostly just talking about how to inject the "recording" into the resulting derivation hash (do you do it with the main derivation or as a post-processing step, really). That seems more tractable than a general hashing generalization 😄 |
hm, already too much time is gone since I last looked here... |
Is this PR dead? Or are people still dabbling with it? |
I marked this as stale due to inactivity. → More info |
I closed this issue due to inactivity. → More info |
This pull request introduces an option to nix-instantiate and nix-env to record all impure dependencies of a nix expression and save it in a json file, thus getting a "pure" nix expression, so it can later be replayed.
This is originally from #553 and a discussion on nixconf with @copumpkin.
We later started hacking this on the following sprint with help from @edolstra.
Usage
Currently, it can be invoked for example like this:
Then an additional derivation /nix/store/...-source-closure gets build which contains a json file consisting of all impurities associated with this build including a closure of all used build files. For example
nix-build --record -A vim "<nixpkgs>"
results in the following json file (pretty printed with python -m 'tool.json').With
nix-store --export $(nix-store -qR /nix/store/5wxzlhm2s98rq97ml96bsb0j4d2ydy3k-source-closure)
we can then bundle the complete source closure. (the dependencies can be seen withnix-store -q --tree /nix/store/...-source-closure/
)In the json file, there are basically the command line arguments to nix-instantiate, calls of impure functions with results (so they can be replayed) and a mapping from paths to store-paths for included files (like .nix, .patch etc.), the closure consists of all the filepaths which are values in the sources map (the keys are not needed).
This should then be reproducable with
This command should currently be usable with nix-instantiate, nix-build and nix-shell; nix-env is not yet covered.
Furthermore, you could alter some things in the json file to create a different, but still reproducable behavior (currently the replay fails when the program wants to access anything else, we could add a mode which generates a new json file with additional sources/impurities though).
TODO
The basic playback works, there are a few things that still need to be done
Important
Nice to have
<hash
>-<name
> (*)Update: just implemented this with a refactoring to produce nix-files instead of json-files
Bugs
nix-instantiate --eval
, no derivations are build, so the source clojure isn't build either (and there's an error message: cannot build missing derivation)Further ideas