aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
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
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')
-rw-r--r--src/libexpr/eval-cache.cc2
-rw-r--r--src/libexpr/eval.cc59
-rw-r--r--src/libexpr/eval.hh10
-rw-r--r--src/libexpr/primops.cc15
-rw-r--r--src/libexpr/primops/context.cc7
-rw-r--r--src/libexpr/tests/derived-path.cc26
-rw-r--r--src/libexpr/tests/value/context.cc70
-rw-r--r--src/libexpr/value/context.cc93
-rw-r--r--src/libexpr/value/context.hh21
9 files changed, 191 insertions, 112 deletions
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 9e734e654..6a60ba87b 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -599,7 +599,7 @@ string_t AttrCursor::getStringWithContext()
return d.drvPath;
},
[&](const NixStringContextElem::Built & b) -> const StorePath & {
- return b.drvPath;
+ return b.drvPath->getBaseStorePath();
},
[&](const NixStringContextElem::Opaque & o) -> const StorePath & {
return o.path;
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index a8e6baea6..3e521b732 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1042,7 +1042,7 @@ void EvalState::mkOutputString(
: DownstreamPlaceholder::unknownCaOutput(drvPath, outputName, xpSettings).render(),
NixStringContext {
NixStringContextElem::Built {
- .drvPath = drvPath,
+ .drvPath = makeConstantStorePathRef(drvPath),
.output = outputName,
}
});
@@ -2299,7 +2299,7 @@ StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, NixStringCon
}
-std::pair<DerivedPath, std::string_view> EvalState::coerceToDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx)
+std::pair<SingleDerivedPath, std::string_view> EvalState::coerceToSingleDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx)
{
NixStringContext context;
auto s = forceString(v, context, pos, errorCtx);
@@ -2310,21 +2310,16 @@ std::pair<DerivedPath, std::string_view> EvalState::coerceToDerivedPathUnchecked
s, csize)
.withTrace(pos, errorCtx).debugThrow<EvalError>();
auto derivedPath = std::visit(overloaded {
- [&](NixStringContextElem::Opaque && o) -> DerivedPath {
- return DerivedPath::Opaque {
- .path = std::move(o.path),
- };
+ [&](NixStringContextElem::Opaque && o) -> SingleDerivedPath {
+ return std::move(o);
},
- [&](NixStringContextElem::DrvDeep &&) -> DerivedPath {
+ [&](NixStringContextElem::DrvDeep &&) -> SingleDerivedPath {
error(
"string '%s' has a context which refers to a complete source and binary closure. This is not supported at this time",
s).withTrace(pos, errorCtx).debugThrow<EvalError>();
},
- [&](NixStringContextElem::Built && b) -> DerivedPath {
- return DerivedPath::Built {
- .drvPath = std::move(b.drvPath),
- .outputs = OutputsSpec::Names { std::move(b.output) },
- };
+ [&](NixStringContextElem::Built && b) -> SingleDerivedPath {
+ return std::move(b);
},
}, ((NixStringContextElem &&) *context.begin()).raw());
return {
@@ -2334,12 +2329,12 @@ std::pair<DerivedPath, std::string_view> EvalState::coerceToDerivedPathUnchecked
}
-DerivedPath EvalState::coerceToDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx)
+SingleDerivedPath EvalState::coerceToSingleDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx)
{
- auto [derivedPath, s_] = coerceToDerivedPathUnchecked(pos, v, errorCtx);
+ auto [derivedPath, s_] = coerceToSingleDerivedPathUnchecked(pos, v, errorCtx);
auto s = s_;
std::visit(overloaded {
- [&](const DerivedPath::Opaque & o) {
+ [&](const SingleDerivedPath::Opaque & o) {
auto sExpected = store->printStorePath(o.path);
if (s != sExpected)
error(
@@ -2347,25 +2342,27 @@ DerivedPath EvalState::coerceToDerivedPath(const PosIdx pos, Value & v, std::str
s, sExpected)
.withTrace(pos, errorCtx).debugThrow<EvalError>();
},
- [&](const DerivedPath::Built & b) {
- // TODO need derived path with single output to make this
- // total. Will add as part of RFC 92 work and then this is
- // cleaned up.
- auto output = *std::get<OutputsSpec::Names>(b.outputs).begin();
-
- auto drv = store->readDerivation(b.drvPath);
- auto i = drv.outputs.find(output);
- if (i == drv.outputs.end())
- throw Error("derivation '%s' does not have output '%s'", store->printStorePath(b.drvPath), output);
- auto optOutputPath = i->second.path(*store, drv.name, output);
- // This is testing for the case of CA derivations
- auto sExpected = optOutputPath
- ? store->printStorePath(*optOutputPath)
- : DownstreamPlaceholder::unknownCaOutput(b.drvPath, output).render();
+ [&](const SingleDerivedPath::Built & b) {
+ auto sExpected = std::visit(overloaded {
+ [&](const SingleDerivedPath::Opaque & o) {
+ auto drv = store->readDerivation(o.path);
+ auto i = drv.outputs.find(b.output);
+ if (i == drv.outputs.end())
+ throw Error("derivation '%s' does not have output '%s'", b.drvPath->to_string(*store), b.output);
+ auto optOutputPath = i->second.path(*store, drv.name, b.output);
+ // This is testing for the case of CA derivations
+ return optOutputPath
+ ? store->printStorePath(*optOutputPath)
+ : DownstreamPlaceholder::fromSingleDerivedPathBuilt(b).render();
+ },
+ [&](const SingleDerivedPath::Built & o) {
+ return DownstreamPlaceholder::fromSingleDerivedPathBuilt(b).render();
+ },
+ }, b.drvPath->raw());
if (s != sExpected)
error(
"string '%s' has context with the output '%s' from derivation '%s', but the string is not the right placeholder for this derivation output. It should be '%s'",
- s, output, store->printStorePath(b.drvPath), sExpected)
+ s, b.output, b.drvPath->to_string(*store), sExpected)
.withTrace(pos, errorCtx).debugThrow<EvalError>();
}
}, derivedPath.raw());
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 29d0f05a1..0268a2a12 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -22,7 +22,7 @@ namespace nix {
class Store;
class EvalState;
class StorePath;
-struct DerivedPath;
+struct SingleDerivedPath;
enum RepairFlag : bool;
@@ -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.
*/
- std::pair<DerivedPath, std::string_view> coerceToDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx);
+ std::pair<SingleDerivedPath, std::string_view> coerceToSingleDerivedPathUnchecked(const PosIdx pos, Value & v, std::string_view errorCtx);
/**
- * Coerce to `DerivedPath`.
+ * Coerce to `SingleDerivedPath`.
*
* Must be a string which is either a literal store path or a
* "placeholder (see `DownstreamPlaceholder`).
@@ -551,7 +551,7 @@ public:
* source of truth, and ultimately tells us what we want, and then
* we ensure the string corresponds to it.
*/
- DerivedPath coerceToDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx);
+ SingleDerivedPath coerceToSingleDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx);
public:
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 430607214..54943b481 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -56,7 +56,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
.drvPath = b.drvPath,
.outputs = OutputsSpec::Names { b.output },
});
- ensureValid(b.drvPath);
+ ensureValid(b.drvPath->getBaseStorePath());
},
[&](const NixStringContextElem::Opaque & o) {
auto ctxS = store->printStorePath(o.path);
@@ -77,7 +77,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
if (!evalSettings.enableImportFromDerivation)
debugThrowLastTrace(Error(
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
- store->printStorePath(drvs.begin()->drvPath)));
+ drvs.begin()->to_string(*store)));
/* Build/substitute the context. */
std::vector<DerivedPath> buildReqs;
@@ -95,7 +95,11 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
/* Get all the output paths corresponding to the placeholders we had */
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
res.insert_or_assign(
- DownstreamPlaceholder::unknownCaOutput(drv.drvPath, outputName).render(),
+ DownstreamPlaceholder::fromSingleDerivedPathBuilt(
+ SingleDerivedPath::Built {
+ .drvPath = drv.drvPath,
+ .output = outputName,
+ }).render(),
store->printStorePath(outputPath)
);
}
@@ -1251,7 +1255,10 @@ drvName, Bindings * attrs, Value & v)
}
},
[&](const NixStringContextElem::Built & b) {
- drv.inputDrvs[b.drvPath].insert(b.output);
+ if (auto * p = std::get_if<DerivedPath::Opaque>(&*b.drvPath))
+ drv.inputDrvs[p->path].insert(b.output);
+ else
+ throw UnimplementedError("Dependencies on the outputs of dynamic derivations are not yet supported");
},
[&](const NixStringContextElem::Opaque & o) {
drv.inputSrcs.insert(o.path);
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index 8b3468009..bfc731744 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -106,7 +106,10 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
contextInfos[std::move(d.drvPath)].allOutputs = true;
},
[&](NixStringContextElem::Built && b) {
- contextInfos[std::move(b.drvPath)].outputs.emplace_back(std::move(b.output));
+ // FIXME should eventually show string context as is, no
+ // resolving here.
+ auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
+ contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
},
[&](NixStringContextElem::Opaque && o) {
contextInfos[std::move(o.path)].path = true;
@@ -222,7 +225,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
for (auto elem : iter->value->listItems()) {
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
context.emplace(NixStringContextElem::Built {
- .drvPath = namePath,
+ .drvPath = makeConstantStorePathRef(namePath),
.output = std::string { outputName },
});
}
diff --git a/src/libexpr/tests/derived-path.cc b/src/libexpr/tests/derived-path.cc
index c713fe28a..2a5ca64f6 100644
--- a/src/libexpr/tests/derived-path.cc
+++ b/src/libexpr/tests/derived-path.cc
@@ -21,12 +21,12 @@ TEST_F(DerivedPathExpressionTest, force_init)
RC_GTEST_FIXTURE_PROP(
DerivedPathExpressionTest,
prop_opaque_path_round_trip,
- (const DerivedPath::Opaque & o))
+ (const SingleDerivedPath::Opaque & o))
{
auto * v = state.allocValue();
state.mkStorePathString(o.path, *v);
- auto d = state.coerceToDerivedPath(noPos, *v, "");
- RC_ASSERT(DerivedPath { o } == d);
+ auto d = state.coerceToSingleDerivedPath(noPos, *v, "");
+ RC_ASSERT(SingleDerivedPath { o } == d);
}
// TODO use DerivedPath::Built for parameter once it supports a single output
@@ -46,12 +46,12 @@ RC_GTEST_FIXTURE_PROP(
auto * v = state.allocValue();
state.mkOutputString(*v, drvPath, outputName.name, std::nullopt, mockXpSettings);
- auto [d, _] = state.coerceToDerivedPathUnchecked(noPos, *v, "");
- DerivedPath::Built b {
- .drvPath = drvPath,
- .outputs = OutputsSpec::Names { outputName.name },
+ auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "");
+ SingleDerivedPath::Built b {
+ .drvPath = makeConstantStorePathRef(drvPath),
+ .output = outputName.name,
};
- RC_ASSERT(DerivedPath { b } == d);
+ RC_ASSERT(SingleDerivedPath { b } == d);
}
RC_GTEST_FIXTURE_PROP(
@@ -61,12 +61,12 @@ RC_GTEST_FIXTURE_PROP(
{
auto * v = state.allocValue();
state.mkOutputString(*v, drvPath, outputName.name, outPath);
- auto [d, _] = state.coerceToDerivedPathUnchecked(noPos, *v, "");
- DerivedPath::Built b {
- .drvPath = drvPath,
- .outputs = OutputsSpec::Names { outputName.name },
+ auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "");
+ SingleDerivedPath::Built b {
+ .drvPath = makeConstantStorePathRef(drvPath),
+ .output = outputName.name,
};
- RC_ASSERT(DerivedPath { b } == d);
+ RC_ASSERT(SingleDerivedPath { b } == d);
}
} /* namespace nix */
diff --git a/src/libexpr/tests/value/context.cc b/src/libexpr/tests/value/context.cc
index 0d9381577..c56b50b59 100644
--- a/src/libexpr/tests/value/context.cc
+++ b/src/libexpr/tests/value/context.cc
@@ -8,6 +8,8 @@
namespace nix {
+// Test a few cases of invalid string context elements.
+
TEST(NixStringContextElemTest, empty_invalid) {
EXPECT_THROW(
NixStringContextElem::parse(""),
@@ -38,6 +40,10 @@ TEST(NixStringContextElemTest, slash_invalid) {
BadStorePath);
}
+/**
+ * Round trip (string <-> data structure) test for
+ * `NixStringContextElem::Opaque`.
+ */
TEST(NixStringContextElemTest, opaque) {
std::string_view opaque = "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
auto elem = NixStringContextElem::parse(opaque);
@@ -47,6 +53,10 @@ TEST(NixStringContextElemTest, opaque) {
ASSERT_EQ(elem.to_string(), opaque);
}
+/**
+ * Round trip (string <-> data structure) test for
+ * `NixStringContextElem::DrvDeep`.
+ */
TEST(NixStringContextElemTest, drvDeep) {
std::string_view drvDeep = "=g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
auto elem = NixStringContextElem::parse(drvDeep);
@@ -56,28 +66,62 @@ TEST(NixStringContextElemTest, drvDeep) {
ASSERT_EQ(elem.to_string(), drvDeep);
}
-TEST(NixStringContextElemTest, built) {
+/**
+ * Round trip (string <-> data structure) test for a simpler
+ * `NixStringContextElem::Built`.
+ */
+TEST(NixStringContextElemTest, built_opaque) {
std::string_view built = "!foo!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
auto elem = NixStringContextElem::parse(built);
auto * p = std::get_if<NixStringContextElem::Built>(&elem);
ASSERT_TRUE(p);
ASSERT_EQ(p->output, "foo");
- ASSERT_EQ(p->drvPath, StorePath { built.substr(5) });
+ ASSERT_EQ(*p->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
+ .path = StorePath { built.substr(5) },
+ }));
ASSERT_EQ(elem.to_string(), built);
}
+/**
+ * Round trip (string <-> data structure) test for a more complex,
+ * inductive `NixStringContextElem::Built`.
+ */
+TEST(NixStringContextElemTest, built_built) {
+ /**
+ * We set these in tests rather than the regular globals so we don't have
+ * to worry about race conditions if the tests run concurrently.
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+ mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
+
+ std::string_view built = "!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
+ auto elem = NixStringContextElem::parse(built, mockXpSettings);
+ auto * p = std::get_if<NixStringContextElem::Built>(&elem);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->output, "foo");
+ auto * drvPath = std::get_if<SingleDerivedPath::Built>(&*p->drvPath);
+ ASSERT_TRUE(drvPath);
+ ASSERT_EQ(drvPath->output, "bar");
+ ASSERT_EQ(*drvPath->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
+ .path = StorePath { built.substr(9) },
+ }));
+ ASSERT_EQ(elem.to_string(), built);
}
-namespace rc {
-using namespace nix;
+/**
+ * Without the right experimental features enabled, we cannot parse a
+ * complex inductive string context element.
+ */
+TEST(NixStringContextElemTest, built_built_xp) {
+ ASSERT_THROW(
+ NixStringContextElem::parse("!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"), MissingExperimentalFeature);
+}
-Gen<NixStringContextElem::Opaque> Arbitrary<NixStringContextElem::Opaque>::arbitrary()
-{
- return gen::just(NixStringContextElem::Opaque {
- .path = *gen::arbitrary<StorePath>(),
- });
}
+namespace rc {
+using namespace nix;
+
Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arbitrary()
{
return gen::just(NixStringContextElem::DrvDeep {
@@ -85,14 +129,6 @@ Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arb
});
}
-Gen<NixStringContextElem::Built> Arbitrary<NixStringContextElem::Built>::arbitrary()
-{
- return gen::just(NixStringContextElem::Built {
- .drvPath = *gen::arbitrary<StorePath>(),
- .output = (*gen::arbitrary<StorePathName>()).name,
- });
-}
-
Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
{
switch (*gen::inRange<uint8_t>(0, std::variant_size_v<NixStringContextElem::Raw>)) {
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;
};