From 4c74f8fc8749765e2cec7ff479210f732fc05944 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 10 Mar 2021 04:22:56 +0000 Subject: [PATCH] Create `outputOf` primop. --- src/libexpr/primops.cc | 44 ++++++++++++++++++++++++++++ src/libstore/derivations.cc | 8 +++++ src/libstore/derivations.hh | 3 ++ tests/dyn-drv/dep-built-drv.sh | 13 ++++++++ tests/dyn-drv/eval-outputOf.sh | 37 +++++++++++++++++++++++ tests/dyn-drv/text-hashed-output.nix | 10 +++++++ tests/local.mk | 2 ++ 7 files changed, 117 insertions(+) create mode 100644 tests/dyn-drv/dep-built-drv.sh create mode 100644 tests/dyn-drv/eval-outputOf.sh diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index f2ea6ecbe23..01486333585 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1489,6 +1489,50 @@ static RegisterPrimOp primop_readDir({ .fun = prim_readDir, }); +/* Extend single element string context with another output. */ +static void prim_outputOf(EvalState & state, const Pos & pos, Value * * args, Value & v) +{ + PathSet context; + Path path = state.coerceToPath(pos, *args[0], context); + + Path outputName = state.forceStringNoCtx(*args[1], pos); + + Path path2; + try { + auto p = DownstreamPlaceholder::parse(path); + path2 = DownstreamPlaceholder { p, outputName }.render(); + } catch (BadHash &) { + path2 = DownstreamPlaceholder { state.store->parseStorePath(path), outputName }.render(); + } + + if (context.size() != 1) + throw EvalError({ + .msg = hintfmt("path '%s' should have exactly one item in string context, but has", context.size()), + .errPos = pos + }); + auto [ctxS, outputNames] = decodeContext(*context.begin()); + outputNames.push_back(outputName); + PathSet context2 = { "!" + concatStringsSep("!", outputNames) + "!" + ctxS }; + + mkString(v, path2, context2); +} + +static RegisterPrimOp primop_outputOf({ + .name = "__outputOf", + .args = {"drv path", "output name"}, + .doc = R"( + Return path (actually placeholder path) to the output of the given drv. + The drv path may itself be a placeholder, which permits chaining this primop. + + For instance, `builtins.outputOf (builtins.outputOf myDrv "out) "out"` + will return a placeholder for the output of the output of `myDrv`, + interpreted as a derivation. + + It may help to compare to the `->` operator in C, which can also by + chained. E.g. compare the above example to `drv->out->out`. + )", + .fun = prim_outputOf, +}); /************************************************************* * Creating files diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index c327822fdcc..c7dbf2a29d0 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -734,6 +734,14 @@ std::string DownstreamPlaceholder::render() const } +DownstreamPlaceholder DownstreamPlaceholder::parse(std::string_view s) +{ + assert(s[0] == '/'); + s = s.substr(1); + return { Hash::parseNonSRIUnprefixed(s, htSHA256) }; +} + + inline Hash DownstreamPlaceholder::worker1(const StorePath & drvPath, std::string_view outputName) { auto drvNameWithExtension = drvPath.name(); diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index fd6f122aefd..19d02d61d00 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -259,6 +259,8 @@ public: dependency which is a CA derivation. */ std::string render() const; + static DownstreamPlaceholder parse(std::string_view); + // For CA derivations DownstreamPlaceholder(const StorePath & drvPath, std::string_view outputName); @@ -266,6 +268,7 @@ public: DownstreamPlaceholder(const DownstreamPlaceholder & placeholder, std::string_view outputName); private: + DownstreamPlaceholder(Hash && h) : hash(std::move(h)) {} static inline Hash worker1(const StorePath & drvPath, std::string_view outputName); static inline Hash worker2(const DownstreamPlaceholder & placeholder, std::string_view outputName); }; diff --git a/tests/dyn-drv/dep-built-drv.sh b/tests/dyn-drv/dep-built-drv.sh new file mode 100644 index 00000000000..b6348459250 --- /dev/null +++ b/tests/dyn-drv/dep-built-drv.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +source common.sh + +feats=(--experimental-features 'nix-command ca-derivations') + +out1=$(nix-build "${feats[@]}" ./text-hashed-output.nix -A root --no-out-link) + +clearStore + +out2=$(nix-build "${feats[@]}" ./text-hashed-output.nix -A wrapper --no-out-link) + +diff -r $out1 $out2 diff --git a/tests/dyn-drv/eval-outputOf.sh b/tests/dyn-drv/eval-outputOf.sh new file mode 100644 index 00000000000..6d51708b8ec --- /dev/null +++ b/tests/dyn-drv/eval-outputOf.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +source common.sh + +feats=(--experimental-features 'nix-command ca-derivations') + +nix eval "${feats[@]}" --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = root.outPath; + b = builtins.outputOf root.drvPath "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' + +nix eval "${feats[@]}" --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = dependent.outPath; + b = builtins.outputOf dependent.drvPath "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' + +nix eval "${feats[@]}" --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = builtins.outputOf dependent.outPath "out"; + b = builtins.outputOf dependent.out "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' + +nix eval "${feats[@]}" --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = builtins.outputOf dependent.out "out"; + b = builtins.outputOf (builtins.outputOf dependent.drvPath "out") "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' diff --git a/tests/dyn-drv/text-hashed-output.nix b/tests/dyn-drv/text-hashed-output.nix index 65312409153..338455d6098 100644 --- a/tests/dyn-drv/text-hashed-output.nix +++ b/tests/dyn-drv/text-hashed-output.nix @@ -26,4 +26,14 @@ rec { outputHashMode = "text"; outputHashAlgo = "sha256"; }; + wrapper = mkDerivation { + name = "put-it-all-together"; + buildCommand = '' + echo "Copying the output of the dynamic derivation" + cp -r ${builtins.outputOf dependent.out "out"} $out + ''; + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + }; } diff --git a/tests/local.mk b/tests/local.mk index 54b039e72ee..421e4ec5e88 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -42,6 +42,8 @@ nix_tests = \ flakes.sh \ dyn-drv/text-hashed-output.sh \ dyn-drv/build-built-drv.sh \ + dyn-drv/eval-outputOf.sh \ + dyn-drv/dep-built-drv.sh \ build.sh \ build-explicit-output.sh \ compute-levels.sh \