From f0c0052d4b16d13d2a826a2839b680b729651179 Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 17 May 2021 09:48:51 +0200 Subject: [PATCH 1/2] Resolve the program path in `nix run` Fix #4768 --- src/nix/app.cc | 96 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 17 deletions(-) diff --git a/src/nix/app.cc b/src/nix/app.cc index cf147c631..1e30deb34 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -3,9 +3,61 @@ #include "eval-inline.hh" #include "eval-cache.hh" #include "names.hh" +#include "command.hh" namespace nix { +struct InstallableDerivedPath : Installable +{ + ref store; + const DerivedPath derivedPath; + + InstallableDerivedPath(ref store, const DerivedPath & derivedPath) + : store(store) + , derivedPath(derivedPath) + { + } + + + std::string what() override { return derivedPath.to_string(*store); } + + DerivedPaths toDerivedPaths() override + { + return {derivedPath}; + } + + std::optional getStorePath() override + { + return std::nullopt; + } +}; + +/** + * Return the rewrites that are needed to resolve a string whose context is + * included in `dependencies` + */ +StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies) +{ + StringPairs res; + for (auto & dep : dependencies) + if (auto drvDep = std::get_if(&dep)) + for (auto & [ outputName, outputPath ] : drvDep->outputs) + res.emplace( + downstreamPlaceholder(store, drvDep->drvPath, outputName), + store.printStorePath(outputPath) + ); + return res; +} + +/** + * Resolve the given string assuming the given context + */ +std::string resolveString(Store & store, const std::string & toResolve, const BuiltPaths dependencies) +{ + auto rewrites = resolveRewrites(store, dependencies); + return rewriteStrings(toResolve, rewrites); +} + App Installable::toApp(EvalState & state) { auto [cursor, attrPath] = getCursor(state); @@ -18,19 +70,21 @@ App Installable::toApp(EvalState & state) throw Error("app program '%s' is not in the Nix store", program); }; + std::vector> context; + std::string unresolvedProgram; + + if (type == "app") { - auto [program, context] = cursor->getAttr("program")->getStringWithContext(); + auto [program, context_] = cursor->getAttr("program")->getStringWithContext(); + unresolvedProgram = program; - checkProgram(program); - - std::vector context2; - for (auto & [path, name] : context) - context2.push_back({state.store->parseStorePath(path), {name}}); - - return App { - .context = std::move(context2), - .program = program, - }; + for (auto & [path, name] : context_) + context.push_back(std::make_shared( + state.store, + DerivedPathBuilt{ + .drvPath = state.store->parseStorePath(path), + .outputs = {name}, + })); } else if (type == "derivation") { @@ -44,16 +98,24 @@ App Installable::toApp(EvalState & state) aMainProgram ? aMainProgram->getString() : DrvName(name).name; - auto program = outPath + "/bin/" + mainProgram; - checkProgram(program); - return App { - .context = { { drvPath, {outputName} } }, - .program = program, - }; + unresolvedProgram = outPath + "/bin/" + mainProgram; + context = {std::make_shared( + state.store, + DerivedPathBuilt{ + .drvPath = drvPath, + .outputs = {outputName}, + })}; } else throw Error("attribute '%s' has unsupported type '%s'", attrPath, type); + + auto builtContext = build(state.store, Realise::Outputs, context); + auto program = resolveString(*state.store, unresolvedProgram, builtContext); + checkProgram(program); + return App { + .program = program, + }; } } From f46adb783c939c61a087cdfd8af964f4ad6f9816 Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 11 May 2021 10:52:17 +0200 Subject: [PATCH 2/2] Add a test for `nix run` with CA derivations --- .gitignore | 1 + tests/ca/config.nix.in | 1 + tests/ca/content-addressed.nix | 34 ++++++++++++++++++++-------------- tests/ca/flake.nix | 3 +++ tests/ca/nix-run.sh | 9 +++++++++ tests/local.mk | 5 +++-- 6 files changed, 37 insertions(+), 16 deletions(-) create mode 120000 tests/ca/config.nix.in create mode 100644 tests/ca/flake.nix create mode 100755 tests/ca/nix-run.sh diff --git a/.gitignore b/.gitignore index 37aada307..2e14561fe 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ perl/Makefile.config /tests/shell /tests/shell.drv /tests/config.nix +/tests/ca/config.nix # /tests/lang/ /tests/lang/*.out diff --git a/tests/ca/config.nix.in b/tests/ca/config.nix.in new file mode 120000 index 000000000..af24ddb30 --- /dev/null +++ b/tests/ca/config.nix.in @@ -0,0 +1 @@ +../config.nix.in \ No newline at end of file diff --git a/tests/ca/content-addressed.nix b/tests/ca/content-addressed.nix index e5b1c4de3..d328fc92c 100644 --- a/tests/ca/content-addressed.nix +++ b/tests/ca/content-addressed.nix @@ -1,4 +1,11 @@ -with import ../config.nix; +with import ./config.nix; + +let mkCADerivation = args: mkDerivation ({ + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; +} // args); +in { seed ? 0 }: # A simple content-addressed derivation. @@ -14,7 +21,7 @@ rec { echo "Hello World" > $out/hello ''; }; - rootCA = mkDerivation { + rootCA = mkCADerivation { name = "rootCA"; outputs = [ "out" "dev" "foo"]; buildCommand = '' @@ -27,11 +34,8 @@ rec { ln -s $out $dev ln -s $out $foo ''; - __contentAddressed = true; - outputHashMode = "recursive"; - outputHashAlgo = "sha256"; }; - dependentCA = mkDerivation { + dependentCA = mkCADerivation { name = "dependent"; buildCommand = '' echo "building a dependent derivation" @@ -39,20 +43,14 @@ rec { cat ${rootCA}/self/dep echo ${rootCA}/self/dep > $out/dep ''; - __contentAddressed = true; - outputHashMode = "recursive"; - outputHashAlgo = "sha256"; }; - transitivelyDependentCA = mkDerivation { + transitivelyDependentCA = mkCADerivation { name = "transitively-dependent"; buildCommand = '' echo "building transitively-dependent" cat ${dependentCA}/dep echo ${dependentCA} > $out ''; - __contentAddressed = true; - outputHashMode = "recursive"; - outputHashAlgo = "sha256"; }; dependentNonCA = mkDerivation { name = "dependent-non-ca"; @@ -72,6 +70,14 @@ rec { cat ${dependentCA}/dep echo foo > $out ''; - + }; + runnable = mkCADerivation rec { + name = "runnable-thing"; + buildCommand = '' + mkdir -p $out/bin + echo ${rootCA} # Just to make it depend on it + echo "" > $out/bin/${name} + chmod +x $out/bin/${name} + ''; }; } diff --git a/tests/ca/flake.nix b/tests/ca/flake.nix new file mode 100644 index 000000000..332c92a67 --- /dev/null +++ b/tests/ca/flake.nix @@ -0,0 +1,3 @@ +{ + outputs = { self }: import ./content-addressed.nix {}; +} diff --git a/tests/ca/nix-run.sh b/tests/ca/nix-run.sh new file mode 100755 index 000000000..81402af10 --- /dev/null +++ b/tests/ca/nix-run.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +source common.sh + +sed -i 's/experimental-features .*/& ca-derivations ca-references nix-command flakes/' "$NIX_CONF_DIR"/nix.conf + +FLAKE_PATH=path:$PWD + +nix run --no-write-lock-file $FLAKE_PATH#runnable diff --git a/tests/local.mk b/tests/local.mk index e2c94dde6..542be6b7e 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -46,6 +46,7 @@ nix_tests = \ ca/build.sh \ ca/substitute.sh \ ca/signatures.sh \ + ca/nix-run.sh \ ca/nix-copy.sh # parallel.sh @@ -53,6 +54,6 @@ install-tests += $(foreach x, $(nix_tests), tests/$(x)) tests-environment = NIX_REMOTE= $(bash) -e -clean-files += $(d)/common.sh $(d)/config.nix +clean-files += $(d)/common.sh $(d)/config.nix $(d)/ca/config.nix -test-deps += tests/common.sh tests/config.nix tests/plugins/libplugintest.$(SO_EXT) +test-deps += tests/common.sh tests/config.nix tests/ca/config.nix tests/plugins/libplugintest.$(SO_EXT)