Skip to content
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

Make the Derived Path family of types inductive for dynamic derivations #8369

Merged
merged 1 commit into from Aug 11, 2023

Conversation

Ericson2314
Copy link
Member

@Ericson2314 Ericson2314 commented May 18, 2023

Motivation

We want to be able to write down /nix/store/asdfasdfasdfasf-foo.drv^bar.drv^baz: /nix/store/asdfasdfasdfasf-foo.drv^bar.drv is the dynamic derivation (since it is itself a derivation output, bar.drv from foo.drv).

To that end, we create Single{Derivation,BuiltPath} types, that are very similar except instead of having multiple outputs (in a set or map), they have a single one. This is for everything to the left of the rightmost ^.

NixStringContextElem has an analogous change, and now can reuse SingleDerivedPath at the top level. In fact, if we ever get rid of DrvDeep, NixStringContextElem could be replaced with SingleDerivedPath entirely!

Context

Important note: some JSON formats have changed.

We already can produce dynamic derivations, but we can't refer to them directly. Today, we can merely express building or example at the top imperatively over time by building foo.drv^bar.drv, and then with a second nix invocation doing <result-from-first>^baz, but this is not declarative. The ethos of Nix of being able to write down the full plan everything you want to do, and then execute than plan with a single command, and for that we need the new inductive form of these types.

Depends on #8691
Depends on #8724

Not in scope

  • builtins.outputsOf
  • scheduling nested ^

Checklist for maintainers

