aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/value
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2023-01-15 17:39:04 -0500
committerJohn Ericson <John.Ericson@Obsidian.Systems>2023-08-10 00:08:32 -0400
commit60b7121d2c6d4322b7c2e8e7acfec7b701b2d3a1 (patch)
treec07508902903edf2d1a11b135ddd2bb512819ea6 /src/libexpr/value
parentd00fe5f22559efc6f8b4b92eab537b08c0e43dee (diff)
Make the Derived Path family of types inductive for dynamic derivations
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>
Diffstat (limited to 'src/libexpr/value')
-rw-r--r--src/libexpr/value/context.cc93
-rw-r--r--src/libexpr/value/context.hh21
2 files changed, 75 insertions, 39 deletions
diff --git a/src/libexpr/value/context.cc b/src/libexpr/value/context.cc
index f76fc76e4..d8116011e 100644
--- a/src/libexpr/value/context.cc
+++ b/src/libexpr/value/context.cc
@@ -4,29 +4,52 @@
namespace nix {
-NixStringContextElem NixStringContextElem::parse(std::string_view s0)
+NixStringContextElem NixStringContextElem::parse(
+ std::string_view s0,
+ const ExperimentalFeatureSettings & xpSettings)
{
std::string_view s = s0;
+ std::function<SingleDerivedPath()> parseRest;
+ parseRest = [&]() -> SingleDerivedPath {
+ // Case on whether there is a '!'
+ size_t index = s.find("!");
+ if (index == std::string_view::npos) {
+ return SingleDerivedPath::Opaque {
+ .path = StorePath { s },
+ };
+ } else {
+ std::string output { s.substr(0, index) };
+ // Advance string to parse after the '!'
+ s = s.substr(index + 1);
+ auto drv = make_ref<SingleDerivedPath>(parseRest());
+ drvRequireExperiment(*drv, xpSettings);
+ return SingleDerivedPath::Built {
+ .drvPath = std::move(drv),
+ .output = std::move(output),
+ };
+ }
+ };
+
if (s.size() == 0) {
throw BadNixStringContextElem(s0,
"String context element should never be an empty string");
}
+
switch (s.at(0)) {
case '!': {
- s = s.substr(1); // advance string to parse after first !
- size_t index = s.find("!");
- // This makes index + 1 safe. Index can be the length (one after index
- // of last character), so given any valid character index --- a
- // successful find --- we can add one.
- if (index == std::string_view::npos) {
+ // Advance string to parse after the '!'
+ s = s.substr(1);
+
+ // Find *second* '!'
+ if (s.find("!") == std::string_view::npos) {
throw BadNixStringContextElem(s0,
"String content element beginning with '!' should have a second '!'");
}
- return NixStringContextElem::Built {
- .drvPath = StorePath { s.substr(index + 1) },
- .output = std::string(s.substr(0, index)),
- };
+
+ return std::visit(
+ [&](auto x) -> NixStringContextElem { return std::move(x); },
+ parseRest());
}
case '=': {
return NixStringContextElem::DrvDeep {
@@ -34,33 +57,51 @@ NixStringContextElem NixStringContextElem::parse(std::string_view s0)
};
}
default: {
- return NixStringContextElem::Opaque {
- .path = StorePath { s },
- };
+ // Ensure no '!'
+ if (s.find("!") != std::string_view::npos) {
+ throw BadNixStringContextElem(s0,
+ "String content element not beginning with '!' should not have a second '!'");
+ }
+ return std::visit(
+ [&](auto x) -> NixStringContextElem { return std::move(x); },
+ parseRest());
}
}
}
-std::string NixStringContextElem::to_string() const {
- return std::visit(overloaded {
+std::string NixStringContextElem::to_string() const
+{
+ std::string res;
+
+ std::function<void(const SingleDerivedPath &)> toStringRest;
+ toStringRest = [&](auto & p) {
+ std::visit(overloaded {
+ [&](const SingleDerivedPath::Opaque & o) {
+ res += o.path.to_string();
+ },
+ [&](const SingleDerivedPath::Built & o) {
+ res += o.output;
+ res += '!';
+ toStringRest(*o.drvPath);
+ },
+ }, p.raw());
+ };
+
+ std::visit(overloaded {
[&](const NixStringContextElem::Built & b) {
- std::string res;
- res += '!';
- res += b.output;
res += '!';
- res += b.drvPath.to_string();
- return res;
+ toStringRest(b);
+ },
+ [&](const NixStringContextElem::Opaque & o) {
+ toStringRest(o);
},
[&](const NixStringContextElem::DrvDeep & d) {
- std::string res;
res += '=';
res += d.drvPath.to_string();
- return res;
- },
- [&](const NixStringContextElem::Opaque & o) {
- return std::string { o.path.to_string() };
},
}, raw());
+
+ return res;
}
}
diff --git a/src/libexpr/value/context.hh b/src/libexpr/value/context.hh
index 287ae08a9..a1b71695b 100644
--- a/src/libexpr/value/context.hh
+++ b/src/libexpr/value/context.hh
@@ -3,7 +3,7 @@
#include "util.hh"
#include "comparator.hh"
-#include "path.hh"
+#include "derived-path.hh"
#include <variant>
@@ -31,11 +31,7 @@ public:
*
* Encoded as just the path: ‘<path>’.
*/
-struct NixStringContextElem_Opaque {
- StorePath path;
-
- GENERATE_CMP(NixStringContextElem_Opaque, me->path);
-};
+typedef SingleDerivedPath::Opaque NixStringContextElem_Opaque;
/**
* Path to a derivation and its entire build closure.
@@ -57,12 +53,7 @@ struct NixStringContextElem_DrvDeep {
*
* Encoded in the form ‘!<output>!<drvPath>’.
*/
-struct NixStringContextElem_Built {
- StorePath drvPath;
- std::string output;
-
- GENERATE_CMP(NixStringContextElem_Built, me->drvPath, me->output);
-};
+typedef SingleDerivedPath::Built NixStringContextElem_Built;
using _NixStringContextElem_Raw = std::variant<
NixStringContextElem_Opaque,
@@ -93,8 +84,12 @@ struct NixStringContextElem : _NixStringContextElem_Raw {
* - ‘<path>’
* - ‘=<path>’
* - ‘!<name>!<path>’
+ *
+ * @param xpSettings Stop-gap to avoid globals during unit tests.
*/
- static NixStringContextElem parse(std::string_view s);
+ static NixStringContextElem parse(
+ std::string_view s,
+ const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
std::string to_string() const;
};