From 2bc01161fa2485ef4adc063da46e75571bde8bce 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 74f40c62d8d7..ac18eeb32391 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1765,6 +1765,50 @@ static RegisterPrimOp primop_readDir({ .fun = prim_readDir, }); +/* Extend single element string context with another output. */ +static void prim_outputOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + PathSet context; + Path path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument to builtins.outputOf"); + + std::string_view outputName = state.forceStringNoCtx(*args[1], pos, "while evaluating teh second argument to builtins.outputOf"); + + 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 = state.positions[pos], + }); + const std::string & ctxRaw = *context.begin(); + size_t off = ctxRaw[0] == '=' || ctxRaw[0] == '!' ? 1 : 0; + PathSet context2 = { "!" + outputName + "!" + ctxRaw[off] }; + + v.mkString(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 d39b48558d9b..e09ed1dd87e6 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -860,6 +860,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 ba575727e484..7e0319cf6222 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -352,6 +352,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); @@ -359,6 +361,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 000000000000..b63484592503 --- /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 000000000000..6d51708b8ec8 --- /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 913843fb87d7..edb680c2dc98 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 bcf819eac130..1ec76a443a5a 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -102,6 +102,8 @@ nix_tests = \ ca/import-derivation.sh \ dyn-drv/text-hashed-output.sh \ dyn-drv/build-built-drv.sh \ + dyn-drv/eval-outputOf.sh \ + dyn-drv/dep-built-drv.sh \ nix_path.sh \ case-hack.sh \ placeholders.sh \