diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bcdd77eaa7d..58663d42f52 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -5453,4 +5453,61 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings) evalFile(derivationInternal, *vDerivation); } +static void prim_derivationOf(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + NixStringContext context; + auto s = state.coerceToString(pos, *args[0], context, + "while evaluating the argument passed to builtins.derivationOf", + false, false).toOwned(); + + if (s.empty() || !state.store->isStorePath(s)) { + state.error("'%s' is not a valid store path", s) + .atPos(pos) + .debugThrow(); + } + + if (context.empty()) { + state.error("'%s' has no derivation in its context", s) + .atPos(pos) + .debugThrow(); + } + + if (context.size() > 1) { + state.error("'%s' has more than one item in its context", s) + .atPos(pos) + .debugThrow(); + } + + // we have exactly one context item. + auto & c = *context.begin(); + if (auto * b = std::get_if(&c.raw)) { + auto drvPath = b->drvPath->getBaseStorePath(); + v.mkString(state.store->printStorePath(drvPath), + NixStringContext{NixStringContextElem::Opaque{.path = drvPath}}, + state.mem); + return; + } + if (auto * d = std::get_if(&c.raw)) { + v.mkString(state.store->printStorePath(d->drvPath), + NixStringContext{NixStringContextElem::Opaque{.path = d->drvPath}}, + state.mem); + return; + } + + // Context item exists but is not a derivation + state.error("'%s' has no derivation in its context", s) + .atPos(pos) + .debugThrow(); +} + +static RegisterPrimOp primop_derivationOf({ + .name = "derivationOf", + .args = {"s"}, + .doc = R"( + Return the store path of the derivation that produces the given output path. + The string must have a derivation in its string context. + )", + .fun = prim_derivationOf, +}); + } // namespace nix diff --git a/tests/functional/lang/eval-fail-derivationOf-attrset.err.exp b/tests/functional/lang/eval-fail-derivationOf-attrset.err.exp new file mode 100644 index 00000000000..aa0098fa906 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-attrset.err.exp @@ -0,0 +1,10 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-attrset.nix:1:1: + 1| builtins.derivationOf { foo = "bar"; } + | ^ + 2| + + … while evaluating the argument passed to builtins.derivationOf + + error: cannot coerce a set to a string: { foo = "bar"; } diff --git a/tests/functional/lang/eval-fail-derivationOf-attrset.nix b/tests/functional/lang/eval-fail-derivationOf-attrset.nix new file mode 100644 index 00000000000..67c7c5ae2dd --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-attrset.nix @@ -0,0 +1 @@ +builtins.derivationOf { foo = "bar"; } diff --git a/tests/functional/lang/eval-fail-derivationOf-empty-string.err.exp b/tests/functional/lang/eval-fail-derivationOf-empty-string.err.exp new file mode 100644 index 00000000000..c6c5031d646 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-empty-string.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-empty-string.nix:1:1: + 1| builtins.derivationOf "" + | ^ + 2| + + error: '' is not a valid store path diff --git a/tests/functional/lang/eval-fail-derivationOf-empty-string.nix b/tests/functional/lang/eval-fail-derivationOf-empty-string.nix new file mode 100644 index 00000000000..a71cdc5b5a7 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-empty-string.nix @@ -0,0 +1 @@ +builtins.derivationOf "" diff --git a/tests/functional/lang/eval-fail-derivationOf-list.err.exp b/tests/functional/lang/eval-fail-derivationOf-list.err.exp new file mode 100644 index 00000000000..1b616195f00 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-list.err.exp @@ -0,0 +1,10 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-list.nix:1:1: + 1| builtins.derivationOf [ "foo" "bar" ] + | ^ + 2| + + … while evaluating the argument passed to builtins.derivationOf + + error: cannot coerce a list to a string: [ "foo" "bar" ] diff --git a/tests/functional/lang/eval-fail-derivationOf-list.nix b/tests/functional/lang/eval-fail-derivationOf-list.nix new file mode 100644 index 00000000000..c02192415f4 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-list.nix @@ -0,0 +1 @@ +builtins.derivationOf [ "foo" "bar" ] diff --git a/tests/functional/lang/eval-fail-derivationOf-multiple-contexts.err.exp b/tests/functional/lang/eval-fail-derivationOf-multiple-contexts.err.exp new file mode 100644 index 00000000000..76f8d8d47d9 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-multiple-contexts.err.exp @@ -0,0 +1,9 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-multiple-contexts.nix:15:1: + 14| in + 15| builtins.derivationOf multipleContexts + | ^ + 16| + + error: '/nix/store/w190vpfkz3a127yhsd3z35rh02nra1l8-test1/nix/store/8xsds8xaq3yf169w0k54j23ir24gnmwi-test2' is not a valid store path diff --git a/tests/functional/lang/eval-fail-derivationOf-multiple-contexts.nix b/tests/functional/lang/eval-fail-derivationOf-multiple-contexts.nix new file mode 100644 index 00000000000..a8862e4cdc7 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-multiple-contexts.nix @@ -0,0 +1,15 @@ +let + drv1 = derivation { + name = "test1"; + builder = "/bin/false"; + system = "x86_64-linux"; + }; + drv2 = derivation { + name = "test2"; + builder = "/bin/false"; + system = "x86_64-linux"; + }; + # Create a string with multiple context items by concatenating paths from different derivations + multipleContexts = "${drv1.outPath}${drv2.outPath}"; +in +builtins.derivationOf multipleContexts diff --git a/tests/functional/lang/eval-fail-derivationOf-nix-path.err.exp b/tests/functional/lang/eval-fail-derivationOf-nix-path.err.exp new file mode 100644 index 00000000000..0b67bbf67de --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-nix-path.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-nix-path.nix:1:1: + 1| builtins.derivationOf ./. + | ^ + 2| + + error: '/pwd/lang' is not a valid store path diff --git a/tests/functional/lang/eval-fail-derivationOf-nix-path.nix b/tests/functional/lang/eval-fail-derivationOf-nix-path.nix new file mode 100644 index 00000000000..3260eaa77b9 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-nix-path.nix @@ -0,0 +1 @@ +builtins.derivationOf ./. diff --git a/tests/functional/lang/eval-fail-derivationOf-no-context-store-path.err.exp b/tests/functional/lang/eval-fail-derivationOf-no-context-store-path.err.exp new file mode 100644 index 00000000000..30cb3106577 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-no-context-store-path.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-no-context-store-path.nix:1:1: + 1| builtins.derivationOf "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-test" + | ^ + 2| + + error: '/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-test' has no derivation in its context diff --git a/tests/functional/lang/eval-fail-derivationOf-no-context-store-path.nix b/tests/functional/lang/eval-fail-derivationOf-no-context-store-path.nix new file mode 100644 index 00000000000..f3ef96d1e2e --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-no-context-store-path.nix @@ -0,0 +1 @@ +builtins.derivationOf "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-test" diff --git a/tests/functional/lang/eval-fail-derivationOf-no-context.err.exp b/tests/functional/lang/eval-fail-derivationOf-no-context.err.exp new file mode 100644 index 00000000000..33a6bfb5d94 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-no-context.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-no-context.nix:1:1: + 1| builtins.derivationOf "foo" + | ^ + 2| + + error: 'foo' is not a valid store path diff --git a/tests/functional/lang/eval-fail-derivationOf-no-context.nix b/tests/functional/lang/eval-fail-derivationOf-no-context.nix new file mode 100644 index 00000000000..f349420b526 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-no-context.nix @@ -0,0 +1 @@ +builtins.derivationOf "foo" diff --git a/tests/functional/lang/eval-fail-derivationOf-number.err.exp b/tests/functional/lang/eval-fail-derivationOf-number.err.exp new file mode 100644 index 00000000000..0625e3bccc9 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-number.err.exp @@ -0,0 +1,10 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-number.nix:1:1: + 1| builtins.derivationOf 42 + | ^ + 2| + + … while evaluating the argument passed to builtins.derivationOf + + error: cannot coerce an integer to a string: 42 diff --git a/tests/functional/lang/eval-fail-derivationOf-number.nix b/tests/functional/lang/eval-fail-derivationOf-number.nix new file mode 100644 index 00000000000..038b3277adf --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-number.nix @@ -0,0 +1 @@ +builtins.derivationOf 42 diff --git a/tests/functional/lang/eval-fail-derivationOf-path.err.exp b/tests/functional/lang/eval-fail-derivationOf-path.err.exp new file mode 100644 index 00000000000..ee50e4e7b68 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-path.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'derivationOf' builtin + at /pwd/lang/eval-fail-derivationOf-path.nix:1:1: + 1| builtins.derivationOf /tmp/foo + | ^ + 2| + + error: '/tmp/foo' is not a valid store path diff --git a/tests/functional/lang/eval-fail-derivationOf-path.nix b/tests/functional/lang/eval-fail-derivationOf-path.nix new file mode 100644 index 00000000000..bbdb7ea58d5 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivationOf-path.nix @@ -0,0 +1 @@ +builtins.derivationOf /tmp/foo diff --git a/tests/functional/lang/eval-okay-derivationOf-coerce.exp b/tests/functional/lang/eval-okay-derivationOf-coerce.exp new file mode 100644 index 00000000000..27ba77ddaf6 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivationOf-coerce.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-derivationOf-coerce.nix b/tests/functional/lang/eval-okay-derivationOf-coerce.nix new file mode 100644 index 00000000000..025157623a8 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivationOf-coerce.nix @@ -0,0 +1,8 @@ +let + pkg = derivation { + name = "test"; + builder = "/bin/false"; + system = "x86_64-linux"; + }; +in +builtins.derivationOf pkg.outPath == drv.drvPath diff --git a/tests/functional/lang/eval-okay-derivationOf-single-context.exp b/tests/functional/lang/eval-okay-derivationOf-single-context.exp new file mode 100644 index 00000000000..27ba77ddaf6 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivationOf-single-context.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-derivationOf-single-context.nix b/tests/functional/lang/eval-okay-derivationOf-single-context.nix new file mode 100644 index 00000000000..6f9566b912b --- /dev/null +++ b/tests/functional/lang/eval-okay-derivationOf-single-context.nix @@ -0,0 +1,10 @@ +let + drv = derivation { + name = "single-test"; + builder = "/bin/false"; + system = "x86_64-linux"; + }; + # Explicitly test with a single context item + singleContext = drv.outPath; +in +builtins.derivationOf singleContext == drv.drvPath diff --git a/tests/functional/lang/eval-okay-derivationOf.exp b/tests/functional/lang/eval-okay-derivationOf.exp new file mode 100644 index 00000000000..27ba77ddaf6 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivationOf.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-derivationOf.nix b/tests/functional/lang/eval-okay-derivationOf.nix new file mode 100644 index 00000000000..2abbac5c970 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivationOf.nix @@ -0,0 +1,8 @@ +let + pkg = derivation { + name = "test"; + builder = "/bin/false"; + system = "x86_64-linux"; + }; +in +builtins.derivationOf pkg == drv.drvPath