From 15a16e5c05d547ec07170df2392263e5e891447b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Nov 2018 15:59:52 +0100 Subject: MultiCommand: Simplify construction --- src/nix/command.cc | 8 +++++--- src/nix/command.hh | 8 ++++---- src/nix/main.cc | 7 ++++++- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index 3d7d582d6..e760c17d5 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -4,7 +4,7 @@ namespace nix { -Commands * RegisterCommand::commands = 0; +std::vector> * RegisterCommand::commands = 0; void Command::printHelp(const string & programName, std::ostream & out) { @@ -21,9 +21,11 @@ void Command::printHelp(const string & programName, std::ostream & out) } } -MultiCommand::MultiCommand(const Commands & _commands) - : commands(_commands) +MultiCommand::MultiCommand(const std::vector> & _commands) { + for (auto & command : _commands) + commands.emplace(command->name(), command); + expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector ss) { assert(!command); auto i = commands.find(ss[0]); diff --git a/src/nix/command.hh b/src/nix/command.hh index 97a6fee7f..2108aa674 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -173,7 +173,7 @@ public: std::shared_ptr command; - MultiCommand(const Commands & commands); + MultiCommand(const std::vector> & commands); void printHelp(const string & programName, std::ostream & out) override; @@ -185,12 +185,12 @@ public: /* A helper class for registering commands globally. */ struct RegisterCommand { - static Commands * commands; + static std::vector> * commands; RegisterCommand(ref command) { - if (!commands) commands = new Commands; - commands->emplace(command->name(), command); + if (!commands) commands = new std::vector>; + commands->push_back(command); } }; diff --git a/src/nix/main.cc b/src/nix/main.cc index 64c1dc357..3d4348d28 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -57,10 +57,15 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs "--help-config' for a list of configuration settings.\n"; } + void printHelp(const string & programName, std::ostream & out) + { + MultiCommand::printHelp(programName, out); + std::cout << "\nNote: this program is EXPERIMENTAL and subject to change.\n"; + } + void showHelpAndExit() { printHelp(programName, std::cout); - std::cout << "\nNote: this program is EXPERIMENTAL and subject to change.\n"; throw Exit(); } }; -- cgit v1.2.3 From f70434b1fbbdb0e188718f0c55a8156a7aa08744 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Nov 2018 16:03:31 +0100 Subject: Move Command and MultiCommand to libutil --- src/nix/command.cc | 74 ------------------------------------------------------ src/nix/command.hh | 41 ------------------------------ src/nix/main.cc | 5 ++++ 3 files changed, 5 insertions(+), 115 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index e760c17d5..5967ab36c 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -6,80 +6,6 @@ namespace nix { std::vector> * RegisterCommand::commands = 0; -void Command::printHelp(const string & programName, std::ostream & out) -{ - Args::printHelp(programName, out); - - auto exs = examples(); - if (!exs.empty()) { - out << "\n"; - out << "Examples:\n"; - for (auto & ex : exs) - out << "\n" - << " " << ex.description << "\n" // FIXME: wrap - << " $ " << ex.command << "\n"; - } -} - -MultiCommand::MultiCommand(const std::vector> & _commands) -{ - for (auto & command : _commands) - commands.emplace(command->name(), command); - - expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector ss) { - assert(!command); - auto i = commands.find(ss[0]); - if (i == commands.end()) - throw UsageError("'%s' is not a recognised command", ss[0]); - command = i->second; - }}); -} - -void MultiCommand::printHelp(const string & programName, std::ostream & out) -{ - if (command) { - command->printHelp(programName + " " + command->name(), out); - return; - } - - out << "Usage: " << programName << " ... ...\n"; - - out << "\n"; - out << "Common flags:\n"; - printFlags(out); - - out << "\n"; - out << "Available commands:\n"; - - Table2 table; - for (auto & command : commands) { - auto descr = command.second->description(); - if (!descr.empty()) - table.push_back(std::make_pair(command.second->name(), descr)); - } - printTable(out, table); - -#if 0 - out << "\n"; - out << "For full documentation, run 'man " << programName << "' or 'man " << programName << "-'.\n"; -#endif -} - -bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end) -{ - if (Args::processFlag(pos, end)) return true; - if (command && command->processFlag(pos, end)) return true; - return false; -} - -bool MultiCommand::processArgs(const Strings & args, bool finish) -{ - if (command) - return command->processArgs(args, finish); - else - return Args::processArgs(args, finish); -} - StoreCommand::StoreCommand() { } diff --git a/src/nix/command.hh b/src/nix/command.hh index 2108aa674..04183c7ed 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -11,27 +11,6 @@ struct Value; class Bindings; class EvalState; -/* A command is an argument parser that can be executed by calling its - run() method. */ -struct Command : virtual Args -{ - virtual std::string name() = 0; - virtual void prepare() { }; - virtual void run() = 0; - - struct Example - { - std::string description; - std::string command; - }; - - typedef std::list Examples; - - virtual Examples examples() { return Examples(); } - - void printHelp(const string & programName, std::ostream & out) override; -}; - class Store; /* A command that require a Nix store. */ @@ -162,26 +141,6 @@ struct StorePathCommand : public InstallablesCommand void run(ref store) override; }; -typedef std::map> Commands; - -/* An argument parser that supports multiple subcommands, - i.e. ‘ ’. */ -class MultiCommand : virtual Args -{ -public: - Commands commands; - - std::shared_ptr command; - - MultiCommand(const std::vector> & commands); - - void printHelp(const string & programName, std::ostream & out) override; - - bool processFlag(Strings::iterator & pos, Strings::iterator end) override; - - bool processArgs(const Strings & args, bool finish) override; -}; - /* A helper class for registering commands globally. */ struct RegisterCommand { diff --git a/src/nix/main.cc b/src/nix/main.cc index 3d4348d28..4b909736d 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -60,6 +60,11 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs void printHelp(const string & programName, std::ostream & out) { MultiCommand::printHelp(programName, out); + +#if 0 + out << "\nFor full documentation, run 'man " << programName << "' or 'man " << programName << "-'.\n"; +#endif + std::cout << "\nNote: this program is EXPERIMENTAL and subject to change.\n"; } -- cgit v1.2.3 From 7a5cf31060289de61370643937277b5d0d5d178c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 29 Nov 2018 19:18:36 +0100 Subject: Initial flake support --- src/nix/flake.cc | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nix/installables.cc | 39 ++--------------------------- 2 files changed, 67 insertions(+), 37 deletions(-) create mode 100644 src/nix/flake.cc (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc new file mode 100644 index 000000000..98cd90c64 --- /dev/null +++ b/src/nix/flake.cc @@ -0,0 +1,65 @@ +#include "command.hh" +#include "common-args.hh" +#include "shared.hh" +#include "progress-bar.hh" +#include "eval.hh" + +using namespace nix; + +struct CmdFlakeList : StoreCommand, MixEvalArgs +{ + std::string name() override + { + return "list"; + } + + std::string description() override + { + return "list available Nix flakes"; + } + + void run(nix::ref store) override + { + auto evalState = std::make_shared(searchPath, store); + + auto registry = evalState->getFlakeRegistry(); + + stopProgressBar(); + + for (auto & entry : registry.entries) { + std::cout << entry.first << " " << entry.second.uri << "\n"; + } + } +}; + +struct CmdFlake : virtual MultiCommand, virtual Command +{ + CmdFlake() + : MultiCommand({make_ref()}) + { + } + + std::string name() override + { + return "flake"; + } + + std::string description() override + { + return "manage Nix flakes"; + } + + void run() override + { + if (!command) + throw UsageError("'nix flake' requires a sub-command."); + command->run(); + } + + void printHelp(const string & programName, std::ostream & out) override + { + MultiCommand::printHelp(programName, out); + } +}; + +static RegisterCommand r1(make_ref()); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 0c1ad3ab3..9b7b96c25 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -26,47 +26,12 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) { if (vSourceExpr) return vSourceExpr; - auto sToplevel = state.symbols.create("_toplevel"); - vSourceExpr = state.allocValue(); if (file != "") state.evalFile(lookupFileArg(state, file), *vSourceExpr); - - else { - - /* Construct the installation source from $NIX_PATH. */ - - auto searchPath = state.getSearchPath(); - - state.mkAttrs(*vSourceExpr, searchPath.size() + 1); - - mkBool(*state.allocAttr(*vSourceExpr, sToplevel), true); - - std::unordered_set seen; - - for (auto & i : searchPath) { - if (i.first == "") continue; - if (seen.count(i.first)) continue; - seen.insert(i.first); -#if 0 - auto res = state.resolveSearchPathElem(i); - if (!res.first) continue; - if (!pathExists(res.second)) continue; - mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)), - state.getBuiltin("import"), - mkString(*state.allocValue(), res.second)); -#endif - Value * v1 = state.allocValue(); - mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath")); - Value * v2 = state.allocValue(); - mkApp(*v2, *v1, mkString(*state.allocValue(), i.first)); - mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)), - state.getBuiltin("import"), *v2); - } - - vSourceExpr->attrs->sort(); - } + else + state.evalFile(lookupFileArg(state, ""), *vSourceExpr); return vSourceExpr; } -- cgit v1.2.3 From c8a0b9d5cbfe6619f8b38118f5b1d1875d1c5309 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Feb 2019 13:43:32 +0100 Subject: experimental/optional -> optional --- src/nix/add-to-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index d0003790d..e86b96e3f 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -8,7 +8,7 @@ using namespace nix; struct CmdAddToStore : MixDryRun, StoreCommand { Path path; - std::experimental::optional namePart; + std::optional namePart; CmdAddToStore() { -- cgit v1.2.3 From 91a6a47b0e98f4114c263ef32895e749639c50ad Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Feb 2019 18:23:11 +0100 Subject: Improve flake references --- src/nix/flake.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 98cd90c64..9b36c3cbd 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1,3 +1,4 @@ +#include "primops/flake.hh" #include "command.hh" #include "common-args.hh" #include "shared.hh" @@ -27,7 +28,7 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs stopProgressBar(); for (auto & entry : registry.entries) { - std::cout << entry.first << " " << entry.second.uri << "\n"; + std::cout << entry.first << " " << entry.second.ref.to_string() << "\n"; } } }; -- cgit v1.2.3 From ba05f29838b3bafe28c3ea491be711229298cb1b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Feb 2019 20:35:03 +0100 Subject: nix: Enable pure mode by default We want to encourage a brave new world of hermetic evaluation for source-level reproducibility, so flakes should not poke around in the filesystem outside of their explicit dependencies. Note that the default installation source remains impure in that it can refer to mutable flakes, so "nix build nixpkgs.hello" still works (and fetches the latest nixpkgs, unless it has been pinned by the user). A problem with pure evaluation is that builtins.currentSystem is unavailable. For the moment, I've hard-coded "x86_64-linux" in the nixpkgs flake. Eventually, "system" should be a flake function argument. --- src/nix/installables.cc | 11 +++++++++-- src/nix/main.cc | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 9b7b96c25..faad057a7 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -30,8 +30,15 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) if (file != "") state.evalFile(lookupFileArg(state, file), *vSourceExpr); - else - state.evalFile(lookupFileArg(state, ""), *vSourceExpr); + else { + auto fun = state.parseExprFromString( + "builtins.mapAttrs (flakeName: flakeInfo:" + " (getFlake flakeInfo.uri).${flakeName}.provides.packages or {})", "/"); + auto vFun = state.allocValue(); + state.eval(fun, *vFun); + auto vRegistry = state.makeFlakeRegistryValue(); + mkApp(*vSourceExpr, *vFun, *vRegistry); + } return vSourceExpr; } diff --git a/src/nix/main.cc b/src/nix/main.cc index 4b909736d..01b0866f2 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -97,6 +97,7 @@ void mainWrapped(int argc, char * * argv) verbosity = lvlError; settings.verboseBuild = false; + evalSettings.pureEval = true; NixArgs args; -- cgit v1.2.3 From 272b58220d17bc862f646dbc2cb38eea126001c0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Feb 2019 21:05:44 +0100 Subject: Enforce use of immutable flakes in pure mode ... plus a temporary hack to allow impure flakes at top-level for the default installation source. --- src/nix/installables.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index faad057a7..b4584f168 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -31,9 +31,11 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) if (file != "") state.evalFile(lookupFileArg(state, file), *vSourceExpr); else { + // FIXME: remove "impure" hack, call some non-user-accessible + // variant of getFlake instead. auto fun = state.parseExprFromString( "builtins.mapAttrs (flakeName: flakeInfo:" - " (getFlake flakeInfo.uri).${flakeName}.provides.packages or {})", "/"); + " (getFlake (\"impure:\" + flakeInfo.uri)).${flakeName}.provides.packages or {})", "/"); auto vFun = state.allocValue(); state.eval(fun, *vFun); auto vRegistry = state.makeFlakeRegistryValue(); -- cgit v1.2.3 From beab05851bfa895fe538f15f8bbb2da3a20db638 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Feb 2019 21:55:43 +0100 Subject: nix: Add --flake flag This allows using an arbitrary "provides" attribute from the specified flake. For example: nix build --flake nixpkgs packages.hello (Maybe provides.packages should be used for consistency...) --- src/nix/command.hh | 3 ++- src/nix/installables.cc | 26 +++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 04183c7ed..a08347945 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -53,7 +53,8 @@ struct Installable struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs { - Path file; + std::optional file; + std::optional flakeUri; SourceExprCommand(); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index b4584f168..0453c72c2 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -7,6 +7,7 @@ #include "get-drvs.hh" #include "store-api.hh" #include "shared.hh" +#include "primops/flake.hh" #include @@ -18,8 +19,15 @@ SourceExprCommand::SourceExprCommand() .shortName('f') .longName("file") .label("file") - .description("evaluate FILE rather than the default") + .description("evaluate FILE rather than use the default installation source") .dest(&file); + + mkFlag() + .shortName('F') + .longName("flake") + .label("flake") + .description("evaluate FLAKE rather than use the default installation source") + .dest(&flakeUri); } Value * SourceExprCommand::getSourceExpr(EvalState & state) @@ -28,9 +36,17 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) vSourceExpr = state.allocValue(); - if (file != "") - state.evalFile(lookupFileArg(state, file), *vSourceExpr); - else { + if (file && flakeUri) + throw Error("cannot use both --file and --flake"); + + if (file) + state.evalFile(lookupFileArg(state, *file), *vSourceExpr); + else if (flakeUri) { + // FIXME: handle flakeUri being a relative path + auto vTemp = state.allocValue(); + auto vFlake = *makeFlakeValue(state, "impure:" + *flakeUri, *vTemp); + *vSourceExpr = *((*vFlake.attrs->get(state.symbols.create("provides")))->value); + } else { // FIXME: remove "impure" hack, call some non-user-accessible // variant of getFlake instead. auto fun = state.parseExprFromString( @@ -38,7 +54,7 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) " (getFlake (\"impure:\" + flakeInfo.uri)).${flakeName}.provides.packages or {})", "/"); auto vFun = state.allocValue(); state.eval(fun, *vFun); - auto vRegistry = state.makeFlakeRegistryValue(); + auto vRegistry = makeFlakeRegistryValue(state); mkApp(*vSourceExpr, *vFun, *vRegistry); } -- cgit v1.2.3 From cfb6ab80cea7f0ed3f525e8120f2e569f963fa0e Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Feb 2019 06:53:01 +0100 Subject: Implemented "nix flake info" --- src/nix/command.hh | 11 +++++++++++ src/nix/flake.cc | 23 ++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index a08347945..b3248222e 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -34,6 +34,17 @@ struct Buildable typedef std::vector Buildables; +struct FlakeCommand : virtual Args, StoreCommand, MixEvalArgs +{ + std::string flakeUri; + +public: + FlakeCommand() + { + expectArg("flake-uri", &flakeUri); + } +}; + struct Installable { virtual std::string what() = 0; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9b36c3cbd..099425688 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -33,10 +33,31 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs } }; +struct CmdFlakeInfo : FlakeCommand +{ + std::string name() override + { + return "info"; + } + + std::string description() override + { + return "list info about a given flake"; + } + + void run(nix::ref store) override + { + auto evalState = std::make_shared(searchPath, store); + nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); + std::cout << "Location: " << flake.path << "\n"; + std::cout << "Description: " << flake.description << "\n"; + } +}; + struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() - : MultiCommand({make_ref()}) + : MultiCommand({make_ref(), make_ref()}) { } -- cgit v1.2.3 From 9ff1a9ea65bdeb520becb843b8300a23fb88a5a9 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Wed, 27 Feb 2019 19:54:18 +0100 Subject: Implemented json flag for `nix flake info` --- src/nix/command.cc | 9 +++++++++ src/nix/command.hh | 7 +++++++ src/nix/flake.cc | 14 +++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index 5967ab36c..e1e32aaae 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -27,6 +27,15 @@ void StoreCommand::run() run(getStore()); } +JsonFormattable::JsonFormattable() +{ + mkFlag() + .longName("json-formattable") + .shortName('j') + .description("output will be printed as json") + .handler([&]() { jsonFormatting = true; }); +} + StorePathsCommand::StorePathsCommand(bool recursive) : recursive(recursive) { diff --git a/src/nix/command.hh b/src/nix/command.hh index b3248222e..5c2f8c304 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -26,6 +26,13 @@ private: std::shared_ptr _store; }; +struct JsonFormattable : virtual Command +{ + bool jsonFormatting = false;; + + JsonFormattable(); +}; + struct Buildable { Path drvPath; // may be empty diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 099425688..22e5b297c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -4,6 +4,7 @@ #include "shared.hh" #include "progress-bar.hh" #include "eval.hh" +#include using namespace nix; @@ -33,7 +34,7 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs } }; -struct CmdFlakeInfo : FlakeCommand +struct CmdFlakeInfo : FlakeCommand, JsonFormattable { std::string name() override { @@ -49,8 +50,15 @@ struct CmdFlakeInfo : FlakeCommand { auto evalState = std::make_shared(searchPath, store); nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); - std::cout << "Location: " << flake.path << "\n"; - std::cout << "Description: " << flake.description << "\n"; + if (jsonFormatting) { + nlohmann::json j; + j["location"] = flake.path; + j["description"] = flake.description; + std::cout << j.dump(4) << std::endl; + } else { + std::cout << "Location: " << flake.path << "\n"; + std::cout << "Description: " << flake.description << "\n"; + } } }; -- cgit v1.2.3 From d4ee8afd59cd7935f59b730c432cf58460af8a84 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Feb 2019 06:53:01 +0100 Subject: Implemented --flake flag for nix build Also fixed Eelco's PR comments --- src/nix/build.cc | 15 +++++++++++++++ src/nix/command.cc | 9 --------- src/nix/command.hh | 7 ------- src/nix/flake.cc | 29 ++++++++++++++++++++++++++--- 4 files changed, 41 insertions(+), 19 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index b329ac38a..12ef08679 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -1,3 +1,5 @@ +#include "primops/flake.hh" +#include "eval.hh" #include "command.hh" #include "common-args.hh" #include "shared.hh" @@ -9,6 +11,8 @@ struct CmdBuild : MixDryRun, InstallablesCommand { Path outLink = "result"; + std::optional flakeUri = std::nullopt; + CmdBuild() { mkFlag() @@ -22,6 +26,11 @@ struct CmdBuild : MixDryRun, InstallablesCommand .longName("no-link") .description("do not create a symlink to the build result") .set(&outLink, Path("")); + + mkFlag() + .longName("flake") + .description("update lock file of given flake") + .dest(&flakeUri); } std::string name() override @@ -52,6 +61,8 @@ struct CmdBuild : MixDryRun, InstallablesCommand { auto buildables = build(store, dryRun ? DryRun : Build, installables); + auto evalState = std::make_shared(searchPath, store); + if (dryRun) return; for (size_t i = 0; i < buildables.size(); ++i) { @@ -66,6 +77,10 @@ struct CmdBuild : MixDryRun, InstallablesCommand store2->addPermRoot(output.second, absPath(symlink), true); } } + + if (flakeUri) { + updateLockFile(*evalState, *flakeUri); + } } }; diff --git a/src/nix/command.cc b/src/nix/command.cc index e1e32aaae..5967ab36c 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -27,15 +27,6 @@ void StoreCommand::run() run(getStore()); } -JsonFormattable::JsonFormattable() -{ - mkFlag() - .longName("json-formattable") - .shortName('j') - .description("output will be printed as json") - .handler([&]() { jsonFormatting = true; }); -} - StorePathsCommand::StorePathsCommand(bool recursive) : recursive(recursive) { diff --git a/src/nix/command.hh b/src/nix/command.hh index 5c2f8c304..b3248222e 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -26,13 +26,6 @@ private: std::shared_ptr _store; }; -struct JsonFormattable : virtual Command -{ - bool jsonFormatting = false;; - - JsonFormattable(); -}; - struct Buildable { Path drvPath; // may be empty diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 22e5b297c..6cef38936 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -34,7 +34,28 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs } }; -struct CmdFlakeInfo : FlakeCommand, JsonFormattable +struct CmdFlakeUpdate : FlakeCommand +{ + std::string name() override + { + return "update"; + } + + std::string description() override + { + return "update flake lock file"; + } + + void run(nix::ref store) override + { + auto evalState = std::make_shared(searchPath, store); + + if (flakeUri == "") flakeUri = absPath("./flake.nix"); + updateLockFile(*evalState, flakeUri); + } +}; + +struct CmdFlakeInfo : FlakeCommand, MixJSON { std::string name() override { @@ -50,7 +71,7 @@ struct CmdFlakeInfo : FlakeCommand, JsonFormattable { auto evalState = std::make_shared(searchPath, store); nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); - if (jsonFormatting) { + if (json) { nlohmann::json j; j["location"] = flake.path; j["description"] = flake.description; @@ -65,7 +86,9 @@ struct CmdFlakeInfo : FlakeCommand, JsonFormattable struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() - : MultiCommand({make_ref(), make_ref()}) + : MultiCommand({make_ref() + , make_ref() + , make_ref()}) { } -- cgit v1.2.3 From e007f367bd605ad14ddf84d1d5ad611aa427d338 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Feb 2019 06:53:01 +0100 Subject: Fixed minor things --- src/nix/build.cc | 13 ++++++------- src/nix/command.hh | 21 +++++++++++++++------ src/nix/flake.cc | 11 ++++++++--- src/nix/installables.cc | 1 + 4 files changed, 30 insertions(+), 16 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 12ef08679..5ab22e26c 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -11,7 +11,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand { Path outLink = "result"; - std::optional flakeUri = std::nullopt; + std::optional gitRepo = std::nullopt; CmdBuild() { @@ -28,9 +28,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand .set(&outLink, Path("")); mkFlag() - .longName("flake") - .description("update lock file of given flake") - .dest(&flakeUri); + .longName("update-lock-file") + .description("update the lock file") + .dest(&gitRepo); } std::string name() override @@ -78,9 +78,8 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } - if (flakeUri) { - updateLockFile(*evalState, *flakeUri); - } + if(gitRepo) + updateLockFile(*evalState, *gitRepo); } }; diff --git a/src/nix/command.hh b/src/nix/command.hh index b3248222e..c58d5d56e 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -34,15 +34,24 @@ struct Buildable typedef std::vector Buildables; +struct GitRepoCommand : virtual Args +{ + std::string gitPath = absPath("."); + + GitRepoCommand () + { + expectArg("git-path", &gitPath, true); + } +}; + struct FlakeCommand : virtual Args, StoreCommand, MixEvalArgs { - std::string flakeUri; + std::string flakeUri; -public: - FlakeCommand() - { - expectArg("flake-uri", &flakeUri); - } + FlakeCommand() + { + expectArg("flake-uri", &flakeUri); + } }; struct Installable diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6cef38936..a5a1d34db 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -34,7 +34,7 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs } }; -struct CmdFlakeUpdate : FlakeCommand +struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs { std::string name() override { @@ -51,7 +51,12 @@ struct CmdFlakeUpdate : FlakeCommand auto evalState = std::make_shared(searchPath, store); if (flakeUri == "") flakeUri = absPath("./flake.nix"); - updateLockFile(*evalState, flakeUri); + int result = updateLockFile(*evalState, flakeUri); + if (result == 1) { + std::cout << "You can only update local flakes, not flakes on GitHub.\n"; + } else if (result == 2) { + std::cout << "You can only update local flakes, not flakes through their FlakeId.\n"; + } } }; @@ -77,8 +82,8 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON j["description"] = flake.description; std::cout << j.dump(4) << std::endl; } else { - std::cout << "Location: " << flake.path << "\n"; std::cout << "Description: " << flake.description << "\n"; + std::cout << "Location: " << flake.path << "\n"; } } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 0453c72c2..21e9e73b8 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -234,6 +234,7 @@ Buildables build(ref store, RealiseMode mode, PathSet pathsToBuild; for (auto & i : installables) { + std::cout << i->what() << std::endl; for (auto & b : i->toBuildables()) { if (b.drvPath != "") { StringSet outputNames; -- cgit v1.2.3 From 5e4d92d267c080bcb81168e37429bbb56bc39fb2 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Sun, 10 Mar 2019 07:05:05 +0100 Subject: Issue #15 is finished --- src/nix/command.hh | 2 +- src/nix/flake.cc | 113 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 105 insertions(+), 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index c58d5d56e..ffe64ccb7 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -44,7 +44,7 @@ struct GitRepoCommand : virtual Args } }; -struct FlakeCommand : virtual Args, StoreCommand, MixEvalArgs +struct FlakeCommand : virtual Args { std::string flakeUri; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a5a1d34db..fda903944 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -50,17 +50,12 @@ struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs { auto evalState = std::make_shared(searchPath, store); - if (flakeUri == "") flakeUri = absPath("./flake.nix"); - int result = updateLockFile(*evalState, flakeUri); - if (result == 1) { - std::cout << "You can only update local flakes, not flakes on GitHub.\n"; - } else if (result == 2) { - std::cout << "You can only update local flakes, not flakes through their FlakeId.\n"; - } + if (gitPath == "") gitPath = absPath("."); + updateLockFile(*evalState, gitPath); } }; -struct CmdFlakeInfo : FlakeCommand, MixJSON +struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand { std::string name() override { @@ -88,12 +83,112 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON } }; +struct CmdFlakeAdd : MixEvalArgs, Command +{ + std::string flakeId; + std::string flakeUri; + + std::string name() override + { + return "add"; + } + + std::string description() override + { + return "upsert flake in user flake registry"; + } + + CmdFlakeAdd() + { + expectArg("flake-id", &flakeId); + expectArg("flake-uri", &flakeUri); + } + + void run() override + { + FlakeRef newFlakeRef(flakeUri); + Path userRegistryPath = getUserRegistryPath(); + auto userRegistry = readRegistry(userRegistryPath); + FlakeRegistry::Entry entry(newFlakeRef); + userRegistry->entries.erase(flakeId); + userRegistry->entries.insert_or_assign(flakeId, newFlakeRef); + writeRegistry(*userRegistry, userRegistryPath); + } +}; + +struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command +{ + std::string flakeId; + + std::string name() override + { + return "remove"; + } + + std::string description() override + { + return "remove flake from user flake registry"; + } + + CmdFlakeRemove() + { + expectArg("flake-id", &flakeId); + } + + void run() override + { + Path userRegistryPath = getUserRegistryPath(); + auto userRegistry = readRegistry(userRegistryPath); + userRegistry->entries.erase(flakeId); + writeRegistry(*userRegistry, userRegistryPath); + } +}; + +struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs +{ + std::string flakeId; + + std::string name() override + { + return "pin"; + } + + std::string description() override + { + return "pin flake require in user flake registry"; + } + + CmdFlakePin() + { + expectArg("flake-id", &flakeId); + } + + void run(nix::ref store) override + { + auto evalState = std::make_shared(searchPath, store); + + Path userRegistryPath = getUserRegistryPath(); + FlakeRegistry userRegistry = *readRegistry(userRegistryPath); + auto it = userRegistry.entries.find(flakeId); + if (it != userRegistry.entries.end()) { + FlakeRef oldRef = it->second.ref; + it->second.ref = getFlake(*evalState, oldRef).ref; + // The 'ref' in 'flake' is immutable. + writeRegistry(userRegistry, userRegistryPath); + } else + throw Error("the flake identifier '%s' does not exist in the user registry", flakeId); + } +}; + struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() : MultiCommand({make_ref() + , make_ref() , make_ref() - , make_ref()}) + , make_ref() + , make_ref() + , make_ref()}) { } -- cgit v1.2.3 From a554f523db34a5d6a8281c5228acfc128a8bd589 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: Combining registries properly --- src/nix/flake.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index fda903944..470dfdc08 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -24,12 +24,14 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs { auto evalState = std::make_shared(searchPath, store); - auto registry = evalState->getFlakeRegistry(); + auto registries = evalState->getFlakeRegistries(); stopProgressBar(); - for (auto & entry : registry.entries) { - std::cout << entry.first << " " << entry.second.ref.to_string() << "\n"; + for (auto & registry : registries) { + for (auto & entry : registry->entries) { + std::cout << entry.first << " " << entry.second.ref.to_string() << "\n"; + } } } }; -- cgit v1.2.3 From be7fd6359559717b83833d96d4b6dc38ceb83092 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 26 Mar 2019 14:25:43 +0100 Subject: Remove debug line --- src/nix/build.cc | 2 +- src/nix/installables.cc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 5ab22e26c..da7c7f614 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -78,7 +78,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } - if(gitRepo) + if (gitRepo) updateLockFile(*evalState, *gitRepo); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 21e9e73b8..0453c72c2 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -234,7 +234,6 @@ Buildables build(ref store, RealiseMode mode, PathSet pathsToBuild; for (auto & i : installables) { - std::cout << i->what() << std::endl; for (auto & b : i->toBuildables()) { if (b.drvPath != "") { StringSet outputNames; -- cgit v1.2.3 From 154244adc6c9831e00a41bf7799a2d29c6a3a3b4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 14:21:13 +0200 Subject: nix: New installables syntax The general syntax for an installable is now :. The attrpath is relative to the flake's 'provides.packages' or 'provides' if the former doesn't yield a result. E.g. $ nix build nixpkgs:hello is equivalent to $ nix build nixpkgs:packages.hello Also, ':' can be omitted, in which case it defaults to 'nixpkgs', e.g. $ nix build hello --- src/nix/installables.cc | 71 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 17 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 0453c72c2..5e4ea6054 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -21,13 +21,6 @@ SourceExprCommand::SourceExprCommand() .label("file") .description("evaluate FILE rather than use the default installation source") .dest(&file); - - mkFlag() - .shortName('F') - .longName("flake") - .label("flake") - .description("evaluate FLAKE rather than use the default installation source") - .dest(&flakeUri); } Value * SourceExprCommand::getSourceExpr(EvalState & state) @@ -36,17 +29,9 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) vSourceExpr = state.allocValue(); - if (file && flakeUri) - throw Error("cannot use both --file and --flake"); - if (file) state.evalFile(lookupFileArg(state, *file), *vSourceExpr); - else if (flakeUri) { - // FIXME: handle flakeUri being a relative path - auto vTemp = state.allocValue(); - auto vFlake = *makeFlakeValue(state, "impure:" + *flakeUri, *vTemp); - *vSourceExpr = *((*vFlake.attrs->get(state.symbols.create("provides")))->value); - } else { + else { // FIXME: remove "impure" hack, call some non-user-accessible // variant of getFlake instead. auto fun = state.parseExprFromString( @@ -176,6 +161,43 @@ struct InstallableAttrPath : InstallableValue } }; +struct InstallableFlake : InstallableValue +{ + FlakeRef flakeRef; + std::string attrPath; + + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, const std::string & attrPath) + : InstallableValue(cmd), flakeRef(flakeRef), attrPath(attrPath) + { } + + std::string what() override { return flakeRef.to_string() + ":" + attrPath; } + + Value * toValue(EvalState & state) override + { + auto vTemp = state.allocValue(); + auto vFlake = *makeFlakeValue(state, flakeRef, true, *vTemp); + + auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value; + + state.forceValue(*vProvides); + + auto emptyArgs = state.allocBindings(0); + + if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) { + try { + auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *aPackages->value); + state.forceValue(*v); + return v; + } catch (AttrPathNotFound & e) { + } + } + + auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vProvides); + state.forceValue(*v); + return v; + } +}; + // FIXME: extend std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex)); @@ -196,19 +218,34 @@ static std::vector> parseInstallables( if (s.compare(0, 1, "(") == 0) result.push_back(std::make_shared(cmd, s)); - else if (s.find("/") != std::string::npos) { + /* + else if (s.find('/') != std::string::npos) { auto path = store->toStorePath(store->followLinksToStore(s)); if (store->isStorePath(path)) result.push_back(std::make_shared(path)); } + */ + + else { + auto colon = s.rfind(':'); + if (colon != std::string::npos) { + auto flakeRef = std::string(s, 0, colon); + auto attrPath = std::string(s, colon + 1); + result.push_back(std::make_shared(cmd, FlakeRef(flakeRef), attrPath)); + } else { + result.push_back(std::make_shared(cmd, FlakeRef("nixpkgs"), s)); + } + } + /* else if (s == "" || std::regex_match(s, attrPathRegex)) result.push_back(std::make_shared(cmd, s)); else throw UsageError("don't know what to do with argument '%s'", s); + */ } return result; -- cgit v1.2.3 From 101d964a59d5c9098845d3109ea1eba99b5f31df Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 16:11:17 +0200 Subject: nix: Make -f work for compatibility --- src/nix/command.hh | 23 +++------- src/nix/installables.cc | 119 ++++++++++++++++++++---------------------------- src/nix/search.cc | 4 +- src/nix/why-depends.cc | 4 +- 4 files changed, 60 insertions(+), 90 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index ffe64ccb7..83959bf9a 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -74,23 +74,20 @@ struct Installable struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs { std::optional file; - std::optional flakeUri; SourceExprCommand(); - /* Return a value representing the Nix expression from which we - are installing. This is either the file specified by ‘--file’, - or an attribute set constructed from $NIX_PATH, e.g. ‘{ nixpkgs - = import ...; bla = import ...; }’. */ - Value * getSourceExpr(EvalState & state); - ref getEvalState(); + std::vector> parseInstallables( + ref store, std::vector ss); + + std::shared_ptr parseInstallable( + ref store, const std::string & installable); + private: std::shared_ptr evalState; - - Value * vSourceExpr = 0; }; enum RealiseMode { Build, NoBuild, DryRun }; @@ -108,8 +105,6 @@ struct InstallablesCommand : virtual Args, SourceExprCommand void prepare() override; - virtual bool useDefaultInstallables() { return true; } - private: std::vector _installables; @@ -148,8 +143,6 @@ public: virtual void run(ref store, Paths storePaths) = 0; void run(ref store) override; - - bool useDefaultInstallables() override { return !all; } }; /* A command that operates on exactly one store path. */ @@ -174,10 +167,6 @@ struct RegisterCommand } }; -std::shared_ptr parseInstallable( - SourceExprCommand & cmd, ref store, const std::string & installable, - bool useDefaultInstallables); - Buildables build(ref store, RealiseMode mode, std::vector> installables); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 5e4ea6054..bcb22349a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -19,33 +19,10 @@ SourceExprCommand::SourceExprCommand() .shortName('f') .longName("file") .label("file") - .description("evaluate FILE rather than use the default installation source") + .description("evaluate a set of attributes from FILE (deprecated)") .dest(&file); } -Value * SourceExprCommand::getSourceExpr(EvalState & state) -{ - if (vSourceExpr) return vSourceExpr; - - vSourceExpr = state.allocValue(); - - if (file) - state.evalFile(lookupFileArg(state, *file), *vSourceExpr); - else { - // FIXME: remove "impure" hack, call some non-user-accessible - // variant of getFlake instead. - auto fun = state.parseExprFromString( - "builtins.mapAttrs (flakeName: flakeInfo:" - " (getFlake (\"impure:\" + flakeInfo.uri)).${flakeName}.provides.packages or {})", "/"); - auto vFun = state.allocValue(); - state.eval(fun, *vFun); - auto vRegistry = makeFlakeRegistryValue(state); - mkApp(*vSourceExpr, *vFun, *vRegistry); - } - - return vSourceExpr; -} - ref SourceExprCommand::getEvalState() { if (!evalState) @@ -140,24 +117,20 @@ struct InstallableExpr : InstallableValue struct InstallableAttrPath : InstallableValue { + Value * v; std::string attrPath; - InstallableAttrPath(SourceExprCommand & cmd, const std::string & attrPath) - : InstallableValue(cmd), attrPath(attrPath) + InstallableAttrPath(SourceExprCommand & cmd, Value * v, const std::string & attrPath) + : InstallableValue(cmd), v(v), attrPath(attrPath) { } std::string what() override { return attrPath; } Value * toValue(EvalState & state) override { - auto source = cmd.getSourceExpr(state); - - Bindings & autoArgs = *cmd.getAutoArgs(state); - - Value * v = findAlongAttrPath(state, attrPath, autoArgs, *source); - state.forceValue(*v); - - return v; + auto vRes = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), *v); + state.forceValue(*vRes); + return vRes; } }; @@ -202,60 +175,66 @@ struct InstallableFlake : InstallableValue std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex)); -static std::vector> parseInstallables( - SourceExprCommand & cmd, ref store, std::vector ss, bool useDefaultInstallables) +std::vector> SourceExprCommand::parseInstallables( + ref store, std::vector ss) { std::vector> result; - if (ss.empty() && useDefaultInstallables) { - if (cmd.file == "") - cmd.file = "."; - ss = {""}; - } + if (file) { + // FIXME: backward compatibility hack + evalSettings.pureEval = false; - for (auto & s : ss) { + auto state = getEvalState(); + auto vFile = state->allocValue(); + state->evalFile(lookupFileArg(*state, *file), *vFile); - if (s.compare(0, 1, "(") == 0) - result.push_back(std::make_shared(cmd, s)); + if (ss.empty()) + ss = {""}; - /* - else if (s.find('/') != std::string::npos) { + for (auto & s : ss) + result.push_back(std::make_shared(*this, vFile, s)); - auto path = store->toStorePath(store->followLinksToStore(s)); + } else { - if (store->isStorePath(path)) - result.push_back(std::make_shared(path)); - } - */ + for (auto & s : ss) { + + size_t colon; - else { - auto colon = s.rfind(':'); - if (colon != std::string::npos) { + if (s.compare(0, 1, "(") == 0) + result.push_back(std::make_shared(*this, s)); + + else if ((colon = s.rfind(':')) != std::string::npos) { auto flakeRef = std::string(s, 0, colon); auto attrPath = std::string(s, colon + 1); - result.push_back(std::make_shared(cmd, FlakeRef(flakeRef), attrPath)); - } else { - result.push_back(std::make_shared(cmd, FlakeRef("nixpkgs"), s)); + result.push_back(std::make_shared(*this, FlakeRef(flakeRef), attrPath)); } - } - /* - else if (s == "" || std::regex_match(s, attrPathRegex)) - result.push_back(std::make_shared(cmd, s)); + else if (s.find('/') != std::string::npos) { + auto path = store->toStorePath(store->followLinksToStore(s)); + result.push_back(std::make_shared(path)); + } + + else { + result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); + } - else - throw UsageError("don't know what to do with argument '%s'", s); - */ + /* + else if (s == "" || std::regex_match(s, attrPathRegex)) + result.push_back(std::make_shared(cmd, s)); + + else + throw UsageError("don't know what to do with argument '%s'", s); + */ + } } return result; } -std::shared_ptr parseInstallable( - SourceExprCommand & cmd, ref store, const std::string & installable, - bool useDefaultInstallables) +std::shared_ptr SourceExprCommand::parseInstallable( + ref store, const std::string & installable) { - auto installables = parseInstallables(cmd, store, {installable}, false); + auto installables = parseInstallables(store, {installable}); assert(installables.size() == 1); return installables.front(); } @@ -342,12 +321,12 @@ PathSet toDerivations(ref store, void InstallablesCommand::prepare() { - installables = parseInstallables(*this, getStore(), _installables, useDefaultInstallables()); + installables = parseInstallables(getStore(), _installables); } void InstallableCommand::prepare() { - installable = parseInstallable(*this, getStore(), _installable, false); + installable = parseInstallable(getStore(), _installable); } } diff --git a/src/nix/search.cc b/src/nix/search.cc index e086de226..55f8d106a 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -257,7 +257,9 @@ struct CmdSearch : SourceExprCommand, MixJSON auto cache = writeCache ? std::make_unique(jsonCacheFile, false) : nullptr; - doExpr(getSourceExpr(*state), "", true, cache.get()); + // FIXME + throw Error("NOT IMPLEMENTED"); + //doExpr(getSourceExpr(*state), "", true, cache.get()); } catch (std::exception &) { /* Fun fact: catching std::ios::failure does not work diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index 325a2be0a..32ba5a1ad 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -74,9 +74,9 @@ struct CmdWhyDepends : SourceExprCommand void run(ref store) override { - auto package = parseInstallable(*this, store, _package, false); + auto package = parseInstallable(store, _package); auto packagePath = toStorePath(store, Build, package); - auto dependency = parseInstallable(*this, store, _dependency, false); + auto dependency = parseInstallable(store, _dependency); auto dependencyPath = toStorePath(store, NoBuild, dependency); auto dependencyPathHash = storePathToHash(dependencyPath); -- cgit v1.2.3 From 4023ae4cdf146b2ee491c12ec64e8605984bf49a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 16:22:04 +0200 Subject: nix: Support nixpkgs. for compatibility --- src/nix/installables.cc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index bcb22349a..6d3969e95 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -203,6 +203,12 @@ std::vector> SourceExprCommand::parseInstallables( if (s.compare(0, 1, "(") == 0) result.push_back(std::make_shared(*this, s)); + else if (hasPrefix(s, "nixpkgs.")) { + bool static warned; + warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); + result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), std::string(s, 8))); + } + else if ((colon = s.rfind(':')) != std::string::npos) { auto flakeRef = std::string(s, 0, colon); auto attrPath = std::string(s, colon + 1); @@ -214,17 +220,8 @@ std::vector> SourceExprCommand::parseInstallables( result.push_back(std::make_shared(path)); } - else { - result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); - } - - /* - else if (s == "" || std::regex_match(s, attrPathRegex)) - result.push_back(std::make_shared(cmd, s)); - else - throw UsageError("don't know what to do with argument '%s'", s); - */ + result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); } } -- cgit v1.2.3 From ee1254d4f50f5908fa4913253a643d14cb263c45 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 23:19:19 +0200 Subject: nix: Add --impure as a shorter alias of --no-pure-eval --- src/nix/installables.cc | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 6d3969e95..631a849cd 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -21,6 +21,13 @@ SourceExprCommand::SourceExprCommand() .label("file") .description("evaluate a set of attributes from FILE (deprecated)") .dest(&file); + + mkFlag() + .longName("impure") + .description("allow access to mutable paths and repositories") + .handler([&](std::vector ss) { + evalSettings.pureEval = false; + }); } ref SourceExprCommand::getEvalState() -- cgit v1.2.3 From 47727252ff4e536dd47b73949033d3349923fbbb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 23:36:12 +0200 Subject: Add "nix flake init" command for creating a flake --- src/nix/flake.cc | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 470dfdc08..01385ff8d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -182,6 +182,51 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs } }; +struct CmdFlakeInit : virtual Args, Command +{ + std::string name() override + { + return "init"; + } + + std::string description() override + { + return "create a skeleton 'flake.nix' file in the current directory"; + } + + void run() override + { + Path flakeDir = absPath("."); + + if (!pathExists(flakeDir + "/.git")) + throw Error("the directory '%s' is not a Git repository", flakeDir); + + Path flakePath = flakeDir + "/flake.nix"; + + if (pathExists(flakePath)) + throw Error("file '%s' already exists", flakePath); + + writeFile(flakePath, +R"str( +{ + name = "hello"; + + description = "A flake for building Hello World"; + + epoch = 2019; + + requires = [ "nixpkgs" ]; + + provides = deps: rec { + + packages.hello = deps.nixpkgs.provides.packages.hello; + + }; +} +)str"); + } +}; + struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() @@ -190,7 +235,9 @@ struct CmdFlake : virtual MultiCommand, virtual Command , make_ref() , make_ref() , make_ref() - , make_ref()}) + , make_ref() + , make_ref() + }) { } -- cgit v1.2.3 From 507da65900ccb3c6356673e93ad2271c58e43b07 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 23:39:38 +0200 Subject: Move flake template into a separate file --- src/nix/flake-template.nix | 15 +++++++++++++++ src/nix/flake.cc | 19 ++----------------- src/nix/local.mk | 2 ++ 3 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 src/nix/flake-template.nix (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix new file mode 100644 index 000000000..fe89e647e --- /dev/null +++ b/src/nix/flake-template.nix @@ -0,0 +1,15 @@ +{ + name = "hello"; + + description = "A flake for building Hello World"; + + epoch = 2019; + + requires = [ "nixpkgs" ]; + + provides = deps: rec { + + packages.hello = deps.nixpkgs.provides.packages.hello; + + }; +} diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 01385ff8d..3d2fb7832 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -207,23 +207,8 @@ struct CmdFlakeInit : virtual Args, Command throw Error("file '%s' already exists", flakePath); writeFile(flakePath, -R"str( -{ - name = "hello"; - - description = "A flake for building Hello World"; - - epoch = 2019; - - requires = [ "nixpkgs" ]; - - provides = deps: rec { - - packages.hello = deps.nixpkgs.provides.packages.hello; - - }; -} -)str"); +#include "flake-template.nix.gen.hh" + ); } }; diff --git a/src/nix/local.mk b/src/nix/local.mk index ca4604d56..4003d0005 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -23,3 +23,5 @@ $(foreach name, \ nix-build nix-channel nix-collect-garbage nix-copy-closure nix-daemon nix-env nix-hash nix-instantiate nix-prefetch-url nix-shell nix-store, \ $(eval $(call install-symlink, nix, $(bindir)/$(name)))) $(eval $(call install-symlink, $(bindir)/nix, $(libexecdir)/nix/build-remote)) + +$(d)/flake.cc: $(d)/flake-template.nix.gen.hh -- cgit v1.2.3 From c996e04aca2db1755ded4864465338afab677ff5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 23:47:29 +0200 Subject: Allow relative paths in flakerefs Also allow "." as an installable to refer to the flake in the current directory. E.g. $ nix build . will build 'provides.defaultPackage' in the flake in the current directory. --- src/nix/installables.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 631a849cd..f3be7b628 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -219,12 +219,18 @@ std::vector> SourceExprCommand::parseInstallables( else if ((colon = s.rfind(':')) != std::string::npos) { auto flakeRef = std::string(s, 0, colon); auto attrPath = std::string(s, colon + 1); - result.push_back(std::make_shared(*this, FlakeRef(flakeRef), attrPath)); + result.push_back(std::make_shared(*this, FlakeRef(flakeRef, true), attrPath)); } - else if (s.find('/') != std::string::npos) { - auto path = store->toStorePath(store->followLinksToStore(s)); - result.push_back(std::make_shared(path)); + else if (s.find('/') != std::string::npos || s == ".") { + Path storePath; + try { + storePath = store->toStorePath(store->followLinksToStore(s)); + } catch (Error) { } + if (storePath != "") + result.push_back(std::make_shared(storePath)); + else + result.push_back(std::make_shared(*this, FlakeRef(s, true), "defaultPackage")); } else -- cgit v1.2.3 From 87033f2c4e32f4851e8c2abf8ab3b56444b65590 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2019 23:58:33 +0200 Subject: Whitespace --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index f3be7b628..e792ce96d 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -300,7 +300,7 @@ Path toStorePath(ref store, RealiseMode mode, auto paths = toStorePaths(store, mode, {installable}); if (paths.size() != 1) - throw Error("argument '%s' should evaluate to one store path", installable->what()); + throw Error("argument '%s' should evaluate to one store path", installable->what()); return *paths.begin(); } -- cgit v1.2.3 From 18c019b616f457b1f9a39da8cafc012be5ddffcc Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: Added nonFlakeRequires and the command `nix flake deps` --- src/nix/build.cc | 14 ++++++------ src/nix/flake.cc | 65 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 15 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index da7c7f614..a6fcf5094 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -11,7 +11,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand { Path outLink = "result"; - std::optional gitRepo = std::nullopt; + bool updateLock = true; CmdBuild() { @@ -28,9 +28,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand .set(&outLink, Path("")); mkFlag() - .longName("update-lock-file") - .description("update the lock file") - .dest(&gitRepo); + .longName("no-update") + .description("don't update the lock file") + .set(&updateLock, false); } std::string name() override @@ -78,8 +78,10 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } - if (gitRepo) - updateLockFile(*evalState, *gitRepo); + if(updateLock) + for (int i = 0; i < installables.size(); i++) + if (auto flakeUri = installableToFlakeUri) + updateLockFile(*evalState, FlakeRef(*flakeUri)); } }; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3d2fb7832..07d31c45a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -36,6 +36,60 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs } }; +void printFlakeInfo(Flake & flake, bool json) { + if (json) { + nlohmann::json j; + j["name"] = flake.id; + j["location"] = flake.path; + j["description"] = flake.description; + std::cout << j.dump(4) << std::endl; + } else { + std::cout << "Name: " << flake.id << "\n"; + std::cout << "Description: " << flake.description << "\n"; + std::cout << "Location: " << flake.path << "\n"; + } +} + +void printNonFlakeInfo(NonFlake & nonFlake, bool json) { + if (json) { + nlohmann::json j; + j["name"] = nonFlake.id; + j["location"] = nonFlake.path; + std::cout << j.dump(4) << std::endl; + } else { + std::cout << "name: " << nonFlake.id << "\n"; + std::cout << "Location: " << nonFlake.path << "\n"; + } +} + +struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs +{ + std::string name() override + { + return "deps"; + } + + std::string description() override + { + return "list informaton about dependencies"; + } + + void run(nix::ref store) override + { + auto evalState = std::make_shared(searchPath, store); + + FlakeRef flakeRef(flakeUri); + + Dependencies deps = resolveFlake(*evalState, flakeRef, true); + + for (auto & flake : deps.flakes) + printFlakeInfo(flake, json); + + for (auto & nonFlake : deps.nonFlakes) + printNonFlakeInfo(nonFlake, json); + } +}; + struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs { std::string name() override @@ -73,15 +127,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand { auto evalState = std::make_shared(searchPath, store); nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); - if (json) { - nlohmann::json j; - j["location"] = flake.path; - j["description"] = flake.description; - std::cout << j.dump(4) << std::endl; - } else { - std::cout << "Description: " << flake.description << "\n"; - std::cout << "Location: " << flake.path << "\n"; - } + printFlakeInfo(flake, json); } }; @@ -218,6 +264,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command : MultiCommand({make_ref() , make_ref() , make_ref() + , make_ref() , make_ref() , make_ref() , make_ref() -- cgit v1.2.3 From 641db127be9df82fe4d51290120a8ba6d0b5f4fd Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: FlakeIds are now properly looked up in registries --- src/nix/build.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index a6fcf5094..f6908b0c0 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -78,10 +78,11 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } + std::string flakeUri = ""; if(updateLock) - for (int i = 0; i < installables.size(); i++) - if (auto flakeUri = installableToFlakeUri) - updateLockFile(*evalState, FlakeRef(*flakeUri)); + for (uint i = 0; i < installables.size(); i++) + // if (auto flakeUri = installableToFlakeUri) + updateLockFile(*evalState, flakeUri); } }; -- cgit v1.2.3 From 3ec0c82fab94533807c5c3bb25df2b43d8339ed3 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Fri, 29 Mar 2019 16:18:25 +0100 Subject: Fixed dependency resolution --- src/nix/build.cc | 10 +++++----- src/nix/flake.cc | 25 +++++++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index f6908b0c0..226c21e9e 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -78,11 +78,11 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } - std::string flakeUri = ""; - if(updateLock) - for (uint i = 0; i < installables.size(); i++) - // if (auto flakeUri = installableToFlakeUri) - updateLockFile(*evalState, flakeUri); + // std::string flakeUri = ""; + // if(updateLock) + // for (uint i = 0; i < installables.size(); i++) + // // if (auto flakeUri = installableToFlakeUri) + // updateLockFile(*evalState, flakeUri, true); } }; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 07d31c45a..ff291aa80 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -5,6 +5,7 @@ #include "progress-bar.hh" #include "eval.hh" #include +#include using namespace nix; @@ -80,13 +81,21 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs FlakeRef flakeRef(flakeUri); - Dependencies deps = resolveFlake(*evalState, flakeRef, true); + Dependencies deps = resolveFlake(*evalState, flakeRef, true, true); - for (auto & flake : deps.flakes) - printFlakeInfo(flake, json); + std::queue todo; + todo.push(deps); - for (auto & nonFlake : deps.nonFlakes) - printNonFlakeInfo(nonFlake, json); + while (!todo.empty()) { + deps = todo.front(); + todo.pop(); + + for (auto & nonFlake : deps.nonFlakeDeps) + printNonFlakeInfo(nonFlake, json); + + for (auto & newDeps : deps.flakeDeps) + todo.push(newDeps); + } } }; @@ -107,7 +116,7 @@ struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs auto evalState = std::make_shared(searchPath, store); if (gitPath == "") gitPath = absPath("."); - updateLockFile(*evalState, gitPath); + updateLockFile(*evalState, gitPath, true); } }; @@ -126,7 +135,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand void run(nix::ref store) override { auto evalState = std::make_shared(searchPath, store); - nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); + nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri), true); printFlakeInfo(flake, json); } }; @@ -220,7 +229,7 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs auto it = userRegistry.entries.find(flakeId); if (it != userRegistry.entries.end()) { FlakeRef oldRef = it->second.ref; - it->second.ref = getFlake(*evalState, oldRef).ref; + it->second.ref = getFlake(*evalState, oldRef, true).ref; // The 'ref' in 'flake' is immutable. writeRegistry(userRegistry, userRegistryPath); } else -- cgit v1.2.3 From c64f98b883515df70e2457ae01070b5af9ae69b9 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: FlakeAlias is implemented --- src/nix/build.cc | 2 +- src/nix/flake.cc | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 226c21e9e..a2fc56e69 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -82,7 +82,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand // if(updateLock) // for (uint i = 0; i < installables.size(); i++) // // if (auto flakeUri = installableToFlakeUri) - // updateLockFile(*evalState, flakeUri, true); + // updateLockFile(*evalState, flakeUri); } }; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index ff291aa80..df944a148 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -40,12 +40,12 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs void printFlakeInfo(Flake & flake, bool json) { if (json) { nlohmann::json j; - j["name"] = flake.id; + j["id"] = flake.id; j["location"] = flake.path; j["description"] = flake.description; std::cout << j.dump(4) << std::endl; } else { - std::cout << "Name: " << flake.id << "\n"; + std::cout << "ID: " << flake.id << "\n"; std::cout << "Description: " << flake.description << "\n"; std::cout << "Location: " << flake.path << "\n"; } @@ -54,11 +54,11 @@ void printFlakeInfo(Flake & flake, bool json) { void printNonFlakeInfo(NonFlake & nonFlake, bool json) { if (json) { nlohmann::json j; - j["name"] = nonFlake.id; + j["name"] = nonFlake.alias; j["location"] = nonFlake.path; std::cout << j.dump(4) << std::endl; } else { - std::cout << "name: " << nonFlake.id << "\n"; + std::cout << "name: " << nonFlake.alias << "\n"; std::cout << "Location: " << nonFlake.path << "\n"; } } @@ -116,7 +116,7 @@ struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs auto evalState = std::make_shared(searchPath, store); if (gitPath == "") gitPath = absPath("."); - updateLockFile(*evalState, gitPath, true); + updateLockFile(*evalState, gitPath); } }; @@ -135,15 +135,15 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand void run(nix::ref store) override { auto evalState = std::make_shared(searchPath, store); - nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri), true); + nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); printFlakeInfo(flake, json); } }; struct CmdFlakeAdd : MixEvalArgs, Command { - std::string flakeId; - std::string flakeUri; + FlakeAlias flakeAlias; + FlakeUri flakeUri; std::string name() override { @@ -157,7 +157,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command CmdFlakeAdd() { - expectArg("flake-id", &flakeId); + expectArg("flake-id", &flakeAlias); expectArg("flake-uri", &flakeUri); } @@ -167,15 +167,15 @@ struct CmdFlakeAdd : MixEvalArgs, Command Path userRegistryPath = getUserRegistryPath(); auto userRegistry = readRegistry(userRegistryPath); FlakeRegistry::Entry entry(newFlakeRef); - userRegistry->entries.erase(flakeId); - userRegistry->entries.insert_or_assign(flakeId, newFlakeRef); + userRegistry->entries.erase(flakeAlias); + userRegistry->entries.insert_or_assign(flakeAlias, newFlakeRef); writeRegistry(*userRegistry, userRegistryPath); } }; struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command { - std::string flakeId; + FlakeAlias flakeAlias; std::string name() override { @@ -189,21 +189,21 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command CmdFlakeRemove() { - expectArg("flake-id", &flakeId); + expectArg("flake-id", &flakeAlias); } void run() override { Path userRegistryPath = getUserRegistryPath(); auto userRegistry = readRegistry(userRegistryPath); - userRegistry->entries.erase(flakeId); + userRegistry->entries.erase(flakeAlias); writeRegistry(*userRegistry, userRegistryPath); } }; struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs { - std::string flakeId; + FlakeAlias flakeAlias; std::string name() override { @@ -217,7 +217,7 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs CmdFlakePin() { - expectArg("flake-id", &flakeId); + expectArg("flake-id", &flakeAlias); } void run(nix::ref store) override @@ -226,14 +226,14 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs Path userRegistryPath = getUserRegistryPath(); FlakeRegistry userRegistry = *readRegistry(userRegistryPath); - auto it = userRegistry.entries.find(flakeId); + auto it = userRegistry.entries.find(flakeAlias); if (it != userRegistry.entries.end()) { FlakeRef oldRef = it->second.ref; it->second.ref = getFlake(*evalState, oldRef, true).ref; // The 'ref' in 'flake' is immutable. writeRegistry(userRegistry, userRegistryPath); } else - throw Error("the flake identifier '%s' does not exist in the user registry", flakeId); + throw Error("the flake alias '%s' does not exist in the user registry", flakeAlias); } }; -- cgit v1.2.3 From 4ad4e4866891a62a6e1bb919d81e224ba0a1cf1c Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Mon, 8 Apr 2019 19:03:00 +0200 Subject: FlakeRegistry = FlakeRef -> FlakeRef --- src/nix/build.cc | 3 +-- src/nix/command.hh | 3 ++- src/nix/flake.cc | 45 ++++++++++++++++++++------------------------- src/nix/installables.cc | 1 - 4 files changed, 23 insertions(+), 29 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index a2fc56e69..5a3d9d31a 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -1,4 +1,3 @@ -#include "primops/flake.hh" #include "eval.hh" #include "command.hh" #include "common-args.hh" @@ -78,7 +77,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } - // std::string flakeUri = ""; + // FlakeUri flakeUri = ""; // if(updateLock) // for (uint i = 0; i < installables.size(); i++) // // if (auto flakeUri = installableToFlakeUri) diff --git a/src/nix/command.hh b/src/nix/command.hh index 83959bf9a..56e1e6f34 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -1,6 +1,7 @@ #pragma once #include "args.hh" +#include "primops/flake.hh" #include "common-eval-args.hh" namespace nix { @@ -46,7 +47,7 @@ struct GitRepoCommand : virtual Args struct FlakeCommand : virtual Args { - std::string flakeUri; + FlakeUri flakeUri; FlakeCommand() { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index df944a148..dbf0d3e9a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1,4 +1,3 @@ -#include "primops/flake.hh" #include "command.hh" #include "common-args.hh" #include "shared.hh" @@ -29,11 +28,9 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs stopProgressBar(); - for (auto & registry : registries) { - for (auto & entry : registry->entries) { - std::cout << entry.first << " " << entry.second.ref.to_string() << "\n"; - } - } + for (auto & registry : registries) + for (auto & entry : registry->entries) + std::cout << entry.first.to_string() << " " << entry.second.to_string() << "\n"; } }; @@ -81,7 +78,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs FlakeRef flakeRef(flakeUri); - Dependencies deps = resolveFlake(*evalState, flakeRef, true, true); + Dependencies deps = resolveFlake(*evalState, flakeRef, true); std::queue todo; todo.push(deps); @@ -135,15 +132,15 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand void run(nix::ref store) override { auto evalState = std::make_shared(searchPath, store); - nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri)); + nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri), true); printFlakeInfo(flake, json); } }; struct CmdFlakeAdd : MixEvalArgs, Command { - FlakeAlias flakeAlias; - FlakeUri flakeUri; + FlakeUri alias; + FlakeUri uri; std::string name() override { @@ -157,25 +154,24 @@ struct CmdFlakeAdd : MixEvalArgs, Command CmdFlakeAdd() { - expectArg("flake-id", &flakeAlias); - expectArg("flake-uri", &flakeUri); + expectArg("alias", &alias); + expectArg("flake-uri", &uri); } void run() override { - FlakeRef newFlakeRef(flakeUri); + FlakeRef aliasRef(alias); Path userRegistryPath = getUserRegistryPath(); auto userRegistry = readRegistry(userRegistryPath); - FlakeRegistry::Entry entry(newFlakeRef); - userRegistry->entries.erase(flakeAlias); - userRegistry->entries.insert_or_assign(flakeAlias, newFlakeRef); + userRegistry->entries.erase(aliasRef); + userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(uri)); writeRegistry(*userRegistry, userRegistryPath); } }; struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command { - FlakeAlias flakeAlias; + FlakeUri alias; std::string name() override { @@ -189,21 +185,21 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command CmdFlakeRemove() { - expectArg("flake-id", &flakeAlias); + expectArg("alias", &alias); } void run() override { Path userRegistryPath = getUserRegistryPath(); auto userRegistry = readRegistry(userRegistryPath); - userRegistry->entries.erase(flakeAlias); + userRegistry->entries.erase(FlakeRef(alias)); writeRegistry(*userRegistry, userRegistryPath); } }; struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs { - FlakeAlias flakeAlias; + FlakeUri alias; std::string name() override { @@ -217,7 +213,7 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs CmdFlakePin() { - expectArg("flake-id", &flakeAlias); + expectArg("alias", &alias); } void run(nix::ref store) override @@ -226,14 +222,13 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs Path userRegistryPath = getUserRegistryPath(); FlakeRegistry userRegistry = *readRegistry(userRegistryPath); - auto it = userRegistry.entries.find(flakeAlias); + auto it = userRegistry.entries.find(FlakeRef(alias)); if (it != userRegistry.entries.end()) { - FlakeRef oldRef = it->second.ref; - it->second.ref = getFlake(*evalState, oldRef, true).ref; + it->second = getFlake(*evalState, it->second, true).ref; // The 'ref' in 'flake' is immutable. writeRegistry(userRegistry, userRegistryPath); } else - throw Error("the flake alias '%s' does not exist in the user registry", flakeAlias); + throw Error("the flake alias '%s' does not exist in the user registry", alias); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index e792ce96d..13a68a797 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -7,7 +7,6 @@ #include "get-drvs.hh" #include "store-api.hh" #include "shared.hh" -#include "primops/flake.hh" #include -- cgit v1.2.3 From 4bf3a8226badcdc70c013dfcfa266ee72f6cb89b Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: Automated lockfile updating with `nix build` --- src/nix/build.cc | 16 ++++++++++------ src/nix/command.hh | 6 ++++++ src/nix/installables.cc | 8 ++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index da7c7f614..608946378 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -11,7 +11,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand { Path outLink = "result"; - std::optional gitRepo = std::nullopt; + bool update = true; CmdBuild() { @@ -28,9 +28,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand .set(&outLink, Path("")); mkFlag() - .longName("update-lock-file") - .description("update the lock file") - .dest(&gitRepo); + .longName("no-update") + .description("don't update the lock files") + .set(&update, false); } std::string name() override @@ -78,8 +78,12 @@ struct CmdBuild : MixDryRun, InstallablesCommand } } - if (gitRepo) - updateLockFile(*evalState, *gitRepo); + if (update) + for (auto installable : installables) { + auto flakeUri = installable->installableToFlakeUri(); + if (flakeUri) + updateLockFile(*evalState, *flakeUri); + } } }; diff --git a/src/nix/command.hh b/src/nix/command.hh index 83959bf9a..5d0c0c82c 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -2,6 +2,7 @@ #include "args.hh" #include "common-eval-args.hh" +#include namespace nix { @@ -65,6 +66,11 @@ struct Installable Buildable toBuildable(); + virtual std::optional installableToFlakeUri() + { + return std::nullopt; + } + virtual Value * toValue(EvalState & state) { throw Error("argument '%s' cannot be evaluated", what()); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index e792ce96d..43e15849b 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -176,6 +176,14 @@ struct InstallableFlake : InstallableValue state.forceValue(*v); return v; } + + std::optional installableToFlakeUri() override + { + if (std::get_if(&flakeRef.data)) + return flakeRef.to_string(); + else + return std::nullopt; + } }; // FIXME: extend -- cgit v1.2.3 From 84c12dbd7c8f2b34c46908f4a0c43cbb86023f20 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Apr 2019 13:45:51 +0200 Subject: Move --impure to MixEvalArgs --- src/nix/installables.cc | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index fc2c34861..9bc5ff41f 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -20,13 +20,6 @@ SourceExprCommand::SourceExprCommand() .label("file") .description("evaluate a set of attributes from FILE (deprecated)") .dest(&file); - - mkFlag() - .longName("impure") - .description("allow access to mutable paths and repositories") - .handler([&](std::vector ss) { - evalSettings.pureEval = false; - }); } ref SourceExprCommand::getEvalState() -- cgit v1.2.3 From 7587d62d02f216f28034f9e0938eb3236494c41b Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Tue, 16 Apr 2019 08:21:52 +0200 Subject: Fixed flake pin issues --- src/nix/flake.cc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index dbf0d3e9a..8634733d6 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -129,6 +129,8 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand return "list info about a given flake"; } + CmdFlakeInfo () { evalSettings.pureEval = false; } + void run(nix::ref store) override { auto evalState = std::make_shared(searchPath, store); @@ -156,6 +158,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command { expectArg("alias", &alias); expectArg("flake-uri", &uri); + evalSettings.pureEval = false; } void run() override @@ -186,6 +189,7 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command CmdFlakeRemove() { expectArg("alias", &alias); + evalSettings.pureEval = false; } void run() override @@ -214,6 +218,7 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs CmdFlakePin() { expectArg("alias", &alias); + evalSettings.pureEval = false; } void run(nix::ref store) override @@ -227,8 +232,16 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs it->second = getFlake(*evalState, it->second, true).ref; // The 'ref' in 'flake' is immutable. writeRegistry(userRegistry, userRegistryPath); - } else - throw Error("the flake alias '%s' does not exist in the user registry", alias); + } else { + std::shared_ptr globalReg = getGlobalRegistry(); + it = globalReg->entries.find(FlakeRef(alias)); + if (it != globalReg->entries.end()) { + FlakeRef newRef = getFlake(*evalState, it->second, true).ref; + userRegistry.entries.insert_or_assign(alias, newRef); + writeRegistry(userRegistry, userRegistryPath); + } else + throw Error("the flake alias '%s' does not exist in the user or global registry", alias); + } } }; -- cgit v1.2.3 From d8fa2fc429c2dbaffce585e08d3070f912a29bf6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 12:26:17 +0200 Subject: Add FIXME for pureEval --- src/nix/flake.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8634733d6..3b37ad7a0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -129,7 +129,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand return "list info about a given flake"; } - CmdFlakeInfo () { evalSettings.pureEval = false; } + CmdFlakeInfo () { evalSettings.pureEval = false; /* FIXME */ } void run(nix::ref store) override { @@ -158,7 +158,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command { expectArg("alias", &alias); expectArg("flake-uri", &uri); - evalSettings.pureEval = false; + evalSettings.pureEval = false; // FIXME } void run() override @@ -189,7 +189,7 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command CmdFlakeRemove() { expectArg("alias", &alias); - evalSettings.pureEval = false; + evalSettings.pureEval = false; // FIXME } void run() override @@ -218,7 +218,7 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs CmdFlakePin() { expectArg("alias", &alias); - evalSettings.pureEval = false; + evalSettings.pureEval = false; // FIXME } void run(nix::ref store) override -- cgit v1.2.3 From 035ac443544b46dc87274ed1eb1393b07db0912c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 13:56:08 +0200 Subject: Fix makeFlakeValue() --- src/nix/installables.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 9bc5ff41f..37217397a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -146,10 +146,10 @@ struct InstallableFlake : InstallableValue Value * toValue(EvalState & state) override { - auto vTemp = state.allocValue(); - auto vFlake = *makeFlakeValue(state, flakeRef, true, *vTemp); + auto vFlake = state.allocValue(); + makeFlakeValue(state, flakeRef, true, *vFlake); - auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value; + auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; state.forceValue(*vProvides); -- cgit v1.2.3 From ba66455636f40264d44c7e1fb87e13653b22042a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 14:10:05 +0200 Subject: Improve incremental build --- src/nix/build.cc | 1 + src/nix/command.hh | 3 +-- src/nix/flake.cc | 2 ++ src/nix/installables.cc | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index ef6b48969..9ef07dcdb 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -3,6 +3,7 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" +#include "primops/flake.hh" using namespace nix; diff --git a/src/nix/command.hh b/src/nix/command.hh index a5ae56fb9..5d0c0c82c 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -1,7 +1,6 @@ #pragma once #include "args.hh" -#include "primops/flake.hh" #include "common-eval-args.hh" #include @@ -48,7 +47,7 @@ struct GitRepoCommand : virtual Args struct FlakeCommand : virtual Args { - FlakeUri flakeUri; + std::string flakeUri; FlakeCommand() { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3b37ad7a0..2079b1c27 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -3,6 +3,8 @@ #include "shared.hh" #include "progress-bar.hh" #include "eval.hh" +#include "primops/flake.hh" + #include #include diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 37217397a..963321336 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -7,6 +7,7 @@ #include "get-drvs.hh" #include "store-api.hh" #include "shared.hh" +#include "primops/flake.hh" #include -- cgit v1.2.3 From 60834492aea935b8043cdc8ccbc1270edebbc20a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 15:02:02 +0200 Subject: Update lock files from InstallableFlake::toValue() This ensures that the lock file is updated *before* evaluating it, and that it gets updated for any nix command, not just 'nix build'. Also, while computing the lock file, allow arbitrary registry lookups, not just at top-level. Also, improve some error messages slightly. --- src/nix/build.cc | 14 -------------- src/nix/command.hh | 7 ++----- src/nix/flake.cc | 2 +- src/nix/installables.cc | 20 +++++++++++--------- 4 files changed, 14 insertions(+), 29 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 9ef07dcdb..d6a6a8071 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -11,8 +11,6 @@ struct CmdBuild : MixDryRun, InstallablesCommand { Path outLink = "result"; - bool update = true; - CmdBuild() { mkFlag() @@ -26,11 +24,6 @@ struct CmdBuild : MixDryRun, InstallablesCommand .longName("no-link") .description("do not create a symlink to the build result") .set(&outLink, Path("")); - - mkFlag() - .longName("no-update") - .description("don't update the lock file") - .set(&update, false); } std::string name() override @@ -77,13 +70,6 @@ struct CmdBuild : MixDryRun, InstallablesCommand store2->addPermRoot(output.second, absPath(symlink), true); } } - - if (update) - for (auto installable : installables) { - auto flakeUri = installable->installableToFlakeUri(); - if (flakeUri) - updateLockFile(*evalState, *flakeUri); - } } }; diff --git a/src/nix/command.hh b/src/nix/command.hh index 5d0c0c82c..a52fbb9ba 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -66,11 +66,6 @@ struct Installable Buildable toBuildable(); - virtual std::optional installableToFlakeUri() - { - return std::nullopt; - } - virtual Value * toValue(EvalState & state) { throw Error("argument '%s' cannot be evaluated", what()); @@ -81,6 +76,8 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs { std::optional file; + bool updateLockFile = true; + SourceExprCommand(); ref getEvalState(); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2079b1c27..1e03669c3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -80,7 +80,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs FlakeRef flakeRef(flakeUri); - Dependencies deps = resolveFlake(*evalState, flakeRef, true); + Dependencies deps = resolveFlake(*evalState, flakeRef, AllowRegistryAtTop); std::queue todo; todo.push(deps); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 963321336..9d87c70c3 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -21,6 +21,11 @@ SourceExprCommand::SourceExprCommand() .label("file") .description("evaluate a set of attributes from FILE (deprecated)") .dest(&file); + + mkFlag() + .longName("no-update") + .description("don't create/update flake lock files") + .set(&updateLockFile, false); } ref SourceExprCommand::getEvalState() @@ -147,8 +152,13 @@ struct InstallableFlake : InstallableValue Value * toValue(EvalState & state) override { + auto path = std::get_if(&flakeRef.data); + if (cmd.updateLockFile && path) { + updateLockFile(state, path->path); + } + auto vFlake = state.allocValue(); - makeFlakeValue(state, flakeRef, true, *vFlake); + makeFlakeValue(state, flakeRef, AllowRegistryAtTop, *vFlake); auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; @@ -169,14 +179,6 @@ struct InstallableFlake : InstallableValue state.forceValue(*v); return v; } - - std::optional installableToFlakeUri() override - { - if (std::get_if(&flakeRef.data)) - return flakeRef.to_string(); - else - return std::nullopt; - } }; // FIXME: extend -- cgit v1.2.3 From cfca793a20862220c53094ee63523c9a09d2c9a3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 15:06:40 +0200 Subject: Remove unneeded pureEval flags --- src/nix/flake.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 1e03669c3..93af71ac3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -131,7 +131,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand return "list info about a given flake"; } - CmdFlakeInfo () { evalSettings.pureEval = false; /* FIXME */ } + CmdFlakeInfo () { } void run(nix::ref store) override { @@ -160,7 +160,6 @@ struct CmdFlakeAdd : MixEvalArgs, Command { expectArg("alias", &alias); expectArg("flake-uri", &uri); - evalSettings.pureEval = false; // FIXME } void run() override @@ -191,7 +190,6 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command CmdFlakeRemove() { expectArg("alias", &alias); - evalSettings.pureEval = false; // FIXME } void run() override @@ -220,7 +218,6 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs CmdFlakePin() { expectArg("alias", &alias); - evalSettings.pureEval = false; // FIXME } void run(nix::ref store) override -- cgit v1.2.3 From 3d0e81051fca850fb7b46d6299a94566b8c1ab62 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 15:40:58 +0200 Subject: Fix lock file generation Before: "requires": { "nixpkgs": { "uri": "nixpkgs" } }, After: "requires": { "nixpkgs": { "uri": "github:edolstra/nixpkgs/f10e8a02eb7fa2b4a070f30cf87f4efcc7f3186d" } }, --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 93af71ac3..7006ab989 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -40,13 +40,13 @@ void printFlakeInfo(Flake & flake, bool json) { if (json) { nlohmann::json j; j["id"] = flake.id; - j["location"] = flake.path; + j["location"] = flake.sourceInfo.storePath; j["description"] = flake.description; std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; std::cout << "Description: " << flake.description << "\n"; - std::cout << "Location: " << flake.path << "\n"; + std::cout << "Location: " << flake.sourceInfo.storePath << "\n"; } } -- cgit v1.2.3 From 3c28cb1b8ff586421ed9e37cef383af0486445cb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2019 16:18:03 +0200 Subject: Improve 'nix flake info' a bit Example: $ nix flake info dwarffs ID: dwarffs URI: github:edolstra/dwarffs/a83d182fe3fe528ed6366a5cec3458bcb1a5f6e1 Description: A filesystem that fetches DWARF debug info from the Internet on demand Revision: a83d182fe3fe528ed6366a5cec3458bcb1a5f6e1 Path: /nix/store/grgd14kxxk8q4n503j87mpz48gcqpqw7-source --- src/nix/flake.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 7006ab989..4b8f1026e 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -40,13 +40,19 @@ void printFlakeInfo(Flake & flake, bool json) { if (json) { nlohmann::json j; j["id"] = flake.id; - j["location"] = flake.sourceInfo.storePath; + j["uri"] = flake.sourceInfo.flakeRef.to_string(); j["description"] = flake.description; + if (flake.sourceInfo.rev) + j["revision"] = flake.sourceInfo.rev->to_string(Base16, false); + j["path"] = flake.sourceInfo.storePath; std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; + std::cout << "URI: " << flake.sourceInfo.flakeRef.to_string() << "\n"; std::cout << "Description: " << flake.description << "\n"; - std::cout << "Location: " << flake.sourceInfo.storePath << "\n"; + if (flake.sourceInfo.rev) + std::cout << "Revision: " << flake.sourceInfo.rev->to_string(Base16, false) << "\n"; + std::cout << "Path: " << flake.sourceInfo.storePath << "\n"; } } -- cgit v1.2.3 From b42ba08fc8a291c549c1f9f92457d72639fac995 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: Add command `flake clone` --- src/nix/flake.cc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 4b8f1026e..35324295d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -280,6 +280,34 @@ struct CmdFlakeInit : virtual Args, Command } }; +struct CmdFlakeClone : StoreCommand, FlakeCommand, MixEvalArgs +{ + Path endDirectory = ""; + + std::string name() override + { + return "clone"; + } + + std::string description() override + { + return "clone flake repository"; + } + + CmdFlakeClone() + { + expectArg("end-dir", &endDirectory, true); + } + + void run(nix::ref store) override + { + auto evalState = std::make_shared(searchPath, store); + + Registries registries = evalState->getFlakeRegistries(); + gitCloneFlake(flakeUri, *evalState, registries, endDirectory); + } +}; + struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() @@ -291,6 +319,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command , make_ref() , make_ref() , make_ref() + , make_ref() }) { } -- cgit v1.2.3 From 160ce18a0e9f569f94e6b0cb8e47bd4008a9fea2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2019 11:43:56 +0200 Subject: Improve missing flake.nix error message --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 4b8f1026e..34d67ee58 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -32,7 +32,7 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs for (auto & registry : registries) for (auto & entry : registry->entries) - std::cout << entry.first.to_string() << " " << entry.second.to_string() << "\n"; + std::cout << entry.first << " " << entry.second << "\n"; } }; @@ -48,7 +48,7 @@ void printFlakeInfo(Flake & flake, bool json) { std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; - std::cout << "URI: " << flake.sourceInfo.flakeRef.to_string() << "\n"; + std::cout << "URI: " << flake.sourceInfo.flakeRef << "\n"; std::cout << "Description: " << flake.description << "\n"; if (flake.sourceInfo.rev) std::cout << "Revision: " << flake.sourceInfo.rev->to_string(Base16, false) << "\n"; -- cgit v1.2.3 From 50ec2bed9edd234eabbd4a3920052ca2f94bca52 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2019 14:19:46 +0200 Subject: nix flake info: Show revcount --- src/nix/flake.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 34d67ee58..eec280584 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -44,6 +44,8 @@ void printFlakeInfo(Flake & flake, bool json) { j["description"] = flake.description; if (flake.sourceInfo.rev) j["revision"] = flake.sourceInfo.rev->to_string(Base16, false); + if (flake.sourceInfo.revCount) + j["revCount"] = *flake.sourceInfo.revCount; j["path"] = flake.sourceInfo.storePath; std::cout << j.dump(4) << std::endl; } else { @@ -52,6 +54,8 @@ void printFlakeInfo(Flake & flake, bool json) { std::cout << "Description: " << flake.description << "\n"; if (flake.sourceInfo.rev) std::cout << "Revision: " << flake.sourceInfo.rev->to_string(Base16, false) << "\n"; + if (flake.sourceInfo.revCount) + std::cout << "Revcount: " << *flake.sourceInfo.revCount << "\n"; std::cout << "Path: " << flake.sourceInfo.storePath << "\n"; } } -- cgit v1.2.3 From e51abb6631ff0f5fc52523ea1819333cb587170c Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Fri, 19 Apr 2019 14:23:35 +0200 Subject: Changed some names --- src/nix/flake.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 34d67ee58..07e9e313a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -86,20 +86,20 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs FlakeRef flakeRef(flakeUri); - Dependencies deps = resolveFlake(*evalState, flakeRef, AllowRegistryAtTop); + ResolvedFlake resFlake = resolveFlake(*evalState, flakeRef, AllowRegistryAtTop); - std::queue todo; - todo.push(deps); + std::queue todo; + todo.push(resFlake); while (!todo.empty()) { - deps = todo.front(); + resFlake = todo.front(); todo.pop(); - for (auto & nonFlake : deps.nonFlakeDeps) + for (NonFlake & nonFlake : resFlake.nonFlakeDeps) printNonFlakeInfo(nonFlake, json); - for (auto & newDeps : deps.flakeDeps) - todo.push(newDeps); + for (ResolvedFlake & newResFlake : resFlake.flakeDeps) + todo.push(newResFlake); } } }; -- cgit v1.2.3 From 3392f1b77869269580b58e4931b7a79f44799ce0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2019 14:41:06 +0200 Subject: Shut up clang warning --- src/nix/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index 01b0866f2..3ec5f48d5 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -57,7 +57,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs "--help-config' for a list of configuration settings.\n"; } - void printHelp(const string & programName, std::ostream & out) + void printHelp(const string & programName, std::ostream & out) override { MultiCommand::printHelp(programName, out); -- cgit v1.2.3 From cc51e37ad09a1dac0a58c543ffe275ddc18819aa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2019 16:07:37 +0200 Subject: Use "." as the default installable This makes e.g. "nix build" do something more or less reasonable (namely, build the default package of the flake in the current directory). --- src/nix/build.cc | 1 - src/nix/command.hh | 8 ++++++-- src/nix/installables.cc | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index d6a6a8071..07b6b2f02 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -3,7 +3,6 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" -#include "primops/flake.hh" using namespace nix; diff --git a/src/nix/command.hh b/src/nix/command.hh index a52fbb9ba..6d43261ac 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -108,6 +108,8 @@ struct InstallablesCommand : virtual Args, SourceExprCommand void prepare() override; + virtual bool useDefaultInstallables() { return true; } + private: std::vector _installables; @@ -119,14 +121,14 @@ struct InstallableCommand : virtual Args, SourceExprCommand InstallableCommand() { - expectArg("installable", &_installable); + expectArg("installable", &_installable, true); } void prepare() override; private: - std::string _installable; + std::string _installable{"."}; }; /* A command that operates on zero or more store paths. */ @@ -146,6 +148,8 @@ public: virtual void run(ref store, Paths storePaths) = 0; void run(ref store) override; + + bool useDefaultInstallables() override { return !all; } }; /* A command that operates on exactly one store path. */ diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 9d87c70c3..c3ca87aa7 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -334,6 +334,10 @@ PathSet toDerivations(ref store, void InstallablesCommand::prepare() { + if (_installables.empty() && !file && useDefaultInstallables()) + // FIXME: commands like "nix install" should not have a + // default, probably. + _installables.push_back("."); installables = parseInstallables(getStore(), _installables); } -- cgit v1.2.3 From d867e1804a39174f21d50095d9e6bc4a87190e16 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Wed, 17 Apr 2019 14:03:04 +0200 Subject: Fix printing FlakeList --- src/nix/flake.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6459df019..a25493f79 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -30,9 +30,14 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs stopProgressBar(); - for (auto & registry : registries) - for (auto & entry : registry->entries) - std::cout << entry.first << " " << entry.second << "\n"; + for (auto & entry : registries[0]->entries) + std::cout << entry.first.to_string() << " flags " << entry.second.to_string() << "\n"; + + for (auto & entry : registries[1]->entries) + std::cout << entry.first.to_string() << " user " << entry.second.to_string() << "\n"; + + for (auto & entry : registries[2]->entries) + std::cout << entry.first.to_string() << " global " << entry.second.to_string() << "\n"; } }; @@ -146,7 +151,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand void run(nix::ref store) override { auto evalState = std::make_shared(searchPath, store); - nix::Flake flake = nix::getFlake(*evalState, FlakeRef(flakeUri), true); + Flake flake = getFlake(*evalState, FlakeRef(flakeUri), true); printFlakeInfo(flake, json); } }; -- cgit v1.2.3 From 35d1c95f7f6fc744a829bb25b75c15cc2da31e99 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Tue, 30 Apr 2019 11:03:31 +0200 Subject: Fix flag registry order --- src/nix/flake.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a25493f79..a80338fd3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -30,13 +30,13 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs stopProgressBar(); - for (auto & entry : registries[0]->entries) + for (auto & entry : registries[FLAG_REGISTRY]->entries) std::cout << entry.first.to_string() << " flags " << entry.second.to_string() << "\n"; - for (auto & entry : registries[1]->entries) + for (auto & entry : registries[USER_REGISTRY]->entries) std::cout << entry.first.to_string() << " user " << entry.second.to_string() << "\n"; - for (auto & entry : registries[2]->entries) + for (auto & entry : registries[GLOBAL_REGISTRY]->entries) std::cout << entry.first.to_string() << " global " << entry.second.to_string() << "\n"; } }; -- cgit v1.2.3 From 24b35bf9e7219feba9399466c41801f0ace3ef74 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Thu, 21 Mar 2019 09:30:16 +0100 Subject: Fixed issue #13 --- src/nix/build.cc | 2 +- src/nix/flake.cc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 07b6b2f02..c08ec0e62 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -54,7 +54,7 @@ struct CmdBuild : MixDryRun, InstallablesCommand auto buildables = build(store, dryRun ? DryRun : Build, installables); auto evalState = std::make_shared(searchPath, store); - + evalState->addRegistryOverrides(registryOverrides); if (dryRun) return; for (size_t i = 0; i < buildables.size(); ++i) { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a25493f79..00c1c384c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -92,6 +92,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs void run(nix::ref store) override { auto evalState = std::make_shared(searchPath, store); + evalState->addRegistryOverrides(registryOverrides); FlakeRef flakeRef(flakeUri); -- cgit v1.2.3 From 2b8c63f303de7d1da133cb3e9a00d509dca40f57 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 May 2019 20:22:14 +0200 Subject: Add 'nix dev-shell' and 'nix print-dev-env' command 'nix dev-shell' is intended to replace nix-shell. It supports flakes, e.g. $ nix dev-shell nixpkgs:hello starts a bash shell providing an environment for building 'hello'. Like Lorri (and unlike nix-shell), it computes the build environment by building a modified top-level derivation that writes the environment after running $stdenv/setup to $out and exits. This provides some caching, so it's faster than nix-shell in some cases (especially for packages with lots of dependencies, where the setup script takes a long time). There also is a command 'nix print-dev-env' that prints out shell code for setting up the build environment in an existing shell, e.g. $ . <(nix print-dev-env nixpkgs:hello) https://github.com/tweag/nix/issues/21 --- src/nix/shell.cc | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 src/nix/shell.cc (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc new file mode 100644 index 000000000..14d88faeb --- /dev/null +++ b/src/nix/shell.cc @@ -0,0 +1,276 @@ +#include "eval.hh" +#include "command.hh" +#include "common-args.hh" +#include "shared.hh" +#include "store-api.hh" +#include "derivations.hh" +#include "affinity.hh" +#include "progress-bar.hh" + +using namespace nix; + +struct BuildEnvironment +{ + // FIXME: figure out which vars should be exported. + std::map env; + std::map functions; +}; + +BuildEnvironment readEnvironment(const Path & path) +{ + BuildEnvironment res; + + auto lines = tokenizeString(readFile(path), "\n"); + + auto getLine = + [&]() { + if (lines.empty()) + throw Error("shell environment '%s' ends unexpectedly", path); + auto line = lines.front(); + lines.pop_front(); + return line; + }; + + while (!lines.empty()) { + auto line = getLine(); + + auto eq = line.find('='); + if (eq != std::string::npos) { + std::string name(line, 0, eq); + std::string value(line, eq + 1); + // FIXME: parse arrays + res.env.insert({name, value}); + } + + else if (hasSuffix(line, " () ")) { + std::string name(line, 0, line.size() - 4); + // FIXME: validate name + auto l = getLine(); + if (l != "{ ") throw Error("shell environment '%s' has unexpected line '%s'", path, l); + std::string body; + while ((l = getLine()) != "}") { + body += l; + body += '\n'; + } + res.functions.insert({name, body}); + } + + else throw Error("shell environment '%s' has unexpected line '%s'", path, line); + } + + return res; +} + +/* Given an existing derivation, return the shell environment as + initialised by stdenv's setup script. We do this by building a + modified derivation with the same dependencies and nearly the same + initial environment variables, that just writes the resulting + environment to a file and exits. */ +BuildEnvironment getDerivationEnvironment(ref store, Derivation drv) +{ + auto builder = baseNameOf(drv.builder); + if (builder != "bash") + throw Error("'nix shell' only works on derivations that use 'bash' as their builder"); + + drv.args = {"-c", "set -e; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; + + /* Remove derivation checks. */ + drv.env.erase("allowedReferences"); + drv.env.erase("allowedRequisites"); + drv.env.erase("disallowedReferences"); + drv.env.erase("disallowedRequisites"); + + // FIXME: handle structured attrs + + /* Rehash and write the derivation. FIXME: would be nice to use + 'buildDerivation', but that's privileged. */ + auto drvName = drv.env["name"] + "-env"; + for (auto & output : drv.outputs) + drv.env.erase(output.first); + drv.env["out"] = ""; + drv.env["outputs"] = "out"; + drv.outputs["out"] = DerivationOutput("", "", ""); + Hash h = hashDerivationModulo(*store, drv); + Path shellOutPath = store->makeOutputPath("out", h, drvName); + drv.outputs["out"].path = shellOutPath; + drv.env["out"] = shellOutPath; + Path shellDrvPath2 = writeDerivation(store, drv, drvName); + + /* Build the derivation. */ + store->buildPaths({shellDrvPath2}); + + assert(store->isValidPath(shellOutPath)); + + return readEnvironment(shellOutPath); +} + +struct Common : InstallableCommand +{ + /* + std::set keepVars{ + "DISPLAY", + "HOME", + "IN_NIX_SHELL", + "LOGNAME", + "NIX_BUILD_SHELL", + "PAGER", + "PATH", + "TERM", + "TZ", + "USER", + }; + */ + + std::set ignoreVars{ + "BASHOPTS", + "EUID", + "NIX_BUILD_TOP", + "PPID", + "PWD", + "SHELLOPTS", + "SHLVL", + "TEMP", + "TEMPDIR", + "TMP", + "TMPDIR", + "UID", + }; + + void makeRcScript(const BuildEnvironment & buildEnvironment, std::ostream & out) + { + for (auto & i : buildEnvironment.env) { + // FIXME: shellEscape + // FIXME: figure out what to export + // FIXME: handle arrays + if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) + out << fmt("export %s=%s\n", i.first, i.second); + } + + for (auto & i : buildEnvironment.functions) { + out << fmt("%s () {\n%s\n}\n", i.first, i.second); + } + + // FIXME: set outputs + + out << "export NIX_BUILD_TOP=\"$(mktemp -d --tmpdir nix-shell.XXXXXX)\"\n"; + for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"}) + out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i); + } +}; + +std::pair createTempFile(const Path & prefix = "nix") +{ + Path tmpl(getEnv("TMPDIR", "/tmp") + "/" + prefix + ".XXXXXX"); + // Strictly speaking, this is UB, but who cares... + AutoCloseFD fd(mkstemp((char *) tmpl.c_str())); + if (!fd) + throw SysError("creating temporary file '%s'", tmpl); + return {std::move(fd), tmpl}; +} + +struct CmdDevShell : Common +{ + + std::string name() override + { + return "dev-shell"; + } + + std::string description() override + { + return "run a bash shell that provides the build environment of a derivation"; + } + + Examples examples() override + { + return { + Example{ + "To get the build environment of GNU hello:", + "nix dev-shell nixpkgs:hello" + }, + Example{ + "To get the build environment of the default package of flake in the current directory:", + "nix dev-shell" + }, + }; + } + + void run(ref store) override + { + auto drvs = toDerivations(store, {installable}); + + if (drvs.size() != 1) + throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", + installable->what(), drvs.size()); + + auto & drvPath = *drvs.begin(); + + auto buildEnvironment = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + + auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); + + std::ostringstream ss; + makeRcScript(buildEnvironment, ss); + + ss << fmt("rm -f '%s'\n", rcFilePath); + + writeFull(rcFileFd.get(), ss.str()); + + stopProgressBar(); + + auto shell = getEnv("SHELL", "bash"); + + auto args = Strings{baseNameOf(shell), "--rcfile", rcFilePath}; + + restoreAffinity(); + restoreSignals(); + + execvp(shell.c_str(), stringsToCharPtrs(args).data()); + + throw SysError("executing shell '%s'", shell); + } +}; + +struct CmdPrintDevEnv : Common +{ + + std::string name() override + { + return "print-dev-env"; + } + + std::string description() override + { + return "print shell code that can be sourced by bash to reproduce the build environment of a derivation"; + } + + Examples examples() override + { + return { + Example{ + "To apply the build environment of GNU hello to the current shell:", + ". <(nix print-dev-env nixpkgs:hello)" + }, + }; + } + + void run(ref store) override + { + auto drvs = toDerivations(store, {installable}); + + if (drvs.size() != 1) + throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", + installable->what(), drvs.size()); + + auto & drvPath = *drvs.begin(); + + auto buildEnvironment = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + + stopProgressBar(); + + makeRcScript(buildEnvironment, std::cout); + } +}; + +static RegisterCommand r1(make_ref()); +static RegisterCommand r2(make_ref()); -- cgit v1.2.3 From 2919c496ea10a99d08baffbef485cfb719233b9f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 May 2019 21:10:13 +0200 Subject: nix dev-shell: Use 'provides.devShell' by default Thus $ nix dev-shell will now build the 'provides.devShell' attribute from the flake in the current directory. If it doesn't exist, it falls back to 'provides.defaultPackage'. --- src/nix/command.hh | 5 +++++ src/nix/installables.cc | 42 +++++++++++++++++++++++++++++++----------- src/nix/shell.cc | 6 ++++++ 3 files changed, 42 insertions(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 6d43261ac..640c6cd16 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -88,6 +88,11 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs std::shared_ptr parseInstallable( ref store, const std::string & installable); + virtual Strings getDefaultFlakeAttrPaths() + { + return {"defaultPackage"}; + } + private: std::shared_ptr evalState; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index c3ca87aa7..db67952e1 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -142,13 +142,18 @@ struct InstallableAttrPath : InstallableValue struct InstallableFlake : InstallableValue { FlakeRef flakeRef; - std::string attrPath; + Strings attrPaths; + bool searchPackages = false; + + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, Strings attrPaths) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(std::move(attrPaths)) + { } - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, const std::string & attrPath) - : InstallableValue(cmd), flakeRef(flakeRef), attrPath(attrPath) + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, std::string attrPath) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, searchPackages(true) { } - std::string what() override { return flakeRef.to_string() + ":" + attrPath; } + std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } Value * toValue(EvalState & state) override { @@ -166,18 +171,31 @@ struct InstallableFlake : InstallableValue auto emptyArgs = state.allocBindings(0); - if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) { + // As a convenience, look for the attribute in + // 'provides.packages'. + if (searchPackages) { + if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) { + try { + auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); + state.forceValue(*v); + return v; + } catch (AttrPathNotFound & e) { + } + } + } + + // Otherwise, look for it in 'provides'. + for (auto & attrPath : attrPaths) { try { - auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *aPackages->value); + auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vProvides); state.forceValue(*v); return v; } catch (AttrPathNotFound & e) { } } - auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vProvides); - state.forceValue(*v); - return v; + throw Error("flake '%s' does not provide attribute %s", + flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); } }; @@ -216,7 +234,8 @@ std::vector> SourceExprCommand::parseInstallables( else if (hasPrefix(s, "nixpkgs.")) { bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); - result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), std::string(s, 8))); + result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), + Strings{"packages." + std::string(s, 8)})); } else if ((colon = s.rfind(':')) != std::string::npos) { @@ -233,7 +252,8 @@ std::vector> SourceExprCommand::parseInstallables( if (storePath != "") result.push_back(std::make_shared(storePath)); else - result.push_back(std::make_shared(*this, FlakeRef(s, true), "defaultPackage")); + result.push_back(std::make_shared(*this, FlakeRef(s, true), + getDefaultFlakeAttrPaths())); } else diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 14d88faeb..2e17111d6 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -125,6 +125,7 @@ struct Common : InstallableCommand "BASHOPTS", "EUID", "NIX_BUILD_TOP", + "NIX_ENFORCE_PURITY", "PPID", "PWD", "SHELLOPTS", @@ -156,6 +157,11 @@ struct Common : InstallableCommand for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"}) out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i); } + + Strings getDefaultFlakeAttrPaths() override + { + return {"devShell", "defaultPackage"}; + } }; std::pair createTempFile(const Path & prefix = "nix") -- cgit v1.2.3 From dea18ff99913349ef0f54b80d45c8dfdc8b31f65 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 May 2019 21:13:19 +0200 Subject: nix dev-shell: Execute shellHook --- src/nix/shell.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 2e17111d6..d3ecf8de4 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -156,6 +156,8 @@ struct Common : InstallableCommand out << "export NIX_BUILD_TOP=\"$(mktemp -d --tmpdir nix-shell.XXXXXX)\"\n"; for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"}) out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i); + + out << "eval \"$shellHook\"\n"; } Strings getDefaultFlakeAttrPaths() override -- cgit v1.2.3 From 8ec77614f63e14d1869734b0d21a646667bbf88b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 May 2019 21:28:41 +0200 Subject: Move createTempFile to libutil --- src/nix/shell.cc | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index d3ecf8de4..95028f10e 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -166,16 +166,6 @@ struct Common : InstallableCommand } }; -std::pair createTempFile(const Path & prefix = "nix") -{ - Path tmpl(getEnv("TMPDIR", "/tmp") + "/" + prefix + ".XXXXXX"); - // Strictly speaking, this is UB, but who cares... - AutoCloseFD fd(mkstemp((char *) tmpl.c_str())); - if (!fd) - throw SysError("creating temporary file '%s'", tmpl); - return {std::move(fd), tmpl}; -} - struct CmdDevShell : Common { -- cgit v1.2.3 From 7ba0f98e644c31ce0c16db10aa87f896937e0ddf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 May 2019 21:28:52 +0200 Subject: nix dev-shell: Less purity --- src/nix/shell.cc | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 95028f10e..0813d122c 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -124,6 +124,7 @@ struct Common : InstallableCommand std::set ignoreVars{ "BASHOPTS", "EUID", + "HOME", // FIXME: don't ignore in pure mode? "NIX_BUILD_TOP", "NIX_ENFORCE_PURITY", "PPID", @@ -134,11 +135,15 @@ struct Common : InstallableCommand "TEMPDIR", "TMP", "TMPDIR", + "TZ", "UID", }; void makeRcScript(const BuildEnvironment & buildEnvironment, std::ostream & out) { + out << "export IN_NIX_SHELL=1\n"; + out << "nix_saved_PATH=\"$PATH\"\n"; + for (auto & i : buildEnvironment.env) { // FIXME: shellEscape // FIXME: figure out what to export @@ -147,6 +152,8 @@ struct Common : InstallableCommand out << fmt("export %s=%s\n", i.first, i.second); } + out << "PATH=\"$PATH:$nix_saved_PATH\"\n"; + for (auto & i : buildEnvironment.functions) { out << fmt("%s () {\n%s\n}\n", i.first, i.second); } -- cgit v1.2.3 From 201f92e02c3a5d58d27ab9a7aca3b76d56d18264 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Wed, 1 May 2019 11:38:48 +0200 Subject: Fixed Flake data type and flake fetching --- src/nix/flake.cc | 55 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 19 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e38c4db0b..0af368570 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -45,35 +45,53 @@ void printFlakeInfo(Flake & flake, bool json) { if (json) { nlohmann::json j; j["id"] = flake.id; - j["uri"] = flake.sourceInfo.flakeRef.to_string(); + j["uri"] = flake.resolvedRef.to_string(); j["description"] = flake.description; - if (flake.sourceInfo.rev) - j["revision"] = flake.sourceInfo.rev->to_string(Base16, false); - if (flake.sourceInfo.revCount) - j["revCount"] = *flake.sourceInfo.revCount; - j["path"] = flake.sourceInfo.storePath; + if (flake.resolvedRef.ref) + j["branch"] = *flake.resolvedRef.ref; + if (flake.resolvedRef.rev) + j["revision"] = flake.resolvedRef.rev->to_string(Base16, false); + if (flake.revCount) + j["revCount"] = *flake.revCount; + j["path"] = flake.storePath; std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; - std::cout << "URI: " << flake.sourceInfo.flakeRef << "\n"; + std::cout << "URI: " << flake.resolvedRef.to_string() << "\n"; std::cout << "Description: " << flake.description << "\n"; - if (flake.sourceInfo.rev) - std::cout << "Revision: " << flake.sourceInfo.rev->to_string(Base16, false) << "\n"; - if (flake.sourceInfo.revCount) - std::cout << "Revcount: " << *flake.sourceInfo.revCount << "\n"; - std::cout << "Path: " << flake.sourceInfo.storePath << "\n"; + if (flake.resolvedRef.ref) + std::cout << "Branch: " << *flake.resolvedRef.ref; + if (flake.resolvedRef.rev) + std::cout << "Revision: " << flake.resolvedRef.rev->to_string(Base16, false) << "\n"; + if (flake.revCount) + std::cout << "Revcount: " << *flake.revCount << "\n"; + std::cout << "Path: " << flake.storePath << "\n"; } } void printNonFlakeInfo(NonFlake & nonFlake, bool json) { if (json) { nlohmann::json j; - j["name"] = nonFlake.alias; - j["location"] = nonFlake.path; + j["id"] = nonFlake.alias; + j["uri"] = nonFlake.resolvedRef.to_string(); + if (nonFlake.resolvedRef.ref) + j["branch"] = *nonFlake.resolvedRef.ref; + if (nonFlake.resolvedRef.rev) + j["revision"] = nonFlake.resolvedRef.rev->to_string(Base16, false); + if (nonFlake.revCount) + j["revCount"] = *nonFlake.revCount; + j["path"] = nonFlake.storePath; std::cout << j.dump(4) << std::endl; } else { - std::cout << "name: " << nonFlake.alias << "\n"; - std::cout << "Location: " << nonFlake.path << "\n"; + std::cout << "ID: " << nonFlake.alias << "\n"; + std::cout << "URI: " << nonFlake.resolvedRef.to_string() << "\n"; + if (nonFlake.resolvedRef.ref) + std::cout << "Branch: " << *nonFlake.resolvedRef.ref; + if (nonFlake.resolvedRef.rev) + std::cout << "Revision: " << nonFlake.resolvedRef.rev->to_string(Base16, false) << "\n"; + if (nonFlake.revCount) + std::cout << "Revcount: " << *nonFlake.revCount << "\n"; + std::cout << "Path: " << nonFlake.storePath << "\n"; } } @@ -244,14 +262,13 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs FlakeRegistry userRegistry = *readRegistry(userRegistryPath); auto it = userRegistry.entries.find(FlakeRef(alias)); if (it != userRegistry.entries.end()) { - it->second = getFlake(*evalState, it->second, true).ref; - // The 'ref' in 'flake' is immutable. + it->second = getFlake(*evalState, it->second, true).resolvedRef; writeRegistry(userRegistry, userRegistryPath); } else { std::shared_ptr globalReg = getGlobalRegistry(); it = globalReg->entries.find(FlakeRef(alias)); if (it != globalReg->entries.end()) { - FlakeRef newRef = getFlake(*evalState, it->second, true).ref; + FlakeRef newRef = getFlake(*evalState, it->second, true).resolvedRef; userRegistry.entries.insert_or_assign(alias, newRef); writeRegistry(userRegistry, userRegistryPath); } else -- cgit v1.2.3 From cb5ebc5c1120da8900ef074ed300685cc3177ae6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 8 May 2019 17:07:35 +0200 Subject: nix dev-shell: Keep $TERM --- src/nix/shell.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 0813d122c..8b9106171 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -133,6 +133,7 @@ struct Common : InstallableCommand "SHLVL", "TEMP", "TEMPDIR", + "TERM", "TMP", "TMPDIR", "TZ", -- cgit v1.2.3 From 156e3a9daacce48f2f53939dee92dd063dfc507a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 11 May 2019 01:50:28 +0200 Subject: nix dev-shell: Ignore SSL_CERT_FILE --- src/nix/shell.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 8b9106171..2ccad930f 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -131,6 +131,7 @@ struct Common : InstallableCommand "PWD", "SHELLOPTS", "SHLVL", + "SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt "TEMP", "TEMPDIR", "TERM", -- cgit v1.2.3 From d9ad3723d59d9df2fb3c89335b5d9239f1860ec9 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Wed, 1 May 2019 11:38:48 +0200 Subject: Fixed issue 65 lockfile updating --- src/nix/command.hh | 4 ++-- src/nix/flake.cc | 9 +++++---- src/nix/installables.cc | 16 +++++++--------- 3 files changed, 14 insertions(+), 15 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 640c6cd16..32a5047a8 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -76,10 +76,10 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs { std::optional file; - bool updateLockFile = true; - SourceExprCommand(); + bool recreateLockFile = false; + ref getEvalState(); std::vector> parseInstallables( diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0af368570..d2cdf6fc9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -114,7 +114,8 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs FlakeRef flakeRef(flakeUri); - ResolvedFlake resFlake = resolveFlake(*evalState, flakeRef, AllowRegistryAtTop); + bool recreateLockFile = false; + ResolvedFlake resFlake = resolveFlake(*evalState, flakeRef, AllowRegistryAtTop, recreateLockFile); std::queue todo; todo.push(resFlake); @@ -132,7 +133,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs } }; -struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs +struct CmdFlakeUpdate : StoreCommand, FlakeCommand, MixEvalArgs { std::string name() override { @@ -148,8 +149,8 @@ struct CmdFlakeUpdate : StoreCommand, GitRepoCommand, MixEvalArgs { auto evalState = std::make_shared(searchPath, store); - if (gitPath == "") gitPath = absPath("."); - updateLockFile(*evalState, gitPath); + bool recreateLockFile = true; + updateLockFile(*evalState, flakeUri, recreateLockFile); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index db67952e1..6d784002a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -23,9 +23,9 @@ SourceExprCommand::SourceExprCommand() .dest(&file); mkFlag() - .longName("no-update") - .description("don't create/update flake lock files") - .set(&updateLockFile, false); + .longName("recreate-lock-file") + .description("recreate lock file from scratch") + .set(&recreateLockFile, true); } ref SourceExprCommand::getEvalState() @@ -157,13 +157,11 @@ struct InstallableFlake : InstallableValue Value * toValue(EvalState & state) override { - auto path = std::get_if(&flakeRef.data); - if (cmd.updateLockFile && path) { - updateLockFile(state, path->path); - } - auto vFlake = state.allocValue(); - makeFlakeValue(state, flakeRef, AllowRegistryAtTop, *vFlake); + if (std::get_if(&flakeRef.data)) + updateLockFile(state, flakeRef.to_string(), cmd.recreateLockFile); + + makeFlakeValue(state, flakeRef, AllowRegistryAtTop, *vFlake, cmd.recreateLockFile); auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; -- cgit v1.2.3 From 98f20dee41e9d4dccb5a6bbbd956ab856c5f7929 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Wed, 1 May 2019 11:38:48 +0200 Subject: Give errors in resolveFlake If DontUpdate but the lockfile isn't correct --- src/nix/flake.cc | 3 +-- src/nix/installables.cc | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d2cdf6fc9..fc0fc76b4 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -114,8 +114,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs FlakeRef flakeRef(flakeUri); - bool recreateLockFile = false; - ResolvedFlake resFlake = resolveFlake(*evalState, flakeRef, AllowRegistryAtTop, recreateLockFile); + ResolvedFlake resFlake = resolveFlake(*evalState, flakeRef, UpdateLockFile); std::queue todo; todo.push(resFlake); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 6d784002a..25f3f4f9d 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -161,7 +161,7 @@ struct InstallableFlake : InstallableValue if (std::get_if(&flakeRef.data)) updateLockFile(state, flakeRef.to_string(), cmd.recreateLockFile); - makeFlakeValue(state, flakeRef, AllowRegistryAtTop, *vFlake, cmd.recreateLockFile); + makeFlakeValue(state, flakeRef, cmd.recreateLockFile ? RecreateLockFile : UpdateLockFile, *vFlake); auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; -- cgit v1.2.3 From ef6ae61503bed7afa73a45ca6a4fb3597a9e514d Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Tue, 14 May 2019 11:34:45 +0200 Subject: Lockfile handling in `resolveFlake` is fixed --- src/nix/command.hh | 4 ++++ src/nix/flake.cc | 4 ++-- src/nix/installables.cc | 18 +++++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 32a5047a8..30d869b19 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -80,6 +80,10 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs bool recreateLockFile = false; + bool saveLockFile = true; + + bool noRegistries = false; + ref getEvalState(); std::vector> parseInstallables( diff --git a/src/nix/flake.cc b/src/nix/flake.cc index fc0fc76b4..bfb3178ad 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -126,8 +126,8 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs for (NonFlake & nonFlake : resFlake.nonFlakeDeps) printNonFlakeInfo(nonFlake, json); - for (ResolvedFlake & newResFlake : resFlake.flakeDeps) - todo.push(newResFlake); + for (auto info : resFlake.flakeDeps) + todo.push(info.second); } } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 25f3f4f9d..a2a55d949 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -26,6 +26,16 @@ SourceExprCommand::SourceExprCommand() .longName("recreate-lock-file") .description("recreate lock file from scratch") .set(&recreateLockFile, true); + + mkFlag() + .longName("dont-save-lock-file") + .description("save the newly generated lock file") + .set(&saveLockFile, false); + + mkFlag() + .longName("no-registries") + .description("don't use flake registries") + .set(&noRegistries, true); } ref SourceExprCommand::getEvalState() @@ -158,10 +168,12 @@ struct InstallableFlake : InstallableValue Value * toValue(EvalState & state) override { auto vFlake = state.allocValue(); - if (std::get_if(&flakeRef.data)) - updateLockFile(state, flakeRef.to_string(), cmd.recreateLockFile); - makeFlakeValue(state, flakeRef, cmd.recreateLockFile ? RecreateLockFile : UpdateLockFile, *vFlake); + HandleLockFile handle = cmd.noRegistries ? AllPure : + cmd.recreateLockFile ? + (cmd.saveLockFile ? RecreateLockFile : UseNewLockFile) + : (cmd.saveLockFile ? UpdateLockFile : UseUpdatedLockFile); + makeFlakeValue(state, flakeRef, handle, *vFlake); auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; -- cgit v1.2.3 From 2468672e305faf672c3901c1a9605ca1cb175908 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 May 2019 22:48:16 +0200 Subject: Improve FlakeCommand It now handles commonality like calling getFlake() and resolving relative local flake refs. Fixes #2822. --- src/nix/command.hh | 37 +++++++------------------- src/nix/flake.cc | 69 +++++++++++++++++++++++++++++++++---------------- src/nix/installables.cc | 2 +- 3 files changed, 58 insertions(+), 50 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 30d869b19..423ac5baa 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -35,26 +35,6 @@ struct Buildable typedef std::vector Buildables; -struct GitRepoCommand : virtual Args -{ - std::string gitPath = absPath("."); - - GitRepoCommand () - { - expectArg("git-path", &gitPath, true); - } -}; - -struct FlakeCommand : virtual Args -{ - std::string flakeUri; - - FlakeCommand() - { - expectArg("flake-uri", &flakeUri); - } -}; - struct Installable { virtual std::string what() = 0; @@ -72,7 +52,16 @@ struct Installable } }; -struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs +struct EvalCommand : virtual StoreCommand, MixEvalArgs +{ + ref getEvalState(); + +private: + + std::shared_ptr evalState; +}; + +struct SourceExprCommand : virtual Args, EvalCommand { std::optional file; @@ -84,8 +73,6 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs bool noRegistries = false; - ref getEvalState(); - std::vector> parseInstallables( ref store, std::vector ss); @@ -96,10 +83,6 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs { return {"defaultPackage"}; } - -private: - - std::shared_ptr evalState; }; enum RealiseMode { Build, NoBuild, DryRun }; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index bfb3178ad..bc2f1cb5b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -10,7 +10,33 @@ using namespace nix; -struct CmdFlakeList : StoreCommand, MixEvalArgs +class FlakeCommand : virtual Args, public EvalCommand +{ + std::string flakeUri = "."; + +public: + + FlakeCommand() + { + expectArg("flake-uri", &flakeUri, true); + } + + FlakeRef getFlakeRef() + { + if (flakeUri.find('/') != std::string::npos || flakeUri == ".") + return FlakeRef(flakeUri, true); + else + return FlakeRef(flakeUri); + } + + Flake getFlake() + { + auto evalState = getEvalState(); + return nix::getFlake(*evalState, getFlakeRef(), true); + } +}; + +struct CmdFlakeList : EvalCommand { std::string name() override { @@ -24,9 +50,7 @@ struct CmdFlakeList : StoreCommand, MixEvalArgs void run(nix::ref store) override { - auto evalState = std::make_shared(searchPath, store); - - auto registries = evalState->getFlakeRegistries(); + auto registries = getEvalState()->getFlakeRegistries(); stopProgressBar(); @@ -60,7 +84,7 @@ void printFlakeInfo(Flake & flake, bool json) { std::cout << "URI: " << flake.resolvedRef.to_string() << "\n"; std::cout << "Description: " << flake.description << "\n"; if (flake.resolvedRef.ref) - std::cout << "Branch: " << *flake.resolvedRef.ref; + std::cout << "Branch: " << *flake.resolvedRef.ref << "\n"; if (flake.resolvedRef.rev) std::cout << "Revision: " << flake.resolvedRef.rev->to_string(Base16, false) << "\n"; if (flake.revCount) @@ -95,7 +119,7 @@ void printNonFlakeInfo(NonFlake & nonFlake, bool json) { } } -struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs +struct CmdFlakeDeps : FlakeCommand, MixJSON { std::string name() override { @@ -109,12 +133,10 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs void run(nix::ref store) override { - auto evalState = std::make_shared(searchPath, store); + auto evalState = getEvalState(); evalState->addRegistryOverrides(registryOverrides); - FlakeRef flakeRef(flakeUri); - - ResolvedFlake resFlake = resolveFlake(*evalState, flakeRef, UpdateLockFile); + ResolvedFlake resFlake = resolveFlake(*evalState, getFlakeRef(), UpdateLockFile); std::queue todo; todo.push(resFlake); @@ -132,7 +154,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON, StoreCommand, MixEvalArgs } }; -struct CmdFlakeUpdate : StoreCommand, FlakeCommand, MixEvalArgs +struct CmdFlakeUpdate : FlakeCommand { std::string name() override { @@ -146,14 +168,18 @@ struct CmdFlakeUpdate : StoreCommand, FlakeCommand, MixEvalArgs void run(nix::ref store) override { - auto evalState = std::make_shared(searchPath, store); + auto evalState = getEvalState(); + + auto flakeRef = getFlakeRef(); - bool recreateLockFile = true; - updateLockFile(*evalState, flakeUri, recreateLockFile); + if (std::get_if(&flakeRef.data)) + updateLockFile(*evalState, flakeRef, true); + else + throw Error("cannot update lockfile of flake '%s'", flakeRef); } }; -struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand +struct CmdFlakeInfo : FlakeCommand, MixJSON { std::string name() override { @@ -169,8 +195,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON, MixEvalArgs, StoreCommand void run(nix::ref store) override { - auto evalState = std::make_shared(searchPath, store); - Flake flake = getFlake(*evalState, FlakeRef(flakeUri), true); + auto flake = getFlake(); printFlakeInfo(flake, json); } }; @@ -235,7 +260,7 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command } }; -struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs +struct CmdFlakePin : virtual Args, EvalCommand { FlakeUri alias; @@ -256,7 +281,7 @@ struct CmdFlakePin : virtual Args, StoreCommand, MixEvalArgs void run(nix::ref store) override { - auto evalState = std::make_shared(searchPath, store); + auto evalState = getEvalState(); Path userRegistryPath = getUserRegistryPath(); FlakeRegistry userRegistry = *readRegistry(userRegistryPath); @@ -307,7 +332,7 @@ struct CmdFlakeInit : virtual Args, Command } }; -struct CmdFlakeClone : StoreCommand, FlakeCommand, MixEvalArgs +struct CmdFlakeClone : FlakeCommand { Path endDirectory = ""; @@ -328,10 +353,10 @@ struct CmdFlakeClone : StoreCommand, FlakeCommand, MixEvalArgs void run(nix::ref store) override { - auto evalState = std::make_shared(searchPath, store); + auto evalState = getEvalState(); Registries registries = evalState->getFlakeRegistries(); - gitCloneFlake(flakeUri, *evalState, registries, endDirectory); + gitCloneFlake(getFlakeRef().to_string(), *evalState, registries, endDirectory); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index a2a55d949..85ef2cb56 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -38,7 +38,7 @@ SourceExprCommand::SourceExprCommand() .set(&noRegistries, true); } -ref SourceExprCommand::getEvalState() +ref EvalCommand::getEvalState() { if (!evalState) evalState = std::make_shared(searchPath, getStore()); -- cgit v1.2.3 From 70136a9bf46bcf5a97b63f356fefd8adabf4c23b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 May 2019 13:46:07 +0200 Subject: Move flake-related flags into a separate class Also, rename --dont-save-lock-file to --no-save-lock-file and change noRegistries to useRegistries. --- src/nix/command.hh | 21 ++++++++++++++------- src/nix/flake.cc | 18 ++++++++++++------ src/nix/installables.cc | 41 +++++++++++++++++++++++++---------------- 3 files changed, 51 insertions(+), 29 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 423ac5baa..a841b879a 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -11,8 +11,8 @@ extern std::string programPath; struct Value; class Bindings; class EvalState; - class Store; +enum HandleLockFile : unsigned int; /* A command that require a Nix store. */ struct StoreCommand : virtual Command @@ -61,17 +61,24 @@ private: std::shared_ptr evalState; }; -struct SourceExprCommand : virtual Args, EvalCommand +struct MixFlakeOptions : virtual Args { - std::optional file; - - SourceExprCommand(); - bool recreateLockFile = false; bool saveLockFile = true; - bool noRegistries = false; + bool useRegistries = true; + + MixFlakeOptions(); + + HandleLockFile getLockFileMode(); +}; + +struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions +{ + std::optional file; + + SourceExprCommand(); std::vector> parseInstallables( ref store, std::vector ss); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index bc2f1cb5b..b4f0f67be 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -10,7 +10,7 @@ using namespace nix; -class FlakeCommand : virtual Args, public EvalCommand +class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions { std::string flakeUri = "."; @@ -32,7 +32,12 @@ public: Flake getFlake() { auto evalState = getEvalState(); - return nix::getFlake(*evalState, getFlakeRef(), true); + return nix::getFlake(*evalState, getFlakeRef(), useRegistries); + } + + ResolvedFlake resolveFlake() + { + return nix::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); } }; @@ -119,6 +124,7 @@ void printNonFlakeInfo(NonFlake & nonFlake, bool json) { } } +// FIXME: merge info CmdFlakeInfo? struct CmdFlakeDeps : FlakeCommand, MixJSON { std::string name() override @@ -136,7 +142,7 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON auto evalState = getEvalState(); evalState->addRegistryOverrides(registryOverrides); - ResolvedFlake resFlake = resolveFlake(*evalState, getFlakeRef(), UpdateLockFile); + auto resFlake = resolveFlake(); std::queue todo; todo.push(resFlake); @@ -334,7 +340,7 @@ struct CmdFlakeInit : virtual Args, Command struct CmdFlakeClone : FlakeCommand { - Path endDirectory = ""; + Path destDir; std::string name() override { @@ -348,7 +354,7 @@ struct CmdFlakeClone : FlakeCommand CmdFlakeClone() { - expectArg("end-dir", &endDirectory, true); + expectArg("dest-dir", &destDir, true); } void run(nix::ref store) override @@ -356,7 +362,7 @@ struct CmdFlakeClone : FlakeCommand auto evalState = getEvalState(); Registries registries = evalState->getFlakeRegistries(); - gitCloneFlake(getFlakeRef().to_string(), *evalState, registries, endDirectory); + gitCloneFlake(getFlakeRef().to_string(), *evalState, registries, destDir); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 85ef2cb56..1a79f49fb 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -13,29 +13,42 @@ namespace nix { -SourceExprCommand::SourceExprCommand() +MixFlakeOptions::MixFlakeOptions() { - mkFlag() - .shortName('f') - .longName("file") - .label("file") - .description("evaluate a set of attributes from FILE (deprecated)") - .dest(&file); - mkFlag() .longName("recreate-lock-file") .description("recreate lock file from scratch") .set(&recreateLockFile, true); mkFlag() - .longName("dont-save-lock-file") - .description("save the newly generated lock file") + .longName("no-save-lock-file") + .description("do not save the newly generated lock file") .set(&saveLockFile, false); mkFlag() .longName("no-registries") .description("don't use flake registries") - .set(&noRegistries, true); + .set(&useRegistries, false); +} + +HandleLockFile MixFlakeOptions::getLockFileMode() +{ + return + useRegistries + ? recreateLockFile + ? (saveLockFile ? RecreateLockFile : UseNewLockFile) + : (saveLockFile ? UpdateLockFile : UseUpdatedLockFile) + : AllPure; +} + +SourceExprCommand::SourceExprCommand() +{ + mkFlag() + .shortName('f') + .longName("file") + .label("file") + .description("evaluate a set of attributes from FILE (deprecated)") + .dest(&file); } ref EvalCommand::getEvalState() @@ -169,11 +182,7 @@ struct InstallableFlake : InstallableValue { auto vFlake = state.allocValue(); - HandleLockFile handle = cmd.noRegistries ? AllPure : - cmd.recreateLockFile ? - (cmd.saveLockFile ? RecreateLockFile : UseNewLockFile) - : (cmd.saveLockFile ? UpdateLockFile : UseUpdatedLockFile); - makeFlakeValue(state, flakeRef, handle, *vFlake); + makeFlakeValue(state, flakeRef, cmd.getLockFileMode(), *vFlake); auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; -- cgit v1.2.3 From 3e8ef9eb22a0e36ef5ecaabf414b6edd46b87858 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 May 2019 13:57:19 +0200 Subject: nix flake deps: Print flake dependencies --- src/nix/flake.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b4f0f67be..ecbb3b81f 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -70,7 +70,7 @@ struct CmdFlakeList : EvalCommand } }; -void printFlakeInfo(Flake & flake, bool json) { +void printFlakeInfo(const Flake & flake, bool json) { if (json) { nlohmann::json j; j["id"] = flake.id; @@ -98,7 +98,7 @@ void printFlakeInfo(Flake & flake, bool json) { } } -void printNonFlakeInfo(NonFlake & nonFlake, bool json) { +void printNonFlakeInfo(const NonFlake & nonFlake, bool json) { if (json) { nlohmann::json j; j["id"] = nonFlake.alias; @@ -142,20 +142,20 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON auto evalState = getEvalState(); evalState->addRegistryOverrides(registryOverrides); - auto resFlake = resolveFlake(); - std::queue todo; - todo.push(resFlake); + todo.push(resolveFlake()); while (!todo.empty()) { - resFlake = todo.front(); + auto resFlake = std::move(todo.front()); todo.pop(); - for (NonFlake & nonFlake : resFlake.nonFlakeDeps) + for (auto & nonFlake : resFlake.nonFlakeDeps) printNonFlakeInfo(nonFlake, json); - for (auto info : resFlake.flakeDeps) + for (auto & info : resFlake.flakeDeps) { + printFlakeInfo(info.second.flake, json); todo.push(info.second); + } } } }; -- cgit v1.2.3 From e414bde6f9f58e599d48307ff3cb0ab64cb47d9a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 May 2019 14:31:40 +0200 Subject: Check the flake epoch Closes #2883. --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index ecbb3b81f..2dcdfc663 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -83,6 +83,7 @@ void printFlakeInfo(const Flake & flake, bool json) { if (flake.revCount) j["revCount"] = *flake.revCount; j["path"] = flake.storePath; + j["epoch"] = flake.epoch; std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; @@ -95,6 +96,7 @@ void printFlakeInfo(const Flake & flake, bool json) { if (flake.revCount) std::cout << "Revcount: " << *flake.revCount << "\n"; std::cout << "Path: " << flake.storePath << "\n"; + std::cout << "Epoch: " << flake.epoch << "\n"; } } -- cgit v1.2.3 From 66f1d7ee95ba693a15ae5dc413289fee954f0f04 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 May 2019 22:56:46 +0200 Subject: Fetch the flake registry from the NixOS/flake-registry repo --- src/nix/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2dcdfc663..912b154c1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -298,7 +298,7 @@ struct CmdFlakePin : virtual Args, EvalCommand it->second = getFlake(*evalState, it->second, true).resolvedRef; writeRegistry(userRegistry, userRegistryPath); } else { - std::shared_ptr globalReg = getGlobalRegistry(); + std::shared_ptr globalReg = evalState->getGlobalFlakeRegistry(); it = globalReg->entries.find(FlakeRef(alias)); if (it != globalReg->entries.end()) { FlakeRef newRef = getFlake(*evalState, it->second, true).resolvedRef; -- cgit v1.2.3 From 90fe1dfd2fd85f17609166c92a2163dfaa09ea99 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 23 May 2019 23:42:13 +0200 Subject: Register flake source trees as GC roots This ensures that flakes don't get garbage-collected, which is important to get nix-channel-like behaviour. For example, running $ nix build hydra: will create a GC root ~/.cache/nix/flake-closures/hydra -> /nix/store/xarfiqcwa4w8r4qpz1a769xxs8c3phgn-flake-closure where the contents/references of the linked file in the store are the flake source trees used by the 'hydra' flake: /nix/store/n6d5f5lkpfjbmkyby0nlg8y1wbkmbc7i-source /nix/store/vbkg4zy1qd29fnhflsv9k2j9jnbqd5m2-source /nix/store/z46xni7d47s5wk694359mq9ay353ar94-source Note that this in itself is not enough to allow offline use; the fetcher for the flakeref (e.g. fetchGit or downloadCached) must not fail if it cannot fetch the latest version of the file, so long as it knows a cached version. Issue #2868. --- src/nix/installables.cc | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 1a79f49fb..6cab06240 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -10,6 +10,7 @@ #include "primops/flake.hh" #include +#include namespace nix { @@ -162,6 +163,44 @@ struct InstallableAttrPath : InstallableValue } }; +void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const ResolvedFlake & resFlake) +{ + if (std::get_if(&origFlakeRef.data)) return; + + /* Get the store paths of all non-local flakes. */ + PathSet closure; + + std::queue> queue; + queue.push(resFlake); + + while (!queue.empty()) { + const ResolvedFlake & flake = queue.front(); + queue.pop(); + if (!std::get_if(&flake.flake.resolvedRef.data)) + closure.insert(flake.flake.storePath); + for (const auto & dep : flake.flakeDeps) + queue.push(dep.second); + } + + if (closure.empty()) return; + + /* Write the closure to a file in the store. */ + auto closurePath = store.addTextToStore("flake-closure", concatStringsSep(" ", closure), closure); + + Path cacheDir = getCacheDir() + "/nix/flake-closures"; + createDirs(cacheDir); + + auto s = origFlakeRef.to_string(); + assert(s[0] != '.'); + s = replaceStrings(s, "%", "%25"); + s = replaceStrings(s, "/", "%2f"); + s = replaceStrings(s, ":", "%3a"); + Path symlink = cacheDir + "/" + s; + debug("writing GC root '%s' for flake closure of '%s'", symlink, origFlakeRef); + replaceSymlink(closurePath, symlink); + store.addIndirectRoot(symlink); +} + struct InstallableFlake : InstallableValue { FlakeRef flakeRef; @@ -182,7 +221,11 @@ struct InstallableFlake : InstallableValue { auto vFlake = state.allocValue(); - makeFlakeValue(state, flakeRef, cmd.getLockFileMode(), *vFlake); + auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); + + callFlake(state, resFlake, *vFlake); + + makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake); auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; -- cgit v1.2.3 From 6d7efcfaeb715b04986ef5a6b1d8f57de4ba5509 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 12:58:28 +0200 Subject: Store SourceInfo in Flake and NonFlake This deduplicates some shared fields. Factoring out the commonality is useful in places like makeFlakeValue(). --- src/nix/flake.cc | 24 ++++++++++++------------ src/nix/installables.cc | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 912b154c1..71a6c16d9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -80,9 +80,9 @@ void printFlakeInfo(const Flake & flake, bool json) { j["branch"] = *flake.resolvedRef.ref; if (flake.resolvedRef.rev) j["revision"] = flake.resolvedRef.rev->to_string(Base16, false); - if (flake.revCount) - j["revCount"] = *flake.revCount; - j["path"] = flake.storePath; + if (flake.sourceInfo.revCount) + j["revCount"] = *flake.sourceInfo.revCount; + j["path"] = flake.sourceInfo.storePath; j["epoch"] = flake.epoch; std::cout << j.dump(4) << std::endl; } else { @@ -93,9 +93,9 @@ void printFlakeInfo(const Flake & flake, bool json) { std::cout << "Branch: " << *flake.resolvedRef.ref << "\n"; if (flake.resolvedRef.rev) std::cout << "Revision: " << flake.resolvedRef.rev->to_string(Base16, false) << "\n"; - if (flake.revCount) - std::cout << "Revcount: " << *flake.revCount << "\n"; - std::cout << "Path: " << flake.storePath << "\n"; + if (flake.sourceInfo.revCount) + std::cout << "Revcount: " << *flake.sourceInfo.revCount << "\n"; + std::cout << "Path: " << flake.sourceInfo.storePath << "\n"; std::cout << "Epoch: " << flake.epoch << "\n"; } } @@ -109,9 +109,9 @@ void printNonFlakeInfo(const NonFlake & nonFlake, bool json) { j["branch"] = *nonFlake.resolvedRef.ref; if (nonFlake.resolvedRef.rev) j["revision"] = nonFlake.resolvedRef.rev->to_string(Base16, false); - if (nonFlake.revCount) - j["revCount"] = *nonFlake.revCount; - j["path"] = nonFlake.storePath; + if (nonFlake.sourceInfo.revCount) + j["revCount"] = *nonFlake.sourceInfo.revCount; + j["path"] = nonFlake.sourceInfo.storePath; std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << nonFlake.alias << "\n"; @@ -120,9 +120,9 @@ void printNonFlakeInfo(const NonFlake & nonFlake, bool json) { std::cout << "Branch: " << *nonFlake.resolvedRef.ref; if (nonFlake.resolvedRef.rev) std::cout << "Revision: " << nonFlake.resolvedRef.rev->to_string(Base16, false) << "\n"; - if (nonFlake.revCount) - std::cout << "Revcount: " << *nonFlake.revCount << "\n"; - std::cout << "Path: " << nonFlake.storePath << "\n"; + if (nonFlake.sourceInfo.revCount) + std::cout << "Revcount: " << *nonFlake.sourceInfo.revCount << "\n"; + std::cout << "Path: " << nonFlake.sourceInfo.storePath << "\n"; } } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 6cab06240..ce09a43d0 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -177,7 +177,7 @@ void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const const ResolvedFlake & flake = queue.front(); queue.pop(); if (!std::get_if(&flake.flake.resolvedRef.data)) - closure.insert(flake.flake.storePath); + closure.insert(flake.flake.sourceInfo.storePath); for (const auto & dep : flake.flakeDeps) queue.push(dep.second); } -- cgit v1.2.3 From dda4f7167b9a421f1bb85ee5eed79a1bf03a13e4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 13:12:43 +0200 Subject: Remove redundant resolvedRef fields since they're already in SourceInfo --- src/nix/flake.cc | 44 ++++++++++++++++++++++---------------------- src/nix/installables.cc | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 71a6c16d9..ce5ce8742 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -74,12 +74,12 @@ void printFlakeInfo(const Flake & flake, bool json) { if (json) { nlohmann::json j; j["id"] = flake.id; - j["uri"] = flake.resolvedRef.to_string(); + j["uri"] = flake.sourceInfo.resolvedRef.to_string(); j["description"] = flake.description; - if (flake.resolvedRef.ref) - j["branch"] = *flake.resolvedRef.ref; - if (flake.resolvedRef.rev) - j["revision"] = flake.resolvedRef.rev->to_string(Base16, false); + if (flake.sourceInfo.resolvedRef.ref) + j["branch"] = *flake.sourceInfo.resolvedRef.ref; + if (flake.sourceInfo.resolvedRef.rev) + j["revision"] = flake.sourceInfo.resolvedRef.rev->to_string(Base16, false); if (flake.sourceInfo.revCount) j["revCount"] = *flake.sourceInfo.revCount; j["path"] = flake.sourceInfo.storePath; @@ -87,12 +87,12 @@ void printFlakeInfo(const Flake & flake, bool json) { std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; - std::cout << "URI: " << flake.resolvedRef.to_string() << "\n"; + std::cout << "URI: " << flake.sourceInfo.resolvedRef.to_string() << "\n"; std::cout << "Description: " << flake.description << "\n"; - if (flake.resolvedRef.ref) - std::cout << "Branch: " << *flake.resolvedRef.ref << "\n"; - if (flake.resolvedRef.rev) - std::cout << "Revision: " << flake.resolvedRef.rev->to_string(Base16, false) << "\n"; + if (flake.sourceInfo.resolvedRef.ref) + std::cout << "Branch: " << *flake.sourceInfo.resolvedRef.ref << "\n"; + if (flake.sourceInfo.resolvedRef.rev) + std::cout << "Revision: " << flake.sourceInfo.resolvedRef.rev->to_string(Base16, false) << "\n"; if (flake.sourceInfo.revCount) std::cout << "Revcount: " << *flake.sourceInfo.revCount << "\n"; std::cout << "Path: " << flake.sourceInfo.storePath << "\n"; @@ -104,22 +104,22 @@ void printNonFlakeInfo(const NonFlake & nonFlake, bool json) { if (json) { nlohmann::json j; j["id"] = nonFlake.alias; - j["uri"] = nonFlake.resolvedRef.to_string(); - if (nonFlake.resolvedRef.ref) - j["branch"] = *nonFlake.resolvedRef.ref; - if (nonFlake.resolvedRef.rev) - j["revision"] = nonFlake.resolvedRef.rev->to_string(Base16, false); + j["uri"] = nonFlake.sourceInfo.resolvedRef.to_string(); + if (nonFlake.sourceInfo.resolvedRef.ref) + j["branch"] = *nonFlake.sourceInfo.resolvedRef.ref; + if (nonFlake.sourceInfo.resolvedRef.rev) + j["revision"] = nonFlake.sourceInfo.resolvedRef.rev->to_string(Base16, false); if (nonFlake.sourceInfo.revCount) j["revCount"] = *nonFlake.sourceInfo.revCount; j["path"] = nonFlake.sourceInfo.storePath; std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << nonFlake.alias << "\n"; - std::cout << "URI: " << nonFlake.resolvedRef.to_string() << "\n"; - if (nonFlake.resolvedRef.ref) - std::cout << "Branch: " << *nonFlake.resolvedRef.ref; - if (nonFlake.resolvedRef.rev) - std::cout << "Revision: " << nonFlake.resolvedRef.rev->to_string(Base16, false) << "\n"; + std::cout << "URI: " << nonFlake.sourceInfo.resolvedRef.to_string() << "\n"; + if (nonFlake.sourceInfo.resolvedRef.ref) + std::cout << "Branch: " << *nonFlake.sourceInfo.resolvedRef.ref; + if (nonFlake.sourceInfo.resolvedRef.rev) + std::cout << "Revision: " << nonFlake.sourceInfo.resolvedRef.rev->to_string(Base16, false) << "\n"; if (nonFlake.sourceInfo.revCount) std::cout << "Revcount: " << *nonFlake.sourceInfo.revCount << "\n"; std::cout << "Path: " << nonFlake.sourceInfo.storePath << "\n"; @@ -295,13 +295,13 @@ struct CmdFlakePin : virtual Args, EvalCommand FlakeRegistry userRegistry = *readRegistry(userRegistryPath); auto it = userRegistry.entries.find(FlakeRef(alias)); if (it != userRegistry.entries.end()) { - it->second = getFlake(*evalState, it->second, true).resolvedRef; + it->second = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; writeRegistry(userRegistry, userRegistryPath); } else { std::shared_ptr globalReg = evalState->getGlobalFlakeRegistry(); it = globalReg->entries.find(FlakeRef(alias)); if (it != globalReg->entries.end()) { - FlakeRef newRef = getFlake(*evalState, it->second, true).resolvedRef; + auto newRef = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; userRegistry.entries.insert_or_assign(alias, newRef); writeRegistry(userRegistry, userRegistryPath); } else diff --git a/src/nix/installables.cc b/src/nix/installables.cc index ce09a43d0..4f9161666 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -176,7 +176,7 @@ void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const while (!queue.empty()) { const ResolvedFlake & flake = queue.front(); queue.pop(); - if (!std::get_if(&flake.flake.resolvedRef.data)) + if (!std::get_if(&flake.flake.sourceInfo.resolvedRef.data)) closure.insert(flake.flake.sourceInfo.storePath); for (const auto & dep : flake.flakeDeps) queue.push(dep.second); -- cgit v1.2.3 From 25e497bf9c3a848afbd7463f132c9229d8a44284 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 13:14:27 +0200 Subject: nix flake info/deps: Stop progress bar before printing output --- src/nix/flake.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index ce5ce8742..66fa11e47 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -147,6 +147,8 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON std::queue todo; todo.push(resolveFlake()); + stopProgressBar(); + while (!todo.empty()) { auto resFlake = std::move(todo.front()); todo.pop(); @@ -204,6 +206,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON void run(nix::ref store) override { auto flake = getFlake(); + stopProgressBar(); printFlakeInfo(flake, json); } }; -- cgit v1.2.3 From fdf06ce72f9695f57f6215683e4b2e1c6ec463cf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 13:21:06 +0200 Subject: printFlakeInfo/printNonFlakeInfo: Factor out commonality --- src/nix/flake.cc | 66 +++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 66fa11e47..da19caa5a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -70,59 +70,57 @@ struct CmdFlakeList : EvalCommand } }; -void printFlakeInfo(const Flake & flake, bool json) { +static void printSourceInfo(const SourceInfo & sourceInfo) +{ + std::cout << "URI: " << sourceInfo.resolvedRef.to_string() << "\n"; + if (sourceInfo.resolvedRef.ref) + std::cout << "Branch: " << *sourceInfo.resolvedRef.ref; + if (sourceInfo.resolvedRef.rev) + std::cout << "Revision: " << sourceInfo.resolvedRef.rev->to_string(Base16, false) << "\n"; + if (sourceInfo.revCount) + std::cout << "Revcount: " << *sourceInfo.revCount << "\n"; + std::cout << "Path: " << sourceInfo.storePath << "\n"; +} + +static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) +{ + j["uri"] = sourceInfo.resolvedRef.to_string(); + if (sourceInfo.resolvedRef.ref) + j["branch"] = *sourceInfo.resolvedRef.ref; + if (sourceInfo.resolvedRef.rev) + j["revision"] = sourceInfo.resolvedRef.rev->to_string(Base16, false); + if (sourceInfo.revCount) + j["revCount"] = *sourceInfo.revCount; + j["path"] = sourceInfo.storePath; +} + +static void printFlakeInfo(const Flake & flake, bool json) +{ if (json) { nlohmann::json j; j["id"] = flake.id; - j["uri"] = flake.sourceInfo.resolvedRef.to_string(); j["description"] = flake.description; - if (flake.sourceInfo.resolvedRef.ref) - j["branch"] = *flake.sourceInfo.resolvedRef.ref; - if (flake.sourceInfo.resolvedRef.rev) - j["revision"] = flake.sourceInfo.resolvedRef.rev->to_string(Base16, false); - if (flake.sourceInfo.revCount) - j["revCount"] = *flake.sourceInfo.revCount; - j["path"] = flake.sourceInfo.storePath; j["epoch"] = flake.epoch; + sourceInfoToJson(flake.sourceInfo, j); std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << flake.id << "\n"; - std::cout << "URI: " << flake.sourceInfo.resolvedRef.to_string() << "\n"; std::cout << "Description: " << flake.description << "\n"; - if (flake.sourceInfo.resolvedRef.ref) - std::cout << "Branch: " << *flake.sourceInfo.resolvedRef.ref << "\n"; - if (flake.sourceInfo.resolvedRef.rev) - std::cout << "Revision: " << flake.sourceInfo.resolvedRef.rev->to_string(Base16, false) << "\n"; - if (flake.sourceInfo.revCount) - std::cout << "Revcount: " << *flake.sourceInfo.revCount << "\n"; - std::cout << "Path: " << flake.sourceInfo.storePath << "\n"; std::cout << "Epoch: " << flake.epoch << "\n"; + printSourceInfo(flake.sourceInfo); } } -void printNonFlakeInfo(const NonFlake & nonFlake, bool json) { +static void printNonFlakeInfo(const NonFlake & nonFlake, bool json) +{ if (json) { nlohmann::json j; j["id"] = nonFlake.alias; - j["uri"] = nonFlake.sourceInfo.resolvedRef.to_string(); - if (nonFlake.sourceInfo.resolvedRef.ref) - j["branch"] = *nonFlake.sourceInfo.resolvedRef.ref; - if (nonFlake.sourceInfo.resolvedRef.rev) - j["revision"] = nonFlake.sourceInfo.resolvedRef.rev->to_string(Base16, false); - if (nonFlake.sourceInfo.revCount) - j["revCount"] = *nonFlake.sourceInfo.revCount; - j["path"] = nonFlake.sourceInfo.storePath; + printSourceInfo(nonFlake.sourceInfo); std::cout << j.dump(4) << std::endl; } else { std::cout << "ID: " << nonFlake.alias << "\n"; - std::cout << "URI: " << nonFlake.sourceInfo.resolvedRef.to_string() << "\n"; - if (nonFlake.sourceInfo.resolvedRef.ref) - std::cout << "Branch: " << *nonFlake.sourceInfo.resolvedRef.ref; - if (nonFlake.sourceInfo.resolvedRef.rev) - std::cout << "Revision: " << nonFlake.sourceInfo.resolvedRef.rev->to_string(Base16, false) << "\n"; - if (nonFlake.sourceInfo.revCount) - std::cout << "Revcount: " << *nonFlake.sourceInfo.revCount << "\n"; - std::cout << "Path: " << nonFlake.sourceInfo.storePath << "\n"; + printSourceInfo(nonFlake.sourceInfo); } } -- cgit v1.2.3 From e7e7a03baf446bad34939c106aee4b69f5619fd0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 13:22:11 +0200 Subject: nix flake deps: Remove --json flag for now It doesn't produce valid JSON at the moment (but a concatenation of JSON objects). Anyway we probably should merge this command info 'nix flake info'. --- src/nix/flake.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index da19caa5a..810529613 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -125,7 +125,7 @@ static void printNonFlakeInfo(const NonFlake & nonFlake, bool json) } // FIXME: merge info CmdFlakeInfo? -struct CmdFlakeDeps : FlakeCommand, MixJSON +struct CmdFlakeDeps : FlakeCommand { std::string name() override { @@ -152,10 +152,10 @@ struct CmdFlakeDeps : FlakeCommand, MixJSON todo.pop(); for (auto & nonFlake : resFlake.nonFlakeDeps) - printNonFlakeInfo(nonFlake, json); + printNonFlakeInfo(nonFlake, false); for (auto & info : resFlake.flakeDeps) { - printFlakeInfo(info.second.flake, json); + printFlakeInfo(info.second.flake, false); todo.push(info.second); } } -- cgit v1.2.3 From 46294d60cd46564ea6b40cf9c32759021f9d1fc9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 14:01:57 +0200 Subject: printFlakeInfo: Separate JSON output --- src/nix/flake.cc | 59 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 810529613..0fea1993c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -94,34 +94,36 @@ static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) j["path"] = sourceInfo.storePath; } -static void printFlakeInfo(const Flake & flake, bool json) +static void printFlakeInfo(const Flake & flake) { - if (json) { - nlohmann::json j; - j["id"] = flake.id; - j["description"] = flake.description; - j["epoch"] = flake.epoch; - sourceInfoToJson(flake.sourceInfo, j); - std::cout << j.dump(4) << std::endl; - } else { - std::cout << "ID: " << flake.id << "\n"; - std::cout << "Description: " << flake.description << "\n"; - std::cout << "Epoch: " << flake.epoch << "\n"; - printSourceInfo(flake.sourceInfo); - } + std::cout << "ID: " << flake.id << "\n"; + std::cout << "Description: " << flake.description << "\n"; + std::cout << "Epoch: " << flake.epoch << "\n"; + printSourceInfo(flake.sourceInfo); } -static void printNonFlakeInfo(const NonFlake & nonFlake, bool json) +static nlohmann::json flakeToJson(const Flake & flake) { - if (json) { - nlohmann::json j; - j["id"] = nonFlake.alias; - printSourceInfo(nonFlake.sourceInfo); - std::cout << j.dump(4) << std::endl; - } else { - std::cout << "ID: " << nonFlake.alias << "\n"; - printSourceInfo(nonFlake.sourceInfo); - } + nlohmann::json j; + j["id"] = flake.id; + j["description"] = flake.description; + j["epoch"] = flake.epoch; + sourceInfoToJson(flake.sourceInfo, j); + return j; +} + +static void printNonFlakeInfo(const NonFlake & nonFlake) +{ + std::cout << "ID: " << nonFlake.alias << "\n"; + printSourceInfo(nonFlake.sourceInfo); +} + +static nlohmann::json nonFlakeToJson(const NonFlake & nonFlake) +{ + nlohmann::json j; + j["id"] = nonFlake.alias; + sourceInfoToJson(nonFlake.sourceInfo, j); + return j; } // FIXME: merge info CmdFlakeInfo? @@ -152,10 +154,10 @@ struct CmdFlakeDeps : FlakeCommand todo.pop(); for (auto & nonFlake : resFlake.nonFlakeDeps) - printNonFlakeInfo(nonFlake, false); + printNonFlakeInfo(nonFlake); for (auto & info : resFlake.flakeDeps) { - printFlakeInfo(info.second.flake, false); + printFlakeInfo(info.second.flake); todo.push(info.second); } } @@ -205,7 +207,10 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON { auto flake = getFlake(); stopProgressBar(); - printFlakeInfo(flake, json); + if (json) + std::cout << flakeToJson(flake).dump() << std::endl; + else + printFlakeInfo(flake); } }; -- cgit v1.2.3 From 444786e6d39209d3f0c222af0fbc80846ac7337d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 14:11:19 +0200 Subject: nix flake info: Add missing newline --- src/nix/flake.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0fea1993c..d8c422d3d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -72,14 +72,14 @@ struct CmdFlakeList : EvalCommand static void printSourceInfo(const SourceInfo & sourceInfo) { - std::cout << "URI: " << sourceInfo.resolvedRef.to_string() << "\n"; + std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string()); if (sourceInfo.resolvedRef.ref) - std::cout << "Branch: " << *sourceInfo.resolvedRef.ref; + std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref); if (sourceInfo.resolvedRef.rev) - std::cout << "Revision: " << sourceInfo.resolvedRef.rev->to_string(Base16, false) << "\n"; + std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false)); if (sourceInfo.revCount) - std::cout << "Revcount: " << *sourceInfo.revCount << "\n"; - std::cout << "Path: " << sourceInfo.storePath << "\n"; + std::cout << fmt("Revcount: %s\n", *sourceInfo.revCount); + std::cout << fmt("Path: %s\n", sourceInfo.storePath); } static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) @@ -96,9 +96,9 @@ static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) static void printFlakeInfo(const Flake & flake) { - std::cout << "ID: " << flake.id << "\n"; - std::cout << "Description: " << flake.description << "\n"; - std::cout << "Epoch: " << flake.epoch << "\n"; + std::cout << fmt("ID: %s\n", flake.id); + std::cout << fmt("Description: %s\n", flake.description); + std::cout << fmt("Epoch: %s\n", flake.epoch); printSourceInfo(flake.sourceInfo); } @@ -114,7 +114,7 @@ static nlohmann::json flakeToJson(const Flake & flake) static void printNonFlakeInfo(const NonFlake & nonFlake) { - std::cout << "ID: " << nonFlake.alias << "\n"; + std::cout << fmt("ID: %s\n", nonFlake.alias); printSourceInfo(nonFlake.sourceInfo); } -- cgit v1.2.3 From 0f840483c731f48983832f7f627909f8463f05f3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 May 2019 20:34:02 +0200 Subject: Add date of last commit to SourceInfo This is primarily useful for version string generation, where we need a monotonically increasing number. The revcount is the preferred thing to use, but isn't available for GitHub flakes (since it requires fetching the entire history). The last commit timestamp OTOH can be extracted from GitHub tarballs. --- src/nix/flake.cc | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d8c422d3d..7836f0cfe 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -7,6 +7,7 @@ #include #include +#include using namespace nix; @@ -72,14 +73,17 @@ struct CmdFlakeList : EvalCommand static void printSourceInfo(const SourceInfo & sourceInfo) { - std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string()); + std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string()); if (sourceInfo.resolvedRef.ref) - std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref); + std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref); if (sourceInfo.resolvedRef.rev) - std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false)); + std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false)); if (sourceInfo.revCount) - std::cout << fmt("Revcount: %s\n", *sourceInfo.revCount); - std::cout << fmt("Path: %s\n", sourceInfo.storePath); + std::cout << fmt("Revisions: %s\n", *sourceInfo.revCount); + if (sourceInfo.lastModified) + std::cout << fmt("Last modified: %s\n", + std::put_time(std::localtime(&*sourceInfo.lastModified), "%F %T")); + std::cout << fmt("Path: %s\n", sourceInfo.storePath); } static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) @@ -91,14 +95,16 @@ static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) j["revision"] = sourceInfo.resolvedRef.rev->to_string(Base16, false); if (sourceInfo.revCount) j["revCount"] = *sourceInfo.revCount; + if (sourceInfo.lastModified) + j["lastModified"] = *sourceInfo.lastModified; j["path"] = sourceInfo.storePath; } static void printFlakeInfo(const Flake & flake) { - std::cout << fmt("ID: %s\n", flake.id); - std::cout << fmt("Description: %s\n", flake.description); - std::cout << fmt("Epoch: %s\n", flake.epoch); + std::cout << fmt("ID: %s\n", flake.id); + std::cout << fmt("Description: %s\n", flake.description); + std::cout << fmt("Epoch: %s\n", flake.epoch); printSourceInfo(flake.sourceInfo); } @@ -114,7 +120,7 @@ static nlohmann::json flakeToJson(const Flake & flake) static void printNonFlakeInfo(const NonFlake & nonFlake) { - std::cout << fmt("ID: %s\n", nonFlake.alias); + std::cout << fmt("ID: %s\n", nonFlake.alias); printSourceInfo(nonFlake.sourceInfo); } -- cgit v1.2.3 From 6e4a8c47f46041a94135c1e91ce4ab606d05f8ee Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 15:31:07 +0200 Subject: Put flake-related stuff in its own namespace --- src/nix/command.hh | 5 ++++- src/nix/flake.cc | 5 +++-- src/nix/installables.cc | 11 +++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index a841b879a..26c308331 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -12,7 +12,10 @@ struct Value; class Bindings; class EvalState; class Store; + +namespace flake { enum HandleLockFile : unsigned int; +} /* A command that require a Nix store. */ struct StoreCommand : virtual Command @@ -71,7 +74,7 @@ struct MixFlakeOptions : virtual Args MixFlakeOptions(); - HandleLockFile getLockFileMode(); + flake::HandleLockFile getLockFileMode(); }; struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 7836f0cfe..c2781531d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -10,6 +10,7 @@ #include using namespace nix; +using namespace nix::flake; class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions { @@ -33,12 +34,12 @@ public: Flake getFlake() { auto evalState = getEvalState(); - return nix::getFlake(*evalState, getFlakeRef(), useRegistries); + return flake::getFlake(*evalState, getFlakeRef(), useRegistries); } ResolvedFlake resolveFlake() { - return nix::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); + return flake::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 4f9161666..df5214f13 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -32,8 +32,9 @@ MixFlakeOptions::MixFlakeOptions() .set(&useRegistries, false); } -HandleLockFile MixFlakeOptions::getLockFileMode() +flake::HandleLockFile MixFlakeOptions::getLockFileMode() { + using namespace flake; return useRegistries ? recreateLockFile @@ -163,18 +164,20 @@ struct InstallableAttrPath : InstallableValue } }; -void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const ResolvedFlake & resFlake) +void makeFlakeClosureGCRoot(Store & store, + const FlakeRef & origFlakeRef, + const flake::ResolvedFlake & resFlake) { if (std::get_if(&origFlakeRef.data)) return; /* Get the store paths of all non-local flakes. */ PathSet closure; - std::queue> queue; + std::queue> queue; queue.push(resFlake); while (!queue.empty()) { - const ResolvedFlake & flake = queue.front(); + const flake::ResolvedFlake & flake = queue.front(); queue.pop(); if (!std::get_if(&flake.flake.sourceInfo.resolvedRef.data)) closure.insert(flake.flake.sourceInfo.storePath); -- cgit v1.2.3 From e0aaf05f4fde3096a86c6481ada64aae53bfce93 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 17:25:41 +0200 Subject: Add 'nix flake check' command This evaluates all the 'provides' of a flake and builds the 'check' attributes. --- src/nix/flake.cc | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index c2781531d..f0231e263 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -3,7 +3,10 @@ #include "shared.hh" #include "progress-bar.hh" #include "eval.hh" +#include "eval-inline.hh" #include "primops/flake.hh" +#include "get-drvs.hh" +#include "store-api.hh" #include #include @@ -208,8 +211,6 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON return "list info about a given flake"; } - CmdFlakeInfo () { } - void run(nix::ref store) override { auto flake = getFlake(); @@ -221,6 +222,93 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON } }; +static void enumerateProvides(EvalState & state, Value & vFlake, + std::function callback) +{ + state.forceAttrs(vFlake); + + auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value; + + state.forceAttrs(*vProvides); + + for (auto & attr : *vProvides->attrs) + callback(attr.name, *attr.value); +} + +struct CmdFlakeCheck : FlakeCommand, MixJSON +{ + bool build = true; + + CmdFlakeCheck() + { + mkFlag() + .longName("no-build") + .description("do not build checks") + .set(&build, false); + } + + std::string name() override + { + return "check"; + } + + std::string description() override + { + return "check whether the flake evaluates and run its tests"; + } + + void run(nix::ref store) override + { + auto state = getEvalState(); + auto flake = resolveFlake(); + + PathSet drvPaths; + + { + Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); + + auto vFlake = state->allocValue(); + flake::callFlake(*state, flake, *vFlake); + + enumerateProvides(*state, + *vFlake, + [&](const std::string & name, Value & vProvide) { + Activity act(*logger, lvlChatty, actUnknown, + fmt("checking flake output '%s'", name)); + + try { + state->forceValue(vProvide); + + if (name == "checks") { + state->forceAttrs(vProvide); + for (auto & aCheck : *vProvide.attrs) { + try { + auto drvInfo = getDerivation(*state, *aCheck.value, false); + if (!drvInfo) + throw Error("flake output 'check.%s' is not a derivation", aCheck.name); + drvPaths.insert(drvInfo->queryDrvPath()); + // FIXME: check meta attributes? + } catch (Error & e) { + e.addPrefix(fmt("while checking flake check '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", aCheck.name)); + throw; + } + } + } + + } catch (Error & e) { + e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name)); + throw; + } + }); + } + + if (build) { + Activity act(*logger, lvlInfo, actUnknown, "running flake checks"); + store->buildPaths(drvPaths); + } + } +}; + struct CmdFlakeAdd : MixEvalArgs, Command { FlakeUri alias; @@ -387,6 +475,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command : MultiCommand({make_ref() , make_ref() , make_ref() + , make_ref() , make_ref() , make_ref() , make_ref() -- cgit v1.2.3 From 0e32b32fa3655e8a3e028ebb5ac838164a606132 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 20:57:08 +0200 Subject: nix flake check: Check defaultPackage, devShell and packages --- src/nix/flake.cc | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index f0231e263..19e97aed9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -262,6 +262,19 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON auto state = getEvalState(); auto flake = resolveFlake(); + auto checkDerivation = [&](const std::string & attrPath, Value & v) { + try { + auto drvInfo = getDerivation(*state, v, false); + if (!drvInfo) + throw Error("flake attribute '%s' is not a derivation", attrPath); + // FIXME: check meta attributes + return drvInfo->queryDrvPath(); + } catch (Error & e) { + e.addPrefix(fmt("while checking flake attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + throw; + } + }; + PathSet drvPaths; { @@ -281,20 +294,21 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON if (name == "checks") { state->forceAttrs(vProvide); - for (auto & aCheck : *vProvide.attrs) { - try { - auto drvInfo = getDerivation(*state, *aCheck.value, false); - if (!drvInfo) - throw Error("flake output 'check.%s' is not a derivation", aCheck.name); - drvPaths.insert(drvInfo->queryDrvPath()); - // FIXME: check meta attributes? - } catch (Error & e) { - e.addPrefix(fmt("while checking flake check '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", aCheck.name)); - throw; - } - } + for (auto & aCheck : *vProvide.attrs) + drvPaths.insert(checkDerivation( + name + "." + (std::string) aCheck.name, *aCheck.value)); + } + + else if (name == "packages") { + state->forceAttrs(vProvide); + for (auto & aCheck : *vProvide.attrs) + checkDerivation( + name + "." + (std::string) aCheck.name, *aCheck.value); } + else if (name == "defaultPackage" || name == "devShell") + checkDerivation(name, vProvide); + } catch (Error & e) { e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name)); throw; -- cgit v1.2.3 From a9d3524e1f4f0212184d611b3ff3b520619dff8e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 21:00:44 +0200 Subject: nix flake check: Use read-only mode if we're not building --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 19e97aed9..0be003da2 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -259,6 +259,8 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON void run(nix::ref store) override { + settings.readOnlyMode = !build; + auto state = getEvalState(); auto flake = resolveFlake(); -- cgit v1.2.3 From 3488fa7c6cef487d3f9501e89894f9e632e678db Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 21:30:22 +0200 Subject: Hack: Use legacyPackages from Nixpkgs Nixpkgs doesn't provide a clean "packages" set yet, so until that's the case, look for packages in "legacyPackages" as well. --- src/nix/installables.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index df5214f13..ea12cd79c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -236,9 +236,9 @@ struct InstallableFlake : InstallableValue auto emptyArgs = state.allocBindings(0); - // As a convenience, look for the attribute in - // 'provides.packages'. if (searchPackages) { + // As a convenience, look for the attribute in + // 'provides.packages'. if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) { try { auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); @@ -247,6 +247,17 @@ struct InstallableFlake : InstallableValue } catch (AttrPathNotFound & e) { } } + + // As a temporary hack until Nixpkgs is properly converted + // to provide a clean 'packages' set, look in 'legacyPackages'. + if (auto aPackages = *vProvides->attrs->get(state.symbols.create("legacyPackages"))) { + try { + auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); + state.forceValue(*v); + return v; + } catch (AttrPathNotFound & e) { + } + } } // Otherwise, look for it in 'provides'. -- cgit v1.2.3 From 49436bdbb77f32ffec2035e836add04f98be49e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 22:17:08 +0200 Subject: nix flake info --json: List the "provides" It also lists the contents of "checks" and "packages". For example: $ nix flake info --json | jq { "branch": "HEAD", "description": "The purely functional package manager", "epoch": 2019, "id": "nix", "lastModified": 1559161142, "path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source", "provides": { "checks": { "binaryTarball": {}, "nix-copy-closure": {}, "perlBindings": {}, "remoteBuilds": {}, "setuid": {} }, "defaultPackage": {}, "devShell": {}, "hydraJobs": {}, "packages": { "nix": {}, "nix-perl-bindings": {} } }, "revCount": 6955, "revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98", "uri": "/home/eelco/Dev/nix" } Fixes #2820. --- src/nix/flake.cc | 56 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0be003da2..872ec2849 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -199,6 +199,19 @@ struct CmdFlakeUpdate : FlakeCommand } }; +static void enumerateProvides(EvalState & state, Value & vFlake, + std::function callback) +{ + state.forceAttrs(vFlake); + + auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value; + + state.forceAttrs(*vProvides); + + for (auto & attr : *vProvides->attrs) + callback(attr.name, *attr.value); +} + struct CmdFlakeInfo : FlakeCommand, MixJSON { std::string name() override @@ -215,25 +228,38 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON { auto flake = getFlake(); stopProgressBar(); - if (json) - std::cout << flakeToJson(flake).dump() << std::endl; - else - printFlakeInfo(flake); - } -}; -static void enumerateProvides(EvalState & state, Value & vFlake, - std::function callback) -{ - state.forceAttrs(vFlake); + if (json) { + auto json = flakeToJson(flake); - auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value; + auto state = getEvalState(); - state.forceAttrs(*vProvides); + auto vFlake = state->allocValue(); + flake::callFlake(*state, flake, *vFlake); - for (auto & attr : *vProvides->attrs) - callback(attr.name, *attr.value); -} + auto provides = nlohmann::json::object(); + + enumerateProvides(*state, + *vFlake, + [&](const std::string & name, Value & vProvide) { + auto provide = nlohmann::json::object(); + + if (name == "checks" || name == "packages") { + state->forceAttrs(vProvide); + for (auto & aCheck : *vProvide.attrs) + provide[aCheck.name] = nlohmann::json::object(); + } + + provides[name] = provide; + }); + + json["provides"] = std::move(provides); + + std::cout << json.dump() << std::endl; + } else + printFlakeInfo(flake); + } +}; struct CmdFlakeCheck : FlakeCommand, MixJSON { -- cgit v1.2.3 From 094539ef4a637bde795bf67ddcc8f1f7443499f9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 May 2019 23:09:23 +0200 Subject: Rename requires -> inputs, provides -> outputs Issue #2828. --- src/nix/flake.cc | 20 ++++++++++---------- src/nix/installables.cc | 14 +++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 872ec2849..8d6716391 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -199,16 +199,16 @@ struct CmdFlakeUpdate : FlakeCommand } }; -static void enumerateProvides(EvalState & state, Value & vFlake, +static void enumerateOutputs(EvalState & state, Value & vFlake, std::function callback) { state.forceAttrs(vFlake); - auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value; + auto vOutputs = (*vFlake.attrs->get(state.symbols.create("outputs")))->value; - state.forceAttrs(*vProvides); + state.forceAttrs(*vOutputs); - for (auto & attr : *vProvides->attrs) + for (auto & attr : *vOutputs->attrs) callback(attr.name, *attr.value); } @@ -237,9 +237,9 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); - auto provides = nlohmann::json::object(); + auto outputs = nlohmann::json::object(); - enumerateProvides(*state, + enumerateOutputs(*state, *vFlake, [&](const std::string & name, Value & vProvide) { auto provide = nlohmann::json::object(); @@ -250,10 +250,10 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON provide[aCheck.name] = nlohmann::json::object(); } - provides[name] = provide; + outputs[name] = provide; }); - json["provides"] = std::move(provides); + json["outputs"] = std::move(outputs); std::cout << json.dump() << std::endl; } else @@ -298,7 +298,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON // FIXME: check meta attributes return drvInfo->queryDrvPath(); } catch (Error & e) { - e.addPrefix(fmt("while checking flake attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + e.addPrefix(fmt("while checking flake output attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); throw; } }; @@ -311,7 +311,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); - enumerateProvides(*state, + enumerateOutputs(*state, *vFlake, [&](const std::string & name, Value & vProvide) { Activity act(*logger, lvlChatty, actUnknown, diff --git a/src/nix/installables.cc b/src/nix/installables.cc index ea12cd79c..fe89a6bb4 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -230,16 +230,16 @@ struct InstallableFlake : InstallableValue makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake); - auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value; + auto vOutputs = (*vFlake->attrs->get(state.symbols.create("outputs")))->value; - state.forceValue(*vProvides); + state.forceValue(*vOutputs); auto emptyArgs = state.allocBindings(0); if (searchPackages) { // As a convenience, look for the attribute in - // 'provides.packages'. - if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) { + // 'outputs.packages'. + if (auto aPackages = *vOutputs->attrs->get(state.symbols.create("packages"))) { try { auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); state.forceValue(*v); @@ -250,7 +250,7 @@ struct InstallableFlake : InstallableValue // As a temporary hack until Nixpkgs is properly converted // to provide a clean 'packages' set, look in 'legacyPackages'. - if (auto aPackages = *vProvides->attrs->get(state.symbols.create("legacyPackages"))) { + if (auto aPackages = *vOutputs->attrs->get(state.symbols.create("legacyPackages"))) { try { auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); state.forceValue(*v); @@ -260,10 +260,10 @@ struct InstallableFlake : InstallableValue } } - // Otherwise, look for it in 'provides'. + // Otherwise, look for it in 'outputs'. for (auto & attrPath : attrPaths) { try { - auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vProvides); + auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); state.forceValue(*v); return v; } catch (AttrPathNotFound & e) { -- cgit v1.2.3 From ccb1bad612e060fc4397d340edc64d18231744b6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 May 2019 20:53:23 +0200 Subject: Allow bare flakerefs as installables So now $ nix build blender-bin works and builds the default package from that flake. You don't need to add a colon at the end anymore. --- src/nix/installables.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index fe89a6bb4..40248eb5d 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -332,6 +332,10 @@ std::vector> SourceExprCommand::parseInstallables( getDefaultFlakeAttrPaths())); } + else if (auto flakeRef = parseFlakeRef(s, true)) + result.push_back(std::make_shared(*this, s, + getDefaultFlakeAttrPaths())); + else result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); } -- cgit v1.2.3 From 8cb3bbd5044b8fbfc65f13455d1619a78ccf33a5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 May 2019 22:17:39 +0200 Subject: Fix handling of bare flakerefs containing a colon --- src/nix/installables.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 40248eb5d..38ae416e3 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -314,6 +314,10 @@ std::vector> SourceExprCommand::parseInstallables( Strings{"packages." + std::string(s, 8)})); } + else if (auto flakeRef = parseFlakeRef(s, true)) + result.push_back(std::make_shared(*this, s, + getDefaultFlakeAttrPaths())); + else if ((colon = s.rfind(':')) != std::string::npos) { auto flakeRef = std::string(s, 0, colon); auto attrPath = std::string(s, colon + 1); @@ -332,10 +336,6 @@ std::vector> SourceExprCommand::parseInstallables( getDefaultFlakeAttrPaths())); } - else if (auto flakeRef = parseFlakeRef(s, true)) - result.push_back(std::make_shared(*this, s, - getDefaultFlakeAttrPaths())); - else result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); } -- cgit v1.2.3 From 15f241775ace2bbd807e7222e822bd5bf0f42ff7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 May 2019 23:21:53 +0200 Subject: Doh --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 38ae416e3..eb3c27d6b 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -315,7 +315,7 @@ std::vector> SourceExprCommand::parseInstallables( } else if (auto flakeRef = parseFlakeRef(s, true)) - result.push_back(std::make_shared(*this, s, + result.push_back(std::make_shared(*this, std::move(*flakeRef), getDefaultFlakeAttrPaths())); else if ((colon = s.rfind(':')) != std::string::npos) { -- cgit v1.2.3 From 5fbd9fee0b4b26cc7bcceb350e56e808c7a70e8c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 May 2019 23:45:13 +0200 Subject: Add 'nix app' command This is like 'nix run', except that the command to execute is defined in a flake output, e.g. defaultApp = { type = "app"; program = "${packages.blender_2_80}/bin/blender"; }; Thus you can do $ nix app blender-bin to start Blender from the 'blender-bin' flake. In the future, we can extend this with sandboxing. (For example we would want to be able to specify that Blender should not have network access by default and should only have access to certain paths in the user's home directory.) --- src/nix/command.hh | 9 +++++ src/nix/installables.cc | 22 +++++++++++ src/nix/run.cc | 101 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 109 insertions(+), 23 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 26c308331..659b724c3 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -38,6 +38,13 @@ struct Buildable typedef std::vector Buildables; +struct App +{ + PathSet context; + Path program; + // FIXME: add args, sandbox settings, metadata, ... +}; + struct Installable { virtual std::string what() = 0; @@ -49,6 +56,8 @@ struct Installable Buildable toBuildable(); + App toApp(EvalState & state); + virtual Value * toValue(EvalState & state) { throw Error("argument '%s' cannot be evaluated", what()); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index eb3c27d6b..b6f05b314 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -68,6 +68,28 @@ Buildable Installable::toBuildable() return std::move(buildables[0]); } +App Installable::toApp(EvalState & state) +{ + auto v = toValue(state); + + state.forceAttrs(*v); + + auto aType = v->attrs->need(state.sType); + if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app") + throw Error("value does not have type 'app', at %s", *aType.pos); + + App app; + + auto aProgram = v->attrs->need(state.symbols.create("program")); + app.program = state.forceString(*aProgram.value, app.context, *aProgram.pos); + + // FIXME: check that 'program' is in the closure of 'context'. + if (!state.store->isInStore(app.program)) + throw Error("app program '%s' is not in the Nix store", app.program); + + return app; +} + struct InstallableStorePath : Installable { Path storePath; diff --git a/src/nix/run.cc b/src/nix/run.cc index 35b763345..00a682832 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -8,6 +8,7 @@ #include "fs-accessor.hh" #include "progress-bar.hh" #include "affinity.hh" +#include "eval.hh" #if __linux__ #include @@ -19,7 +20,44 @@ using namespace nix; std::string chrootHelperName = "__run_in_chroot"; -struct CmdRun : InstallablesCommand +struct RunCommon : virtual Command +{ + void runProgram(ref store, + const std::string & program, + const Strings & args) + { + stopProgressBar(); + + restoreSignals(); + + restoreAffinity(); + + /* If this is a diverted store (i.e. its "logical" location + (typically /nix/store) differs from its "physical" location + (e.g. /home/eelco/nix/store), then run the command in a + chroot. For non-root users, this requires running it in new + mount and user namespaces. Unfortunately, + unshare(CLONE_NEWUSER) doesn't work in a multithreaded + program (which "nix" is), so we exec() a single-threaded + helper program (chrootHelper() below) to do the work. */ + auto store2 = store.dynamic_pointer_cast(); + + if (store2 && store->storeDir != store2->realStoreDir) { + Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, program }; + for (auto & arg : args) helperArgs.push_back(arg); + + execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data()); + + throw SysError("could not execute chroot helper"); + } + + execvp(program.c_str(), stringsToCharPtrs(args).data()); + + throw SysError("unable to execute '%s'", program); + } +}; + +struct CmdRun : InstallablesCommand, RunCommon { std::vector command = { "bash" }; StringSet keep, unset; @@ -147,42 +185,59 @@ struct CmdRun : InstallablesCommand setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); - std::string cmd = *command.begin(); Strings args; for (auto & arg : command) args.push_back(arg); - stopProgressBar(); + runProgram(store, *command.begin(), args); + } +}; - restoreSignals(); +static RegisterCommand r1(make_ref()); - restoreAffinity(); +struct CmdApp : InstallableCommand, RunCommon +{ + CmdApp() + { + } - /* If this is a diverted store (i.e. its "logical" location - (typically /nix/store) differs from its "physical" location - (e.g. /home/eelco/nix/store), then run the command in a - chroot. For non-root users, this requires running it in new - mount and user namespaces. Unfortunately, - unshare(CLONE_NEWUSER) doesn't work in a multithreaded - program (which "nix" is), so we exec() a single-threaded - helper program (chrootHelper() below) to do the work. */ - auto store2 = store.dynamic_pointer_cast(); + std::string name() override + { + return "app"; + } - if (store2 && store->storeDir != store2->realStoreDir) { - Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, cmd }; - for (auto & arg : args) helperArgs.push_back(arg); + std::string description() override + { + return "run a Nix application"; + } - execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data()); + Examples examples() override + { + return { + Example{ + "To run Blender:", + "nix app blender-bin" + }, + }; + } - throw SysError("could not execute chroot helper"); - } + Strings getDefaultFlakeAttrPaths() override + { + return {"defaultApp"}; + } - execvp(cmd.c_str(), stringsToCharPtrs(args).data()); + void run(ref store) override + { + auto state = getEvalState(); + + auto app = installable->toApp(*state); - throw SysError("unable to exec '%s'", cmd); + state->realiseContext(app.context); + + runProgram(store, app.program, {app.program}); } }; -static RegisterCommand r1(make_ref()); +static RegisterCommand r2(make_ref()); void chrootHelper(int argc, char * * argv) { -- cgit v1.2.3 From d9a6a75ed28c590dde2dba846e356cbcda38d977 Mon Sep 17 00:00:00 2001 From: Nick Van den Broeck Date: Mon, 3 Jun 2019 14:47:47 +0200 Subject: Made epochs more fine-grained Fixes #2894 --- src/nix/flake-template.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix index fe89e647e..bec613f6c 100644 --- a/src/nix/flake-template.nix +++ b/src/nix/flake-template.nix @@ -3,7 +3,7 @@ description = "A flake for building Hello World"; - epoch = 2019; + epoch = 201906; requires = [ "nixpkgs" ]; -- cgit v1.2.3 From 6dbd5c26e6c853f302cd9d3ed171d134ff24ffe1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2019 19:10:35 +0200 Subject: Make flake input fetching lazy As long as the flake input is locked, it is now only fetched when it is evaluated (e.g. "nixpkgs" is fetched when "inputs.nixpkgs." is evaluated). This required adding an "id" attribute to the members of "inputs" in lockfiles, e.g. "inputs": { "nixpkgs/release-19.03": { "id": "nixpkgs", "inputs": {}, "narHash": "sha256-eYtxncIMFVmOHaHBtTdPGcs/AnJqKqA6tHCm0UmPYQU=", "nonFlakeInputs": {}, "uri": "github:edolstra/nixpkgs/e9d5882bb861dc48f8d46960e7c820efdbe8f9c1" } } because the flake ID needs to be known beforehand to construct the "inputs" attrset. Fixes #2913. --- src/nix/flake.cc | 6 +++++- src/nix/installables.cc | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8d6716391..d229c7512 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -136,6 +136,7 @@ static nlohmann::json nonFlakeToJson(const NonFlake & nonFlake) return j; } +#if 0 // FIXME: merge info CmdFlakeInfo? struct CmdFlakeDeps : FlakeCommand { @@ -173,6 +174,7 @@ struct CmdFlakeDeps : FlakeCommand } } }; +#endif struct CmdFlakeUpdate : FlakeCommand { @@ -232,6 +234,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON if (json) { auto json = flakeToJson(flake); +#if 0 auto state = getEvalState(); auto vFlake = state->allocValue(); @@ -254,6 +257,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON }); json["outputs"] = std::move(outputs); +#endif std::cout << json.dump() << std::endl; } else @@ -518,7 +522,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command , make_ref() , make_ref() , make_ref() - , make_ref() + //, make_ref() , make_ref() , make_ref() , make_ref() diff --git a/src/nix/installables.cc b/src/nix/installables.cc index b6f05b314..86b4a9b93 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -190,6 +190,7 @@ void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const flake::ResolvedFlake & resFlake) { +#if 0 if (std::get_if(&origFlakeRef.data)) return; /* Get the store paths of all non-local flakes. */ @@ -224,6 +225,7 @@ void makeFlakeClosureGCRoot(Store & store, debug("writing GC root '%s' for flake closure of '%s'", symlink, origFlakeRef); replaceSymlink(closurePath, symlink); store.addIndirectRoot(symlink); +#endif } struct InstallableFlake : InstallableValue -- cgit v1.2.3 From 278114d559109199ff8e6f23b6700ab7909f5320 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2019 19:45:16 +0200 Subject: Fix GC closure generation --- src/nix/installables.cc | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 86b4a9b93..c44a37f1e 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -190,22 +190,28 @@ void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const flake::ResolvedFlake & resFlake) { -#if 0 if (std::get_if(&origFlakeRef.data)) return; /* Get the store paths of all non-local flakes. */ PathSet closure; - std::queue> queue; - queue.push(resFlake); + assert(store.isValidPath(resFlake.flake.sourceInfo.storePath)); + closure.insert(resFlake.flake.sourceInfo.storePath); + + std::queue> queue; + queue.push(resFlake.lockFile); while (!queue.empty()) { - const flake::ResolvedFlake & flake = queue.front(); + const flake::FlakeInputs & flake = queue.front(); queue.pop(); - if (!std::get_if(&flake.flake.sourceInfo.resolvedRef.data)) - closure.insert(flake.flake.sourceInfo.storePath); - for (const auto & dep : flake.flakeDeps) + /* Note: due to lazy fetching, these paths might not exist + yet. */ + for (auto & dep : flake.flakeDeps) { + closure.insert(dep.second.computeStorePath(store)); queue.push(dep.second); + } + for (auto & dep : flake.nonFlakeDeps) + closure.insert(dep.second.computeStorePath(store)); } if (closure.empty()) return; @@ -225,7 +231,6 @@ void makeFlakeClosureGCRoot(Store & store, debug("writing GC root '%s' for flake closure of '%s'", symlink, origFlakeRef); replaceSymlink(closurePath, symlink); store.addIndirectRoot(symlink); -#endif } struct InstallableFlake : InstallableValue -- cgit v1.2.3 From 5fe7be2409966d673d59d049c3fc6e7710d03b53 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2019 20:08:13 +0200 Subject: Rename dep -> input Also use nlohmann::json range-based for. --- src/nix/installables.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index c44a37f1e..e7549b57c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -206,11 +206,11 @@ void makeFlakeClosureGCRoot(Store & store, queue.pop(); /* Note: due to lazy fetching, these paths might not exist yet. */ - for (auto & dep : flake.flakeDeps) { + for (auto & dep : flake.flakeInputs) { closure.insert(dep.second.computeStorePath(store)); queue.push(dep.second); } - for (auto & dep : flake.nonFlakeDeps) + for (auto & dep : flake.nonFlakeInputs) closure.insert(dep.second.computeStorePath(store)); } -- cgit v1.2.3 From 45b5c606ac44550de14562df4fa99773a81a1015 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2019 20:34:08 +0200 Subject: Don't register invalid paths as GC roots Unfortunately this doesn't work. Maybe we should keep separate roots for each path. --- src/nix/installables.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index e7549b57c..ca88ec0da 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -207,11 +207,16 @@ void makeFlakeClosureGCRoot(Store & store, /* Note: due to lazy fetching, these paths might not exist yet. */ for (auto & dep : flake.flakeInputs) { - closure.insert(dep.second.computeStorePath(store)); + auto path = dep.second.computeStorePath(store); + if (store.isValidPath(path)) + closure.insert(path); queue.push(dep.second); } - for (auto & dep : flake.nonFlakeInputs) - closure.insert(dep.second.computeStorePath(store)); + for (auto & dep : flake.nonFlakeInputs) { + auto path = dep.second.computeStorePath(store); + if (store.isValidPath(path)) + closure.insert(path); + } } if (closure.empty()) return; -- cgit v1.2.3 From 1e53a07712fba830eb3967cc16894992d5a33922 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2019 20:56:13 +0200 Subject: Make non-flake inputs lazy Also add a proper test for non-flake inputs. --- src/nix/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d229c7512..653154aaa 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -122,6 +122,7 @@ static nlohmann::json flakeToJson(const Flake & flake) return j; } +#if 0 static void printNonFlakeInfo(const NonFlake & nonFlake) { std::cout << fmt("ID: %s\n", nonFlake.alias); @@ -136,7 +137,6 @@ static nlohmann::json nonFlakeToJson(const NonFlake & nonFlake) return j; } -#if 0 // FIXME: merge info CmdFlakeInfo? struct CmdFlakeDeps : FlakeCommand { -- cgit v1.2.3 From 54aff8430c4e7739903f6dbed713cc088e38507f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2019 16:51:54 +0200 Subject: Move flake-related stuff to src/libexpr/flake --- src/nix/flake.cc | 2 +- src/nix/installables.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 653154aaa..af1a361b3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -4,7 +4,7 @@ #include "progress-bar.hh" #include "eval.hh" #include "eval-inline.hh" -#include "primops/flake.hh" +#include "flake/flake.hh" #include "get-drvs.hh" #include "store-api.hh" diff --git a/src/nix/installables.cc b/src/nix/installables.cc index ca88ec0da..a85295a09 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -7,7 +7,7 @@ #include "get-drvs.hh" #include "store-api.hh" #include "shared.hh" -#include "primops/flake.hh" +#include "flake/flake.hh" #include #include -- cgit v1.2.3 From 6644b6099be2d3393206bf1c9c091c888c0a0f57 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 Jun 2019 22:25:48 +0200 Subject: Add flake evaluation cache This exploits the hermetic nature of flake evaluation to speed up repeated evaluations of a flake output attribute. For example (doing 'nix build' on an already present package): $ time nix build nixpkgs:firefox real 0m1.497s user 0m1.160s sys 0m0.139s $ time nix build nixpkgs:firefox real 0m0.052s user 0m0.038s sys 0m0.007s The cache is ~/.cache/nix/eval-cache-v1.sqlite, which has entries like INSERT INTO Attributes VALUES( X'92a907d4efe933af2a46959b082cdff176aa5bfeb47a98fabd234809a67ab195', 'packages.firefox', 1, '/nix/store/pbalzf8x19hckr8cwdv62rd6g0lqgc38-firefox-67.0.drv /nix/store/g6q0gx0v6xvdnizp8lrcw7c4gdkzana0-firefox-67.0 out'); where the hash 92a9... is a fingerprint over the flake store path and the contents of the lockfile. Because flakes are evaluated in pure mode, this uniquely identifies the evaluation result. --- src/nix/installables.cc | 130 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 32 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index a85295a09..86e601bc4 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -8,6 +8,7 @@ #include "store-api.hh" #include "shared.hh" #include "flake/flake.hh" +#include "flake/eval-cache.hh" #include #include @@ -110,7 +111,7 @@ struct InstallableValue : Installable InstallableValue(SourceExprCommand & cmd) : cmd(cmd) { } - Buildables toBuildables() override + virtual std::vector toDerivations() { auto state = cmd.getEvalState(); @@ -118,22 +119,36 @@ struct InstallableValue : Installable Bindings & autoArgs = *cmd.getAutoArgs(*state); - DrvInfos drvs; - getDerivations(*state, *v, "", autoArgs, drvs, false); + DrvInfos drvInfos; + getDerivations(*state, *v, "", autoArgs, drvInfos, false); + + std::vector res; + for (auto & drvInfo : drvInfos) { + res.push_back({ + drvInfo.queryDrvPath(), + drvInfo.queryOutPath(), + drvInfo.queryOutputName() + }); + } + + return res; + } + Buildables toBuildables() override + { Buildables res; PathSet drvPaths; - for (auto & drv : drvs) { - Buildable b{drv.queryDrvPath()}; + for (auto & drv : toDerivations()) { + Buildable b{drv.drvPath}; drvPaths.insert(b.drvPath); - auto outputName = drv.queryOutputName(); + auto outputName = drv.outputName; if (outputName == "") throw Error("derivation '%s' lacks an 'outputName' attribute", b.drvPath); - b.outputs.emplace(outputName, drv.queryOutPath()); + b.outputs.emplace(outputName, drv.outPath); res.push_back(std::move(b)); } @@ -254,11 +269,29 @@ struct InstallableFlake : InstallableValue std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } - Value * toValue(EvalState & state) override + std::vector getActualAttrPaths() { - auto vFlake = state.allocValue(); + std::vector res; - auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); + if (searchPackages) { + // As a convenience, look for the attribute in + // 'outputs.packages'. + res.push_back("packages." + *attrPaths.begin()); + + // As a temporary hack until Nixpkgs is properly converted + // to provide a clean 'packages' set, look in 'legacyPackages'. + res.push_back("legacyPackages." + *attrPaths.begin()); + } + + for (auto & s : attrPaths) + res.push_back(s); + + return res; + } + + Value * getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake) + { + auto vFlake = state.allocValue(); callFlake(state, resFlake, *vFlake); @@ -268,34 +301,67 @@ struct InstallableFlake : InstallableValue state.forceValue(*vOutputs); - auto emptyArgs = state.allocBindings(0); + return vOutputs; + } - if (searchPackages) { - // As a convenience, look for the attribute in - // 'outputs.packages'. - if (auto aPackages = *vOutputs->attrs->get(state.symbols.create("packages"))) { - try { - auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); - state.forceValue(*v); - return v; - } catch (AttrPathNotFound & e) { - } + std::vector toDerivations() override + { + auto state = cmd.getEvalState(); + + auto resFlake = resolveFlake(*state, flakeRef, cmd.getLockFileMode()); + + Value * vOutputs = nullptr; + + auto emptyArgs = state->allocBindings(0); + + auto & evalCache = flake::EvalCache::singleton(); + + auto fingerprint = resFlake.getFingerprint(); + + for (auto & attrPath : getActualAttrPaths()) { + auto drv = evalCache.getDerivation(fingerprint, attrPath); + if (drv) { + if (state->store->isValidPath(drv->drvPath)) + return {*drv}; } - // As a temporary hack until Nixpkgs is properly converted - // to provide a clean 'packages' set, look in 'legacyPackages'. - if (auto aPackages = *vOutputs->attrs->get(state.symbols.create("legacyPackages"))) { - try { - auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value); - state.forceValue(*v); - return v; - } catch (AttrPathNotFound & e) { - } + if (!vOutputs) + vOutputs = getFlakeOutputs(*state, resFlake); + + try { + auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs); + state->forceValue(*v); + + auto drvInfo = getDerivation(*state, *v, false); + if (!drvInfo) + throw Error("flake output attribute '%s' is not a derivation", attrPath); + + auto drv = flake::EvalCache::Derivation{ + drvInfo->queryDrvPath(), + drvInfo->queryOutPath(), + drvInfo->queryOutputName() + }; + + evalCache.addDerivation(fingerprint, attrPath, drv); + + return {drv}; + } catch (AttrPathNotFound & e) { } } - // Otherwise, look for it in 'outputs'. - for (auto & attrPath : attrPaths) { + throw Error("flake '%s' does not provide attribute %s", + flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); + } + + Value * toValue(EvalState & state) override + { + auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); + + auto vOutputs = getFlakeOutputs(state, resFlake); + + auto emptyArgs = state.allocBindings(0); + + for (auto & attrPath : getActualAttrPaths()) { try { auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); state.forceValue(*v); -- cgit v1.2.3 From 415fc233e3f01a3bb3cfac01deb9f4ca4af7fb19 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 13 Jun 2019 14:07:25 +0200 Subject: For nixpkgs., use legacyPackages This makes commands like 'nix run nixpkgs.chromium' work again. --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 86e601bc4..1c7debf4e 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -411,7 +411,7 @@ std::vector> SourceExprCommand::parseInstallables( bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), - Strings{"packages." + std::string(s, 8)})); + Strings{"legacyPackages." + std::string(s, 8)})); } else if (auto flakeRef = parseFlakeRef(s, true)) -- cgit v1.2.3 From 615a9d031d22a6aee64f8511e15685e47b6f8796 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 09:12:03 +0200 Subject: Add "warning" verbosity level This ensures that "nix" shows warnings. Previously these were hidden because they were at "info" level. --- src/nix/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index a1fcb892a..ced87f653 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -102,7 +102,7 @@ void mainWrapped(int argc, char * * argv) if (legacy) return legacy(argc, argv); } - verbosity = lvlError; + verbosity = lvlWarn; settings.verboseBuild = false; evalSettings.pureEval = true; -- cgit v1.2.3 From 04a59769963fe2a28d10ba15de743fe499333c80 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 09:57:22 +0200 Subject: Automatically use --no-net if there are no network interfaces --- src/nix/main.cc | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index ced87f653..101ed5d5c 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -8,19 +8,52 @@ #include "shared.hh" #include "store-api.hh" #include "progress-bar.hh" +#include "download.hh" #include "finally.hh" +#include +#include +#include +#include + extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); namespace nix { +/* Check if we have a non-loopback/link-local network interface. */ +static bool haveInternet() +{ + struct ifaddrs * addrs; + + if (getifaddrs(&addrs)) + return true; + + Finally free([&]() { freeifaddrs(addrs); }); + + for (auto i = addrs; i; i = i->ifa_next) { + if (!i->ifa_addr) continue; + if (i->ifa_addr->sa_family == AF_INET) { + if (ntohl(((sockaddr_in *) i->ifa_addr)->sin_addr.s_addr) != INADDR_LOOPBACK) { + return true; + } + } else if (i->ifa_addr->sa_family == AF_INET6) { + if (!IN6_IS_ADDR_LOOPBACK(((sockaddr_in6 *) i->ifa_addr)->sin6_addr.s6_addr) && + !IN6_IS_ADDR_LINKLOCAL(((sockaddr_in6 *) i->ifa_addr)->sin6_addr.s6_addr)) + return true; + } + } + + return false; +} + std::string programPath; struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { bool printBuildLogs = false; + bool useNet = true; NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix") { @@ -52,6 +85,11 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs .longName("version") .description("show version information") .handler([&]() { printVersion(programName); }); + + mkFlag() + .longName("no-net") + .description("disable substituters and consider all previously downloaded files up-to-date") + .handler([&]() { useNet = false; }); } void printFlags(std::ostream & out) override @@ -118,6 +156,23 @@ void mainWrapped(int argc, char * * argv) startProgressBar(args.printBuildLogs); + if (args.useNet && !haveInternet()) { + warn("you don't have Internet access; disabling some network-dependent features"); + args.useNet = false; + } + + if (!args.useNet) { + // FIXME: should check for command line overrides only. + if (!settings.useSubstitutes.overriden) + settings.useSubstitutes = false; + if (!settings.tarballTtl.overriden) + settings.tarballTtl = std::numeric_limits::max(); + if (!downloadSettings.tries.overriden) + downloadSettings.tries = 0; + if (!downloadSettings.connectTimeout.overriden) + downloadSettings.connectTimeout = 1; + } + args.command->prepare(); args.command->run(); } -- cgit v1.2.3 From 2467c9837500b26aab5c1dcd3cac12cda44898ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 16:58:59 +0200 Subject: nix app: Search for installable in the 'apps' output I.e. you can write $ nix app blender-bin:blender_2_80 which is equivalent to $ nix app blender-bin:apps.blender_2_80 --- src/nix/command.hh | 12 ++++++++++++ src/nix/installables.cc | 27 +++++++++++++-------------- src/nix/run.cc | 5 +++++ 3 files changed, 30 insertions(+), 14 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 659b724c3..3dad64947 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -102,6 +102,18 @@ struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions { return {"defaultPackage"}; } + + virtual Strings getDefaultFlakeAttrPathPrefixes() + { + return { + // As a convenience, look for the attribute in + // 'outputs.packages'. + "packages.", + // As a temporary hack until Nixpkgs is properly converted + // to provide a clean 'packages' set, look in 'legacyPackages'. + "legacyPackages." + }; + } }; enum RealiseMode { Build, NoBuild, DryRun }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 1c7debf4e..d7dd95606 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -257,14 +257,16 @@ struct InstallableFlake : InstallableValue { FlakeRef flakeRef; Strings attrPaths; - bool searchPackages = false; + Strings prefixes; InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, Strings attrPaths) : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(std::move(attrPaths)) { } - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, std::string attrPath) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, searchPackages(true) + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, + std::string attrPath, Strings && prefixes) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, + prefixes(prefixes) { } std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } @@ -273,15 +275,8 @@ struct InstallableFlake : InstallableValue { std::vector res; - if (searchPackages) { - // As a convenience, look for the attribute in - // 'outputs.packages'. - res.push_back("packages." + *attrPaths.begin()); - - // As a temporary hack until Nixpkgs is properly converted - // to provide a clean 'packages' set, look in 'legacyPackages'. - res.push_back("legacyPackages." + *attrPaths.begin()); - } + for (auto & prefix : prefixes) + res.push_back(prefix + *attrPaths.begin()); for (auto & s : attrPaths) res.push_back(s); @@ -421,7 +416,11 @@ std::vector> SourceExprCommand::parseInstallables( else if ((colon = s.rfind(':')) != std::string::npos) { auto flakeRef = std::string(s, 0, colon); auto attrPath = std::string(s, colon + 1); - result.push_back(std::make_shared(*this, FlakeRef(flakeRef, true), attrPath)); + result.push_back(std::make_shared( + *this, + FlakeRef(flakeRef, true), + attrPath, + getDefaultFlakeAttrPathPrefixes())); } else if (s.find('/') != std::string::npos || s == ".") { @@ -437,7 +436,7 @@ std::vector> SourceExprCommand::parseInstallables( } else - result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); + throw Error("unsupported argument '%s'", s); } } diff --git a/src/nix/run.cc b/src/nix/run.cc index 00a682832..62aae12f6 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -225,6 +225,11 @@ struct CmdApp : InstallableCommand, RunCommon return {"defaultApp"}; } + Strings getDefaultFlakeAttrPathPrefixes() override + { + return {"apps."}; + } + void run(ref store) override { auto state = getEvalState(); -- cgit v1.2.3 From 0d69f7f3f012aceb4c494f3c1cc866b378c5eac1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 17:05:37 +0200 Subject: nix app: Accept arguments Example: $ nix app blender-bin -- --version Blender 2.80 (sub 74) --- src/nix/run.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/run.cc b/src/nix/run.cc index 62aae12f6..d30851d47 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -196,8 +196,11 @@ static RegisterCommand r1(make_ref()); struct CmdApp : InstallableCommand, RunCommon { + std::vector args; + CmdApp() { + expectArgs("args", &args); } std::string name() override @@ -238,7 +241,10 @@ struct CmdApp : InstallableCommand, RunCommon state->realiseContext(app.context); - runProgram(store, app.program, {app.program}); + Strings allArgs{app.program}; + for (auto & i : args) allArgs.push_back(i); + + runProgram(store, app.program, allArgs); } }; -- cgit v1.2.3 From 3b2ebd029cac0b1ce715d8aca44204130c93a869 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 17:31:34 +0200 Subject: nix flake info --json: Revive enumerating the outputs --- src/nix/flake.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index af1a361b3..6f6d3c130 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -234,8 +234,8 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON if (json) { auto json = flakeToJson(flake); -#if 0 auto state = getEvalState(); + auto flake = resolveFlake(); auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); @@ -257,7 +257,6 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON }); json["outputs"] = std::move(outputs); -#endif std::cout << json.dump() << std::endl; } else -- cgit v1.2.3 From 9d1207c02c091a00454fcb7266653d18a6023923 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 17:59:57 +0200 Subject: nix flake check: Check apps --- src/nix/command.hh | 2 ++ src/nix/flake.cc | 29 +++++++++++++++++++++++++++-- src/nix/installables.cc | 23 +++++++++++------------ 3 files changed, 40 insertions(+), 14 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 3dad64947..0ffbe46f5 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -43,6 +43,8 @@ struct App PathSet context; Path program; // FIXME: add args, sandbox settings, metadata, ... + + App(EvalState & state, Value & vApp); }; struct Installable diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6f6d3c130..6f6d1a0aa 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -7,6 +7,7 @@ #include "flake/flake.hh" #include "get-drvs.hh" #include "store-api.hh" +#include "derivations.hh" #include #include @@ -301,13 +302,27 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON // FIXME: check meta attributes return drvInfo->queryDrvPath(); } catch (Error & e) { - e.addPrefix(fmt("while checking flake output attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); throw; } }; PathSet drvPaths; + auto checkApp = [&](const std::string & attrPath, Value & v) { + try { + auto app = App(*state, v); + for (auto & i : app.context) { + auto [drvPath, outputName] = decodeContext(i); + if (!outputName.empty() && nix::isDerivation(drvPath)) + drvPaths.insert(drvPath + "!" + outputName); + } + } catch (Error & e) { + e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + throw; + } + }; + { Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); @@ -337,9 +352,19 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON name + "." + (std::string) aCheck.name, *aCheck.value); } + else if (name == "apps") { + state->forceAttrs(vProvide); + for (auto & aCheck : *vProvide.attrs) + checkApp( + name + "." + (std::string) aCheck.name, *aCheck.value); + } + else if (name == "defaultPackage" || name == "devShell") checkDerivation(name, vProvide); + else if (name == "defaultApp") + checkApp(name, vProvide); + } catch (Error & e) { e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name)); throw; @@ -347,7 +372,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON }); } - if (build) { + if (build && !drvPaths.empty()) { Activity act(*logger, lvlInfo, actUnknown, "running flake checks"); store->buildPaths(drvPaths); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index d7dd95606..feaf57f0c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -69,26 +69,25 @@ Buildable Installable::toBuildable() return std::move(buildables[0]); } -App Installable::toApp(EvalState & state) +App::App(EvalState & state, Value & vApp) { - auto v = toValue(state); - - state.forceAttrs(*v); + state.forceAttrs(vApp); - auto aType = v->attrs->need(state.sType); + auto aType = vApp.attrs->need(state.sType); if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app") throw Error("value does not have type 'app', at %s", *aType.pos); - App app; - - auto aProgram = v->attrs->need(state.symbols.create("program")); - app.program = state.forceString(*aProgram.value, app.context, *aProgram.pos); + auto aProgram = vApp.attrs->need(state.symbols.create("program")); + program = state.forceString(*aProgram.value, context, *aProgram.pos); // FIXME: check that 'program' is in the closure of 'context'. - if (!state.store->isInStore(app.program)) - throw Error("app program '%s' is not in the Nix store", app.program); + if (!state.store->isInStore(program)) + throw Error("app program '%s' is not in the Nix store", program); +} - return app; +App Installable::toApp(EvalState & state) +{ + return App(state, *toValue(state)); } struct InstallableStorePath : Installable -- cgit v1.2.3 From f2fcc163fa6c38566f3a78cd3757ab22f39821fc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 18:05:32 +0200 Subject: nix flake check: Warn about unknown flake outputs --- src/nix/flake.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6f6d1a0aa..8248a5d7b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -365,6 +365,9 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON else if (name == "defaultApp") checkApp(name, vProvide); + else + warn("unknown flake output '%s'", name); + } catch (Error & e) { e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name)); throw; -- cgit v1.2.3 From 556f33422d0a064ef100fe232f60b42b34bf40e2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 18 Jun 2019 09:45:14 +0200 Subject: nix flake check: Ignore legacyPackages --- src/nix/flake.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8248a5d7b..b98cce078 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -365,6 +365,10 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON else if (name == "defaultApp") checkApp(name, vProvide); + else if (name == "legacyPackages") + // FIXME: do getDerivations? + ; + else warn("unknown flake output '%s'", name); -- cgit v1.2.3 From 8a6704d826578d5640d3fca347308298020a6fde Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 26 May 2019 15:59:50 +0200 Subject: Updated documentation --- src/nix/flake.cc | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8d6716391..b673ca73e 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -128,14 +128,6 @@ static void printNonFlakeInfo(const NonFlake & nonFlake) printSourceInfo(nonFlake.sourceInfo); } -static nlohmann::json nonFlakeToJson(const NonFlake & nonFlake) -{ - nlohmann::json j; - j["id"] = nonFlake.alias; - sourceInfoToJson(nonFlake.sourceInfo, j); - return j; -} - // FIXME: merge info CmdFlakeInfo? struct CmdFlakeDeps : FlakeCommand { -- cgit v1.2.3 From a0de58f471c9087d8e6cc60a6078f9940a125b15 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 18 Jun 2019 16:01:35 +0200 Subject: Make subcommand construction in MultiCommand lazy --- src/nix/add-to-store.cc | 7 +--- src/nix/build.cc | 7 +--- src/nix/cat.cc | 14 ++------ src/nix/command.cc | 2 +- src/nix/command.hh | 15 ++++++--- src/nix/copy.cc | 7 +--- src/nix/doctor.cc | 7 +--- src/nix/dump-path.cc | 7 +--- src/nix/edit.cc | 7 +--- src/nix/eval.cc | 7 +--- src/nix/flake.cc | 79 +++++++--------------------------------------- src/nix/hash.cc | 26 ++++----------- src/nix/log.cc | 11 +------ src/nix/ls.cc | 14 ++------ src/nix/optimise-store.cc | 11 +------ src/nix/path-info.cc | 7 +--- src/nix/ping-store.cc | 7 +--- src/nix/repl.cc | 4 +-- src/nix/run.cc | 14 ++------ src/nix/search.cc | 7 +--- src/nix/shell.cc | 16 ++-------- src/nix/show-config.cc | 11 +------ src/nix/show-derivation.cc | 7 +--- src/nix/sigs.cc | 14 ++------ src/nix/upgrade-nix.cc | 7 +--- src/nix/verify.cc | 7 +--- src/nix/why-depends.cc | 7 +--- 27 files changed, 58 insertions(+), 271 deletions(-) (limited to 'src/nix') diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index e86b96e3f..296b2c7e4 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -22,11 +22,6 @@ struct CmdAddToStore : MixDryRun, StoreCommand .dest(&namePart); } - std::string name() override - { - return "add-to-store"; - } - std::string description() override { return "add a path to the Nix store"; @@ -58,4 +53,4 @@ struct CmdAddToStore : MixDryRun, StoreCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("add-to-store"); diff --git a/src/nix/build.cc b/src/nix/build.cc index c08ec0e62..d8ce8cc80 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -25,11 +25,6 @@ struct CmdBuild : MixDryRun, InstallablesCommand .set(&outLink, Path("")); } - std::string name() override - { - return "build"; - } - std::string description() override { return "build a derivation or fetch a store path"; @@ -72,4 +67,4 @@ struct CmdBuild : MixDryRun, InstallablesCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("build"); diff --git a/src/nix/cat.cc b/src/nix/cat.cc index a35f640d8..851f90abd 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -28,11 +28,6 @@ struct CmdCatStore : StoreCommand, MixCat expectArg("path", &path); } - std::string name() override - { - return "cat-store"; - } - std::string description() override { return "print the contents of a store file on stdout"; @@ -54,11 +49,6 @@ struct CmdCatNar : StoreCommand, MixCat expectArg("path", &path); } - std::string name() override - { - return "cat-nar"; - } - std::string description() override { return "print the contents of a file inside a NAR file"; @@ -70,5 +60,5 @@ struct CmdCatNar : StoreCommand, MixCat } }; -static RegisterCommand r1(make_ref()); -static RegisterCommand r2(make_ref()); +static auto r1 = registerCommand("cat-store"); +static auto r2 = registerCommand("cat-nar"); diff --git a/src/nix/command.cc b/src/nix/command.cc index 5967ab36c..89fa0cba4 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -4,7 +4,7 @@ namespace nix { -std::vector> * RegisterCommand::commands = 0; +Commands * RegisterCommand::commands = nullptr; StoreCommand::StoreCommand() { diff --git a/src/nix/command.hh b/src/nix/command.hh index 0ffbe46f5..59c6f8578 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -190,15 +190,22 @@ struct StorePathCommand : public InstallablesCommand /* A helper class for registering commands globally. */ struct RegisterCommand { - static std::vector> * commands; + static Commands * commands; - RegisterCommand(ref command) + RegisterCommand(const std::string & name, + std::function()> command) { - if (!commands) commands = new std::vector>; - commands->push_back(command); + if (!commands) commands = new Commands; + commands->emplace(name, command); } }; +template +static RegisterCommand registerCommand(const std::string & name) +{ + return RegisterCommand(name, [](){ return make_ref(); }); +} + Buildables build(ref store, RealiseMode mode, std::vector> installables); diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 96bd453d8..5f9051f40 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -42,11 +42,6 @@ struct CmdCopy : StorePathsCommand .set(&substitute, Substitute); } - std::string name() override - { - return "copy"; - } - std::string description() override { return "copy paths between Nix stores"; @@ -97,4 +92,4 @@ struct CmdCopy : StorePathsCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("copy"); diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc index 7b5444619..f2cf04758 100644 --- a/src/nix/doctor.cc +++ b/src/nix/doctor.cc @@ -20,11 +20,6 @@ struct CmdDoctor : StoreCommand { bool success = true; - std::string name() override - { - return "doctor"; - } - std::string description() override { return "check your system for potential problems"; @@ -121,4 +116,4 @@ struct CmdDoctor : StoreCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("doctore"); diff --git a/src/nix/dump-path.cc b/src/nix/dump-path.cc index f411c0cb7..90f1552d9 100644 --- a/src/nix/dump-path.cc +++ b/src/nix/dump-path.cc @@ -5,11 +5,6 @@ using namespace nix; struct CmdDumpPath : StorePathCommand { - std::string name() override - { - return "dump-path"; - } - std::string description() override { return "dump a store path to stdout (in NAR format)"; @@ -33,4 +28,4 @@ struct CmdDumpPath : StorePathCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("dump-path"); diff --git a/src/nix/edit.cc b/src/nix/edit.cc index c9671f76d..c62b35c46 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -10,11 +10,6 @@ using namespace nix; struct CmdEdit : InstallableCommand { - std::string name() override - { - return "edit"; - } - std::string description() override { return "open the Nix expression of a Nix package in $EDITOR"; @@ -78,4 +73,4 @@ struct CmdEdit : InstallableCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("edit"); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index b7058361c..524bac304 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -18,11 +18,6 @@ struct CmdEval : MixJSON, InstallableCommand mkFlag(0, "raw", "print strings unquoted", &raw); } - std::string name() override - { - return "eval"; - } - std::string description() override { return "evaluate a Nix expression"; @@ -74,4 +69,4 @@ struct CmdEval : MixJSON, InstallableCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("eval"); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6fc56827f..91c6b4276 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -49,11 +49,6 @@ public: struct CmdFlakeList : EvalCommand { - std::string name() override - { - return "list"; - } - std::string description() override { return "list available Nix flakes"; @@ -133,11 +128,6 @@ static void printNonFlakeInfo(const NonFlake & nonFlake) // FIXME: merge info CmdFlakeInfo? struct CmdFlakeDeps : FlakeCommand { - std::string name() override - { - return "deps"; - } - std::string description() override { return "list informaton about dependencies"; @@ -171,11 +161,6 @@ struct CmdFlakeDeps : FlakeCommand struct CmdFlakeUpdate : FlakeCommand { - std::string name() override - { - return "update"; - } - std::string description() override { return "update flake lock file"; @@ -209,11 +194,6 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, struct CmdFlakeInfo : FlakeCommand, MixJSON { - std::string name() override - { - return "info"; - } - std::string description() override { return "list info about a given flake"; @@ -269,11 +249,6 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON .set(&build, false); } - std::string name() override - { - return "check"; - } - std::string description() override { return "check whether the flake evaluates and run its tests"; @@ -383,11 +358,6 @@ struct CmdFlakeAdd : MixEvalArgs, Command FlakeUri alias; FlakeUri uri; - std::string name() override - { - return "add"; - } - std::string description() override { return "upsert flake in user flake registry"; @@ -414,11 +384,6 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command { FlakeUri alias; - std::string name() override - { - return "remove"; - } - std::string description() override { return "remove flake from user flake registry"; @@ -442,11 +407,6 @@ struct CmdFlakePin : virtual Args, EvalCommand { FlakeUri alias; - std::string name() override - { - return "pin"; - } - std::string description() override { return "pin flake require in user flake registry"; @@ -482,11 +442,6 @@ struct CmdFlakePin : virtual Args, EvalCommand struct CmdFlakeInit : virtual Args, Command { - std::string name() override - { - return "init"; - } - std::string description() override { return "create a skeleton 'flake.nix' file in the current directory"; @@ -514,11 +469,6 @@ struct CmdFlakeClone : FlakeCommand { Path destDir; - std::string name() override - { - return "clone"; - } - std::string description() override { return "clone flake repository"; @@ -541,23 +491,18 @@ struct CmdFlakeClone : FlakeCommand struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() - : MultiCommand({make_ref() - , make_ref() - , make_ref() - , make_ref() - //, make_ref() - , make_ref() - , make_ref() - , make_ref() - , make_ref() - , make_ref() - }) - { - } - - std::string name() override + : MultiCommand({ + {"list", []() { return make_ref(); }}, + {"update", []() { return make_ref(); }}, + {"info", []() { return make_ref(); }}, + {"check", []() { return make_ref(); }}, + {"add", []() { return make_ref(); }}, + {"remove", []() { return make_ref(); }}, + {"pin", []() { return make_ref(); }}, + {"init", []() { return make_ref(); }}, + {"clone", []() { return make_ref(); }}, + }) { - return "flake"; } std::string description() override @@ -578,4 +523,4 @@ struct CmdFlake : virtual MultiCommand, virtual Command } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("flake"); diff --git a/src/nix/hash.cc b/src/nix/hash.cc index af4105e28..1b3ba729e 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -26,11 +26,6 @@ struct CmdHash : Command expectArgs("paths", &paths); } - std::string name() override - { - return mode == mFile ? "hash-file" : "hash-path"; - } - std::string description() override { return mode == mFile @@ -49,8 +44,8 @@ struct CmdHash : Command } }; -static RegisterCommand r1(make_ref(CmdHash::mFile)); -static RegisterCommand r2(make_ref(CmdHash::mPath)); +static RegisterCommand r1("hash-file", [](){ return make_ref(CmdHash::mFile); }); +static RegisterCommand r2("hash-path", [](){ return make_ref(CmdHash::mPath); }); struct CmdToBase : Command { @@ -66,15 +61,6 @@ struct CmdToBase : Command expectArgs("strings", &args); } - std::string name() override - { - return - base == Base16 ? "to-base16" : - base == Base32 ? "to-base32" : - base == Base64 ? "to-base64" : - "to-sri"; - } - std::string description() override { return fmt("convert a hash to %s representation", @@ -91,10 +77,10 @@ struct CmdToBase : Command } }; -static RegisterCommand r3(make_ref(Base16)); -static RegisterCommand r4(make_ref(Base32)); -static RegisterCommand r5(make_ref(Base64)); -static RegisterCommand r6(make_ref(SRI)); +static RegisterCommand r3("to-base16", [](){ return make_ref(Base16); }); +static RegisterCommand r4("to-base32", [](){ return make_ref(Base32); }); +static RegisterCommand r5("to-base64", [](){ return make_ref(Base64); }); +static RegisterCommand r6("to-sri", [](){ return make_ref(SRI); }); /* Legacy nix-hash command. */ static int compatNixHash(int argc, char * * argv) diff --git a/src/nix/log.cc b/src/nix/log.cc index f07ec4e93..122a3d690 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -8,15 +8,6 @@ using namespace nix; struct CmdLog : InstallableCommand { - CmdLog() - { - } - - std::string name() override - { - return "log"; - } - std::string description() override { return "show the build log of the specified packages or paths, if available"; @@ -68,4 +59,4 @@ struct CmdLog : InstallableCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("log"); diff --git a/src/nix/ls.cc b/src/nix/ls.cc index d089be42f..9408cc9da 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -100,11 +100,6 @@ struct CmdLsStore : StoreCommand, MixLs }; } - std::string name() override - { - return "ls-store"; - } - std::string description() override { return "show information about a store path"; @@ -136,11 +131,6 @@ struct CmdLsNar : Command, MixLs }; } - std::string name() override - { - return "ls-nar"; - } - std::string description() override { return "show information about the contents of a NAR file"; @@ -152,5 +142,5 @@ struct CmdLsNar : Command, MixLs } }; -static RegisterCommand r1(make_ref()); -static RegisterCommand r2(make_ref()); +static auto r1 = registerCommand("ls-store"); +static auto r2 = registerCommand("ls-nar"); diff --git a/src/nix/optimise-store.cc b/src/nix/optimise-store.cc index 725fb75a1..fed012b04 100644 --- a/src/nix/optimise-store.cc +++ b/src/nix/optimise-store.cc @@ -8,15 +8,6 @@ using namespace nix; struct CmdOptimiseStore : StoreCommand { - CmdOptimiseStore() - { - } - - std::string name() override - { - return "optimise-store"; - } - std::string description() override { return "replace identical files in the store by hard links"; @@ -38,4 +29,4 @@ struct CmdOptimiseStore : StoreCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("optimise-store"); diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index dea5f0557..2cb718f12 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -24,11 +24,6 @@ struct CmdPathInfo : StorePathsCommand, MixJSON mkFlag(0, "sigs", "show signatures", &showSigs); } - std::string name() override - { - return "path-info"; - } - std::string description() override { return "query information about store paths"; @@ -130,4 +125,4 @@ struct CmdPathInfo : StorePathsCommand, MixJSON } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("path-info"); diff --git a/src/nix/ping-store.cc b/src/nix/ping-store.cc index 310942574..3a2e542a3 100644 --- a/src/nix/ping-store.cc +++ b/src/nix/ping-store.cc @@ -6,11 +6,6 @@ using namespace nix; struct CmdPingStore : StoreCommand { - std::string name() override - { - return "ping-store"; - } - std::string description() override { return "test whether a store can be opened"; @@ -32,4 +27,4 @@ struct CmdPingStore : StoreCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("ping-store"); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index d8f812149..ce17a2b9f 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -764,8 +764,6 @@ struct CmdRepl : StoreCommand, MixEvalArgs expectArgs("files", &files); } - std::string name() override { return "repl"; } - std::string description() override { return "start an interactive environment for evaluating Nix expressions"; @@ -779,6 +777,6 @@ struct CmdRepl : StoreCommand, MixEvalArgs } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("repl"); } diff --git a/src/nix/run.cc b/src/nix/run.cc index d30851d47..9c15b6749 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -99,11 +99,6 @@ struct CmdRun : InstallablesCommand, RunCommon .handler([&](std::vector ss) { unset.insert(ss.front()); }); } - std::string name() override - { - return "run"; - } - std::string description() override { return "run a shell in which the specified packages are available"; @@ -192,7 +187,7 @@ struct CmdRun : InstallablesCommand, RunCommon } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("run"); struct CmdApp : InstallableCommand, RunCommon { @@ -203,11 +198,6 @@ struct CmdApp : InstallableCommand, RunCommon expectArgs("args", &args); } - std::string name() override - { - return "app"; - } - std::string description() override { return "run a Nix application"; @@ -248,7 +238,7 @@ struct CmdApp : InstallableCommand, RunCommon } }; -static RegisterCommand r2(make_ref()); +static auto r2 = registerCommand("app"); void chrootHelper(int argc, char * * argv) { diff --git a/src/nix/search.cc b/src/nix/search.cc index 55f8d106a..70de717d1 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -52,11 +52,6 @@ struct CmdSearch : SourceExprCommand, MixJSON .handler([&]() { writeCache = false; useCache = false; }); } - std::string name() override - { - return "search"; - } - std::string description() override { return "query available packages"; @@ -282,4 +277,4 @@ struct CmdSearch : SourceExprCommand, MixJSON } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("search"); diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 2ccad930f..f42947b7c 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -177,12 +177,6 @@ struct Common : InstallableCommand struct CmdDevShell : Common { - - std::string name() override - { - return "dev-shell"; - } - std::string description() override { return "run a bash shell that provides the build environment of a derivation"; @@ -240,12 +234,6 @@ struct CmdDevShell : Common struct CmdPrintDevEnv : Common { - - std::string name() override - { - return "print-dev-env"; - } - std::string description() override { return "print shell code that can be sourced by bash to reproduce the build environment of a derivation"; @@ -279,5 +267,5 @@ struct CmdPrintDevEnv : Common } }; -static RegisterCommand r1(make_ref()); -static RegisterCommand r2(make_ref()); +static auto r1 = registerCommand("print-dev-env"); +static auto r2 = registerCommand("dev-shell"); diff --git a/src/nix/show-config.cc b/src/nix/show-config.cc index 86638b50d..87544f937 100644 --- a/src/nix/show-config.cc +++ b/src/nix/show-config.cc @@ -8,15 +8,6 @@ using namespace nix; struct CmdShowConfig : Command, MixJSON { - CmdShowConfig() - { - } - - std::string name() override - { - return "show-config"; - } - std::string description() override { return "show the Nix configuration"; @@ -37,4 +28,4 @@ struct CmdShowConfig : Command, MixJSON } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("show-config"); diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index ee94fded3..6065adc4d 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -22,11 +22,6 @@ struct CmdShowDerivation : InstallablesCommand .set(&recursive, true); } - std::string name() override - { - return "show-derivation"; - } - std::string description() override { return "show the contents of a store derivation"; @@ -116,4 +111,4 @@ struct CmdShowDerivation : InstallablesCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("show-derivation"); diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index b1825c412..23bc83ad0 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -22,11 +22,6 @@ struct CmdCopySigs : StorePathsCommand .handler([&](std::vector ss) { substituterUris.push_back(ss[0]); }); } - std::string name() override - { - return "copy-sigs"; - } - std::string description() override { return "copy path signatures from substituters (like binary caches)"; @@ -93,7 +88,7 @@ struct CmdCopySigs : StorePathsCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("copy-sigs"); struct CmdSignPaths : StorePathsCommand { @@ -109,11 +104,6 @@ struct CmdSignPaths : StorePathsCommand .dest(&secretKeyFile); } - std::string name() override - { - return "sign-paths"; - } - std::string description() override { return "sign the specified paths"; @@ -146,4 +136,4 @@ struct CmdSignPaths : StorePathsCommand } }; -static RegisterCommand r3(make_ref()); +static auto r2 = registerCommand("sign-paths"); diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 35c44a70c..13d8504a6 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -30,11 +30,6 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand .dest(&storePathsUrl); } - std::string name() override - { - return "upgrade-nix"; - } - std::string description() override { return "upgrade Nix to the latest stable version"; @@ -157,4 +152,4 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("upgrade-nix"); diff --git a/src/nix/verify.cc b/src/nix/verify.cc index 7ef571561..f55766eda 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -29,11 +29,6 @@ struct CmdVerify : StorePathsCommand mkIntFlag('n', "sigs-needed", "require that each path has at least N valid signatures", &sigsNeeded); } - std::string name() override - { - return "verify"; - } - std::string description() override { return "verify the integrity of store paths"; @@ -175,4 +170,4 @@ struct CmdVerify : StorePathsCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("verify"); diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index 32ba5a1ad..3d13a77e4 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -44,11 +44,6 @@ struct CmdWhyDepends : SourceExprCommand .set(&all, true); } - std::string name() override - { - return "why-depends"; - } - std::string description() override { return "show why a package has another package in its closure"; @@ -264,4 +259,4 @@ struct CmdWhyDepends : SourceExprCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("why-depends"); -- cgit v1.2.3 From d132d057a85aa1812c4133feed6c9b34ca70671d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 21 Jun 2019 15:29:05 +0200 Subject: Handle store symlinks in flake directories E.g. 'nix path-info ./result' inside a flake directory now works again. --- src/nix/installables.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index feaf57f0c..d43f86c0c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -394,9 +394,18 @@ std::vector> SourceExprCommand::parseInstallables( } else { + auto follow = [&](const std::string & s) -> std::optional { + try { + return store->followLinksToStorePath(s); + } catch (NotInStore &) { + return {}; + } + }; + for (auto & s : ss) { size_t colon; + std::optional storePath; if (s.compare(0, 1, "(") == 0) result.push_back(std::make_shared(*this, s)); @@ -422,17 +431,8 @@ std::vector> SourceExprCommand::parseInstallables( getDefaultFlakeAttrPathPrefixes())); } - else if (s.find('/') != std::string::npos || s == ".") { - Path storePath; - try { - storePath = store->toStorePath(store->followLinksToStore(s)); - } catch (Error) { } - if (storePath != "") - result.push_back(std::make_shared(storePath)); - else - result.push_back(std::make_shared(*this, FlakeRef(s, true), - getDefaultFlakeAttrPaths())); - } + else if (s.find('/') != std::string::npos && (storePath = follow(s))) + result.push_back(std::make_shared(*storePath)); else throw Error("unsupported argument '%s'", s); -- cgit v1.2.3 From d4fe9daed6f48ebdcea18a1952cbecd30a846e70 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 21 Jun 2019 19:04:58 +0200 Subject: Simplify getFlake() / fetchFlake() logic --- src/nix/flake.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 91c6b4276..49f7c33c7 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -38,7 +38,8 @@ public: Flake getFlake() { auto evalState = getEvalState(); - return flake::getFlake(*evalState, getFlakeRef(), useRegistries); + return flake::getFlake(*evalState, + maybeLookupFlake(*evalState, getFlakeRef(), useRegistries)); } ResolvedFlake resolveFlake() @@ -425,13 +426,13 @@ struct CmdFlakePin : virtual Args, EvalCommand FlakeRegistry userRegistry = *readRegistry(userRegistryPath); auto it = userRegistry.entries.find(FlakeRef(alias)); if (it != userRegistry.entries.end()) { - it->second = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; + it->second = getFlake(*evalState, maybeLookupFlake(*evalState, it->second, true)).sourceInfo.resolvedRef; writeRegistry(userRegistry, userRegistryPath); } else { std::shared_ptr globalReg = evalState->getGlobalFlakeRegistry(); it = globalReg->entries.find(FlakeRef(alias)); if (it != globalReg->entries.end()) { - auto newRef = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; + auto newRef = getFlake(*evalState, maybeLookupFlake(*evalState, it->second, true)).sourceInfo.resolvedRef; userRegistry.entries.insert_or_assign(alias, newRef); writeRegistry(userRegistry, userRegistryPath); } else -- cgit v1.2.3 From 96c6b08ed7f99be84cb1816515a368392d19dbb5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 23 Jun 2019 22:19:14 +0200 Subject: nix doctor: Fix typo --- src/nix/doctor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc index f2cf04758..98260127b 100644 --- a/src/nix/doctor.cc +++ b/src/nix/doctor.cc @@ -116,4 +116,4 @@ struct CmdDoctor : StoreCommand } }; -static auto r1 = registerCommand("doctore"); +static auto r1 = registerCommand("doctor"); -- cgit v1.2.3 From ad42a784690449873fccb20192bd2150da81c56d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Jul 2019 13:54:53 +0200 Subject: Rename 'epoch' -> 'edition' --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 49f7c33c7..aab29b626 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -105,7 +105,7 @@ static void printFlakeInfo(const Flake & flake) { std::cout << fmt("ID: %s\n", flake.id); std::cout << fmt("Description: %s\n", flake.description); - std::cout << fmt("Epoch: %s\n", flake.epoch); + std::cout << fmt("Edition: %s\n", flake.edition); printSourceInfo(flake.sourceInfo); } @@ -114,7 +114,7 @@ static nlohmann::json flakeToJson(const Flake & flake) nlohmann::json j; j["id"] = flake.id; j["description"] = flake.description; - j["epoch"] = flake.epoch; + j["edition"] = flake.edition; sourceInfoToJson(flake.sourceInfo, j); return j; } -- cgit v1.2.3 From 990b5b2dcfaacd82caf4c92789a9c234b342c3b4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 12 Jul 2019 15:32:17 +0200 Subject: nix build: Add '--profile' flag This replaces 'nix-env --set'. For example: $ nix build --profile /nix/var/nix/profiles/system \ ~/Misc/eelco-configurations:nixosConfigurations.vyr.config.system.build.toplevel updates the NixOS system profile from a flake. This could have been a separate command (e.g. 'nix set-profile') but 1) '--profile' is pretty similar to '--out-link'; and 2) '--profile' could be useful for other command (like 'nix dev-shell'). --- src/nix/build.cc | 17 +++++++++++------ src/nix/command.cc | 41 +++++++++++++++++++++++++++++++++++++++++ src/nix/command.hh | 14 ++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index d8ce8cc80..f63150012 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -6,7 +6,7 @@ using namespace nix; -struct CmdBuild : MixDryRun, InstallablesCommand +struct CmdBuild : MixDryRun, MixProfile, InstallablesCommand { Path outLink = "result"; @@ -41,6 +41,10 @@ struct CmdBuild : MixDryRun, InstallablesCommand "To build the build.x86_64-linux attribute from release.nix:", "nix build -f release.nix build.x86_64-linux" }, + Example{ + "To make a profile point at GNU Hello:", + "nix build --profile /tmp/profile nixpkgs:hello" + }, }; } @@ -52,18 +56,19 @@ struct CmdBuild : MixDryRun, InstallablesCommand evalState->addRegistryOverrides(registryOverrides); if (dryRun) return; - for (size_t i = 0; i < buildables.size(); ++i) { - auto & b(buildables[i]); - - if (outLink != "") - for (auto & output : b.outputs) + if (outLink != "") { + for (size_t i = 0; i < buildables.size(); ++i) { + for (auto & output : buildables[i].outputs) if (auto store2 = store.dynamic_pointer_cast()) { std::string symlink = outLink; if (i) symlink += fmt("-%d", i); if (output.first != "out") symlink += fmt("-%s", output.first); store2->addPermRoot(output.second, absPath(symlink), true); } + } } + + updateProfile(buildables); } }; diff --git a/src/nix/command.cc b/src/nix/command.cc index 89fa0cba4..8191cb831 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -1,6 +1,7 @@ #include "command.hh" #include "store-api.hh" #include "derivations.hh" +#include "profiles.hh" namespace nix { @@ -81,4 +82,44 @@ void StorePathCommand::run(ref store) run(store, *storePaths.begin()); } +MixProfile::MixProfile() +{ + mkFlag() + .longName("profile") + .description("profile to update") + .labels({"path"}) + .dest(&profile); +} + +void MixProfile::updateProfile(const Path & storePath) +{ + if (!profile) return; + auto store = getStore().dynamic_pointer_cast(); + if (!store) throw Error("'--profile' is not supported for this Nix store"); + switchLink(*profile, + createGeneration( + ref(store), + *profile, storePath)); +} + +void MixProfile::updateProfile(const Buildables & buildables) +{ + if (!profile) return; + + std::optional result; + + for (auto & buildable : buildables) { + for (auto & output : buildable.outputs) { + if (result) + throw Error("'--profile' requires that the arguments produce a single store path, but there are multiple"); + result = output.second; + } + } + + if (!result) + throw Error("'--profile' requires that the arguments produce a single store path, but there are none"); + + updateProfile(*result); +} + } diff --git a/src/nix/command.hh b/src/nix/command.hh index 59c6f8578..d6153e42b 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -219,4 +219,18 @@ PathSet toDerivations(ref store, std::vector> installables, bool useDeriver = false); +struct MixProfile : virtual Args, virtual StoreCommand +{ + std::optional profile; + + MixProfile(); + + /* If 'profile' is set, make it point at 'storePath'. */ + void updateProfile(const Path & storePath); + + /* If 'profile' is set, make it point at the store path produced + by 'buildables'. */ + void updateProfile(const Buildables & buildables); +}; + } -- cgit v1.2.3 From 7ba928116ef1677b7403525df9e8abb49001820e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 12 Jul 2019 16:10:58 +0200 Subject: nix dev-shell: Add --profile flag This is useful to prevent the shell environment from being garbage-collected. --- src/nix/shell.cc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index f42947b7c..4c7b701dc 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -66,7 +66,7 @@ BuildEnvironment readEnvironment(const Path & path) modified derivation with the same dependencies and nearly the same initial environment variables, that just writes the resulting environment to a file and exits. */ -BuildEnvironment getDerivationEnvironment(ref store, Derivation drv) +Path getDerivationEnvironment(ref store, Derivation drv) { auto builder = baseNameOf(drv.builder); if (builder != "bash") @@ -101,7 +101,7 @@ BuildEnvironment getDerivationEnvironment(ref store, Derivation drv) assert(store->isValidPath(shellOutPath)); - return readEnvironment(shellOutPath); + return shellOutPath; } struct Common : InstallableCommand @@ -175,7 +175,7 @@ struct Common : InstallableCommand } }; -struct CmdDevShell : Common +struct CmdDevShell : Common, MixProfile { std::string description() override { @@ -193,6 +193,10 @@ struct CmdDevShell : Common "To get the build environment of the default package of flake in the current directory:", "nix dev-shell" }, + Example{ + "To store the build environment in a profile:", + "nix dev-shell --profile /tmp/my-shell" + }, }; } @@ -206,7 +210,11 @@ struct CmdDevShell : Common auto & drvPath = *drvs.begin(); - auto buildEnvironment = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + auto shellOutPath = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + + updateProfile(shellOutPath); + + auto buildEnvironment = readEnvironment(shellOutPath); auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); @@ -259,7 +267,9 @@ struct CmdPrintDevEnv : Common auto & drvPath = *drvs.begin(); - auto buildEnvironment = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + auto buildEnvironment = readEnvironment( + getDerivationEnvironment(store, + store->derivationFromPath(drvPath))); stopProgressBar(); -- cgit v1.2.3 From 731bc65ec04900834ca5e5b8e9dae1aa8c2c1027 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 12 Jul 2019 16:16:27 +0200 Subject: Refactor a bit --- src/nix/shell.cc | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 4c7b701dc..442835d38 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -104,7 +104,7 @@ Path getDerivationEnvironment(ref store, Derivation drv) return shellOutPath; } -struct Common : InstallableCommand +struct Common : InstallableCommand, MixProfile { /* std::set keepVars{ @@ -173,9 +173,26 @@ struct Common : InstallableCommand { return {"devShell", "defaultPackage"}; } + + BuildEnvironment getBuildEnvironment(ref store) + { + auto drvs = toDerivations(store, {installable}); + + if (drvs.size() != 1) + throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", + installable->what(), drvs.size()); + + auto & drvPath = *drvs.begin(); + + auto shellOutPath = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + + updateProfile(shellOutPath); + + return readEnvironment(shellOutPath); + } }; -struct CmdDevShell : Common, MixProfile +struct CmdDevShell : Common { std::string description() override { @@ -202,19 +219,7 @@ struct CmdDevShell : Common, MixProfile void run(ref store) override { - auto drvs = toDerivations(store, {installable}); - - if (drvs.size() != 1) - throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", - installable->what(), drvs.size()); - - auto & drvPath = *drvs.begin(); - - auto shellOutPath = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); - - updateProfile(shellOutPath); - - auto buildEnvironment = readEnvironment(shellOutPath); + auto buildEnvironment = getBuildEnvironment(store); auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); @@ -259,17 +264,7 @@ struct CmdPrintDevEnv : Common void run(ref store) override { - auto drvs = toDerivations(store, {installable}); - - if (drvs.size() != 1) - throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", - installable->what(), drvs.size()); - - auto & drvPath = *drvs.begin(); - - auto buildEnvironment = readEnvironment( - getDerivationEnvironment(store, - store->derivationFromPath(drvPath))); + auto buildEnvironment = getBuildEnvironment(store); stopProgressBar(); -- cgit v1.2.3 From aa82f8b2d2a2c42f0d713e8404b668cef1a4b108 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 12 Jul 2019 16:28:39 +0200 Subject: nix dev-shell: Make it possible to enter a profile For example: $ nix dev-shell --profile /tmp/my-shell dwarffs (later) $ nix dev-shell /tmp/my-shell --- src/nix/command.hh | 7 +++++++ src/nix/installables.cc | 5 +++++ src/nix/shell.cc | 30 ++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 8 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index d6153e42b..00c202f20 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -64,6 +64,13 @@ struct Installable { throw Error("argument '%s' cannot be evaluated", what()); } + + /* Return a value only if this installable is a store path or a + symlink to it. */ + virtual std::optional getStorePath() + { + return {}; + } }; struct EvalCommand : virtual StoreCommand, MixEvalArgs diff --git a/src/nix/installables.cc b/src/nix/installables.cc index d43f86c0c..aa5ef5184 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -102,6 +102,11 @@ struct InstallableStorePath : Installable { return {{isDerivation(storePath) ? storePath : "", {{"out", storePath}}}}; } + + std::optional getStorePath() override + { + return storePath; + } }; struct InstallableValue : Installable diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 442835d38..93732f6a3 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -174,17 +174,27 @@ struct Common : InstallableCommand, MixProfile return {"devShell", "defaultPackage"}; } - BuildEnvironment getBuildEnvironment(ref store) + Path getShellOutPath(ref store) { - auto drvs = toDerivations(store, {installable}); + auto path = installable->getStorePath(); + if (path && hasSuffix(*path, "-env")) + return *path; + else { + auto drvs = toDerivations(store, {installable}); + + if (drvs.size() != 1) + throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", + installable->what(), drvs.size()); - if (drvs.size() != 1) - throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", - installable->what(), drvs.size()); + auto & drvPath = *drvs.begin(); - auto & drvPath = *drvs.begin(); + return getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + } + } - auto shellOutPath = getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + BuildEnvironment getBuildEnvironment(ref store) + { + auto shellOutPath = getShellOutPath(store); updateProfile(shellOutPath); @@ -212,7 +222,11 @@ struct CmdDevShell : Common }, Example{ "To store the build environment in a profile:", - "nix dev-shell --profile /tmp/my-shell" + "nix dev-shell --profile /tmp/my-shell nixpkgs:hello" + }, + Example{ + "To use a build environment previously recorded in a profile:", + "nix dev-shell /tmp/my-shell" }, }; } -- cgit v1.2.3 From 336afe4d5fe374569c2b13d2db90caac663573b3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2019 20:09:44 +0200 Subject: nix dev-shell: Set IN_NIX_SHELL in the derivation This ensures that stdenv / setup hooks take $IN_NIX_SHELL into account. For example, stdenv only sets NIX_SSL_CERT_FILE=/no-cert-file.crt if we're not in a shell. --- src/nix/shell.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 93732f6a3..5f2724961 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -72,7 +72,7 @@ Path getDerivationEnvironment(ref store, Derivation drv) if (builder != "bash") throw Error("'nix shell' only works on derivations that use 'bash' as their builder"); - drv.args = {"-c", "set -e; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; + drv.args = {"-c", "set -e; export IN_NIX_SHELL=impure; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; /* Remove derivation checks. */ drv.env.erase("allowedReferences"); @@ -143,7 +143,6 @@ struct Common : InstallableCommand, MixProfile void makeRcScript(const BuildEnvironment & buildEnvironment, std::ostream & out) { - out << "export IN_NIX_SHELL=1\n"; out << "nix_saved_PATH=\"$PATH\"\n"; for (auto & i : buildEnvironment.env) { -- cgit v1.2.3 From 662db921e2f5bebeb0fd455aa744708468df8bfa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 9 Aug 2019 18:51:52 +0200 Subject: nix dev-shell: Set dontAddDisableDepTrack --- src/nix/shell.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 5f2724961..a3827c297 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -72,7 +72,7 @@ Path getDerivationEnvironment(ref store, Derivation drv) if (builder != "bash") throw Error("'nix shell' only works on derivations that use 'bash' as their builder"); - drv.args = {"-c", "set -e; export IN_NIX_SHELL=impure; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; + drv.args = {"-c", "set -e; export IN_NIX_SHELL=impure; export dontAddDisableDepTrack=1; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; /* Remove derivation checks. */ drv.env.erase("allowedReferences"); -- cgit v1.2.3 From 30ccf4e52d31ea0f1531feb8cccdc0314a41265d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 30 Aug 2019 16:27:51 +0200 Subject: Turn flake inputs into an attrset Instead of a list, inputs are now an attrset like inputs = { nixpkgs.uri = github:NixOS/nixpkgs; }; If 'uri' is omitted, than the flake is a lookup in the flake registry, e.g. inputs = { nixpkgs = {}; }; but in that case, you can also just omit the input altogether and specify it as an argument to the 'outputs' function, as in outputs = { self, nixpkgs }: ... This also gets rid of 'nonFlakeInputs', which are now just a special kind of input that have a 'flake = false' attribute, e.g. inputs = { someRepo = { uri = github:example/repo; flake = false; }; }; --- src/nix/installables.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index aa5ef5184..dbbf58861 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -217,25 +217,20 @@ void makeFlakeClosureGCRoot(Store & store, assert(store.isValidPath(resFlake.flake.sourceInfo.storePath)); closure.insert(resFlake.flake.sourceInfo.storePath); - std::queue> queue; + std::queue> queue; queue.push(resFlake.lockFile); while (!queue.empty()) { - const flake::FlakeInputs & flake = queue.front(); + const flake::LockedInputs & flake = queue.front(); queue.pop(); /* Note: due to lazy fetching, these paths might not exist yet. */ - for (auto & dep : flake.flakeInputs) { + for (auto & dep : flake.inputs) { auto path = dep.second.computeStorePath(store); if (store.isValidPath(path)) closure.insert(path); queue.push(dep.second); } - for (auto & dep : flake.nonFlakeInputs) { - auto path = dep.second.computeStorePath(store); - if (store.isValidPath(path)) - closure.insert(path); - } } if (closure.empty()) return; -- cgit v1.2.3 From 80c36d4562af71a90c67b3adb886a1003834890e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 30 Aug 2019 16:38:27 +0200 Subject: Remove 'name' attribute from flakes This is no longer needed since flakes are given an identity in the 'inputs' attribute. --- src/nix/flake.cc | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index aab29b626..4129ef323 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -103,7 +103,6 @@ static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) static void printFlakeInfo(const Flake & flake) { - std::cout << fmt("ID: %s\n", flake.id); std::cout << fmt("Description: %s\n", flake.description); std::cout << fmt("Edition: %s\n", flake.edition); printSourceInfo(flake.sourceInfo); @@ -112,7 +111,6 @@ static void printFlakeInfo(const Flake & flake) static nlohmann::json flakeToJson(const Flake & flake) { nlohmann::json j; - j["id"] = flake.id; j["description"] = flake.description; j["edition"] = flake.edition; sourceInfoToJson(flake.sourceInfo, j); @@ -120,12 +118,6 @@ static nlohmann::json flakeToJson(const Flake & flake) } #if 0 -static void printNonFlakeInfo(const NonFlake & nonFlake) -{ - std::cout << fmt("ID: %s\n", nonFlake.alias); - printSourceInfo(nonFlake.sourceInfo); -} - // FIXME: merge info CmdFlakeInfo? struct CmdFlakeDeps : FlakeCommand { @@ -148,9 +140,6 @@ struct CmdFlakeDeps : FlakeCommand auto resFlake = std::move(todo.front()); todo.pop(); - for (auto & nonFlake : resFlake.nonFlakeDeps) - printNonFlakeInfo(nonFlake); - for (auto & info : resFlake.flakeDeps) { printFlakeInfo(info.second.flake); todo.push(info.second); -- cgit v1.2.3 From 5ec2a1ed82d485429aaf6fbad55fd6c1320b2d8c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Sep 2019 15:59:19 +0200 Subject: nix dev-shell --profile: Support relative path --- src/nix/command.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index 8191cb831..9cca443dc 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -96,10 +96,11 @@ void MixProfile::updateProfile(const Path & storePath) if (!profile) return; auto store = getStore().dynamic_pointer_cast(); if (!store) throw Error("'--profile' is not supported for this Nix store"); - switchLink(*profile, + auto profile2 = absPath(*profile); + switchLink(profile2, createGeneration( ref(store), - *profile, storePath)); + profile2, storePath)); } void MixProfile::updateProfile(const Buildables & buildables) -- cgit v1.2.3 From 61fdb16aacf9ff18c96b72a37e1b46eb14586eb4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Sep 2019 17:33:07 +0200 Subject: Improve error message when a directory is not a flake So you now get $ nix build error: path '.' is not a flake (because it does not reference a Git repository) rather than $ nix build error: unsupported argument '.' --- src/nix/installables.cc | 52 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 17 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index dbbf58861..a4726a59e 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -417,25 +417,43 @@ std::vector> SourceExprCommand::parseInstallables( Strings{"legacyPackages." + std::string(s, 8)})); } - else if (auto flakeRef = parseFlakeRef(s, true)) - result.push_back(std::make_shared(*this, std::move(*flakeRef), - getDefaultFlakeAttrPaths())); - - else if ((colon = s.rfind(':')) != std::string::npos) { - auto flakeRef = std::string(s, 0, colon); - auto attrPath = std::string(s, colon + 1); - result.push_back(std::make_shared( - *this, - FlakeRef(flakeRef, true), - attrPath, - getDefaultFlakeAttrPathPrefixes())); - } + else { + + std::exception_ptr flakeEx; + + try { + auto flakeRef = FlakeRef(s, true); + result.push_back(std::make_shared( + *this, std::move(flakeRef), getDefaultFlakeAttrPaths())); + continue; + } catch (MissingFlake &) { + /* 's' could be parsed as a flakeref, but it + references a local path that is not a flake. So + take note of that. */ + flakeEx = std::current_exception(); + } catch (BadFlakeRef &) { + } + + if ((colon = s.rfind(':')) != std::string::npos) { + auto flakeRef = std::string(s, 0, colon); + auto attrPath = std::string(s, colon + 1); + result.push_back(std::make_shared( + *this, + FlakeRef(flakeRef, true), + attrPath, + getDefaultFlakeAttrPathPrefixes())); + } - else if (s.find('/') != std::string::npos && (storePath = follow(s))) - result.push_back(std::make_shared(*storePath)); + else if (s.find('/') != std::string::npos && (storePath = follow(s))) + result.push_back(std::make_shared(*storePath)); - else - throw Error("unsupported argument '%s'", s); + else { + if (flakeEx) + std::rethrow_exception(flakeEx); + else + throw Error("unsupported argument '%s'", s); + } + } } } -- cgit v1.2.3 From c693f80b814c244dcdae7a2e87fb9e444d9d1ca5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Sep 2019 17:43:27 +0200 Subject: Shut up some clang warnings --- src/nix/command.hh | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 00c202f20..92f606bbe 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -49,6 +49,8 @@ struct App struct Installable { + virtual ~Installable() { } + virtual std::string what() = 0; virtual Buildables toBuildables() -- cgit v1.2.3 From 2dbd69dbf4538d5b7947d192979ff4feab322c2e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Sep 2019 23:04:27 +0200 Subject: nix repl: Run in impure mode --- src/nix/repl.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 3a70a23c7..0fa1594cc 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -778,6 +778,7 @@ struct CmdRepl : StoreCommand, MixEvalArgs void run(ref store) override { + evalSettings.pureEval = false; auto repl = std::make_unique(searchPath, openStore()); repl->autoArgs = getAutoArgs(repl->state); repl->mainLoop(files); -- cgit v1.2.3 From dc3f52a1447df8523f44c89e25e48e8b7f5341a0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 10 Sep 2019 14:52:22 +0200 Subject: nix flake check: Check overlays --- src/nix/flake.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 4129ef323..10ce9addc 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -280,6 +280,22 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON } }; + auto checkOverlay = [&](const std::string & attrPath, Value & v) { + try { + state->forceValue(v); + if (v.type != tLambda || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final") + throw Error("overlay does not take an argument named 'final'"); + auto body = dynamic_cast(v.lambda.fun->body); + if (!body || body->matchAttrs || std::string(body->arg) != "prev") + throw Error("overlay does not take an argument named 'prev'"); + // FIXME: if we have a 'nixpkgs' input, use it to + // evaluate the overlay. + } catch (Error & e) { + e.addPrefix(fmt("while checking the overlay '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + throw; + } + }; + { Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); @@ -326,6 +342,9 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON // FIXME: do getDerivations? ; + else if (name == "overlay") + checkOverlay(name, vProvide); + else warn("unknown flake output '%s'", name); -- cgit v1.2.3 From 4b9dee6bcca48bd60f341cb07273a33e632bafc2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 10 Sep 2019 15:25:10 +0200 Subject: nix flake check: Do some basic checks on NixOS modules Also show more position info. --- src/nix/flake.cc | 87 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 23 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 10ce9addc..b29aa212c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -170,7 +170,7 @@ struct CmdFlakeUpdate : FlakeCommand }; static void enumerateOutputs(EvalState & state, Value & vFlake, - std::function callback) + std::function callback) { state.forceAttrs(vFlake); @@ -179,7 +179,7 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, state.forceAttrs(*vOutputs); for (auto & attr : *vOutputs->attrs) - callback(attr.name, *attr.value); + callback(attr.name, *attr.value, *attr.pos); } struct CmdFlakeInfo : FlakeCommand, MixJSON @@ -207,7 +207,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON enumerateOutputs(*state, *vFlake, - [&](const std::string & name, Value & vProvide) { + [&](const std::string & name, Value & vProvide, const Pos & pos) { auto provide = nlohmann::json::object(); if (name == "checks" || name == "packages") { @@ -251,7 +251,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON auto state = getEvalState(); auto flake = resolveFlake(); - auto checkDerivation = [&](const std::string & attrPath, Value & v) { + auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { auto drvInfo = getDerivation(*state, v, false); if (!drvInfo) @@ -259,14 +259,14 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON // FIXME: check meta attributes return drvInfo->queryDrvPath(); } catch (Error & e) { - e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); throw; } }; PathSet drvPaths; - auto checkApp = [&](const std::string & attrPath, Value & v) { + auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { auto app = App(*state, v); for (auto & i : app.context) { @@ -275,12 +275,12 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON drvPaths.insert(drvPath + "!" + outputName); } } catch (Error & e) { - e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); throw; } }; - auto checkOverlay = [&](const std::string & attrPath, Value & v) { + auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { state->forceValue(v); if (v.type != tLambda || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final") @@ -291,7 +291,31 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON // FIXME: if we have a 'nixpkgs' input, use it to // evaluate the overlay. } catch (Error & e) { - e.addPrefix(fmt("while checking the overlay '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + e.addPrefix(fmt("while checking the overlay '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); + throw; + } + }; + + auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) { + try { + state->forceValue(v); + if (v.type == tLambda) { + if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis) + throw Error("module must match an open attribute set ('{ config, ... }')"); + } else if (v.type == tAttrs) { + for (auto & attr : *v.attrs) + try { + state->forceValue(*attr.value); + } catch (Error & e) { + e.addPrefix(fmt("while evaluating the option '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attr.name, *attr.pos)); + throw; + } + } else + throw Error("module must be a function or an attribute set"); + // FIXME: if we have a 'nixpkgs' input, use it to + // check the module. + } catch (Error & e) { + e.addPrefix(fmt("while checking the NixOS module '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); throw; } }; @@ -304,46 +328,63 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON enumerateOutputs(*state, *vFlake, - [&](const std::string & name, Value & vProvide) { + [&](const std::string & name, Value & vOutput, const Pos & pos) { Activity act(*logger, lvlChatty, actUnknown, fmt("checking flake output '%s'", name)); try { - state->forceValue(vProvide); + state->forceValue(vOutput); if (name == "checks") { - state->forceAttrs(vProvide); - for (auto & aCheck : *vProvide.attrs) + state->forceAttrs(vOutput); + for (auto & attr : *vOutput.attrs) drvPaths.insert(checkDerivation( - name + "." + (std::string) aCheck.name, *aCheck.value)); + name + "." + (std::string) attr.name, *attr.value, *attr.pos)); } else if (name == "packages") { - state->forceAttrs(vProvide); - for (auto & aCheck : *vProvide.attrs) + state->forceAttrs(vOutput); + for (auto & attr : *vOutput.attrs) checkDerivation( - name + "." + (std::string) aCheck.name, *aCheck.value); + name + "." + (std::string) attr.name, *attr.value, *attr.pos); } else if (name == "apps") { - state->forceAttrs(vProvide); - for (auto & aCheck : *vProvide.attrs) + state->forceAttrs(vOutput); + for (auto & attr : *vOutput.attrs) checkApp( - name + "." + (std::string) aCheck.name, *aCheck.value); + name + "." + (std::string) attr.name, *attr.value, *attr.pos); } else if (name == "defaultPackage" || name == "devShell") - checkDerivation(name, vProvide); + checkDerivation(name, vOutput, pos); else if (name == "defaultApp") - checkApp(name, vProvide); + checkApp(name, vOutput, pos); else if (name == "legacyPackages") // FIXME: do getDerivations? ; else if (name == "overlay") - checkOverlay(name, vProvide); + checkOverlay(name, vOutput, pos); + + else if (name == "overlays") { + state->forceAttrs(vOutput); + for (auto & attr : *vOutput.attrs) + checkOverlay(name + "." + (std::string) attr.name, + *attr.value, *attr.pos); + } + + else if (name == "nixosModule") + checkModule(name, vOutput, pos); + + else if (name == "nixosModules") { + state->forceAttrs(vOutput); + for (auto & attr : *vOutput.attrs) + checkModule(name + "." + (std::string) attr.name, + *attr.value, *attr.pos); + } else warn("unknown flake output '%s'", name); -- cgit v1.2.3 From 55e55b34e6ea770ef1310dffb96f476bf37d460c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 10 Sep 2019 17:39:55 +0200 Subject: nix flake check: Check hydraJobs --- src/nix/flake.cc | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b29aa212c..5fd3f5508 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -211,7 +211,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON auto provide = nlohmann::json::object(); if (name == "checks" || name == "packages") { - state->forceAttrs(vProvide); + state->forceAttrs(vProvide, pos); for (auto & aCheck : *vProvide.attrs) provide[aCheck.name] = nlohmann::json::object(); } @@ -282,7 +282,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { - state->forceValue(v); + state->forceValue(v, pos); if (v.type != tLambda || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final") throw Error("overlay does not take an argument named 'final'"); auto body = dynamic_cast(v.lambda.fun->body); @@ -298,14 +298,14 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { - state->forceValue(v); + state->forceValue(v, pos); if (v.type == tLambda) { if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis) throw Error("module must match an open attribute set ('{ config, ... }')"); } else if (v.type == tAttrs) { for (auto & attr : *v.attrs) try { - state->forceValue(*attr.value); + state->forceValue(*attr.value, *attr.pos); } catch (Error & e) { e.addPrefix(fmt("while evaluating the option '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attr.name, *attr.pos)); throw; @@ -320,6 +320,28 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON } }; + std::function checkHydraJobs; + + checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) { + try { + state->forceAttrs(v, pos); + + if (state->isDerivation(v)) + throw Error("jobset should not be a derivation at top-level"); + + for (auto & attr : *v.attrs) { + state->forceAttrs(*attr.value, *attr.pos); + if (!state->isDerivation(*attr.value)) + checkHydraJobs(attrPath + "." + (std::string) attr.name, + *attr.value, *attr.pos); + } + + } catch (Error & e) { + e.addPrefix(fmt("while checking the Hydra jobset '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); + throw; + } + }; + { Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); @@ -333,24 +355,24 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON fmt("checking flake output '%s'", name)); try { - state->forceValue(vOutput); + state->forceValue(vOutput, pos); if (name == "checks") { - state->forceAttrs(vOutput); + state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) drvPaths.insert(checkDerivation( name + "." + (std::string) attr.name, *attr.value, *attr.pos)); } else if (name == "packages") { - state->forceAttrs(vOutput); + state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) checkDerivation( name + "." + (std::string) attr.name, *attr.value, *attr.pos); } else if (name == "apps") { - state->forceAttrs(vOutput); + state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) checkApp( name + "." + (std::string) attr.name, *attr.value, *attr.pos); @@ -370,7 +392,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON checkOverlay(name, vOutput, pos); else if (name == "overlays") { - state->forceAttrs(vOutput); + state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) checkOverlay(name + "." + (std::string) attr.name, *attr.value, *attr.pos); @@ -380,12 +402,15 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON checkModule(name, vOutput, pos); else if (name == "nixosModules") { - state->forceAttrs(vOutput); + state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) checkModule(name + "." + (std::string) attr.name, *attr.value, *attr.pos); } + else if (name == "hydraJobs") + checkHydraJobs(name, vOutput, pos); + else warn("unknown flake output '%s'", name); -- cgit v1.2.3 From c67407172d8383394f4962ad177c84bf04529e5e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 18 Sep 2019 21:17:27 +0200 Subject: Record original flakerefs in the lock file again If 'input..uri' changes, then the entry in the lockfile for input should be considered stale. Also print some messages when lock file entries are added/updated. --- src/nix/flake.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5fd3f5508..599bf12eb 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -38,8 +38,7 @@ public: Flake getFlake() { auto evalState = getEvalState(); - return flake::getFlake(*evalState, - maybeLookupFlake(*evalState, getFlakeRef(), useRegistries)); + return flake::getFlake(*evalState, getFlakeRef(), useRegistries); } ResolvedFlake resolveFlake() @@ -500,13 +499,13 @@ struct CmdFlakePin : virtual Args, EvalCommand FlakeRegistry userRegistry = *readRegistry(userRegistryPath); auto it = userRegistry.entries.find(FlakeRef(alias)); if (it != userRegistry.entries.end()) { - it->second = getFlake(*evalState, maybeLookupFlake(*evalState, it->second, true)).sourceInfo.resolvedRef; + it->second = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; writeRegistry(userRegistry, userRegistryPath); } else { std::shared_ptr globalReg = evalState->getGlobalFlakeRegistry(); it = globalReg->entries.find(FlakeRef(alias)); if (it != globalReg->entries.end()) { - auto newRef = getFlake(*evalState, maybeLookupFlake(*evalState, it->second, true)).sourceInfo.resolvedRef; + auto newRef = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; userRegistry.entries.insert_or_assign(alias, newRef); writeRegistry(userRegistry, userRegistryPath); } else -- cgit v1.2.3 From 5573365dffc96df5fcb6898077aeff1cd5e2b711 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 19 Sep 2019 20:15:42 +0200 Subject: nix flake check: Validate nixosConfigurations outputs --- src/nix/flake.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 599bf12eb..2e352306e 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -8,6 +8,7 @@ #include "get-drvs.hh" #include "store-api.hh" #include "derivations.hh" +#include "attr-path.hh" #include #include @@ -341,6 +342,21 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON } }; + auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const Pos & pos) { + try { + Activity act(*logger, lvlChatty, actUnknown, + fmt("checking NixOS configuration '%s'", attrPath)); + Bindings & bindings(*state->allocBindings(0)); + auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v); + state->forceAttrs(*vToplevel, pos); + if (!state->isDerivation(*vToplevel)) + throw Error("attribute 'config.system.build.toplevel' is not a derivation"); + } catch (Error & e) { + e.addPrefix(fmt("while checking the NixOS configuration '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); + throw; + } + }; + { Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); @@ -407,6 +423,13 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON *attr.value, *attr.pos); } + else if (name == "nixosConfigurations") { + state->forceAttrs(vOutput, pos); + for (auto & attr : *vOutput.attrs) + checkNixOSConfiguration(name + "." + (std::string) attr.name, + *attr.value, *attr.pos); + } + else if (name == "hydraJobs") checkHydraJobs(name, vOutput, pos); -- cgit v1.2.3 From 5a0e98d1e5697d1d28131e3fd893282be660e008 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 20 Sep 2019 16:01:40 +0200 Subject: Use '#' instead of ':' to separate flakeref and attrpath This is less ambiguous. --- src/nix/installables.cc | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index a4726a59e..987b744fe 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -404,7 +404,7 @@ std::vector> SourceExprCommand::parseInstallables( for (auto & s : ss) { - size_t colon; + size_t hash; std::optional storePath; if (s.compare(0, 1, "(") == 0) @@ -417,8 +417,14 @@ std::vector> SourceExprCommand::parseInstallables( Strings{"legacyPackages." + std::string(s, 8)})); } - else { + else if ((hash = s.rfind('#')) != std::string::npos) + result.push_back(std::make_shared( + *this, + FlakeRef(std::string(s, 0, hash), true), + std::string(s, hash + 1), + getDefaultFlakeAttrPathPrefixes())); + else { std::exception_ptr flakeEx; try { @@ -434,17 +440,7 @@ std::vector> SourceExprCommand::parseInstallables( } catch (BadFlakeRef &) { } - if ((colon = s.rfind(':')) != std::string::npos) { - auto flakeRef = std::string(s, 0, colon); - auto attrPath = std::string(s, colon + 1); - result.push_back(std::make_shared( - *this, - FlakeRef(flakeRef, true), - attrPath, - getDefaultFlakeAttrPathPrefixes())); - } - - else if (s.find('/') != std::string::npos && (storePath = follow(s))) + if (s.find('/') != std::string::npos && (storePath = follow(s))) result.push_back(std::make_shared(*storePath)); else { -- cgit v1.2.3 From 14d3f450098e0f4a9b8d538545f1d1bd1decdab3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 20 Sep 2019 16:06:49 +0200 Subject: Simplify --- src/nix/installables.cc | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 987b744fe..85005cc95 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -425,29 +425,15 @@ std::vector> SourceExprCommand::parseInstallables( getDefaultFlakeAttrPathPrefixes())); else { - std::exception_ptr flakeEx; - try { auto flakeRef = FlakeRef(s, true); result.push_back(std::make_shared( *this, std::move(flakeRef), getDefaultFlakeAttrPaths())); - continue; - } catch (MissingFlake &) { - /* 's' could be parsed as a flakeref, but it - references a local path that is not a flake. So - take note of that. */ - flakeEx = std::current_exception(); - } catch (BadFlakeRef &) { - } - - if (s.find('/') != std::string::npos && (storePath = follow(s))) - result.push_back(std::make_shared(*storePath)); - - else { - if (flakeEx) - std::rethrow_exception(flakeEx); + } catch (...) { + if (s.find('/') != std::string::npos && (storePath = follow(s))) + result.push_back(std::make_shared(*storePath)); else - throw Error("unsupported argument '%s'", s); + throw; } } } -- cgit v1.2.3 From 893be6f5e36abb58bbaa9c49055a5218114dd514 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 22 Sep 2019 21:29:33 +0200 Subject: Don't catch exceptions by value --- src/nix/edit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/edit.cc b/src/nix/edit.cc index c62b35c46..632a99d11 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -50,7 +50,7 @@ struct CmdEdit : InstallableCommand int lineno; try { lineno = std::stoi(std::string(pos, colon + 1)); - } catch (std::invalid_argument e) { + } catch (std::invalid_argument & e) { throw Error("cannot parse line number '%s'", pos); } -- cgit v1.2.3 From 382aa05ff71b61379f5c2792eaf517bdf4a5c5bf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 22 Sep 2019 21:53:01 +0200 Subject: nix flake info --json: Get rid of duplicate getFlake() call Also fix some gcc warnings. --- src/nix/flake.cc | 19 ++++++++++--------- src/nix/installables.cc | 7 ++++--- 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2e352306e..d0135143c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -174,11 +174,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, { state.forceAttrs(vFlake); - auto vOutputs = (*vFlake.attrs->get(state.symbols.create("outputs")))->value; + auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); + assert(aOutputs); - state.forceAttrs(*vOutputs); + state.forceAttrs(*(*aOutputs)->value); - for (auto & attr : *vOutputs->attrs) + for (auto & attr : *((*aOutputs)->value->attrs)) callback(attr.name, *attr.value, *attr.pos); } @@ -191,15 +192,12 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON void run(nix::ref store) override { - auto flake = getFlake(); - stopProgressBar(); - if (json) { - auto json = flakeToJson(flake); - auto state = getEvalState(); auto flake = resolveFlake(); + auto json = flakeToJson(flake.flake); + auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); @@ -222,8 +220,11 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON json["outputs"] = std::move(outputs); std::cout << json.dump() << std::endl; - } else + } else { + auto flake = getFlake(); + stopProgressBar(); printFlakeInfo(flake); + } } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 85005cc95..867133653 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -291,11 +291,12 @@ struct InstallableFlake : InstallableValue makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake); - auto vOutputs = (*vFlake->attrs->get(state.symbols.create("outputs")))->value; + auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + assert(aOutputs); - state.forceValue(*vOutputs); + state.forceValue(*(*aOutputs)->value); - return vOutputs; + return (*aOutputs)->value; } std::vector toDerivations() override -- cgit v1.2.3 From 9b9de3a5e321c764ec018b32c1f949a49b0c69ef Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 27 Sep 2019 17:01:25 +0200 Subject: nix dev-shell: Improve environment handling Only variables that were marked as exported are exported in the dev shell. Also, we no longer try to parse the function section of the env file, fixing $ nix dev-shell error: shell environment '/nix/store/h7ama3kahb8lypf4nvjx34z06g9ncw4h-nixops-1.7pre20190926.4c7acbb-env' has unexpected line '/^[a-z]?"""/ {' --- src/nix/shell.cc | 110 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 41 deletions(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index a3827c297..db7106793 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -7,55 +7,76 @@ #include "affinity.hh" #include "progress-bar.hh" +#include + using namespace nix; +struct Var +{ + bool exported; + std::string value; // quoted string or array +}; + struct BuildEnvironment { - // FIXME: figure out which vars should be exported. - std::map env; - std::map functions; + std::map env; + std::string bashFunctions; }; BuildEnvironment readEnvironment(const Path & path) { BuildEnvironment res; - auto lines = tokenizeString(readFile(path), "\n"); + std::set exported; - auto getLine = - [&]() { - if (lines.empty()) - throw Error("shell environment '%s' ends unexpectedly", path); - auto line = lines.front(); - lines.pop_front(); - return line; - }; + auto file = readFile(path); + //auto file = readFile("/tmp/x"); + + auto pos = file.cbegin(); + + static std::string varNameRegex = + R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re"; + + static std::regex declareRegex( + "^declare -x (" + varNameRegex + ")" + + R"re((?:="((?:[^"\\]|\\.)*)")?\n)re"); + + static std::string simpleStringRegex = + R"re((?:[a-zA-Z0-9_/:\.\-1\+]*))re"; + + static std::string quotedStringRegex = + R"re((?:\$?'[^']*'))re"; - while (!lines.empty()) { - auto line = getLine(); + static std::string arrayRegex = + R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re"; - auto eq = line.find('='); - if (eq != std::string::npos) { - std::string name(line, 0, eq); - std::string value(line, eq + 1); - // FIXME: parse arrays - res.env.insert({name, value}); + static std::regex varRegex( + "^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + arrayRegex + ")\n"); + + static std::regex functionRegex( + "^" + varNameRegex + " \\(\\) *\n"); + + while (pos != file.end()) { + + std::smatch match; + + if (std::regex_search(pos, file.cend(), match, declareRegex)) { + pos = match[0].second; + exported.insert(match[1]); } - else if (hasSuffix(line, " () ")) { - std::string name(line, 0, line.size() - 4); - // FIXME: validate name - auto l = getLine(); - if (l != "{ ") throw Error("shell environment '%s' has unexpected line '%s'", path, l); - std::string body; - while ((l = getLine()) != "}") { - body += l; - body += '\n'; - } - res.functions.insert({name, body}); + else if (std::regex_search(pos, file.cend(), match, varRegex)) { + pos = match[0].second; + res.env.insert({match[1], Var { (bool) exported.count(match[1]), match[2] }}); } - else throw Error("shell environment '%s' has unexpected line '%s'", path, line); + else if (std::regex_search(pos, file.cend(), match, functionRegex)) { + res.bashFunctions = std::string(pos, file.cend()); + break; + } + + else throw Error("shell environment '%s' has unexpected line '%s'", + path, file.substr(pos - file.cbegin(), 60)); } return res; @@ -72,7 +93,16 @@ Path getDerivationEnvironment(ref store, Derivation drv) if (builder != "bash") throw Error("'nix shell' only works on derivations that use 'bash' as their builder"); - drv.args = {"-c", "set -e; export IN_NIX_SHELL=impure; export dontAddDisableDepTrack=1; if [[ -n $stdenv ]]; then source $stdenv/setup; fi; set > $out"}; + drv.args = { + "-c", + "set -e; " + "export IN_NIX_SHELL=impure; " + "export dontAddDisableDepTrack=1; " + "if [[ -n $stdenv ]]; then " + " source $stdenv/setup; " + "fi; " + "export > $out; " + "set >> $out "}; /* Remove derivation checks. */ drv.env.erase("allowedReferences"); @@ -146,18 +176,16 @@ struct Common : InstallableCommand, MixProfile out << "nix_saved_PATH=\"$PATH\"\n"; for (auto & i : buildEnvironment.env) { - // FIXME: shellEscape - // FIXME: figure out what to export - // FIXME: handle arrays - if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) - out << fmt("export %s=%s\n", i.first, i.second); + if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) { + out << fmt("%s=%s\n", i.first, i.second.value); + if (i.second.exported) + out << fmt("export %s\n", i.first); + } } out << "PATH=\"$PATH:$nix_saved_PATH\"\n"; - for (auto & i : buildEnvironment.functions) { - out << fmt("%s () {\n%s\n}\n", i.first, i.second); - } + out << buildEnvironment.bashFunctions << "\n"; // FIXME: set outputs -- cgit v1.2.3 From 780c1a8f271113cea7d70d57f44afce5da5928d6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 2 Oct 2019 10:52:56 +0200 Subject: nix dev-shell: Ignore $NIX_LOG_FD --- src/nix/shell.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index db7106793..5e1d53aff 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -157,6 +157,7 @@ struct Common : InstallableCommand, MixProfile "HOME", // FIXME: don't ignore in pure mode? "NIX_BUILD_TOP", "NIX_ENFORCE_PURITY", + "NIX_LOG_FD", "PPID", "PWD", "SHELLOPTS", -- cgit v1.2.3 From d24bfe29a1edd30900f9205b9446252f3f3836af Mon Sep 17 00:00:00 2001 From: Emilio Karakey Date: Mon, 7 Oct 2019 18:28:10 -0500 Subject: deleted comment --- src/nix/shell.cc | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 5e1d53aff..a4488b229 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -30,7 +30,6 @@ BuildEnvironment readEnvironment(const Path & path) std::set exported; auto file = readFile(path); - //auto file = readFile("/tmp/x"); auto pos = file.cbegin(); -- cgit v1.2.3 From 21304c11f926892b1a0098ba5d424445d5ae30d6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 8 Oct 2019 16:30:04 +0200 Subject: uri -> url for consistency --- src/nix/flake.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d0135143c..cf4fcf722 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -19,21 +19,21 @@ using namespace nix::flake; class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions { - std::string flakeUri = "."; + std::string flakeUrl = "."; public: FlakeCommand() { - expectArg("flake-uri", &flakeUri, true); + expectArg("flake-url", &flakeUrl, true); } FlakeRef getFlakeRef() { - if (flakeUri.find('/') != std::string::npos || flakeUri == ".") - return FlakeRef(flakeUri, true); + if (flakeUrl.find('/') != std::string::npos || flakeUrl == ".") + return FlakeRef(flakeUrl, true); else - return FlakeRef(flakeUri); + return FlakeRef(flakeUrl); } Flake getFlake() @@ -74,7 +74,7 @@ struct CmdFlakeList : EvalCommand static void printSourceInfo(const SourceInfo & sourceInfo) { - std::cout << fmt("URI: %s\n", sourceInfo.resolvedRef.to_string()); + std::cout << fmt("URL: %s\n", sourceInfo.resolvedRef.to_string()); if (sourceInfo.resolvedRef.ref) std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref); if (sourceInfo.resolvedRef.rev) @@ -89,7 +89,7 @@ static void printSourceInfo(const SourceInfo & sourceInfo) static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) { - j["uri"] = sourceInfo.resolvedRef.to_string(); + j["url"] = sourceInfo.resolvedRef.to_string(); if (sourceInfo.resolvedRef.ref) j["branch"] = *sourceInfo.resolvedRef.ref; if (sourceInfo.resolvedRef.rev) @@ -454,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON struct CmdFlakeAdd : MixEvalArgs, Command { FlakeUri alias; - FlakeUri uri; + FlakeUri url; std::string description() override { @@ -464,7 +464,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command CmdFlakeAdd() { expectArg("alias", &alias); - expectArg("flake-uri", &uri); + expectArg("flake-url", &url); } void run() override @@ -473,7 +473,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command Path userRegistryPath = getUserRegistryPath(); auto userRegistry = readRegistry(userRegistryPath); userRegistry->entries.erase(aliasRef); - userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(uri)); + userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(url)); writeRegistry(*userRegistry, userRegistryPath); } }; -- cgit v1.2.3 From a0bd088d8485039ccff7c7bab761cfce1b06a6e9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 8 Oct 2019 16:44:09 +0200 Subject: Move addRegistrOverrides --- src/nix/build.cc | 2 -- src/nix/flake.cc | 1 - src/nix/installables.cc | 4 +++- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index f63150012..4fd1de026 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -52,8 +52,6 @@ struct CmdBuild : MixDryRun, MixProfile, InstallablesCommand { auto buildables = build(store, dryRun ? DryRun : Build, installables); - auto evalState = std::make_shared(searchPath, store); - evalState->addRegistryOverrides(registryOverrides); if (dryRun) return; if (outLink != "") { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index cf4fcf722..e6dc5680f 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -129,7 +129,6 @@ struct CmdFlakeDeps : FlakeCommand void run(nix::ref store) override { auto evalState = getEvalState(); - evalState->addRegistryOverrides(registryOverrides); std::queue todo; todo.push(resolveFlake()); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 867133653..5611a84ae 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -56,8 +56,10 @@ SourceExprCommand::SourceExprCommand() ref EvalCommand::getEvalState() { - if (!evalState) + if (!evalState) { evalState = std::make_shared(searchPath, getStore()); + evalState->addRegistryOverrides(registryOverrides); + } return ref(evalState); } -- cgit v1.2.3 From 0bc8f1669d542ef65fbfa80ea3728f4dd36d63f2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 14 Oct 2019 14:40:16 +0200 Subject: Move code around --- src/nix/command.hh | 49 +--------- src/nix/installables.cc | 237 ++++++++++++++++++++++-------------------------- src/nix/installables.hh | 97 ++++++++++++++++++++ 3 files changed, 205 insertions(+), 178 deletions(-) create mode 100644 src/nix/installables.hh (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 92f606bbe..802dd9828 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -1,15 +1,15 @@ #pragma once +#include "installables.hh" #include "args.hh" #include "common-eval-args.hh" + #include namespace nix { extern std::string programPath; -struct Value; -class Bindings; class EvalState; class Store; @@ -30,51 +30,6 @@ private: std::shared_ptr _store; }; -struct Buildable -{ - Path drvPath; // may be empty - std::map outputs; -}; - -typedef std::vector Buildables; - -struct App -{ - PathSet context; - Path program; - // FIXME: add args, sandbox settings, metadata, ... - - App(EvalState & state, Value & vApp); -}; - -struct Installable -{ - virtual ~Installable() { } - - virtual std::string what() = 0; - - virtual Buildables toBuildables() - { - throw Error("argument '%s' cannot be built", what()); - } - - Buildable toBuildable(); - - App toApp(EvalState & state); - - virtual Value * toValue(EvalState & state) - { - throw Error("argument '%s' cannot be evaluated", what()); - } - - /* Return a value only if this installable is a store path or a - symlink to it. */ - virtual std::optional getStorePath() - { - return {}; - } -}; - struct EvalCommand : virtual StoreCommand, MixEvalArgs { ref getEvalState(); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 5611a84ae..93509955a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -1,3 +1,4 @@ +#include "installables.hh" #include "command.hh" #include "attr-path.hh" #include "common-eval-args.hh" @@ -111,65 +112,58 @@ struct InstallableStorePath : Installable } }; -struct InstallableValue : Installable +std::vector InstallableValue::toDerivations() { - SourceExprCommand & cmd; + auto state = cmd.getEvalState(); - InstallableValue(SourceExprCommand & cmd) : cmd(cmd) { } + auto v = toValue(*state); - virtual std::vector toDerivations() - { - auto state = cmd.getEvalState(); - - auto v = toValue(*state); - - Bindings & autoArgs = *cmd.getAutoArgs(*state); + Bindings & autoArgs = *cmd.getAutoArgs(*state); - DrvInfos drvInfos; - getDerivations(*state, *v, "", autoArgs, drvInfos, false); + DrvInfos drvInfos; + getDerivations(*state, *v, "", autoArgs, drvInfos, false); - std::vector res; - for (auto & drvInfo : drvInfos) { - res.push_back({ - drvInfo.queryDrvPath(), - drvInfo.queryOutPath(), - drvInfo.queryOutputName() - }); - } - - return res; + std::vector res; + for (auto & drvInfo : drvInfos) { + res.push_back({ + drvInfo.queryDrvPath(), + drvInfo.queryOutPath(), + drvInfo.queryOutputName() + }); } - Buildables toBuildables() override - { - Buildables res; + return res; +} - PathSet drvPaths; +Buildables InstallableValue::toBuildables() +{ + Buildables res; - for (auto & drv : toDerivations()) { - Buildable b{drv.drvPath}; - drvPaths.insert(b.drvPath); + PathSet drvPaths; - auto outputName = drv.outputName; - if (outputName == "") - throw Error("derivation '%s' lacks an 'outputName' attribute", b.drvPath); + for (auto & drv : toDerivations()) { + Buildable b{drv.drvPath}; + drvPaths.insert(b.drvPath); - b.outputs.emplace(outputName, drv.outPath); + auto outputName = drv.outputName; + if (outputName == "") + throw Error("derivation '%s' lacks an 'outputName' attribute", b.drvPath); - res.push_back(std::move(b)); - } + b.outputs.emplace(outputName, drv.outPath); - // Hack to recognize .all: if all drvs have the same drvPath, - // merge the buildables. - if (drvPaths.size() == 1) { - Buildable b{*drvPaths.begin()}; - for (auto & b2 : res) - b.outputs.insert(b2.outputs.begin(), b2.outputs.end()); - return {b}; - } else - return res; + res.push_back(std::move(b)); } -}; + + // Hack to recognize .all: if all drvs have the same drvPath, + // merge the buildables. + if (drvPaths.size() == 1) { + Buildable b{*drvPaths.begin()}; + for (auto & b2 : res) + b.outputs.insert(b2.outputs.begin(), b2.outputs.end()); + return {b}; + } else + return res; +} struct InstallableExpr : InstallableValue { @@ -254,123 +248,104 @@ void makeFlakeClosureGCRoot(Store & store, store.addIndirectRoot(symlink); } -struct InstallableFlake : InstallableValue +std::vector InstallableFlake::getActualAttrPaths() { - FlakeRef flakeRef; - Strings attrPaths; - Strings prefixes; - - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, Strings attrPaths) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(std::move(attrPaths)) - { } + std::vector res; - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, - std::string attrPath, Strings && prefixes) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, - prefixes(prefixes) - { } - - std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } - - std::vector getActualAttrPaths() - { - std::vector res; - - for (auto & prefix : prefixes) - res.push_back(prefix + *attrPaths.begin()); + for (auto & prefix : prefixes) + res.push_back(prefix + *attrPaths.begin()); - for (auto & s : attrPaths) - res.push_back(s); + for (auto & s : attrPaths) + res.push_back(s); - return res; - } + return res; +} - Value * getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake) - { - auto vFlake = state.allocValue(); +Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake) +{ + auto vFlake = state.allocValue(); - callFlake(state, resFlake, *vFlake); + callFlake(state, resFlake, *vFlake); - makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake); + makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake); - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); - assert(aOutputs); + auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + assert(aOutputs); - state.forceValue(*(*aOutputs)->value); + state.forceValue(*(*aOutputs)->value); - return (*aOutputs)->value; - } + return (*aOutputs)->value; +} - std::vector toDerivations() override - { - auto state = cmd.getEvalState(); +std::vector InstallableFlake::toDerivations() +{ + auto state = cmd.getEvalState(); - auto resFlake = resolveFlake(*state, flakeRef, cmd.getLockFileMode()); + auto resFlake = resolveFlake(*state, flakeRef, cmd.getLockFileMode()); - Value * vOutputs = nullptr; + Value * vOutputs = nullptr; - auto emptyArgs = state->allocBindings(0); + auto emptyArgs = state->allocBindings(0); - auto & evalCache = flake::EvalCache::singleton(); + auto & evalCache = flake::EvalCache::singleton(); - auto fingerprint = resFlake.getFingerprint(); + auto fingerprint = resFlake.getFingerprint(); - for (auto & attrPath : getActualAttrPaths()) { - auto drv = evalCache.getDerivation(fingerprint, attrPath); - if (drv) { - if (state->store->isValidPath(drv->drvPath)) - return {*drv}; - } + for (auto & attrPath : getActualAttrPaths()) { + auto drv = evalCache.getDerivation(fingerprint, attrPath); + if (drv) { + if (state->store->isValidPath(drv->drvPath)) + return {*drv}; + } - if (!vOutputs) - vOutputs = getFlakeOutputs(*state, resFlake); + if (!vOutputs) + vOutputs = getFlakeOutputs(*state, resFlake); - try { - auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs); - state->forceValue(*v); + try { + auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs); + state->forceValue(*v); - auto drvInfo = getDerivation(*state, *v, false); - if (!drvInfo) - throw Error("flake output attribute '%s' is not a derivation", attrPath); + auto drvInfo = getDerivation(*state, *v, false); + if (!drvInfo) + throw Error("flake output attribute '%s' is not a derivation", attrPath); - auto drv = flake::EvalCache::Derivation{ - drvInfo->queryDrvPath(), - drvInfo->queryOutPath(), - drvInfo->queryOutputName() - }; + auto drv = flake::EvalCache::Derivation{ + drvInfo->queryDrvPath(), + drvInfo->queryOutPath(), + drvInfo->queryOutputName() + }; - evalCache.addDerivation(fingerprint, attrPath, drv); + evalCache.addDerivation(fingerprint, attrPath, drv); - return {drv}; - } catch (AttrPathNotFound & e) { - } + return {drv}; + } catch (AttrPathNotFound & e) { } - - throw Error("flake '%s' does not provide attribute %s", - flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); } - Value * toValue(EvalState & state) override - { - auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); + throw Error("flake '%s' does not provide attribute %s", + flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); +} - auto vOutputs = getFlakeOutputs(state, resFlake); +Value * InstallableFlake::toValue(EvalState & state) +{ + auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); - auto emptyArgs = state.allocBindings(0); + auto vOutputs = getFlakeOutputs(state, resFlake); - for (auto & attrPath : getActualAttrPaths()) { - try { - auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); - state.forceValue(*v); - return v; - } catch (AttrPathNotFound & e) { - } - } + auto emptyArgs = state.allocBindings(0); - throw Error("flake '%s' does not provide attribute %s", - flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); + for (auto & attrPath : getActualAttrPaths()) { + try { + auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); + state.forceValue(*v); + return v; + } catch (AttrPathNotFound & e) { + } } -}; + + throw Error("flake '%s' does not provide attribute %s", + flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); +} // FIXME: extend std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; diff --git a/src/nix/installables.hh b/src/nix/installables.hh new file mode 100644 index 000000000..020a61a2b --- /dev/null +++ b/src/nix/installables.hh @@ -0,0 +1,97 @@ +#pragma once + +#include "util.hh" +#include "flake/eval-cache.hh" + +#include + +namespace nix { + +struct Value; +class EvalState; +class SourceExprCommand; + +struct Buildable +{ + Path drvPath; // may be empty + std::map outputs; +}; + +typedef std::vector Buildables; + +struct App +{ + PathSet context; + Path program; + // FIXME: add args, sandbox settings, metadata, ... + + App(EvalState & state, Value & vApp); +}; + +struct Installable +{ + virtual ~Installable() { } + + virtual std::string what() = 0; + + virtual Buildables toBuildables() + { + throw Error("argument '%s' cannot be built", what()); + } + + Buildable toBuildable(); + + App toApp(EvalState & state); + + virtual Value * toValue(EvalState & state) + { + throw Error("argument '%s' cannot be evaluated", what()); + } + + /* Return a value only if this installable is a store path or a + symlink to it. */ + virtual std::optional getStorePath() + { + return {}; + } +}; + +struct InstallableValue : Installable +{ + SourceExprCommand & cmd; + + InstallableValue(SourceExprCommand & cmd) : cmd(cmd) { } + + virtual std::vector toDerivations(); + + Buildables toBuildables() override; +}; + +struct InstallableFlake : InstallableValue +{ + FlakeRef flakeRef; + Strings attrPaths; + Strings prefixes; + + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, Strings attrPaths) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(std::move(attrPaths)) + { } + + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, + std::string attrPath, Strings && prefixes) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, + prefixes(prefixes) + { } + + std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } + + std::vector getActualAttrPaths(); + + Value * getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake); + + std::vector toDerivations() override; + + Value * toValue(EvalState & state) override; +}; + +} -- cgit v1.2.3 From 7d38060a0da2698052e84a0cfee422d409a38187 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 15 Oct 2019 17:52:10 +0200 Subject: Support non-x86_64-linux system types in flakes A command like $ nix run nixpkgs#hello will now build the attribute 'packages.${system}.hello' rather than 'packages.hello'. Note that this does mean that the flake needs to export an attribute for every system type it supports, and you can't build on unsupported systems. So 'packages' typically looks like this: packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: { hello = ...; }); The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp' outputs similarly are now attrsets that map system types to derivations/apps. 'nix flake check' checks that the derivations for all platforms evaluate correctly, but only builds the derivations in 'checks.${system}'. Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs and --arg, but I think it's reasonable to say that flakes shouldn't support those.) The alternative to attribute selection is to pass the system type as an argument to the flake's 'outputs' function, e.g. 'outputs = { self, nixpkgs, system }: ...'. However, that approach would be at odds with hermetic evaluation and make it impossible to enumerate the packages provided by a flake. --- src/nix/command.hh | 17 ++--------- src/nix/flake.cc | 80 +++++++++++++++++++++++++++++++++++++------------ src/nix/installables.cc | 17 +++++++++++ src/nix/run.cc | 4 +-- src/nix/shell.cc | 2 +- 5 files changed, 83 insertions(+), 37 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 802dd9828..93f324071 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -64,22 +64,9 @@ struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions std::shared_ptr parseInstallable( ref store, const std::string & installable); - virtual Strings getDefaultFlakeAttrPaths() - { - return {"defaultPackage"}; - } + virtual Strings getDefaultFlakeAttrPaths(); - virtual Strings getDefaultFlakeAttrPathPrefixes() - { - return { - // As a convenience, look for the attribute in - // 'outputs.packages'. - "packages.", - // As a temporary hack until Nixpkgs is properly converted - // to provide a clean 'packages' set, look in 'legacyPackages'. - "legacyPackages." - }; - } + virtual Strings getDefaultFlakeAttrPathPrefixes(); }; enum RealiseMode { Build, NoBuild, DryRun }; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e6dc5680f..d928af3b9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -251,6 +251,12 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON auto state = getEvalState(); auto flake = resolveFlake(); + auto checkSystemName = [&](const std::string & system, const Pos & pos) { + // FIXME: what's the format of "system"? + if (system.find('-') == std::string::npos) + throw Error("'%s' is not a valid system type, at %s", system, pos); + }; + auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { auto drvInfo = getDerivation(*state, v, false); @@ -374,34 +380,70 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON if (name == "checks") { state->forceAttrs(vOutput, pos); - for (auto & attr : *vOutput.attrs) - drvPaths.insert(checkDerivation( - name + "." + (std::string) attr.name, *attr.value, *attr.pos)); + for (auto & attr : *vOutput.attrs) { + checkSystemName(attr.name, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos); + for (auto & attr2 : *attr.value->attrs) { + auto drvPath = checkDerivation( + fmt("%s.%s.%s", name, attr.name, attr2.name), + *attr2.value, *attr2.pos); + if ((std::string) attr.name == settings.thisSystem.get()) + drvPaths.insert(drvPath); + } + } } else if (name == "packages") { state->forceAttrs(vOutput, pos); - for (auto & attr : *vOutput.attrs) - checkDerivation( - name + "." + (std::string) attr.name, *attr.value, *attr.pos); + for (auto & attr : *vOutput.attrs) { + checkSystemName(attr.name, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos); + for (auto & attr2 : *attr.value->attrs) + checkDerivation( + fmt("%s.%s.%s", name, attr.name, attr2.name), + *attr2.value, *attr2.pos); + } } else if (name == "apps") { state->forceAttrs(vOutput, pos); - for (auto & attr : *vOutput.attrs) - checkApp( - name + "." + (std::string) attr.name, *attr.value, *attr.pos); + for (auto & attr : *vOutput.attrs) { + checkSystemName(attr.name, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos); + for (auto & attr2 : *attr.value->attrs) + checkApp( + fmt("%s.%s.%s", name, attr.name, attr2.name), + *attr2.value, *attr2.pos); + } } - else if (name == "defaultPackage" || name == "devShell") - checkDerivation(name, vOutput, pos); + else if (name == "defaultPackage" || name == "devShell") { + state->forceAttrs(vOutput, pos); + for (auto & attr : *vOutput.attrs) { + checkSystemName(attr.name, *attr.pos); + checkDerivation( + fmt("%s.%s", name, attr.name), + *attr.value, *attr.pos); + } + } - else if (name == "defaultApp") - checkApp(name, vOutput, pos); + else if (name == "defaultApp") { + state->forceAttrs(vOutput, pos); + for (auto & attr : *vOutput.attrs) { + checkSystemName(attr.name, *attr.pos); + checkApp( + fmt("%s.%s", name, attr.name), + *attr.value, *attr.pos); + } + } - else if (name == "legacyPackages") - // FIXME: do getDerivations? - ; + else if (name == "legacyPackages") { + state->forceAttrs(vOutput, pos); + for (auto & attr : *vOutput.attrs) { + checkSystemName(attr.name, *attr.pos); + // FIXME: do getDerivations? + } + } else if (name == "overlay") checkOverlay(name, vOutput, pos); @@ -409,7 +451,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON else if (name == "overlays") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkOverlay(name + "." + (std::string) attr.name, + checkOverlay(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); } @@ -419,14 +461,14 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON else if (name == "nixosModules") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkModule(name + "." + (std::string) attr.name, + checkModule(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); } else if (name == "nixosConfigurations") { state->forceAttrs(vOutput, pos); for (auto & attr : *vOutput.attrs) - checkNixOSConfiguration(name + "." + (std::string) attr.name, + checkNixOSConfiguration(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 93509955a..bc8fbeb8f 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -55,6 +55,23 @@ SourceExprCommand::SourceExprCommand() .dest(&file); } +Strings SourceExprCommand::getDefaultFlakeAttrPaths() +{ + return {"defaultPackage." + settings.thisSystem.get()}; +} + +Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() +{ + return { + // As a convenience, look for the attribute in + // 'outputs.packages'. + "packages." + settings.thisSystem.get() + ".", + // As a temporary hack until Nixpkgs is properly converted + // to provide a clean 'packages' set, look in 'legacyPackages'. + "legacyPackages." + settings.thisSystem.get() + "." + }; +} + ref EvalCommand::getEvalState() { if (!evalState) { diff --git a/src/nix/run.cc b/src/nix/run.cc index 01ec9a6f8..d444fd2eb 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -215,12 +215,12 @@ struct CmdApp : InstallableCommand, RunCommon Strings getDefaultFlakeAttrPaths() override { - return {"defaultApp"}; + return {"defaultApp." + settings.thisSystem.get()}; } Strings getDefaultFlakeAttrPathPrefixes() override { - return {"apps."}; + return {"apps." + settings.thisSystem.get() + "."}; } void run(ref store) override diff --git a/src/nix/shell.cc b/src/nix/shell.cc index a4488b229..50d0f9c88 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -198,7 +198,7 @@ struct Common : InstallableCommand, MixProfile Strings getDefaultFlakeAttrPaths() override { - return {"devShell", "defaultPackage"}; + return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()}; } Path getShellOutPath(ref store) -- cgit v1.2.3 From 14a89aa8cdc4cf9d22187a34513a6f9709be0c5d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 15 Oct 2019 19:53:29 +0200 Subject: Fix 'nix flake init' --- src/nix/flake-template.nix | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix index bec613f6c..eb8eb14fc 100644 --- a/src/nix/flake-template.nix +++ b/src/nix/flake-template.nix @@ -1,15 +1,11 @@ { - name = "hello"; - description = "A flake for building Hello World"; - epoch = 201906; - - requires = [ "nixpkgs" ]; + edition = 201909; - provides = deps: rec { + outputs = { self, nixpkgs }: { - packages.hello = deps.nixpkgs.provides.packages.hello; + packages.x86_64-linux.hello = nixpkgs.packages.x86_64-linux.hello; }; } -- cgit v1.2.3 From 8e478c234100cf03ea1b777d4bd42a9be7be9e8c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 16 Oct 2019 17:45:09 +0200 Subject: Add experimental-features setting Experimental features are now opt-in. There are currently two experimental features: "nix-command" (which enables the "nix" command), and "flakes" (which enables support for flakes). This will allow us to merge experimental features more quickly, without committing to supporting them indefinitely. Typical usage: $ nix build --experimental-features 'nix-command flakes' nixpkgs#hello --- src/nix/main.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index eedf8656e..8cdeed8f5 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -149,6 +149,8 @@ void mainWrapped(int argc, char * * argv) args.parseCmdline(argvToStrings(argc, argv)); + settings.requireExperimentalFeature("nix-command"); + initPlugins(); if (!args.command) args.showHelpAndExit(); -- cgit v1.2.3 From 8426d99b0efc0b22a01db0e2e97adb72134ac5bf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 20 Oct 2019 16:43:00 +0200 Subject: Fix InstallableFlake::what() --- src/nix/installables.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 020a61a2b..a635cb96f 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -83,7 +83,7 @@ struct InstallableFlake : InstallableValue prefixes(prefixes) { } - std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } + std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); } std::vector getActualAttrPaths(); -- cgit v1.2.3 From cb1a79a96aa0602383f2fe33702f6adeee708922 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 21 Oct 2019 18:58:38 +0200 Subject: Fix build --- src/nix/make-content-addressable.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src/nix') diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc index 16344ee14..5b99b5084 100644 --- a/src/nix/make-content-addressable.cc +++ b/src/nix/make-content-addressable.cc @@ -11,11 +11,6 @@ struct CmdMakeContentAddressable : StorePathsCommand realiseMode = Build; } - std::string name() override - { - return "make-content-addressable"; - } - std::string description() override { return "rewrite a path or closure to content-addressable form"; @@ -92,4 +87,4 @@ struct CmdMakeContentAddressable : StorePathsCommand } }; -static RegisterCommand r1(make_ref()); +static auto r1 = registerCommand("make-content-addressable"); -- cgit v1.2.3 From 91a88f3acba8978d17ca40fd64e381514a51244d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 21 Oct 2019 23:38:07 +0200 Subject: Fix "nixpkgs." compatibility --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index bc8fbeb8f..38f37adb1 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -409,7 +409,7 @@ std::vector> SourceExprCommand::parseInstallables( bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), - Strings{"legacyPackages." + std::string(s, 8)})); + Strings{"legacyPackages." + settings.thisSystem.get() + "." + std::string(s, 8)})); } else if ((hash = s.rfind('#')) != std::string::npos) -- cgit v1.2.3 From ce279209363cb6a9cbbe68a13fab9d8550b721f3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 22 Oct 2019 00:21:58 +0200 Subject: Add start of 'nix profile' command --- src/nix/command.cc | 5 ++ src/nix/command.hh | 5 ++ src/nix/flake.cc | 1 + src/nix/installables.cc | 11 ++- src/nix/installables.hh | 3 + src/nix/profile.cc | 234 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 src/nix/profile.cc (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index 57f3754cc..1cb4cc92a 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -123,4 +123,9 @@ void MixProfile::updateProfile(const Buildables & buildables) updateProfile(*result); } +MixDefaultProfile::MixDefaultProfile() +{ + profile = getDefaultProfile(); +} + } diff --git a/src/nix/command.hh b/src/nix/command.hh index 546c27a71..ef29381cf 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -188,4 +188,9 @@ struct MixProfile : virtual Args, virtual StoreCommand void updateProfile(const Buildables & buildables); }; +struct MixDefaultProfile : MixProfile +{ + MixDefaultProfile(); +}; + } diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d928af3b9..6e7c5e2eb 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -653,6 +653,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command { if (!command) throw UsageError("'nix flake' requires a sub-command."); + command->prepare(); command->run(); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 38f37adb1..671cf513a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -294,7 +294,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Resolv return (*aOutputs)->value; } -std::vector InstallableFlake::toDerivations() +std::tuple InstallableFlake::toDerivation() { auto state = cmd.getEvalState(); @@ -312,7 +312,7 @@ std::vector InstallableFlake::toDerivations() auto drv = evalCache.getDerivation(fingerprint, attrPath); if (drv) { if (state->store->isValidPath(drv->drvPath)) - return {*drv}; + return {attrPath, resFlake.flake.sourceInfo.resolvedRef, *drv}; } if (!vOutputs) @@ -334,7 +334,7 @@ std::vector InstallableFlake::toDerivations() evalCache.addDerivation(fingerprint, attrPath, drv); - return {drv}; + return {attrPath, resFlake.flake.sourceInfo.resolvedRef, drv}; } catch (AttrPathNotFound & e) { } } @@ -343,6 +343,11 @@ std::vector InstallableFlake::toDerivations() flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); } +std::vector InstallableFlake::toDerivations() +{ + return {std::get<2>(toDerivation())}; +} + Value * InstallableFlake::toValue(EvalState & state) { auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); diff --git a/src/nix/installables.hh b/src/nix/installables.hh index a635cb96f..9388c673e 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -8,6 +8,7 @@ namespace nix { struct Value; +struct DrvInfo; class EvalState; class SourceExprCommand; @@ -89,6 +90,8 @@ struct InstallableFlake : InstallableValue Value * getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake); + std::tuple toDerivation(); + std::vector toDerivations() override; Value * toValue(EvalState & state) override; diff --git a/src/nix/profile.cc b/src/nix/profile.cc new file mode 100644 index 000000000..bc5c3870e --- /dev/null +++ b/src/nix/profile.cc @@ -0,0 +1,234 @@ +#include "command.hh" +#include "common-args.hh" +#include "shared.hh" +#include "store-api.hh" +#include "derivations.hh" +#include "archive.hh" +#include "builtins/buildenv.hh" +#include "flake/flakeref.hh" + +#include + +using namespace nix; + +struct ProfileElementSource +{ + FlakeRef originalRef; + FlakeRef resolvedRef; + std::string attrPath; + // FIXME: output names +}; + +struct ProfileElement +{ + PathSet storePaths; + std::optional source; + bool active = true; + // FIXME: priority +}; + +struct ProfileManifest +{ + std::vector elements; + + ProfileManifest(const Path & profile) + { + auto manifestPath = profile + "/manifest.json"; + + if (pathExists(manifestPath)) { + auto json = nlohmann::json::parse(readFile(manifestPath)); + + auto version = json.value("version", 0); + if (version != 1) + throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version); + + for (auto & e : json["elements"]) { + ProfileElement element; + for (auto & p : e["storePaths"]) + element.storePaths.insert((std::string) p); + element.active = e["active"]; + if (e.value("uri", "") != "") { + element.source = ProfileElementSource{ + FlakeRef(e["originalUri"]), + FlakeRef(e["uri"]), + e["attrPath"] + }; + } + elements.emplace_back(std::move(element)); + } + } + } + + std::string toJSON() const + { + auto array = nlohmann::json::array(); + for (auto & element : elements) { + auto paths = nlohmann::json::array(); + for (auto & path : element.storePaths) + paths.push_back(path); + nlohmann::json obj; + obj["storePaths"] = paths; + obj["active"] = element.active; + if (element.source) { + obj["originalUri"] = element.source->originalRef.to_string(); + obj["uri"] = element.source->resolvedRef.to_string(); + obj["attrPath"] = element.source->attrPath; + } + array.push_back(obj); + } + nlohmann::json json; + json["version"] = 1; + json["elements"] = array; + return json.dump(); + } + + Path build(ref store) + { + auto tempDir = createTempDir(); + + ValidPathInfo info; + + Packages pkgs; + for (auto & element : elements) { + for (auto & path : element.storePaths) { + if (element.active) + pkgs.emplace_back(path, true, 5); + info.references.insert(path); + } + } + + buildProfile(tempDir, std::move(pkgs)); + + writeFile(tempDir + "/manifest.json", toJSON()); + + /* Add the symlink tree to the store. */ + StringSink sink; + dumpPath(tempDir, sink); + + info.narHash = hashString(htSHA256, *sink.s); + info.narSize = sink.s->size(); + info.path = store->makeFixedOutputPath(true, info.narHash, "profile", info.references); + info.ca = makeFixedOutputCA(true, info.narHash); + + store->addToStore(info, sink.s); + + return info.path; + } +}; + +struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile +{ + std::string description() override + { + return "install a package into a profile"; + } + + Examples examples() override + { + return { + Example{ + "To install a package from Nixpkgs:", + "nix profile install nixpkgs#hello" + }, + Example{ + "To install a package from a specific branch of Nixpkgs:", + "nix profile install nixpkgs/release-19.09#hello" + }, + Example{ + "To install a package from a specific revision of Nixpkgs:", + "nix profile install nixpkgs/1028bb33859f8dfad7f98e1c8d185f3d1aaa7340#hello" + }, + }; + } + + void run(ref store) override + { + ProfileManifest manifest(*profile); + + PathSet pathsToBuild; + + for (auto & installable : installables) { + if (auto installable2 = std::dynamic_pointer_cast(installable)) { + auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); + + ProfileElement element; + element.storePaths = {drv.outPath}; // FIXME + element.source = ProfileElementSource{ + installable2->flakeRef, + resolvedRef, + attrPath, + }; + + pathsToBuild.insert(makeDrvPathWithOutputs(drv.drvPath, {"out"})); // FIXME + + manifest.elements.emplace_back(std::move(element)); + } else + throw Error("'nix profile install' does not support argument '%s'", installable->what()); + } + + store->buildPaths(pathsToBuild); + + updateProfile(manifest.build(store)); + } +}; + +struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile +{ + std::string description() override + { + return "info"; + } + + Examples examples() override + { + return { + Example{ + "To show what packages are installed in the default profile:", + "nix profile info" + }, + }; + } + + void run(ref store) override + { + ProfileManifest manifest(*profile); + + for (auto & element : manifest.elements) { + std::cout << fmt("%s %s\n", + element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-", + element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-", + concatStringsSep(" ", element.storePaths)); + } + } +}; + +struct CmdProfile : virtual MultiCommand, virtual Command +{ + CmdProfile() + : MultiCommand({ + {"install", []() { return make_ref(); }}, + {"info", []() { return make_ref(); }}, + }) + { } + + std::string description() override + { + return "manage Nix profiles"; + } + + void run() override + { + if (!command) + throw UsageError("'nix profile' requires a sub-command."); + command->prepare(); + command->run(); + } + + void printHelp(const string & programName, std::ostream & out) override + { + MultiCommand::printHelp(programName, out); + } +}; + +static auto r1 = registerCommand("profile"); + -- cgit v1.2.3 From 555ca59f2b34bb8f3e738789e9548895766609cf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 22 Oct 2019 00:28:16 +0200 Subject: nix profile info: Index elements --- src/nix/profile.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index bc5c3870e..2303900c0 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -14,6 +14,7 @@ using namespace nix; struct ProfileElementSource { FlakeRef originalRef; + // FIXME: record original attrpath. FlakeRef resolvedRef; std::string attrPath; // FIXME: output names @@ -193,8 +194,9 @@ struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile { ProfileManifest manifest(*profile); - for (auto & element : manifest.elements) { - std::cout << fmt("%s %s\n", + for (size_t i = 0; i < manifest.elements.size(); ++i) { + auto & element(manifest.elements[i]); + std::cout << fmt("%d %s %s\n", i, element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-", element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-", concatStringsSep(" ", element.storePaths)); -- cgit v1.2.3 From e30a0155d47a2e8a2eb9d0801b8b1602f71c5fd7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 22 Oct 2019 13:06:32 +0200 Subject: Add "nix profile remove" command --- src/nix/profile.cc | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 2303900c0..c9a6a5355 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -8,6 +8,7 @@ #include "flake/flakeref.hh" #include +#include using namespace nix; @@ -32,6 +33,8 @@ struct ProfileManifest { std::vector elements; + ProfileManifest() { } + ProfileManifest(const Path & profile) { auto manifestPath = profile + "/manifest.json"; @@ -173,11 +176,112 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile } }; +class MixProfileElementMatchers : virtual Args +{ + std::vector _matchers; + +public: + + MixProfileElementMatchers() + { + expectArgs("elements", &_matchers); + } + + typedef std::variant Matcher; + + std::vector getMatchers(ref store) + { + std::vector res; + + for (auto & s : _matchers) { + size_t n; + if (string2Int(s, n)) + res.push_back(n); + else if (store->isStorePath(s)) + res.push_back(s); + else + res.push_back(std::regex(s, std::regex::extended | std::regex::icase)); + } + + return res; + } + + bool matches(const ProfileElement & element, size_t pos, std::vector matchers) + { + for (auto & matcher : matchers) { + if (auto n = std::get_if(&matcher)) { + if (*n == pos) return true; + } else if (auto path = std::get_if(&matcher)) { + if (element.storePaths.count(*path)) return true; + } else if (auto regex = std::get_if(&matcher)) { + if (element.source + && std::regex_match(element.source->attrPath, *regex)) + return true; + } + } + + return false; + } +}; + +struct CmdProfileRemove : virtual StoreCommand, MixDefaultProfile, MixProfileElementMatchers +{ + std::string description() override + { + return "remove packages from a profile"; + } + + Examples examples() override + { + return { + Example{ + "To remove a package by attribute path:", + "nix profile remove packages.x86_64-linux.hello" + }, + Example{ + "To remove all package:", + "nix profile remove '.*'" + }, + Example{ + "To remove a package by store path:", + "nix profile remove /nix/store/rr3y0c6zyk7kjjl8y19s4lsrhn4aiq1z-hello-2.10" + }, + Example{ + "To remove a package by position:", + "nix profile remove 3" + }, + }; + } + + void run(ref store) override + { + ProfileManifest oldManifest(*profile); + + auto matchers = getMatchers(store); + + ProfileManifest newManifest; + + for (size_t i = 0; i < oldManifest.elements.size(); ++i) { + auto & element(oldManifest.elements[i]); + if (!matches(element, i, matchers)) + newManifest.elements.push_back(element); + } + + // FIXME: warn about unused matchers? + + printInfo("removed %d packages, kept %d packages", + oldManifest.elements.size() - newManifest.elements.size(), + newManifest.elements.size()); + + updateProfile(newManifest.build(store)); + } +}; + struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile { std::string description() override { - return "info"; + return "list installed packages"; } Examples examples() override @@ -196,7 +300,7 @@ struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); - std::cout << fmt("%d %s %s\n", i, + std::cout << fmt("%d %s %s %s\n", i, element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-", element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-", concatStringsSep(" ", element.storePaths)); @@ -209,6 +313,7 @@ struct CmdProfile : virtual MultiCommand, virtual Command CmdProfile() : MultiCommand({ {"install", []() { return make_ref(); }}, + {"remove", []() { return make_ref(); }}, {"info", []() { return make_ref(); }}, }) { } -- cgit v1.2.3 From af786432c53a0eb4f7a0aea2d0faf5f0655cbb05 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 22 Oct 2019 14:44:51 +0200 Subject: Add "nix profile upgrade" command --- src/nix/profile.cc | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c9a6a5355..8d387ef2e 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -239,7 +239,7 @@ struct CmdProfileRemove : virtual StoreCommand, MixDefaultProfile, MixProfileEle "nix profile remove packages.x86_64-linux.hello" }, Example{ - "To remove all package:", + "To remove all packages:", "nix profile remove '.*'" }, Example{ @@ -277,6 +277,71 @@ struct CmdProfileRemove : virtual StoreCommand, MixDefaultProfile, MixProfileEle } }; +struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProfileElementMatchers +{ + std::string description() override + { + return "upgrade packages using their most recent flake"; + } + + Examples examples() override + { + return { + Example{ + "To upgrade all packages that were installed using a mutable flake reference:", + "nix profile upgrade '.*'" + }, + Example{ + "To upgrade a specific package:", + "nix profile upgrade packages.x86_64-linux.hello" + }, + }; + } + + void run(ref store) override + { + ProfileManifest manifest(*profile); + + auto matchers = getMatchers(store); + + // FIXME: code duplication + PathSet pathsToBuild; + + for (size_t i = 0; i < manifest.elements.size(); ++i) { + auto & element(manifest.elements[i]); + if (element.source + && !element.source->originalRef.isImmutable() + && matches(element, i, matchers)) + { + Activity act(*logger, lvlChatty, actUnknown, + fmt("checking '%s' for updates", element.source->attrPath)); + + InstallableFlake installable(*this, FlakeRef(element.source->originalRef), {element.source->attrPath}); + + auto [attrPath, resolvedRef, drv] = installable.toDerivation(); + + if (element.source->resolvedRef == resolvedRef) continue; + + printInfo("upgrading '%s' from flake '%s' to '%s'", + element.source->attrPath, element.source->resolvedRef, resolvedRef); + + element.storePaths = {drv.outPath}; // FIXME + element.source = ProfileElementSource{ + installable.flakeRef, + resolvedRef, + attrPath, + }; + + pathsToBuild.insert(makeDrvPathWithOutputs(drv.drvPath, {"out"})); // FIXME + } + } + + store->buildPaths(pathsToBuild); + + updateProfile(manifest.build(store)); + } +}; + struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile { std::string description() override @@ -314,6 +379,7 @@ struct CmdProfile : virtual MultiCommand, virtual Command : MultiCommand({ {"install", []() { return make_ref(); }}, {"remove", []() { return make_ref(); }}, + {"upgrade", []() { return make_ref(); }}, {"info", []() { return make_ref(); }}, }) { } -- cgit v1.2.3 From ac9b427541cdc36fb696d6fe413872e12151ec3d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 22 Oct 2019 15:16:57 +0200 Subject: Convert old-style profile manifest --- src/nix/profile.cc | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 8d387ef2e..786ebddef 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -6,6 +6,7 @@ #include "archive.hh" #include "builtins/buildenv.hh" #include "flake/flakeref.hh" +#include "nix-env/user-env.hh" #include #include @@ -35,7 +36,7 @@ struct ProfileManifest ProfileManifest() { } - ProfileManifest(const Path & profile) + ProfileManifest(EvalState & state, const Path & profile) { auto manifestPath = profile + "/manifest.json"; @@ -61,6 +62,22 @@ struct ProfileManifest elements.emplace_back(std::move(element)); } } + + else if (pathExists(profile + "/manifest.nix")) { + // FIXME: needed because of pure mode; ugly. + if (state.allowedPaths) { + state.allowedPaths->insert(state.store->followLinksToStore(profile)); + state.allowedPaths->insert(state.store->followLinksToStore(profile + "/manifest.nix")); + } + + auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile)); + + for (auto & drvInfo : drvInfos) { + ProfileElement element; + element.storePaths = {drvInfo.queryOutPath()}; + elements.emplace_back(std::move(element)); + } + } } std::string toJSON() const @@ -147,7 +164,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile void run(ref store) override { - ProfileManifest manifest(*profile); + ProfileManifest manifest(*getEvalState(), *profile); PathSet pathsToBuild; @@ -224,7 +241,7 @@ public: } }; -struct CmdProfileRemove : virtual StoreCommand, MixDefaultProfile, MixProfileElementMatchers +struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElementMatchers { std::string description() override { @@ -255,7 +272,7 @@ struct CmdProfileRemove : virtual StoreCommand, MixDefaultProfile, MixProfileEle void run(ref store) override { - ProfileManifest oldManifest(*profile); + ProfileManifest oldManifest(*getEvalState(), *profile); auto matchers = getMatchers(store); @@ -300,7 +317,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf void run(ref store) override { - ProfileManifest manifest(*profile); + ProfileManifest manifest(*getEvalState(), *profile); auto matchers = getMatchers(store); @@ -342,7 +359,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf } }; -struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile +struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile { std::string description() override { @@ -361,7 +378,7 @@ struct CmdProfileInfo : virtual StoreCommand, MixDefaultProfile void run(ref store) override { - ProfileManifest manifest(*profile); + ProfileManifest manifest(*getEvalState(), *profile); for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); -- cgit v1.2.3 From d865085c7e66599ebcfb30b5e60c80565cc2b077 Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 31 Oct 2019 23:13:08 -0500 Subject: change deprecated attribute syntax in run examples --- src/nix/run.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/run.cc b/src/nix/run.cc index d444fd2eb..ed15527b8 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -113,15 +113,15 @@ struct CmdRun : InstallablesCommand, RunCommon }, Example{ "To start a shell providing youtube-dl from your 'nixpkgs' channel:", - "nix run nixpkgs.youtube-dl" + "nix run nixpkgs#youtube-dl" }, Example{ "To run GNU Hello:", - "nix run nixpkgs.hello -c hello --greeting 'Hi everybody!'" + "nix run nixpkgs#hello -c hello --greeting 'Hi everybody!'" }, Example{ "To run GNU Hello in a chroot store:", - "nix run --store ~/my-nix nixpkgs.hello -c hello" + "nix run --store ~/my-nix nixpkgs#hello -c hello" }, }; } -- cgit v1.2.3 From d2438f86d50399467b42284a0ba8f74e13b1defe Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 31 Oct 2019 20:46:49 -0500 Subject: environment fixes in run Move environment related code to a separate function. Create a new char** if ignoreEnvironment is set rather than calling clearEnv --- src/nix/run.cc | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'src/nix') diff --git a/src/nix/run.cc b/src/nix/run.cc index ed15527b8..33e821162 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -24,7 +24,7 @@ struct RunCommon : virtual Command { void runProgram(ref store, const std::string & program, - const Strings & args) + const Strings & args, char** env = environ) { stopProgressBar(); @@ -46,12 +46,12 @@ struct RunCommon : virtual Command Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, program }; for (auto & arg : args) helperArgs.push_back(arg); - execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data()); + execve(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data(), env); throw SysError("could not execute chroot helper"); } - execvp(program.c_str(), stringsToCharPtrs(args).data()); + execvpe(program.c_str(), stringsToCharPtrs(args).data(), env); throw SysError("unable to execute '%s'", program); } @@ -61,6 +61,7 @@ struct CmdRun : InstallablesCommand, RunCommon { std::vector command = { "bash" }; StringSet keep, unset; + Strings stringEnv; bool ignoreEnvironment = false; CmdRun() @@ -126,42 +127,44 @@ struct CmdRun : InstallablesCommand, RunCommon }; } - void run(ref store) override - { - auto outPaths = toStorePaths(store, Build, installables); - - auto accessor = store->getFSAccessor(); - + char** newEnviron() { if (ignoreEnvironment) { if (!unset.empty()) throw UsageError("--unset does not make sense with --ignore-environment"); - std::map kept; - for (auto & var : keep) { - auto s = getenv(var.c_str()); - if (s) kept[var] = s; + for (const auto & var : keep) { + auto val = getenv(var.c_str()); + if (val) stringEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); } - clearEnv(); - - for (auto & var : kept) - setenv(var.first.c_str(), var.second.c_str(), 1); + return stringsToCharPtrs(stringEnv).data(); } else { - if (!keep.empty()) throw UsageError("--keep does not make sense without --ignore-environment"); - for (auto & var : unset) + for (const auto & var : unset) unsetenv(var.c_str()); + + return environ; } + } + + void run(ref store) override + { + auto outPaths = toStorePaths(store, Build, installables); + + auto accessor = store->getFSAccessor(); + std::unordered_set done; std::queue todo; for (auto & path : outPaths) todo.push(path); - auto unixPath = tokenizeString(getEnv("PATH"), ":"); + Strings unixPath; + if (!ignoreEnvironment || keep.find("PATH") != keep.end()) + unixPath = tokenizeString(getEnv("PATH"), ":"); while (!todo.empty()) { Path path = todo.front(); @@ -179,11 +182,16 @@ struct CmdRun : InstallablesCommand, RunCommon } setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); + if (ignoreEnvironment) { + keep.emplace("PATH"); + } else { + unset.erase("PATH"); + } Strings args; for (auto & arg : command) args.push_back(arg); - runProgram(store, *command.begin(), args); + runProgram(store, *command.begin(), args, newEnviron()); } }; -- cgit v1.2.3 From 693e8b1286dc3f39eb00871c91aaf96773e24a68 Mon Sep 17 00:00:00 2001 From: matthew Date: Mon, 4 Nov 2019 18:40:25 -0600 Subject: changes --- src/nix/run.cc | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'src/nix') diff --git a/src/nix/run.cc b/src/nix/run.cc index 33e821162..d65a642c2 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -24,7 +24,7 @@ struct RunCommon : virtual Command { void runProgram(ref store, const std::string & program, - const Strings & args, char** env = environ) + const Strings & args) { stopProgressBar(); @@ -46,12 +46,12 @@ struct RunCommon : virtual Command Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, program }; for (auto & arg : args) helperArgs.push_back(arg); - execve(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data(), env); + execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data()); throw SysError("could not execute chroot helper"); } - execvpe(program.c_str(), stringsToCharPtrs(args).data(), env); + execvp(program.c_str(), stringsToCharPtrs(args).data()); throw SysError("unable to execute '%s'", program); } @@ -127,7 +127,7 @@ struct CmdRun : InstallablesCommand, RunCommon }; } - char** newEnviron() { + void setNewEnviron() { if (ignoreEnvironment) { if (!unset.empty()) @@ -138,7 +138,7 @@ struct CmdRun : InstallablesCommand, RunCommon if (val) stringEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); } - return stringsToCharPtrs(stringEnv).data(); + environ = stringsToCharPtrs(stringEnv).data(); } else { if (!keep.empty()) @@ -146,8 +146,6 @@ struct CmdRun : InstallablesCommand, RunCommon for (const auto & var : unset) unsetenv(var.c_str()); - - return environ; } } @@ -162,9 +160,9 @@ struct CmdRun : InstallablesCommand, RunCommon std::queue todo; for (auto & path : outPaths) todo.push(path); - Strings unixPath; - if (!ignoreEnvironment || keep.find("PATH") != keep.end()) - unixPath = tokenizeString(getEnv("PATH"), ":"); + setNewEnviron(); + + auto unixPath = tokenizeString(getEnv("PATH"), ":"); while (!todo.empty()) { Path path = todo.front(); @@ -182,16 +180,11 @@ struct CmdRun : InstallablesCommand, RunCommon } setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); - if (ignoreEnvironment) { - keep.emplace("PATH"); - } else { - unset.erase("PATH"); - } Strings args; for (auto & arg : command) args.push_back(arg); - runProgram(store, *command.begin(), args, newEnviron()); + runProgram(store, *command.begin(), args); } }; -- cgit v1.2.3 From 75c897cf3d2be5cc156a87ec54c6726e8dc2a926 Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 7 Nov 2019 17:16:48 -0600 Subject: Factor out code to handle environment in run into MixEnvironment --- src/nix/command.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/nix/command.hh | 15 ++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index 1cb4cc92a..aa5c8cf1e 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -128,4 +128,48 @@ MixDefaultProfile::MixDefaultProfile() profile = getDefaultProfile(); } +MixEnvironment::MixEnvironment() : ignoreEnvironment(false) { + mkFlag() + .longName("ignore-environment") + .shortName('i') + .description("clear the entire environment (except those specified with --keep)") + .set(&ignoreEnvironment, true); + + mkFlag() + .longName("keep") + .shortName('k') + .description("keep specified environment variable") + .arity(1) + .labels({"name"}) + .handler([&](std::vector ss) { keep.insert(ss.front()); }); + + mkFlag() + .longName("unset") + .shortName('u') + .description("unset specified environment variable") + .arity(1) + .labels({"name"}) + .handler([&](std::vector ss) { unset.insert(ss.front()); }); +} + +void MixEnvironment::setEnviron() { + if (ignoreEnvironment) { + if (!unset.empty()) + throw UsageError("--unset does not make sense with --ignore-environment"); + + for (const auto & var : keep) { + auto val = getenv(var.c_str()); + if (val) stringEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); + } + vectorEnv = stringsToCharPtrs(stringsEnv); + environ = vectorEnv.data(); + } else { + if (!keep.empty()) + throw UsageError("--keep does not make sense without --ignore-environment"); + + for (const auto & var : unset) + unsetenv(var.c_str()); + } +} + } diff --git a/src/nix/command.hh b/src/nix/command.hh index 13f3a0dc9..52fb4e1f5 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -194,4 +194,17 @@ struct MixDefaultProfile : MixProfile MixDefaultProfile(); }; -} +struct MixEnvironment : virtual Args { + + StringSet keep, unset; + Strings stringsEnv; + std::vector vectorEnv; + bool ignoreEnvironment; + + MixEnvironment(); + + /* Modify global environ based on ignoreEnvironment, keep, and unset. It's expected that exec will be called before this class goes out of scope, otherwise environ will become invalid. */ + void setEnviron(); +}; + +} \ No newline at end of file -- cgit v1.2.3 From 6419f5028b30e8e43222d71d9fd45fd674eed1b7 Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 7 Nov 2019 17:18:31 -0600 Subject: use MixEnvironment in run and shell --- src/nix/run.cc | 51 ++------------------------------------------------- src/nix/shell.cc | 4 +++- 2 files changed, 5 insertions(+), 50 deletions(-) (limited to 'src/nix') diff --git a/src/nix/run.cc b/src/nix/run.cc index d65a642c2..0fbd0b8a8 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -57,12 +57,9 @@ struct RunCommon : virtual Command } }; -struct CmdRun : InstallablesCommand, RunCommon +struct CmdRun : InstallablesCommand, RunCommon, MixEnvironment { std::vector command = { "bash" }; - StringSet keep, unset; - Strings stringEnv; - bool ignoreEnvironment = false; CmdRun() { @@ -76,28 +73,6 @@ struct CmdRun : InstallablesCommand, RunCommon if (ss.empty()) throw UsageError("--command requires at least one argument"); command = ss; }); - - mkFlag() - .longName("ignore-environment") - .shortName('i') - .description("clear the entire environment (except those specified with --keep)") - .set(&ignoreEnvironment, true); - - mkFlag() - .longName("keep") - .shortName('k') - .description("keep specified environment variable") - .arity(1) - .labels({"name"}) - .handler([&](std::vector ss) { keep.insert(ss.front()); }); - - mkFlag() - .longName("unset") - .shortName('u') - .description("unset specified environment variable") - .arity(1) - .labels({"name"}) - .handler([&](std::vector ss) { unset.insert(ss.front()); }); } std::string description() override @@ -127,28 +102,6 @@ struct CmdRun : InstallablesCommand, RunCommon }; } - void setNewEnviron() { - if (ignoreEnvironment) { - - if (!unset.empty()) - throw UsageError("--unset does not make sense with --ignore-environment"); - - for (const auto & var : keep) { - auto val = getenv(var.c_str()); - if (val) stringEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); - } - - environ = stringsToCharPtrs(stringEnv).data(); - - } else { - if (!keep.empty()) - throw UsageError("--keep does not make sense without --ignore-environment"); - - for (const auto & var : unset) - unsetenv(var.c_str()); - } - } - void run(ref store) override { auto outPaths = toStorePaths(store, Build, installables); @@ -160,7 +113,7 @@ struct CmdRun : InstallablesCommand, RunCommon std::queue todo; for (auto & path : outPaths) todo.push(path); - setNewEnviron(); + setEnviron(); auto unixPath = tokenizeString(getEnv("PATH"), ":"); diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 50d0f9c88..5e13604bc 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -229,7 +229,7 @@ struct Common : InstallableCommand, MixProfile } }; -struct CmdDevShell : Common +struct CmdDevShell : Common, MixEnvironment { std::string description() override { @@ -275,6 +275,8 @@ struct CmdDevShell : Common auto shell = getEnv("SHELL", "bash"); + setEnviron(); + auto args = Strings{baseNameOf(shell), "--rcfile", rcFilePath}; restoreAffinity(); -- cgit v1.2.3 From 8d2eb1ff56ad17fe20e7f42a2e4b9b790640e806 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 Nov 2019 12:45:48 +0100 Subject: nix dev-shell: Improve bash output parsing Fixes handling of '=' in unquoted strings and escaped characters in $'...' strings. --- src/nix/shell.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 50d0f9c88..6fa471dfc 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -29,6 +29,8 @@ BuildEnvironment readEnvironment(const Path & path) std::set exported; + debug("reading environment file '%s'", path); + auto file = readFile(path); auto pos = file.cbegin(); @@ -41,10 +43,10 @@ BuildEnvironment readEnvironment(const Path & path) R"re((?:="((?:[^"\\]|\\.)*)")?\n)re"); static std::string simpleStringRegex = - R"re((?:[a-zA-Z0-9_/:\.\-1\+]*))re"; + R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re"; static std::string quotedStringRegex = - R"re((?:\$?'[^']*'))re"; + R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re"; static std::string arrayRegex = R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re"; -- cgit v1.2.3 From b8bddb63e68c727b9e5e06fab605a6c233acfcd9 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Thu, 14 Nov 2019 23:13:37 +0100 Subject: Fix attr path to nixpkgs flake in flake template This doesn't work anymore since `packages` was removed from the `nixpkgs`-fork with flake support[1], now it's only possible to refer to pkgs via `legacyPackages`. [1] https://github.com/edolstra/nixpkgs/commit/49c9b71e4ca98722cecd64ce8d53c1a4b9c46b64 --- src/nix/flake-template.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix index eb8eb14fc..321961013 100644 --- a/src/nix/flake-template.nix +++ b/src/nix/flake-template.nix @@ -5,7 +5,7 @@ outputs = { self, nixpkgs }: { - packages.x86_64-linux.hello = nixpkgs.packages.x86_64-linux.hello; + packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; }; } -- cgit v1.2.3 From ca8caaec5e7581c37b46f79622c81adf52f06314 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 27 Nov 2019 00:05:30 +0100 Subject: nix: Add --expr flag This replaces the '(...)' installable syntax, which is not very discoverable. The downside is that you can't have multiple expressions or mix expressions and other installables. --- src/nix/command.hh | 3 ++- src/nix/eval.cc | 2 +- src/nix/installables.cc | 33 +++++++++++++++++++++------------ 3 files changed, 24 insertions(+), 14 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 82dbb55d0..42c14927f 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -56,6 +56,7 @@ struct MixFlakeOptions : virtual Args struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions { std::optional file; + std::optional expr; SourceExprCommand(); @@ -106,7 +107,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand private: - std::string _installable{"."}; + std::string _installable{""}; }; /* A command that operates on zero or more store paths. */ diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 276fdf003..a991ee608 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -28,7 +28,7 @@ struct CmdEval : MixJSON, InstallableCommand return { Example{ "To evaluate a Nix expression given on the command line:", - "nix eval '(1 + 2)'" + "nix eval --expr '1 + 2'" }, Example{ "To evaluate a Nix expression from a file or URI:", diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 671cf513a..3e0fe2606 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -51,8 +51,14 @@ SourceExprCommand::SourceExprCommand() .shortName('f') .longName("file") .label("file") - .description("evaluate a set of attributes from FILE (deprecated)") + .description("evaluate attributes from FILE") .dest(&file); + + mkFlag() + .longName("expr") + .label("expr") + .description("evaluate attributes from EXPR") + .dest(&expr); } Strings SourceExprCommand::getDefaultFlakeAttrPaths() @@ -378,19 +384,25 @@ std::vector> SourceExprCommand::parseInstallables( { std::vector> result; - if (file) { + if (file || expr) { + if (file && expr) + throw UsageError("'--file' and '--expr' are exclusive"); + // FIXME: backward compatibility hack - evalSettings.pureEval = false; + if (file) evalSettings.pureEval = false; auto state = getEvalState(); auto vFile = state->allocValue(); - state->evalFile(lookupFileArg(*state, *file), *vFile); - if (ss.empty()) - ss = {""}; + if (file) + state->evalFile(lookupFileArg(*state, *file), *vFile); + else { + auto e = state->parseExprFromString(*expr, absPath(".")); + state->eval(e, *vFile); + } for (auto & s : ss) - result.push_back(std::make_shared(*this, vFile, s)); + result.push_back(std::make_shared(*this, vFile, s == "." ? "" : s)); } else { @@ -407,10 +419,7 @@ std::vector> SourceExprCommand::parseInstallables( size_t hash; std::optional storePath; - if (s.compare(0, 1, "(") == 0) - result.push_back(std::make_shared(*this, s)); - - else if (hasPrefix(s, "nixpkgs.")) { + if (hasPrefix(s, "nixpkgs.")) { bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), @@ -532,7 +541,7 @@ PathSet toDerivations(ref store, void InstallablesCommand::prepare() { - if (_installables.empty() && !file && useDefaultInstallables()) + if (_installables.empty() && useDefaultInstallables()) // FIXME: commands like "nix install" should not have a // default, probably. _installables.push_back("."); -- cgit v1.2.3 From 062012eee15810de522613f1ca3ca9df75fa39e9 Mon Sep 17 00:00:00 2001 From: matthew Date: Sun, 1 Dec 2019 18:34:59 -0700 Subject: typo --- src/nix/command.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index aa5c8cf1e..06655b8e2 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -159,7 +159,7 @@ void MixEnvironment::setEnviron() { for (const auto & var : keep) { auto val = getenv(var.c_str()); - if (val) stringEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); + if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); } vectorEnv = stringsToCharPtrs(stringsEnv); environ = vectorEnv.data(); -- cgit v1.2.3 From e721f99817bb7154d8098c902e25f84521a90b7f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Dec 2019 15:56:37 +0100 Subject: nix: Add --refresh as an alias for --tarball-ttl 0 --- src/nix/main.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index 5c0faf879..272c944ba 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -92,6 +92,11 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs .longName("no-net") .description("disable substituters and consider all previously downloaded files up-to-date") .handler([&]() { useNet = false; }); + + mkFlag() + .longName("refresh") + .description("consider all previously downloaded files out-of-date") + .handler([&]() { settings.tarballTtl = 0; }); } void printFlags(std::ostream & out) override -- cgit v1.2.3 From 1789c56f430b935ac2c17153317947a7fcf4f07f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Dec 2019 00:36:04 +0100 Subject: Fix macOS build https://hydra.nixos.org/build/107716759 --- src/nix/command.cc | 3 +++ src/nix/installables.hh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index de761166b..2da5736e7 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -4,6 +4,8 @@ #include "nixexpr.hh" #include "profiles.hh" +extern char * * environ; + namespace nix { Commands * RegisterCommand::commands = nullptr; @@ -175,6 +177,7 @@ void MixEnvironment::setEnviron() { auto val = getenv(var.c_str()); if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); } + vectorEnv = stringsToCharPtrs(stringsEnv); environ = vectorEnv.data(); } else { diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 9388c673e..612c8ac92 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -10,7 +10,7 @@ namespace nix { struct Value; struct DrvInfo; class EvalState; -class SourceExprCommand; +struct SourceExprCommand; struct Buildable { -- cgit v1.2.3 From b8a38fa52171f0a8077db29f23df86c31ee02545 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 14 Dec 2019 23:09:57 +0100 Subject: Fix 'nix profile' --- src/nix/profile.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 6ea529f52..8ff0e4dd9 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -126,9 +126,11 @@ struct ProfileManifest StringSink sink; dumpPath(tempDir, sink); - ValidPathInfo info(store->makeFixedOutputPath(true, info.narHash, "profile", references)); + auto narHash = hashString(htSHA256, *sink.s); + + ValidPathInfo info(store->makeFixedOutputPath(true, narHash, "profile", references)); info.references = std::move(references); - info.narHash = hashString(htSHA256, *sink.s); + info.narHash = narHash; info.narSize = sink.s->size(); info.ca = makeFixedOutputCA(true, info.narHash); -- cgit v1.2.3 From 4947e0a91aeefd898e5b976733e70f1fbc1c0ae7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 14 Dec 2019 23:15:36 +0100 Subject: Fix InstallableCommand --- src/nix/command.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index ba75ba6e6..fc69d7154 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -106,7 +106,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand private: - std::string _installable{""}; + std::string _installable{"."}; }; /* A command that operates on zero or more store paths. */ -- cgit v1.2.3 From 9f4d8c6170517c9452e25dc29c56a6fbb43d40a1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 21 Jan 2020 16:27:53 +0100 Subject: Pluggable fetchers Flakes are now fetched using an extensible mechanism. Also lots of other flake cleanups. --- src/nix/flake.cc | 164 +++++++++++++++++++++--------------------------- src/nix/installables.cc | 35 +++++------ src/nix/installables.hh | 8 +-- src/nix/profile.cc | 6 +- 4 files changed, 94 insertions(+), 119 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 22e994e58..5bec5903f 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -9,6 +9,8 @@ #include "store-api.hh" #include "derivations.hh" #include "attr-path.hh" +#include "fetchers/fetchers.hh" +#include "fetchers/registry.hh" #include #include @@ -30,10 +32,7 @@ public: FlakeRef getFlakeRef() { - if (flakeUrl.find('/') != std::string::npos || flakeUrl == ".") - return FlakeRef(flakeUrl, true); - else - return FlakeRef(flakeUrl); + return parseFlakeRef(flakeUrl, absPath(".")); //FIXME } Flake getFlake() @@ -57,63 +56,54 @@ struct CmdFlakeList : EvalCommand void run(nix::ref store) override { - auto registries = getEvalState()->getFlakeRegistries(); - - stopProgressBar(); + using namespace fetchers; - for (auto & entry : registries[FLAG_REGISTRY]->entries) - std::cout << entry.first.to_string() << " flags " << entry.second.to_string() << "\n"; + auto registries = getRegistries(store); - for (auto & entry : registries[USER_REGISTRY]->entries) - std::cout << entry.first.to_string() << " user " << entry.second.to_string() << "\n"; + stopProgressBar(); - for (auto & entry : registries[GLOBAL_REGISTRY]->entries) - std::cout << entry.first.to_string() << " global " << entry.second.to_string() << "\n"; + for (auto & registry : registries) { + for (auto & entry : registry->entries) { + // FIXME: format nicely + std::cout << fmt("%s %s %s\n", + registry->type == Registry::Flag ? "flags " : + registry->type == Registry::User ? "user " : + "global", + entry.first->to_string(), + entry.second->to_string()); + } + } } }; -static void printSourceInfo(const SourceInfo & sourceInfo) -{ - std::cout << fmt("URL: %s\n", sourceInfo.resolvedRef.to_string()); - if (sourceInfo.resolvedRef.ref) - std::cout << fmt("Branch: %s\n",*sourceInfo.resolvedRef.ref); - if (sourceInfo.resolvedRef.rev) - std::cout << fmt("Revision: %s\n", sourceInfo.resolvedRef.rev->to_string(Base16, false)); - if (sourceInfo.revCount) - std::cout << fmt("Revisions: %s\n", *sourceInfo.revCount); - if (sourceInfo.lastModified) - std::cout << fmt("Last modified: %s\n", - std::put_time(std::localtime(&*sourceInfo.lastModified), "%F %T")); - std::cout << fmt("Path: %s\n", sourceInfo.storePath); -} - -static void sourceInfoToJson(const SourceInfo & sourceInfo, nlohmann::json & j) -{ - j["url"] = sourceInfo.resolvedRef.to_string(); - if (sourceInfo.resolvedRef.ref) - j["branch"] = *sourceInfo.resolvedRef.ref; - if (sourceInfo.resolvedRef.rev) - j["revision"] = sourceInfo.resolvedRef.rev->to_string(Base16, false); - if (sourceInfo.revCount) - j["revCount"] = *sourceInfo.revCount; - if (sourceInfo.lastModified) - j["lastModified"] = *sourceInfo.lastModified; - j["path"] = sourceInfo.storePath; -} - -static void printFlakeInfo(const Flake & flake) +static void printFlakeInfo(const Store & store, const Flake & flake) { - std::cout << fmt("Description: %s\n", flake.description); + std::cout << fmt("URL: %s\n", flake.resolvedRef.input->to_string()); std::cout << fmt("Edition: %s\n", flake.edition); - printSourceInfo(flake.sourceInfo); + std::cout << fmt("Description: %s\n", flake.description); + std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); + if (flake.sourceInfo->rev) + std::cout << fmt("Revision: %s\n", flake.sourceInfo->rev->to_string(Base16, false)); + if (flake.sourceInfo->revCount) + std::cout << fmt("Revisions: %s\n", *flake.sourceInfo->revCount); + if (flake.sourceInfo->lastModified) + std::cout << fmt("Last modified: %s\n", + std::put_time(std::localtime(&*flake.sourceInfo->lastModified), "%F %T")); } -static nlohmann::json flakeToJson(const Flake & flake) +static nlohmann::json flakeToJson(const Store & store, const Flake & flake) { nlohmann::json j; j["description"] = flake.description; j["edition"] = flake.edition; - sourceInfoToJson(flake.sourceInfo, j); + j["url"] = flake.resolvedRef.input->to_string(); + if (flake.sourceInfo->rev) + j["revision"] = flake.sourceInfo->rev->to_string(Base16, false); + if (flake.sourceInfo->revCount) + j["revCount"] = *flake.sourceInfo->revCount; + if (flake.sourceInfo->lastModified) + j["lastModified"] = *flake.sourceInfo->lastModified; + j["path"] = store.printStorePath(flake.sourceInfo->storePath); return j; } @@ -140,7 +130,7 @@ struct CmdFlakeDeps : FlakeCommand todo.pop(); for (auto & info : resFlake.flakeDeps) { - printFlakeInfo(info.second.flake); + printFlakeInfo(*store, info.second.flake); todo.push(info.second); } } @@ -161,10 +151,12 @@ struct CmdFlakeUpdate : FlakeCommand auto flakeRef = getFlakeRef(); +#if 0 if (std::get_if(&flakeRef.data)) updateLockFile(*evalState, flakeRef, true); else throw Error("cannot update lockfile of flake '%s'", flakeRef); +#endif } }; @@ -195,7 +187,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON auto state = getEvalState(); auto flake = resolveFlake(); - auto json = flakeToJson(flake.flake); + auto json = flakeToJson(*store, flake.flake); auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); @@ -222,7 +214,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON } else { auto flake = getFlake(); stopProgressBar(); - printFlakeInfo(flake); + printFlakeInfo(*store, flake); } } }; @@ -495,8 +487,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON struct CmdFlakeAdd : MixEvalArgs, Command { - FlakeUri alias; - FlakeUri url; + std::string fromUrl, toUrl; std::string description() override { @@ -505,24 +496,24 @@ struct CmdFlakeAdd : MixEvalArgs, Command CmdFlakeAdd() { - expectArg("alias", &alias); - expectArg("flake-url", &url); + expectArg("from-url", &fromUrl); + expectArg("to-url", &toUrl); } void run() override { - FlakeRef aliasRef(alias); - Path userRegistryPath = getUserRegistryPath(); - auto userRegistry = readRegistry(userRegistryPath); - userRegistry->entries.erase(aliasRef); - userRegistry->entries.insert_or_assign(aliasRef, FlakeRef(url)); - writeRegistry(*userRegistry, userRegistryPath); + auto fromRef = parseFlakeRef(fromUrl); + auto toRef = parseFlakeRef(toUrl); + auto userRegistry = fetchers::getUserRegistry(); + userRegistry->remove(fromRef.input); + userRegistry->add(fromRef.input, toRef.input); + userRegistry->write(fetchers::getUserRegistryPath()); } }; struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command { - FlakeUri alias; + std::string url; std::string description() override { @@ -531,52 +522,38 @@ struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command CmdFlakeRemove() { - expectArg("alias", &alias); + expectArg("url", &url); } void run() override { - Path userRegistryPath = getUserRegistryPath(); - auto userRegistry = readRegistry(userRegistryPath); - userRegistry->entries.erase(FlakeRef(alias)); - writeRegistry(*userRegistry, userRegistryPath); + auto userRegistry = fetchers::getUserRegistry(); + userRegistry->remove(parseFlakeRef(url).input); + userRegistry->write(fetchers::getUserRegistryPath()); } }; struct CmdFlakePin : virtual Args, EvalCommand { - FlakeUri alias; + std::string url; std::string description() override { - return "pin flake require in user flake registry"; + return "pin a flake to its current version in user flake registry"; } CmdFlakePin() { - expectArg("alias", &alias); + expectArg("url", &url); } void run(nix::ref store) override { - auto evalState = getEvalState(); - - Path userRegistryPath = getUserRegistryPath(); - FlakeRegistry userRegistry = *readRegistry(userRegistryPath); - auto it = userRegistry.entries.find(FlakeRef(alias)); - if (it != userRegistry.entries.end()) { - it->second = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; - writeRegistry(userRegistry, userRegistryPath); - } else { - std::shared_ptr globalReg = evalState->getGlobalFlakeRegistry(); - it = globalReg->entries.find(FlakeRef(alias)); - if (it != globalReg->entries.end()) { - auto newRef = getFlake(*evalState, it->second, true).sourceInfo.resolvedRef; - userRegistry.entries.insert_or_assign(alias, newRef); - writeRegistry(userRegistry, userRegistryPath); - } else - throw Error("the flake alias '%s' does not exist in the user or global registry", alias); - } + auto ref = parseFlakeRef(url); + auto userRegistry = fetchers::getUserRegistry(); + userRegistry->remove(ref.input); + auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); + userRegistry->add(ref.input, resolved); } }; @@ -616,15 +593,20 @@ struct CmdFlakeClone : FlakeCommand CmdFlakeClone() { - expectArg("dest-dir", &destDir, true); + mkFlag() + .shortName('f') + .longName("dest") + .label("path") + .description("destination path") + .dest(&destDir); } void run(nix::ref store) override { - auto evalState = getEvalState(); + if (destDir.empty()) + throw Error("missing flag '--dest'"); - Registries registries = evalState->getFlakeRegistries(); - gitCloneFlake(getFlakeRef().to_string(), *evalState, registries, destDir); + getFlakeRef().resolve(store).input->clone(destDir); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 86b9fbdb9..74cd85380 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -10,6 +10,7 @@ #include "shared.hh" #include "flake/flake.hh" #include "flake/eval-cache.hh" +#include "fetchers/parse.hh" #include #include @@ -80,10 +81,8 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() ref EvalCommand::getEvalState() { - if (!evalState) { + if (!evalState) evalState = std::make_shared(searchPath, getStore()); - evalState->addRegistryOverrides(registryOverrides); - } return ref(evalState); } @@ -243,6 +242,7 @@ void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, const flake::ResolvedFlake & resFlake) { +#if 0 if (std::get_if(&origFlakeRef.data)) return; /* Get the store paths of all non-local flakes. */ @@ -285,6 +285,7 @@ void makeFlakeClosureGCRoot(Store & store, debug("writing GC root '%s' for flake closure of '%s'", symlink, origFlakeRef); replaceSymlink(store.printStorePath(closurePath), symlink); store.addIndirectRoot(symlink); +#endif } std::vector InstallableFlake::getActualAttrPaths() @@ -334,7 +335,7 @@ std::tuple InstallableFlake auto drv = evalCache.getDerivation(fingerprint, attrPath); if (drv) { if (state->store->isValidPath(drv->drvPath)) - return {attrPath, resFlake.flake.sourceInfo.resolvedRef, std::move(*drv)}; + return {attrPath, resFlake.flake.resolvedRef, std::move(*drv)}; } if (!vOutputs) @@ -356,7 +357,7 @@ std::tuple InstallableFlake evalCache.addDerivation(fingerprint, attrPath, drv); - return {attrPath, resFlake.flake.sourceInfo.resolvedRef, std::move(drv)}; + return {attrPath, resFlake.flake.resolvedRef, std::move(drv)}; } catch (AttrPathNotFound & e) { } } @@ -440,27 +441,23 @@ std::vector> SourceExprCommand::parseInstallables( if (hasPrefix(s, "nixpkgs.")) { bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); - result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), - Strings{"legacyPackages." + settings.thisSystem.get() + "." + std::string(s, 8)})); + result.push_back(std::make_shared(*this, parseFlakeRef("flake:nixpkgs"), + Strings{"legacyPackages." + settings.thisSystem.get() + "." + std::string(s, 8)}, Strings{})); } - else if ((hash = s.rfind('#')) != std::string::npos) - result.push_back(std::make_shared( - *this, - FlakeRef(std::string(s, 0, hash), true), - std::string(s, hash + 1), - getDefaultFlakeAttrPathPrefixes())); - else { - try { - auto flakeRef = FlakeRef(s, true); + auto res = maybeParseFlakeRefWithFragment(s, absPath(".")); + if (res) { + auto &[flakeRef, fragment] = *res; result.push_back(std::make_shared( - *this, std::move(flakeRef), getDefaultFlakeAttrPaths())); - } catch (...) { + *this, std::move(flakeRef), + fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, + getDefaultFlakeAttrPathPrefixes())); + } else { if (s.find('/') != std::string::npos && (storePath = follow(s))) result.push_back(std::make_shared(store, store->printStorePath(*storePath))); else - throw; + throw Error("unrecognized argument '%s'", s); } } } diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 22e4b38f9..340c2c9da 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -75,13 +75,9 @@ struct InstallableFlake : InstallableValue Strings attrPaths; Strings prefixes; - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, Strings attrPaths) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(std::move(attrPaths)) - { } - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, - std::string attrPath, Strings && prefixes) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, + Strings && attrPaths, Strings && prefixes) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(attrPaths), prefixes(prefixes) { } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 8ff0e4dd9..c94d92567 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -54,8 +54,8 @@ struct ProfileManifest element.active = e["active"]; if (e.value("uri", "") != "") { element.source = ProfileElementSource{ - FlakeRef(e["originalUri"]), - FlakeRef(e["uri"]), + parseFlakeRef(e["originalUri"]), + parseFlakeRef(e["uri"]), e["attrPath"] }; } @@ -336,7 +336,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf Activity act(*logger, lvlChatty, actUnknown, fmt("checking '%s' for updates", element.source->attrPath)); - InstallableFlake installable(*this, FlakeRef(element.source->originalRef), {element.source->attrPath}); + InstallableFlake installable(*this, FlakeRef(element.source->originalRef), {element.source->attrPath}, {}); auto [attrPath, resolvedRef, drv] = installable.toDerivation(); -- cgit v1.2.3 From 32f31a8c63d35a8ecfd341802465afacbe8d5759 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 Jan 2020 17:20:21 +0100 Subject: nix flake info: Don't show empty descriptions --- src/nix/flake.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5bec5903f..bfff682d9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -80,7 +80,8 @@ static void printFlakeInfo(const Store & store, const Flake & flake) { std::cout << fmt("URL: %s\n", flake.resolvedRef.input->to_string()); std::cout << fmt("Edition: %s\n", flake.edition); - std::cout << fmt("Description: %s\n", flake.description); + if (flake.description) + std::cout << fmt("Description: %s\n", *flake.description); std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); if (flake.sourceInfo->rev) std::cout << fmt("Revision: %s\n", flake.sourceInfo->rev->to_string(Base16, false)); @@ -94,7 +95,8 @@ static void printFlakeInfo(const Store & store, const Flake & flake) static nlohmann::json flakeToJson(const Store & store, const Flake & flake) { nlohmann::json j; - j["description"] = flake.description; + if (flake.description) + j["description"] = *flake.description; j["edition"] = flake.edition; j["url"] = flake.resolvedRef.input->to_string(); if (flake.sourceInfo->rev) -- cgit v1.2.3 From 90d55ed275220962f7239f4869905b0237dd24fb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 Jan 2020 19:28:27 +0100 Subject: Fix 'nix flake update' --- src/nix/flake.cc | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index bfff682d9..4f43f549b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -150,15 +150,7 @@ struct CmdFlakeUpdate : FlakeCommand void run(nix::ref store) override { auto evalState = getEvalState(); - - auto flakeRef = getFlakeRef(); - -#if 0 - if (std::get_if(&flakeRef.data)) - updateLockFile(*evalState, flakeRef, true); - else - throw Error("cannot update lockfile of flake '%s'", flakeRef); -#endif + resolveFlake(); } }; -- cgit v1.2.3 From 872a22fa2344bdb95e0697bca168d6798d60ff5d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 22 Jan 2020 20:59:59 +0100 Subject: resolveFlake -> lockFlake "resolve" is ambiguous (also used for registry resolution). --- src/nix/flake.cc | 18 +++++++++--------- src/nix/installables.cc | 28 ++++++++++++++-------------- src/nix/installables.hh | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 4f43f549b..f7e329b49 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -41,9 +41,9 @@ public: return flake::getFlake(*evalState, getFlakeRef(), useRegistries); } - ResolvedFlake resolveFlake() + LockedFlake lockFlake() { - return flake::resolveFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); + return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); } }; @@ -122,16 +122,16 @@ struct CmdFlakeDeps : FlakeCommand { auto evalState = getEvalState(); - std::queue todo; - todo.push(resolveFlake()); + std::queue todo; + todo.push(lockFlake()); stopProgressBar(); while (!todo.empty()) { - auto resFlake = std::move(todo.front()); + auto lockedFlake = std::move(todo.front()); todo.pop(); - for (auto & info : resFlake.flakeDeps) { + for (auto & info : lockedFlake.flakeDeps) { printFlakeInfo(*store, info.second.flake); todo.push(info.second); } @@ -150,7 +150,7 @@ struct CmdFlakeUpdate : FlakeCommand void run(nix::ref store) override { auto evalState = getEvalState(); - resolveFlake(); + lockFlake(); } }; @@ -179,7 +179,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON { if (json) { auto state = getEvalState(); - auto flake = resolveFlake(); + auto flake = lockFlake(); auto json = flakeToJson(*store, flake.flake); @@ -235,7 +235,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON settings.readOnlyMode = !build; auto state = getEvalState(); - auto flake = resolveFlake(); + auto flake = lockFlake(); auto checkSystemName = [&](const std::string & system, const Pos & pos) { // FIXME: what's the format of "system"? diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 74cd85380..a42f7a76f 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -240,7 +240,7 @@ struct InstallableAttrPath : InstallableValue void makeFlakeClosureGCRoot(Store & store, const FlakeRef & origFlakeRef, - const flake::ResolvedFlake & resFlake) + const flake::LockedFlake & lockedFlake) { #if 0 if (std::get_if(&origFlakeRef.data)) return; @@ -248,11 +248,11 @@ void makeFlakeClosureGCRoot(Store & store, /* Get the store paths of all non-local flakes. */ StorePathSet closure; - assert(store.isValidPath(store.parseStorePath(resFlake.flake.sourceInfo.storePath))); - closure.insert(store.parseStorePath(resFlake.flake.sourceInfo.storePath)); + assert(store.isValidPath(store.parseStorePath(lockedFlake.flake.sourceInfo.storePath))); + closure.insert(store.parseStorePath(lockedFlake.flake.sourceInfo.storePath)); std::queue> queue; - queue.push(resFlake.lockFile); + queue.push(lockedFlake.lockFile); while (!queue.empty()) { const flake::LockedInputs & flake = queue.front(); @@ -301,13 +301,13 @@ std::vector InstallableFlake::getActualAttrPaths() return res; } -Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake) +Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake) { auto vFlake = state.allocValue(); - callFlake(state, resFlake, *vFlake); + callFlake(state, lockedFlake, *vFlake); - makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake); + makeFlakeClosureGCRoot(*state.store, flakeRef, lockedFlake); auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); @@ -321,7 +321,7 @@ std::tuple InstallableFlake { auto state = cmd.getEvalState(); - auto resFlake = resolveFlake(*state, flakeRef, cmd.getLockFileMode()); + auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode()); Value * vOutputs = nullptr; @@ -329,17 +329,17 @@ std::tuple InstallableFlake auto & evalCache = flake::EvalCache::singleton(); - auto fingerprint = resFlake.getFingerprint(); + auto fingerprint = lockedFlake.getFingerprint(); for (auto & attrPath : getActualAttrPaths()) { auto drv = evalCache.getDerivation(fingerprint, attrPath); if (drv) { if (state->store->isValidPath(drv->drvPath)) - return {attrPath, resFlake.flake.resolvedRef, std::move(*drv)}; + return {attrPath, lockedFlake.flake.resolvedRef, std::move(*drv)}; } if (!vOutputs) - vOutputs = getFlakeOutputs(*state, resFlake); + vOutputs = getFlakeOutputs(*state, lockedFlake); try { auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs); @@ -357,7 +357,7 @@ std::tuple InstallableFlake evalCache.addDerivation(fingerprint, attrPath, drv); - return {attrPath, resFlake.flake.resolvedRef, std::move(drv)}; + return {attrPath, lockedFlake.flake.resolvedRef, std::move(drv)}; } catch (AttrPathNotFound & e) { } } @@ -375,9 +375,9 @@ std::vector InstallableFlake::toDerivations() Value * InstallableFlake::toValue(EvalState & state) { - auto resFlake = resolveFlake(state, flakeRef, cmd.getLockFileMode()); + auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode()); - auto vOutputs = getFlakeOutputs(state, resFlake); + auto vOutputs = getFlakeOutputs(state, lockedFlake); auto emptyArgs = state.allocBindings(0); diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 340c2c9da..2fd09dbf8 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -85,7 +85,7 @@ struct InstallableFlake : InstallableValue std::vector getActualAttrPaths(); - Value * getFlakeOutputs(EvalState & state, const flake::ResolvedFlake & resFlake); + Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); std::tuple toDerivation(); -- cgit v1.2.3 From 2b8ca654b058cf18101a5243090706be97930275 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 24 Jan 2020 13:07:03 +0100 Subject: HandleLockFile -> LockFileMode --- src/nix/command.hh | 4 ++-- src/nix/installables.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index fc69d7154..08fa0c5fa 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -16,7 +16,7 @@ struct Pos; class Store; namespace flake { -enum HandleLockFile : unsigned int; +enum LockFileMode : unsigned int; } /* A command that requires a Nix store. */ @@ -49,7 +49,7 @@ struct MixFlakeOptions : virtual Args MixFlakeOptions(); - flake::HandleLockFile getLockFileMode(); + flake::LockFileMode getLockFileMode(); }; struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions diff --git a/src/nix/installables.cc b/src/nix/installables.cc index a42f7a76f..85b090bfc 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -35,7 +35,7 @@ MixFlakeOptions::MixFlakeOptions() .set(&useRegistries, false); } -flake::HandleLockFile MixFlakeOptions::getLockFileMode() +flake::LockFileMode MixFlakeOptions::getLockFileMode() { using namespace flake; return -- cgit v1.2.3 From 6be04476dc076fc1f054d9b905616bfb65c9ff75 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 Jan 2020 13:16:19 +0100 Subject: Shut up warning --- src/nix/installables.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 85b090bfc..8e4b53308 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -434,10 +434,6 @@ std::vector> SourceExprCommand::parseInstallables( }; for (auto & s : ss) { - - size_t hash; - std::optional storePath; - if (hasPrefix(s, "nixpkgs.")) { bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); @@ -454,6 +450,7 @@ std::vector> SourceExprCommand::parseInstallables( fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, getDefaultFlakeAttrPathPrefixes())); } else { + std::optional storePath; if (s.find('/') != std::string::npos && (storePath = follow(s))) result.push_back(std::make_shared(store, store->printStorePath(*storePath))); else -- cgit v1.2.3 From 5bbe793abf18414878a069399d1759673d693fb6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 28 Jan 2020 17:34:48 +0100 Subject: Fix --refresh with --no-net https://hydra.nixos.org/build/110879699 --- src/nix/main.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index f0e6cac4c..c5ca089ee 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -55,6 +55,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { bool printBuildLogs = false; bool useNet = true; + bool refresh = false; NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix") { @@ -96,7 +97,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs mkFlag() .longName("refresh") .description("consider all previously downloaded files out-of-date") - .handler([&]() { settings.tarballTtl = 0; }); + .handler([&]() { refresh = true; }); } void printFlags(std::ostream & out) override @@ -182,6 +183,9 @@ void mainWrapped(int argc, char * * argv) downloadSettings.connectTimeout = 1; } + if (args.refresh) + settings.tarballTtl = 0; + args.command->prepare(); args.command->run(); } -- cgit v1.2.3 From f68bed7f67d9acc13ebe38e6f5aa8a641f6e557d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Jan 2020 14:57:57 +0100 Subject: Add flag --override-input to override specific lock file entries E.g. $ nix flake update ~/Misc/eelco-configurations/hagbard \ --override-input 'dwarffs/nixpkgs' ../my-nixpkgs overrides the 'nixpkgs' input of the 'dwarffs' input of the top-level flake. Fixes #2837. --- src/nix/command.hh | 5 +++-- src/nix/flake.cc | 2 +- src/nix/installables.cc | 15 +++++++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 08fa0c5fa..eb44caf05 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -4,6 +4,7 @@ #include "args.hh" #include "common-eval-args.hh" #include "path.hh" +#include "flake/lockfile.hh" #include @@ -42,11 +43,11 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs struct MixFlakeOptions : virtual Args { bool recreateLockFile = false; - bool saveLockFile = true; - bool useRegistries = true; + flake::LockFlags lockFlags; + MixFlakeOptions(); flake::LockFileMode getLockFileMode(); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index f7e329b49..2852a627d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -43,7 +43,7 @@ public: LockedFlake lockFlake() { - return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode()); + return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode(), lockFlags); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 8e4b53308..7d59a25ee 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -33,6 +33,17 @@ MixFlakeOptions::MixFlakeOptions() .longName("no-registries") .description("don't use flake registries") .set(&useRegistries, false); + + mkFlag() + .longName("override-input") + .description("override a specific flake input (e.g. 'dwarffs/nixpkgs')") + .arity(2) + .labels({"input-path", "flake-url"}) + .handler([&](std::vector ss) { + lockFlags.inputOverrides.insert_or_assign( + flake::parseInputPath(ss[0]), + parseFlakeRef(ss[1], absPath("."))); + }); } flake::LockFileMode MixFlakeOptions::getLockFileMode() @@ -321,7 +332,7 @@ std::tuple InstallableFlake { auto state = cmd.getEvalState(); - auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode()); + auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags); Value * vOutputs = nullptr; @@ -375,7 +386,7 @@ std::vector InstallableFlake::toDerivations() Value * InstallableFlake::toValue(EvalState & state) { - auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode()); + auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags); auto vOutputs = getFlakeOutputs(state, lockedFlake); -- cgit v1.2.3 From 26f895a26d37ec6049628fa835e20dfae5eb94dd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Jan 2020 21:01:34 +0100 Subject: Clean up the lock file handling flags Added a flag --no-update-lock-file to barf if the lock file needs any changes. This is useful for CI systems if you're building a checkout. Fixes #2947. Renamed --no-save-lock-file to --no-write-lock-file. It is now a fatal error if the lock file needs changes but --no-write-lock-file is not given. --- src/nix/command.hh | 10 ---------- src/nix/flake.cc | 4 ++-- src/nix/installables.cc | 30 ++++++++++++------------------ 3 files changed, 14 insertions(+), 30 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index eb44caf05..305ce5588 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -16,10 +16,6 @@ class EvalState; struct Pos; class Store; -namespace flake { -enum LockFileMode : unsigned int; -} - /* A command that requires a Nix store. */ struct StoreCommand : virtual Command { @@ -42,15 +38,9 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs struct MixFlakeOptions : virtual Args { - bool recreateLockFile = false; - bool saveLockFile = true; - bool useRegistries = true; - flake::LockFlags lockFlags; MixFlakeOptions(); - - flake::LockFileMode getLockFileMode(); }; struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2852a627d..49c0d30f0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -38,12 +38,12 @@ public: Flake getFlake() { auto evalState = getEvalState(); - return flake::getFlake(*evalState, getFlakeRef(), useRegistries); + return flake::getFlake(*evalState, getFlakeRef(), lockFlags.useRegistries); } LockedFlake lockFlake() { - return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode(), lockFlags); + return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 7d59a25ee..24eb739f5 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -22,17 +22,22 @@ MixFlakeOptions::MixFlakeOptions() mkFlag() .longName("recreate-lock-file") .description("recreate lock file from scratch") - .set(&recreateLockFile, true); + .set(&lockFlags.recreateLockFile, true); mkFlag() - .longName("no-save-lock-file") - .description("do not save the newly generated lock file") - .set(&saveLockFile, false); + .longName("no-update-lock-file") + .description("do not allow any updates to the lock file") + .set(&lockFlags.updateLockFile, false); + + mkFlag() + .longName("no-write-lock-file") + .description("do not write the newly generated lock file") + .set(&lockFlags.writeLockFile, false); mkFlag() .longName("no-registries") .description("don't use flake registries") - .set(&useRegistries, false); + .set(&lockFlags.useRegistries, false); mkFlag() .longName("override-input") @@ -46,17 +51,6 @@ MixFlakeOptions::MixFlakeOptions() }); } -flake::LockFileMode MixFlakeOptions::getLockFileMode() -{ - using namespace flake; - return - useRegistries - ? recreateLockFile - ? (saveLockFile ? RecreateLockFile : UseNewLockFile) - : (saveLockFile ? UpdateLockFile : UseUpdatedLockFile) - : AllPure; -} - SourceExprCommand::SourceExprCommand() { mkFlag() @@ -332,7 +326,7 @@ std::tuple InstallableFlake { auto state = cmd.getEvalState(); - auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags); + auto lockedFlake = lockFlake(*state, flakeRef, cmd.lockFlags); Value * vOutputs = nullptr; @@ -386,7 +380,7 @@ std::vector InstallableFlake::toDerivations() Value * InstallableFlake::toValue(EvalState & state) { - auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags); + auto lockedFlake = lockFlake(state, flakeRef, cmd.lockFlags); auto vOutputs = getFlakeOutputs(state, lockedFlake); -- cgit v1.2.3 From b9fb3720750a24d8b2fbe6b1491696d6a51dcff3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Jan 2020 23:12:58 +0100 Subject: Add --update-input flag to update a specific flake input Typical usage: $ nix flake update ~/Misc/eelco-configurations/hagbard --update-input nixpkgs to update the 'nixpkgs' input of a flake while leaving every other input unchanged. The argument is an input path, so you can do e.g. '--update-input dwarffs/nixpkgs' to update an input of an input. Fixes #2928. --- src/nix/installables.cc | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 24eb739f5..77a43ef2a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -39,6 +39,14 @@ MixFlakeOptions::MixFlakeOptions() .description("don't use flake registries") .set(&lockFlags.useRegistries, false); + mkFlag() + .longName("update-input") + .description("update a specific flake input") + .label("input-path") + .handler([&](std::vector ss) { + lockFlags.inputUpdates.insert(flake::parseInputPath(ss[0])); + }); + mkFlag() .longName("override-input") .description("override a specific flake input (e.g. 'dwarffs/nixpkgs')") -- cgit v1.2.3 From 3c54e9ba011a42c454ce401b861735a2dbee0d79 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 30 Jan 2020 00:58:55 +0100 Subject: Add 'nix flake archive' command This copies a flake and all its inputs recursively to a store (e.g. a binary cache). This is intended to enable long-term reproducibility for flakes. However this will also require #3253. Example: $ nix flake archive --json --to file:///tmp/my-cache nixops {"path":"/nix/store/272igzkgl1gdzmabsjvb2kb2zqbphb3p-source","inputs":{"nixops-aws":{"path":"/nix/store/ybcykw13gr7iq1pzg18iyibbcv8k9q1v-source","inputs":{}},"nixops-hetzner":{"path":"/nix/store/6yn0205x3nz55w8ms3335p2841javz2d-source","inputs":{}},"nixpkgs":{"path":"/nix/store/li3lkr2ajrzphqqz3jj2avndnyd3i5lc-source","inputs":{}}}} $ ll /tmp/my-cache total 16 -rw-r--r-- 1 eelco users 403 Jan 30 01:01 272igzkgl1gdzmabsjvb2kb2zqbphb3p.narinfo -rw-r--r-- 1 eelco users 403 Jan 30 01:01 6yn0205x3nz55w8ms3335p2841javz2d.narinfo -rw-r--r-- 1 eelco users 408 Jan 30 01:01 li3lkr2ajrzphqqz3jj2avndnyd3i5lc.narinfo drwxr-xr-x 2 eelco users 6 Jan 30 01:01 nar -rw-r--r-- 1 eelco users 21 Jan 30 01:01 nix-cache-info -rw-r--r-- 1 eelco users 404 Jan 30 01:01 ybcykw13gr7iq1pzg18iyibbcv8k9q1v.narinfo Fixes #3336. --- src/nix/flake.cc | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 49c0d30f0..295383681 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -11,6 +11,7 @@ #include "attr-path.hh" #include "fetchers/fetchers.hh" #include "fetchers/registry.hh" +#include "json.hh" #include #include @@ -213,7 +214,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON } }; -struct CmdFlakeCheck : FlakeCommand, MixJSON +struct CmdFlakeCheck : FlakeCommand { bool build = true; @@ -604,6 +605,79 @@ struct CmdFlakeClone : FlakeCommand } }; +struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun +{ + std::string dstUri; + + CmdFlakeArchive() + { + mkFlag() + .longName("to") + .labels({"store-uri"}) + .description("URI of the destination Nix store") + .dest(&dstUri); + } + + std::string description() override + { + return "copy a flake and all its inputs to a store"; + } + + Examples examples() override + { + return { + Example{ + "To copy the dwarffs flake and its dependencies to a binary cache:", + "nix flake archive --to file:///tmp/my-cache dwarffs" + }, + Example{ + "To fetch the dwarffs flake and its dependencies to the local Nix store:", + "nix flake archive dwarffs" + }, + Example{ + "To print the store paths of the flake sources of NixOps without fetching them:", + "nix flake archive --json --dry-run nixops" + }, + }; + } + + void run(nix::ref store) override + { + auto flake = lockFlake(); + + auto jsonRoot = json ? std::optional(std::cout) : std::optional(); + + StorePathSet sources; + + sources.insert(flake.flake.sourceInfo->storePath.clone()); + if (jsonRoot) + jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath)); + + std::function & jsonObj)> traverse; + traverse = [&](const LockedInputs & inputs, std::optional & jsonObj) + { + auto jsonObj2 = jsonObj ? jsonObj->object("inputs") : std::optional(); + for (auto & input : inputs.inputs) { + auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional(); + if (!dryRun) + input.second.ref.input->fetchTree(store); + auto storePath = input.second.computeStorePath(*store); + if (jsonObj3) + jsonObj3->attr("path", store->printStorePath(storePath)); + sources.insert(std::move(storePath)); + traverse(input.second, jsonObj3); + } + }; + + traverse(flake.lockFile, jsonRoot); + + if (!dryRun && !dstUri.empty()) { + ref dstStore = dstUri.empty() ? openStore() : openStore(dstUri); + copyPaths(store, dstStore, sources); + } + } +}; + struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() @@ -617,6 +691,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command {"pin", []() { return make_ref(); }}, {"init", []() { return make_ref(); }}, {"clone", []() { return make_ref(); }}, + {"archive", []() { return make_ref(); }}, }) { } -- cgit v1.2.3 From ebfbfe95158fd074fa057930e1e0fb858059e358 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 30 Jan 2020 01:10:26 +0100 Subject: Use std::nullopt --- src/nix/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 295383681..dafd1ae7e 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -645,7 +645,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun { auto flake = lockFlake(); - auto jsonRoot = json ? std::optional(std::cout) : std::optional(); + auto jsonRoot = json ? std::optional(std::cout) : std::nullopt; StorePathSet sources; -- cgit v1.2.3 From a6e2b6b36044d7708cf50b464009e50c8d120730 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 Jan 2020 12:54:52 +0100 Subject: nix flake deps -> nix flake list-inputs Also add a --json flag. --- src/nix/flake.cc | 64 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index dafd1ae7e..5450f1fb5 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -110,37 +110,6 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) return j; } -#if 0 -// FIXME: merge info CmdFlakeInfo? -struct CmdFlakeDeps : FlakeCommand -{ - std::string description() override - { - return "list informaton about dependencies"; - } - - void run(nix::ref store) override - { - auto evalState = getEvalState(); - - std::queue todo; - todo.push(lockFlake()); - - stopProgressBar(); - - while (!todo.empty()) { - auto lockedFlake = std::move(todo.front()); - todo.pop(); - - for (auto & info : lockedFlake.flakeDeps) { - printFlakeInfo(*store, info.second.flake); - todo.push(info.second); - } - } - } -}; -#endif - struct CmdFlakeUpdate : FlakeCommand { std::string description() override @@ -150,7 +119,6 @@ struct CmdFlakeUpdate : FlakeCommand void run(nix::ref store) override { - auto evalState = getEvalState(); lockFlake(); } }; @@ -214,6 +182,37 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON } }; +struct CmdFlakeListInputs : FlakeCommand, MixJSON +{ + std::string description() override + { + return "list flake inputs"; + } + + void run(nix::ref store) override + { + auto flake = lockFlake(); + + if (json) + std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n"; + else { + std::cout << fmt("%s\n", flake.flake.resolvedRef); + + std::function recurse; + + recurse = [&](const LockedInputs & inputs, size_t depth) + { + for (auto & input : inputs.inputs) { + std::cout << fmt("%s%s: %s\n", std::string(depth * 2, ' '), input.first, input.second.ref); + recurse(input.second, depth + 1); + } + }; + + recurse(flake.lockFile, 1); + } + } +}; + struct CmdFlakeCheck : FlakeCommand { bool build = true; @@ -685,6 +684,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command {"list", []() { return make_ref(); }}, {"update", []() { return make_ref(); }}, {"info", []() { return make_ref(); }}, + {"list-inputs", []() { return make_ref(); }}, {"check", []() { return make_ref(); }}, {"add", []() { return make_ref(); }}, {"remove", []() { return make_ref(); }}, -- cgit v1.2.3 From 678301072f05b650dc15c5edb4c25f08f0d6cace Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 Jan 2020 14:01:46 +0100 Subject: nix flake list-inputs: Pretty-print the tree --- src/nix/flake.cc | 17 +++++++++++------ src/nix/why-depends.cc | 5 ----- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5450f1fb5..9b23570bd 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -193,22 +193,27 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON { auto flake = lockFlake(); + stopProgressBar(); + if (json) std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n"; else { std::cout << fmt("%s\n", flake.flake.resolvedRef); - std::function recurse; + std::function recurse; - recurse = [&](const LockedInputs & inputs, size_t depth) + recurse = [&](const LockedInputs & inputs, const std::string & prefix) { - for (auto & input : inputs.inputs) { - std::cout << fmt("%s%s: %s\n", std::string(depth * 2, ' '), input.first, input.second.ref); - recurse(input.second, depth + 1); + for (const auto & [i, input] : enumerate(inputs.inputs)) { + //auto tree2 = tree.child(i + 1 == inputs.inputs.size()); + bool last = i + 1 == inputs.inputs.size(); + std::cout << fmt("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s\n", + prefix + (last ? treeLast : treeConn), input.first, input.second.ref); + recurse(input.second, prefix + (last ? treeNull : treeLine)); } }; - recurse(flake.lockFile, 1); + recurse(flake.lockFile, ""); } } }; diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index c539adb7f..fb12d5380 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -143,11 +143,6 @@ struct CmdWhyDepends : SourceExprCommand and `dependency`. */ std::function printNode; - const string treeConn = "╠═══"; - const string treeLast = "╚═══"; - const string treeLine = "║ "; - const string treeNull = " "; - struct BailOut { }; printNode = [&](Node & node, const string & firstPad, const string & tailPad) { -- cgit v1.2.3 From e91f32f2b58eda31fa8d8206ce9d0a8aef52ad13 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 Jan 2020 14:06:26 +0100 Subject: Use light box drawing symbols --- src/nix/why-depends.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index fb12d5380..900466538 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -152,7 +152,7 @@ struct CmdWhyDepends : SourceExprCommand std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n", firstPad, node.visited ? "\e[38;5;244m" : "", - firstPad != "" ? "=> " : "", + firstPad != "" ? "→ " : "", pathS); if (node.path == dependencyPath && !all -- cgit v1.2.3 From 185c3c824015f03027b77b85221df32cdb16e759 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 Jan 2020 19:35:28 +0100 Subject: Cleanup --- src/nix/installables.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 77a43ef2a..cf91be51b 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -450,7 +450,8 @@ std::vector> SourceExprCommand::parseInstallables( if (hasPrefix(s, "nixpkgs.")) { bool static warned; warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); - result.push_back(std::make_shared(*this, parseFlakeRef("flake:nixpkgs"), + result.push_back(std::make_shared(*this, + FlakeRef::fromAttrs({{"type", "indirect"}, {"id", "nixpkgs"}}), Strings{"legacyPackages." + settings.thisSystem.get() + "." + std::string(s, 8)}, Strings{})); } -- cgit v1.2.3 From 5d70b454be7da683bf9f265d95cd0066140ea956 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 1 Feb 2020 12:26:05 +0100 Subject: nix flake update: Imply --refresh --- src/nix/flake.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9b23570bd..dc576c82d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -119,6 +119,9 @@ struct CmdFlakeUpdate : FlakeCommand void run(nix::ref store) override { + /* Use --refresh by default for 'nix flake update'. */ + settings.tarballTtl = 0; + lockFlake(); } }; -- cgit v1.2.3 From 8451298b35353abafe385124cb55e8d4911032ad Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 1 Feb 2020 16:41:54 +0100 Subject: Factor out TreeInfo --- src/nix/flake.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index dc576c82d..d3c03e214 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -84,13 +84,13 @@ static void printFlakeInfo(const Store & store, const Flake & flake) if (flake.description) std::cout << fmt("Description: %s\n", *flake.description); std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); - if (flake.sourceInfo->rev) - std::cout << fmt("Revision: %s\n", flake.sourceInfo->rev->to_string(Base16, false)); - if (flake.sourceInfo->revCount) - std::cout << fmt("Revisions: %s\n", *flake.sourceInfo->revCount); - if (flake.sourceInfo->lastModified) + if (flake.sourceInfo->info.rev) + std::cout << fmt("Revision: %s\n", flake.sourceInfo->info.rev->to_string(Base16, false)); + if (flake.sourceInfo->info.revCount) + std::cout << fmt("Revisions: %s\n", *flake.sourceInfo->info.revCount); + if (flake.sourceInfo->info.lastModified) std::cout << fmt("Last modified: %s\n", - std::put_time(std::localtime(&*flake.sourceInfo->lastModified), "%F %T")); + std::put_time(std::localtime(&*flake.sourceInfo->info.lastModified), "%F %T")); } static nlohmann::json flakeToJson(const Store & store, const Flake & flake) @@ -100,12 +100,12 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) j["description"] = *flake.description; j["edition"] = flake.edition; j["url"] = flake.resolvedRef.input->to_string(); - if (flake.sourceInfo->rev) - j["revision"] = flake.sourceInfo->rev->to_string(Base16, false); - if (flake.sourceInfo->revCount) - j["revCount"] = *flake.sourceInfo->revCount; - if (flake.sourceInfo->lastModified) - j["lastModified"] = *flake.sourceInfo->lastModified; + if (flake.sourceInfo->info.rev) + j["revision"] = flake.sourceInfo->info.rev->to_string(Base16, false); + if (flake.sourceInfo->info.revCount) + j["revCount"] = *flake.sourceInfo->info.revCount; + if (flake.sourceInfo->info.lastModified) + j["lastModified"] = *flake.sourceInfo->info.lastModified; j["path"] = store.printStorePath(flake.sourceInfo->storePath); return j; } -- cgit v1.2.3 From 887730aab39dd63a37d49d72a07c6441ea3b2f92 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 1 Feb 2020 23:54:20 +0100 Subject: Remove superfluous TreeInfo::rev field --- src/nix/flake.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d3c03e214..7924cec7c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -84,8 +84,8 @@ static void printFlakeInfo(const Store & store, const Flake & flake) if (flake.description) std::cout << fmt("Description: %s\n", *flake.description); std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); - if (flake.sourceInfo->info.rev) - std::cout << fmt("Revision: %s\n", flake.sourceInfo->info.rev->to_string(Base16, false)); + if (auto rev = flake.resolvedRef.input->getRev()) + std::cout << fmt("Revision: %s\n", rev->to_string(Base16, false)); if (flake.sourceInfo->info.revCount) std::cout << fmt("Revisions: %s\n", *flake.sourceInfo->info.revCount); if (flake.sourceInfo->info.lastModified) @@ -100,8 +100,8 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) j["description"] = *flake.description; j["edition"] = flake.edition; j["url"] = flake.resolvedRef.input->to_string(); - if (flake.sourceInfo->info.rev) - j["revision"] = flake.sourceInfo->info.rev->to_string(Base16, false); + if (auto rev = flake.resolvedRef.input->getRev()) + j["revision"] = rev->to_string(Base16, false); if (flake.sourceInfo->info.revCount) j["revCount"] = *flake.sourceInfo->info.revCount; if (flake.sourceInfo->info.lastModified) -- cgit v1.2.3 From b2708694664f41f85ffc8cb6ca51cd0cc599806e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 2 Feb 2020 00:05:53 +0100 Subject: Renamed ref / resolvedRef -> lockedRef --- src/nix/flake.cc | 14 +++++++------- src/nix/installables.cc | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 7924cec7c..3b473de73 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -79,12 +79,12 @@ struct CmdFlakeList : EvalCommand static void printFlakeInfo(const Store & store, const Flake & flake) { - std::cout << fmt("URL: %s\n", flake.resolvedRef.input->to_string()); + std::cout << fmt("URL: %s\n", flake.lockedRef.input->to_string()); std::cout << fmt("Edition: %s\n", flake.edition); if (flake.description) std::cout << fmt("Description: %s\n", *flake.description); std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); - if (auto rev = flake.resolvedRef.input->getRev()) + if (auto rev = flake.lockedRef.input->getRev()) std::cout << fmt("Revision: %s\n", rev->to_string(Base16, false)); if (flake.sourceInfo->info.revCount) std::cout << fmt("Revisions: %s\n", *flake.sourceInfo->info.revCount); @@ -99,8 +99,8 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) if (flake.description) j["description"] = *flake.description; j["edition"] = flake.edition; - j["url"] = flake.resolvedRef.input->to_string(); - if (auto rev = flake.resolvedRef.input->getRev()) + j["url"] = flake.lockedRef.input->to_string(); + if (auto rev = flake.lockedRef.input->getRev()) j["revision"] = rev->to_string(Base16, false); if (flake.sourceInfo->info.revCount) j["revCount"] = *flake.sourceInfo->info.revCount; @@ -201,7 +201,7 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON if (json) std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n"; else { - std::cout << fmt("%s\n", flake.flake.resolvedRef); + std::cout << fmt("%s\n", flake.flake.lockedRef); std::function recurse; @@ -211,7 +211,7 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON //auto tree2 = tree.child(i + 1 == inputs.inputs.size()); bool last = i + 1 == inputs.inputs.size(); std::cout << fmt("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s\n", - prefix + (last ? treeLast : treeConn), input.first, input.second.ref); + prefix + (last ? treeLast : treeConn), input.first, input.second.lockedRef); recurse(input.second, prefix + (last ? treeNull : treeLine)); } }; @@ -667,7 +667,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun for (auto & input : inputs.inputs) { auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional(); if (!dryRun) - input.second.ref.input->fetchTree(store); + input.second.lockedRef.input->fetchTree(store); auto storePath = input.second.computeStorePath(*store); if (jsonObj3) jsonObj3->attr("path", store->printStorePath(storePath)); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index cf91be51b..545b5e839 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -348,7 +348,7 @@ std::tuple InstallableFlake auto drv = evalCache.getDerivation(fingerprint, attrPath); if (drv) { if (state->store->isValidPath(drv->drvPath)) - return {attrPath, lockedFlake.flake.resolvedRef, std::move(*drv)}; + return {attrPath, lockedFlake.flake.lockedRef, std::move(*drv)}; } if (!vOutputs) @@ -370,7 +370,7 @@ std::tuple InstallableFlake evalCache.addDerivation(fingerprint, attrPath, drv); - return {attrPath, lockedFlake.flake.resolvedRef, std::move(drv)}; + return {attrPath, lockedFlake.flake.lockedRef, std::move(drv)}; } catch (AttrPathNotFound & e) { } } -- cgit v1.2.3 From 958ec5de568904a07ef050418088d882cbf2ea61 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 2 Feb 2020 11:31:58 +0100 Subject: Cleanup --- src/nix/profile.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c94d92567..1759b83a3 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -330,7 +330,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); if (element.source - && !element.source->originalRef.isImmutable() + && !element.source->originalRef.input->isImmutable() && matches(*store, element, i, matchers)) { Activity act(*logger, lvlChatty, actUnknown, -- cgit v1.2.3 From 9d7fb62db6e8ee6da7f8dbc5f5509271dc12f2ba Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Feb 2020 14:48:49 +0100 Subject: Add option --commit-lock-file --- src/nix/installables.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 545b5e839..752a1466f 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -39,6 +39,11 @@ MixFlakeOptions::MixFlakeOptions() .description("don't use flake registries") .set(&lockFlags.useRegistries, false); + mkFlag() + .longName("commit-lock-file") + .description("commit changes to the lock file") + .set(&lockFlags.commitLockFile, true); + mkFlag() .longName("update-input") .description("update a specific flake input") -- cgit v1.2.3 From 0b013a54dc570395bed887369f8dd622b8ce337b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 Feb 2020 14:08:24 +0100 Subject: findAlongAttrPath(): Return position --- src/nix/flake.cc | 2 +- src/nix/installables.cc | 6 +++--- src/nix/upgrade-nix.cc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3b473de73..8945d8829 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -348,7 +348,7 @@ struct CmdFlakeCheck : FlakeCommand Activity act(*logger, lvlChatty, actUnknown, fmt("checking NixOS configuration '%s'", attrPath)); Bindings & bindings(*state->allocBindings(0)); - auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v); + auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; state->forceAttrs(*vToplevel, pos); if (!state->isDerivation(*vToplevel)) throw Error("attribute 'config.system.build.toplevel' is not a derivation"); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 752a1466f..fc9c8ab45 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -250,7 +250,7 @@ struct InstallableAttrPath : InstallableValue Value * toValue(EvalState & state) override { - auto vRes = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), *v); + auto vRes = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), *v).first; state.forceValue(*vRes); return vRes; } @@ -360,7 +360,7 @@ std::tuple InstallableFlake vOutputs = getFlakeOutputs(*state, lockedFlake); try { - auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs); + auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs).first; state->forceValue(*v); auto drvInfo = getDerivation(*state, *v, false); @@ -401,7 +401,7 @@ Value * InstallableFlake::toValue(EvalState & state) for (auto & attrPath : getActualAttrPaths()) { try { - auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); + auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs).first; state.forceValue(*v); return v; } catch (AttrPathNotFound & e) { diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 87f1f9d1b..c05c29517 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -145,7 +145,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto v = state->allocValue(); state->eval(state->parseExprFromString(*res.data, "/no-such-path"), *v); Bindings & bindings(*state->allocBindings(0)); - auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v); + auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; return store->parseStorePath(state->forceString(*v2)); } -- cgit v1.2.3 From d2032edb2f86e955a8a7724a27c0c3225f386500 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 7 Feb 2020 14:22:01 +0100 Subject: nix edit: Support non-derivation attributes E.g. $ nix edit .#nixosConfigurations.bla now works. --- src/nix/edit.cc | 10 ++++++++-- src/nix/eval.cc | 2 +- src/nix/installables.cc | 20 ++++++++++---------- src/nix/installables.hh | 7 +++---- 4 files changed, 22 insertions(+), 17 deletions(-) (limited to 'src/nix') diff --git a/src/nix/edit.cc b/src/nix/edit.cc index ca410cd1f..1683eada0 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -29,9 +29,15 @@ struct CmdEdit : InstallableCommand { auto state = getEvalState(); - auto v = installable->toValue(*state); + auto [v, pos] = installable->toValue(*state); - Pos pos = findDerivationFilename(*state, *v, installable->what()); + try { + pos = findDerivationFilename(*state, *v, installable->what()); + } catch (NoPositionInfo &) { + } + + if (pos == noPos) + throw Error("cannot find position information for '%s", installable->what()); stopProgressBar(); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index a991ee608..f23625161 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -52,7 +52,7 @@ struct CmdEval : MixJSON, InstallableCommand auto state = getEvalState(); - auto v = installable->toValue(*state); + auto v = installable->toValue(*state).first; PathSet context; stopProgressBar(); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index fc9c8ab45..39ce9b38f 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -130,7 +130,7 @@ App::App(EvalState & state, Value & vApp) App Installable::toApp(EvalState & state) { - return App(state, *toValue(state)); + return App(state, *toValue(state).first); } struct InstallableStorePath : Installable @@ -166,7 +166,7 @@ std::vector InstallableValue::toDerivations() { auto state = cmd.getEvalState(); - auto v = toValue(*state); + auto v = toValue(*state).first; Bindings & autoArgs = *cmd.getAutoArgs(*state); @@ -229,11 +229,11 @@ struct InstallableExpr : InstallableValue std::string what() override { return text; } - Value * toValue(EvalState & state) override + std::pair toValue(EvalState & state) override { auto v = state.allocValue(); state.eval(state.parseExprFromString(text, absPath(".")), *v); - return v; + return {v, noPos}; } }; @@ -248,11 +248,11 @@ struct InstallableAttrPath : InstallableValue std::string what() override { return attrPath; } - Value * toValue(EvalState & state) override + std::pair toValue(EvalState & state) override { - auto vRes = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), *v).first; + auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), *v); state.forceValue(*vRes); - return vRes; + return {vRes, pos}; } }; @@ -391,7 +391,7 @@ std::vector InstallableFlake::toDerivations() return res; } -Value * InstallableFlake::toValue(EvalState & state) +std::pair InstallableFlake::toValue(EvalState & state) { auto lockedFlake = lockFlake(state, flakeRef, cmd.lockFlags); @@ -401,9 +401,9 @@ Value * InstallableFlake::toValue(EvalState & state) for (auto & attrPath : getActualAttrPaths()) { try { - auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs).first; + auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); state.forceValue(*v); - return v; + return {v, pos}; } catch (AttrPathNotFound & e) { } } diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 2fd09dbf8..a96b07718 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -3,14 +3,13 @@ #include "util.hh" #include "path.hh" #include "flake/eval-cache.hh" +#include "eval.hh" #include namespace nix { -struct Value; struct DrvInfo; -class EvalState; struct SourceExprCommand; struct Buildable @@ -45,7 +44,7 @@ struct Installable App toApp(EvalState & state); - virtual Value * toValue(EvalState & state) + virtual std::pair toValue(EvalState & state) { throw Error("argument '%s' cannot be evaluated", what()); } @@ -91,7 +90,7 @@ struct InstallableFlake : InstallableValue std::vector toDerivations() override; - Value * toValue(EvalState & state) override; + std::pair toValue(EvalState & state) override; }; } -- cgit v1.2.3 From 442e665d6d3fcbdee7dece2f62a597142f8784b1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Feb 2020 23:50:16 +0100 Subject: nix path-info --json: Print hash in SRI format --- src/nix/path-info.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index bffa7b356..45ec297d2 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -89,7 +89,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON store->pathInfoToJSON(jsonRoot, // FIXME: preserve order? storePathsToSet(storePaths), - true, showClosureSize, AllowInvalid); + true, showClosureSize, SRI, AllowInvalid); } else { -- cgit v1.2.3 From 46a284263fe03bc81950586376b9be43d1b5c713 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 14 Feb 2020 22:45:33 +0100 Subject: Fix build --- src/nix/flake.cc | 4 ++-- src/nix/installables.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8945d8829..9c94c59fc 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -134,9 +134,9 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceAttrs(*(*aOutputs)->value); + state.forceAttrs(*aOutputs->value); - for (auto & attr : *((*aOutputs)->value->attrs)) + for (auto & attr : *aOutputs->value->attrs) callback(attr.name, *attr.value, *attr.pos); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 39ce9b38f..071edf432 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -330,9 +330,9 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceValue(*(*aOutputs)->value); + state.forceValue(*aOutputs->value); - return (*aOutputs)->value; + return aOutputs->value; } std::tuple InstallableFlake::toDerivation() -- cgit v1.2.3 From e375da6899a939d5b987eff8a5a85fd083b24849 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 13 Feb 2020 19:35:40 +0100 Subject: Add 'nix eval-hydra-jobs' command --- src/nix/eval-hydra-jobs.cc | 191 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 src/nix/eval-hydra-jobs.cc (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc new file mode 100644 index 000000000..dd3fcf8be --- /dev/null +++ b/src/nix/eval-hydra-jobs.cc @@ -0,0 +1,191 @@ +#include "command.hh" +#include "eval.hh" +#include "eval-inline.hh" +#include "derivations.hh" +#include "common-args.hh" +#include "json.hh" +#include "get-drvs.hh" + +using namespace nix; + +static std::string queryMetaStrings(EvalState & state, DrvInfo & drv, const string & name, const string & subAttribute) +{ + Strings res; + std::function rec; + + rec = [&](Value & v) { + state.forceValue(v); + if (v.type == tString) + res.push_back(v.string.s); + else if (v.isList()) + for (unsigned int n = 0; n < v.listSize(); ++n) + rec(*v.listElems()[n]); + else if (v.type == tAttrs) { + auto a = v.attrs->find(state.symbols.create(subAttribute)); + if (a != v.attrs->end()) + res.push_back(state.forceString(*a->value)); + } + }; + + Value * v = drv.queryMeta(name); + if (v) rec(*v); + + return concatStringsSep(", ", res); +} + +struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand +{ + std::optional gcRootsDir; + + CmdEvalHydraJobs() + { + mkFlag() + .longName("gc-roots-dir") + .description("garbage collector roots directory") + .labels({"path"}) + .dest(&gcRootsDir); + } + + std::string description() override + { + return "evaluate a Hydra jobset"; + } + + Examples examples() override + { + return { + Example{ + "Evaluate Nixpkgs' release-combined jobset:", + "nix eval-hydra-jobs -f '' '' --json" + }, + }; + } + + void run(ref store) override + { + auto state = getEvalState(); + + if (!gcRootsDir) warn("'--gc-roots-dir' not specified"); + + if (dryRun) settings.readOnlyMode = true; + + /* Prevent access to paths outside of the Nix search path and + to the environment. */ + evalSettings.restrictEval = true; + + auto v = installable->toValue(*state).first; + + auto jsonObj = json ? std::make_unique(std::cout, true) : nullptr; + + std::function findJobs; + + auto autoArgs = getAutoArgs(*state); + + findJobs = [&](Value & vIn, const string & attrPath) + { + try { + Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", attrPath)); + + checkInterrupt(); + + auto v = state->allocValue(); + state->autoCallFunction(*autoArgs, vIn, *v); + + if (v->type == tAttrs) { + auto drv = getDerivation(*state, *v, false); + + if (drv) { + + DrvInfo::Outputs outputs = drv->queryOutputs(); + + if (drv->querySystem() == "unknown") + throw EvalError("derivation must have a 'system' attribute"); + + auto drvPath = drv->queryDrvPath(); + + if (jsonObj) { + auto res = jsonObj->object(attrPath); + res.attr("nixName", drv->queryName()); + res.attr("system", drv->querySystem()); + res.attr("drvPath", drvPath); + res.attr("description", drv->queryMetaString("description")); + res.attr("license", queryMetaStrings(*state, *drv, "license", "shortName")); + res.attr("homepage", drv->queryMetaString("homepage")); + res.attr("maintainers", queryMetaStrings(*state, *drv, "maintainers", "email")); + res.attr("schedulingPriority", drv->queryMetaInt("schedulingPriority", 100)); + res.attr("timeout", drv->queryMetaInt("timeout", 36000)); + res.attr("maxSilent", drv->queryMetaInt("maxSilent", 7200)); + res.attr("isChannel", drv->queryMetaBool("isHydraChannel", false)); + + /* If this is an aggregate, then get its constituents. */ + auto a = v->attrs->get(state->symbols.create("_hydraAggregate")); + if (a && state->forceBool(*a->value, *a->pos)) { + auto a = v->attrs->get(state->symbols.create("constituents")); + if (!a) + throw EvalError("derivation must have a ‘constituents’ attribute"); + PathSet context; + state->coerceToString(*a->pos, *a->value, context, true, false); + PathSet drvs; + for (auto & i : context) + if (i.at(0) == '!') { + size_t index = i.find("!", 1); + drvs.insert(string(i, index + 1)); + } + res.attr("constituents", concatStringsSep(" ", drvs)); + } + + /* Register the derivation as a GC root. !!! This + registers roots for jobs that we may have already + done. */ + auto localStore = state->store.dynamic_pointer_cast(); + if (gcRootsDir && localStore) { + Path root = *gcRootsDir + "/" + std::string(baseNameOf(drvPath)); + if (!pathExists(root)) + localStore->addPermRoot(localStore->parseStorePath(drvPath), root, false); + } + + auto res2 = res.object("outputs"); + for (auto & j : outputs) + res2.attr(j.first, j.second); + } else + std::cout << fmt("%d: %d\n", attrPath, drvPath); + + } + + else { + if (!state->isDerivation(*v)) { + for (auto & i : v->attrs->lexicographicOrder()) { + std::string name(i->name); + + /* Skip jobs with dots in the name. */ + if (name.find('.') != std::string::npos) { + printError("skipping job with illegal name '%s'", name); + continue; + } + + findJobs(*i->value, (attrPath.empty() ? "" : attrPath + ".") + name); + } + } + } + } + + else if (v->type == tNull) { + // allow null values, meaning 'do nothing' + } + + else + throw TypeError("unsupported value: %s", *v); + + } catch (EvalError & e) { + if (jsonObj) + jsonObj->object(attrPath).attr("error", filterANSIEscapes(e.msg(), true)); + else + printError("in job '%s': %s", attrPath, e.what()); + } + }; + + findJobs(*v, ""); + } +}; + +static auto r1 = registerCommand("eval-hydra-jobs"); -- cgit v1.2.3 From 4c242639673a832f5b2dd3439205811c42992bb4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 14 Feb 2020 22:34:14 +0100 Subject: nix eval-hydra-jobs: Support parallel evaluation Example usage: $ nix eval-hydra-jobs -f '' '' \ --max-memory-size 2048 --workers 8 --- src/nix/eval-hydra-jobs.cc | 327 ++++++++++++++++++++++++++++++++------------- 1 file changed, 235 insertions(+), 92 deletions(-) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index dd3fcf8be..60e41c3eb 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -5,6 +5,10 @@ #include "common-args.hh" #include "json.hh" #include "get-drvs.hh" +#include "attr-path.hh" + +#include +#include using namespace nix; @@ -36,6 +40,8 @@ static std::string queryMetaStrings(EvalState & state, DrvInfo & drv, const stri struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand { std::optional gcRootsDir; + size_t nrWorkers = 1; + size_t maxMemorySize = 4ULL * 1024; CmdEvalHydraJobs() { @@ -44,6 +50,10 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand .description("garbage collector roots directory") .labels({"path"}) .dest(&gcRootsDir); + + mkIntFlag(0, "workers", "number of concurrent worker processes", &nrWorkers); + + mkIntFlag(0, "max-memory-size", "maximum memory usage per worker process (in MiB)", &maxMemorySize); } std::string description() override @@ -61,11 +71,11 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand }; } - void run(ref store) override + void worker(AutoCloseFD & to, AutoCloseFD & from) { auto state = getEvalState(); - if (!gcRootsDir) warn("'--gc-roots-dir' not specified"); + // FIXME: should re-open state->store. if (dryRun) settings.readOnlyMode = true; @@ -73,118 +83,251 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand to the environment. */ evalSettings.restrictEval = true; - auto v = installable->toValue(*state).first; + auto autoArgs = getAutoArgs(*state); - auto jsonObj = json ? std::make_unique(std::cout, true) : nullptr; + auto vTop = installable->toValue(*state).first; - std::function findJobs; + auto vRoot = state->allocValue(); + state->autoCallFunction(*autoArgs, *vTop, *vRoot); - auto autoArgs = getAutoArgs(*state); + while (true) { + /* Wait for the master to send us a job name. */ + writeLine(to.get(), "next"); + + auto s = readLine(from.get()); + if (s == "exit") break; + if (!hasPrefix(s, "do ")) abort(); + std::string attrPath(s, 3); + + debug("worker process %d at '%s'", getpid(), attrPath); + + /* Evaluate it and send info back to the master. */ + nlohmann::json reply; - findJobs = [&](Value & vIn, const string & attrPath) - { try { - Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", attrPath)); - - checkInterrupt(); - - auto v = state->allocValue(); - state->autoCallFunction(*autoArgs, vIn, *v); - - if (v->type == tAttrs) { - auto drv = getDerivation(*state, *v, false); - - if (drv) { - - DrvInfo::Outputs outputs = drv->queryOutputs(); - - if (drv->querySystem() == "unknown") - throw EvalError("derivation must have a 'system' attribute"); - - auto drvPath = drv->queryDrvPath(); - - if (jsonObj) { - auto res = jsonObj->object(attrPath); - res.attr("nixName", drv->queryName()); - res.attr("system", drv->querySystem()); - res.attr("drvPath", drvPath); - res.attr("description", drv->queryMetaString("description")); - res.attr("license", queryMetaStrings(*state, *drv, "license", "shortName")); - res.attr("homepage", drv->queryMetaString("homepage")); - res.attr("maintainers", queryMetaStrings(*state, *drv, "maintainers", "email")); - res.attr("schedulingPriority", drv->queryMetaInt("schedulingPriority", 100)); - res.attr("timeout", drv->queryMetaInt("timeout", 36000)); - res.attr("maxSilent", drv->queryMetaInt("maxSilent", 7200)); - res.attr("isChannel", drv->queryMetaBool("isHydraChannel", false)); - - /* If this is an aggregate, then get its constituents. */ - auto a = v->attrs->get(state->symbols.create("_hydraAggregate")); - if (a && state->forceBool(*a->value, *a->pos)) { - auto a = v->attrs->get(state->symbols.create("constituents")); - if (!a) - throw EvalError("derivation must have a ‘constituents’ attribute"); - PathSet context; - state->coerceToString(*a->pos, *a->value, context, true, false); - PathSet drvs; - for (auto & i : context) - if (i.at(0) == '!') { - size_t index = i.find("!", 1); - drvs.insert(string(i, index + 1)); - } - res.attr("constituents", concatStringsSep(" ", drvs)); + auto v = findAlongAttrPath(*state, attrPath, *autoArgs, *vRoot).first; + + state->forceValue(*v); + + if (auto drv = getDerivation(*state, *v, false)) { + + DrvInfo::Outputs outputs = drv->queryOutputs(); + + if (drv->querySystem() == "unknown") + throw EvalError("derivation must have a 'system' attribute"); + + auto drvPath = drv->queryDrvPath(); + + nlohmann::json job; + + job["nixName"] = drv->queryName(); + job["system"] =drv->querySystem(); + job["drvPath"] = drvPath; + job["description"] = drv->queryMetaString("description"); + job["license"] = queryMetaStrings(*state, *drv, "license", "shortName"); + job["homepage"] = drv->queryMetaString("homepage"); + job["maintainers"] = queryMetaStrings(*state, *drv, "maintainers", "email"); + job["schedulingPriority"] = drv->queryMetaInt("schedulingPriority", 100); + job["timeout"] = drv->queryMetaInt("timeout", 36000); + job["maxSilent"] = drv->queryMetaInt("maxSilent", 7200); + job["isChannel"] = drv->queryMetaBool("isHydraChannel", false); + + /* If this is an aggregate, then get its constituents. */ + auto a = v->attrs->get(state->symbols.create("_hydraAggregate")); + if (a && state->forceBool(*a->value, *a->pos)) { + auto a = v->attrs->get(state->symbols.create("constituents")); + if (!a) + throw EvalError("derivation must have a ‘constituents’ attribute"); + PathSet context; + state->coerceToString(*a->pos, *a->value, context, true, false); + PathSet drvs; + for (auto & i : context) + if (i.at(0) == '!') { + size_t index = i.find("!", 1); + drvs.insert(string(i, index + 1)); } + job["constituents"] = concatStringsSep(" ", drvs); + } - /* Register the derivation as a GC root. !!! This - registers roots for jobs that we may have already - done. */ - auto localStore = state->store.dynamic_pointer_cast(); - if (gcRootsDir && localStore) { - Path root = *gcRootsDir + "/" + std::string(baseNameOf(drvPath)); - if (!pathExists(root)) - localStore->addPermRoot(localStore->parseStorePath(drvPath), root, false); - } + /* Register the derivation as a GC root. !!! This + registers roots for jobs that we may have already + done. */ + auto localStore = state->store.dynamic_pointer_cast(); + if (gcRootsDir && localStore) { + Path root = *gcRootsDir + "/" + std::string(baseNameOf(drvPath)); + if (!pathExists(root)) + localStore->addPermRoot(localStore->parseStorePath(drvPath), root, false); + } - auto res2 = res.object("outputs"); - for (auto & j : outputs) - res2.attr(j.first, j.second); - } else - std::cout << fmt("%d: %d\n", attrPath, drvPath); + nlohmann::json out; + for (auto & j : outputs) + out[j.first] = j.second; + job["outputs"] = std::move(out); + + reply["job"] = std::move(job); + } + else if (v->type == tAttrs) { + auto attrs = nlohmann::json::array(); + StringSet ss; + for (auto & i : v->attrs->lexicographicOrder()) { + std::string name(i->name); + if (name.find('.') != std::string::npos || name.find(' ') != std::string::npos) { + printError("skipping job with illegal name '%s'", name); + continue; + } + attrs.push_back(name); } + reply["attrs"] = std::move(attrs); + } + + } catch (EvalError & e) { + reply["error"] = filterANSIEscapes(e.msg(), true); + } + + writeLine(to.get(), reply.dump()); - else { - if (!state->isDerivation(*v)) { - for (auto & i : v->attrs->lexicographicOrder()) { - std::string name(i->name); + /* If our RSS exceeds the maximum, exit. The master will + start a new process. */ + struct rusage r; + getrusage(RUSAGE_SELF, &r); + if ((size_t) r.ru_maxrss > maxMemorySize * 1024) break; + } - /* Skip jobs with dots in the name. */ - if (name.find('.') != std::string::npos) { - printError("skipping job with illegal name '%s'", name); - continue; + writeLine(to.get(), "restart"); + } + + void run(ref store) override + { + if (!gcRootsDir) warn("'--gc-roots-dir' not specified"); + + struct State + { + std::set todo{""}; + std::set active; + nlohmann::json result; + }; + + std::condition_variable wakeup; + + Sync state_; + + /* Start a handler thread per worker process. */ + auto handler = [this, &state_, &wakeup]() + { + try { + pid_t pid = -1; + AutoCloseFD from, to; + + while (true) { + + /* Start a new worker process if necessary. */ + if (pid == -1) { + Pipe toPipe, fromPipe; + toPipe.create(); + fromPipe.create(); + pid = startProcess( + [this, + to{std::make_shared(std::move(fromPipe.writeSide))}, + from{std::make_shared(std::move(toPipe.readSide))} + ]() + { + try { + worker(*to, *from); + } catch (Error & e) { + printError("unexpected worker error: %s", e.msg()); + _exit(1); } + }, + ProcessOptions { .allowVfork = false }); + from = std::move(fromPipe.readSide); + to = std::move(toPipe.writeSide); + debug("created worker process %d", pid); + } - findJobs(*i->value, (attrPath.empty() ? "" : attrPath + ".") + name); - } + /* Check whether the existing worker process is still there. */ + auto s = readLine(from.get()); + if (s == "restart") { + pid = -1; + continue; + } else if (s != "next") + throw Error("unexpected worker request: %s", s); + + /* Wait for a job name to become available. */ + std::string attrPath; + + while (true) { + checkInterrupt(); + auto state(state_.lock()); + if (state->todo.empty() && state->active.empty()) { + writeLine(to.get(), "exit"); + return; } + if (!state->todo.empty()) { + attrPath = *state->todo.begin(); + state->todo.erase(state->todo.begin()); + state->active.insert(attrPath); + break; + } else + state.wait(wakeup); } - } - else if (v->type == tNull) { - // allow null values, meaning 'do nothing' - } + /* Tell the worker to evaluate it. */ + writeLine(to.get(), "do " + attrPath); - else - throw TypeError("unsupported value: %s", *v); + /* Wait for the response. */ + auto response = nlohmann::json::parse(readLine(from.get())); - } catch (EvalError & e) { - if (jsonObj) - jsonObj->object(attrPath).attr("error", filterANSIEscapes(e.msg(), true)); - else - printError("in job '%s': %s", attrPath, e.what()); + /* Handle the response. */ + StringSet newAttrs; + + if (response.find("job") != response.end()) { + auto state(state_.lock()); + if (json) + state->result[attrPath] = response["job"]; + else + std::cout << fmt("%d: %d\n", attrPath, (std::string) response["job"]["drvPath"]); + } + + if (response.find("attrs") != response.end()) { + for (auto & i : response["attrs"]) { + auto s = (attrPath.empty() ? "" : attrPath + ".") + (std::string) i; + newAttrs.insert(s); + } + } + + if (response.find("error") != response.end()) { + auto state(state_.lock()); + if (json) + state->result[attrPath]["error"] = response["error"]; + else + printError("error in job '%s': %s", + attrPath, (std::string) response["error"]); + } + + /* Add newly discovered job names to the queue. */ + { + auto state(state_.lock()); + state->active.erase(attrPath); + for (auto & s : newAttrs) + state->todo.insert(s); + wakeup.notify_all(); + } + } + } catch (Error & e) { + printError("unexpected handler thread error: %s", e.msg()); + abort(); } }; - findJobs(*v, ""); + std::vector threads; + for (size_t i = 0; i < nrWorkers; i++) + threads.emplace_back(std::thread(handler)); + + for (auto & thread : threads) + thread.join(); + + if (json) std::cout << state_.lock()->result.dump(2) << "\n"; } }; -- cgit v1.2.3 From eb19ff3b82240326fc0e999e09f81b6c8ed98640 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 14 Feb 2020 23:23:41 +0100 Subject: nix eval-hydra-jobs: Support flakes --- src/nix/eval-hydra-jobs.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index 60e41c3eb..18a681eb4 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -71,6 +71,11 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand }; } + Strings getDefaultFlakeAttrPaths() override + { + return {"hydraJobs", "checks"}; + } + void worker(AutoCloseFD & to, AutoCloseFD & from) { auto state = getEvalState(); -- cgit v1.2.3 From c6e63065f3c39044c148e39838a44cb6d1922809 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Feb 2020 13:36:13 +0100 Subject: nix eval-hydra-jobs: Improve error handling --- src/nix/eval-hydra-jobs.cc | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index 18a681eb4..579ab9cf7 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -211,6 +211,7 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand std::set todo{""}; std::set active; nlohmann::json result; + std::exception_ptr exc; }; std::condition_variable wakeup; @@ -239,9 +240,10 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand { try { worker(*to, *from); - } catch (Error & e) { - printError("unexpected worker error: %s", e.msg()); - _exit(1); + } catch (std::exception & e) { + nlohmann::json err; + err["error"] = e.what(); + writeLine(to->get(), err.dump()); } }, ProcessOptions { .allowVfork = false }); @@ -255,8 +257,10 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand if (s == "restart") { pid = -1; continue; - } else if (s != "next") - throw Error("unexpected worker request: %s", s); + } else if (s != "next") { + auto json = nlohmann::json::parse(s); + throw Error("worker error: %s", (std::string) json["error"]); + } /* Wait for a job name to become available. */ std::string attrPath; @@ -264,7 +268,7 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand while (true) { checkInterrupt(); auto state(state_.lock()); - if (state->todo.empty() && state->active.empty()) { + if ((state->todo.empty() && state->active.empty()) || state->exc) { writeLine(to.get(), "exit"); return; } @@ -319,9 +323,10 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand wakeup.notify_all(); } } - } catch (Error & e) { - printError("unexpected handler thread error: %s", e.msg()); - abort(); + } catch (...) { + auto state(state_.lock()); + state->exc = std::current_exception(); + wakeup.notify_all(); } }; @@ -332,7 +337,12 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand for (auto & thread : threads) thread.join(); - if (json) std::cout << state_.lock()->result.dump(2) << "\n"; + auto state(state_.lock()); + + if (state->exc) + std::rethrow_exception(state->exc); + + if (json) std::cout << state->result.dump(2) << "\n"; } }; -- cgit v1.2.3 From b0336e7cf77f9e170c74a730bc45453393ba0db2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Feb 2020 14:33:10 +0100 Subject: nix eval-hydra-job: Progress indicator --- src/nix/eval-hydra-jobs.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index 579ab9cf7..180c401b2 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -281,6 +281,8 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand state.wait(wakeup); } + Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", attrPath)); + /* Tell the worker to evaluate it. */ writeLine(to.get(), "do " + attrPath); -- cgit v1.2.3 From 6529490cc10018d5191e50c482ac1180b96b1a3c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Feb 2020 15:53:59 +0100 Subject: nix eval-hydra-jobs: Support job names as aggregate constituents Fixes https://github.com/NixOS/hydra/issues/715. --- src/nix/eval-hydra-jobs.cc | 60 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index 180c401b2..b04e6df7b 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -143,15 +143,23 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand auto a = v->attrs->get(state->symbols.create("constituents")); if (!a) throw EvalError("derivation must have a ‘constituents’ attribute"); + + PathSet context; state->coerceToString(*a->pos, *a->value, context, true, false); - PathSet drvs; for (auto & i : context) if (i.at(0) == '!') { size_t index = i.find("!", 1); - drvs.insert(string(i, index + 1)); + job["constituents"].push_back(string(i, index + 1)); } - job["constituents"] = concatStringsSep(" ", drvs); + + state->forceList(*a->value, *a->pos); + for (unsigned int n = 0; n < a->value->listSize(); ++n) { + auto v = a->value->listElems()[n]; + state->forceValue(*v); + if (v->type == tString) + job["namedConstituents"].push_back(state->forceStringNoCtx(*v)); + } } /* Register the derivation as a GC root. !!! This @@ -210,7 +218,7 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand { std::set todo{""}; std::set active; - nlohmann::json result; + nlohmann::json jobs; std::exception_ptr exc; }; @@ -295,7 +303,7 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand if (response.find("job") != response.end()) { auto state(state_.lock()); if (json) - state->result[attrPath] = response["job"]; + state->jobs[attrPath] = response["job"]; else std::cout << fmt("%d: %d\n", attrPath, (std::string) response["job"]["drvPath"]); } @@ -310,7 +318,7 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand if (response.find("error") != response.end()) { auto state(state_.lock()); if (json) - state->result[attrPath]["error"] = response["error"]; + state->jobs[attrPath]["error"] = response["error"]; else printError("error in job '%s': %s", attrPath, (std::string) response["error"]); @@ -344,7 +352,45 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand if (state->exc) std::rethrow_exception(state->exc); - if (json) std::cout << state->result.dump(2) << "\n"; + /* For aggregate jobs that have named consistuents + (i.e. constituents that are a job name rather than a + derivation), look up the referenced job and add it to the + dependencies of the aggregate derivation. */ + for (auto i = state->jobs.begin(); i != state->jobs.end(); ++i) { + auto jobName = i.key(); + auto & job = i.value(); + + auto named = job.find("namedConstituents"); + if (named == job.end() || dryRun) continue; + + std::string drvPath = job["drvPath"]; + auto drv = readDerivation(*store, drvPath); + + for (std::string jobName2 : *named) { + auto job2 = state->jobs.find(jobName2); + if (job2 == state->jobs.end()) + throw Error("aggregate job '%s' references non-existent job '%s'", jobName, jobName2); + std::string drvPath2 = (*job2)["drvPath"]; + auto drv2 = readDerivation(*store, drvPath2); + job["constituents"].push_back(drvPath2); + drv.inputDrvs[store->parseStorePath(drvPath2)] = {drv2.outputs.begin()->first}; + } + + std::string drvName(store->parseStorePath(drvPath).name()); + auto h = hashDerivationModulo(*store, drv, true); + auto outPath = store->makeOutputPath("out", h, drvName); + drv.env["out"] = store->printStorePath(outPath); + drv.outputs.insert_or_assign("out", DerivationOutput(outPath.clone(), "", "")); + auto newDrvPath = store->printStorePath(writeDerivation(store, drv, drvName)); + + debug("rewrote aggregate derivation %s -> %s", drvPath, newDrvPath); + + job["drvPath"] = newDrvPath; + job["outputs"]["out"] = store->printStorePath(outPath); + job.erase("namedConstituents"); + } + + if (json) std::cout << state->jobs.dump(2) << "\n"; } }; -- cgit v1.2.3 From 8f9dcfc67175992dbfe8e5cfb10b8716dcad89a8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 18 Feb 2020 17:47:53 +0100 Subject: Disable the progress bar if $TERM == dumb or unset Fixes #3363. --- src/nix/progress-bar.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/progress-bar.cc b/src/nix/progress-bar.cc index c445f31cc..26631416c 100644 --- a/src/nix/progress-bar.cc +++ b/src/nix/progress-bar.cc @@ -446,7 +446,9 @@ public: void startProgressBar(bool printBuildLogs) { - logger = new ProgressBar(printBuildLogs, isatty(STDERR_FILENO)); + logger = new ProgressBar( + printBuildLogs, + isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"); } void stopProgressBar() -- cgit v1.2.3 From 1351101c2875982557fd8ed6ccbe19a4df9d613b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 18 Feb 2020 19:26:11 +0100 Subject: nix eval-hydra-jobs: Check aggregate jobs in --dry-run mode --- src/nix/eval-hydra-jobs.cc | 57 +++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 23 deletions(-) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index b04e6df7b..57edfc70c 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -361,32 +361,43 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand auto & job = i.value(); auto named = job.find("namedConstituents"); - if (named == job.end() || dryRun) continue; - - std::string drvPath = job["drvPath"]; - auto drv = readDerivation(*store, drvPath); - - for (std::string jobName2 : *named) { - auto job2 = state->jobs.find(jobName2); - if (job2 == state->jobs.end()) - throw Error("aggregate job '%s' references non-existent job '%s'", jobName, jobName2); - std::string drvPath2 = (*job2)["drvPath"]; - auto drv2 = readDerivation(*store, drvPath2); - job["constituents"].push_back(drvPath2); - drv.inputDrvs[store->parseStorePath(drvPath2)] = {drv2.outputs.begin()->first}; - } + if (named == job.end()) continue; + + if (dryRun) { + for (std::string jobName2 : *named) { + auto job2 = state->jobs.find(jobName2); + if (job2 == state->jobs.end()) + throw Error("aggregate job '%s' references non-existent job '%s'", jobName, jobName2); + std::string drvPath2 = (*job2)["drvPath"]; + job["constituents"].push_back(drvPath2); + } + } else { + std::string drvPath = job["drvPath"]; + auto drv = readDerivation(*store, drvPath); + + for (std::string jobName2 : *named) { + auto job2 = state->jobs.find(jobName2); + if (job2 == state->jobs.end()) + throw Error("aggregate job '%s' references non-existent job '%s'", jobName, jobName2); + std::string drvPath2 = (*job2)["drvPath"]; + auto drv2 = readDerivation(*store, drvPath2); + job["constituents"].push_back(drvPath2); + drv.inputDrvs[store->parseStorePath(drvPath2)] = {drv2.outputs.begin()->first}; + } - std::string drvName(store->parseStorePath(drvPath).name()); - auto h = hashDerivationModulo(*store, drv, true); - auto outPath = store->makeOutputPath("out", h, drvName); - drv.env["out"] = store->printStorePath(outPath); - drv.outputs.insert_or_assign("out", DerivationOutput(outPath.clone(), "", "")); - auto newDrvPath = store->printStorePath(writeDerivation(store, drv, drvName)); + std::string drvName(store->parseStorePath(drvPath).name()); + auto h = hashDerivationModulo(*store, drv, true); + auto outPath = store->makeOutputPath("out", h, drvName); + drv.env["out"] = store->printStorePath(outPath); + drv.outputs.insert_or_assign("out", DerivationOutput(outPath.clone(), "", "")); + auto newDrvPath = store->printStorePath(writeDerivation(store, drv, drvName)); - debug("rewrote aggregate derivation %s -> %s", drvPath, newDrvPath); + debug("rewrote aggregate derivation %s -> %s", drvPath, newDrvPath); + + job["drvPath"] = newDrvPath; + job["outputs"]["out"] = store->printStorePath(outPath); + } - job["drvPath"] = newDrvPath; - job["outputs"]["out"] = store->printStorePath(outPath); job.erase("namedConstituents"); } -- cgit v1.2.3 From edee6169bf772ba101be7cd2dcb5aa7814415466 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 18 Feb 2020 22:05:49 +0100 Subject: nix eval-hydra-jobs: Fix aggregate derivation name --- src/nix/eval-hydra-jobs.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index 57edfc70c..579e7ad31 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -386,6 +386,8 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand } std::string drvName(store->parseStorePath(drvPath).name()); + assert(hasSuffix(drvName, drvExtension)); + drvName.resize(drvName.size() - drvExtension.size()); auto h = hashDerivationModulo(*store, drv, true); auto outPath = store->makeOutputPath("out", h, drvName); drv.env["out"] = store->printStorePath(outPath); -- cgit v1.2.3 From 95468e3c1e5e42c501bae61d80d08c509629a0f9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 19 Feb 2020 00:09:42 +0100 Subject: Fix nixpkgs. warning --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 071edf432..3bf4fa8f4 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -454,7 +454,7 @@ std::vector> SourceExprCommand::parseInstallables( for (auto & s : ss) { if (hasPrefix(s, "nixpkgs.")) { bool static warned; - warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs:' instead"); + warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs#' instead"); result.push_back(std::make_shared(*this, FlakeRef::fromAttrs({{"type", "indirect"}, {"id", "nixpkgs"}}), Strings{"legacyPackages." + settings.thisSystem.get() + "." + std::string(s, 8)}, Strings{})); -- cgit v1.2.3 From 30c8297ded3caf7ebd862d5eef23333b19a9b554 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 19 Feb 2020 12:35:03 +0100 Subject: nix eval-hydra-jobs: Add feature --- src/nix/eval-hydra-jobs.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc index 579e7ad31..f66fb3571 100644 --- a/src/nix/eval-hydra-jobs.cc +++ b/src/nix/eval-hydra-jobs.cc @@ -212,6 +212,8 @@ struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand void run(ref store) override { + settings.requireExperimentalFeature("eval-hydra-jobs"); + if (!gcRootsDir) warn("'--gc-roots-dir' not specified"); struct State -- cgit v1.2.3 From 4ad5826a182f8eeb4cbc61e27d2128b08050b0d4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 19 Feb 2020 16:12:49 +0100 Subject: nix eval-hydra-jobs: Remove On second thought, let's move this back to Hydra. --- src/nix/eval-hydra-jobs.cc | 412 --------------------------------------------- 1 file changed, 412 deletions(-) delete mode 100644 src/nix/eval-hydra-jobs.cc (limited to 'src/nix') diff --git a/src/nix/eval-hydra-jobs.cc b/src/nix/eval-hydra-jobs.cc deleted file mode 100644 index f66fb3571..000000000 --- a/src/nix/eval-hydra-jobs.cc +++ /dev/null @@ -1,412 +0,0 @@ -#include "command.hh" -#include "eval.hh" -#include "eval-inline.hh" -#include "derivations.hh" -#include "common-args.hh" -#include "json.hh" -#include "get-drvs.hh" -#include "attr-path.hh" - -#include -#include - -using namespace nix; - -static std::string queryMetaStrings(EvalState & state, DrvInfo & drv, const string & name, const string & subAttribute) -{ - Strings res; - std::function rec; - - rec = [&](Value & v) { - state.forceValue(v); - if (v.type == tString) - res.push_back(v.string.s); - else if (v.isList()) - for (unsigned int n = 0; n < v.listSize(); ++n) - rec(*v.listElems()[n]); - else if (v.type == tAttrs) { - auto a = v.attrs->find(state.symbols.create(subAttribute)); - if (a != v.attrs->end()) - res.push_back(state.forceString(*a->value)); - } - }; - - Value * v = drv.queryMeta(name); - if (v) rec(*v); - - return concatStringsSep(", ", res); -} - -struct CmdEvalHydraJobs : MixJSON, MixDryRun, InstallableCommand -{ - std::optional gcRootsDir; - size_t nrWorkers = 1; - size_t maxMemorySize = 4ULL * 1024; - - CmdEvalHydraJobs() - { - mkFlag() - .longName("gc-roots-dir") - .description("garbage collector roots directory") - .labels({"path"}) - .dest(&gcRootsDir); - - mkIntFlag(0, "workers", "number of concurrent worker processes", &nrWorkers); - - mkIntFlag(0, "max-memory-size", "maximum memory usage per worker process (in MiB)", &maxMemorySize); - } - - std::string description() override - { - return "evaluate a Hydra jobset"; - } - - Examples examples() override - { - return { - Example{ - "Evaluate Nixpkgs' release-combined jobset:", - "nix eval-hydra-jobs -f '' '' --json" - }, - }; - } - - Strings getDefaultFlakeAttrPaths() override - { - return {"hydraJobs", "checks"}; - } - - void worker(AutoCloseFD & to, AutoCloseFD & from) - { - auto state = getEvalState(); - - // FIXME: should re-open state->store. - - if (dryRun) settings.readOnlyMode = true; - - /* Prevent access to paths outside of the Nix search path and - to the environment. */ - evalSettings.restrictEval = true; - - auto autoArgs = getAutoArgs(*state); - - auto vTop = installable->toValue(*state).first; - - auto vRoot = state->allocValue(); - state->autoCallFunction(*autoArgs, *vTop, *vRoot); - - while (true) { - /* Wait for the master to send us a job name. */ - writeLine(to.get(), "next"); - - auto s = readLine(from.get()); - if (s == "exit") break; - if (!hasPrefix(s, "do ")) abort(); - std::string attrPath(s, 3); - - debug("worker process %d at '%s'", getpid(), attrPath); - - /* Evaluate it and send info back to the master. */ - nlohmann::json reply; - - try { - auto v = findAlongAttrPath(*state, attrPath, *autoArgs, *vRoot).first; - - state->forceValue(*v); - - if (auto drv = getDerivation(*state, *v, false)) { - - DrvInfo::Outputs outputs = drv->queryOutputs(); - - if (drv->querySystem() == "unknown") - throw EvalError("derivation must have a 'system' attribute"); - - auto drvPath = drv->queryDrvPath(); - - nlohmann::json job; - - job["nixName"] = drv->queryName(); - job["system"] =drv->querySystem(); - job["drvPath"] = drvPath; - job["description"] = drv->queryMetaString("description"); - job["license"] = queryMetaStrings(*state, *drv, "license", "shortName"); - job["homepage"] = drv->queryMetaString("homepage"); - job["maintainers"] = queryMetaStrings(*state, *drv, "maintainers", "email"); - job["schedulingPriority"] = drv->queryMetaInt("schedulingPriority", 100); - job["timeout"] = drv->queryMetaInt("timeout", 36000); - job["maxSilent"] = drv->queryMetaInt("maxSilent", 7200); - job["isChannel"] = drv->queryMetaBool("isHydraChannel", false); - - /* If this is an aggregate, then get its constituents. */ - auto a = v->attrs->get(state->symbols.create("_hydraAggregate")); - if (a && state->forceBool(*a->value, *a->pos)) { - auto a = v->attrs->get(state->symbols.create("constituents")); - if (!a) - throw EvalError("derivation must have a ‘constituents’ attribute"); - - - PathSet context; - state->coerceToString(*a->pos, *a->value, context, true, false); - for (auto & i : context) - if (i.at(0) == '!') { - size_t index = i.find("!", 1); - job["constituents"].push_back(string(i, index + 1)); - } - - state->forceList(*a->value, *a->pos); - for (unsigned int n = 0; n < a->value->listSize(); ++n) { - auto v = a->value->listElems()[n]; - state->forceValue(*v); - if (v->type == tString) - job["namedConstituents"].push_back(state->forceStringNoCtx(*v)); - } - } - - /* Register the derivation as a GC root. !!! This - registers roots for jobs that we may have already - done. */ - auto localStore = state->store.dynamic_pointer_cast(); - if (gcRootsDir && localStore) { - Path root = *gcRootsDir + "/" + std::string(baseNameOf(drvPath)); - if (!pathExists(root)) - localStore->addPermRoot(localStore->parseStorePath(drvPath), root, false); - } - - nlohmann::json out; - for (auto & j : outputs) - out[j.first] = j.second; - job["outputs"] = std::move(out); - - reply["job"] = std::move(job); - } - - else if (v->type == tAttrs) { - auto attrs = nlohmann::json::array(); - StringSet ss; - for (auto & i : v->attrs->lexicographicOrder()) { - std::string name(i->name); - if (name.find('.') != std::string::npos || name.find(' ') != std::string::npos) { - printError("skipping job with illegal name '%s'", name); - continue; - } - attrs.push_back(name); - } - reply["attrs"] = std::move(attrs); - } - - } catch (EvalError & e) { - reply["error"] = filterANSIEscapes(e.msg(), true); - } - - writeLine(to.get(), reply.dump()); - - /* If our RSS exceeds the maximum, exit. The master will - start a new process. */ - struct rusage r; - getrusage(RUSAGE_SELF, &r); - if ((size_t) r.ru_maxrss > maxMemorySize * 1024) break; - } - - writeLine(to.get(), "restart"); - } - - void run(ref store) override - { - settings.requireExperimentalFeature("eval-hydra-jobs"); - - if (!gcRootsDir) warn("'--gc-roots-dir' not specified"); - - struct State - { - std::set todo{""}; - std::set active; - nlohmann::json jobs; - std::exception_ptr exc; - }; - - std::condition_variable wakeup; - - Sync state_; - - /* Start a handler thread per worker process. */ - auto handler = [this, &state_, &wakeup]() - { - try { - pid_t pid = -1; - AutoCloseFD from, to; - - while (true) { - - /* Start a new worker process if necessary. */ - if (pid == -1) { - Pipe toPipe, fromPipe; - toPipe.create(); - fromPipe.create(); - pid = startProcess( - [this, - to{std::make_shared(std::move(fromPipe.writeSide))}, - from{std::make_shared(std::move(toPipe.readSide))} - ]() - { - try { - worker(*to, *from); - } catch (std::exception & e) { - nlohmann::json err; - err["error"] = e.what(); - writeLine(to->get(), err.dump()); - } - }, - ProcessOptions { .allowVfork = false }); - from = std::move(fromPipe.readSide); - to = std::move(toPipe.writeSide); - debug("created worker process %d", pid); - } - - /* Check whether the existing worker process is still there. */ - auto s = readLine(from.get()); - if (s == "restart") { - pid = -1; - continue; - } else if (s != "next") { - auto json = nlohmann::json::parse(s); - throw Error("worker error: %s", (std::string) json["error"]); - } - - /* Wait for a job name to become available. */ - std::string attrPath; - - while (true) { - checkInterrupt(); - auto state(state_.lock()); - if ((state->todo.empty() && state->active.empty()) || state->exc) { - writeLine(to.get(), "exit"); - return; - } - if (!state->todo.empty()) { - attrPath = *state->todo.begin(); - state->todo.erase(state->todo.begin()); - state->active.insert(attrPath); - break; - } else - state.wait(wakeup); - } - - Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", attrPath)); - - /* Tell the worker to evaluate it. */ - writeLine(to.get(), "do " + attrPath); - - /* Wait for the response. */ - auto response = nlohmann::json::parse(readLine(from.get())); - - /* Handle the response. */ - StringSet newAttrs; - - if (response.find("job") != response.end()) { - auto state(state_.lock()); - if (json) - state->jobs[attrPath] = response["job"]; - else - std::cout << fmt("%d: %d\n", attrPath, (std::string) response["job"]["drvPath"]); - } - - if (response.find("attrs") != response.end()) { - for (auto & i : response["attrs"]) { - auto s = (attrPath.empty() ? "" : attrPath + ".") + (std::string) i; - newAttrs.insert(s); - } - } - - if (response.find("error") != response.end()) { - auto state(state_.lock()); - if (json) - state->jobs[attrPath]["error"] = response["error"]; - else - printError("error in job '%s': %s", - attrPath, (std::string) response["error"]); - } - - /* Add newly discovered job names to the queue. */ - { - auto state(state_.lock()); - state->active.erase(attrPath); - for (auto & s : newAttrs) - state->todo.insert(s); - wakeup.notify_all(); - } - } - } catch (...) { - auto state(state_.lock()); - state->exc = std::current_exception(); - wakeup.notify_all(); - } - }; - - std::vector threads; - for (size_t i = 0; i < nrWorkers; i++) - threads.emplace_back(std::thread(handler)); - - for (auto & thread : threads) - thread.join(); - - auto state(state_.lock()); - - if (state->exc) - std::rethrow_exception(state->exc); - - /* For aggregate jobs that have named consistuents - (i.e. constituents that are a job name rather than a - derivation), look up the referenced job and add it to the - dependencies of the aggregate derivation. */ - for (auto i = state->jobs.begin(); i != state->jobs.end(); ++i) { - auto jobName = i.key(); - auto & job = i.value(); - - auto named = job.find("namedConstituents"); - if (named == job.end()) continue; - - if (dryRun) { - for (std::string jobName2 : *named) { - auto job2 = state->jobs.find(jobName2); - if (job2 == state->jobs.end()) - throw Error("aggregate job '%s' references non-existent job '%s'", jobName, jobName2); - std::string drvPath2 = (*job2)["drvPath"]; - job["constituents"].push_back(drvPath2); - } - } else { - std::string drvPath = job["drvPath"]; - auto drv = readDerivation(*store, drvPath); - - for (std::string jobName2 : *named) { - auto job2 = state->jobs.find(jobName2); - if (job2 == state->jobs.end()) - throw Error("aggregate job '%s' references non-existent job '%s'", jobName, jobName2); - std::string drvPath2 = (*job2)["drvPath"]; - auto drv2 = readDerivation(*store, drvPath2); - job["constituents"].push_back(drvPath2); - drv.inputDrvs[store->parseStorePath(drvPath2)] = {drv2.outputs.begin()->first}; - } - - std::string drvName(store->parseStorePath(drvPath).name()); - assert(hasSuffix(drvName, drvExtension)); - drvName.resize(drvName.size() - drvExtension.size()); - auto h = hashDerivationModulo(*store, drv, true); - auto outPath = store->makeOutputPath("out", h, drvName); - drv.env["out"] = store->printStorePath(outPath); - drv.outputs.insert_or_assign("out", DerivationOutput(outPath.clone(), "", "")); - auto newDrvPath = store->printStorePath(writeDerivation(store, drv, drvName)); - - debug("rewrote aggregate derivation %s -> %s", drvPath, newDrvPath); - - job["drvPath"] = newDrvPath; - job["outputs"]["out"] = store->printStorePath(outPath); - } - - job.erase("namedConstituents"); - } - - if (json) std::cout << state->jobs.dump(2) << "\n"; - } -}; - -static auto r1 = registerCommand("eval-hydra-jobs"); -- cgit v1.2.3 From d068f9ffff3d2a98e6dde0834a250e4930d44778 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 20 Feb 2020 22:14:44 +0100 Subject: Restore subdir support in registries Hacky... --- src/nix/flake.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9c94c59fc..42f5b2ce0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -70,8 +70,8 @@ struct CmdFlakeList : EvalCommand registry->type == Registry::Flag ? "flags " : registry->type == Registry::User ? "user " : "global", - entry.first->to_string(), - entry.second->to_string()); + std::get<0>(entry)->to_string(), + std::get<1>(entry)->to_string()); } } } @@ -506,9 +506,11 @@ struct CmdFlakeAdd : MixEvalArgs, Command { auto fromRef = parseFlakeRef(fromUrl); auto toRef = parseFlakeRef(toUrl); + fetchers::Input::Attrs extraAttrs; + if (toRef.subdir != "") extraAttrs["subdir"] = toRef.subdir; auto userRegistry = fetchers::getUserRegistry(); userRegistry->remove(fromRef.input); - userRegistry->add(fromRef.input, toRef.input); + userRegistry->add(fromRef.input, toRef.input, extraAttrs); userRegistry->write(fetchers::getUserRegistryPath()); } }; @@ -555,7 +557,9 @@ struct CmdFlakePin : virtual Args, EvalCommand auto userRegistry = fetchers::getUserRegistry(); userRegistry->remove(ref.input); auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); - userRegistry->add(ref.input, resolved); + fetchers::Input::Attrs extraAttrs; + if (ref.subdir != "") extraAttrs["subdir"] = ref.subdir; + userRegistry->add(ref.input, resolved, extraAttrs); } }; -- cgit v1.2.3 From 73c98405695837dd93448ed38c0eeb0b56060dfc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 20 Feb 2020 23:44:06 +0100 Subject: Restore subdir -> dir Got this mixed up somewhere. --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 42f5b2ce0..0887fb402 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -507,7 +507,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command auto fromRef = parseFlakeRef(fromUrl); auto toRef = parseFlakeRef(toUrl); fetchers::Input::Attrs extraAttrs; - if (toRef.subdir != "") extraAttrs["subdir"] = toRef.subdir; + if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; auto userRegistry = fetchers::getUserRegistry(); userRegistry->remove(fromRef.input); userRegistry->add(fromRef.input, toRef.input, extraAttrs); @@ -558,7 +558,7 @@ struct CmdFlakePin : virtual Args, EvalCommand userRegistry->remove(ref.input); auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); fetchers::Input::Attrs extraAttrs; - if (ref.subdir != "") extraAttrs["subdir"] = ref.subdir; + if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; userRegistry->add(ref.input, resolved, extraAttrs); } }; -- cgit v1.2.3 From 2672a28bb4ba38a8358c306b8af4897c804b690d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 27 Feb 2020 15:17:37 +0100 Subject: nix dev-shell: Add --command option Note: like 'nix run', and unlike 'nix-shell', this takes an argv vector rather than a shell command. So nix dev-shell -c 'echo $PATH' doesn't work. Instead you need to do nix dev-shell -c bash -c 'echo $PATH' --- src/nix/shell.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/nix') diff --git a/src/nix/shell.cc b/src/nix/shell.cc index 6b3d02b6b..bee0bddcc 100644 --- a/src/nix/shell.cc +++ b/src/nix/shell.cc @@ -232,6 +232,22 @@ struct Common : InstallableCommand, MixProfile struct CmdDevShell : Common, MixEnvironment { + std::vector command; + + CmdDevShell() + { + mkFlag() + .longName("command") + .shortName('c') + .description("command and arguments to be executed insted of an interactive shell") + .labels({"command", "args"}) + .arity(ArityAny) + .handler([&](std::vector ss) { + if (ss.empty()) throw UsageError("--command requires at least one argument"); + command = ss; + }); + } + std::string description() override { return "run a bash shell that provides the build environment of a derivation"; @@ -270,6 +286,13 @@ struct CmdDevShell : Common, MixEnvironment ss << fmt("rm -f '%s'\n", rcFilePath); + if (!command.empty()) { + std::vector args; + for (auto s : command) + args.push_back(shellEscape(s)); + ss << fmt("exec %s\n", concatStringsSep(" ", args)); + } + writeFull(rcFileFd.get(), ss.str()); stopProgressBar(); -- cgit v1.2.3 From ae9119167ebb24c95e8e45e12889ea147926ceb7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 12 Mar 2020 22:06:57 +0100 Subject: Change the lock file to a graph This enables support for cycles between flakes. --- src/nix/flake.cc | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0887fb402..82357aef8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -199,24 +199,25 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON stopProgressBar(); if (json) - std::cout << ((LockedInputs &) flake.lockFile).toJson() << "\n"; + std::cout << flake.lockFile.toJson() << "\n"; else { std::cout << fmt("%s\n", flake.flake.lockedRef); - std::function recurse; + std::function recurse; - recurse = [&](const LockedInputs & inputs, const std::string & prefix) + recurse = [&](const Node & node, const std::string & prefix) { - for (const auto & [i, input] : enumerate(inputs.inputs)) { + for (const auto & [i, input] : enumerate(node.inputs)) { //auto tree2 = tree.child(i + 1 == inputs.inputs.size()); - bool last = i + 1 == inputs.inputs.size(); + bool last = i + 1 == node.inputs.size(); std::cout << fmt("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s\n", - prefix + (last ? treeLast : treeConn), input.first, input.second.lockedRef); - recurse(input.second, prefix + (last ? treeNull : treeLine)); + prefix + (last ? treeLast : treeConn), input.first, + std::dynamic_pointer_cast(input.second)->lockedRef); + recurse(*input.second, prefix + (last ? treeNull : treeLine)); } }; - recurse(flake.lockFile, ""); + recurse(*flake.lockFile.root, ""); } } }; @@ -664,23 +665,26 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun if (jsonRoot) jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath)); - std::function & jsonObj)> traverse; - traverse = [&](const LockedInputs & inputs, std::optional & jsonObj) + // FIXME: use graph output, handle cycles. + std::function & jsonObj)> traverse; + traverse = [&](const Node & node, std::optional & jsonObj) { auto jsonObj2 = jsonObj ? jsonObj->object("inputs") : std::optional(); - for (auto & input : inputs.inputs) { + for (auto & input : node.inputs) { + auto lockedInput = std::dynamic_pointer_cast(input.second); + assert(lockedInput); auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional(); if (!dryRun) - input.second.lockedRef.input->fetchTree(store); - auto storePath = input.second.computeStorePath(*store); + lockedInput->lockedRef.input->fetchTree(store); + auto storePath = lockedInput->computeStorePath(*store); if (jsonObj3) jsonObj3->attr("path", store->printStorePath(storePath)); sources.insert(std::move(storePath)); - traverse(input.second, jsonObj3); + traverse(*lockedInput, jsonObj3); } }; - traverse(flake.lockFile, jsonRoot); + traverse(*flake.lockFile.root, jsonRoot); if (!dryRun && !dstUri.empty()) { ref dstStore = dstUri.empty() ? openStore() : openStore(dstUri); -- cgit v1.2.3 From 2a4e4f6a6e021481f0e92b7d3006345e68e77684 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 17 Mar 2020 20:54:36 +0100 Subject: Unified fetcher caching system --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 82357aef8..317d1bc18 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -507,7 +507,7 @@ struct CmdFlakeAdd : MixEvalArgs, Command { auto fromRef = parseFlakeRef(fromUrl); auto toRef = parseFlakeRef(toUrl); - fetchers::Input::Attrs extraAttrs; + fetchers::Attrs extraAttrs; if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; auto userRegistry = fetchers::getUserRegistry(); userRegistry->remove(fromRef.input); @@ -558,7 +558,7 @@ struct CmdFlakePin : virtual Args, EvalCommand auto userRegistry = fetchers::getUserRegistry(); userRegistry->remove(ref.input); auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); - fetchers::Input::Attrs extraAttrs; + fetchers::Attrs extraAttrs; if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; userRegistry->add(ref.input, resolved, extraAttrs); } -- cgit v1.2.3 From 1b494798360cca30971b43adda5baa154bf1991e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 18 Mar 2020 14:11:58 +0100 Subject: Remove flake closure caching This is not compatible with lazy flake input fetching. --- src/nix/installables.cc | 52 ------------------------------------------------- 1 file changed, 52 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 3bf4fa8f4..64ea1e000 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -256,56 +256,6 @@ struct InstallableAttrPath : InstallableValue } }; -void makeFlakeClosureGCRoot(Store & store, - const FlakeRef & origFlakeRef, - const flake::LockedFlake & lockedFlake) -{ -#if 0 - if (std::get_if(&origFlakeRef.data)) return; - - /* Get the store paths of all non-local flakes. */ - StorePathSet closure; - - assert(store.isValidPath(store.parseStorePath(lockedFlake.flake.sourceInfo.storePath))); - closure.insert(store.parseStorePath(lockedFlake.flake.sourceInfo.storePath)); - - std::queue> queue; - queue.push(lockedFlake.lockFile); - - while (!queue.empty()) { - const flake::LockedInputs & flake = queue.front(); - queue.pop(); - /* Note: due to lazy fetching, these paths might not exist - yet. */ - for (auto & dep : flake.inputs) { - auto path = dep.second.computeStorePath(store); - if (store.isValidPath(store.parseStorePath(path))) - closure.insert(store.parseStorePath(path)); - queue.push(dep.second); - } - } - - if (closure.empty()) return; - - /* Write the closure to a file in the store. */ - auto closurePath = store.addTextToStore("flake-closure", - concatStringsSep(" ", store.printStorePathSet(closure)), closure); - - Path cacheDir = getCacheDir() + "/nix/flake-closures"; - createDirs(cacheDir); - - auto s = origFlakeRef.to_string(); - assert(s[0] != '.'); - s = replaceStrings(s, "%", "%25"); - s = replaceStrings(s, "/", "%2f"); - s = replaceStrings(s, ":", "%3a"); - Path symlink = cacheDir + "/" + s; - debug("writing GC root '%s' for flake closure of '%s'", symlink, origFlakeRef); - replaceSymlink(store.printStorePath(closurePath), symlink); - store.addIndirectRoot(symlink); -#endif -} - std::vector InstallableFlake::getActualAttrPaths() { std::vector res; @@ -325,8 +275,6 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked callFlake(state, lockedFlake, *vFlake); - makeFlakeClosureGCRoot(*state.store, flakeRef, lockedFlake); - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); -- cgit v1.2.3 From 2287e2f279ac544a2c11921be51f2f556cb78abc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 28 Mar 2020 18:05:50 +0100 Subject: nix flake info: Show flake subdirectory --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 317d1bc18..b090ea201 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -79,7 +79,7 @@ struct CmdFlakeList : EvalCommand static void printFlakeInfo(const Store & store, const Flake & flake) { - std::cout << fmt("URL: %s\n", flake.lockedRef.input->to_string()); + std::cout << fmt("URL: %s\n", flake.lockedRef.to_string()); std::cout << fmt("Edition: %s\n", flake.edition); if (flake.description) std::cout << fmt("Description: %s\n", *flake.description); @@ -99,7 +99,7 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) if (flake.description) j["description"] = *flake.description; j["edition"] = flake.edition; - j["url"] = flake.lockedRef.input->to_string(); + j["url"] = flake.lockedRef.to_string(); if (auto rev = flake.lockedRef.input->getRev()) j["revision"] = rev->to_string(Base16, false); if (flake.sourceInfo->info.revCount) -- cgit v1.2.3 From 4989c04dd2795bdd1bc8a6b7336a6e53179eca63 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 28 Mar 2020 22:59:38 +0100 Subject: nix flake info --json: Dump attr-style flakeref --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b090ea201..e5aa424b5 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -100,6 +100,8 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) j["description"] = *flake.description; j["edition"] = flake.edition; j["url"] = flake.lockedRef.to_string(); + j["original"] = attrsToJson(flake.originalRef.toAttrs()); + j["locked"] = attrsToJson(flake.lockedRef.toAttrs()); if (auto rev = flake.lockedRef.input->getRev()) j["revision"] = rev->to_string(Base16, false); if (flake.sourceInfo->info.revCount) -- cgit v1.2.3 From e0a0ae0467fa8cdcc542f593b9d94283f04508ff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 30 Mar 2020 14:03:28 +0200 Subject: Move fetchers from libstore to libfetchers --- src/nix/flake.cc | 4 ++-- src/nix/installables.cc | 2 +- src/nix/local.mk | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e5aa424b5..2cc61932d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -9,8 +9,8 @@ #include "store-api.hh" #include "derivations.hh" #include "attr-path.hh" -#include "fetchers/fetchers.hh" -#include "fetchers/registry.hh" +#include "fetchers.hh" +#include "registry.hh" #include "json.hh" #include diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 64ea1e000..99bbe9769 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -10,7 +10,7 @@ #include "shared.hh" #include "flake/flake.hh" #include "flake/eval-cache.hh" -#include "fetchers/parse.hh" +#include "url.hh" #include #include diff --git a/src/nix/local.mk b/src/nix/local.mk index 6483000db..622f49019 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -15,7 +15,7 @@ nix_SOURCES := \ $(wildcard src/nix-prefetch-url/*.cc) \ $(wildcard src/nix-store/*.cc) \ -nix_LIBS = libexpr libmain libstore libutil libnixrust +nix_LIBS = libexpr libmain libfetchers libstore libutil libnixrust nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system -- cgit v1.2.3 From 2c692a3b144523bca68dd6de618124ba6c9bb332 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 30 Mar 2020 14:29:29 +0200 Subject: Remove global -I flags --- src/nix/local.mk | 2 ++ src/nix/profile.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/local.mk b/src/nix/local.mk index 622f49019..3fcd15dc6 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -15,6 +15,8 @@ nix_SOURCES := \ $(wildcard src/nix-prefetch-url/*.cc) \ $(wildcard src/nix-store/*.cc) \ +nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain + nix_LIBS = libexpr libmain libfetchers libstore libutil libnixrust nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 1759b83a3..c6a4ddd34 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -6,7 +6,7 @@ #include "archive.hh" #include "builtins/buildenv.hh" #include "flake/flakeref.hh" -#include "nix-env/user-env.hh" +#include "../nix-env/user-env.hh" #include #include -- cgit v1.2.3 From 03b56e96bfa89e82beee3f57de79d64c9f173afd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 31 Mar 2020 23:55:07 +0200 Subject: Typo --- src/nix/build.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 4fd1de026..ae931873f 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -43,7 +43,7 @@ struct CmdBuild : MixDryRun, MixProfile, InstallablesCommand }, Example{ "To make a profile point at GNU Hello:", - "nix build --profile /tmp/profile nixpkgs:hello" + "nix build --profile /tmp/profile nixpkgs#hello" }, }; } -- cgit v1.2.3 From 77ffaea4fa12f0525758d6c4a3a3bd074906978d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Apr 2020 22:56:50 +0200 Subject: Add a system-wide flake registry /etc/nix/registry.json One application for this is pinning the 'nixpkgs' flake to the exact revision used to build the NixOS system, e.g. { "flakes": [ { "from": { "id": "nixpkgs", "type": "indirect" }, "to": { "owner": "NixOS", "repo": "nixpkgs", "type": "github", "rev": "b0c285807d6a9f1b7562ec417c24fa1a30ecc31a" } } ], "version": 2 } --- src/nix/flake.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2cc61932d..16b797de8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -67,8 +67,9 @@ struct CmdFlakeList : EvalCommand for (auto & entry : registry->entries) { // FIXME: format nicely std::cout << fmt("%s %s %s\n", - registry->type == Registry::Flag ? "flags " : - registry->type == Registry::User ? "user " : + registry->type == Registry::Flag ? "flags " : + registry->type == Registry::User ? "user " : + registry->type == Registry::System ? "system" : "global", std::get<0>(entry)->to_string(), std::get<1>(entry)->to_string()); -- cgit v1.2.3 From bd10a07d17161fb4a4e1af5aa365b23d405a5216 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Apr 2020 23:03:27 +0200 Subject: Registry: Use a struct instead of a tuple for entries --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 16b797de8..2b7497a84 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -71,8 +71,8 @@ struct CmdFlakeList : EvalCommand registry->type == Registry::User ? "user " : registry->type == Registry::System ? "system" : "global", - std::get<0>(entry)->to_string(), - std::get<1>(entry)->to_string()); + entry.from->to_string(), + entry.to->to_string()); } } } -- cgit v1.2.3 From ed13457dbf1a78f47f760e349985b23dfd5f16c4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Apr 2020 11:51:34 +0200 Subject: nix flake info --json: Show TreeInfo --- src/nix/flake.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2b7497a84..93dbb9601 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -103,6 +103,7 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) j["url"] = flake.lockedRef.to_string(); j["original"] = attrsToJson(flake.originalRef.toAttrs()); j["locked"] = attrsToJson(flake.lockedRef.toAttrs()); + j["info"] = flake.sourceInfo->info.toJson(); if (auto rev = flake.lockedRef.input->getRev()) j["revision"] = rev->to_string(Base16, false); if (flake.sourceInfo->info.revCount) -- cgit v1.2.3 From ce3173edc15ad704c5083baba09b7bcdb99d5104 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 Apr 2020 14:39:47 +0200 Subject: nix flake info --json: Don't evaluate This makes its behaviour consistent with the non-json variant. Querying the outputs should be done by another command (e.g. 'nix search') --- src/nix/flake.cc | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 93dbb9601..03bb9fe37 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -153,39 +153,14 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON void run(nix::ref store) override { - if (json) { - auto state = getEvalState(); - auto flake = lockFlake(); - - auto json = flakeToJson(*store, flake.flake); - - auto vFlake = state->allocValue(); - flake::callFlake(*state, flake, *vFlake); - - auto outputs = nlohmann::json::object(); - - enumerateOutputs(*state, - *vFlake, - [&](const std::string & name, Value & vProvide, const Pos & pos) { - auto provide = nlohmann::json::object(); - - if (name == "checks" || name == "packages") { - state->forceAttrs(vProvide, pos); - for (auto & aCheck : *vProvide.attrs) - provide[aCheck.name] = nlohmann::json::object(); - } - - outputs[name] = provide; - }); - - json["outputs"] = std::move(outputs); + auto flake = getFlake(); + stopProgressBar(); + if (json) { + auto json = flakeToJson(*store, flake); std::cout << json.dump() << std::endl; - } else { - auto flake = getFlake(); - stopProgressBar(); + } else printFlakeInfo(*store, flake); - } } }; -- cgit v1.2.3 From 68b43e01ddf990182c87a924d647dc7aa93b34f7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 Apr 2020 14:56:13 +0200 Subject: nix flake info: Show resolved URL This is useful for finding out what a registry lookup resolves to, e.g $ nix flake info patchelf Resolved URL: github:NixOS/patchelf Locked URL: github:NixOS/patchelf/cd7955af31698c571c30b7a0f78e59fd624d0229 --- src/nix/flake.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 03bb9fe37..e79f4129a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -80,7 +80,8 @@ struct CmdFlakeList : EvalCommand static void printFlakeInfo(const Store & store, const Flake & flake) { - std::cout << fmt("URL: %s\n", flake.lockedRef.to_string()); + std::cout << fmt("Resolved URL: %s\n", flake.resolvedRef.to_string()); + std::cout << fmt("Locked URL: %s\n", flake.lockedRef.to_string()); std::cout << fmt("Edition: %s\n", flake.edition); if (flake.description) std::cout << fmt("Description: %s\n", *flake.description); @@ -100,8 +101,11 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) if (flake.description) j["description"] = *flake.description; j["edition"] = flake.edition; - j["url"] = flake.lockedRef.to_string(); + j["originalUrl"] = flake.originalRef.to_string(); j["original"] = attrsToJson(flake.originalRef.toAttrs()); + j["resolvedUrl"] = flake.resolvedRef.to_string(); + j["resolved"] = attrsToJson(flake.resolvedRef.toAttrs()); + j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl j["locked"] = attrsToJson(flake.lockedRef.toAttrs()); j["info"] = flake.sourceInfo->info.toJson(); if (auto rev = flake.lockedRef.input->getRev()) -- cgit v1.2.3 From e5ea01c1a8bbd328dcc576928bf3e4271cb55399 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 10 Apr 2020 10:24:09 +0200 Subject: Remove flake 'edition' field Future editions of flakes or the Nix language can be supported by renaming flake.nix (e.g. flake-v2.nix). This avoids a bootstrap problem where we don't know which grammar to use to parse flake*.nix. It also allows a project to support multiple flake editions, in theory. --- src/nix/flake-template.nix | 2 -- src/nix/flake.cc | 2 -- 2 files changed, 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix index 321961013..60d7171f3 100644 --- a/src/nix/flake-template.nix +++ b/src/nix/flake-template.nix @@ -1,8 +1,6 @@ { description = "A flake for building Hello World"; - edition = 201909; - outputs = { self, nixpkgs }: { packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e79f4129a..e8ca0fd03 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -82,7 +82,6 @@ static void printFlakeInfo(const Store & store, const Flake & flake) { std::cout << fmt("Resolved URL: %s\n", flake.resolvedRef.to_string()); std::cout << fmt("Locked URL: %s\n", flake.lockedRef.to_string()); - std::cout << fmt("Edition: %s\n", flake.edition); if (flake.description) std::cout << fmt("Description: %s\n", *flake.description); std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); @@ -100,7 +99,6 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) nlohmann::json j; if (flake.description) j["description"] = *flake.description; - j["edition"] = flake.edition; j["originalUrl"] = flake.originalRef.to_string(); j["original"] = attrsToJson(flake.originalRef.toAttrs()); j["resolvedUrl"] = flake.resolvedRef.to_string(); -- cgit v1.2.3 From 696c026006a6ac46adc990ed5cb0f31535bac076 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Apr 2020 13:12:58 +0200 Subject: Logger: Add method for writing to stdout Usually this just writes to stdout, but for ProgressBar, we need to clear the current line, write the line to stdout, and then redraw the progress bar. --- src/nix/progress-bar.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/nix') diff --git a/src/nix/progress-bar.cc b/src/nix/progress-bar.cc index adc9b9a5d..8e7ba95a3 100644 --- a/src/nix/progress-bar.cc +++ b/src/nix/progress-bar.cc @@ -7,6 +7,7 @@ #include #include #include +#include namespace nix { @@ -442,6 +443,18 @@ public: return res; } + + void writeToStdout(std::string_view s) override + { + auto state(state_.lock()); + if (state->active) { + std::cerr << "\r\e[K"; + Logger::writeToStdout(s); + draw(*state); + } else { + Logger::writeToStdout(s); + } + } }; void startProgressBar(bool printBuildLogs) -- cgit v1.2.3 From 8f41847394524fcac40d3b5620139ca7e94a18e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Apr 2020 13:46:37 +0200 Subject: Use Logger::stdout() --- src/nix/add-to-store.cc | 2 +- src/nix/eval.cc | 5 ++--- src/nix/flake.cc | 30 ++++++++++++------------------ src/nix/hash.cc | 5 ++--- src/nix/ls.cc | 10 ++++------ src/nix/profile.cc | 2 +- src/nix/show-config.cc | 2 +- src/nix/why-depends.cc | 2 +- 8 files changed, 24 insertions(+), 34 deletions(-) (limited to 'src/nix') diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index 139db3657..ed02227db 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -50,7 +50,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand if (!dryRun) store->addToStore(info, sink.s); - std::cout << fmt("%s\n", store->printStorePath(info.path)); + logger->stdout("%s", store->printStorePath(info.path)); } }; diff --git a/src/nix/eval.cc b/src/nix/eval.cc index f23625161..96ca83325 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -55,16 +55,15 @@ struct CmdEval : MixJSON, InstallableCommand auto v = installable->toValue(*state).first; PathSet context; - stopProgressBar(); - if (raw) { + stopProgressBar(); std::cout << state->coerceToString(noPos, *v, context); } else if (json) { JSONPlaceholder jsonOut(std::cout); printValueAsJSON(*state, true, *v, jsonOut, context); } else { state->forceValueDeep(*v); - std::cout << *v << "\n"; + logger->stdout("%s", *v); } } }; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index e8ca0fd03..a8518a543 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1,7 +1,6 @@ #include "command.hh" #include "common-args.hh" #include "shared.hh" -#include "progress-bar.hh" #include "eval.hh" #include "eval-inline.hh" #include "flake/flake.hh" @@ -61,12 +60,10 @@ struct CmdFlakeList : EvalCommand auto registries = getRegistries(store); - stopProgressBar(); - for (auto & registry : registries) { for (auto & entry : registry->entries) { // FIXME: format nicely - std::cout << fmt("%s %s %s\n", + logger->stdout("%s %s %s", registry->type == Registry::Flag ? "flags " : registry->type == Registry::User ? "user " : registry->type == Registry::System ? "system" : @@ -80,17 +77,17 @@ struct CmdFlakeList : EvalCommand static void printFlakeInfo(const Store & store, const Flake & flake) { - std::cout << fmt("Resolved URL: %s\n", flake.resolvedRef.to_string()); - std::cout << fmt("Locked URL: %s\n", flake.lockedRef.to_string()); + logger->stdout("Resolved URL: %s", flake.resolvedRef.to_string()); + logger->stdout("Locked URL: %s", flake.lockedRef.to_string()); if (flake.description) - std::cout << fmt("Description: %s\n", *flake.description); - std::cout << fmt("Path: %s\n", store.printStorePath(flake.sourceInfo->storePath)); + logger->stdout("Description: %s", *flake.description); + logger->stdout("Path: %s", store.printStorePath(flake.sourceInfo->storePath)); if (auto rev = flake.lockedRef.input->getRev()) - std::cout << fmt("Revision: %s\n", rev->to_string(Base16, false)); + logger->stdout("Revision: %s", rev->to_string(Base16, false)); if (flake.sourceInfo->info.revCount) - std::cout << fmt("Revisions: %s\n", *flake.sourceInfo->info.revCount); + logger->stdout("Revisions: %s", *flake.sourceInfo->info.revCount); if (flake.sourceInfo->info.lastModified) - std::cout << fmt("Last modified: %s\n", + logger->stdout("Last modified: %s", std::put_time(std::localtime(&*flake.sourceInfo->info.lastModified), "%F %T")); } @@ -156,11 +153,10 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON void run(nix::ref store) override { auto flake = getFlake(); - stopProgressBar(); if (json) { auto json = flakeToJson(*store, flake); - std::cout << json.dump() << std::endl; + logger->stdout(json.dump()); } else printFlakeInfo(*store, flake); } @@ -177,12 +173,10 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON { auto flake = lockFlake(); - stopProgressBar(); - if (json) - std::cout << flake.lockFile.toJson() << "\n"; + logger->stdout(flake.lockFile.toJson()); else { - std::cout << fmt("%s\n", flake.flake.lockedRef); + logger->stdout("%s", flake.flake.lockedRef); std::function recurse; @@ -191,7 +185,7 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON for (const auto & [i, input] : enumerate(node.inputs)) { //auto tree2 = tree.child(i + 1 == inputs.inputs.size()); bool last = i + 1 == node.inputs.size(); - std::cout << fmt("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s\n", + logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", prefix + (last ? treeLast : treeConn), input.first, std::dynamic_pointer_cast(input.second)->lockedRef); recurse(*input.second, prefix + (last ? treeNull : treeLine)); diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 0cc523f50..01628cf6c 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -60,8 +60,7 @@ struct CmdHash : Command Hash h = hashSink->finish().first; if (truncate && h.hashSize > 20) h = compressHash(h, 20); - std::cout << format("%1%\n") % - h.to_string(base, base == SRI); + logger->stdout(h.to_string(base, base == SRI)); } } }; @@ -95,7 +94,7 @@ struct CmdToBase : Command void run() override { for (auto s : args) - std::cout << fmt("%s\n", Hash(s, ht).to_string(base, base == SRI)); + logger->stdout(Hash(s, ht).to_string(base, base == SRI)); } }; diff --git a/src/nix/ls.cc b/src/nix/ls.cc index 3ef1f2750..8590199d7 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -34,16 +34,14 @@ struct MixLs : virtual Args, MixJSON (st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--") : st.type == FSAccessor::Type::tSymlink ? "lrwxrwxrwx" : "dr-xr-xr-x"; - std::cout << - (format("%s %20d %s") % tp % st.fileSize % relPath); + auto line = fmt("%s %20d %s", tp, st.fileSize, relPath); if (st.type == FSAccessor::Type::tSymlink) - std::cout << " -> " << accessor->readLink(curPath) - ; - std::cout << "\n"; + line += " -> " + accessor->readLink(curPath); + logger->stdout(line); if (recursive && st.type == FSAccessor::Type::tDirectory) doPath(st, curPath, relPath, false); } else { - std::cout << relPath << "\n"; + logger->stdout(relPath); if (recursive) { auto st = accessor->stat(curPath); if (st.type == FSAccessor::Type::tDirectory) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c6a4ddd34..e473be47f 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -385,7 +385,7 @@ struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultPro for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); - std::cout << fmt("%d %s %s %s\n", i, + logger->stdout("%d %s %s %s", i, element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-", element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-", concatStringsSep(" ", store->printStorePathSet(element.storePaths))); diff --git a/src/nix/show-config.cc b/src/nix/show-config.cc index 87544f937..55a3255d2 100644 --- a/src/nix/show-config.cc +++ b/src/nix/show-config.cc @@ -23,7 +23,7 @@ struct CmdShowConfig : Command, MixJSON std::map settings; globalConfig.getSettings(settings); for (auto & s : settings) - std::cout << s.first + " = " + s.second.value + "\n"; + logger->stdout(s.first + " = " + s.second.value); } } }; diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index a9a5ec8f8..6037f37fe 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -149,7 +149,7 @@ struct CmdWhyDepends : SourceExprCommand auto pathS = store->printStorePath(node.path); assert(node.dist != inf); - std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n", + logger->stdout("%s%s%s%s" ANSI_NORMAL, firstPad, node.visited ? "\e[38;5;244m" : "", firstPad != "" ? "→ " : "", -- cgit v1.2.3 From c277231b7d8c11b8612acda578f759a6fd427c8f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Apr 2020 16:54:34 +0200 Subject: Use RootValue --- src/nix/installables.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 99bbe9769..b8c75aaaf 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -239,18 +239,18 @@ struct InstallableExpr : InstallableValue struct InstallableAttrPath : InstallableValue { - Value * v; + RootValue v; std::string attrPath; InstallableAttrPath(SourceExprCommand & cmd, Value * v, const std::string & attrPath) - : InstallableValue(cmd), v(v), attrPath(attrPath) + : InstallableValue(cmd), v(allocRootValue(v)), attrPath(attrPath) { } std::string what() override { return attrPath; } std::pair toValue(EvalState & state) override { - auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), *v); + auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); state.forceValue(*vRes); return {vRes, pos}; } -- cgit v1.2.3 From 29043e7e9edad1ff81b45ec147da32fbd6607385 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Apr 2020 19:01:49 +0200 Subject: Fix --- src/nix/flake.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a8518a543..9d3f3c002 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -156,7 +156,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON if (json) { auto json = flakeToJson(*store, flake); - logger->stdout(json.dump()); + logger->stdout("%s", json.dump()); } else printFlakeInfo(*store, flake); } @@ -174,7 +174,7 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON auto flake = lockFlake(); if (json) - logger->stdout(flake.lockFile.toJson()); + logger->stdout("%s", flake.lockFile.toJson()); else { logger->stdout("%s", flake.flake.lockedRef); -- cgit v1.2.3 From 3b489e8843f4730d4dd0753453ccb1c21429b0e9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Apr 2020 15:36:15 +0200 Subject: Add 'nix flake show' command --- src/nix/flake.cc | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nix/search.cc | 15 +++- 2 files changed, 224 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9d3f3c002..b94da23f3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -668,6 +668,218 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun } }; +struct AttrCursor : std::enable_shared_from_this +{ + EvalState & state; + typedef std::optional, std::string>> Parent; + Parent parent; + RootValue value; + + AttrCursor( + EvalState & state, + Parent parent, + Value * value) + : state(state), parent(parent), value(allocRootValue(value)) + { + } + + std::vector getAttrPath() const + { + if (parent) { + auto attrPath = parent->first->getAttrPath(); + attrPath.push_back(parent->second); + return attrPath; + } else + return {}; + } + + std::shared_ptr maybeGetAttr(const std::string & name) + { + state.forceValue(**value); + + if ((*value)->type != tAttrs) + return nullptr; + + auto attr = (*value)->attrs->get(state.symbols.create(name)); + + if (!attr) + return nullptr; + + return std::allocate_shared(traceable_allocator(), state, std::make_pair(shared_from_this(), name), attr->value); + } + + std::shared_ptr getAttr(const std::string & name) + { + auto p = maybeGetAttr(name); + if (!p) { + auto attrPath = getAttrPath(); + attrPath.push_back(name); + throw Error("attribute '%s' does not exist", concatStringsSep(".", attrPath)); + } + return p; + } + + std::string getString() + { + return state.forceString(**value); + } + + StringSet getAttrs() + { + StringSet attrs; + state.forceAttrs(**value); + for (auto & attr : *(*value)->attrs) + attrs.insert(attr.name); + return attrs; + } + + bool isDerivation() + { + auto aType = maybeGetAttr("type"); + return aType && aType->getString() == "derivation"; + } +}; + +struct CmdFlakeShow : FlakeCommand +{ + bool showLegacy = false; + + CmdFlakeShow() + { + mkFlag() + .longName("legacy") + .description("enumerate the contents of the 'legacyPackages' output") + .set(&showLegacy, true); + } + + std::string description() override + { + return "show the outputs provided by a flake"; + } + + void run(nix::ref store) override + { + auto state = getEvalState(); + auto flake = lockFlake(); + + auto vFlake = state->allocValue(); + flake::callFlake(*state, flake, *vFlake); + + state->forceAttrs(*vFlake); + + auto aOutputs = vFlake->attrs->get(state->symbols.create("outputs")); + assert(aOutputs); + + std::function & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; + + visit = [&](AttrCursor & visitor, const std::vector & attrPath, const std::string & headerPrefix, const std::string & nextPrefix) + { + Activity act(*logger, lvlInfo, actUnknown, + fmt("evaluating '%s'", concatStringsSep(".", attrPath))); + try { + auto recurse = [&]() + { + logger->stdout("%s", headerPrefix); + auto attrs = visitor.getAttrs(); + for (const auto & [i, attr] : enumerate(attrs)) { + bool last = i + 1 == attrs.size(); + auto visitor2 = visitor.getAttr(attr); + auto attrPath2(attrPath); + attrPath2.push_back(attr); + visit(*visitor2, attrPath2, + fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attr), + nextPrefix + (last ? treeNull : treeLine)); + } + }; + + auto showDerivation = [&]() + { + auto name = visitor.getAttr("name")->getString(); + + /* + std::string description; + + if (auto aMeta = visitor.maybeGetAttr("meta")) { + if (auto aDescription = aMeta->maybeGetAttr("description")) + description = aDescription->getString(); + } + */ + + logger->stdout("%s: %s '%s'", + headerPrefix, + attrPath.size() == 2 && attrPath[0] == "devShell" ? "development environment" : + attrPath.size() == 3 && attrPath[0] == "checks" ? "derivation" : + attrPath.size() >= 1 && attrPath[0] == "hydraJobs" ? "derivation" : + "package", + name); + }; + + if (attrPath.size() == 0 + || (attrPath.size() == 1 && ( + attrPath[0] == "defaultPackage" + || attrPath[0] == "devShell" + || attrPath[0] == "nixosConfigurations" + || attrPath[0] == "nixosModules")) + || ((attrPath.size() == 1 || attrPath.size() == 2) + && (attrPath[0] == "checks" + || attrPath[0] == "packages")) + ) + { + recurse(); + } + + else if ( + (attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell")) + || (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages")) + ) + { + if (visitor.isDerivation()) + showDerivation(); + else + throw Error("expected a derivation"); + } + + else if (attrPath.size() > 0 && attrPath[0] == "hydraJobs") { + if (visitor.isDerivation()) + showDerivation(); + else + recurse(); + } + + else if (attrPath.size() > 0 && attrPath[0] == "legacyPackages") { + if (attrPath.size() == 1) + recurse(); + else if (!showLegacy) + logger->stdout("%s: " ANSI_YELLOW "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix); + else { + if (visitor.isDerivation()) + showDerivation(); + else if (attrPath.size() <= 2) + // FIXME: handle recurseIntoAttrs + recurse(); + } + } + + else { + logger->stdout("%s: %s", + headerPrefix, + attrPath.size() == 1 && attrPath[0] == "overlay" ? "Nixpkgs overlay" : + attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? "NixOS configuration" : + attrPath.size() == 2 && attrPath[0] == "nixosModules" ? "NixOS module" : + ANSI_YELLOW "unknown" ANSI_NORMAL); + } + } catch (EvalError & e) { + if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages")) + logger->stdout("%s: " ANSI_RED "%s" ANSI_NORMAL, headerPrefix, e.what()); + } + }; + + auto root = std::make_shared(*state, std::nullopt, aOutputs->value); + + visit(*root, {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), ""); + } +}; + struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() @@ -683,6 +895,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command {"init", []() { return make_ref(); }}, {"clone", []() { return make_ref(); }}, {"archive", []() { return make_ref(); }}, + {"show", []() { return make_ref(); }}, }) { } diff --git a/src/nix/search.cc b/src/nix/search.cc index caea25cdc..7f4bd818f 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -29,7 +29,7 @@ std::string hilite(const std::string & s, const std::smatch & m, std::string pos + std::string(m.suffix()); } -struct CmdSearch : SourceExprCommand, MixJSON +struct CmdSearch : InstallableCommand, MixJSON { std::vector res; @@ -79,6 +79,11 @@ struct CmdSearch : SourceExprCommand, MixJSON }; } + Strings getDefaultFlakeAttrPaths() override + { + return {""}; + } + void run(ref store) override { settings.readOnlyMode = true; @@ -93,12 +98,14 @@ struct CmdSearch : SourceExprCommand, MixJSON std::vector regexes; regexes.reserve(res.size()); - for (auto &re : res) { + for (auto & re : res) regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); - } auto state = getEvalState(); + //auto [value, pos] = installable->toValue(*state); + +#if 0 auto jsonOut = json ? std::make_unique(std::cout) : nullptr; auto sToplevel = state->symbols.create("_toplevel"); @@ -270,7 +277,7 @@ struct CmdSearch : SourceExprCommand, MixJSON RunPager pager; for (auto el : results) std::cout << el.second << "\n"; - +#endif } }; -- cgit v1.2.3 From 12b7eefbc5cc80ab0bc2db6c576a4b15574cacd6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 01:02:29 +0200 Subject: nix flake show: Use evaluation cache --- src/nix/flake.cc | 337 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 299 insertions(+), 38 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b94da23f3..3c75befb9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -11,6 +11,7 @@ #include "fetchers.hh" #include "registry.hh" #include "json.hh" +#include "sqlite.hh" #include #include @@ -668,22 +669,203 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun } }; -struct AttrCursor : std::enable_shared_from_this +// FIXME: inefficient representation of attrs / fingerprints +static const char * schema = R"sql( + +create table if not exists Fingerprints ( + fingerprint blob primary key not null, + timestamp integer not null +); + +create table if not exists Attributes ( + fingerprint blob not null, + attrPath text not null, + type integer, + value text, + primary key (fingerprint, attrPath), + foreign key (fingerprint) references Fingerprints(fingerprint) on delete cascade +); +)sql"; + +enum AttrType { + Attrs = 1, + String = 2, +}; + +struct AttrDb +{ + struct State + { + SQLite db; + SQLiteStmt insertFingerprint; + SQLiteStmt insertAttribute; + SQLiteStmt queryAttribute; + std::set fingerprints; + }; + + std::unique_ptr> _state; + + AttrDb() + : _state(std::make_unique>()) + { + auto state(_state->lock()); + + Path dbPath = getCacheDir() + "/nix/eval-cache-v2.sqlite"; + createDirs(dirOf(dbPath)); + + state->db = SQLite(dbPath); + state->db.isCache(); + state->db.exec(schema); + + state->insertFingerprint.create(state->db, + "insert or ignore into Fingerprints(fingerprint, timestamp) values (?, ?)"); + + state->insertAttribute.create(state->db, + "insert or replace into Attributes(fingerprint, attrPath, type, value) values (?, ?, ?, ?)"); + + state->queryAttribute.create(state->db, + "select type, value from Attributes where fingerprint = ? and attrPath = ?"); + } + + void addFingerprint(State & state, const Fingerprint & fingerprint) + { + if (state.fingerprints.insert(fingerprint).second) + // FIXME: update timestamp + state.insertFingerprint.use() + (fingerprint.hash, fingerprint.hashSize) + (time(0)).exec(); + } + + void setAttr( + const Fingerprint & fingerprint, + const std::vector & attrPath, + const std::vector & attrs) + { + auto state(_state->lock()); + + addFingerprint(*state, fingerprint); + + state->insertAttribute.use() + (fingerprint.hash, fingerprint.hashSize) + (concatStringsSep(".", attrPath)) + (AttrType::Attrs) + (concatStringsSep("\n", attrs)).exec(); + } + + void setAttr( + const Fingerprint & fingerprint, + const std::vector & attrPath, + std::string_view s) + { + auto state(_state->lock()); + + addFingerprint(*state, fingerprint); + + state->insertAttribute.use() + (fingerprint.hash, fingerprint.hashSize) + (concatStringsSep(".", attrPath)) + (AttrType::String) + (s).exec(); + } + + typedef std::variant, std::string> AttrValue; + + std::optional getAttr( + const Fingerprint & fingerprint, + const std::vector & attrPath, + SymbolTable & symbols) + { + auto state(_state->lock()); + + addFingerprint(*state, fingerprint); + + auto queryAttribute(state->queryAttribute.use() + (fingerprint.hash, fingerprint.hashSize) + (concatStringsSep(".", attrPath))); + if (!queryAttribute.next()) return {}; + + auto type = (AttrType) queryAttribute.getInt(0); + + if (type == AttrType::Attrs) { + std::vector attrs; + for (auto & s : tokenizeString>(queryAttribute.getStr(1), "\n")) + attrs.push_back(symbols.create(s)); + return attrs; + } else if (type == AttrType::String) { + return queryAttribute.getStr(1); + } else + throw Error("unexpected type in evaluation cache"); + } +}; + +struct AttrCursor; + +struct AttrRoot : std::enable_shared_from_this { + std::shared_ptr db; EvalState & state; - typedef std::optional, std::string>> Parent; - Parent parent; + Fingerprint fingerprint; + typedef std::function RootLoader; + RootLoader rootLoader; RootValue value; + AttrRoot(std::shared_ptr db, EvalState & state, const Fingerprint & fingerprint, RootLoader rootLoader) + : db(db) + , state(state) + , fingerprint(fingerprint) + , rootLoader(rootLoader) + { + } + + Value * getRootValue() + { + if (!value) { + //printError("GET ROOT"); + value = allocRootValue(rootLoader()); + } + return *value; + } + + std::shared_ptr getRoot() + { + return std::make_shared(ref(shared_from_this()), std::nullopt); + } +}; + +struct AttrCursor : std::enable_shared_from_this +{ + ref root; + typedef std::optional, Symbol>> Parent; + Parent parent; + RootValue _value; + AttrCursor( - EvalState & state, + ref root, Parent parent, - Value * value) - : state(state), parent(parent), value(allocRootValue(value)) + Value * value = nullptr) + : root(root), parent(parent) + { + if (value) + _value = allocRootValue(value); + } + + Value & getValue() { + if (!_value) { + if (parent) { + auto & vParent = parent->first->getValue(); + root->state.forceAttrs(vParent); + auto attr = vParent.attrs->get(parent->second); + if (!attr) + throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); + _value = allocRootValue(attr->value); + } else + _value = allocRootValue(root->getRootValue()); + } + return **_value; } - std::vector getAttrPath() const + std::vector getAttrPath() const { if (parent) { auto attrPath = parent->first->getAttrPath(); @@ -693,43 +875,115 @@ struct AttrCursor : std::enable_shared_from_this return {}; } - std::shared_ptr maybeGetAttr(const std::string & name) + std::vector getAttrPath(Symbol name) const + { + auto attrPath = getAttrPath(); + attrPath.push_back(name); + return attrPath; + } + + std::string getAttrPathStr() const + { + return concatStringsSep(".", getAttrPath()); + } + + std::string getAttrPathStr(Symbol name) const + { + return concatStringsSep(".", getAttrPath(name)); + } + + std::shared_ptr maybeGetAttr(Symbol name) { - state.forceValue(**value); + if (root->db) { + auto attr = root->db->getAttr(root->fingerprint, getAttrPath(), root->state.symbols); + if (attr) { + if (auto attrs = std::get_if>(&*attr)) { + for (auto & attr : *attrs) + if (attr == name) + return std::make_shared(root, std::make_pair(shared_from_this(), name)); + } + return nullptr; + } + + attr = root->db->getAttr(root->fingerprint, getAttrPath(name), root->state.symbols); + if (attr) + // FIXME: store *attr + return std::make_shared(root, std::make_pair(shared_from_this(), name)); + } - if ((*value)->type != tAttrs) + //printError("GET ATTR %s", getAttrPathStr(name)); + + root->state.forceValue(getValue()); + + if (getValue().type != tAttrs) return nullptr; - auto attr = (*value)->attrs->get(state.symbols.create(name)); + auto attr = getValue().attrs->get(name); if (!attr) return nullptr; - return std::allocate_shared(traceable_allocator(), state, std::make_pair(shared_from_this(), name), attr->value); + return std::make_shared(root, std::make_pair(shared_from_this(), name), attr->value); + } + + std::shared_ptr maybeGetAttr(std::string_view name) + { + return maybeGetAttr(root->state.symbols.create(name)); } - std::shared_ptr getAttr(const std::string & name) + std::shared_ptr getAttr(Symbol name) { auto p = maybeGetAttr(name); - if (!p) { - auto attrPath = getAttrPath(); - attrPath.push_back(name); - throw Error("attribute '%s' does not exist", concatStringsSep(".", attrPath)); - } + if (!p) + throw Error("attribute '%s' does not exist", getAttrPathStr(name)); return p; } + std::shared_ptr getAttr(std::string_view name) + { + return getAttr(root->state.symbols.create(name)); + } + std::string getString() { - return state.forceString(**value); + if (root->db) { + auto attr = root->db->getAttr(root->fingerprint, getAttrPath(), root->state.symbols); + if (auto s = std::get_if(&*attr)) { + //printError("GOT STRING %s", getAttrPathStr()); + return *s; + } + } + + //printError("GET STRING %s", getAttrPathStr()); + auto s = root->state.forceString(getValue()); + if (root->db) + root->db->setAttr(root->fingerprint, getAttrPath(), s); + return s; } - StringSet getAttrs() + std::vector getAttrs() { - StringSet attrs; - state.forceAttrs(**value); - for (auto & attr : *(*value)->attrs) - attrs.insert(attr.name); + if (root->db) { + auto attr = root->db->getAttr(root->fingerprint, getAttrPath(), root->state.symbols); + if (attr) { + if (auto attrs = std::get_if>(&*attr)) { + //printError("GOT ATTRS %s", getAttrPathStr()); + return std::move(*attrs); + } else + throw Error("unexpected type mismatch in evaluation cache"); + } + } + + //printError("GET ATTRS %s", getAttrPathStr()); + std::vector attrs; + root->state.forceAttrs(getValue()); + for (auto & attr : *getValue().attrs) + attrs.push_back(attr.name); + std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { + return (const string &) a < (const string &) b; + }); + if (root->db) + root->db->setAttr(root->fingerprint, getAttrPath(), attrs); return attrs; } @@ -748,7 +1002,7 @@ struct CmdFlakeShow : FlakeCommand { mkFlag() .longName("legacy") - .description("enumerate the contents of the 'legacyPackages' output") + .description("show the contents of the 'legacyPackages' output") .set(&showLegacy, true); } @@ -762,17 +1016,9 @@ struct CmdFlakeShow : FlakeCommand auto state = getEvalState(); auto flake = lockFlake(); - auto vFlake = state->allocValue(); - flake::callFlake(*state, flake, *vFlake); - - state->forceAttrs(*vFlake); - - auto aOutputs = vFlake->attrs->get(state->symbols.create("outputs")); - assert(aOutputs); + std::function & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; - std::function & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; - - visit = [&](AttrCursor & visitor, const std::vector & attrPath, const std::string & headerPrefix, const std::string & nextPrefix) + visit = [&](AttrCursor & visitor, const std::vector & attrPath, const std::string & headerPrefix, const std::string & nextPrefix) { Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPath))); @@ -794,7 +1040,7 @@ struct CmdFlakeShow : FlakeCommand auto showDerivation = [&]() { - auto name = visitor.getAttr("name")->getString(); + auto name = visitor.getAttr(state->sName)->getString(); /* std::string description; @@ -874,9 +1120,24 @@ struct CmdFlakeShow : FlakeCommand } }; - auto root = std::make_shared(*state, std::nullopt, aOutputs->value); + auto db = std::make_shared(); + + auto root = std::make_shared(db, *state, + flake.getFingerprint(), + [&]() + { + auto vFlake = state->allocValue(); + flake::callFlake(*state, flake, *vFlake); + + state->forceAttrs(*vFlake); + + auto aOutputs = vFlake->attrs->get(state->symbols.create("outputs")); + assert(aOutputs); + + return aOutputs->value; + }); - visit(*root, {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), ""); + visit(*root->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), ""); } }; -- cgit v1.2.3 From a6c4fd044c204cf07d1ed523fe59801d234b7f25 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 01:13:13 +0200 Subject: Hide progress bar on exit --- src/nix/progress-bar.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/progress-bar.cc b/src/nix/progress-bar.cc index 8e7ba95a3..2eccea4ad 100644 --- a/src/nix/progress-bar.cc +++ b/src/nix/progress-bar.cc @@ -113,8 +113,10 @@ public: state->active = false; std::string status = getStatus(*state); writeToStderr("\r\e[K"); + /* if (status != "") writeToStderr("[" + status + "]\n"); + */ updateCV.notify_one(); quitCV.notify_one(); } -- cgit v1.2.3 From 9ea4f93f8833506c1ae18c65e0cd39d84b82298e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 01:21:24 +0200 Subject: nix flake show: Support apps --- src/nix/flake.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3c75befb9..52c8a122b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1065,10 +1065,12 @@ struct CmdFlakeShow : FlakeCommand attrPath[0] == "defaultPackage" || attrPath[0] == "devShell" || attrPath[0] == "nixosConfigurations" - || attrPath[0] == "nixosModules")) + || attrPath[0] == "nixosModules" + || attrPath[0] == "defaultApp")) || ((attrPath.size() == 1 || attrPath.size() == 2) && (attrPath[0] == "checks" - || attrPath[0] == "packages")) + || attrPath[0] == "packages" + || attrPath[0] == "apps")) ) { recurse(); @@ -1106,6 +1108,16 @@ struct CmdFlakeShow : FlakeCommand } } + else if ( + (attrPath.size() == 2 && attrPath[0] == "defaultApp") || + (attrPath.size() == 3 && attrPath[0] == "apps")) + { + auto aType = visitor.maybeGetAttr("type"); + if (!aType || aType->getString() != "app") + throw EvalError("not an app definition"); + logger->stdout("%s: app", headerPrefix); + } + else { logger->stdout("%s: %s", headerPrefix, -- cgit v1.2.3 From aa34c0ef512367fdc923c9fa0ec41345a4535fd0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 13:57:02 +0200 Subject: nix flake show: Speed up eval cache bigly In the fully cached case for the 'nixpkgs' flake, it went from 101s to 4.6s. Populating the cache went from 132s to 17.4s (which could probably be improved further by combining INSERTs). --- src/nix/flake.cc | 116 ++++++++++++++++++++++--------------------------------- 1 file changed, 47 insertions(+), 69 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 52c8a122b..753e9e29a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -669,21 +669,12 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun } }; -// FIXME: inefficient representation of attrs / fingerprints +// FIXME: inefficient representation of attrs static const char * schema = R"sql( - -create table if not exists Fingerprints ( - fingerprint blob primary key not null, - timestamp integer not null -); - create table if not exists Attributes ( - fingerprint blob not null, - attrPath text not null, - type integer, - value text, - primary key (fingerprint, attrPath), - foreign key (fingerprint) references Fingerprints(fingerprint) on delete cascade + attrPath text primary key, + type integer not null, + value text not null ); )sql"; @@ -697,72 +688,52 @@ struct AttrDb struct State { SQLite db; - SQLiteStmt insertFingerprint; SQLiteStmt insertAttribute; SQLiteStmt queryAttribute; - std::set fingerprints; }; std::unique_ptr> _state; - AttrDb() + AttrDb(const Fingerprint & fingerprint) : _state(std::make_unique>()) { auto state(_state->lock()); - Path dbPath = getCacheDir() + "/nix/eval-cache-v2.sqlite"; - createDirs(dirOf(dbPath)); + Path cacheDir = getCacheDir() + "/nix/eval-cache-v2"; + createDirs(cacheDir); + + Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; state->db = SQLite(dbPath); state->db.isCache(); state->db.exec(schema); - state->insertFingerprint.create(state->db, - "insert or ignore into Fingerprints(fingerprint, timestamp) values (?, ?)"); - state->insertAttribute.create(state->db, - "insert or replace into Attributes(fingerprint, attrPath, type, value) values (?, ?, ?, ?)"); + "insert or replace into Attributes(attrPath, type, value) values (?, ?, ?)"); state->queryAttribute.create(state->db, - "select type, value from Attributes where fingerprint = ? and attrPath = ?"); - } - - void addFingerprint(State & state, const Fingerprint & fingerprint) - { - if (state.fingerprints.insert(fingerprint).second) - // FIXME: update timestamp - state.insertFingerprint.use() - (fingerprint.hash, fingerprint.hashSize) - (time(0)).exec(); + "select type, value from Attributes where attrPath = ?"); } void setAttr( - const Fingerprint & fingerprint, const std::vector & attrPath, const std::vector & attrs) { auto state(_state->lock()); - addFingerprint(*state, fingerprint); - state->insertAttribute.use() - (fingerprint.hash, fingerprint.hashSize) (concatStringsSep(".", attrPath)) (AttrType::Attrs) (concatStringsSep("\n", attrs)).exec(); } void setAttr( - const Fingerprint & fingerprint, const std::vector & attrPath, std::string_view s) { auto state(_state->lock()); - addFingerprint(*state, fingerprint); - state->insertAttribute.use() - (fingerprint.hash, fingerprint.hashSize) (concatStringsSep(".", attrPath)) (AttrType::String) (s).exec(); @@ -771,16 +742,12 @@ struct AttrDb typedef std::variant, std::string> AttrValue; std::optional getAttr( - const Fingerprint & fingerprint, const std::vector & attrPath, SymbolTable & symbols) { auto state(_state->lock()); - addFingerprint(*state, fingerprint); - auto queryAttribute(state->queryAttribute.use() - (fingerprint.hash, fingerprint.hashSize) (concatStringsSep(".", attrPath))); if (!queryAttribute.next()) return {}; @@ -804,15 +771,13 @@ struct AttrRoot : std::enable_shared_from_this { std::shared_ptr db; EvalState & state; - Fingerprint fingerprint; typedef std::function RootLoader; RootLoader rootLoader; RootValue value; - AttrRoot(std::shared_ptr db, EvalState & state, const Fingerprint & fingerprint, RootLoader rootLoader) + AttrRoot(std::shared_ptr db, EvalState & state, RootLoader rootLoader) : db(db) , state(state) - , fingerprint(fingerprint) , rootLoader(rootLoader) { } @@ -838,12 +803,14 @@ struct AttrCursor : std::enable_shared_from_this typedef std::optional, Symbol>> Parent; Parent parent; RootValue _value; + std::optional cachedValue; AttrCursor( ref root, Parent parent, - Value * value = nullptr) - : root(root), parent(parent) + Value * value = nullptr, + std::optional && cachedValue = {}) + : root(root), parent(parent), cachedValue(std::move(cachedValue)) { if (value) _value = allocRootValue(value); @@ -895,9 +862,11 @@ struct AttrCursor : std::enable_shared_from_this std::shared_ptr maybeGetAttr(Symbol name) { if (root->db) { - auto attr = root->db->getAttr(root->fingerprint, getAttrPath(), root->state.symbols); - if (attr) { - if (auto attrs = std::get_if>(&*attr)) { + if (!cachedValue) + cachedValue = root->db->getAttr(getAttrPath(), root->state.symbols); + + if (cachedValue) { + if (auto attrs = std::get_if>(&*cachedValue)) { for (auto & attr : *attrs) if (attr == name) return std::make_shared(root, std::make_pair(shared_from_this(), name)); @@ -905,10 +874,9 @@ struct AttrCursor : std::enable_shared_from_this return nullptr; } - attr = root->db->getAttr(root->fingerprint, getAttrPath(name), root->state.symbols); + auto attr = root->db->getAttr(getAttrPath(name), root->state.symbols); if (attr) - // FIXME: store *attr - return std::make_shared(root, std::make_pair(shared_from_this(), name)); + return std::make_shared(root, std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); } //printError("GET ATTR %s", getAttrPathStr(name)); @@ -947,28 +915,36 @@ struct AttrCursor : std::enable_shared_from_this std::string getString() { if (root->db) { - auto attr = root->db->getAttr(root->fingerprint, getAttrPath(), root->state.symbols); - if (auto s = std::get_if(&*attr)) { - //printError("GOT STRING %s", getAttrPathStr()); - return *s; + if (!cachedValue) + cachedValue = root->db->getAttr(getAttrPath(), root->state.symbols); + if (cachedValue) { + if (auto s = std::get_if(&*cachedValue)) { + //printError("GOT STRING %s", getAttrPathStr()); + return *s; + } else + throw Error("unexpected type mismatch in evaluation cache"); } } //printError("GET STRING %s", getAttrPathStr()); auto s = root->state.forceString(getValue()); - if (root->db) - root->db->setAttr(root->fingerprint, getAttrPath(), s); + if (root->db) { + root->db->setAttr(getAttrPath(), s); + cachedValue = s; + } + return s; } std::vector getAttrs() { if (root->db) { - auto attr = root->db->getAttr(root->fingerprint, getAttrPath(), root->state.symbols); - if (attr) { - if (auto attrs = std::get_if>(&*attr)) { + if (!cachedValue) + cachedValue = root->db->getAttr(getAttrPath(), root->state.symbols); + if (cachedValue) { + if (auto attrs = std::get_if>(&*cachedValue)) { //printError("GOT ATTRS %s", getAttrPathStr()); - return std::move(*attrs); + return *attrs; } else throw Error("unexpected type mismatch in evaluation cache"); } @@ -982,8 +958,11 @@ struct AttrCursor : std::enable_shared_from_this std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { return (const string &) a < (const string &) b; }); - if (root->db) - root->db->setAttr(root->fingerprint, getAttrPath(), attrs); + if (root->db) { + root->db->setAttr(getAttrPath(), attrs); + cachedValue = attrs; + } + return attrs; } @@ -1132,10 +1111,9 @@ struct CmdFlakeShow : FlakeCommand } }; - auto db = std::make_shared(); + auto db = std::make_shared(flake.getFingerprint()); auto root = std::make_shared(db, *state, - flake.getFingerprint(), [&]() { auto vFlake = state->allocValue(); -- cgit v1.2.3 From bdb32266079f13f687790426dcbe1941c6a959f0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 14:30:04 +0200 Subject: Add flag to disable the eval cache --- src/nix/flake.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 753e9e29a..c78e6f2f1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -976,6 +976,7 @@ struct AttrCursor : std::enable_shared_from_this struct CmdFlakeShow : FlakeCommand { bool showLegacy = false; + bool useEvalCache = true; CmdFlakeShow() { @@ -983,6 +984,11 @@ struct CmdFlakeShow : FlakeCommand .longName("legacy") .description("show the contents of the 'legacyPackages' output") .set(&showLegacy, true); + + mkFlag() + .longName("no-eval-cache") + .description("do not use the flake evaluation cache") + .handler([&]() { useEvalCache = false; }); } std::string description() override @@ -1111,7 +1117,7 @@ struct CmdFlakeShow : FlakeCommand } }; - auto db = std::make_shared(flake.getFingerprint()); + auto db = useEvalCache ? std::make_shared(flake.getFingerprint()) : nullptr; auto root = std::make_shared(db, *state, [&]() -- cgit v1.2.3 From aaa109565e4fb662e423f23bc48c9ad9831dd281 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 23:04:21 +0200 Subject: Use a more space/time-efficient representation for the eval cache --- src/nix/flake.cc | 141 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 49 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index c78e6f2f1..5370841ec 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -672,13 +672,17 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun // FIXME: inefficient representation of attrs static const char * schema = R"sql( create table if not exists Attributes ( - attrPath text primary key, + parent integer not null, + name text, type integer not null, - value text not null + value text, + primary key (parent, name) ); )sql"; enum AttrType { + Placeholder = 0, + // FIXME: distinguish between full / partial attrsets Attrs = 1, String = 2, }; @@ -690,8 +694,14 @@ struct AttrDb SQLite db; SQLiteStmt insertAttribute; SQLiteStmt queryAttribute; + SQLiteStmt queryAttributes; }; + struct placeholder_t {}; + typedef uint64_t AttrId; + typedef std::pair AttrKey; + typedef std::variant, std::string, placeholder_t> AttrValue; + std::unique_ptr> _state; AttrDb(const Fingerprint & fingerprint) @@ -709,57 +719,78 @@ struct AttrDb state->db.exec(schema); state->insertAttribute.create(state->db, - "insert or replace into Attributes(attrPath, type, value) values (?, ?, ?)"); + "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)"); state->queryAttribute.create(state->db, - "select type, value from Attributes where attrPath = ?"); + "select rowid, type, value from Attributes where parent = ? and name = ?"); + + state->queryAttributes.create(state->db, + "select name from Attributes where parent = ?"); } - void setAttr( - const std::vector & attrPath, + AttrId setAttr( + AttrKey key, const std::vector & attrs) { auto state(_state->lock()); state->insertAttribute.use() - (concatStringsSep(".", attrPath)) + (key.first) + (key.second) (AttrType::Attrs) - (concatStringsSep("\n", attrs)).exec(); + (0, false).exec(); + + AttrId rowId = state->db.getLastInsertedRowId(); + assert(rowId); + + for (auto & attr : attrs) + state->insertAttribute.use() + (rowId) + (attr) + (AttrType::Placeholder) + (0, false).exec(); + + return rowId; } - void setAttr( - const std::vector & attrPath, + AttrId setAttr( + AttrKey key, std::string_view s) { auto state(_state->lock()); state->insertAttribute.use() - (concatStringsSep(".", attrPath)) + (key.first) + (key.second) (AttrType::String) (s).exec(); - } - typedef std::variant, std::string> AttrValue; + return state->db.getLastInsertedRowId(); + } - std::optional getAttr( - const std::vector & attrPath, + std::optional> getAttr( + AttrKey key, SymbolTable & symbols) { auto state(_state->lock()); - auto queryAttribute(state->queryAttribute.use() - (concatStringsSep(".", attrPath))); + auto queryAttribute(state->queryAttribute.use()(key.first)(key.second)); if (!queryAttribute.next()) return {}; - auto type = (AttrType) queryAttribute.getInt(0); + auto rowId = (AttrType) queryAttribute.getInt(0); + auto type = (AttrType) queryAttribute.getInt(1); - if (type == AttrType::Attrs) { + if (type == AttrType::Placeholder) + return {{rowId, placeholder_t()}}; + else if (type == AttrType::Attrs) { + // FIXME: expensive, should separate this out. std::vector attrs; - for (auto & s : tokenizeString>(queryAttribute.getStr(1), "\n")) - attrs.push_back(symbols.create(s)); - return attrs; + auto queryAttributes(state->queryAttributes.use()(rowId)); + while (queryAttributes.next()) + attrs.push_back(symbols.create(queryAttributes.getStr(0))); + return {{rowId, attrs}}; } else if (type == AttrType::String) { - return queryAttribute.getStr(1); + return {{rowId, queryAttribute.getStr(2)}}; } else throw Error("unexpected type in evaluation cache"); } @@ -803,19 +834,31 @@ struct AttrCursor : std::enable_shared_from_this typedef std::optional, Symbol>> Parent; Parent parent; RootValue _value; - std::optional cachedValue; + std::optional> cachedValue; AttrCursor( ref root, Parent parent, Value * value = nullptr, - std::optional && cachedValue = {}) + std::optional> && cachedValue = {}) : root(root), parent(parent), cachedValue(std::move(cachedValue)) { if (value) _value = allocRootValue(value); } + AttrDb::AttrKey getAttrKey() + { + if (!parent) + return {0, root->state.sEpsilon}; + if (!parent->first->cachedValue) { + parent->first->cachedValue = root->db->getAttr( + parent->first->getAttrKey(), root->state.symbols); + assert(parent->first->cachedValue); + } + return {parent->first->cachedValue->first, parent->second}; + } + Value & getValue() { if (!_value) { @@ -863,20 +906,24 @@ struct AttrCursor : std::enable_shared_from_this { if (root->db) { if (!cachedValue) - cachedValue = root->db->getAttr(getAttrPath(), root->state.symbols); + cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); if (cachedValue) { - if (auto attrs = std::get_if>(&*cachedValue)) { + if (auto attrs = std::get_if>(&cachedValue->second)) { for (auto & attr : *attrs) if (attr == name) return std::make_shared(root, std::make_pair(shared_from_this(), name)); - } - return nullptr; + return nullptr; + } else if (std::get_if(&cachedValue->second)) { + auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols); + if (attr) + return std::make_shared(root, std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); + // Incomplete attrset, so need to fall thru and + // evaluate to see whether 'name' exists + } else + // FIXME: throw error? + return nullptr; } - - auto attr = root->db->getAttr(getAttrPath(name), root->state.symbols); - if (attr) - return std::make_shared(root, std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); } //printError("GET ATTR %s", getAttrPathStr(name)); @@ -916,22 +963,20 @@ struct AttrCursor : std::enable_shared_from_this { if (root->db) { if (!cachedValue) - cachedValue = root->db->getAttr(getAttrPath(), root->state.symbols); - if (cachedValue) { - if (auto s = std::get_if(&*cachedValue)) { + cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); + if (cachedValue && !std::get_if(&cachedValue->second)) { + if (auto s = std::get_if(&cachedValue->second)) { //printError("GOT STRING %s", getAttrPathStr()); return *s; } else - throw Error("unexpected type mismatch in evaluation cache"); + throw Error("unexpected type mismatch in evaluation cache (string expected)"); } } //printError("GET STRING %s", getAttrPathStr()); auto s = root->state.forceString(getValue()); - if (root->db) { - root->db->setAttr(getAttrPath(), s); - cachedValue = s; - } + if (root->db) + cachedValue = {root->db->setAttr(getAttrKey(), s), s}; return s; } @@ -940,13 +985,13 @@ struct AttrCursor : std::enable_shared_from_this { if (root->db) { if (!cachedValue) - cachedValue = root->db->getAttr(getAttrPath(), root->state.symbols); - if (cachedValue) { - if (auto attrs = std::get_if>(&*cachedValue)) { + cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); + if (cachedValue && !std::get_if(&cachedValue->second)) { + if (auto attrs = std::get_if>(&cachedValue->second)) { //printError("GOT ATTRS %s", getAttrPathStr()); return *attrs; } else - throw Error("unexpected type mismatch in evaluation cache"); + throw Error("unexpected type mismatch in evaluation cache (attrs expected)"); } } @@ -958,10 +1003,8 @@ struct AttrCursor : std::enable_shared_from_this std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { return (const string &) a < (const string &) b; }); - if (root->db) { - root->db->setAttr(getAttrPath(), attrs); - cachedValue = attrs; - } + if (root->db) + cachedValue = {root->db->setAttr(getAttrKey(), attrs), attrs}; return attrs; } -- cgit v1.2.3 From 69cb9f7eeef967ed80a6e75388e488361310f115 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 17 Apr 2020 23:15:34 +0200 Subject: Wrap eval cache creation in a giant transaction This speeds up the creation of the cache for the nixpkgs flake from 21.2s to 10.2s. Oddly, it also speeds up querying the cache (i.e. running 'nix flake show nixpkgs/nixos-20.03 --legacy') from 4.2s to 3.4s. (For comparison, running with --no-eval-cache takes 9.5s, so the overhead of building the SQLite cache is only 0.7s.) --- src/nix/flake.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5370841ec..d311d331c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -695,6 +695,7 @@ struct AttrDb SQLiteStmt insertAttribute; SQLiteStmt queryAttribute; SQLiteStmt queryAttributes; + std::unique_ptr txn; }; struct placeholder_t {}; @@ -726,6 +727,19 @@ struct AttrDb state->queryAttributes.create(state->db, "select name from Attributes where parent = ?"); + + state->txn = std::make_unique(state->db); + } + + ~AttrDb() + { + try { + auto state(_state->lock()); + state->txn->commit(); + state->txn.reset(); + } catch (...) { + ignoreException(); + } } AttrId setAttr( -- cgit v1.2.3 From 3738bcb05e7ed32a39fbd78cef45d1996e8fb484 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 18 Apr 2020 15:12:31 +0200 Subject: Eval cache: Don't replace real attributes with placeholders --- src/nix/flake.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d311d331c..15a903daf 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -693,6 +693,7 @@ struct AttrDb { SQLite db; SQLiteStmt insertAttribute; + SQLiteStmt insertPlaceholder; SQLiteStmt queryAttribute; SQLiteStmt queryAttributes; std::unique_ptr txn; @@ -722,6 +723,9 @@ struct AttrDb state->insertAttribute.create(state->db, "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)"); + state->insertPlaceholder.create(state->db, + fmt("insert or ignore into Attributes(parent, name, type) values (?, ?, %d)", Placeholder)); + state->queryAttribute.create(state->db, "select rowid, type, value from Attributes where parent = ? and name = ?"); @@ -758,11 +762,9 @@ struct AttrDb assert(rowId); for (auto & attr : attrs) - state->insertAttribute.use() + state->insertPlaceholder.use() (rowId) - (attr) - (AttrType::Placeholder) - (0, false).exec(); + (attr).exec(); return rowId; } -- cgit v1.2.3 From 0725ab2fd7d0d8b6606bb21fd00a2b0624bb7623 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 19 Apr 2020 23:07:06 +0200 Subject: Store more stuff in the evaluation cache In particular, we store whether an attribute failed to evaluate (threw an exception) or was an unsupported type. This is to ensure that a repeated 'nix flake show' never has to evaluate anything, so it can execute without fetching the flake. With this, 'nix flake show nixpkgs/nixos-20.03 --legacy' executes in 0.6s (was 3.4s). --- src/nix/flake.cc | 189 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 152 insertions(+), 37 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 15a903daf..9e46cc55a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -682,9 +682,11 @@ create table if not exists Attributes ( enum AttrType { Placeholder = 0, - // FIXME: distinguish between full / partial attrsets - Attrs = 1, + FullAttrs = 1, String = 2, + Missing = 3, + Misc = 4, + Failed = 5, }; struct AttrDb @@ -693,16 +695,18 @@ struct AttrDb { SQLite db; SQLiteStmt insertAttribute; - SQLiteStmt insertPlaceholder; SQLiteStmt queryAttribute; SQLiteStmt queryAttributes; std::unique_ptr txn; }; struct placeholder_t {}; + struct missing_t {}; + struct misc_t {}; + struct failed_t {}; typedef uint64_t AttrId; typedef std::pair AttrKey; - typedef std::variant, std::string, placeholder_t> AttrValue; + typedef std::variant, std::string, placeholder_t, missing_t, misc_t, failed_t> AttrValue; std::unique_ptr> _state; @@ -723,9 +727,6 @@ struct AttrDb state->insertAttribute.create(state->db, "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)"); - state->insertPlaceholder.create(state->db, - fmt("insert or ignore into Attributes(parent, name, type) values (?, ?, %d)", Placeholder)); - state->queryAttribute.create(state->db, "select rowid, type, value from Attributes where parent = ? and name = ?"); @@ -746,7 +747,7 @@ struct AttrDb } } - AttrId setAttr( + AttrId setAttrs( AttrKey key, const std::vector & attrs) { @@ -755,21 +756,23 @@ struct AttrDb state->insertAttribute.use() (key.first) (key.second) - (AttrType::Attrs) + (AttrType::FullAttrs) (0, false).exec(); AttrId rowId = state->db.getLastInsertedRowId(); assert(rowId); for (auto & attr : attrs) - state->insertPlaceholder.use() + state->insertAttribute.use() (rowId) - (attr).exec(); + (attr) + (AttrType::Placeholder) + (0, false).exec(); return rowId; } - AttrId setAttr( + AttrId setString( AttrKey key, std::string_view s) { @@ -784,6 +787,58 @@ struct AttrDb return state->db.getLastInsertedRowId(); } + AttrId setPlaceholder(AttrKey key) + { + auto state(_state->lock()); + + state->insertAttribute.use() + (key.first) + (key.second) + (AttrType::Placeholder) + (0, false).exec(); + + return state->db.getLastInsertedRowId(); + } + + AttrId setMissing(AttrKey key) + { + auto state(_state->lock()); + + state->insertAttribute.use() + (key.first) + (key.second) + (AttrType::Missing) + (0, false).exec(); + + return state->db.getLastInsertedRowId(); + } + + AttrId setMisc(AttrKey key) + { + auto state(_state->lock()); + + state->insertAttribute.use() + (key.first) + (key.second) + (AttrType::Misc) + (0, false).exec(); + + return state->db.getLastInsertedRowId(); + } + + AttrId setFailed(AttrKey key) + { + auto state(_state->lock()); + + state->insertAttribute.use() + (key.first) + (key.second) + (AttrType::Failed) + (0, false).exec(); + + return state->db.getLastInsertedRowId(); + } + std::optional> getAttr( AttrKey key, SymbolTable & symbols) @@ -798,7 +853,7 @@ struct AttrDb if (type == AttrType::Placeholder) return {{rowId, placeholder_t()}}; - else if (type == AttrType::Attrs) { + else if (type == AttrType::FullAttrs) { // FIXME: expensive, should separate this out. std::vector attrs; auto queryAttributes(state->queryAttributes.use()(rowId)); @@ -807,6 +862,12 @@ struct AttrDb return {{rowId, attrs}}; } else if (type == AttrType::String) { return {{rowId, queryAttribute.getStr(2)}}; + } else if (type == AttrType::Missing) { + return {{rowId, missing_t()}}; + } else if (type == AttrType::Misc) { + return {{rowId, misc_t()}}; + } else if (type == AttrType::Failed) { + return {{rowId, failed_t()}}; } else throw Error("unexpected type in evaluation cache"); } @@ -832,7 +893,7 @@ struct AttrRoot : std::enable_shared_from_this Value * getRootValue() { if (!value) { - //printError("GET ROOT"); + debug("getting root value"); value = allocRootValue(rootLoader()); } return *value; @@ -918,6 +979,33 @@ struct AttrCursor : std::enable_shared_from_this return concatStringsSep(".", getAttrPath(name)); } + Value & forceValue() + { + debug("evaluating uncached attribute %s", getAttrPathStr()); + + auto & v = getValue(); + + try { + root->state.forceValue(v); + } catch (EvalError &) { + debug("setting '%s' to failed", getAttrPathStr()); + if (root->db) + cachedValue = {root->db->setFailed(getAttrKey()), AttrDb::failed_t()}; + throw; + } + + if (root->db && (!cachedValue || std::get_if(&cachedValue->second))) { + if (v.type == tString) + cachedValue = {root->db->setString(getAttrKey(), v.string.s), v.string.s}; + else if (v.type == tAttrs) + ; // FIXME: do something? + else + cachedValue = {root->db->setMisc(getAttrKey()), AttrDb::misc_t()}; + } + + return v; + } + std::shared_ptr maybeGetAttr(Symbol name) { if (root->db) { @@ -932,29 +1020,47 @@ struct AttrCursor : std::enable_shared_from_this return nullptr; } else if (std::get_if(&cachedValue->second)) { auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols); - if (attr) - return std::make_shared(root, std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); + if (attr) { + if (std::get_if(&attr->second)) + return nullptr; + else if (std::get_if(&attr->second)) + throw EvalError("cached failure of attribute '%s'", getAttrPathStr(name)); + else + return std::make_shared(root, + std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); + } // Incomplete attrset, so need to fall thru and // evaluate to see whether 'name' exists } else - // FIXME: throw error? return nullptr; + //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); } } - //printError("GET ATTR %s", getAttrPathStr(name)); + auto & v = forceValue(); - root->state.forceValue(getValue()); - - if (getValue().type != tAttrs) + if (v.type != tAttrs) return nullptr; + //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); - auto attr = getValue().attrs->get(name); + auto attr = v.attrs->get(name); - if (!attr) + if (!attr) { + if (root->db) { + assert(cachedValue); + root->db->setMissing({cachedValue->first, name}); + } return nullptr; + } - return std::make_shared(root, std::make_pair(shared_from_this(), name), attr->value); + std::optional> cachedValue2; + if (root->db) { + assert(cachedValue); + cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), AttrDb::placeholder_t()}; + } + + return std::make_shared( + root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); } std::shared_ptr maybeGetAttr(std::string_view name) @@ -982,19 +1088,19 @@ struct AttrCursor : std::enable_shared_from_this cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); if (cachedValue && !std::get_if(&cachedValue->second)) { if (auto s = std::get_if(&cachedValue->second)) { - //printError("GOT STRING %s", getAttrPathStr()); + debug("using cached string attribute '%s'", getAttrPathStr()); return *s; } else - throw Error("unexpected type mismatch in evaluation cache (string expected)"); + throw TypeError("'%s' is not a string", getAttrPathStr()); } } - //printError("GET STRING %s", getAttrPathStr()); - auto s = root->state.forceString(getValue()); - if (root->db) - cachedValue = {root->db->setAttr(getAttrKey(), s), s}; + auto & v = forceValue(); + + if (v.type != tString) + throw TypeError("'%s' is not a string", getAttrPathStr()); - return s; + return v.string.s; } std::vector getAttrs() @@ -1004,23 +1110,27 @@ struct AttrCursor : std::enable_shared_from_this cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); if (cachedValue && !std::get_if(&cachedValue->second)) { if (auto attrs = std::get_if>(&cachedValue->second)) { - //printError("GOT ATTRS %s", getAttrPathStr()); + debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - throw Error("unexpected type mismatch in evaluation cache (attrs expected)"); + throw TypeError("'%s' is not an attribute set", getAttrPathStr()); } } - //printError("GET ATTRS %s", getAttrPathStr()); + auto & v = forceValue(); + + if (v.type != tAttrs) + throw TypeError("'%s' is not an attribute set", getAttrPathStr()); + std::vector attrs; - root->state.forceAttrs(getValue()); for (auto & attr : *getValue().attrs) attrs.push_back(attr.name); std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { return (const string &) a < (const string &) b; }); + if (root->db) - cachedValue = {root->db->setAttr(getAttrKey(), attrs), attrs}; + cachedValue = {root->db->setAttrs(getAttrKey(), attrs), attrs}; return attrs; } @@ -1172,7 +1282,7 @@ struct CmdFlakeShow : FlakeCommand } } catch (EvalError & e) { if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages")) - logger->stdout("%s: " ANSI_RED "%s" ANSI_NORMAL, headerPrefix, e.what()); + throw; } }; @@ -1181,6 +1291,11 @@ struct CmdFlakeShow : FlakeCommand auto root = std::make_shared(db, *state, [&]() { + /* For testing whether the evaluation cache is + complete. */ + if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0") + throw Error("not everything is cached, but evaluation is not allowed"); + auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); -- cgit v1.2.3 From 539a9c1c5f0e4c9ab2262e7cf48460e02b8b0e12 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Apr 2020 13:13:52 +0200 Subject: Get rid of the old eval cache --- src/nix/flake.cc | 502 +----------------------------------------------- src/nix/installables.cc | 92 +++++---- src/nix/installables.hh | 22 ++- 3 files changed, 78 insertions(+), 538 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9e46cc55a..2f2dd65c7 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -11,7 +11,7 @@ #include "fetchers.hh" #include "registry.hh" #include "json.hh" -#include "sqlite.hh" +#include "flake/eval-cache.hh" #include #include @@ -669,479 +669,6 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun } }; -// FIXME: inefficient representation of attrs -static const char * schema = R"sql( -create table if not exists Attributes ( - parent integer not null, - name text, - type integer not null, - value text, - primary key (parent, name) -); -)sql"; - -enum AttrType { - Placeholder = 0, - FullAttrs = 1, - String = 2, - Missing = 3, - Misc = 4, - Failed = 5, -}; - -struct AttrDb -{ - struct State - { - SQLite db; - SQLiteStmt insertAttribute; - SQLiteStmt queryAttribute; - SQLiteStmt queryAttributes; - std::unique_ptr txn; - }; - - struct placeholder_t {}; - struct missing_t {}; - struct misc_t {}; - struct failed_t {}; - typedef uint64_t AttrId; - typedef std::pair AttrKey; - typedef std::variant, std::string, placeholder_t, missing_t, misc_t, failed_t> AttrValue; - - std::unique_ptr> _state; - - AttrDb(const Fingerprint & fingerprint) - : _state(std::make_unique>()) - { - auto state(_state->lock()); - - Path cacheDir = getCacheDir() + "/nix/eval-cache-v2"; - createDirs(cacheDir); - - Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; - - state->db = SQLite(dbPath); - state->db.isCache(); - state->db.exec(schema); - - state->insertAttribute.create(state->db, - "insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)"); - - state->queryAttribute.create(state->db, - "select rowid, type, value from Attributes where parent = ? and name = ?"); - - state->queryAttributes.create(state->db, - "select name from Attributes where parent = ?"); - - state->txn = std::make_unique(state->db); - } - - ~AttrDb() - { - try { - auto state(_state->lock()); - state->txn->commit(); - state->txn.reset(); - } catch (...) { - ignoreException(); - } - } - - AttrId setAttrs( - AttrKey key, - const std::vector & attrs) - { - auto state(_state->lock()); - - state->insertAttribute.use() - (key.first) - (key.second) - (AttrType::FullAttrs) - (0, false).exec(); - - AttrId rowId = state->db.getLastInsertedRowId(); - assert(rowId); - - for (auto & attr : attrs) - state->insertAttribute.use() - (rowId) - (attr) - (AttrType::Placeholder) - (0, false).exec(); - - return rowId; - } - - AttrId setString( - AttrKey key, - std::string_view s) - { - auto state(_state->lock()); - - state->insertAttribute.use() - (key.first) - (key.second) - (AttrType::String) - (s).exec(); - - return state->db.getLastInsertedRowId(); - } - - AttrId setPlaceholder(AttrKey key) - { - auto state(_state->lock()); - - state->insertAttribute.use() - (key.first) - (key.second) - (AttrType::Placeholder) - (0, false).exec(); - - return state->db.getLastInsertedRowId(); - } - - AttrId setMissing(AttrKey key) - { - auto state(_state->lock()); - - state->insertAttribute.use() - (key.first) - (key.second) - (AttrType::Missing) - (0, false).exec(); - - return state->db.getLastInsertedRowId(); - } - - AttrId setMisc(AttrKey key) - { - auto state(_state->lock()); - - state->insertAttribute.use() - (key.first) - (key.second) - (AttrType::Misc) - (0, false).exec(); - - return state->db.getLastInsertedRowId(); - } - - AttrId setFailed(AttrKey key) - { - auto state(_state->lock()); - - state->insertAttribute.use() - (key.first) - (key.second) - (AttrType::Failed) - (0, false).exec(); - - return state->db.getLastInsertedRowId(); - } - - std::optional> getAttr( - AttrKey key, - SymbolTable & symbols) - { - auto state(_state->lock()); - - auto queryAttribute(state->queryAttribute.use()(key.first)(key.second)); - if (!queryAttribute.next()) return {}; - - auto rowId = (AttrType) queryAttribute.getInt(0); - auto type = (AttrType) queryAttribute.getInt(1); - - if (type == AttrType::Placeholder) - return {{rowId, placeholder_t()}}; - else if (type == AttrType::FullAttrs) { - // FIXME: expensive, should separate this out. - std::vector attrs; - auto queryAttributes(state->queryAttributes.use()(rowId)); - while (queryAttributes.next()) - attrs.push_back(symbols.create(queryAttributes.getStr(0))); - return {{rowId, attrs}}; - } else if (type == AttrType::String) { - return {{rowId, queryAttribute.getStr(2)}}; - } else if (type == AttrType::Missing) { - return {{rowId, missing_t()}}; - } else if (type == AttrType::Misc) { - return {{rowId, misc_t()}}; - } else if (type == AttrType::Failed) { - return {{rowId, failed_t()}}; - } else - throw Error("unexpected type in evaluation cache"); - } -}; - -struct AttrCursor; - -struct AttrRoot : std::enable_shared_from_this -{ - std::shared_ptr db; - EvalState & state; - typedef std::function RootLoader; - RootLoader rootLoader; - RootValue value; - - AttrRoot(std::shared_ptr db, EvalState & state, RootLoader rootLoader) - : db(db) - , state(state) - , rootLoader(rootLoader) - { - } - - Value * getRootValue() - { - if (!value) { - debug("getting root value"); - value = allocRootValue(rootLoader()); - } - return *value; - } - - std::shared_ptr getRoot() - { - return std::make_shared(ref(shared_from_this()), std::nullopt); - } -}; - -struct AttrCursor : std::enable_shared_from_this -{ - ref root; - typedef std::optional, Symbol>> Parent; - Parent parent; - RootValue _value; - std::optional> cachedValue; - - AttrCursor( - ref root, - Parent parent, - Value * value = nullptr, - std::optional> && cachedValue = {}) - : root(root), parent(parent), cachedValue(std::move(cachedValue)) - { - if (value) - _value = allocRootValue(value); - } - - AttrDb::AttrKey getAttrKey() - { - if (!parent) - return {0, root->state.sEpsilon}; - if (!parent->first->cachedValue) { - parent->first->cachedValue = root->db->getAttr( - parent->first->getAttrKey(), root->state.symbols); - assert(parent->first->cachedValue); - } - return {parent->first->cachedValue->first, parent->second}; - } - - Value & getValue() - { - if (!_value) { - if (parent) { - auto & vParent = parent->first->getValue(); - root->state.forceAttrs(vParent); - auto attr = vParent.attrs->get(parent->second); - if (!attr) - throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); - _value = allocRootValue(attr->value); - } else - _value = allocRootValue(root->getRootValue()); - } - return **_value; - } - - std::vector getAttrPath() const - { - if (parent) { - auto attrPath = parent->first->getAttrPath(); - attrPath.push_back(parent->second); - return attrPath; - } else - return {}; - } - - std::vector getAttrPath(Symbol name) const - { - auto attrPath = getAttrPath(); - attrPath.push_back(name); - return attrPath; - } - - std::string getAttrPathStr() const - { - return concatStringsSep(".", getAttrPath()); - } - - std::string getAttrPathStr(Symbol name) const - { - return concatStringsSep(".", getAttrPath(name)); - } - - Value & forceValue() - { - debug("evaluating uncached attribute %s", getAttrPathStr()); - - auto & v = getValue(); - - try { - root->state.forceValue(v); - } catch (EvalError &) { - debug("setting '%s' to failed", getAttrPathStr()); - if (root->db) - cachedValue = {root->db->setFailed(getAttrKey()), AttrDb::failed_t()}; - throw; - } - - if (root->db && (!cachedValue || std::get_if(&cachedValue->second))) { - if (v.type == tString) - cachedValue = {root->db->setString(getAttrKey(), v.string.s), v.string.s}; - else if (v.type == tAttrs) - ; // FIXME: do something? - else - cachedValue = {root->db->setMisc(getAttrKey()), AttrDb::misc_t()}; - } - - return v; - } - - std::shared_ptr maybeGetAttr(Symbol name) - { - if (root->db) { - if (!cachedValue) - cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); - - if (cachedValue) { - if (auto attrs = std::get_if>(&cachedValue->second)) { - for (auto & attr : *attrs) - if (attr == name) - return std::make_shared(root, std::make_pair(shared_from_this(), name)); - return nullptr; - } else if (std::get_if(&cachedValue->second)) { - auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols); - if (attr) { - if (std::get_if(&attr->second)) - return nullptr; - else if (std::get_if(&attr->second)) - throw EvalError("cached failure of attribute '%s'", getAttrPathStr(name)); - else - return std::make_shared(root, - std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); - } - // Incomplete attrset, so need to fall thru and - // evaluate to see whether 'name' exists - } else - return nullptr; - //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); - } - } - - auto & v = forceValue(); - - if (v.type != tAttrs) - return nullptr; - //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); - - auto attr = v.attrs->get(name); - - if (!attr) { - if (root->db) { - assert(cachedValue); - root->db->setMissing({cachedValue->first, name}); - } - return nullptr; - } - - std::optional> cachedValue2; - if (root->db) { - assert(cachedValue); - cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), AttrDb::placeholder_t()}; - } - - return std::make_shared( - root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); - } - - std::shared_ptr maybeGetAttr(std::string_view name) - { - return maybeGetAttr(root->state.symbols.create(name)); - } - - std::shared_ptr getAttr(Symbol name) - { - auto p = maybeGetAttr(name); - if (!p) - throw Error("attribute '%s' does not exist", getAttrPathStr(name)); - return p; - } - - std::shared_ptr getAttr(std::string_view name) - { - return getAttr(root->state.symbols.create(name)); - } - - std::string getString() - { - if (root->db) { - if (!cachedValue) - cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); - if (cachedValue && !std::get_if(&cachedValue->second)) { - if (auto s = std::get_if(&cachedValue->second)) { - debug("using cached string attribute '%s'", getAttrPathStr()); - return *s; - } else - throw TypeError("'%s' is not a string", getAttrPathStr()); - } - } - - auto & v = forceValue(); - - if (v.type != tString) - throw TypeError("'%s' is not a string", getAttrPathStr()); - - return v.string.s; - } - - std::vector getAttrs() - { - if (root->db) { - if (!cachedValue) - cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols); - if (cachedValue && !std::get_if(&cachedValue->second)) { - if (auto attrs = std::get_if>(&cachedValue->second)) { - debug("using cached attrset attribute '%s'", getAttrPathStr()); - return *attrs; - } else - throw TypeError("'%s' is not an attribute set", getAttrPathStr()); - } - } - - auto & v = forceValue(); - - if (v.type != tAttrs) - throw TypeError("'%s' is not an attribute set", getAttrPathStr()); - - std::vector attrs; - for (auto & attr : *getValue().attrs) - attrs.push_back(attr.name); - std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { - return (const string &) a < (const string &) b; - }); - - if (root->db) - cachedValue = {root->db->setAttrs(getAttrKey(), attrs), attrs}; - - return attrs; - } - - bool isDerivation() - { - auto aType = maybeGetAttr("type"); - return aType && aType->getString() == "derivation"; - } -}; - struct CmdFlakeShow : FlakeCommand { bool showLegacy = false; @@ -1170,9 +697,9 @@ struct CmdFlakeShow : FlakeCommand auto state = getEvalState(); auto flake = lockFlake(); - std::function & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; + std::function & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; - visit = [&](AttrCursor & visitor, const std::vector & attrPath, const std::string & headerPrefix, const std::string & nextPrefix) + visit = [&](eval_cache::AttrCursor & visitor, const std::vector & attrPath, const std::string & headerPrefix, const std::string & nextPrefix) { Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPath))); @@ -1286,28 +813,9 @@ struct CmdFlakeShow : FlakeCommand } }; - auto db = useEvalCache ? std::make_shared(flake.getFingerprint()) : nullptr; - - auto root = std::make_shared(db, *state, - [&]() - { - /* For testing whether the evaluation cache is - complete. */ - if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0") - throw Error("not everything is cached, but evaluation is not allowed"); - - auto vFlake = state->allocValue(); - flake::callFlake(*state, flake, *vFlake); - - state->forceAttrs(*vFlake); - - auto aOutputs = vFlake->attrs->get(state->symbols.create("outputs")); - assert(aOutputs); - - return aOutputs->value; - }); + auto cache = openEvalCache(*state, flake, useEvalCache); - visit(*root->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), ""); + visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), ""); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index b8c75aaaf..160fde589 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -162,7 +162,7 @@ struct InstallableStorePath : Installable } }; -std::vector InstallableValue::toDerivations() +std::vector InstallableValue::toDerivations() { auto state = cmd.getEvalState(); @@ -173,7 +173,7 @@ std::vector InstallableValue::toDerivations() DrvInfos drvInfos; getDerivations(*state, *v, "", autoArgs, drvInfos, false); - std::vector res; + std::vector res; for (auto & drvInfo : drvInfos) { res.push_back({ state->store->parseStorePath(drvInfo.queryDrvPath()), @@ -283,58 +283,76 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked return aOutputs->value; } -std::tuple InstallableFlake::toDerivation() +ref openEvalCache( + EvalState & state, + const flake::LockedFlake & lockedFlake, + bool useEvalCache) +{ + return ref(std::make_shared( + useEvalCache, + lockedFlake.getFingerprint(), + state, + [&]() + { + /* For testing whether the evaluation cache is + complete. */ + if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0") + throw Error("not everything is cached, but evaluation is not allowed"); + + auto vFlake = state.allocValue(); + flake::callFlake(state, lockedFlake, *vFlake); + + state.forceAttrs(*vFlake); + + auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + assert(aOutputs); + + return aOutputs->value; + })); +} + +std::tuple InstallableFlake::toDerivation() { auto state = cmd.getEvalState(); auto lockedFlake = lockFlake(*state, flakeRef, cmd.lockFlags); - Value * vOutputs = nullptr; - - auto emptyArgs = state->allocBindings(0); - - auto & evalCache = flake::EvalCache::singleton(); - - auto fingerprint = lockedFlake.getFingerprint(); + auto cache = openEvalCache(*state, lockedFlake, true); + auto root = cache->getRoot(); for (auto & attrPath : getActualAttrPaths()) { - auto drv = evalCache.getDerivation(fingerprint, attrPath); - if (drv) { - if (state->store->isValidPath(drv->drvPath)) - return {attrPath, lockedFlake.flake.lockedRef, std::move(*drv)}; + auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); + if (!attr) continue; + + if (!attr->isDerivation()) + throw Error("flake output attribute '%s' is not a derivation", attrPath); + + auto aDrvPath = attr->getAttr(state->sDrvPath); + auto drvPath = state->store->parseStorePath(aDrvPath->getString()); + if (!state->store->isValidPath(drvPath)) { + /* The eval cache contains 'drvPath', but the actual path + has been garbage-collected. So force it to be + regenerated. */ + aDrvPath->forceValue(); + assert(state->store->isValidPath(drvPath)); } - if (!vOutputs) - vOutputs = getFlakeOutputs(*state, lockedFlake); - - try { - auto * v = findAlongAttrPath(*state, attrPath, *emptyArgs, *vOutputs).first; - state->forceValue(*v); - - auto drvInfo = getDerivation(*state, *v, false); - if (!drvInfo) - throw Error("flake output attribute '%s' is not a derivation", attrPath); - - auto drv = flake::EvalCache::Derivation{ - state->store->parseStorePath(drvInfo->queryDrvPath()), - state->store->parseStorePath(drvInfo->queryOutPath()), - drvInfo->queryOutputName() - }; - - evalCache.addDerivation(fingerprint, attrPath, drv); + auto drvInfo = DerivationInfo{ + std::move(drvPath), + state->store->parseStorePath(attr->getAttr(state->sOutPath)->getString()), + attr->getAttr(state->sOutputName)->getString() + }; - return {attrPath, lockedFlake.flake.lockedRef, std::move(drv)}; - } catch (AttrPathNotFound & e) { - } + return {attrPath, lockedFlake.flake.lockedRef, std::move(drvInfo)}; } throw Error("flake '%s' does not provide attribute %s", flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); } -std::vector InstallableFlake::toDerivations() +std::vector InstallableFlake::toDerivations() { - std::vector res; + std::vector res; res.push_back(std::get<2>(toDerivation())); return res; } diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 8f2d50077..b258fb336 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -3,7 +3,7 @@ #include "util.hh" #include "path.hh" #include "eval.hh" -#include "flake/eval-cache.hh" +#include "flake/flake.hh" #include @@ -12,6 +12,8 @@ namespace nix { struct DrvInfo; struct SourceExprCommand; +namespace eval_cache { class EvalCache; } + struct Buildable { std::optional drvPath; @@ -63,7 +65,14 @@ struct InstallableValue : Installable InstallableValue(SourceExprCommand & cmd) : cmd(cmd) { } - virtual std::vector toDerivations(); + struct DerivationInfo + { + StorePath drvPath; + StorePath outPath; + std::string outputName; + }; + + virtual std::vector toDerivations(); Buildables toBuildables() override; }; @@ -86,11 +95,16 @@ struct InstallableFlake : InstallableValue Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); - std::tuple toDerivation(); + std::tuple toDerivation(); - std::vector toDerivations() override; + std::vector toDerivations() override; std::pair toValue(EvalState & state) override; }; +ref openEvalCache( + EvalState & state, + const flake::LockedFlake & lockedFlake, + bool useEvalCache); + } -- cgit v1.2.3 From 42a12f9232c8e3373df7f4660b2dfd837db92e4b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Apr 2020 13:14:59 +0200 Subject: Move eval-cache.{cc,hh} --- src/nix/flake.cc | 2 +- src/nix/installables.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2f2dd65c7..d316cda36 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -11,7 +11,7 @@ #include "fetchers.hh" #include "registry.hh" #include "json.hh" -#include "flake/eval-cache.hh" +#include "eval-cache.hh" #include #include diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 160fde589..38977b4d6 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -9,7 +9,7 @@ #include "store-api.hh" #include "shared.hh" #include "flake/flake.hh" -#include "flake/eval-cache.hh" +#include "eval-cache.hh" #include "url.hh" #include -- cgit v1.2.3 From b69323f8c93d5b067b6baaa0acbb93f800fcd9bd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 Apr 2020 15:27:09 +0200 Subject: Revive 'nix search' It uses the evaluation cache now rather than the ad hoc JSON cache. --- src/nix/flake.cc | 4 +- src/nix/installables.cc | 41 +++++++-- src/nix/installables.hh | 10 ++- src/nix/search.cc | 229 ++++++++++++++---------------------------------- 4 files changed, 109 insertions(+), 175 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d316cda36..c09ba3610 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -695,7 +695,7 @@ struct CmdFlakeShow : FlakeCommand void run(nix::ref store) override { auto state = getEvalState(); - auto flake = lockFlake(); + auto flake = std::make_shared(lockFlake()); std::function & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit; @@ -815,7 +815,7 @@ struct CmdFlakeShow : FlakeCommand auto cache = openEvalCache(*state, flake, useEvalCache); - visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), ""); + visit(*cache->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake->flake.lockedRef), ""); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 38977b4d6..0c2c5fe63 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -133,6 +133,15 @@ App Installable::toApp(EvalState & state) return App(state, *toValue(state).first); } +std::vector, std::string>> +Installable::getCursor(EvalState & state, bool useEvalCache) +{ + auto evalCache = + std::make_shared(false, Hash(), state, + [&]() { return toValue(state).first; }); + return {{evalCache->getRoot(), ""}}; +} + struct InstallableStorePath : Installable { ref store; @@ -285,14 +294,14 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked ref openEvalCache( EvalState & state, - const flake::LockedFlake & lockedFlake, + std::shared_ptr lockedFlake, bool useEvalCache) { return ref(std::make_shared( useEvalCache, - lockedFlake.getFingerprint(), + lockedFlake->getFingerprint(), state, - [&]() + [&state, lockedFlake]() { /* For testing whether the evaluation cache is complete. */ @@ -300,7 +309,7 @@ ref openEvalCache( throw Error("not everything is cached, but evaluation is not allowed"); auto vFlake = state.allocValue(); - flake::callFlake(state, lockedFlake, *vFlake); + flake::callFlake(state, *lockedFlake, *vFlake); state.forceAttrs(*vFlake); @@ -315,7 +324,8 @@ std::tuple InstallableF { auto state = cmd.getEvalState(); - auto lockedFlake = lockFlake(*state, flakeRef, cmd.lockFlags); + auto lockedFlake = std::make_shared( + lockFlake(*state, flakeRef, cmd.lockFlags)); auto cache = openEvalCache(*state, lockedFlake, true); auto root = cache->getRoot(); @@ -343,7 +353,7 @@ std::tuple InstallableF attr->getAttr(state->sOutputName)->getString() }; - return {attrPath, lockedFlake.flake.lockedRef, std::move(drvInfo)}; + return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)}; } throw Error("flake '%s' does not provide attribute %s", @@ -378,6 +388,25 @@ std::pair InstallableFlake::toValue(EvalState & state) flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); } +std::vector, std::string>> +InstallableFlake::getCursor(EvalState & state, bool useEvalCache) +{ + auto evalCache = openEvalCache(state, + std::make_shared(lockFlake(state, flakeRef, cmd.lockFlags)), + useEvalCache); + + auto root = evalCache->getRoot(); + + std::vector, std::string>> res; + + for (auto & attrPath : getActualAttrPaths()) { + auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); + if (attr) res.push_back({attr, attrPath}); + } + + return res; +} + // FIXME: extend std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex)); diff --git a/src/nix/installables.hh b/src/nix/installables.hh index b258fb336..c9e277a51 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -12,7 +12,7 @@ namespace nix { struct DrvInfo; struct SourceExprCommand; -namespace eval_cache { class EvalCache; } +namespace eval_cache { class EvalCache; class AttrCursor; } struct Buildable { @@ -57,6 +57,9 @@ struct Installable { return {}; } + + virtual std::vector, std::string>> + getCursor(EvalState & state, bool useEvalCache); }; struct InstallableValue : Installable @@ -100,11 +103,14 @@ struct InstallableFlake : InstallableValue std::vector toDerivations() override; std::pair toValue(EvalState & state) override; + + std::vector, std::string>> + getCursor(EvalState & state, bool useEvalCache) override; }; ref openEvalCache( EvalState & state, - const flake::LockedFlake & lockedFlake, + std::shared_ptr lockedFlake, bool useEvalCache); } diff --git a/src/nix/search.cc b/src/nix/search.cc index 7f4bd818f..9c11af490 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -6,8 +6,9 @@ #include "get-drvs.hh" #include "common-args.hh" #include "json.hh" -#include "json-to-value.hh" #include "shared.hh" +#include "eval-cache.hh" +#include "attr-path.hh" #include #include @@ -25,7 +26,7 @@ std::string hilite(const std::string & s, const std::smatch & m, std::string pos m.empty() ? s : std::string(m.prefix()) - + ANSI_RED + std::string(m.str()) + postfix + + ANSI_GREEN + std::string(m.str()) + postfix + std::string(m.suffix()); } @@ -33,23 +34,9 @@ struct CmdSearch : InstallableCommand, MixJSON { std::vector res; - bool writeCache = true; - bool useCache = true; - CmdSearch() { expectArgs("regex", &res); - - mkFlag() - .longName("update-cache") - .shortName('u') - .description("update the package search cache") - .handler([&]() { writeCache = true; useCache = false; }); - - mkFlag() - .longName("no-cache") - .description("do not use or update the package search cache") - .handler([&]() { writeCache = false; useCache = false; }); } std::string description() override @@ -61,27 +48,30 @@ struct CmdSearch : InstallableCommand, MixJSON { return { Example{ - "To show all available packages:", + "To show all packages in the flake in the current directory:", "nix search" }, Example{ - "To show any packages containing 'blender' in its name or description:", - "nix search blender" + "To show packages in the 'nixpkgs' flake containing 'blender' in its name or description:", + "nix search nixpkgs blender" }, Example{ "To search for Firefox or Chromium:", - "nix search 'firefox|chromium'" + "nix search nixpkgs 'firefox|chromium'" }, Example{ - "To search for git and frontend or gui:", - "nix search git 'frontend|gui'" + "To search for packages containing 'git' and either 'frontend' or 'gui':", + "nix search nixpkgs git 'frontend|gui'" } }; } Strings getDefaultFlakeAttrPaths() override { - return {""}; + return { + "packages." + settings.thisSystem.get() + ".", + "legacyPackages." + settings.thisSystem.get() + "." + }; } void run(ref store) override @@ -91,9 +81,8 @@ struct CmdSearch : InstallableCommand, MixJSON // Empty search string should match all packages // Use "^" here instead of ".*" due to differences in resulting highlighting // (see #1893 -- libc++ claims empty search string is not in POSIX grammar) - if (res.empty()) { + if (res.empty()) res.push_back("^"); - } std::vector regexes; regexes.reserve(res.size()); @@ -103,181 +92,91 @@ struct CmdSearch : InstallableCommand, MixJSON auto state = getEvalState(); - //auto [value, pos] = installable->toValue(*state); - -#if 0 auto jsonOut = json ? std::make_unique(std::cout) : nullptr; - auto sToplevel = state->symbols.create("_toplevel"); - auto sRecurse = state->symbols.create("recurseForDerivations"); + uint64_t results = 0; - bool fromCache = false; - - std::map results; - - std::function doExpr; - - doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) { - debug("at attribute '%s'", attrPath); + std::function & attrPath)> visit; + visit = [&](eval_cache::AttrCursor & cursor, const std::vector & attrPath) + { + Activity act(*logger, lvlInfo, actUnknown, + fmt("evaluating '%s'", concatStringsSep(".", attrPath))); try { - uint found = 0; + auto recurse = [&]() + { + for (const auto & attr : cursor.getAttrs()) { + auto cursor2 = cursor.getAttr(attr); + auto attrPath2(attrPath); + attrPath2.push_back(attr); + visit(*cursor2, attrPath2); + } + }; - state->forceValue(*v); + if (cursor.isDerivation()) { + size_t found = 0; - if (v->type == tLambda && toplevel) { - Value * v2 = state->allocValue(); - state->autoCallFunction(*state->allocBindings(1), *v, *v2); - v = v2; - state->forceValue(*v); - } + DrvName name(cursor.getAttr("name")->getString()); - if (state->isDerivation(*v)) { + auto aMeta = cursor.maybeGetAttr("meta"); + auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr; + auto description = aDescription ? aDescription->getString() : ""; + std::replace(description.begin(), description.end(), '\n', ' '); + auto attrPath2 = concatStringsSep(".", attrPath); - DrvInfo drv(*state, attrPath, v->attrs); - std::string description; std::smatch attrPathMatch; std::smatch descriptionMatch; std::smatch nameMatch; - std::string name; - - DrvName parsed(drv.queryName()); - for (auto ®ex : regexes) { - std::regex_search(attrPath, attrPathMatch, regex); - - name = parsed.name; - std::regex_search(name, nameMatch, regex); - - description = drv.queryMetaString("description"); - std::replace(description.begin(), description.end(), '\n', ' '); + for (auto & regex : regexes) { + std::regex_search(attrPath2, attrPathMatch, regex); + std::regex_search(name.name, nameMatch, regex); std::regex_search(description, descriptionMatch, regex); - if (!attrPathMatch.empty() || !nameMatch.empty() || !descriptionMatch.empty()) - { found++; - } } if (found == res.size()) { + results++; if (json) { - - auto jsonElem = jsonOut->object(attrPath); - - jsonElem.attr("pkgName", parsed.name); - jsonElem.attr("version", parsed.version); + auto jsonElem = jsonOut->object(attrPath2); + jsonElem.attr("pkgName", name.name); + jsonElem.attr("version", name.version); jsonElem.attr("description", description); - } else { - auto name = hilite(parsed.name, nameMatch, "\e[0;2m") - + std::string(parsed.fullName, parsed.name.length()); - results[attrPath] = fmt( - "* %s (%s)\n %s\n", - wrap("\e[0;1m", hilite(attrPath, attrPathMatch, "\e[0;1m")), - wrap("\e[0;2m", hilite(name, nameMatch, "\e[0;2m")), - hilite(description, descriptionMatch, ANSI_NORMAL)); - } - } - - if (cache) { - cache->attr("type", "derivation"); - cache->attr("name", drv.queryName()); - cache->attr("system", drv.querySystem()); - if (description != "") { - auto meta(cache->object("meta")); - meta.attr("description", description); + auto name2 = hilite(name.name, nameMatch, "\e[0;2m") + + std::string(name.fullName, name.name.length()); + if (results > 1) logger->stdout(""); + logger->stdout( + "* %s (%s)", + wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")), + wrap("\e[0;2m", hilite(name2, nameMatch, "\e[0;2m"))); + if (description != "") + logger->stdout( + " %s", hilite(description, descriptionMatch, ANSI_NORMAL)); } } } - else if (v->type == tAttrs) { + else if ( + attrPath.size() == 0 + || (attrPath[0] == "legacyPackages" && attrPath.size() <= 2) + || (attrPath[0] == "packages" && attrPath.size() <= 2)) + recurse(); - if (!toplevel) { - auto attrs = v->attrs; - Bindings::iterator j = attrs->find(sRecurse); - if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) { - debug("skip attribute '%s'", attrPath); - return; - } - } - - bool toplevel2 = false; - if (!fromCache) { - Bindings::iterator j = v->attrs->find(sToplevel); - toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos); - } - - for (auto & i : *v->attrs) { - auto cache2 = - cache ? std::make_unique(cache->object(i.name)) : nullptr; - doExpr(i.value, - attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name, - toplevel2 || fromCache, cache2 ? cache2.get() : nullptr); - } - } - - } catch (AssertionError & e) { - } catch (Error & e) { - if (!toplevel) { - e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath)); + } catch (EvalError & e) { + if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages")) throw; - } } }; - Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json"; - - if (useCache && pathExists(jsonCacheFileName)) { - - warn("using cached results; pass '-u' to update the cache"); - - Value vRoot; - parseJSON(*state, readFile(jsonCacheFileName), vRoot); - - fromCache = true; - - doExpr(&vRoot, "", true, nullptr); - } + for (auto & [cursor, prefix] : installable->getCursor(*state, true)) + visit(*cursor, parseAttrPath(*state, prefix)); - else { - createDirs(dirOf(jsonCacheFileName)); - - Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid()); - - std::ofstream jsonCacheFile; - - try { - // iostream considered harmful - jsonCacheFile.exceptions(std::ofstream::failbit); - jsonCacheFile.open(tmpFile); - - auto cache = writeCache ? std::make_unique(jsonCacheFile, false) : nullptr; - - // FIXME - throw Error("NOT IMPLEMENTED"); - //doExpr(getSourceExpr(*state), "", true, cache.get()); - - } catch (std::exception &) { - /* Fun fact: catching std::ios::failure does not work - due to C++11 ABI shenanigans. - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 */ - if (!jsonCacheFile) - throw Error("error writing to %s", tmpFile); - throw; - } - - if (writeCache && rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1) - throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName); - } - - if (results.size() == 0) + if (!results) throw Error("no results for the given search term(s)!"); - - RunPager pager; - for (auto el : results) std::cout << el.second << "\n"; -#endif } }; -- cgit v1.2.3 From 0469795978599d84bc47226f373e178332fc0aaa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 24 Apr 2020 14:42:17 +0200 Subject: nix search: Show version --- src/nix/search.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/nix') diff --git a/src/nix/search.cc b/src/nix/search.cc index 9c11af490..586bff5e5 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -142,17 +142,16 @@ struct CmdSearch : InstallableCommand, MixJSON results++; if (json) { auto jsonElem = jsonOut->object(attrPath2); - jsonElem.attr("pkgName", name.name); + jsonElem.attr("pname", name.name); jsonElem.attr("version", name.version); jsonElem.attr("description", description); } else { - auto name2 = hilite(name.name, nameMatch, "\e[0;2m") - + std::string(name.fullName, name.name.length()); + auto name2 = hilite(name.name, nameMatch, "\e[0;2m"); if (results > 1) logger->stdout(""); logger->stdout( - "* %s (%s)", + "* %s%s", wrap("\e[0;1m", hilite(attrPath2, attrPathMatch, "\e[0;1m")), - wrap("\e[0;2m", hilite(name2, nameMatch, "\e[0;2m"))); + name.version != "" ? " (" + name.version + ")" : ""); if (description != "") logger->stdout( " %s", hilite(description, descriptionMatch, ANSI_NORMAL)); -- cgit v1.2.3 From b4e23dcd9e0a52644517f8a7777e3c2e6b92481a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 27 Apr 2020 16:29:26 +0200 Subject: nix search: Search legacyPackages recursively --- src/nix/search.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nix') diff --git a/src/nix/search.cc b/src/nix/search.cc index 586bff5e5..fca49350a 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -165,6 +165,12 @@ struct CmdSearch : InstallableCommand, MixJSON || (attrPath[0] == "packages" && attrPath.size() <= 2)) recurse(); + else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) { + auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations); + if (attr && attr->getBool()) + recurse(); + } + } catch (EvalError & e) { if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages")) throw; -- cgit v1.2.3 From b51dff431c1e7d37cec97e0fe7c8945547dfd7f0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 27 Apr 2020 18:55:20 +0200 Subject: Improve error message when an argument is not a flake --- src/nix/installables.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 0c2c5fe63..3871536e1 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -468,7 +468,10 @@ std::vector> SourceExprCommand::parseInstallables( if (s.find('/') != std::string::npos && (storePath = follow(s))) result.push_back(std::make_shared(store, store->printStorePath(*storePath))); else - throw Error("unrecognized argument '%s'", s); + throw Error( + pathExists(s) + ? "path '%s' is not a flake or a store path" + : "don't know how to handle argument '%s'", s); } } } -- cgit v1.2.3 From 829dcb35d52443636d7c6df0d54270b9995bc71a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 27 Apr 2020 22:52:49 +0200 Subject: flake-template.nix: Add defaultPackage --- src/nix/flake-template.nix | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix index 60d7171f3..195aef2cc 100644 --- a/src/nix/flake-template.nix +++ b/src/nix/flake-template.nix @@ -5,5 +5,7 @@ packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; + defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello; + }; } -- cgit v1.2.3 From 6521c92ce8289a5f9e959c6789ab24dacdad082e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 27 Apr 2020 22:53:11 +0200 Subject: Improve path:// handling In particular, doing 'nix build /path/to/dir' now works if /path/to/dir is not a Git tree (it only has to contain a flake.nix file). Also, 'nix flake init' no longer requires a Git tree (but it will do a 'git add flake.nix' if it's a Git tree) --- src/nix/flake.cc | 11 ++++++----- src/nix/installables.cc | 47 +++++++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 25 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index c09ba3610..c47d51fe4 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -551,17 +551,18 @@ struct CmdFlakeInit : virtual Args, Command { Path flakeDir = absPath("."); - if (!pathExists(flakeDir + "/.git")) - throw Error("the directory '%s' is not a Git repository", flakeDir); - Path flakePath = flakeDir + "/flake.nix"; if (pathExists(flakePath)) throw Error("file '%s' already exists", flakePath); writeFile(flakePath, -#include "flake-template.nix.gen.hh" - ); + #include "flake-template.nix.gen.hh" + ); + + if (pathExists(flakeDir + "/.git")) + runProgram("git", true, + { "-C", flakeDir, "add", "--intent-to-add", "flake.nix" }); } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 3871536e1..6b9e2ee96 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -438,14 +438,6 @@ std::vector> SourceExprCommand::parseInstallables( } else { - auto follow = [&](const std::string & s) -> std::optional { - try { - return store->followLinksToStorePath(s); - } catch (NotInStore &) { - return {}; - } - }; - for (auto & s : ss) { if (hasPrefix(s, "nixpkgs.")) { bool static warned; @@ -456,23 +448,38 @@ std::vector> SourceExprCommand::parseInstallables( } else { - auto res = maybeParseFlakeRefWithFragment(s, absPath(".")); - if (res) { - auto &[flakeRef, fragment] = *res; + std::exception_ptr ex; + + try { + auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); result.push_back(std::make_shared( *this, std::move(flakeRef), fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, getDefaultFlakeAttrPathPrefixes())); - } else { - std::optional storePath; - if (s.find('/') != std::string::npos && (storePath = follow(s))) - result.push_back(std::make_shared(store, store->printStorePath(*storePath))); - else - throw Error( - pathExists(s) - ? "path '%s' is not a flake or a store path" - : "don't know how to handle argument '%s'", s); + continue; + } catch (...) { + ex = std::current_exception(); } + + if (s.find('/') != std::string::npos) { + try { + result.push_back(std::make_shared(store, store->printStorePath(store->followLinksToStorePath(s)))); + continue; + } catch (NotInStore &) { + } catch (...) { + if (!ex) + ex = std::current_exception(); + } + } + + std::rethrow_exception(ex); + + /* + throw Error( + pathExists(s) + ? "path '%s' is not a flake or a store path" + : "don't know how to handle argument '%s'", s); + */ } } } -- cgit v1.2.3 From 70bcd6a55ccf583858342c2a1be6efe30167759c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Apr 2020 15:42:53 +0200 Subject: Evaluation cache: Don't barf in read-only mode Fixes $ nix copy warning: Git tree '/home/eelco/Dev/nix-flake' is dirty nix: src/nix/installables.cc:348: std::tuple, std::allocator >, nix::FlakeRef, nix::InstallableValue::DerivationInfo> nix::InstallableFlake::toDerivation(): Assertion `state->store->isValidPath(drvPath)' failed. Aborted (core dumped) --- src/nix/installables.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 6b9e2ee96..c2e2a6573 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -339,12 +339,14 @@ std::tuple InstallableF auto aDrvPath = attr->getAttr(state->sDrvPath); auto drvPath = state->store->parseStorePath(aDrvPath->getString()); - if (!state->store->isValidPath(drvPath)) { + if (!state->store->isValidPath(drvPath) && !settings.readOnlyMode) { /* The eval cache contains 'drvPath', but the actual path has been garbage-collected. So force it to be regenerated. */ aDrvPath->forceValue(); - assert(state->store->isValidPath(drvPath)); + if (!state->store->isValidPath(drvPath)) + throw Error("don't know how to recreate store derivation '%s'!", + state->store->printStorePath(drvPath)); } auto drvInfo = DerivationInfo{ -- cgit v1.2.3 From 9c4e05766bb7f3776cfc5eb1a3da358419a65406 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Apr 2020 15:50:59 +0200 Subject: nix copy: Move --from / --to check This means you now get an error message *before* stuff gets built: $ nix copy .#hydraJobs.vendoredCrates error: you must pass '--from' and/or '--to' Try 'nix --help' for more information. --- src/nix/copy.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 85c777d38..66993ac21 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -80,11 +80,16 @@ struct CmdCopy : StorePathsCommand return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri); } - void run(ref srcStore, StorePaths storePaths) override + void run(ref store) override { if (srcUri.empty() && dstUri.empty()) throw UsageError("you must pass '--from' and/or '--to'"); + StorePathsCommand::run(store); + } + + void run(ref srcStore, StorePaths storePaths) override + { ref dstStore = dstUri.empty() ? openStore() : openStore(dstUri); copyPaths(srcStore, dstStore, storePathsToSet(storePaths), -- cgit v1.2.3 From 9570036146b9bdbd66ce0b9f71479d0f56f3bf35 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 29 Apr 2020 15:51:45 +0200 Subject: nix copy: Build derivations Fixes $ nix copy .#hydraJobs.vendoredCrates --to /tmp/nix error: path '/nix/store/...' is not valid --- src/nix/copy.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nix') diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 66993ac21..23323a36e 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -40,6 +40,8 @@ struct CmdCopy : StorePathsCommand .shortName('s') .description("whether to try substitutes on the destination store (only supported by SSH)") .set(&substitute, Substitute); + + realiseMode = Build; } std::string description() override -- cgit v1.2.3 From 5d8504b9789ffebabe8226227c4061dd48354177 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Wed, 29 Apr 2020 14:02:37 -0600 Subject: rename nix run to nix shell and nix app to nix run --- src/nix/dev-shell.cc | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nix/run.cc | 22 ++-- src/nix/shell.cc | 332 --------------------------------------------------- 3 files changed, 343 insertions(+), 343 deletions(-) create mode 100644 src/nix/dev-shell.cc delete mode 100644 src/nix/shell.cc (limited to 'src/nix') diff --git a/src/nix/dev-shell.cc b/src/nix/dev-shell.cc new file mode 100644 index 000000000..b0710906b --- /dev/null +++ b/src/nix/dev-shell.cc @@ -0,0 +1,332 @@ +#include "eval.hh" +#include "command.hh" +#include "common-args.hh" +#include "shared.hh" +#include "store-api.hh" +#include "derivations.hh" +#include "affinity.hh" +#include "progress-bar.hh" + +#include + +using namespace nix; + +struct Var +{ + bool exported; + std::string value; // quoted string or array +}; + +struct BuildEnvironment +{ + std::map env; + std::string bashFunctions; +}; + +BuildEnvironment readEnvironment(const Path & path) +{ + BuildEnvironment res; + + std::set exported; + + debug("reading environment file '%s'", path); + + auto file = readFile(path); + + auto pos = file.cbegin(); + + static std::string varNameRegex = + R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re"; + + static std::regex declareRegex( + "^declare -x (" + varNameRegex + ")" + + R"re((?:="((?:[^"\\]|\\.)*)")?\n)re"); + + static std::string simpleStringRegex = + R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re"; + + static std::string quotedStringRegex = + R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re"; + + static std::string arrayRegex = + R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re"; + + static std::regex varRegex( + "^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + arrayRegex + ")\n"); + + static std::regex functionRegex( + "^" + varNameRegex + " \\(\\) *\n"); + + while (pos != file.end()) { + + std::smatch match; + + if (std::regex_search(pos, file.cend(), match, declareRegex)) { + pos = match[0].second; + exported.insert(match[1]); + } + + else if (std::regex_search(pos, file.cend(), match, varRegex)) { + pos = match[0].second; + res.env.insert({match[1], Var { (bool) exported.count(match[1]), match[2] }}); + } + + else if (std::regex_search(pos, file.cend(), match, functionRegex)) { + res.bashFunctions = std::string(pos, file.cend()); + break; + } + + else throw Error("shell environment '%s' has unexpected line '%s'", + path, file.substr(pos - file.cbegin(), 60)); + } + + return res; +} + +/* Given an existing derivation, return the shell environment as + initialised by stdenv's setup script. We do this by building a + modified derivation with the same dependencies and nearly the same + initial environment variables, that just writes the resulting + environment to a file and exits. */ +StorePath getDerivationEnvironment(ref store, Derivation drv) +{ + auto builder = baseNameOf(drv.builder); + if (builder != "bash") + throw Error("'nix dev-shell' only works on derivations that use 'bash' as their builder"); + + drv.args = { + "-c", + "set -e; " + "export IN_NIX_SHELL=impure; " + "export dontAddDisableDepTrack=1; " + "if [[ -n $stdenv ]]; then " + " source $stdenv/setup; " + "fi; " + "export > $out; " + "set >> $out "}; + + /* Remove derivation checks. */ + drv.env.erase("allowedReferences"); + drv.env.erase("allowedRequisites"); + drv.env.erase("disallowedReferences"); + drv.env.erase("disallowedRequisites"); + + // FIXME: handle structured attrs + + /* Rehash and write the derivation. FIXME: would be nice to use + 'buildDerivation', but that's privileged. */ + auto drvName = drv.env["name"] + "-env"; + for (auto & output : drv.outputs) + drv.env.erase(output.first); + drv.env["out"] = ""; + drv.env["outputs"] = "out"; + Hash h = hashDerivationModulo(*store, drv, true); + auto shellOutPath = store->makeOutputPath("out", h, drvName); + drv.outputs.insert_or_assign("out", DerivationOutput(shellOutPath.clone(), "", "")); + drv.env["out"] = store->printStorePath(shellOutPath); + auto shellDrvPath2 = writeDerivation(store, drv, drvName); + + /* Build the derivation. */ + store->buildPaths({shellDrvPath2}); + + assert(store->isValidPath(shellOutPath)); + + return shellOutPath; +} + +struct Common : InstallableCommand, MixProfile +{ + std::set ignoreVars{ + "BASHOPTS", + "EUID", + "HOME", // FIXME: don't ignore in pure mode? + "NIX_BUILD_TOP", + "NIX_ENFORCE_PURITY", + "NIX_LOG_FD", + "PPID", + "PWD", + "SHELLOPTS", + "SHLVL", + "SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt + "TEMP", + "TEMPDIR", + "TERM", + "TMP", + "TMPDIR", + "TZ", + "UID", + }; + + void makeRcScript(const BuildEnvironment & buildEnvironment, std::ostream & out) + { + out << "nix_saved_PATH=\"$PATH\"\n"; + + for (auto & i : buildEnvironment.env) { + if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) { + out << fmt("%s=%s\n", i.first, i.second.value); + if (i.second.exported) + out << fmt("export %s\n", i.first); + } + } + + out << "PATH=\"$PATH:$nix_saved_PATH\"\n"; + + out << buildEnvironment.bashFunctions << "\n"; + + // FIXME: set outputs + + out << "export NIX_BUILD_TOP=\"$(mktemp -d --tmpdir nix-shell.XXXXXX)\"\n"; + for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"}) + out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i); + + out << "eval \"$shellHook\"\n"; + } + + Strings getDefaultFlakeAttrPaths() override + { + return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()}; + } + + StorePath getShellOutPath(ref store) + { + auto path = installable->getStorePath(); + if (path && hasSuffix(path->to_string(), "-env")) + return path->clone(); + else { + auto drvs = toDerivations(store, {installable}); + + if (drvs.size() != 1) + throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", + installable->what(), drvs.size()); + + auto & drvPath = *drvs.begin(); + + return getDerivationEnvironment(store, store->derivationFromPath(drvPath)); + } + } + + std::pair getBuildEnvironment(ref store) + { + auto shellOutPath = getShellOutPath(store); + + auto strPath = store->printStorePath(shellOutPath); + + updateProfile(shellOutPath); + + return {readEnvironment(strPath), strPath}; + } +}; + +struct CmdDevShell : Common, MixEnvironment +{ + std::vector command; + + CmdDevShell() + { + mkFlag() + .longName("command") + .shortName('c') + .description("command and arguments to be executed insted of an interactive shell") + .labels({"command", "args"}) + .arity(ArityAny) + .handler([&](std::vector ss) { + if (ss.empty()) throw UsageError("--command requires at least one argument"); + command = ss; + }); + } + + std::string description() override + { + return "run a bash shell that provides the build environment of a derivation"; + } + + Examples examples() override + { + return { + Example{ + "To get the build environment of GNU hello:", + "nix dev-shell nixpkgs#hello" + }, + Example{ + "To get the build environment of the default package of flake in the current directory:", + "nix dev-shell" + }, + Example{ + "To store the build environment in a profile:", + "nix dev-shell --profile /tmp/my-shell nixpkgs#hello" + }, + Example{ + "To use a build environment previously recorded in a profile:", + "nix dev-shell /tmp/my-shell" + }, + }; + } + + void run(ref store) override + { + auto [buildEnvironment, gcroot] = getBuildEnvironment(store); + + auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); + + std::ostringstream ss; + makeRcScript(buildEnvironment, ss); + + ss << fmt("rm -f '%s'\n", rcFilePath); + + if (!command.empty()) { + std::vector args; + for (auto s : command) + args.push_back(shellEscape(s)); + ss << fmt("exec %s\n", concatStringsSep(" ", args)); + } + + writeFull(rcFileFd.get(), ss.str()); + + stopProgressBar(); + + auto shell = getEnv("SHELL").value_or("bash"); + + setEnviron(); + // prevent garbage collection until shell exits + setenv("NIX_GCROOT", gcroot.data(), 1); + + auto args = Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; + + restoreAffinity(); + restoreSignals(); + + execvp(shell.c_str(), stringsToCharPtrs(args).data()); + + throw SysError("executing shell '%s'", shell); + } +}; + +struct CmdPrintDevEnv : Common +{ + std::string description() override + { + return "print shell code that can be sourced by bash to reproduce the build environment of a derivation"; + } + + Examples examples() override + { + return { + Example{ + "To apply the build environment of GNU hello to the current shell:", + ". <(nix print-dev-env nixpkgs#hello)" + }, + }; + } + + void run(ref store) override + { + auto buildEnvironment = getBuildEnvironment(store).first; + + stopProgressBar(); + + makeRcScript(buildEnvironment, std::cout); + } +}; + +static auto r1 = registerCommand("print-dev-env"); +static auto r2 = registerCommand("dev-shell"); diff --git a/src/nix/run.cc b/src/nix/run.cc index 901b87fbb..f14e221e2 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -57,11 +57,11 @@ struct RunCommon : virtual Command } }; -struct CmdRun : InstallablesCommand, RunCommon, MixEnvironment +struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment { std::vector command = { getEnv("SHELL").value_or("bash") }; - CmdRun() + CmdShell() { mkFlag() .longName("command") @@ -85,19 +85,19 @@ struct CmdRun : InstallablesCommand, RunCommon, MixEnvironment return { Example{ "To start a shell providing GNU Hello from NixOS 17.03:", - "nix run -f channel:nixos-17.03 hello" + "nix shell -f channel:nixos-17.03 hello" }, Example{ "To start a shell providing youtube-dl from your 'nixpkgs' channel:", - "nix run nixpkgs#youtube-dl" + "nix shell nixpkgs#youtube-dl" }, Example{ "To run GNU Hello:", - "nix run nixpkgs#hello -c hello --greeting 'Hi everybody!'" + "nix shell nixpkgs#hello -c hello --greeting 'Hi everybody!'" }, Example{ "To run GNU Hello in a chroot store:", - "nix run --store ~/my-nix nixpkgs#hello -c hello" + "nix shell --store ~/my-nix nixpkgs#hello -c hello" }, }; } @@ -141,13 +141,13 @@ struct CmdRun : InstallablesCommand, RunCommon, MixEnvironment } }; -static auto r1 = registerCommand("run"); +static auto r1 = registerCommand("shell"); -struct CmdApp : InstallableCommand, RunCommon +struct CmdRun : InstallableCommand, RunCommon { std::vector args; - CmdApp() + CmdRun() { expectArgs("args", &args); } @@ -162,7 +162,7 @@ struct CmdApp : InstallableCommand, RunCommon return { Example{ "To run Blender:", - "nix app blender-bin" + "nix run blender-bin" }, }; } @@ -192,7 +192,7 @@ struct CmdApp : InstallableCommand, RunCommon } }; -static auto r2 = registerCommand("app"); +static auto r2 = registerCommand("run"); void chrootHelper(int argc, char * * argv) { diff --git a/src/nix/shell.cc b/src/nix/shell.cc deleted file mode 100644 index b0710906b..000000000 --- a/src/nix/shell.cc +++ /dev/null @@ -1,332 +0,0 @@ -#include "eval.hh" -#include "command.hh" -#include "common-args.hh" -#include "shared.hh" -#include "store-api.hh" -#include "derivations.hh" -#include "affinity.hh" -#include "progress-bar.hh" - -#include - -using namespace nix; - -struct Var -{ - bool exported; - std::string value; // quoted string or array -}; - -struct BuildEnvironment -{ - std::map env; - std::string bashFunctions; -}; - -BuildEnvironment readEnvironment(const Path & path) -{ - BuildEnvironment res; - - std::set exported; - - debug("reading environment file '%s'", path); - - auto file = readFile(path); - - auto pos = file.cbegin(); - - static std::string varNameRegex = - R"re((?:[a-zA-Z_][a-zA-Z0-9_]*))re"; - - static std::regex declareRegex( - "^declare -x (" + varNameRegex + ")" + - R"re((?:="((?:[^"\\]|\\.)*)")?\n)re"); - - static std::string simpleStringRegex = - R"re((?:[a-zA-Z0-9_/:\.\-\+=]*))re"; - - static std::string quotedStringRegex = - R"re((?:\$?'(?:[^'\\]|\\[abeEfnrtv\\'"?])*'))re"; - - static std::string arrayRegex = - R"re((?:\(( *\[[^\]]+\]="(?:[^"\\]|\\.)*")*\)))re"; - - static std::regex varRegex( - "^(" + varNameRegex + ")=(" + simpleStringRegex + "|" + quotedStringRegex + "|" + arrayRegex + ")\n"); - - static std::regex functionRegex( - "^" + varNameRegex + " \\(\\) *\n"); - - while (pos != file.end()) { - - std::smatch match; - - if (std::regex_search(pos, file.cend(), match, declareRegex)) { - pos = match[0].second; - exported.insert(match[1]); - } - - else if (std::regex_search(pos, file.cend(), match, varRegex)) { - pos = match[0].second; - res.env.insert({match[1], Var { (bool) exported.count(match[1]), match[2] }}); - } - - else if (std::regex_search(pos, file.cend(), match, functionRegex)) { - res.bashFunctions = std::string(pos, file.cend()); - break; - } - - else throw Error("shell environment '%s' has unexpected line '%s'", - path, file.substr(pos - file.cbegin(), 60)); - } - - return res; -} - -/* Given an existing derivation, return the shell environment as - initialised by stdenv's setup script. We do this by building a - modified derivation with the same dependencies and nearly the same - initial environment variables, that just writes the resulting - environment to a file and exits. */ -StorePath getDerivationEnvironment(ref store, Derivation drv) -{ - auto builder = baseNameOf(drv.builder); - if (builder != "bash") - throw Error("'nix dev-shell' only works on derivations that use 'bash' as their builder"); - - drv.args = { - "-c", - "set -e; " - "export IN_NIX_SHELL=impure; " - "export dontAddDisableDepTrack=1; " - "if [[ -n $stdenv ]]; then " - " source $stdenv/setup; " - "fi; " - "export > $out; " - "set >> $out "}; - - /* Remove derivation checks. */ - drv.env.erase("allowedReferences"); - drv.env.erase("allowedRequisites"); - drv.env.erase("disallowedReferences"); - drv.env.erase("disallowedRequisites"); - - // FIXME: handle structured attrs - - /* Rehash and write the derivation. FIXME: would be nice to use - 'buildDerivation', but that's privileged. */ - auto drvName = drv.env["name"] + "-env"; - for (auto & output : drv.outputs) - drv.env.erase(output.first); - drv.env["out"] = ""; - drv.env["outputs"] = "out"; - Hash h = hashDerivationModulo(*store, drv, true); - auto shellOutPath = store->makeOutputPath("out", h, drvName); - drv.outputs.insert_or_assign("out", DerivationOutput(shellOutPath.clone(), "", "")); - drv.env["out"] = store->printStorePath(shellOutPath); - auto shellDrvPath2 = writeDerivation(store, drv, drvName); - - /* Build the derivation. */ - store->buildPaths({shellDrvPath2}); - - assert(store->isValidPath(shellOutPath)); - - return shellOutPath; -} - -struct Common : InstallableCommand, MixProfile -{ - std::set ignoreVars{ - "BASHOPTS", - "EUID", - "HOME", // FIXME: don't ignore in pure mode? - "NIX_BUILD_TOP", - "NIX_ENFORCE_PURITY", - "NIX_LOG_FD", - "PPID", - "PWD", - "SHELLOPTS", - "SHLVL", - "SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt - "TEMP", - "TEMPDIR", - "TERM", - "TMP", - "TMPDIR", - "TZ", - "UID", - }; - - void makeRcScript(const BuildEnvironment & buildEnvironment, std::ostream & out) - { - out << "nix_saved_PATH=\"$PATH\"\n"; - - for (auto & i : buildEnvironment.env) { - if (!ignoreVars.count(i.first) && !hasPrefix(i.first, "BASH_")) { - out << fmt("%s=%s\n", i.first, i.second.value); - if (i.second.exported) - out << fmt("export %s\n", i.first); - } - } - - out << "PATH=\"$PATH:$nix_saved_PATH\"\n"; - - out << buildEnvironment.bashFunctions << "\n"; - - // FIXME: set outputs - - out << "export NIX_BUILD_TOP=\"$(mktemp -d --tmpdir nix-shell.XXXXXX)\"\n"; - for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"}) - out << fmt("export %s=\"$NIX_BUILD_TOP\"\n", i); - - out << "eval \"$shellHook\"\n"; - } - - Strings getDefaultFlakeAttrPaths() override - { - return {"devShell." + settings.thisSystem.get(), "defaultPackage." + settings.thisSystem.get()}; - } - - StorePath getShellOutPath(ref store) - { - auto path = installable->getStorePath(); - if (path && hasSuffix(path->to_string(), "-env")) - return path->clone(); - else { - auto drvs = toDerivations(store, {installable}); - - if (drvs.size() != 1) - throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", - installable->what(), drvs.size()); - - auto & drvPath = *drvs.begin(); - - return getDerivationEnvironment(store, store->derivationFromPath(drvPath)); - } - } - - std::pair getBuildEnvironment(ref store) - { - auto shellOutPath = getShellOutPath(store); - - auto strPath = store->printStorePath(shellOutPath); - - updateProfile(shellOutPath); - - return {readEnvironment(strPath), strPath}; - } -}; - -struct CmdDevShell : Common, MixEnvironment -{ - std::vector command; - - CmdDevShell() - { - mkFlag() - .longName("command") - .shortName('c') - .description("command and arguments to be executed insted of an interactive shell") - .labels({"command", "args"}) - .arity(ArityAny) - .handler([&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }); - } - - std::string description() override - { - return "run a bash shell that provides the build environment of a derivation"; - } - - Examples examples() override - { - return { - Example{ - "To get the build environment of GNU hello:", - "nix dev-shell nixpkgs#hello" - }, - Example{ - "To get the build environment of the default package of flake in the current directory:", - "nix dev-shell" - }, - Example{ - "To store the build environment in a profile:", - "nix dev-shell --profile /tmp/my-shell nixpkgs#hello" - }, - Example{ - "To use a build environment previously recorded in a profile:", - "nix dev-shell /tmp/my-shell" - }, - }; - } - - void run(ref store) override - { - auto [buildEnvironment, gcroot] = getBuildEnvironment(store); - - auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); - - std::ostringstream ss; - makeRcScript(buildEnvironment, ss); - - ss << fmt("rm -f '%s'\n", rcFilePath); - - if (!command.empty()) { - std::vector args; - for (auto s : command) - args.push_back(shellEscape(s)); - ss << fmt("exec %s\n", concatStringsSep(" ", args)); - } - - writeFull(rcFileFd.get(), ss.str()); - - stopProgressBar(); - - auto shell = getEnv("SHELL").value_or("bash"); - - setEnviron(); - // prevent garbage collection until shell exits - setenv("NIX_GCROOT", gcroot.data(), 1); - - auto args = Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; - - restoreAffinity(); - restoreSignals(); - - execvp(shell.c_str(), stringsToCharPtrs(args).data()); - - throw SysError("executing shell '%s'", shell); - } -}; - -struct CmdPrintDevEnv : Common -{ - std::string description() override - { - return "print shell code that can be sourced by bash to reproduce the build environment of a derivation"; - } - - Examples examples() override - { - return { - Example{ - "To apply the build environment of GNU hello to the current shell:", - ". <(nix print-dev-env nixpkgs#hello)" - }, - }; - } - - void run(ref store) override - { - auto buildEnvironment = getBuildEnvironment(store).first; - - stopProgressBar(); - - makeRcScript(buildEnvironment, std::cout); - } -}; - -static auto r1 = registerCommand("print-dev-env"); -static auto r2 = registerCommand("dev-shell"); -- cgit v1.2.3 From ff394ff206fea222a30d6f68a9d8852170e1ad55 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 May 2020 17:20:23 +0200 Subject: Remove the nixpkgs. compatibility hack Since we've changed a lot of things in the 'nix' command (e.g. rename 'nix run') there is not much point in keeping this around. --- src/nix/installables.cc | 62 +++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 38 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index ab938281f..57060e9b1 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -417,10 +417,6 @@ InstallableFlake::getCursor(EvalState & state, bool useEvalCache) return res; } -// FIXME: extend -std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; -static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex)); - std::vector> SourceExprCommand::parseInstallables( ref store, std::vector ss) { @@ -449,48 +445,38 @@ std::vector> SourceExprCommand::parseInstallables( } else { for (auto & s : ss) { - if (hasPrefix(s, "nixpkgs.")) { - bool static warned; - warnOnce(warned, "the syntax 'nixpkgs.' is deprecated; use 'nixpkgs#' instead"); - result.push_back(std::make_shared(*this, - FlakeRef::fromAttrs({{"type", "indirect"}, {"id", "nixpkgs"}}), - Strings{"legacyPackages." + settings.thisSystem.get() + "." + std::string(s, 8)}, Strings{})); + std::exception_ptr ex; + + try { + auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); + result.push_back(std::make_shared( + *this, std::move(flakeRef), + fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, + getDefaultFlakeAttrPathPrefixes())); + continue; + } catch (...) { + ex = std::current_exception(); } - else { - std::exception_ptr ex; - + if (s.find('/') != std::string::npos) { try { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); - result.push_back(std::make_shared( - *this, std::move(flakeRef), - fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, - getDefaultFlakeAttrPathPrefixes())); + result.push_back(std::make_shared(store, store->printStorePath(store->followLinksToStorePath(s)))); continue; + } catch (NotInStore &) { } catch (...) { - ex = std::current_exception(); - } - - if (s.find('/') != std::string::npos) { - try { - result.push_back(std::make_shared(store, store->printStorePath(store->followLinksToStorePath(s)))); - continue; - } catch (NotInStore &) { - } catch (...) { - if (!ex) - ex = std::current_exception(); - } + if (!ex) + ex = std::current_exception(); } + } - std::rethrow_exception(ex); + std::rethrow_exception(ex); - /* - throw Error( - pathExists(s) - ? "path '%s' is not a flake or a store path" - : "don't know how to handle argument '%s'", s); - */ - } + /* + throw Error( + pathExists(s) + ? "path '%s' is not a flake or a store path" + : "don't know how to handle argument '%s'", s); + */ } } -- cgit v1.2.3 From 14a3a62bfca6c572b9a415cfa80cdbd7ad4326b3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 7 May 2020 12:13:16 +0200 Subject: Update src/nix/search.cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- src/nix/search.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/search.cc b/src/nix/search.cc index fca49350a..bbac56fcb 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -180,7 +180,7 @@ struct CmdSearch : InstallableCommand, MixJSON for (auto & [cursor, prefix] : installable->getCursor(*state, true)) visit(*cursor, parseAttrPath(*state, prefix)); - if (!results) + if (!json && !results) throw Error("no results for the given search term(s)!"); } }; -- cgit v1.2.3 From bf81dd40e91ef7e94582f051412f160114426df4 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Sat, 9 May 2020 10:14:57 -0600 Subject: InstallableExpr unused --- src/nix/installables.cc | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 57060e9b1..16e03875c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -237,23 +237,6 @@ Buildables InstallableValue::toBuildables() return res; } -struct InstallableExpr : InstallableValue -{ - std::string text; - - InstallableExpr(SourceExprCommand & cmd, const std::string & text) - : InstallableValue(cmd), text(text) { } - - std::string what() override { return text; } - - std::pair toValue(EvalState & state) override - { - auto v = state.allocValue(); - state.eval(state.parseExprFromString(text, absPath(".")), *v); - return {v, noPos}; - } -}; - struct InstallableAttrPath : InstallableValue { RootValue v; -- cgit v1.2.3 From 9f4cfbb2e70e074fad8f03fdb26523387e2a5456 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Sat, 9 May 2020 09:35:33 -0600 Subject: Refactor installables InstallableValue has children InstallableFlake and InstallableAttrPath, but InstallableFlake was overriding toDerivations, and usage was changed so that InstallableFlake didn't need cmd. So these changes were made: InstallableValue::toDerivations() -> InstalllableAttrPath::toDerivations() InstallableValue::cmd -> InstallableAttrPath::cmd InstallableValue uses state instead of cmd toBuildables() and toDerivations() were made abstract --- src/nix/installables.cc | 26 ++++++++++++-------------- src/nix/installables.hh | 20 +++++++++----------- src/nix/profile.cc | 2 +- 3 files changed, 22 insertions(+), 26 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 16e03875c..5267a37a0 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -179,10 +179,8 @@ struct InstallableStorePath : Installable } }; -std::vector InstallableValue::toDerivations() +std::vector InstallableAttrPath::toDerivations() { - auto state = cmd.getEvalState(); - auto v = toValue(*state).first; Bindings & autoArgs = *cmd.getAutoArgs(*state); @@ -204,8 +202,6 @@ std::vector InstallableValue::toDerivations() Buildables InstallableValue::toBuildables() { - auto state = cmd.getEvalState(); - Buildables res; StorePathSet drvPaths; @@ -239,11 +235,12 @@ Buildables InstallableValue::toBuildables() struct InstallableAttrPath : InstallableValue { + SourceExprCommand & cmd; RootValue v; std::string attrPath; - InstallableAttrPath(SourceExprCommand & cmd, Value * v, const std::string & attrPath) - : InstallableValue(cmd), v(allocRootValue(v)), attrPath(attrPath) + InstallableAttrPath(ref state, SourceExprCommand & cmd, Value * v, const std::string & attrPath) + : InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath) { } std::string what() override { return attrPath; } @@ -254,6 +251,8 @@ struct InstallableAttrPath : InstallableValue state.forceValue(*vRes); return {vRes, pos}; } + + virtual std::vector toDerivations() override; }; std::vector InstallableFlake::getActualAttrPaths() @@ -313,10 +312,9 @@ ref openEvalCache( std::tuple InstallableFlake::toDerivation() { - auto state = cmd.getEvalState(); auto lockedFlake = std::make_shared( - lockFlake(*state, flakeRef, cmd.lockFlags)); + lockFlake(*state, flakeRef, lockFlags)); auto cache = openEvalCache(*state, lockedFlake, true); auto root = cache->getRoot(); @@ -362,7 +360,7 @@ std::vector InstallableFlake::toDerivations() std::pair InstallableFlake::toValue(EvalState & state) { - auto lockedFlake = lockFlake(state, flakeRef, cmd.lockFlags); + auto lockedFlake = lockFlake(state, flakeRef, lockFlags); auto vOutputs = getFlakeOutputs(state, lockedFlake); @@ -385,7 +383,7 @@ std::vector, std::string>> InstallableFlake::getCursor(EvalState & state, bool useEvalCache) { auto evalCache = openEvalCache(state, - std::make_shared(lockFlake(state, flakeRef, cmd.lockFlags)), + std::make_shared(lockFlake(state, flakeRef, lockFlags)), useEvalCache); auto root = evalCache->getRoot(); @@ -423,7 +421,7 @@ std::vector> SourceExprCommand::parseInstallables( } for (auto & s : ss) - result.push_back(std::make_shared(*this, vFile, s == "." ? "" : s)); + result.push_back(std::make_shared(state, *this, vFile, s == "." ? "" : s)); } else { @@ -433,9 +431,9 @@ std::vector> SourceExprCommand::parseInstallables( try { auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); result.push_back(std::make_shared( - *this, std::move(flakeRef), + getEvalState(), std::move(flakeRef), fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, - getDefaultFlakeAttrPathPrefixes())); + getDefaultFlakeAttrPathPrefixes(), lockFlags)); continue; } catch (...) { ex = std::current_exception(); diff --git a/src/nix/installables.hh b/src/nix/installables.hh index c9e277a51..531720de6 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -37,10 +37,7 @@ struct Installable virtual std::string what() = 0; - virtual Buildables toBuildables() - { - throw Error("argument '%s' cannot be built", what()); - } + virtual Buildables toBuildables() = 0; Buildable toBuildable(); @@ -64,9 +61,9 @@ struct Installable struct InstallableValue : Installable { - SourceExprCommand & cmd; + ref state; - InstallableValue(SourceExprCommand & cmd) : cmd(cmd) { } + InstallableValue(ref state) : state(state) {} struct DerivationInfo { @@ -75,7 +72,7 @@ struct InstallableValue : Installable std::string outputName; }; - virtual std::vector toDerivations(); + virtual std::vector toDerivations() = 0; Buildables toBuildables() override; }; @@ -85,11 +82,12 @@ struct InstallableFlake : InstallableValue FlakeRef flakeRef; Strings attrPaths; Strings prefixes; + const flake::LockFlags & lockFlags; - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, - Strings && attrPaths, Strings && prefixes) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(attrPaths), - prefixes(prefixes) + InstallableFlake(ref state, FlakeRef && flakeRef, + Strings && attrPaths, Strings && prefixes, const flake::LockFlags & lockFlags) + : InstallableValue(state), flakeRef(flakeRef), attrPaths(attrPaths), + prefixes(prefixes), lockFlags(lockFlags) { } std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 5aaf5234c..cc239052d 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -336,7 +336,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf Activity act(*logger, lvlChatty, actUnknown, fmt("checking '%s' for updates", element.source->attrPath)); - InstallableFlake installable(*this, FlakeRef(element.source->originalRef), {element.source->attrPath}, {}); + InstallableFlake installable(getEvalState(), FlakeRef(element.source->originalRef), {element.source->attrPath}, {}, lockFlags); auto [attrPath, resolvedRef, drv] = installable.toDerivation(); -- cgit v1.2.3 From 73ee1afffe4db88c4b8db486804a2f4368f3ab78 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Sat, 9 May 2020 14:07:18 -0600 Subject: Reorder to build This reverts commit 883948d7a0add742ccae58e9845d769a8064371c. --- src/nix/installables.cc | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 5267a37a0..a06022f8c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -179,27 +179,6 @@ struct InstallableStorePath : Installable } }; -std::vector InstallableAttrPath::toDerivations() -{ - auto v = toValue(*state).first; - - Bindings & autoArgs = *cmd.getAutoArgs(*state); - - DrvInfos drvInfos; - getDerivations(*state, *v, "", autoArgs, drvInfos, false); - - std::vector res; - for (auto & drvInfo : drvInfos) { - res.push_back({ - state->store->parseStorePath(drvInfo.queryDrvPath()), - state->store->parseStorePath(drvInfo.queryOutPath()), - drvInfo.queryOutputName() - }); - } - - return res; -} - Buildables InstallableValue::toBuildables() { Buildables res; @@ -255,6 +234,27 @@ struct InstallableAttrPath : InstallableValue virtual std::vector toDerivations() override; }; +std::vector InstallableAttrPath::toDerivations() +{ + auto v = toValue(*state).first; + + Bindings & autoArgs = *cmd.getAutoArgs(*state); + + DrvInfos drvInfos; + getDerivations(*state, *v, "", autoArgs, drvInfos, false); + + std::vector res; + for (auto & drvInfo : drvInfos) { + res.push_back({ + state->store->parseStorePath(drvInfo.queryDrvPath()), + state->store->parseStorePath(drvInfo.queryOutPath()), + drvInfo.queryOutputName() + }); + } + + return res; +} + std::vector InstallableFlake::getActualAttrPaths() { std::vector res; -- cgit v1.2.3 From 91ddee6bf045b1c6144d14233abdb96127186ec3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 10 May 2020 20:32:21 +0200 Subject: nix: Implement basic bash completion --- src/nix/main.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index 3915a4896..c491bc264 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -166,7 +166,21 @@ void mainWrapped(int argc, char * * argv) NixArgs args; - args.parseCmdline(argvToStrings(argc, argv)); + Finally printCompletions([&]() + { + if (completions) { + for (auto & s : *completions) + std::cout << s << "\n"; + } + }); + + try { + args.parseCmdline(argvToStrings(argc, argv)); + } catch (UsageError &) { + if (!completions) throw; + } + + if (completions) return; settings.requireExperimentalFeature("nix-command"); -- cgit v1.2.3 From e0c19ee620c53b52ca7cf69c19d414d782338be1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 10 May 2020 21:35:07 +0200 Subject: Add completion for paths --- src/nix/build.cc | 1 + src/nix/cat.cc | 4 ++-- src/nix/command.cc | 1 + src/nix/hash.cc | 2 +- src/nix/installables.cc | 3 ++- src/nix/ls.cc | 4 ++-- src/nix/main.cc | 5 +++-- src/nix/repl.cc | 2 +- src/nix/run.cc | 2 +- src/nix/sigs.cc | 3 ++- 10 files changed, 16 insertions(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 83d47acd4..474337208 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -18,6 +18,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixProfile .description = "path of the symlink to the build result", .labels = {"path"}, .handler = {&outLink}, + .completer = completePath }); addFlag({ diff --git a/src/nix/cat.cc b/src/nix/cat.cc index fd91f2036..eeee1e529 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -25,7 +25,7 @@ struct CmdCatStore : StoreCommand, MixCat { CmdCatStore() { - expectArg("path", &path); + expectPathArg("path", &path); } std::string description() override @@ -47,7 +47,7 @@ struct CmdCatNar : StoreCommand, MixCat CmdCatNar() { - expectArg("nar", &narPath); + expectPathArg("nar", &narPath); expectArg("path", &path); } diff --git a/src/nix/command.cc b/src/nix/command.cc index 71b027719..803a36e84 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -108,6 +108,7 @@ MixProfile::MixProfile() .description = "profile to update", .labels = {"path"}, .handler = {&profile}, + .completer = completePath }); } diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 366314227..0f460c668 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -31,7 +31,7 @@ struct CmdHash : Command .labels({"modulus"}) .dest(&modulus); #endif - expectArgs("paths", &paths); + expectPathArgs("paths", &paths); } std::string description() override diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 57060e9b1..c144a7e70 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -77,7 +77,8 @@ SourceExprCommand::SourceExprCommand() .shortName = 'f', .description = "evaluate FILE rather than the default", .labels = {"file"}, - .handler = {&file} + .handler = {&file}, + .completer = completePath }); addFlag({ diff --git a/src/nix/ls.cc b/src/nix/ls.cc index b9716a6a1..aac082422 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -85,7 +85,7 @@ struct CmdLsStore : StoreCommand, MixLs { CmdLsStore() { - expectArg("path", &path); + expectPathArg("path", &path); } Examples examples() override @@ -117,7 +117,7 @@ struct CmdLsNar : Command, MixLs CmdLsNar() { - expectArg("nar", &narPath); + expectPathArg("nar", &narPath); expectArg("path", &path); } diff --git a/src/nix/main.cc b/src/nix/main.cc index c491bc264..fffdeab90 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -68,7 +68,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs addFlag({ .longName = "help", .description = "show usage information", - .handler = {[&]() { showHelpAndExit(); }}, + .handler = {[&]() { if (!completions) showHelpAndExit(); }}, }); addFlag({ @@ -96,7 +96,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs addFlag({ .longName = "version", .description = "show version information", - .handler = {[&]() { printVersion(programName); }}, + .handler = {[&]() { if (!completions) printVersion(programName); }}, }); addFlag({ @@ -169,6 +169,7 @@ void mainWrapped(int argc, char * * argv) Finally printCompletions([&]() { if (completions) { + std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n"); for (auto & s : *completions) std::cout << s << "\n"; } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 0a6a7ab19..2e7b14d08 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -767,7 +767,7 @@ struct CmdRepl : StoreCommand, MixEvalArgs CmdRepl() { - expectArgs("files", &files); + expectPathArgs("files", &files); } std::string description() override diff --git a/src/nix/run.cc b/src/nix/run.cc index 3e2c8b4f3..3701ffe3d 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -149,7 +149,7 @@ struct CmdRun : InstallableCommand, RunCommon CmdRun() { - expectArgs("args", &args); + expectPathArgs("args", &args); } std::string description() override diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index 6c9b9a792..7821a5432 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -105,7 +105,8 @@ struct CmdSignPaths : StorePathsCommand .shortName = 'k', .description = "file containing the secret signing key", .labels = {"file"}, - .handler = {&secretKeyFile} + .handler = {&secretKeyFile}, + .completer = completePath }); } -- cgit v1.2.3 From 4c3c638a05e52cdc3bd96255873b711a28630288 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 May 2020 15:46:18 +0200 Subject: Cleanup --- src/nix/cat.cc | 12 ++++++++++-- src/nix/command.hh | 5 +---- src/nix/hash.cc | 6 +++++- src/nix/installables.cc | 8 ++++++++ src/nix/ls.cc | 12 ++++++++++-- src/nix/repl.cc | 6 +++++- src/nix/run.cc | 6 +++++- 7 files changed, 44 insertions(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/cat.cc b/src/nix/cat.cc index eeee1e529..b528a0507 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -25,7 +25,11 @@ struct CmdCatStore : StoreCommand, MixCat { CmdCatStore() { - expectPathArg("path", &path); + expectArgs({ + .label = "path", + .handler = {&path}, + .completer = completePath + }); } std::string description() override @@ -47,7 +51,11 @@ struct CmdCatNar : StoreCommand, MixCat CmdCatNar() { - expectPathArg("nar", &narPath); + expectArgs({ + .label = "nar", + .handler = {&narPath}, + .completer = completePath + }); expectArg("path", &path); } diff --git a/src/nix/command.hh b/src/nix/command.hh index 6b4781303..0738c0f91 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -73,10 +73,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand { std::vector> installables; - InstallablesCommand() - { - expectArgs("installables", &_installables); - } + InstallablesCommand(); void prepare() override; diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 0f460c668..d5636eb47 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -31,7 +31,11 @@ struct CmdHash : Command .labels({"modulus"}) .dest(&modulus); #endif - expectPathArgs("paths", &paths); + expectArgs({ + .label = "paths", + .handler = {&paths}, + .completer = completePath + }); } std::string description() override diff --git a/src/nix/installables.cc b/src/nix/installables.cc index c144a7e70..abc642e11 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -571,6 +571,14 @@ StorePathSet toDerivations(ref store, return drvPaths; } +InstallablesCommand::InstallablesCommand() +{ + expectArgs({ + .label = "installables", + .handler = {&_installables}, + }); +} + void InstallablesCommand::prepare() { if (_installables.empty() && useDefaultInstallables()) diff --git a/src/nix/ls.cc b/src/nix/ls.cc index aac082422..dc7e370b9 100644 --- a/src/nix/ls.cc +++ b/src/nix/ls.cc @@ -85,7 +85,11 @@ struct CmdLsStore : StoreCommand, MixLs { CmdLsStore() { - expectPathArg("path", &path); + expectArgs({ + .label = "path", + .handler = {&path}, + .completer = completePath + }); } Examples examples() override @@ -117,7 +121,11 @@ struct CmdLsNar : Command, MixLs CmdLsNar() { - expectPathArg("nar", &narPath); + expectArgs({ + .label = "nar", + .handler = {&narPath}, + .completer = completePath + }); expectArg("path", &path); } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 2e7b14d08..c936f9cc2 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -767,7 +767,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs CmdRepl() { - expectPathArgs("files", &files); + expectArgs({ + .label = "files", + .handler = {&files}, + .completer = completePath + }); } std::string description() override diff --git a/src/nix/run.cc b/src/nix/run.cc index 3701ffe3d..f9b1298f1 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -149,7 +149,11 @@ struct CmdRun : InstallableCommand, RunCommon CmdRun() { - expectPathArgs("args", &args); + expectArgs({ + .label = "args", + .handler = {&args}, + .completer = completePath + }); } std::string description() override -- cgit v1.2.3 From 259ff74bdeeaac5a987a4eb88654ba80d9553543 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 May 2020 21:49:02 +0200 Subject: Add completion for installables This completes flakerefs using the registry (e.g. 'nix' => 'nix nixpkgs') and flake output attributes by evaluating the flake (e.g. 'dwarffs#nix' => 'dwarffs#nixosModules'). --- src/nix/command.hh | 7 ++-- src/nix/installables.cc | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 0738c0f91..6a6c3fed9 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -63,6 +63,8 @@ struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions virtual Strings getDefaultFlakeAttrPaths(); virtual Strings getDefaultFlakeAttrPathPrefixes(); + + void completeInstallable(std::string_view prefix); }; enum RealiseMode { Build, NoBuild, DryRun }; @@ -89,10 +91,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand { std::shared_ptr installable; - InstallableCommand() - { - expectArg("installable", &_installable, true); - } + InstallableCommand(); void prepare() override; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index abc642e11..ade8f83f9 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -11,6 +11,7 @@ #include "flake/flake.hh" #include "eval-cache.hh" #include "url.hh" +#include "registry.hh" #include #include @@ -106,6 +107,91 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() }; } +void SourceExprCommand::completeInstallable(std::string_view prefix) +{ + completePath(0, prefix); + + if (file) return; // FIXME + + /* Look for flake output attributes that match the + prefix. */ + try { + auto hash = prefix.find('#'); + if (hash != std::string::npos) { + auto fragment = prefix.substr(hash + 1); + auto flakeRefS = std::string(prefix.substr(0, hash)); + // FIXME: do tilde expansion. + auto flakeRef = parseFlakeRef(flakeRefS, absPath(".")); + + auto state = getEvalState(); + + auto evalCache = openEvalCache(*state, + std::make_shared(lockFlake(*state, flakeRef, lockFlags)), + true); + + auto root = evalCache->getRoot(); + + /* Complete 'fragment' relative to all the + attrpath prefixes as well as the root of the + flake. */ + auto attrPathPrefixes = getDefaultFlakeAttrPathPrefixes(); + attrPathPrefixes.push_back(""); + + for (auto & attrPathPrefixS : attrPathPrefixes) { + auto attrPathPrefix = parseAttrPath(*state, attrPathPrefixS); + auto attrPathS = attrPathPrefixS + std::string(fragment); + auto attrPath = parseAttrPath(*state, attrPathS); + + std::string lastAttr; + if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) { + lastAttr = attrPath.back(); + attrPath.pop_back(); + } + + auto attr = root->findAlongAttrPath(attrPath); + if (!attr) continue; + + auto attrs = attr->getAttrs(); + for (auto & attr2 : attrs) { + if (hasPrefix(attr2, lastAttr)) { + auto attrPath2 = attr->getAttrPath(attr2); + /* Strip the attrpath prefix. */ + attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); + completions->insert(flakeRefS + "#" + concatStringsSep(".", attrPath2)); + } + } + } + + /* And add an empty completion for the default + attrpaths. */ + if (fragment.empty()) { + for (auto & attrPath : getDefaultFlakeAttrPaths()) { + auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); + if (!attr) continue; + completions->insert(flakeRefS + "#"); + } + } + } + } catch (Error & e) { + warn(e.msg()); + } + + /* Look for registry entries that match the prefix. */ + for (auto & registry : fetchers::getRegistries(getStore())) { + for (auto & entry : registry->entries) { + auto from = entry.from->to_string(); + if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { + std::string from2(from, 6); + if (hasPrefix(from2, prefix)) + completions->insert(from2); + } else { + if (hasPrefix(from, prefix)) + completions->insert(from); + } + } + } +} + ref EvalCommand::getEvalState() { if (!evalState) @@ -576,6 +662,9 @@ InstallablesCommand::InstallablesCommand() expectArgs({ .label = "installables", .handler = {&_installables}, + .completer = {[&](size_t, std::string_view prefix) { + completeInstallable(prefix); + }} }); } @@ -588,6 +677,17 @@ void InstallablesCommand::prepare() installables = parseInstallables(getStore(), _installables); } +InstallableCommand::InstallableCommand() +{ + expectArgs({ + .label = "installable", + .handler = {&_installable}, + .completer = {[&](size_t, std::string_view prefix) { + completeInstallable(prefix); + }} + }); +} + void InstallableCommand::prepare() { installable = parseInstallable(getStore(), _installable); -- cgit v1.2.3 From 27d34ef770356f86823ca832f278e72bb0a07982 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 May 2020 22:04:13 +0200 Subject: When completing flakerefs, only return directories --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index ade8f83f9..5ba48d69a 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -109,7 +109,7 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() void SourceExprCommand::completeInstallable(std::string_view prefix) { - completePath(0, prefix); + completeDir(0, prefix); if (file) return; // FIXME -- cgit v1.2.3 From 649c2db308f16ce4b2cbefe4a8760577541cfb47 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 May 2020 22:10:33 +0200 Subject: nix flake: Add completion support --- src/nix/command.hh | 2 ++ src/nix/flake.cc | 9 ++++++++- src/nix/installables.cc | 26 +++++++++++++++++--------- 3 files changed, 27 insertions(+), 10 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index 6a6c3fed9..faa19c8ea 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -38,6 +38,8 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs ref getEvalState(); std::shared_ptr evalState; + + void completeFlakeRef(std::string_view prefix); }; struct MixFlakeOptions : virtual Args diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6eee781aa..b6cc7eb54 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -28,7 +28,14 @@ public: FlakeCommand() { - expectArg("flake-url", &flakeUrl, true); + expectArgs({ + .label = "flake-url", + .optional = true, + .handler = {&flakeUrl}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(prefix); + }} + }); } FlakeRef getFlakeRef() diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 5ba48d69a..2d23b33b7 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -109,8 +109,6 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() void SourceExprCommand::completeInstallable(std::string_view prefix) { - completeDir(0, prefix); - if (file) return; // FIXME /* Look for flake output attributes that match the @@ -176,6 +174,23 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) warn(e.msg()); } + completeFlakeRef(prefix); +} + +ref EvalCommand::getEvalState() +{ + if (!evalState) + evalState = std::make_shared(searchPath, getStore()); + return ref(evalState); +} + +void EvalCommand::completeFlakeRef(std::string_view prefix) +{ + if (prefix == "") + completions->insert("."); + + completeDir(0, prefix); + /* Look for registry entries that match the prefix. */ for (auto & registry : fetchers::getRegistries(getStore())) { for (auto & entry : registry->entries) { @@ -192,13 +207,6 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) } } -ref EvalCommand::getEvalState() -{ - if (!evalState) - evalState = std::make_shared(searchPath, getStore()); - return ref(evalState); -} - Buildable Installable::toBuildable() { auto buildables = toBuildables(); -- cgit v1.2.3 From b8b2dbf27204bc6ce5f57fa5fc5c76f9265fcee1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 12 May 2020 11:53:32 +0200 Subject: Fix InstallableCommand --- src/nix/installables.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 2d23b33b7..5385484df 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -689,6 +689,7 @@ InstallableCommand::InstallableCommand() { expectArgs({ .label = "installable", + .optional = true, .handler = {&_installable}, .completer = {[&](size_t, std::string_view prefix) { completeInstallable(prefix); -- cgit v1.2.3 From 5f64655ff429be08aa0787761697787e7050f373 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 15 May 2020 14:38:10 +0200 Subject: Move registry-related commands from 'nix flake' to 'nix registry' This makes 'nix flake' less cluttered and more consistent (it's only subcommands that operator on a flake). Also, the registry is not inherently flake-related (e.g. fetchTree could also use it to remap inputs). --- src/nix/flake.cc | 108 ------------------------------------- src/nix/registry.cc | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nix/run.cc | 1 - 3 files changed, 150 insertions(+), 109 deletions(-) create mode 100644 src/nix/registry.cc (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b6cc7eb54..d255bdda5 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -55,34 +55,6 @@ public: } }; -struct CmdFlakeList : EvalCommand -{ - std::string description() override - { - return "list available Nix flakes"; - } - - void run(nix::ref store) override - { - using namespace fetchers; - - auto registries = getRegistries(store); - - for (auto & registry : registries) { - for (auto & entry : registry->entries) { - // FIXME: format nicely - logger->stdout("%s %s %s", - registry->type == Registry::Flag ? "flags " : - registry->type == Registry::User ? "user " : - registry->type == Registry::System ? "system" : - "global", - entry.from->to_string(), - entry.to->to_string()); - } - } - } -}; - static void printFlakeInfo(const Store & store, const Flake & flake) { logger->stdout("Resolved URL: %s", flake.resolvedRef.to_string()); @@ -472,82 +444,6 @@ struct CmdFlakeCheck : FlakeCommand } }; -struct CmdFlakeAdd : MixEvalArgs, Command -{ - std::string fromUrl, toUrl; - - std::string description() override - { - return "upsert flake in user flake registry"; - } - - CmdFlakeAdd() - { - expectArg("from-url", &fromUrl); - expectArg("to-url", &toUrl); - } - - void run() override - { - auto fromRef = parseFlakeRef(fromUrl); - auto toRef = parseFlakeRef(toUrl); - fetchers::Attrs extraAttrs; - if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; - auto userRegistry = fetchers::getUserRegistry(); - userRegistry->remove(fromRef.input); - userRegistry->add(fromRef.input, toRef.input, extraAttrs); - userRegistry->write(fetchers::getUserRegistryPath()); - } -}; - -struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command -{ - std::string url; - - std::string description() override - { - return "remove flake from user flake registry"; - } - - CmdFlakeRemove() - { - expectArg("url", &url); - } - - void run() override - { - auto userRegistry = fetchers::getUserRegistry(); - userRegistry->remove(parseFlakeRef(url).input); - userRegistry->write(fetchers::getUserRegistryPath()); - } -}; - -struct CmdFlakePin : virtual Args, EvalCommand -{ - std::string url; - - std::string description() override - { - return "pin a flake to its current version in user flake registry"; - } - - CmdFlakePin() - { - expectArg("url", &url); - } - - void run(nix::ref store) override - { - auto ref = parseFlakeRef(url); - auto userRegistry = fetchers::getUserRegistry(); - userRegistry->remove(ref.input); - auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); - fetchers::Attrs extraAttrs; - if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; - userRegistry->add(ref.input, resolved, extraAttrs); - } -}; - struct CmdFlakeInit : virtual Args, Command { std::string description() override @@ -836,14 +732,10 @@ struct CmdFlake : virtual MultiCommand, virtual Command { CmdFlake() : MultiCommand({ - {"list", []() { return make_ref(); }}, {"update", []() { return make_ref(); }}, {"info", []() { return make_ref(); }}, {"list-inputs", []() { return make_ref(); }}, {"check", []() { return make_ref(); }}, - {"add", []() { return make_ref(); }}, - {"remove", []() { return make_ref(); }}, - {"pin", []() { return make_ref(); }}, {"init", []() { return make_ref(); }}, {"clone", []() { return make_ref(); }}, {"archive", []() { return make_ref(); }}, diff --git a/src/nix/registry.cc b/src/nix/registry.cc new file mode 100644 index 000000000..e9c76e6c6 --- /dev/null +++ b/src/nix/registry.cc @@ -0,0 +1,150 @@ +#include "command.hh" +#include "common-args.hh" +#include "shared.hh" +#include "eval.hh" +#include "flake/flake.hh" +#include "store-api.hh" +#include "fetchers.hh" +#include "registry.hh" + +using namespace nix; +using namespace nix::flake; + +struct CmdRegistryList : StoreCommand +{ + std::string description() override + { + return "list available Nix flakes"; + } + + void run(nix::ref store) override + { + using namespace fetchers; + + auto registries = getRegistries(store); + + for (auto & registry : registries) { + for (auto & entry : registry->entries) { + // FIXME: format nicely + logger->stdout("%s %s %s", + registry->type == Registry::Flag ? "flags " : + registry->type == Registry::User ? "user " : + registry->type == Registry::System ? "system" : + "global", + entry.from->to_string(), + entry.to->to_string()); + } + } + } +}; + +struct CmdRegistryAdd : MixEvalArgs, Command +{ + std::string fromUrl, toUrl; + + std::string description() override + { + return "add/replace flake in user flake registry"; + } + + CmdRegistryAdd() + { + expectArg("from-url", &fromUrl); + expectArg("to-url", &toUrl); + } + + void run() override + { + auto fromRef = parseFlakeRef(fromUrl); + auto toRef = parseFlakeRef(toUrl); + fetchers::Attrs extraAttrs; + if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; + auto userRegistry = fetchers::getUserRegistry(); + userRegistry->remove(fromRef.input); + userRegistry->add(fromRef.input, toRef.input, extraAttrs); + userRegistry->write(fetchers::getUserRegistryPath()); + } +}; + +struct CmdRegistryRemove : virtual Args, MixEvalArgs, Command +{ + std::string url; + + std::string description() override + { + return "remove flake from user flake registry"; + } + + CmdRegistryRemove() + { + expectArg("url", &url); + } + + void run() override + { + auto userRegistry = fetchers::getUserRegistry(); + userRegistry->remove(parseFlakeRef(url).input); + userRegistry->write(fetchers::getUserRegistryPath()); + } +}; + +struct CmdRegistryPin : virtual Args, EvalCommand +{ + std::string url; + + std::string description() override + { + return "pin a flake to its current version in user flake registry"; + } + + CmdRegistryPin() + { + expectArg("url", &url); + } + + void run(nix::ref store) override + { + auto ref = parseFlakeRef(url); + auto userRegistry = fetchers::getUserRegistry(); + userRegistry->remove(ref.input); + auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); + fetchers::Attrs extraAttrs; + if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; + userRegistry->add(ref.input, resolved, extraAttrs); + } +}; + +struct CmdRegistry : virtual MultiCommand, virtual Command +{ + CmdRegistry() + : MultiCommand({ + {"list", []() { return make_ref(); }}, + {"add", []() { return make_ref(); }}, + {"remove", []() { return make_ref(); }}, + {"pin", []() { return make_ref(); }}, + }) + { + } + + std::string description() override + { + return "manage the flake registry"; + } + + Category category() override { return catSecondary; } + + void run() override + { + if (!command) + throw UsageError("'nix registry' requires a sub-command."); + command->second->prepare(); + command->second->run(); + } + + void printHelp(const string & programName, std::ostream & out) override + { + MultiCommand::printHelp(programName, out); + } +}; + +static auto r1 = registerCommand("registry"); diff --git a/src/nix/run.cc b/src/nix/run.cc index f9b1298f1..2ab9d9ef4 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -108,7 +108,6 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment auto accessor = store->getFSAccessor(); - std::unordered_set done; std::queue todo; for (auto & path : outPaths) todo.push(path.clone()); -- cgit v1.2.3 From 8fbc8540d3ea540d985b577166a1168d472c2d70 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Mon, 27 Apr 2020 12:17:58 -0600 Subject: use nixpkgs#bashInteractive for dev-shell --- src/nix/dev-shell.cc | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/dev-shell.cc b/src/nix/dev-shell.cc index 6b1cf0ffd..d842be79f 100644 --- a/src/nix/dev-shell.cc +++ b/src/nix/dev-shell.cc @@ -6,6 +6,9 @@ #include "derivations.hh" #include "affinity.hh" #include "progress-bar.hh" +#include "attr-path.hh" +#include "eval-cache.hh" +#include "flake/flake.hh" #include @@ -281,6 +284,41 @@ struct CmdDevShell : Common, MixEnvironment }; } + std::string getBashPath(ref store) + { + auto flakeRef = FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); + auto state = getEvalState(); + auto lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlags)); + auto cache = openEvalCache(*state, lockedFlake, true); + auto root = cache->getRoot(); + + auto attrPath = "legacyPackages." + settings.thisSystem.get() + ".bashInteractive"; + auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); + if (!attr || !attr->isDerivation()) throw Error("couldn't find bashInteractive derivation"); + + auto aDrvPath = attr->getAttr(state->sDrvPath); + auto drvPath = store->parseStorePath(aDrvPath->getString()); + if (!store->isValidPath(drvPath) && !settings.readOnlyMode) { + /* The eval cache contains 'drvPath', but the actual path + has been garbage-collected. So force it to be + regenerated. */ + aDrvPath->forceValue(); + if (!store->isValidPath(drvPath)) + throw Error("don't know how to recreate store derivation '%s'!", + store->printStorePath(drvPath)); + } + + auto outputName = attr->getAttr(state->sOutputName)->getString(); + if (outputName == "") + throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath)); + + auto outPath = store->parseStorePath(attr->getAttr(state->sOutPath)->getString()); + + store->buildPaths({{drvPath, {outputName}}}); + + return store->printStorePath(outPath) + "/bin/bash"; + } + void run(ref store) override { auto [buildEnvironment, gcroot] = getBuildEnvironment(store); @@ -303,12 +341,11 @@ struct CmdDevShell : Common, MixEnvironment stopProgressBar(); - auto shell = getEnv("SHELL").value_or("bash"); - setEnviron(); // prevent garbage collection until shell exits setenv("NIX_GCROOT", gcroot.data(), 1); + auto shell = getBashPath(store); auto args = Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; restoreAffinity(); -- cgit v1.2.3 From 04821bc1718aff975b51e98ce7bfb91888167c6d Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Tue, 5 May 2020 10:49:39 -0600 Subject: use flake's nixpkgs to find bashInteractive --- src/nix/dev-shell.cc | 3 +-- src/nix/installables.cc | 12 ++++++++++++ src/nix/installables.hh | 7 +++++++ 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/dev-shell.cc b/src/nix/dev-shell.cc index d842be79f..b3314021f 100644 --- a/src/nix/dev-shell.cc +++ b/src/nix/dev-shell.cc @@ -286,9 +286,8 @@ struct CmdDevShell : Common, MixEnvironment std::string getBashPath(ref store) { - auto flakeRef = FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); auto state = getEvalState(); - auto lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlags)); + auto lockedFlake = std::make_shared(lockFlake(*state, installable->nixpkgsFlakeRef(), lockFlags)); auto cache = openEvalCache(*state, lockedFlake, true); auto root = cache->getRoot(); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index cae85b34e..4e38876ca 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -493,6 +493,18 @@ InstallableFlake::getCursor(EvalState & state, bool useEvalCache) return res; } +FlakeRef InstallableFlake::nixpkgsFlakeRef() const +{ + auto lockedFlake = lockFlake(*(cmd.getEvalState()), flakeRef, cmd.lockFlags); + + auto nixpkgsInput = lockedFlake.flake.inputs.find("nixpkgs"); + if (nixpkgsInput != lockedFlake.flake.inputs.end()) { + return std::move(nixpkgsInput->second.ref); + } + + return Installable::nixpkgsFlakeRef(); +} + std::vector> SourceExprCommand::parseInstallables( ref store, std::vector ss) { diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 531720de6..57e3d6c8e 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -57,6 +57,11 @@ struct Installable virtual std::vector, std::string>> getCursor(EvalState & state, bool useEvalCache); + + virtual FlakeRef nixpkgsFlakeRef() const + { + return std::move(FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}})); + } }; struct InstallableValue : Installable @@ -104,6 +109,8 @@ struct InstallableFlake : InstallableValue std::vector, std::string>> getCursor(EvalState & state, bool useEvalCache) override; + + FlakeRef nixpkgsFlakeRef() const override; }; ref openEvalCache( -- cgit v1.2.3 From 085879360415a2c667c6d75dce5765f01afbb25d Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Tue, 12 May 2020 23:45:45 -0600 Subject: Call lockFlake once and store in _lockedFlake --- src/nix/installables.cc | 20 +++++++++++++------- src/nix/installables.hh | 3 +++ 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 4e38876ca..21fd54f64 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -408,8 +408,7 @@ ref openEvalCache( std::tuple InstallableFlake::toDerivation() { - auto lockedFlake = std::make_shared( - lockFlake(*state, flakeRef, lockFlags)); + auto lockedFlake = getLockedFlake(); auto cache = openEvalCache(*state, lockedFlake, true); auto root = cache->getRoot(); @@ -455,9 +454,9 @@ std::vector InstallableFlake::toDerivations() std::pair InstallableFlake::toValue(EvalState & state) { - auto lockedFlake = lockFlake(state, flakeRef, lockFlags); + auto lockedFlake = getLockedFlake(); - auto vOutputs = getFlakeOutputs(state, lockedFlake); + auto vOutputs = getFlakeOutputs(state, *lockedFlake); auto emptyArgs = state.allocBindings(0); @@ -493,12 +492,19 @@ InstallableFlake::getCursor(EvalState & state, bool useEvalCache) return res; } +std::shared_ptr InstallableFlake::getLockedFlake() const +{ + if (!_lockedFlake) + _lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlags)); + return _lockedFlake; +} + FlakeRef InstallableFlake::nixpkgsFlakeRef() const { - auto lockedFlake = lockFlake(*(cmd.getEvalState()), flakeRef, cmd.lockFlags); + auto lockedFlake = getLockedFlake(); - auto nixpkgsInput = lockedFlake.flake.inputs.find("nixpkgs"); - if (nixpkgsInput != lockedFlake.flake.inputs.end()) { + auto nixpkgsInput = lockedFlake->flake.inputs.find("nixpkgs"); + if (nixpkgsInput != lockedFlake->flake.inputs.end()) { return std::move(nixpkgsInput->second.ref); } diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 57e3d6c8e..a2db71389 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -88,6 +88,7 @@ struct InstallableFlake : InstallableValue Strings attrPaths; Strings prefixes; const flake::LockFlags & lockFlags; + mutable std::shared_ptr _lockedFlake; InstallableFlake(ref state, FlakeRef && flakeRef, Strings && attrPaths, Strings && prefixes, const flake::LockFlags & lockFlags) @@ -110,6 +111,8 @@ struct InstallableFlake : InstallableValue std::vector, std::string>> getCursor(EvalState & state, bool useEvalCache) override; + std::shared_ptr getLockedFlake() const; + FlakeRef nixpkgsFlakeRef() const override; }; -- cgit v1.2.3 From ba7d7ed2e34b57ed15fc7473622ecfac2df77434 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Thu, 14 May 2020 16:43:06 -0600 Subject: Create bashInteractive InstallableFlake --- src/nix/dev-shell.cc | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) (limited to 'src/nix') diff --git a/src/nix/dev-shell.cc b/src/nix/dev-shell.cc index b3314021f..9f84992b4 100644 --- a/src/nix/dev-shell.cc +++ b/src/nix/dev-shell.cc @@ -284,40 +284,6 @@ struct CmdDevShell : Common, MixEnvironment }; } - std::string getBashPath(ref store) - { - auto state = getEvalState(); - auto lockedFlake = std::make_shared(lockFlake(*state, installable->nixpkgsFlakeRef(), lockFlags)); - auto cache = openEvalCache(*state, lockedFlake, true); - auto root = cache->getRoot(); - - auto attrPath = "legacyPackages." + settings.thisSystem.get() + ".bashInteractive"; - auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); - if (!attr || !attr->isDerivation()) throw Error("couldn't find bashInteractive derivation"); - - auto aDrvPath = attr->getAttr(state->sDrvPath); - auto drvPath = store->parseStorePath(aDrvPath->getString()); - if (!store->isValidPath(drvPath) && !settings.readOnlyMode) { - /* The eval cache contains 'drvPath', but the actual path - has been garbage-collected. So force it to be - regenerated. */ - aDrvPath->forceValue(); - if (!store->isValidPath(drvPath)) - throw Error("don't know how to recreate store derivation '%s'!", - store->printStorePath(drvPath)); - } - - auto outputName = attr->getAttr(state->sOutputName)->getString(); - if (outputName == "") - throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath)); - - auto outPath = store->parseStorePath(attr->getAttr(state->sOutPath)->getString()); - - store->buildPaths({{drvPath, {outputName}}}); - - return store->printStorePath(outPath) + "/bin/bash"; - } - void run(ref store) override { auto [buildEnvironment, gcroot] = getBuildEnvironment(store); @@ -344,7 +310,9 @@ struct CmdDevShell : Common, MixEnvironment // prevent garbage collection until shell exits setenv("NIX_GCROOT", gcroot.data(), 1); - auto shell = getBashPath(store); + auto state = getEvalState(); + auto bashInstallable = std::make_shared(state, std::move(installable->nixpkgsFlakeRef()), Strings{"bashInteractive"}, Strings{"legacyPackages." + settings.thisSystem.get() + "."}, lockFlags); + auto shell = state->store->printStorePath(toStorePath(state->store, Build, bashInstallable)) + "/bin/bash"; auto args = Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; restoreAffinity(); -- cgit v1.2.3 From c4beded32e2909651c95f6fabf5c78537bd37e68 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Sat, 16 May 2020 11:19:41 -0600 Subject: rm includes --- src/nix/dev-shell.cc | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/dev-shell.cc b/src/nix/dev-shell.cc index 9f84992b4..53d2e4e25 100644 --- a/src/nix/dev-shell.cc +++ b/src/nix/dev-shell.cc @@ -6,9 +6,6 @@ #include "derivations.hh" #include "affinity.hh" #include "progress-bar.hh" -#include "attr-path.hh" -#include "eval-cache.hh" -#include "flake/flake.hh" #include -- cgit v1.2.3 From 8d67794da1b1307c3d117058f85649b05fe7c554 Mon Sep 17 00:00:00 2001 From: Matthew Kenigsberg Date: Thu, 21 May 2020 16:58:02 -0600 Subject: handle circular flake dependencies in list-inputs --- src/nix/flake.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d255bdda5..59bb40110 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -158,20 +158,26 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON else { logger->stdout("%s", flake.flake.lockedRef); + std::unordered_set> visited; + std::function recurse; recurse = [&](const Node & node, const std::string & prefix) { for (const auto & [i, input] : enumerate(node.inputs)) { - //auto tree2 = tree.child(i + 1 == inputs.inputs.size()); + bool firstVisit = visited.insert(input.second).second; bool last = i + 1 == node.inputs.size(); + auto lockedNode = std::dynamic_pointer_cast(input.second); + logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", prefix + (last ? treeLast : treeConn), input.first, - std::dynamic_pointer_cast(input.second)->lockedRef); - recurse(*input.second, prefix + (last ? treeNull : treeLine)); + lockedNode ? lockedNode->lockedRef : flake.flake.lockedRef); + + if (firstVisit) recurse(*input.second, prefix + (last ? treeNull : treeLine)); } }; + visited.insert(flake.lockFile.root); recurse(*flake.lockFile.root, ""); } } -- cgit v1.2.3 From 6286272371434dc597da793417787ecd3cb14cc9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 28 May 2020 12:13:13 +0200 Subject: nixpkgsFlakeRef(): Use locked nixpkgs --- src/nix/installables.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 21fd54f64..fde1ca7aa 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -503,9 +503,12 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const { auto lockedFlake = getLockedFlake(); - auto nixpkgsInput = lockedFlake->flake.inputs.find("nixpkgs"); - if (nixpkgsInput != lockedFlake->flake.inputs.end()) { - return std::move(nixpkgsInput->second.ref); + auto nixpkgsInput = lockedFlake->lockFile.root->inputs.find("nixpkgs"); + if (nixpkgsInput != lockedFlake->lockFile.root->inputs.end()) { + if (auto lockedNode = std::dynamic_pointer_cast(nixpkgsInput->second)) { + debug("using nixpkgs flake '%s'", lockedNode->lockedRef); + return std::move(lockedNode->lockedRef); + } } return Installable::nixpkgsFlakeRef(); -- cgit v1.2.3 From 950b46821f644eb3f92725460584a3102f356179 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 30 May 2020 00:44:11 +0200 Subject: Remove TreeInfo The attributes previously stored in TreeInfo (narHash, revCount, lastModified) are now stored in Input. This makes it less arbitrary what attributes are stored where. As a result, the lock file format has changed. An entry like "info": { "lastModified": 1585405475, "narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE=" }, "locked": { "owner": "NixOS", "repo": "nixpkgs", "rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be", "type": "github" }, is now stored as "locked": { "owner": "NixOS", "repo": "nixpkgs", "rev": "b88ff468e9850410070d4e0ccd68c7011f15b2be", "type": "github", "lastModified": 1585405475, "narHash": "sha256-bESW0n4KgPmZ0luxvwJ+UyATrC6iIltVCsGdLiphVeE=" }, The 'Input' class is now a dumb set of attributes. All the fetcher implementations subclass InputScheme, not Input. This simplifies the API. Also, fix substitution of flake inputs. This was broken since lazy flake fetching started using fetchTree internally. --- src/nix/flake.cc | 32 ++++++++++++++++---------------- src/nix/installables.cc | 2 +- src/nix/profile.cc | 2 +- src/nix/registry.cc | 6 +++--- 4 files changed, 21 insertions(+), 21 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 59bb40110..15a3e261a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -62,13 +62,13 @@ static void printFlakeInfo(const Store & store, const Flake & flake) if (flake.description) logger->stdout("Description: %s", *flake.description); logger->stdout("Path: %s", store.printStorePath(flake.sourceInfo->storePath)); - if (auto rev = flake.lockedRef.input->getRev()) + if (auto rev = flake.lockedRef.input.getRev()) logger->stdout("Revision: %s", rev->to_string(Base16, false)); - if (flake.sourceInfo->info.revCount) - logger->stdout("Revisions: %s", *flake.sourceInfo->info.revCount); - if (flake.sourceInfo->info.lastModified) + if (auto revCount = flake.lockedRef.input.getRevCount()) + logger->stdout("Revisions: %s", *revCount); + if (auto lastModified = flake.lockedRef.input.getLastModified()) logger->stdout("Last modified: %s", - std::put_time(std::localtime(&*flake.sourceInfo->info.lastModified), "%F %T")); + std::put_time(std::localtime(&*lastModified), "%F %T")); } static nlohmann::json flakeToJson(const Store & store, const Flake & flake) @@ -82,13 +82,12 @@ static nlohmann::json flakeToJson(const Store & store, const Flake & flake) j["resolved"] = attrsToJson(flake.resolvedRef.toAttrs()); j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl j["locked"] = attrsToJson(flake.lockedRef.toAttrs()); - j["info"] = flake.sourceInfo->info.toJson(); - if (auto rev = flake.lockedRef.input->getRev()) + if (auto rev = flake.lockedRef.input.getRev()) j["revision"] = rev->to_string(Base16, false); - if (flake.sourceInfo->info.revCount) - j["revCount"] = *flake.sourceInfo->info.revCount; - if (flake.sourceInfo->info.lastModified) - j["lastModified"] = *flake.sourceInfo->info.lastModified; + if (auto revCount = flake.lockedRef.input.getRevCount()) + j["revCount"] = *revCount; + if (auto lastModified = flake.lockedRef.input.getLastModified()) + j["lastModified"] = *lastModified; j["path"] = store.printStorePath(flake.sourceInfo->storePath); return j; } @@ -172,7 +171,7 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", prefix + (last ? treeLast : treeConn), input.first, lockedNode ? lockedNode->lockedRef : flake.flake.lockedRef); - + if (firstVisit) recurse(*input.second, prefix + (last ? treeNull : treeLine)); } }; @@ -501,7 +500,7 @@ struct CmdFlakeClone : FlakeCommand if (destDir.empty()) throw Error("missing flag '--dest'"); - getFlakeRef().resolve(store).input->clone(destDir); + getFlakeRef().resolve(store).input.clone(destDir); } }; @@ -563,9 +562,10 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun auto lockedInput = std::dynamic_pointer_cast(input.second); assert(lockedInput); auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional(); - if (!dryRun) - lockedInput->lockedRef.input->fetchTree(store); - auto storePath = lockedInput->computeStorePath(*store); + auto storePath = + dryRun + ? lockedInput->lockedRef.input.computeStorePath(*store) + : lockedInput->lockedRef.input.fetch(store).first.storePath; if (jsonObj3) jsonObj3->attr("path", store->printStorePath(storePath)); sources.insert(std::move(storePath)); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index fde1ca7aa..86d3bfd20 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -194,7 +194,7 @@ void EvalCommand::completeFlakeRef(std::string_view prefix) /* Look for registry entries that match the prefix. */ for (auto & registry : fetchers::getRegistries(getStore())) { for (auto & entry : registry->entries) { - auto from = entry.from->to_string(); + auto from = entry.from.to_string(); if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { std::string from2(from, 6); if (hasPrefix(from2, prefix)) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index cc239052d..f39213b8f 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -330,7 +330,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); if (element.source - && !element.source->originalRef.input->isImmutable() + && !element.source->originalRef.input.isImmutable() && matches(*store, element, i, matchers)) { Activity act(*logger, lvlChatty, actUnknown, diff --git a/src/nix/registry.cc b/src/nix/registry.cc index e9c76e6c6..16d7e511f 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -31,8 +31,8 @@ struct CmdRegistryList : StoreCommand registry->type == Registry::User ? "user " : registry->type == Registry::System ? "system" : "global", - entry.from->to_string(), - entry.to->to_string()); + entry.from.to_string(), + entry.to.to_string()); } } } @@ -107,7 +107,7 @@ struct CmdRegistryPin : virtual Args, EvalCommand auto ref = parseFlakeRef(url); auto userRegistry = fetchers::getUserRegistry(); userRegistry->remove(ref.input); - auto [tree, resolved] = ref.resolve(store).input->fetchTree(store); + auto [tree, resolved] = ref.resolve(store).input.fetch(store); fetchers::Attrs extraAttrs; if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; userRegistry->add(ref.input, resolved, extraAttrs); -- cgit v1.2.3 From d746ef4a813b3fc8f31a9c58c60b499a62589806 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Jun 2020 14:53:51 +0200 Subject: Disable eval cache with --impure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes $ nix build nixpkgs#zoom-us error: Package ‘zoom-us-5.0.399860.0429’ in /nix/store/m79v7h75b69fkk8d2qcwm555l3wq6fmv-source/pkgs/applications/networking/instant-messengers/zoom-us/default.nix:126 has an unfree license (‘unfree’), refusing to evaluate. $ nix build nixpkgs#zoom-us --impure error: cached failure of attribute 'legacyPackages.x86_64-linux.zoom-us.drvPath' --- src/nix/installables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 86d3bfd20..f471319be 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -383,7 +383,7 @@ ref openEvalCache( bool useEvalCache) { return ref(std::make_shared( - useEvalCache, + useEvalCache && evalSettings.pureEval, lockedFlake->getFingerprint(), state, [&state, lockedFlake]() -- cgit v1.2.3 From 810b2c6a48b5ecd468bd4f65963ce5a7aa96832e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Jun 2020 20:02:50 +0200 Subject: nix flake init: Add a '--template' flag The initial contents of the flake is specified by the 'templates.' or 'defaultTemplate' output of another flake. E.g. outputs = { self }: { templates = { nixos-container = { path = ./nixos-container; description = "An example of a NixOS container"; }; }; }; allows $ nix flake init -t templates#nixos-container Also add a command 'nix flake new', which is identical to 'nix flake init' except that it initializes a specified directory rather than the current directory. --- src/nix/flake-template.nix | 11 --- src/nix/flake.cc | 192 +++++++++++++++++++++++++++++++++++++++++---- src/nix/installables.cc | 13 ++- src/nix/installables.hh | 5 +- src/nix/local.mk | 2 - src/nix/search.cc | 2 +- 6 files changed, 193 insertions(+), 32 deletions(-) delete mode 100644 src/nix/flake-template.nix (limited to 'src/nix') diff --git a/src/nix/flake-template.nix b/src/nix/flake-template.nix deleted file mode 100644 index 195aef2cc..000000000 --- a/src/nix/flake-template.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ - description = "A flake for building Hello World"; - - outputs = { self, nixpkgs }: { - - packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; - - defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello; - - }; -} diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 15a3e261a..439003908 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -207,6 +207,8 @@ struct CmdFlakeCheck : FlakeCommand auto state = getEvalState(); auto flake = lockFlake(); + // FIXME: rewrite to use EvalCache. + auto checkSystemName = [&](const std::string & system, const Pos & pos) { // FIXME: what's the format of "system"? if (system.find('-') == std::string::npos) @@ -320,6 +322,40 @@ struct CmdFlakeCheck : FlakeCommand } }; + auto checkTemplate = [&](const std::string & attrPath, Value & v, const Pos & pos) { + try { + Activity act(*logger, lvlChatty, actUnknown, + fmt("checking template '%s'", attrPath)); + + state->forceAttrs(v, pos); + + if (auto attr = v.attrs->get(state->symbols.create("path"))) { + if (attr->name == state->symbols.create("path")) { + PathSet context; + auto path = state->coerceToPath(*attr->pos, *attr->value, context); + if (!store->isInStore(path)) + throw Error("template '%s' has a bad 'path' attribute"); + // TODO: recursively check the flake in 'path'. + } + } else + throw Error("template '%s' lacks attribute 'path'", attrPath); + + if (auto attr = v.attrs->get(state->symbols.create("description"))) + state->forceStringNoCtx(*attr->value, *attr->pos); + else + throw Error("template '%s' lacks attribute 'description'", attrPath); + + for (auto & attr : *v.attrs) { + std::string name(attr.name); + if (name != "path" && name != "description") + throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); + } + } catch (Error & e) { + e.addPrefix(fmt("while checking the template '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); + throw; + } + }; + { Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); @@ -432,6 +468,16 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "hydraJobs") checkHydraJobs(name, vOutput, pos); + else if (name == "defaultTemplate") + checkTemplate(name, vOutput, pos); + + else if (name == "templates") { + state->forceAttrs(vOutput, pos); + for (auto & attr : *vOutput.attrs) + checkTemplate(fmt("%s.%s", name, attr.name), + *attr.value, *attr.pos); + } + else warn("unknown flake output '%s'", name); @@ -449,29 +495,135 @@ struct CmdFlakeCheck : FlakeCommand } }; -struct CmdFlakeInit : virtual Args, Command +struct CmdFlakeInitCommon : virtual Args, EvalCommand { - std::string description() override + std::string templateUrl = "templates"; + Path destDir; + + CmdFlakeInitCommon() { - return "create a skeleton 'flake.nix' file in the current directory"; + addFlag({ + .longName = "template", + .shortName = 't', + .description = "the template to use", + .labels = {"template"}, + .handler = {&templateUrl}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(prefix); + }} + }); } - void run() override + void run(nix::ref store) override + { + auto flakeDir = absPath(destDir); + + auto evalState = getEvalState(); + + auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath(".")); + + auto installable = InstallableFlake( + evalState, std::move(templateFlakeRef), + Strings{templateName == "" ? "defaultTemplate" : templateName}, + Strings{"templates."}, { .writeLockFile = false }); + + auto cursor = installable.getCursor(*evalState, true); + + auto templateDir = cursor.first->getAttr("path")->getString(); + + assert(store->isInStore(templateDir)); + + std::vector files; + + std::function copyDir; + copyDir = [&](const Path & from, const Path & to) + { + createDirs(to); + + for (auto & entry : readDirectory(from)) { + auto from2 = from + "/" + entry.name; + auto to2 = to + "/" + entry.name; + auto st = lstat(from2); + if (S_ISDIR(st.st_mode)) + copyDir(from2, to2); + else if (S_ISREG(st.st_mode)) { + auto contents = readFile(from2); + if (pathExists(to2)) { + auto contents2 = readFile(to2); + if (contents != contents2) + throw Error("refusing to overwrite existing file '%s'", to2); + } else + writeFile(to2, contents); + } + else if (S_ISLNK(st.st_mode)) { + auto target = readLink(from2); + if (pathExists(to2)) { + if (readLink(to2) != target) + throw Error("refusing to overwrite existing symlink '%s'", to2); + } else + createSymlink(target, to2); + } + else + throw Error("file '%s' has unsupported type", from2); + files.push_back(to2); + } + }; + + copyDir(templateDir, flakeDir); + + if (pathExists(flakeDir + "/.git")) { + Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--" }; + for (auto & s : files) args.push_back(s); + runProgram("git", true, args); + } + } +}; + +struct CmdFlakeInit : CmdFlakeInitCommon +{ + std::string description() override { - Path flakeDir = absPath("."); + return "create a flake in the current directory from a template"; + } - Path flakePath = flakeDir + "/flake.nix"; + Examples examples() override + { + return { + Example{ + "To create a flake using the default template:", + "nix flake init" + }, + Example{ + "To see available templates:", + "nix flake show templates" + }, + Example{ + "To create a flake from a specific template:", + "nix flake init -t templates#nixos-container" + }, + }; + } - if (pathExists(flakePath)) - throw Error("file '%s' already exists", flakePath); + CmdFlakeInit() + { + destDir = "."; + } +}; - writeFile(flakePath, - #include "flake-template.nix.gen.hh" - ); +struct CmdFlakeNew : CmdFlakeInitCommon +{ + std::string description() override + { + return "create a flake in the specified directory from a template"; + } - if (pathExists(flakeDir + "/.git")) - runProgram("git", true, - { "-C", flakeDir, "add", "--intent-to-add", "flake.nix" }); + CmdFlakeNew() + { + expectArgs({ + .label = "dest-dir", + .handler = {&destDir}, + .completer = completePath + }); } }; @@ -662,7 +814,8 @@ struct CmdFlakeShow : FlakeCommand || attrPath[0] == "devShell" || attrPath[0] == "nixosConfigurations" || attrPath[0] == "nixosModules" - || attrPath[0] == "defaultApp")) + || attrPath[0] == "defaultApp" + || attrPath[0] == "templates")) || ((attrPath.size() == 1 || attrPath.size() == 2) && (attrPath[0] == "checks" || attrPath[0] == "packages" @@ -714,6 +867,14 @@ struct CmdFlakeShow : FlakeCommand logger->stdout("%s: app", headerPrefix); } + else if ( + (attrPath.size() == 1 && attrPath[0] == "defaultTemplate") || + (attrPath.size() == 2 && attrPath[0] == "templates")) + { + auto description = visitor.getAttr("description")->getString(); + logger->stdout("%s: template: " ANSI_BOLD "%s" ANSI_NORMAL, headerPrefix, description); + } + else { logger->stdout("%s: %s", headerPrefix, @@ -743,6 +904,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command {"list-inputs", []() { return make_ref(); }}, {"check", []() { return make_ref(); }}, {"init", []() { return make_ref(); }}, + {"new", []() { return make_ref(); }}, {"clone", []() { return make_ref(); }}, {"archive", []() { return make_ref(); }}, {"show", []() { return make_ref(); }}, diff --git a/src/nix/installables.cc b/src/nix/installables.cc index f471319be..4b171dcba 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -237,7 +237,7 @@ App Installable::toApp(EvalState & state) } std::vector, std::string>> -Installable::getCursor(EvalState & state, bool useEvalCache) +Installable::getCursors(EvalState & state, bool useEvalCache) { auto evalCache = std::make_shared(false, Hash(), state, @@ -245,6 +245,15 @@ Installable::getCursor(EvalState & state, bool useEvalCache) return {{evalCache->getRoot(), ""}}; } +std::pair, std::string> +Installable::getCursor(EvalState & state, bool useEvalCache) +{ + auto cursors = getCursors(state, useEvalCache); + if (cursors.empty()) + throw Error("cannot find flake attribute '%s'", what()); + return cursors[0]; +} + struct InstallableStorePath : Installable { ref store; @@ -474,7 +483,7 @@ std::pair InstallableFlake::toValue(EvalState & state) } std::vector, std::string>> -InstallableFlake::getCursor(EvalState & state, bool useEvalCache) +InstallableFlake::getCursors(EvalState & state, bool useEvalCache) { auto evalCache = openEvalCache(state, std::make_shared(lockFlake(state, flakeRef, lockFlags)), diff --git a/src/nix/installables.hh b/src/nix/installables.hh index a2db71389..1e6623f88 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -56,6 +56,9 @@ struct Installable } virtual std::vector, std::string>> + getCursors(EvalState & state, bool useEvalCache); + + std::pair, std::string> getCursor(EvalState & state, bool useEvalCache); virtual FlakeRef nixpkgsFlakeRef() const @@ -109,7 +112,7 @@ struct InstallableFlake : InstallableValue std::pair toValue(EvalState & state) override; std::vector, std::string>> - getCursor(EvalState & state, bool useEvalCache) override; + getCursors(EvalState & state, bool useEvalCache) override; std::shared_ptr getLockedFlake() const; diff --git a/src/nix/local.mk b/src/nix/local.mk index 9ed55f1f6..43b7754e3 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -29,5 +29,3 @@ $(eval $(call install-symlink, $(bindir)/nix, $(libexecdir)/nix/build-remote)) src/nix-env/user-env.cc: src/nix-env/buildenv.nix.gen.hh src/nix/develop.cc: src/nix/get-env.sh.gen.hh - -$(d)/flake.cc: $(d)/flake-template.nix.gen.hh diff --git a/src/nix/search.cc b/src/nix/search.cc index bbac56fcb..65a1e1818 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -177,7 +177,7 @@ struct CmdSearch : InstallableCommand, MixJSON } }; - for (auto & [cursor, prefix] : installable->getCursor(*state, true)) + for (auto & [cursor, prefix] : installable->getCursors(*state, true)) visit(*cursor, parseAttrPath(*state, prefix)); if (!json && !results) -- cgit v1.2.3 From 488ff83e6b5b3120ce100b0f7b065280f2ce30c2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Jun 2020 14:09:12 +0200 Subject: Fix completion of --template --- src/nix/command.hh | 11 +++++++++-- src/nix/flake.cc | 14 +++++++++++--- src/nix/installables.cc | 39 +++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 19 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index faa19c8ea..fe8e40835 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -38,8 +38,6 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs ref getEvalState(); std::shared_ptr evalState; - - void completeFlakeRef(std::string_view prefix); }; struct MixFlakeOptions : virtual Args @@ -205,4 +203,13 @@ struct MixEnvironment : virtual Args { void setEnviron(); }; +void completeFlakeRef(ref store, std::string_view prefix); + +void completeFlakeRefWithFragment( + ref evalState, + flake::LockFlags lockFlags, + Strings attrPathPrefixes, + const Strings & defaultFlakeAttrPaths, + std::string_view prefix); + } diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 439003908..33bcc9604 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -33,7 +33,7 @@ public: .optional = true, .handler = {&flakeUrl}, .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(prefix); + completeFlakeRef(getStore(), prefix); }} }); } @@ -500,6 +500,9 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand std::string templateUrl = "templates"; Path destDir; + const Strings attrsPathPrefixes{"templates."}; + const LockFlags lockFlags{ .writeLockFile = false }; + CmdFlakeInitCommon() { addFlag({ @@ -509,7 +512,12 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand .labels = {"template"}, .handler = {&templateUrl}, .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(prefix); + completeFlakeRefWithFragment( + getEvalState(), + lockFlags, + attrsPathPrefixes, + {"defaultTemplate"}, + prefix); }} }); } @@ -525,7 +533,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand auto installable = InstallableFlake( evalState, std::move(templateFlakeRef), Strings{templateName == "" ? "defaultTemplate" : templateName}, - Strings{"templates."}, { .writeLockFile = false }); + Strings(attrsPathPrefixes), lockFlags); auto cursor = installable.getCursor(*evalState, true); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 4b171dcba..4b1e31000 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -111,6 +111,21 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) { if (file) return; // FIXME + completeFlakeRefWithFragment( + getEvalState(), + lockFlags, + getDefaultFlakeAttrPathPrefixes(), + getDefaultFlakeAttrPaths(), + prefix); +} + +void completeFlakeRefWithFragment( + ref evalState, + flake::LockFlags lockFlags, + Strings attrPathPrefixes, + const Strings & defaultFlakeAttrPaths, + std::string_view prefix) +{ /* Look for flake output attributes that match the prefix. */ try { @@ -121,10 +136,8 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) // FIXME: do tilde expansion. auto flakeRef = parseFlakeRef(flakeRefS, absPath(".")); - auto state = getEvalState(); - - auto evalCache = openEvalCache(*state, - std::make_shared(lockFlake(*state, flakeRef, lockFlags)), + auto evalCache = openEvalCache(*evalState, + std::make_shared(lockFlake(*evalState, flakeRef, lockFlags)), true); auto root = evalCache->getRoot(); @@ -132,13 +145,12 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) /* Complete 'fragment' relative to all the attrpath prefixes as well as the root of the flake. */ - auto attrPathPrefixes = getDefaultFlakeAttrPathPrefixes(); attrPathPrefixes.push_back(""); for (auto & attrPathPrefixS : attrPathPrefixes) { - auto attrPathPrefix = parseAttrPath(*state, attrPathPrefixS); + auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS); auto attrPathS = attrPathPrefixS + std::string(fragment); - auto attrPath = parseAttrPath(*state, attrPathS); + auto attrPath = parseAttrPath(*evalState, attrPathS); std::string lastAttr; if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) { @@ -149,8 +161,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) auto attr = root->findAlongAttrPath(attrPath); if (!attr) continue; - auto attrs = attr->getAttrs(); - for (auto & attr2 : attrs) { + for (auto & attr2 : attr->getAttrs()) { if (hasPrefix(attr2, lastAttr)) { auto attrPath2 = attr->getAttrPath(attr2); /* Strip the attrpath prefix. */ @@ -163,8 +174,8 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) /* And add an empty completion for the default attrpaths. */ if (fragment.empty()) { - for (auto & attrPath : getDefaultFlakeAttrPaths()) { - auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); + for (auto & attrPath : defaultFlakeAttrPaths) { + auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); if (!attr) continue; completions->insert(flakeRefS + "#"); } @@ -174,7 +185,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) warn(e.msg()); } - completeFlakeRef(prefix); + completeFlakeRef(evalState->store, prefix); } ref EvalCommand::getEvalState() @@ -184,7 +195,7 @@ ref EvalCommand::getEvalState() return ref(evalState); } -void EvalCommand::completeFlakeRef(std::string_view prefix) +void completeFlakeRef(ref store, std::string_view prefix) { if (prefix == "") completions->insert("."); @@ -192,7 +203,7 @@ void EvalCommand::completeFlakeRef(std::string_view prefix) completeDir(0, prefix); /* Look for registry entries that match the prefix. */ - for (auto & registry : fetchers::getRegistries(getStore())) { + for (auto & registry : fetchers::getRegistries(store)) { for (auto & entry : registry->entries) { auto from = entry.from.to_string(); if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { -- cgit v1.2.3 From 6470450ab48196668099eeca2f171fec110d647b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Jun 2020 16:20:00 +0200 Subject: Add completion for --update-input --- src/nix/command.hh | 14 ++++++++++++-- src/nix/flake.cc | 7 ++++++- src/nix/installables.cc | 25 +++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.hh b/src/nix/command.hh index fe8e40835..a8779b0e6 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -40,14 +40,17 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs std::shared_ptr evalState; }; -struct MixFlakeOptions : virtual Args +struct MixFlakeOptions : virtual Args, EvalCommand { flake::LockFlags lockFlags; MixFlakeOptions(); + + virtual std::optional getFlakeRefForCompletion() + { return {}; } }; -struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions +struct SourceExprCommand : virtual Args, MixFlakeOptions { std::optional file; std::optional expr; @@ -81,6 +84,8 @@ struct InstallablesCommand : virtual Args, SourceExprCommand virtual bool useDefaultInstallables() { return true; } + std::optional getFlakeRefForCompletion() override; + private: std::vector _installables; @@ -95,6 +100,11 @@ struct InstallableCommand : virtual Args, SourceExprCommand void prepare() override; + std::optional getFlakeRefForCompletion() override + { + return parseFlakeRef(_installable, absPath(".")); + } + private: std::string _installable{"."}; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 33bcc9604..57ce996cd 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -20,7 +20,7 @@ using namespace nix; using namespace nix::flake; -class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions +class FlakeCommand : virtual Args, public MixFlakeOptions { std::string flakeUrl = "."; @@ -53,6 +53,11 @@ public: { return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags); } + + std::optional getFlakeRefForCompletion() override + { + return getFlakeRef(); + } }; static void printFlakeInfo(const Store & store, const Flake & flake) diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 4b1e31000..583b9e021 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -18,6 +18,17 @@ namespace nix { +void completeFlakeInputPath( + ref evalState, + const FlakeRef & flakeRef, + std::string_view prefix) +{ + auto flake = flake::getFlake(*evalState, flakeRef, true); + for (auto & input : flake.inputs) + if (hasPrefix(input.first, prefix)) + completions->insert(input.first); +} + MixFlakeOptions::MixFlakeOptions() { addFlag({ @@ -56,6 +67,10 @@ MixFlakeOptions::MixFlakeOptions() .labels = {"input-path"}, .handler = {[&](std::string s) { lockFlags.inputUpdates.insert(flake::parseInputPath(s)); + }}, + .completer = {[&](size_t, std::string_view prefix) { + if (auto flakeRef = getFlakeRefForCompletion()) + completeFlakeInputPath(getEvalState(), *flakeRef, prefix); }} }); @@ -707,6 +722,16 @@ void InstallablesCommand::prepare() installables = parseInstallables(getStore(), _installables); } +std::optional InstallablesCommand::getFlakeRefForCompletion() +{ + if (_installables.empty()) { + if (useDefaultInstallables()) + return parseFlakeRef(".", absPath(".")); + return {}; + } + return parseFlakeRef(_installables.front(), absPath(".")); +} + InstallableCommand::InstallableCommand() { expectArgs({ -- cgit v1.2.3 From e073f2c584a42a2029e9196a01e9747ed46f6a0c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Jun 2020 16:23:54 +0200 Subject: nix flake: Require 'flakes' feature --- src/nix/flake.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 57ce996cd..865ac8cb8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -934,6 +934,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command { if (!command) throw UsageError("'nix flake' requires a sub-command."); + settings.requireExperimentalFeature("flakes"); command->second->prepare(); command->second->run(); } -- cgit v1.2.3 From 0c62b4ad0f80d2801a7e7caabf20cc8e50182540 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Jun 2020 14:40:21 +0200 Subject: Represent 'follows' inputs explicitly in the lock file This fixes an issue where lockfile generation was not idempotent: after updating a lockfile, a "follows" node would end up pointing to a new copy of the node, rather than to the original node. --- src/nix/flake.cc | 42 ++++++++++++++++++++++++------------------ src/nix/installables.cc | 5 ++--- 2 files changed, 26 insertions(+), 21 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 865ac8cb8..57c5478c3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -169,15 +169,21 @@ struct CmdFlakeListInputs : FlakeCommand, MixJSON recurse = [&](const Node & node, const std::string & prefix) { for (const auto & [i, input] : enumerate(node.inputs)) { - bool firstVisit = visited.insert(input.second).second; bool last = i + 1 == node.inputs.size(); - auto lockedNode = std::dynamic_pointer_cast(input.second); - logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", - prefix + (last ? treeLast : treeConn), input.first, - lockedNode ? lockedNode->lockedRef : flake.flake.lockedRef); + if (auto lockedNode = std::get_if<0>(&input.second)) { + logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", + prefix + (last ? treeLast : treeConn), input.first, + *lockedNode ? (*lockedNode)->lockedRef : flake.flake.lockedRef); - if (firstVisit) recurse(*input.second, prefix + (last ? treeNull : treeLine)); + bool firstVisit = visited.insert(*lockedNode).second; + + if (firstVisit) recurse(**lockedNode, prefix + (last ? treeNull : treeLine)); + } else if (auto follows = std::get_if<1>(&input.second)) { + logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL " follows input '%s'", + prefix + (last ? treeLast : treeConn), input.first, + printInputPath(*follows)); + } } }; @@ -723,18 +729,18 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun traverse = [&](const Node & node, std::optional & jsonObj) { auto jsonObj2 = jsonObj ? jsonObj->object("inputs") : std::optional(); - for (auto & input : node.inputs) { - auto lockedInput = std::dynamic_pointer_cast(input.second); - assert(lockedInput); - auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional(); - auto storePath = - dryRun - ? lockedInput->lockedRef.input.computeStorePath(*store) - : lockedInput->lockedRef.input.fetch(store).first.storePath; - if (jsonObj3) - jsonObj3->attr("path", store->printStorePath(storePath)); - sources.insert(std::move(storePath)); - traverse(*lockedInput, jsonObj3); + for (auto & [inputName, input] : node.inputs) { + if (auto inputNode = std::get_if<0>(&input)) { + auto jsonObj3 = jsonObj2 ? jsonObj2->object(inputName) : std::optional(); + auto storePath = + dryRun + ? (*inputNode)->lockedRef.input.computeStorePath(*store) + : (*inputNode)->lockedRef.input.fetch(store).first.storePath; + if (jsonObj3) + jsonObj3->attr("path", store->printStorePath(storePath)); + sources.insert(std::move(storePath)); + traverse(**inputNode, jsonObj3); + } } }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 583b9e021..d5d42ee57 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -538,9 +538,8 @@ FlakeRef InstallableFlake::nixpkgsFlakeRef() const { auto lockedFlake = getLockedFlake(); - auto nixpkgsInput = lockedFlake->lockFile.root->inputs.find("nixpkgs"); - if (nixpkgsInput != lockedFlake->lockFile.root->inputs.end()) { - if (auto lockedNode = std::dynamic_pointer_cast(nixpkgsInput->second)) { + if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) { + if (auto lockedNode = std::dynamic_pointer_cast(nixpkgsInput)) { debug("using nixpkgs flake '%s'", lockedNode->lockedRef); return std::move(lockedNode->lockedRef); } -- cgit v1.2.3 From 5332c439d008a51b578a1e255bce98040a697191 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jun 2020 17:13:01 +0200 Subject: InstallableFlake: Show all possible attribute names E.g. $ nix run nixpkgs#hello error: --- Error ---------- nix flake 'flake:nixpkgs' does not provide attribute 'apps.x86_64-linux.hello' or 'hello' instead of $ nix run nixpkgs#hello error: --- Error ---------- nix flake 'flake:nixpkgs' does not provide attribute 'hello' --- src/nix/installables.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index c41aa859c..01be68cdb 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -440,6 +440,16 @@ ref openEvalCache( })); } +static std::string showAttrPaths(const std::vector & paths) +{ + std::string s; + for (const auto & [n, i] : enumerate(paths)) { + if (n > 0) s += n + 1 == paths.size() ? " or " : ", "; + s += '\''; s += i; s += '\''; + } + return s; +} + std::tuple InstallableFlake::toDerivation() { @@ -477,7 +487,7 @@ std::tuple InstallableF } throw Error("flake '%s' does not provide attribute %s", - flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); + flakeRef, showAttrPaths(getActualAttrPaths())); } std::vector InstallableFlake::toDerivations() @@ -505,7 +515,7 @@ std::pair InstallableFlake::toValue(EvalState & state) } throw Error("flake '%s' does not provide attribute %s", - flakeRef, concatStringsSep(", ", quoteStrings(attrPaths))); + flakeRef, showAttrPaths(getActualAttrPaths())); } std::vector, std::string>> -- cgit v1.2.3 From 2e4bd782115c5842781db31f6e06c162f8afe780 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jun 2020 18:11:53 +0200 Subject: nix eval: Add --apply flag for post-processing the result --- src/nix/eval.cc | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'src/nix') diff --git a/src/nix/eval.cc b/src/nix/eval.cc index ea1798bbe..a8ca446be 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -12,10 +12,18 @@ using namespace nix; struct CmdEval : MixJSON, InstallableCommand { bool raw = false; + std::optional apply; CmdEval() { mkFlag(0, "raw", "print strings unquoted", &raw); + + addFlag({ + .longName = "apply", + .description = "apply a function to each argument", + .labels = {"expr"}, + .handler = {&apply}, + }); } std::string description() override @@ -26,21 +34,25 @@ struct CmdEval : MixJSON, InstallableCommand Examples examples() override { return { - Example{ + { "To evaluate a Nix expression given on the command line:", "nix eval --expr '1 + 2'" }, - Example{ + { "To evaluate a Nix expression from a file or URI:", - "nix eval -f channel:nixos-17.09 hello.name" + "nix eval -f ./my-nixpkgs hello.name" }, - Example{ + { "To get the current version of Nixpkgs:", - "nix eval --raw nixpkgs.lib.version" + "nix eval --raw nixpkgs#lib.version" }, - Example{ + { "To print the store path of the Hello package:", - "nix eval --raw nixpkgs.hello" + "nix eval --raw nixpkgs#hello" + }, + { + "To get a list of checks in the 'nix' flake:", + "nix eval nix#checks.x86_64-linux --apply builtins.attrNames" }, }; } @@ -57,6 +69,14 @@ struct CmdEval : MixJSON, InstallableCommand auto v = installable->toValue(*state).first; PathSet context; + if (apply) { + auto vApply = state->allocValue(); + state->eval(state->parseExprFromString(*apply, absPath(".")), *vApply); + auto vRes = state->allocValue(); + state->callFunction(*vApply, *v, *vRes, noPos); + v = vRes; + } + if (raw) { stopProgressBar(); std::cout << state->coerceToString(noPos, *v, context); -- cgit v1.2.3 From 334e26bfc2ce82912602e8a0f9f9c7e0fb5c3221 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jun 2020 11:31:07 +0200 Subject: nix flake check: Don't build apps This was inconsistent since we're not building 'packages' or 'defaultPackage' either. Closes #3726. --- src/nix/flake.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 323810090..133e6c7e0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -246,9 +246,7 @@ struct CmdFlakeCheck : FlakeCommand auto app = App(*state, v); for (auto & i : app.context) { auto [drvPathS, outputName] = decodeContext(i); - auto drvPath = store->parseStorePath(drvPathS); - if (!outputName.empty() && drvPath.isDerivation()) - drvPaths.push_back({drvPath}); + store->parseStorePath(drvPathS); } } catch (Error & e) { e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); -- cgit v1.2.3 From 09fc06daab280735dd2ec94276f00a9c5bffd9b2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Jun 2020 16:25:32 +0200 Subject: nix flake init: Use git add --force --- src/nix/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 133e6c7e0..17df29fdb 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -589,7 +589,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand copyDir(templateDir, flakeDir); if (pathExists(flakeDir + "/.git")) { - Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--" }; + Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" }; for (auto & s : files) args.push_back(s); runProgram("git", true, args); } -- cgit v1.2.3 From bc03c6f23dbb2d5491410075f805946d4cc62ca2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jun 2020 14:14:23 +0200 Subject: Move App --- src/nix/app.cc | 28 ++++++++++++++++++++++++++++ src/nix/installables.cc | 21 --------------------- 2 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 src/nix/app.cc (limited to 'src/nix') diff --git a/src/nix/app.cc b/src/nix/app.cc new file mode 100644 index 000000000..749dea02b --- /dev/null +++ b/src/nix/app.cc @@ -0,0 +1,28 @@ +#include "installables.hh" +#include "store-api.hh" +#include "eval-inline.hh" + +namespace nix { + +App::App(EvalState & state, Value & vApp) +{ + state.forceAttrs(vApp); + + auto aType = vApp.attrs->need(state.sType); + if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app") + throw Error("value does not have type 'app', at %s", *aType.pos); + + auto aProgram = vApp.attrs->need(state.symbols.create("program")); + program = state.forceString(*aProgram.value, context, *aProgram.pos); + + // FIXME: check that 'program' is in the closure of 'context'. + if (!state.store->isInStore(program)) + throw Error("app program '%s' is not in the Nix store", program); +} + +App Installable::toApp(EvalState & state) +{ + return App(state, *toValue(state).first); +} + +} diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 01be68cdb..fb79fad5d 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -241,27 +241,6 @@ Buildable Installable::toBuildable() return std::move(buildables[0]); } -App::App(EvalState & state, Value & vApp) -{ - state.forceAttrs(vApp); - - auto aType = vApp.attrs->need(state.sType); - if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app") - throw Error("value does not have type 'app', at %s", *aType.pos); - - auto aProgram = vApp.attrs->need(state.symbols.create("program")); - program = state.forceString(*aProgram.value, context, *aProgram.pos); - - // FIXME: check that 'program' is in the closure of 'context'. - if (!state.store->isInStore(program)) - throw Error("app program '%s' is not in the Nix store", program); -} - -App Installable::toApp(EvalState & state) -{ - return App(state, *toValue(state).first); -} - std::vector, std::string>> Installable::getCursors(EvalState & state, bool useEvalCache) { -- cgit v1.2.3 From ca946860ce6ce5d4800b0d93d3f83c30d3c953c0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jun 2020 14:37:22 +0200 Subject: Fix bash completion --- src/nix/main.cc | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/main.cc b/src/nix/main.cc index 2ad748289..e62657e95 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -7,7 +7,6 @@ #include "legacy.hh" #include "shared.hh" #include "store-api.hh" -#include "progress-bar.hh" #include "filetransfer.hh" #include "finally.hh" #include "loggers.hh" -- cgit v1.2.3 From b681408879c9ad1e500fa6e4566c6d119def4271 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jun 2020 16:39:41 +0200 Subject: Factor out EvalCache::forceDerivation() --- src/nix/installables.cc | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index fb79fad5d..3683ab945 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -444,17 +444,7 @@ std::tuple InstallableF if (!attr->isDerivation()) throw Error("flake output attribute '%s' is not a derivation", attrPath); - auto aDrvPath = attr->getAttr(state->sDrvPath); - auto drvPath = state->store->parseStorePath(aDrvPath->getString()); - if (!state->store->isValidPath(drvPath) && !settings.readOnlyMode) { - /* The eval cache contains 'drvPath', but the actual path - has been garbage-collected. So force it to be - regenerated. */ - aDrvPath->forceValue(); - if (!state->store->isValidPath(drvPath)) - throw Error("don't know how to recreate store derivation '%s'!", - state->store->printStorePath(drvPath)); - } + auto drvPath = attr->forceDerivation(); auto drvInfo = DerivationInfo{ std::move(drvPath), -- cgit v1.2.3 From 26cf0c674f543ec86a5ccb7e05a4773559bb8f8a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jun 2020 19:08:50 +0200 Subject: nix run: Use packages/legacyPackages as fallback if there is no app definition 'nix run' will try to run $out/bin/, where is the derivation name (excluding the version). This often works well: $ nix run nixpkgs#hello Hello, world! $ nix run nix -- --version nix (Nix) 2.4pre20200626_adf2fbb $ nix run patchelf -- --version patchelf 0.11.20200623.e61654b $ nix run nixpkgs#firefox -- --version Mozilla Firefox 77.0.1 $ nix run nixpkgs#gimp -- --version GNU Image Manipulation Program version 2.10.14 though not always: $ nix run nixpkgs#git error: unable to execute '/nix/store/kp7wp760l4gryq9s36x481b2x4rfklcy-git-2.25.4/bin/git-minimal': No such file or directory --- src/nix/app.cc | 46 ++++++++++++++++++++++++++++++++-------------- src/nix/flake.cc | 7 +++++-- src/nix/installables.hh | 4 +--- src/nix/run.cc | 12 +++++++++--- 4 files changed, 47 insertions(+), 22 deletions(-) (limited to 'src/nix') diff --git a/src/nix/app.cc b/src/nix/app.cc index 749dea02b..3935297cf 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -1,28 +1,46 @@ #include "installables.hh" #include "store-api.hh" #include "eval-inline.hh" +#include "eval-cache.hh" +#include "names.hh" namespace nix { -App::App(EvalState & state, Value & vApp) +App Installable::toApp(EvalState & state) { - state.forceAttrs(vApp); + auto [cursor, attrPath] = getCursor(state, true); - auto aType = vApp.attrs->need(state.sType); - if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app") - throw Error("value does not have type 'app', at %s", *aType.pos); + auto type = cursor->getAttr("type")->getString(); - auto aProgram = vApp.attrs->need(state.symbols.create("program")); - program = state.forceString(*aProgram.value, context, *aProgram.pos); + if (type == "app") { + auto [program, context] = cursor->getAttr("program")->getStringWithContext(); - // FIXME: check that 'program' is in the closure of 'context'. - if (!state.store->isInStore(program)) - throw Error("app program '%s' is not in the Nix store", program); -} + if (!state.store->isInStore(program)) + throw Error("app program '%s' is not in the Nix store", program); -App Installable::toApp(EvalState & state) -{ - return App(state, *toValue(state).first); + std::vector context2; + for (auto & [path, name] : context) + context2.push_back({state.store->parseStorePath(path), {name}}); + + return App { + .context = std::move(context2), + .program = program, + }; + } + + else if (type == "derivation") { + auto drvPath = cursor->forceDerivation(); + auto outPath = cursor->getAttr(state.sOutPath)->getString(); + auto outputName = cursor->getAttr(state.sOutputName)->getString(); + auto name = cursor->getAttr(state.sName)->getString(); + return App { + .context = { { drvPath, {outputName} } }, + .program = outPath + "/bin/" + DrvName(name).name, + }; + } + + else + throw Error("attribute '%s' has unsupported type '%s'", attrPath, type); } } diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 17df29fdb..847985ea3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -243,11 +243,14 @@ struct CmdFlakeCheck : FlakeCommand auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { + #if 0 + // FIXME auto app = App(*state, v); for (auto & i : app.context) { auto [drvPathS, outputName] = decodeContext(i); store->parseStorePath(drvPathS); } + #endif } catch (Error & e) { e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos)); throw; @@ -544,9 +547,9 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand Strings{templateName == "" ? "defaultTemplate" : templateName}, Strings(attrsPathPrefixes), lockFlags); - auto cursor = installable.getCursor(*evalState, true); + auto [cursor, attrPath] = installable.getCursor(*evalState, true); - auto templateDir = cursor.first->getAttr("path")->getString(); + auto templateDir = cursor->getAttr("path")->getString(); assert(store->isInStore(templateDir)); diff --git a/src/nix/installables.hh b/src/nix/installables.hh index 1e6623f88..eb34365d4 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -24,11 +24,9 @@ typedef std::vector Buildables; struct App { - PathSet context; + std::vector context; Path program; // FIXME: add args, sandbox settings, metadata, ... - - App(EvalState & state, Value & vApp); }; struct Installable diff --git a/src/nix/run.cc b/src/nix/run.cc index 204937cbc..59a6c7252 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -172,12 +172,18 @@ struct CmdRun : InstallableCommand, RunCommon Strings getDefaultFlakeAttrPaths() override { - return {"defaultApp." + settings.thisSystem.get()}; + Strings res{"defaultApp." + settings.thisSystem.get()}; + for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths()) + res.push_back(s); + return res; } Strings getDefaultFlakeAttrPathPrefixes() override { - return {"apps." + settings.thisSystem.get() + "."}; + Strings res{"apps." + settings.thisSystem.get() + ".", "packages"}; + for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes()) + res.push_back(s); + return res; } void run(ref store) override @@ -186,7 +192,7 @@ struct CmdRun : InstallableCommand, RunCommon auto app = installable->toApp(*state); - state->realiseContext(app.context); + state->store->buildPaths(app.context); Strings allArgs{app.program}; for (auto & i : args) allArgs.push_back(i); -- cgit v1.2.3 From d746503e5c6fafd0fb6b2a4e6527c12cfc626637 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 1 Jul 2020 20:23:39 +0200 Subject: Add --inputs-from to use flake inputs as registry entries This allows you to refer to an input from another flake. For example, $ nix run --inputs-from /path/to/hydra nixpkgs#hello runs 'hello' from the 'nixpkgs' inputs of the 'hydra' flake. Fixes #3769. --- src/nix/installables.cc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 3683ab945..adfd14a75 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -84,6 +84,31 @@ MixFlakeOptions::MixFlakeOptions() parseFlakeRef(flakeRef, absPath("."))); }} }); + + addFlag({ + .longName = "inputs-from", + .description = "use the inputs of the specified flake as registry entries", + .labels = {"flake-url"}, + .handler = {[&](std::string flakeRef) { + auto evalState = getEvalState(); + auto flake = flake::lockFlake( + *evalState, + parseFlakeRef(flakeRef, absPath(".")), + { .writeLockFile = false }); + for (auto & [inputName, input] : flake.lockFile.root->inputs) { + auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes + if (auto input3 = std::dynamic_pointer_cast(input2)) { + overrideRegistry( + fetchers::Input::fromAttrs({{"type","indirect"}, {"id", inputName}}), + input3->lockedRef.input, + {}); + } + } + }}, + .completer = {[&](size_t, std::string_view prefix) { + completeFlakeRef(getEvalState()->store, prefix); + }} + }); } SourceExprCommand::SourceExprCommand() -- cgit v1.2.3 From cd8eb8a7d1bd4d2405df9f0d6aeeaa3e24c7d593 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 Jul 2020 17:08:54 +0200 Subject: nix develop: Fall back to "bash" if nixpkgs#bashInteractive is unavailable --- src/nix/develop.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/nix') diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 0bd862daa..ae6a23346 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -307,9 +307,23 @@ struct CmdDevelop : Common, MixEnvironment // prevent garbage collection until shell exits setenv("NIX_GCROOT", gcroot.data(), 1); - auto state = getEvalState(); - auto bashInstallable = std::make_shared(state, std::move(installable->nixpkgsFlakeRef()), Strings{"bashInteractive"}, Strings{"legacyPackages." + settings.thisSystem.get() + "."}, lockFlags); - auto shell = state->store->printStorePath(toStorePath(state->store, Build, bashInstallable)) + "/bin/bash"; + Path shell = "bash"; + + try { + auto state = getEvalState(); + + auto bashInstallable = std::make_shared( + state, + std::move(installable->nixpkgsFlakeRef()), + Strings{"bashInteractive"}, + Strings{"legacyPackages." + settings.thisSystem.get() + "."}, + lockFlags); + + shell = state->store->printStorePath(toStorePath(state->store, Build, bashInstallable)) + "/bin/bash"; + } catch (Error &) { + ignoreException(); + } + auto args = Strings{std::string(baseNameOf(shell)), "--rcfile", rcFilePath}; restoreAffinity(); -- cgit v1.2.3 From 68f524d717bb53e5faee8c7ff0a1f9d18dccbead Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 Jul 2020 18:34:58 +0200 Subject: nix develop: Support derivations with multiple outputs --- src/nix/develop.cc | 2 ++ src/nix/get-env.sh | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'src/nix') diff --git a/src/nix/develop.cc b/src/nix/develop.cc index ae6a23346..171eeeb3c 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -130,7 +130,9 @@ StorePath getDerivationEnvironment(ref store, const StorePath & drvPath) drvName += "-env"; for (auto & output : drv.outputs) drv.env.erase(output.first); + drv.outputs = {{"out", DerivationOutput { .path = StorePath::dummy }}}; drv.env["out"] = ""; + drv.env["_outputs_saved"] = drv.env["outputs"]; drv.env["outputs"] = "out"; drv.inputSrcs.insert(std::move(getEnvShPath)); Hash h = hashDerivationModulo(*store, drv, true); diff --git a/src/nix/get-env.sh b/src/nix/get-env.sh index a25ec43a9..2e0e83561 100644 --- a/src/nix/get-env.sh +++ b/src/nix/get-env.sh @@ -1,9 +1,18 @@ set -e if [ -e .attrs.sh ]; then source .attrs.sh; fi + +outputs=$_outputs_saved +for __output in $_outputs_saved; do + declare "$__output"="$out" +done +unset _outputs_saved __output + export IN_NIX_SHELL=impure export dontAddDisableDepTrack=1 + if [[ -n $stdenv ]]; then source $stdenv/setup fi + export > $out set >> $out -- cgit v1.2.3 From 0356f14459f1fbb855fd5ec75424c2a4c84c1000 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Jul 2020 16:18:06 +0200 Subject: Add 'nix diff-closures' command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This command makes it easier to see what changed between two closures, i.e. what packages/versions got added or removed, and whether there were any notable changes in path size. For example: $ nix diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link blender-bin: 2.83.0 → 2.83.2, -294.2 KiB curl: 7.68.0 → 7.70.0, +19.1 KiB firmware-linux-nonfree: 2020-01-22 → 2020-05-19, +30827.7 KiB ibus: -21.8 KiB initrd-linux: 5.4.46 → 5.4.51, +16.9 KiB libexif: 0.6.21 → 0.6.22, +497.6 KiB linux: 5.4.46 → 5.4.51, +13.2 KiB mesa: 19.3.3 → 19.3.5, -183.9 KiB nix: 2.4pre20200701_6ff9aa8 → 2.4pre20200708_9223603, +9.7 KiB nix-bash-completions: 0.6.8 → ∅, -57.6 KiB nixos-system-hagbard: 20.03.20200615.a84b797 → 20.03.20200713.add5529 nvidia-persistenced: 440.82 → 440.100 nvidia-settings: 440.82 → 440.100 nvidia-x11: 440.82-5.4.46 → 440.100-5.4.51, +664.7 KiB pcre: 8.43 → 8.44 php: 7.3.16 → 7.3.20, -26.2 KiB python3.7-youtube-dl: 2020.06.06 → 2020.06.16.1, +8.4 KiB samba: 4.11.5 → 4.11.9, +30.1 KiB sane-backends: 1.0.28 → 1.0.30, +680.5 KiB source: -182.0 KiB zfs-kernel: 0.8.3-5.4.46 → 0.8.4-5.4.51, +9.9 KiB zfs-user: 0.8.3 → 0.8.4, +20.1 KiB --- src/nix/diff-closures.cc | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/nix/diff-closures.cc (limited to 'src/nix') diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc new file mode 100644 index 000000000..df0b26ab1 --- /dev/null +++ b/src/nix/diff-closures.cc @@ -0,0 +1,134 @@ +#include "command.hh" +#include "shared.hh" +#include "store-api.hh" +#include "common-args.hh" +#include "names.hh" + +#include + +using namespace nix; + +struct Info +{ + std::string outputName; +}; + +// name -> version -> store paths +typedef std::map>> GroupedPaths; + +GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) +{ + StorePathSet closure; + store->computeFSClosure({toplevel}, closure); + + GroupedPaths groupedPaths; + + for (auto & path : closure) { + /* Strip the output name. Unfortunately this is ambiguous (we + can't distinguish between output names like "bin" and + version suffixes like "unstable"). */ + static std::regex regex("(.*)-([a-z]+|lib32|lib64)"); + std::smatch match; + std::string name(path.name()); + std::string outputName; + if (std::regex_match(name, match, regex)) { + name = match[1]; + outputName = match[2]; + } + + DrvName drvName(name); + groupedPaths[drvName.name][drvName.version].emplace(path, Info { .outputName = outputName }); + } + + return groupedPaths; +} + +std::string showVersions(const std::set & versions) +{ + if (versions.empty()) return "∅"; + std::set versions2; + for (auto & version : versions) + versions2.insert(version.empty() ? "ε" : version); + return concatStringsSep(", ", versions2); +} + +struct CmdDiffClosures : SourceExprCommand +{ + std::string _before, _after; + + CmdDiffClosures() + { + expectArg("before", &_before); + expectArg("after", &_after); + } + + std::string description() override + { + return "show what packages and versions were added and removed between two closures"; + } + + Category category() override { return catSecondary; } + + Examples examples() override + { + return { + { + "To show what got added and removed between two versions of the NixOS system profile:", + "nix diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link", + }, + }; + } + + void run(ref store) override + { + auto before = parseInstallable(*this, store, _before, false); + auto beforePath = toStorePath(store, Build, before); + auto after = parseInstallable(*this, store, _after, false); + auto afterPath = toStorePath(store, NoBuild, after); + + auto beforeClosure = getClosureInfo(store, beforePath); + auto afterClosure = getClosureInfo(store, afterPath); + + std::set allNames; + for (auto & [name, _] : beforeClosure) allNames.insert(name); + for (auto & [name, _] : afterClosure) allNames.insert(name); + + for (auto & name : allNames) { + auto & beforeVersions = beforeClosure[name]; + auto & afterVersions = afterClosure[name]; + + auto totalSize = [&](const std::map> & versions) + { + uint64_t sum = 0; + for (auto & [_, paths] : versions) + for (auto & [path, _] : paths) + sum += store->queryPathInfo(path)->narSize; + return sum; + }; + + auto beforeSize = totalSize(beforeVersions); + auto afterSize = totalSize(afterVersions); + auto sizeDelta = (int64_t) afterSize - (int64_t) beforeSize; + auto showDelta = abs(sizeDelta) >= 8 * 1024; + + std::set removed, unchanged; + for (auto & [version, _] : beforeVersions) + if (!afterVersions.count(version)) removed.insert(version); else unchanged.insert(version); + + std::set added; + for (auto & [version, _] : afterVersions) + if (!beforeVersions.count(version)) added.insert(version); + + if (showDelta || !removed.empty() || !added.empty()) { + std::vector items; + if (!removed.empty() || !added.empty()) + items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added))); + if (showDelta) + items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0)); + std::cout << fmt("%s: %s\n", name, concatStringsSep(", ", items)); + } + } + } +}; + +static auto r1 = registerCommand("diff-closures"); -- cgit v1.2.3 From e3c2b0023774eed05657ca1dbb469a85bd294d23 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Jul 2020 19:50:32 +0200 Subject: Make InstallableStorePath behave consistently with InstallableValue That is, the commands 'nix path-info nixpkgs#hello' and 'nix path-info /nix/store/00ls0qi49qkqpqblmvz5s1ajl3gc63lr-hello-2.10.drv' now do the same thing (i.e. build the derivation and operate on the output store path, rather than the .drv path). --- src/nix/installables.cc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'src/nix') diff --git a/src/nix/installables.cc b/src/nix/installables.cc index c97a0bdcf..415358970 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -296,15 +296,24 @@ struct InstallableStorePath : Installable Buildables toBuildables() override { - std::map outputs; - outputs.insert_or_assign("out", storePath); - Buildable b{ - .drvPath = storePath.isDerivation() ? storePath : std::optional(), - .outputs = std::move(outputs) - }; - Buildables bs; - bs.push_back(std::move(b)); - return bs; + if (storePath.isDerivation()) { + std::map outputs; + for (auto & [name, output] : store->readDerivation(storePath).outputs) + outputs.emplace(name, output.path); + return { + Buildable { + .drvPath = storePath, + .outputs = std::move(outputs) + } + }; + } else { + return { + Buildable { + .drvPath = {}, + .outputs = {{"out", storePath}} + } + }; + } } std::optional getStorePath() override -- cgit v1.2.3 From 94eb5fad76cd086e3f49329532f81083726f89b3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Jul 2020 20:05:42 +0200 Subject: Clean up RealiseMode --- src/nix/build.cc | 2 +- src/nix/command.cc | 2 +- src/nix/command.hh | 19 ++++++++++++++----- src/nix/copy.cc | 2 +- src/nix/develop.cc | 2 +- src/nix/installables.cc | 12 ++++++------ src/nix/make-content-addressable.cc | 2 +- src/nix/run.cc | 2 +- src/nix/why-depends.cc | 4 ++-- 9 files changed, 28 insertions(+), 19 deletions(-) (limited to 'src/nix') diff --git a/src/nix/build.cc b/src/nix/build.cc index 474337208..0f7e0e123 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -53,7 +53,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixProfile void run(ref store) override { - auto buildables = build(store, dryRun ? DryRun : Build, installables); + auto buildables = build(store, dryRun ? Realise::Nothing : Realise::Outputs, installables); if (dryRun) return; diff --git a/src/nix/command.cc b/src/nix/command.cc index dbf5e0988..a4faccb6d 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -80,7 +80,7 @@ void StorePathsCommand::run(ref store) void StorePathCommand::run(ref store) { - auto storePaths = toStorePaths(store, NoBuild, installables); + auto storePaths = toStorePaths(store, Realise::Nothing, installables); if (storePaths.size() != 1) throw UsageError("this command requires exactly one store path"); diff --git a/src/nix/command.hh b/src/nix/command.hh index a8779b0e6..5fa8f9333 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -70,7 +70,16 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions void completeInstallable(std::string_view prefix); }; -enum RealiseMode { Build, NoBuild, DryRun }; +enum class Realise { + /* Build the derivation. Postcondition: the + derivation outputs exist. */ + Outputs, + /* Don't build the derivation. Postcondition: the store derivation + exists. */ + Derivation, + /* Evaluate in dry-run mode. Postcondition: nothing. */ + Nothing +}; /* A command that operates on a list of "installables", which can be store paths, attribute paths, Nix expressions, etc. */ @@ -120,7 +129,7 @@ private: protected: - RealiseMode realiseMode = NoBuild; + Realise realiseMode = Realise::Derivation; public: @@ -164,13 +173,13 @@ static RegisterCommand registerCommand(const std::string & name) return RegisterCommand(name, [](){ return make_ref(); }); } -Buildables build(ref store, RealiseMode mode, +Buildables build(ref store, Realise mode, std::vector> installables); -std::set toStorePaths(ref store, RealiseMode mode, +std::set toStorePaths(ref store, Realise mode, std::vector> installables); -StorePath toStorePath(ref store, RealiseMode mode, +StorePath toStorePath(ref store, Realise mode, std::shared_ptr installable); std::set toDerivations(ref store, diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 815e653b0..5281ff5ee 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -46,7 +46,7 @@ struct CmdCopy : StorePathsCommand .handler = {&substitute, Substitute}, }); - realiseMode = Build; + realiseMode = Realise::Outputs; } std::string description() override diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 171eeeb3c..bf4890e95 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -321,7 +321,7 @@ struct CmdDevelop : Common, MixEnvironment Strings{"legacyPackages." + settings.thisSystem.get() + "."}, lockFlags); - shell = state->store->printStorePath(toStorePath(state->store, Build, bashInstallable)) + "/bin/bash"; + shell = state->store->printStorePath(toStorePath(state->store, Realise::Outputs, bashInstallable)) + "/bin/bash"; } catch (Error &) { ignoreException(); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 415358970..d464efc35 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -635,10 +635,10 @@ std::shared_ptr SourceExprCommand::parseInstallable( return installables.front(); } -Buildables build(ref store, RealiseMode mode, +Buildables build(ref store, Realise mode, std::vector> installables) { - if (mode != Build) + if (mode == Realise::Nothing) settings.readOnlyMode = true; Buildables buildables; @@ -659,15 +659,15 @@ Buildables build(ref store, RealiseMode mode, } } - if (mode == DryRun) + if (mode == Realise::Nothing) printMissing(store, pathsToBuild, lvlError); - else if (mode == Build) + else if (mode == Realise::Outputs) store->buildPaths(pathsToBuild); return buildables; } -StorePathSet toStorePaths(ref store, RealiseMode mode, +StorePathSet toStorePaths(ref store, Realise mode, std::vector> installables) { StorePathSet outPaths; @@ -679,7 +679,7 @@ StorePathSet toStorePaths(ref store, RealiseMode mode, return outPaths; } -StorePath toStorePath(ref store, RealiseMode mode, +StorePath toStorePath(ref store, Realise mode, std::shared_ptr installable) { auto paths = toStorePaths(store, mode, {installable}); diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc index fb36fc410..b2ded35dd 100644 --- a/src/nix/make-content-addressable.cc +++ b/src/nix/make-content-addressable.cc @@ -10,7 +10,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON { CmdMakeContentAddressable() { - realiseMode = Build; + realiseMode = Realise::Outputs; } std::string description() override diff --git a/src/nix/run.cc b/src/nix/run.cc index 59a6c7252..a27538085 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -104,7 +104,7 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment void run(ref store) override { - auto outPaths = toStorePaths(store, Build, installables); + auto outPaths = toStorePaths(store, Realise::Outputs, installables); auto accessor = store->getFSAccessor(); diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index a208e0081..c39a0435d 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -73,9 +73,9 @@ struct CmdWhyDepends : SourceExprCommand void run(ref store) override { auto package = parseInstallable(store, _package); - auto packagePath = toStorePath(store, Build, package); + auto packagePath = toStorePath(store, Realise::Outputs, package); auto dependency = parseInstallable(store, _dependency); - auto dependencyPath = toStorePath(store, NoBuild, dependency); + auto dependencyPath = toStorePath(store, Realise::Derivation, dependency); auto dependencyPathHash = dependencyPath.hashPart(); StorePathSet closure; -- cgit v1.2.3 From dfe8f3ebc6c5a3d96528794b8822536aac718046 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Jul 2020 20:09:50 +0200 Subject: nix why-depends: Fix misleading message --- src/nix/why-depends.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nix') diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index c39a0435d..55bb43f95 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -82,7 +82,9 @@ struct CmdWhyDepends : SourceExprCommand store->computeFSClosure({packagePath}, closure, false, false); if (!closure.count(dependencyPath)) { - printError("'%s' does not depend on '%s'", package->what(), dependency->what()); + printError("'%s' does not depend on '%s'", + store->printStorePath(packagePath), + store->printStorePath(dependencyPath)); return; } -- cgit v1.2.3 From 3624c042ace05db88794e87ee37f3296bab19bc8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Jul 2020 20:22:52 +0200 Subject: nix: Add --derivation flag to operate on .drv paths For instance, 'nix why-depends --use-derivation nixpkgs#hello nixpkgs#glibc' shows why hello's .drv depends on glibc's .drv. --- src/nix/command.cc | 4 ++-- src/nix/command.hh | 17 +++++++++++++++-- src/nix/develop.cc | 3 ++- src/nix/installables.cc | 30 ++++++++++++++++++++++++------ src/nix/run.cc | 2 +- src/nix/why-depends.cc | 4 ++-- 6 files changed, 46 insertions(+), 14 deletions(-) (limited to 'src/nix') diff --git a/src/nix/command.cc b/src/nix/command.cc index a4faccb6d..af36dda89 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -63,7 +63,7 @@ void StorePathsCommand::run(ref store) } else { - for (auto & p : toStorePaths(store, realiseMode, installables)) + for (auto & p : toStorePaths(store, realiseMode, operateOn, installables)) storePaths.push_back(p); if (recursive) { @@ -80,7 +80,7 @@ void StorePathsCommand::run(ref store) void StorePathCommand::run(ref store) { - auto storePaths = toStorePaths(store, Realise::Nothing, installables); + auto storePaths = toStorePaths(store, Realise::Nothing, operateOn, installables); if (storePaths.size() != 1) throw UsageError("this command requires exactly one store path"); diff --git a/src/nix/command.hh b/src/nix/command.hh index 5fa8f9333..1c7413300 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -50,11 +50,22 @@ struct MixFlakeOptions : virtual Args, EvalCommand { return {}; } }; +/* How to handle derivations in commands that operate on store paths. */ +enum class OperateOn { + /* Operate on the output path. */ + Output, + /* Operate on the .drv path. */ + Derivation +}; + struct SourceExprCommand : virtual Args, MixFlakeOptions { std::optional file; std::optional expr; + // FIXME: move this; not all commands (e.g. 'nix run') use it. + OperateOn operateOn = OperateOn::Output; + SourceExprCommand(); std::vector> parseInstallables( @@ -176,10 +187,12 @@ static RegisterCommand registerCommand(const std::string & name) Buildables build(ref store, Realise mode, std::vector> installables); -std::set toStorePaths(ref store, Realise mode, +std::set toStorePaths(ref store, + Realise mode, OperateOn operateOn, std::vector> installables); -StorePath toStorePath(ref store, Realise mode, +StorePath toStorePath(ref store, + Realise mode, OperateOn operateOn, std::shared_ptr installable); std::set toDerivations(ref store, diff --git a/src/nix/develop.cc b/src/nix/develop.cc index bf4890e95..a6d7d6add 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -321,7 +321,8 @@ struct CmdDevelop : Common, MixEnvironment Strings{"legacyPackages." + settings.thisSystem.get() + "."}, lockFlags); - shell = state->store->printStorePath(toStorePath(state->store, Realise::Outputs, bashInstallable)) + "/bin/bash"; + shell = state->store->printStorePath( + toStorePath(state->store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash"; } catch (Error &) { ignoreException(); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index d464efc35..a13e5a3df 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -128,6 +128,12 @@ SourceExprCommand::SourceExprCommand() .labels = {"expr"}, .handler = {&expr} }); + + addFlag({ + .longName ="derivation", + .description = "operate on the store derivation rather than its outputs", + .handler = {&operateOn, OperateOn::Derivation}, + }); } Strings SourceExprCommand::getDefaultFlakeAttrPaths() @@ -667,22 +673,34 @@ Buildables build(ref store, Realise mode, return buildables; } -StorePathSet toStorePaths(ref store, Realise mode, +StorePathSet toStorePaths(ref store, + Realise mode, OperateOn operateOn, std::vector> installables) { StorePathSet outPaths; - for (auto & b : build(store, mode, installables)) - for (auto & output : b.outputs) - outPaths.insert(output.second); + if (operateOn == OperateOn::Output) { + for (auto & b : build(store, mode, installables)) + for (auto & output : b.outputs) + outPaths.insert(output.second); + } else { + if (mode == Realise::Nothing) + settings.readOnlyMode = true; + + for (auto & i : installables) + for (auto & b : i->toBuildables()) + if (b.drvPath) + outPaths.insert(*b.drvPath); + } return outPaths; } -StorePath toStorePath(ref store, Realise mode, +StorePath toStorePath(ref store, + Realise mode, OperateOn operateOn, std::shared_ptr installable) { - auto paths = toStorePaths(store, mode, {installable}); + auto paths = toStorePaths(store, mode, operateOn, {installable}); if (paths.size() != 1) throw Error("argument '%s' should evaluate to one store path", installable->what()); diff --git a/src/nix/run.cc b/src/nix/run.cc index a27538085..cbaba9d90 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -104,7 +104,7 @@ struct CmdShell : InstallablesCommand, RunCommon, MixEnvironment void run(ref store) override { - auto outPaths = toStorePaths(store, Realise::Outputs, installables); + auto outPaths = toStorePaths(store, Realise::Outputs, OperateOn::Output, installables); auto accessor = store->getFSAccessor(); diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index 55bb43f95..f854bedb2 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -73,9 +73,9 @@ struct CmdWhyDepends : SourceExprCommand void run(ref store) override { auto package = parseInstallable(store, _package); - auto packagePath = toStorePath(store, Realise::Outputs, package); + auto packagePath = toStorePath(store, Realise::Outputs, operateOn, package); auto dependency = parseInstallable(store, _dependency); - auto dependencyPath = toStorePath(store, Realise::Derivation, dependency); + auto dependencyPath = toStorePath(store, Realise::Derivation, operateOn, dependency); auto dependencyPathHash = dependencyPath.hashPart(); StorePathSet closure; -- cgit v1.2.3 From 2d6d53bc87ef7468ad73431cf76123316f4c82bf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Jul 2020 20:28:16 +0200 Subject: nix: Fix examples --- src/nix/copy.cc | 4 ++-- src/nix/edit.cc | 2 +- src/nix/log.cc | 4 ++-- src/nix/make-content-addressable.cc | 2 +- src/nix/path-info.cc | 2 +- src/nix/show-derivation.cc | 2 +- src/nix/why-depends.cc | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/nix') diff --git a/src/nix/copy.cc b/src/nix/copy.cc index 5281ff5ee..811c8ab34 100644 --- a/src/nix/copy.cc +++ b/src/nix/copy.cc @@ -72,11 +72,11 @@ struct CmdCopy : StorePathsCommand #ifdef ENABLE_S3 Example{ "To copy Hello to an S3 binary cache:", - "nix copy --to s3://my-bucket?region=eu-west-1 nixpkgs.hello" + "nix copy --to s3://my-bucket?region=eu-west-1 nixpkgs#hello" }, Example{ "To copy Hello to an S3-compatible binary cache:", - "nix copy --to s3://my-bucket?region=eu-west-1&endpoint=example.com nixpkgs.hello" + "nix copy --to s3://my-bucket?region=eu-west-1&endpoint=example.com nixpkgs#hello" }, #endif }; diff --git a/src/nix/edit.cc b/src/nix/edit.cc index 067d3a973..dc9775635 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -20,7 +20,7 @@ struct CmdEdit : InstallableCommand return { Example{ "To open the Nix expression of the GNU Hello package:", - "nix edit nixpkgs.hello" + "nix edit nixpkgs#hello" }, }; } diff --git a/src/nix/log.cc b/src/nix/log.cc index 3fe22f6c2..7e10d373a 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -18,7 +18,7 @@ struct CmdLog : InstallableCommand return { Example{ "To get the build log of GNU Hello:", - "nix log nixpkgs.hello" + "nix log nixpkgs#hello" }, Example{ "To get the build log of a specific path:", @@ -26,7 +26,7 @@ struct CmdLog : InstallableCommand }, Example{ "To get a build log from a specific binary cache:", - "nix log --store https://cache.nixos.org nixpkgs.hello" + "nix log --store https://cache.nixos.org nixpkgs#hello" }, }; } diff --git a/src/nix/make-content-addressable.cc b/src/nix/make-content-addressable.cc index b2ded35dd..712043978 100644 --- a/src/nix/make-content-addressable.cc +++ b/src/nix/make-content-addressable.cc @@ -23,7 +23,7 @@ struct CmdMakeContentAddressable : StorePathsCommand, MixJSON return { Example{ "To create a content-addressable representation of GNU Hello (but not its dependencies):", - "nix make-content-addressable nixpkgs.hello" + "nix make-content-addressable nixpkgs#hello" }, Example{ "To compute a content-addressable representation of the current NixOS system closure:", diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index b89a44f83..65f73cd94 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -40,7 +40,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON }, Example{ "To show a package's closure size and all its dependencies with human readable sizes:", - "nix path-info -rsSh nixpkgs.rust" + "nix path-info -rsSh nixpkgs#rust" }, Example{ "To check the existence of a path in a binary cache:", diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index 5d77cfdca..b5434f982 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -33,7 +33,7 @@ struct CmdShowDerivation : InstallablesCommand return { Example{ "To show the store derivation that results from evaluating the Hello package:", - "nix show-derivation nixpkgs.hello" + "nix show-derivation nixpkgs#hello" }, Example{ "To show the full derivation graph (if available) that produced your NixOS system:", diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index f854bedb2..7d6ef9be0 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -55,15 +55,15 @@ struct CmdWhyDepends : SourceExprCommand return { Example{ "To show one path through the dependency graph leading from Hello to Glibc:", - "nix why-depends nixpkgs.hello nixpkgs.glibc" + "nix why-depends nixpkgs#hello nixpkgs#glibc" }, Example{ "To show all files and paths in the dependency graph leading from Thunderbird to libX11:", - "nix why-depends --all nixpkgs.thunderbird nixpkgs.xorg.libX11" + "nix why-depends --all nixpkgs#thunderbird nixpkgs#xorg.libX11" }, Example{ "To show why Glibc depends on itself:", - "nix why-depends nixpkgs.glibc nixpkgs.glibc" + "nix why-depends nixpkgs#glibc nixpkgs#glibc" }, }; } -- cgit v1.2.3 From 8807ff902e1b543410a9572cc146efa6c90dec87 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Jul 2020 14:25:51 +0200 Subject: nix diff-closures: Fix build --- src/nix/diff-closures.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index df0b26ab1..56ddb575b 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -81,10 +81,10 @@ struct CmdDiffClosures : SourceExprCommand void run(ref store) override { - auto before = parseInstallable(*this, store, _before, false); - auto beforePath = toStorePath(store, Build, before); - auto after = parseInstallable(*this, store, _after, false); - auto afterPath = toStorePath(store, NoBuild, after); + auto before = parseInstallable(store, _before); + auto beforePath = toStorePath(store, Realise::Outputs, operateOn, before); + auto after = parseInstallable(store, _after); + auto afterPath = toStorePath(store, Realise::Outputs, operateOn, after); auto beforeClosure = getClosureInfo(store, beforePath); auto afterClosure = getClosureInfo(store, afterPath); -- cgit v1.2.3