Maintainers: tick if completed or explain if not relevant

  • agreed on idea
  • agreed on implementation strategy
  • tests, as appropriate
    • functional tests - tests/**.sh
    • unit tests - src/*/tests
    • integration tests - tests/nixos/*
  • documentation in the manual
  • documentation in the internal API docs
  • code and comments are self-explanatory
  • commit message explains why the change was made
  • new feature or incompatible change: updated release notes

Priorities

Add 👍 to pull requests you find important.

@Ericson2314 Ericson2314 added the RFC Related to an accepted RFC label May 18, 2023
@github-actions github-actions bot added new-cli Relating to the "nix" command repl The Read Eval Print Loop, "nix repl" command and debugger store Issues and pull requests concerning the Nix store with-tests Issues related to testing. PRs with tests have some priority labels May 18, 2023
@Ericson2314
Copy link
Member Author

Ericson2314 commented May 18, 2023

I am kinda of nauseated by how big this commit is, but at least it is the last semi-preparatory one changing data structures without adding much by way of new features. There are just a whole bunch of data structures that need to be changed at once.

Comment on lines 42 to 55
// TODO dedup with copy-paste in installables.cc
template<typename DP>
static StorePath getStorePathFrom(const DP & derivedPath)
{
return std::visit(overloaded {
[&](const typename DP::Built & bfd) {
return getStorePathFrom<SingleDerivedPath>(*bfd.drvPath);
},
[&](const typename DP::Opaque & bo) {
return bo.path;
},
}, derivedPath.raw());
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was skeptical of making this a method when it just occurred in installables.cc, because it is rarely the right thing and yet awfully tempting to use as the one DerivedPath -> StorePath (no Store, no extra IO) function. But now that we are using it twice, I guess it should be...

Copy link
Contributor

@fricklerhandwerk fricklerhandwerk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed collaboratively in Nix maintainer meeting:

Add a release note that the JSON output format has changed, and possibly that it was never documented to begin with. We could update the release note if we add docs before the next release.

src/libstore/derived-path.hh Outdated Show resolved Hide resolved
src/libstore/downstream-placeholder.cc Show resolved Hide resolved
src/libstore/downstream-placeholder.hh Outdated Show resolved Hide resolved
src/libstore/downstream-placeholder.hh Outdated Show resolved Hide resolved
src/libstore/misc.cc Outdated Show resolved Hide resolved
src/libstore/remote-store.cc Outdated Show resolved Hide resolved
src/libstore/tests/derived-path.cc Show resolved Hide resolved
return req;
},
[&](const SingleDerivedPath::Built & bfd0) -> SingleDerivedPath {
SingleDerivedPath::Built bfd {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is confusing, what does it mean?

auto & evalStore = evalStore_ ? *evalStore_ : store;

return std::visit(overloaded {
[&](const SingleDerivedPath::Opaque &) -> SingleDerivedPath {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roberth suggests: We may want to rename this to DerivablePath as it's not derived yet, and also the constructors to Constant and Output.

Ericson2314 added a commit to obsidiansystems/nix that referenced this pull request Jul 13, 2023
This is a part of CA derivations that we forgot to put behind the
experimental feature.

This was caught by @fricklerhandwerk in
NixOS#8369 (comment)
Ericson2314 added a commit to obsidiansystems/nix that referenced this pull request Jul 13, 2023
This is a part of CA derivations that we forgot to put behind the
experimental feature.

This was caught by @fricklerhandwerk in
NixOS#8369 (comment)
@dpulls
Copy link

dpulls bot commented Jul 14, 2023

🎉 All dependencies have been resolved !

@Ericson2314 Ericson2314 force-pushed the inductive-derived-path branch 3 times, most recently from 744a8a0 to 220fcc1 Compare July 14, 2023 12:31
@Ericson2314 Ericson2314 marked this pull request as draft July 17, 2023 14:10
Copy link
Member

@roberth roberth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First part of first round.

Note to self: resume review at src/libexpr/eval.cc

src/libstore/derived-path.hh Outdated Show resolved Hide resolved
tests/build.sh Outdated Show resolved Hide resolved
src/libcmd/installables.cc Outdated Show resolved Hide resolved
src/libcmd/installables.cc Outdated Show resolved Hide resolved
src/libcmd/installables.cc Outdated Show resolved Hide resolved
src/libexpr/eval-cache.cc Outdated Show resolved Hide resolved
@Ericson2314 Ericson2314 force-pushed the inductive-derived-path branch 2 times, most recently from ccfe59e to 88ba8d9 Compare July 20, 2023 19:48
Ericson2314 added a commit to obsidiansystems/nix that referenced this pull request Jul 20, 2023
This makes it more useful. In general, the derivation will be in one
store, and the realisation info is in another.

This also helps us avoid duplication. See how `resolveDerivedPath` is
now simpler because it uses `queryPartialDerivationOutputMap`. In NixOS#8369
we get more flavors of derived path, and need more code to resolve them
all, and this problem only gets worse.

The fact that we need a new method to deal with the multiple dispatch is
unfortunate, but this generally relates to the fact that `Store` is a
sub-par interface, too bulky/unwieldy and conflating separate concerns.
Solving that is out of scope of this PR.

This is part of the RFC 92 work. See tracking issue NixOS#6316
@dpulls
Copy link

dpulls bot commented Jul 21, 2023

🎉 All dependencies have been resolved !

@Ericson2314 Ericson2314 marked this pull request as ready for review July 21, 2023 12:55
@Ericson2314 Ericson2314 force-pushed the inductive-derived-path branch 2 times, most recently from bed2acb to 07c0d6b Compare July 21, 2023 13:19

GENERATE_CMP(DerivedPathOpaque, me->path);
};

struct SingleDerivedPath;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use a high-level description and/or depiction of what the various kinds of "Paths" are, their relationship, and intended meaning.

@@ -532,12 +532,12 @@ public:
StorePath coerceToStorePath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx);

/**
* Part of `coerceToDerivedPath()` without any store IO which is exposed for unit testing only.
* Part of `coerceToSingleDerivedPath()` without any store IO which is exposed for unit testing only.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would the check be?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be reading the derivation to check that it in fact has the outputs requested. But do we want to improve the error message for this in this already large PR? I think the division of labor between these functions did not change, right? So improving this message seems orthogonal?

Happy to do afterwords, however.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#8780 made and assigned to self.

* much as possible, but where the derivation resolution doesn't exists, leave
* as-is.
*/
SingleDerivedPath tryResolveDerivedPath(Store &, const SingleDerivedPath &, Store * evalStore = nullptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to wrap my head around this one, but other than that, I've completed a review of the full diff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not even sure if it is needed, tbh.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it is just used by Installables::toDerivations (virtual method, multiple implementations). The idea is that we want to try to get the "one up" prior derivation, and fail if it is not built.

But maybe this is bad for the same reason --derivation is bad: be explicit, and pass what you mean instead.

Either way, maybe we should just just unconditionally build the thing, and its up to you to pass some sort of --must-be-already-built if one doesn't want that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it, and there was problem! It's in obsidiansystems@try-resolve-derived-path if we ever need it later. it is actually needed later, so it is just part of the main PR now.

@Ericson2314

This comment was marked as resolved.

We want to be able to write down `foo.drv^bar.drv^baz`:
`foo.drv^bar.drv` is the dynamic derivation (since it is itself a
derivation output, `bar.drv` from `foo.drv`).

To that end, we create `Single{Derivation,BuiltPath}` types, that are
very similar except instead of having multiple outputs (in a set or
map), they have a single one. This is for everything to the left of the
rightmost `^`.

`NixStringContextElem` has an analogous change, and now can reuse
`SingleDerivedPath` at the top level. In fact, if we ever get rid of
`DrvDeep`, `NixStringContextElem` could be replaced with
`SingleDerivedPath` entirely!

Important note: some JSON formats have changed.

We already can *produce* dynamic derivations, but we can't refer to them
directly. Today, we can merely express building or example at the top
imperatively over time by building `foo.drv^bar.drv`, and then with a
second nix invocation doing `<result-from-first>^baz`, but this is not
declarative. The ethos of Nix of being able to write down the full plan
everything you want to do, and then execute than plan with a single
command, and for that we need the new inductive form of these types.

Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Copy link
Contributor

@tomberek tomberek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed. Nothing particularly surprising; additional changes will expand the scope too much. Looking forward to the final upcoming steps that bring in the user-facing changes.

@tomberek tomberek merged commit 010dc79 into NixOS:master Aug 11, 2023
8 checks passed
@Ericson2314 Ericson2314 deleted the inductive-derived-path branch August 11, 2023 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new-cli Relating to the "nix" command repl The Read Eval Print Loop, "nix repl" command and debugger RFC Related to an accepted RFC store Issues and pull requests concerning the Nix store with-tests Issues related to testing. PRs with tests have some priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants