From 7f5cf87d563a6f8b756c4ef985a6cc5b3b9dbcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Fri, 18 Feb 2022 13:19:57 +0100 Subject: Accept and discard fragments in getFlakeRefForCompletion Otherwise trying to complete `nix build foo#bar --update-input ` fails with "unexpected fragment" --- src/libcmd/command.hh | 2 +- src/libcmd/installables.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 0f6125f11..f1ef9e69c 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -134,7 +134,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand std::optional getFlakeRefForCompletion() override { - return parseFlakeRef(_installable, absPath(".")); + return parseFlakeRefWithFragment(_installable, absPath(".")).first; } private: diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 3209456bf..1af0675c6 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -905,10 +905,10 @@ std::optional InstallablesCommand::getFlakeRefForCompletion() { if (_installables.empty()) { if (useDefaultInstallables()) - return parseFlakeRef(".", absPath(".")); + return parseFlakeRefWithFragment(".", absPath(".")).first; return {}; } - return parseFlakeRef(_installables.front(), absPath(".")); + return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first; } InstallableCommand::InstallableCommand() -- cgit v1.2.3 From 7ddcb3920629bfaa0caf17b54eb63f3f951ba485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sat, 19 Feb 2022 18:36:02 +0100 Subject: Add shell completion for --override-input --- src/libcmd/installables.cc | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 1af0675c6..13e85b1bb 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -98,6 +98,14 @@ MixFlakeOptions::MixFlakeOptions() lockFlags.inputOverrides.insert_or_assign( flake::parseInputPath(inputPath), parseFlakeRef(flakeRef, absPath("."), true)); + }}, + .completer = {[&](size_t n, std::string_view prefix) { + if (n == 0) { + if (auto flakeRef = getFlakeRefForCompletion()) + completeFlakeInputPath(getEvalState(), *flakeRef, prefix); + } else if (n == 1) { + completeFlakeRef(getEvalState()->store, prefix); + } }} }); -- cgit v1.2.3 From 5f06a91bf77e45c580202324ba2a5f0338a78b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sat, 19 Feb 2022 18:51:18 +0100 Subject: Fix completion of nested attributes in completeInstallable Without this, completing `nix eval -f file.nix foo.` suggests `bar` instead of `foo.bar`, which messes up the command --- src/libcmd/installables.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 13e85b1bb..c4fefb971 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -187,6 +187,8 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() void SourceExprCommand::completeInstallable(std::string_view prefix) { if (file) { + completionType = ctAttrs; + evalSettings.pureEval = false; auto state = getEvalState(); Expr *e = state->parseExprFromFile( @@ -215,13 +217,14 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) Value v2; state->autoCallFunction(*autoArgs, v1, v2); - completionType = ctAttrs; - if (v2.type() == nAttrs) { for (auto & i : *v2.attrs) { std::string name = i.name; if (name.find(searchWord) == 0) { - completions->add(i.name); + if (prefix_ == "") + completions->add(name); + else + completions->add(prefix_ + "." + name); } } } @@ -249,6 +252,8 @@ void completeFlakeRefWithFragment( if (hash == std::string::npos) { completeFlakeRef(evalState->store, prefix); } else { + completionType = ctAttrs; + auto fragment = prefix.substr(hash + 1); auto flakeRefS = std::string(prefix.substr(0, hash)); // FIXME: do tilde expansion. @@ -264,8 +269,6 @@ void completeFlakeRefWithFragment( flake. */ attrPathPrefixes.push_back(""); - completionType = ctAttrs; - for (auto & attrPathPrefixS : attrPathPrefixes) { auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS); auto attrPathS = attrPathPrefixS + std::string(fragment); -- cgit v1.2.3 From 55c6906701ee7fc7e915f7889fea86957b020f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sat, 19 Feb 2022 14:26:34 +0100 Subject: Perform tilde expansion when completing flake fragments Allows completing `nix build ~/flake#`. We can implement expansion for `~user` later if needed. Not using wordexp(3) since that expands way too much. --- src/libcmd/installables.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index c4fefb971..a0b8d0af5 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -1,4 +1,5 @@ #include "installables.hh" +#include "util.hh" #include "command.hh" #include "attr-path.hh" #include "common-eval-args.hh" @@ -256,8 +257,7 @@ void completeFlakeRefWithFragment( auto fragment = prefix.substr(hash + 1); auto flakeRefS = std::string(prefix.substr(0, hash)); - // FIXME: do tilde expansion. - auto flakeRef = parseFlakeRef(flakeRefS, absPath(".")); + auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); auto evalCache = openEvalCache(*evalState, std::make_shared(lockFlake(*evalState, flakeRef, lockFlags))); -- cgit v1.2.3 From da7d8daa77c2cce5805947a012060b02be9e4a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sat, 19 Feb 2022 16:59:52 +0100 Subject: Add shell completion for --override-flake Requires moving the MixEvalArgs class from libexpr to libcmd because that's where completeFlakeRef is. --- src/libcmd/common-eval-args.cc | 105 +++++++++++++++++++++++++++++++++++++++++ src/libcmd/common-eval-args.hh | 27 +++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/libcmd/common-eval-args.cc create mode 100644 src/libcmd/common-eval-args.hh (limited to 'src/libcmd') diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc new file mode 100644 index 000000000..5b6e82388 --- /dev/null +++ b/src/libcmd/common-eval-args.cc @@ -0,0 +1,105 @@ +#include "common-eval-args.hh" +#include "shared.hh" +#include "filetransfer.hh" +#include "util.hh" +#include "eval.hh" +#include "fetchers.hh" +#include "registry.hh" +#include "flake/flakeref.hh" +#include "store-api.hh" +#include "command.hh" + +namespace nix { + +MixEvalArgs::MixEvalArgs() +{ + auto category = "Common evaluation options"; + + addFlag({ + .longName = "arg", + .description = "Pass the value *expr* as the argument *name* to Nix functions.", + .category = category, + .labels = {"name", "expr"}, + .handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }} + }); + + addFlag({ + .longName = "argstr", + .description = "Pass the string *string* as the argument *name* to Nix functions.", + .category = category, + .labels = {"name", "string"}, + .handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }}, + }); + + addFlag({ + .longName = "include", + .shortName = 'I', + .description = "Add *path* to the list of locations used to look up `<...>` file names.", + .category = category, + .labels = {"path"}, + .handler = {[&](std::string s) { searchPath.push_back(s); }} + }); + + addFlag({ + .longName = "impure", + .description = "Allow access to mutable paths and repositories.", + .category = category, + .handler = {[&]() { + evalSettings.pureEval = false; + }}, + }); + + addFlag({ + .longName = "override-flake", + .description = "Override the flake registries, redirecting *original-ref* to *resolved-ref*.", + .category = category, + .labels = {"original-ref", "resolved-ref"}, + .handler = {[&](std::string _from, std::string _to) { + auto from = parseFlakeRef(_from, absPath(".")); + auto to = parseFlakeRef(_to, absPath(".")); + fetchers::Attrs extraAttrs; + if (to.subdir != "") extraAttrs["dir"] = to.subdir; + fetchers::overrideRegistry(from.input, to.input, extraAttrs); + }}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(openStore(), prefix); + }} + }); + + addFlag({ + .longName = "eval-store", + .description = "The Nix store to use for evaluations.", + .category = category, + .labels = {"store-url"}, + .handler = {&evalStoreUrl}, + }); +} + +Bindings * MixEvalArgs::getAutoArgs(EvalState & state) +{ + auto res = state.buildBindings(autoArgs.size()); + for (auto & i : autoArgs) { + auto v = state.allocValue(); + if (i.second[0] == 'E') + state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath("."))); + else + v->mkString(((std::string_view) i.second).substr(1)); + res.insert(state.symbols.create(i.first), v); + } + return res.finish(); +} + +Path lookupFileArg(EvalState & state, std::string_view s) +{ + if (isUri(s)) { + return state.store->toRealPath( + fetchers::downloadTarball( + state.store, resolveUri(s), "source", false).first.storePath); + } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { + Path p(s.substr(1, s.size() - 2)); + return state.findFile(p); + } else + return absPath(std::string(s)); +} + +} diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh new file mode 100644 index 000000000..03fa226aa --- /dev/null +++ b/src/libcmd/common-eval-args.hh @@ -0,0 +1,27 @@ +#pragma once + +#include "args.hh" + +namespace nix { + +class Store; +class EvalState; +class Bindings; + +struct MixEvalArgs : virtual Args +{ + MixEvalArgs(); + + Bindings * getAutoArgs(EvalState & state); + + Strings searchPath; + + std::optional evalStoreUrl; + +private: + std::map autoArgs; +}; + +Path lookupFileArg(EvalState & state, std::string_view s); + +} -- cgit v1.2.3 From a5c969db496843b9a42b3aee494a02714ee9ca78 Mon Sep 17 00:00:00 2001 From: Artturin Date: Wed, 16 Mar 2022 20:48:50 +0200 Subject: nix: allow using --file - to read from stdin --- src/libcmd/installables.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index b7623d4ba..784117569 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -134,7 +134,9 @@ SourceExprCommand::SourceExprCommand() addFlag({ .longName = "file", .shortName = 'f', - .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.", + .description = + "Interpret installables as attribute paths relative to the Nix expression stored in *file*. " + "If *file* is the character -, then a Nix expression will be read from standard input.", .category = installablesCategory, .labels = {"file"}, .handler = {&file}, @@ -695,7 +697,10 @@ std::vector> SourceExprCommand::parseInstallables( auto state = getEvalState(); auto vFile = state->allocValue(); - if (file) + if (file == "-") { + auto e = state->parseStdin(); + state->eval(e, *vFile); + } else if (file) state->evalFile(lookupFileArg(*state, *file), *vFile); else { auto e = state->parseExprFromString(*expr, absPath(".")); -- cgit v1.2.3 From 3b776cb0a7574bf11c20fe0a1d91ec1d21d8ebeb Mon Sep 17 00:00:00 2001 From: Hideaki Kawai Date: Tue, 22 Mar 2022 23:18:02 +0900 Subject: nix edit: support kakoune --- src/libcmd/command.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/libcmd') diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index dc8fa9e5a..a53b029b7 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -204,7 +204,8 @@ Strings editorFor(const Pos & pos) if (pos.line > 0 && ( editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos || - editor.find("vim") != std::string::npos)) + editor.find("vim") != std::string::npos || + editor.find("kak") != std::string::npos)) args.push_back(fmt("+%d", pos.line)); args.push_back(pos.file); return args; -- cgit v1.2.3 From 679b3b32c952eed3c3be6c450c8189948e5645cd Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Sat, 26 Mar 2022 11:32:37 +0100 Subject: Minor comment fix --- src/libcmd/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 784117569..2dc6c3ff8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -935,7 +935,7 @@ InstallablesCommand::InstallablesCommand() void InstallablesCommand::prepare() { if (_installables.empty() && useDefaultInstallables()) - // FIXME: commands like "nix install" should not have a + // FIXME: commands like "nix profile install" should not have a // default, probably. _installables.push_back("."); installables = parseInstallables(getStore(), _installables); -- cgit v1.2.3 From 16860a0328094c04c3e877089119a56f9ddfbcd6 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Sat, 26 Mar 2022 11:32:38 +0100 Subject: nix eval: Add option `read-only` --- src/libcmd/command.hh | 5 +++-- src/libcmd/installables.cc | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 0f6125f11..15bc13c6e 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -85,11 +85,12 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions { std::optional file; std::optional expr; + bool readOnlyMode = false; // FIXME: move this; not all commands (e.g. 'nix run') use it. OperateOn operateOn = OperateOn::Output; - SourceExprCommand(); + SourceExprCommand(bool supportReadOnlyMode = false); std::vector> parseInstallables( ref store, std::vector ss); @@ -128,7 +129,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand { std::shared_ptr installable; - InstallableCommand(); + InstallableCommand(bool supportReadOnlyMode = false); void prepare() override; diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 2dc6c3ff8..e5d69ae14 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -1,3 +1,4 @@ +#include "globals.hh" #include "installables.hh" #include "command.hh" #include "attr-path.hh" @@ -129,7 +130,7 @@ MixFlakeOptions::MixFlakeOptions() }); } -SourceExprCommand::SourceExprCommand() +SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode) { addFlag({ .longName = "file", @@ -157,6 +158,17 @@ SourceExprCommand::SourceExprCommand() .category = installablesCategory, .handler = {&operateOn, OperateOn::Derivation}, }); + + if (supportReadOnlyMode) { + addFlag({ + .longName = "read-only", + .description = + "Do not instantiate each evaluated derivation. " + "This improves performance, but can cause errors when accessing " + "store paths of derivations during evaluation.", + .handler = {&readOnlyMode, true}, + }); + } } Strings SourceExprCommand::getDefaultFlakeAttrPaths() @@ -687,6 +699,10 @@ std::vector> SourceExprCommand::parseInstallables( { std::vector> result; + if (readOnlyMode) { + settings.readOnlyMode = true; + } + if (file || expr) { if (file && expr) throw UsageError("'--file' and '--expr' are exclusive"); @@ -951,7 +967,8 @@ std::optional InstallablesCommand::getFlakeRefForCompletion() return parseFlakeRef(_installables.front(), absPath(".")); } -InstallableCommand::InstallableCommand() +InstallableCommand::InstallableCommand(bool supportReadOnlyMode) + : SourceExprCommand(supportReadOnlyMode) { expectArgs({ .label = "installable", -- cgit v1.2.3 From 057f9ee1900312f42efe6c5cebb02b07b4ff2131 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 28 Mar 2022 14:21:35 +0200 Subject: nix profile install: Don't use queryDerivationOutputMap() Instead get the outputs from Installable::build(). This will also allow 'nix profile install' to support impure derivations. Fixes #6286. --- src/libcmd/installables.cc | 139 ++++++++++++++++++++++++++------------------- src/libcmd/installables.hh | 12 ++-- 2 files changed, 86 insertions(+), 65 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 784117569..955bbe6fb 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -756,55 +756,20 @@ std::shared_ptr SourceExprCommand::parseInstallable( return installables.front(); } -BuiltPaths getBuiltPaths(ref evalStore, ref store, const DerivedPaths & hopefullyBuiltPaths) +BuiltPaths Installable::build( + ref evalStore, + ref store, + Realise mode, + const std::vector> & installables, + BuildMode bMode) { BuiltPaths res; - for (const auto & b : hopefullyBuiltPaths) - std::visit( - overloaded{ - [&](const DerivedPath::Opaque & bo) { - res.push_back(BuiltPath::Opaque{bo.path}); - }, - [&](const DerivedPath::Built & bfd) { - OutputPathMap outputs; - auto drv = evalStore->readDerivation(bfd.drvPath); - auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive - auto drvOutputs = drv.outputsAndOptPaths(*store); - for (auto & output : bfd.outputs) { - if (!outputHashes.count(output)) - throw Error( - "the derivation '%s' doesn't have an output named '%s'", - store->printStorePath(bfd.drvPath), output); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { - auto outputId = - DrvOutput{outputHashes.at(output), output}; - auto realisation = - store->queryRealisation(outputId); - if (!realisation) - throw Error( - "cannot operate on an output of unbuilt " - "content-addressed derivation '%s'", - outputId.to_string()); - outputs.insert_or_assign( - output, realisation->outPath); - } else { - // If ca-derivations isn't enabled, assume that - // the output path is statically known. - assert(drvOutputs.count(output)); - assert(drvOutputs.at(output).second); - outputs.insert_or_assign( - output, *drvOutputs.at(output).second); - } - } - res.push_back(BuiltPath::Built{bfd.drvPath, outputs}); - }, - }, - b.raw()); - + for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode)) + res.push_back(builtPath); return res; } -BuiltPaths Installable::build( +std::vector, BuiltPath>> Installable::build2( ref evalStore, ref store, Realise mode, @@ -815,39 +780,93 @@ BuiltPaths Installable::build( settings.readOnlyMode = true; std::vector pathsToBuild; + std::map>> backmap; for (auto & i : installables) { - auto b = i->toDerivedPaths(); - pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end()); + for (auto b : i->toDerivedPaths()) { + pathsToBuild.push_back(b); + backmap[b].push_back(i); + } } + std::vector, BuiltPath>> res; + switch (mode) { + case Realise::Nothing: case Realise::Derivation: printMissing(store, pathsToBuild, lvlError); - return getBuiltPaths(evalStore, store, pathsToBuild); + + for (auto & path : pathsToBuild) { + for (auto & installable : backmap[path]) { + std::visit(overloaded { + [&](const DerivedPath::Built & bfd) { + OutputPathMap outputs; + auto drv = evalStore->readDerivation(bfd.drvPath); + auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive + auto drvOutputs = drv.outputsAndOptPaths(*store); + for (auto & output : bfd.outputs) { + if (!outputHashes.count(output)) + throw Error( + "the derivation '%s' doesn't have an output named '%s'", + store->printStorePath(bfd.drvPath), output); + if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { + DrvOutput outputId { outputHashes.at(output), output }; + auto realisation = store->queryRealisation(outputId); + if (!realisation) + throw Error( + "cannot operate on an output of unbuilt " + "content-addressed derivation '%s'", + outputId.to_string()); + outputs.insert_or_assign(output, realisation->outPath); + } else { + // If ca-derivations isn't enabled, assume that + // the output path is statically known. + assert(drvOutputs.count(output)); + assert(drvOutputs.at(output).second); + outputs.insert_or_assign( + output, *drvOutputs.at(output).second); + } + } + res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); + }, + [&](const DerivedPath::Opaque & bo) { + res.push_back({installable, BuiltPath::Opaque { bo.path }}); + }, + }, path.raw()); + } + } + + break; + case Realise::Outputs: { - BuiltPaths res; for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { if (!buildResult.success()) buildResult.rethrow(); - std::visit(overloaded { - [&](const DerivedPath::Built & bfd) { - std::map outputs; - for (auto & path : buildResult.builtOutputs) - outputs.emplace(path.first.outputName, path.second.outPath); - res.push_back(BuiltPath::Built { bfd.drvPath, outputs }); - }, - [&](const DerivedPath::Opaque & bo) { - res.push_back(BuiltPath::Opaque { bo.path }); - }, - }, buildResult.path.raw()); + + for (auto & installable : backmap[buildResult.path]) { + std::visit(overloaded { + [&](const DerivedPath::Built & bfd) { + std::map outputs; + for (auto & path : buildResult.builtOutputs) + outputs.emplace(path.first.outputName, path.second.outPath); + res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }}); + }, + [&](const DerivedPath::Opaque & bo) { + res.push_back({installable, BuiltPath::Opaque { bo.path }}); + }, + }, buildResult.path.raw()); + } } - return res; + + break; } + default: assert(false); } + + return res; } BuiltPaths Installable::toBuiltPaths( diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index e172b71b0..f4bf0d406 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -98,6 +98,13 @@ struct Installable const std::vector> & installables, BuildMode bMode = bmNormal); + static std::vector, BuiltPath>> build2( + ref evalStore, + ref store, + Realise mode, + const std::vector> & installables, + BuildMode bMode = bmNormal); + static std::set toStorePaths( ref evalStore, ref store, @@ -185,9 +192,4 @@ ref openEvalCache( EvalState & state, std::shared_ptr lockedFlake); -BuiltPaths getBuiltPaths( - ref evalStore, - ref store, - const DerivedPaths & hopefullyBuiltPaths); - } -- cgit v1.2.3 From d89840b103e57e81e5245c3fe9edfbf7c3477ad5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 14 Apr 2022 14:04:19 +0200 Subject: Make InstallableFlake::toValue() and toDerivation() behave consistently In particular, this means that 'nix eval` (which uses toValue()) no longer auto-calls functions or functors (because AttrCursor::findAlongAttrPath() doesn't). Fixes #6152. Also use ref<> in a few places, and don't return attrpaths from getCursor() because cursors already have a getAttrPath() method. --- src/libcmd/installables.cc | 117 ++++++++++++++++++++------------------------- src/libcmd/installables.hh | 12 +++-- 2 files changed, 62 insertions(+), 67 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 955bbe6fb..1bb5d6300 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -334,16 +334,16 @@ DerivedPath Installable::toDerivedPath() return std::move(buildables[0]); } -std::vector, std::string>> +std::vector> Installable::getCursors(EvalState & state) { auto evalCache = std::make_shared(std::nullopt, state, [&]() { return toValue(state).first; }); - return {{evalCache->getRoot(), ""}}; + return {evalCache->getRoot()}; } -std::pair, std::string> +ref Installable::getCursor(EvalState & state) { auto cursors = getCursors(state); @@ -566,43 +566,21 @@ InstallableFlake::InstallableFlake( std::tuple InstallableFlake::toDerivation() { - auto lockedFlake = getLockedFlake(); - - auto cache = openEvalCache(*state, lockedFlake); - auto root = cache->getRoot(); - - Suggestions suggestions; - - for (auto & attrPath : getActualAttrPaths()) { - debug("trying flake output attribute '%s'", attrPath); + auto attr = getCursor(*state); - auto attrOrSuggestions = root->findAlongAttrPath( - parseAttrPath(*state, attrPath), - true - ); - - if (!attrOrSuggestions) { - suggestions += attrOrSuggestions.getSuggestions(); - continue; - } + auto attrPath = attr->getAttrPathStr(); - auto attr = *attrOrSuggestions; + if (!attr->isDerivation()) + throw Error("flake output attribute '%s' is not a derivation", attrPath); - if (!attr->isDerivation()) - throw Error("flake output attribute '%s' is not a derivation", attrPath); + auto drvPath = attr->forceDerivation(); - auto drvPath = attr->forceDerivation(); - - auto drvInfo = DerivationInfo { - std::move(drvPath), - attr->getAttr(state->sOutputName)->getString() - }; - - return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)}; - } + auto drvInfo = DerivationInfo { + std::move(drvPath), + attr->getAttr(state->sOutputName)->getString() + }; - throw Error(suggestions, "flake '%s' does not provide attribute %s", - flakeRef, showAttrPaths(getActualAttrPaths())); + return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)}; } std::vector InstallableFlake::toDerivations() @@ -614,33 +592,10 @@ std::vector InstallableFlake::toDerivations() std::pair InstallableFlake::toValue(EvalState & state) { - auto lockedFlake = getLockedFlake(); - - auto vOutputs = getFlakeOutputs(state, *lockedFlake); - - auto emptyArgs = state.allocBindings(0); - - Suggestions suggestions; - - for (auto & attrPath : getActualAttrPaths()) { - try { - auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); - state.forceValue(*v, pos); - return {v, pos}; - } catch (AttrPathNotFound & e) { - suggestions += e.info().suggestions; - } - } - - throw Error( - suggestions, - "flake '%s' does not provide attribute %s", - flakeRef, - showAttrPaths(getActualAttrPaths()) - ); + return {&getCursor(state)->forceValue(), noPos}; } -std::vector, std::string>> +std::vector> InstallableFlake::getCursors(EvalState & state) { auto evalCache = openEvalCache(state, @@ -648,21 +603,55 @@ InstallableFlake::getCursors(EvalState & state) auto root = evalCache->getRoot(); - std::vector, std::string>> res; + std::vector> res; for (auto & attrPath : getActualAttrPaths()) { auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); - if (attr) res.push_back({*attr, attrPath}); + if (attr) res.push_back(ref(*attr)); } return res; } +ref InstallableFlake::getCursor(EvalState & state) +{ + auto lockedFlake = getLockedFlake(); + + auto cache = openEvalCache(state, lockedFlake); + auto root = cache->getRoot(); + + Suggestions suggestions; + + auto attrPaths = getActualAttrPaths(); + + for (auto & attrPath : attrPaths) { + debug("trying flake output attribute '%s'", attrPath); + + auto attrOrSuggestions = root->findAlongAttrPath( + parseAttrPath(state, attrPath), + true + ); + + if (!attrOrSuggestions) { + suggestions += attrOrSuggestions.getSuggestions(); + continue; + } + + return *attrOrSuggestions; + } + + throw Error( + suggestions, + "flake '%s' does not provide attribute %s", + flakeRef, + showAttrPaths(attrPaths)); +} + std::shared_ptr InstallableFlake::getLockedFlake() const { - flake::LockFlags lockFlagsApplyConfig = lockFlags; - lockFlagsApplyConfig.applyNixConfig = true; if (!_lockedFlake) { + flake::LockFlags lockFlagsApplyConfig = lockFlags; + lockFlagsApplyConfig.applyNixConfig = true; _lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); } return _lockedFlake; diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index f4bf0d406..b847f8939 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -80,10 +80,10 @@ struct Installable return {}; } - virtual std::vector, std::string>> + virtual std::vector> getCursors(EvalState & state); - std::pair, std::string> + virtual ref getCursor(EvalState & state); virtual FlakeRef nixpkgsFlakeRef() const @@ -180,9 +180,15 @@ struct InstallableFlake : InstallableValue std::pair toValue(EvalState & state) override; - std::vector, std::string>> + /* Get a cursor to every attrpath in getActualAttrPaths() that + exists. */ + std::vector> getCursors(EvalState & state) override; + /* Get a cursor to the first attrpath in getActualAttrPaths() that + exists, or throw an exception with suggestions if none exists. */ + ref getCursor(EvalState & state) override; + std::shared_ptr getLockedFlake() const; FlakeRef nixpkgsFlakeRef() const override; -- cgit v1.2.3 From 445ddebde533cc1605ca817d97d573e0161db66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 21 Apr 2022 09:26:45 +0200 Subject: Fix the error message in case of a missing realisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t say that the derivation is CA as it might happen on a non-ca derivation too. Technically we could always recover _something_ for a purely input-addressed derivation (like we already do when the `ca-derivations` xp feature isn’t enabled), but it seems better to consistently fail − the end-result wouldn’t really make sense anyways in most cases. --- src/libcmd/installables.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 74c8a6df5..4250c321e 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -831,8 +831,8 @@ std::vector, BuiltPath>> Installable::bui auto realisation = store->queryRealisation(outputId); if (!realisation) throw Error( - "cannot operate on an output of unbuilt " - "content-addressed derivation '%s'", + "cannot operate on an output of the " + "unbuilt derivation '%s'", outputId.to_string()); outputs.insert_or_assign(output, realisation->outPath); } else { -- cgit v1.2.3 From 39df15fb8e766c0a4fa2fda83784fb8a478a766c Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 4 Mar 2022 20:54:50 +0100 Subject: don't use full Pos for findPackageFilename/editorFor only file and line of the returned position were ever used, it wasn't actually used a position. as such we may as well use a path+int pair for only those two values and remove a use of Pos that would not work well with a position table. --- src/libcmd/command.cc | 8 ++++---- src/libcmd/command.hh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index a53b029b7..f28cfe5de 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -197,17 +197,17 @@ void StorePathCommand::run(ref store, std::vector && storePath run(store, *storePaths.begin()); } -Strings editorFor(const Pos & pos) +Strings editorFor(const Path & file, uint32_t line) { auto editor = getEnv("EDITOR").value_or("cat"); auto args = tokenizeString(editor); - if (pos.line > 0 && ( + if (line > 0 && ( editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos || editor.find("vim") != std::string::npos || editor.find("kak") != std::string::npos)) - args.push_back(fmt("+%d", pos.line)); - args.push_back(pos.file); + args.push_back(fmt("+%d", line)); + args.push_back(file); return args; } diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 84bbb5292..078e2a2ce 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -219,7 +219,7 @@ static RegisterCommand registerCommand2(std::vector && name) /* Helper function to generate args that invoke $EDITOR on filename:lineno. */ -Strings editorFor(const Pos & pos); +Strings editorFor(const Path & file, uint32_t line); struct MixProfile : virtual StoreCommand { -- cgit v1.2.3 From 6526d1676ba5a645f65d751e7529ccd273579017 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 4 Mar 2022 19:31:59 +0100 Subject: replace most Pos objects/ptrs with indexes into a position table Pos objects are somewhat wasteful as they duplicate the origin file name and input type for each object. on files that produce more than one Pos when parsed this a sizeable waste of memory (one pointer per Pos). the same goes for ptr on 64 bit machines: parsing enough source to require 8 bytes to locate a position would need at least 8GB of input and 64GB of expression memory. it's not likely that we'll hit that any time soon, so we can use a uint32_t index to locate positions instead. --- src/libcmd/installables.cc | 4 ++-- src/libcmd/installables.hh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 4250c321e..6197f4be4 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -473,7 +473,7 @@ struct InstallableAttrPath : InstallableValue std::string what() const override { return attrPath; } - std::pair toValue(EvalState & state) override + std::pair toValue(EvalState & state) override { auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); state.forceValue(*vRes, pos); @@ -613,7 +613,7 @@ std::vector InstallableFlake::toDerivations() return res; } -std::pair InstallableFlake::toValue(EvalState & state) +std::pair InstallableFlake::toValue(EvalState & state) { return {&getCursor(state)->forceValue(), noPos}; } diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index b847f8939..de8b08525 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -68,7 +68,7 @@ struct Installable UnresolvedApp toApp(EvalState & state); - virtual std::pair toValue(EvalState & state) + virtual std::pair toValue(EvalState & state) { throw Error("argument '%s' cannot be evaluated", what()); } @@ -178,7 +178,7 @@ struct InstallableFlake : InstallableValue std::vector toDerivations() override; - std::pair toValue(EvalState & state) override; + std::pair toValue(EvalState & state) override; /* Get a cursor to every attrpath in getActualAttrPaths() that exists. */ -- cgit v1.2.3 From 8775be33931ec3b1cad97035ff3d5370a97178a1 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 5 Mar 2022 14:40:24 +0100 Subject: store Symbols in a table as well, like positions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this slightly increases the amount of memory used for any given symbol, but this increase is more than made up for if the symbol is referenced more than once in the EvalState that holds it. on average every symbol should be referenced at least twice (once to introduce a binding, once to use it), so we expect no increase in memory on average. symbol tables are limited to 2³² entries like position tables, and similar arguments apply to why overflow is not likely: 2³² symbols would require as many string instances (at 24 bytes each) and map entries (at 24 bytes or more each, assuming that the map holds on average at most one item per bucket as the docs say). a full symbol table would require at least 192GB of memory just for symbols, which is well out of reach. (an ofborg eval of nixpks today creates less than a million symbols!) --- src/libcmd/installables.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libcmd') diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 6197f4be4..c81f76a22 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -235,7 +235,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) if (v2.type() == nAttrs) { for (auto & i : *v2.attrs) { - std::string name = i.name; + std::string name = state->symbols[i.name]; if (name.find(searchWord) == 0) { if (prefix_ == "") completions->add(name); @@ -600,7 +600,7 @@ std::tuple InstallableF auto drvInfo = DerivationInfo { std::move(drvPath), - attr->getAttr(state->sOutputName)->getString() + attr->getAttr("outputName")->getString() }; return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)}; -- cgit v1.2.3