diff options
Diffstat (limited to 'third_party/nix/src/nix')
31 files changed, 3314 insertions, 3726 deletions
diff --git a/third_party/nix/src/nix/add-to-store.cc b/third_party/nix/src/nix/add-to-store.cc index e86b96e3f3f2..cc85cc02ea6e 100644 --- a/third_party/nix/src/nix/add-to-store.cc +++ b/third_party/nix/src/nix/add-to-store.cc @@ -1,61 +1,47 @@ +#include "archive.hh" #include "command.hh" #include "common-args.hh" #include "store-api.hh" -#include "archive.hh" using namespace nix; -struct CmdAddToStore : MixDryRun, StoreCommand -{ - Path path; - std::optional<std::string> namePart; - - CmdAddToStore() - { - expectArg("path", &path); - - mkFlag() - .longName("name") - .shortName('n') - .description("name component of the store path") - .labels({"name"}) - .dest(&namePart); - } - - std::string name() override - { - return "add-to-store"; - } - - std::string description() override - { - return "add a path to the Nix store"; - } - - Examples examples() override - { - return { - }; - } - - void run(ref<Store> store) override - { - if (!namePart) namePart = baseNameOf(path); - - StringSink sink; - dumpPath(path, sink); - - ValidPathInfo info; - info.narHash = hashString(htSHA256, *sink.s); - info.narSize = sink.s->size(); - info.path = store->makeFixedOutputPath(true, info.narHash, *namePart); - info.ca = makeFixedOutputCA(true, info.narHash); - - if (!dryRun) - store->addToStore(info, sink.s); - - std::cout << fmt("%s\n", info.path); - } +struct CmdAddToStore : MixDryRun, StoreCommand { + Path path; + std::optional<std::string> namePart; + + CmdAddToStore() { + expectArg("path", &path); + + mkFlag() + .longName("name") + .shortName('n') + .description("name component of the store path") + .labels({"name"}) + .dest(&namePart); + } + + std::string name() override { return "add-to-store"; } + + std::string description() override { return "add a path to the Nix store"; } + + Examples examples() override { return {}; } + + void run(ref<Store> store) override { + if (!namePart) namePart = baseNameOf(path); + + StringSink sink; + dumpPath(path, sink); + + ValidPathInfo info; + info.narHash = hashString(htSHA256, *sink.s); + info.narSize = sink.s->size(); + info.path = store->makeFixedOutputPath(true, info.narHash, *namePart); + info.ca = makeFixedOutputCA(true, info.narHash); + + if (!dryRun) store->addToStore(info, sink.s); + + std::cout << fmt("%s\n", info.path); + } }; static RegisterCommand r1(make_ref<CmdAddToStore>()); diff --git a/third_party/nix/src/nix/build.cc b/third_party/nix/src/nix/build.cc index b329ac38ac2b..e23f63199fa2 100644 --- a/third_party/nix/src/nix/build.cc +++ b/third_party/nix/src/nix/build.cc @@ -5,68 +5,56 @@ using namespace nix; -struct CmdBuild : MixDryRun, InstallablesCommand -{ - Path outLink = "result"; - - CmdBuild() - { - mkFlag() - .longName("out-link") - .shortName('o') - .description("path of the symlink to the build result") - .labels({"path"}) - .dest(&outLink); - - mkFlag() - .longName("no-link") - .description("do not create a symlink to the build result") - .set(&outLink, Path("")); - } - - std::string name() override - { - return "build"; - } - - std::string description() override - { - return "build a derivation or fetch a store path"; - } - - Examples examples() override - { - return { - Example{ - "To build and run GNU Hello from NixOS 17.03:", - "nix build -f channel:nixos-17.03 hello; ./result/bin/hello" - }, - Example{ - "To build the build.x86_64-linux attribute from release.nix:", - "nix build -f release.nix build.x86_64-linux" - }, - }; - } - - void run(ref<Store> store) override - { - auto buildables = build(store, dryRun ? DryRun : Build, installables); - - if (dryRun) return; - - for (size_t i = 0; i < buildables.size(); ++i) { - auto & b(buildables[i]); - - if (outLink != "") - for (auto & output : b.outputs) - if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) { - 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); - } - } +struct CmdBuild : MixDryRun, InstallablesCommand { + Path outLink = "result"; + + CmdBuild() { + mkFlag() + .longName("out-link") + .shortName('o') + .description("path of the symlink to the build result") + .labels({"path"}) + .dest(&outLink); + + mkFlag() + .longName("no-link") + .description("do not create a symlink to the build result") + .set(&outLink, Path("")); + } + + std::string name() override { return "build"; } + + std::string description() override { + return "build a derivation or fetch a store path"; + } + + Examples examples() override { + return { + Example{"To build and run GNU Hello from NixOS 17.03:", + "nix build -f channel:nixos-17.03 hello; ./result/bin/hello"}, + Example{"To build the build.x86_64-linux attribute from release.nix:", + "nix build -f release.nix build.x86_64-linux"}, + }; + } + + void run(ref<Store> store) override { + auto buildables = build(store, dryRun ? DryRun : Build, installables); + + if (dryRun) return; + + for (size_t i = 0; i < buildables.size(); ++i) { + auto& b(buildables[i]); + + if (outLink != "") + for (auto& output : b.outputs) + if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) { + 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); + } } + } }; static RegisterCommand r1(make_ref<CmdBuild>()); diff --git a/third_party/nix/src/nix/cat.cc b/third_party/nix/src/nix/cat.cc index a35f640d8840..ea9fd0d942fd 100644 --- a/third_party/nix/src/nix/cat.cc +++ b/third_party/nix/src/nix/cat.cc @@ -1,73 +1,53 @@ #include "command.hh" -#include "store-api.hh" #include "fs-accessor.hh" #include "nar-accessor.hh" +#include "store-api.hh" using namespace nix; -struct MixCat : virtual Args -{ - std::string path; +struct MixCat : virtual Args { + std::string path; - void cat(ref<FSAccessor> accessor) - { - auto st = accessor->stat(path); - if (st.type == FSAccessor::Type::tMissing) - throw Error(format("path '%1%' does not exist") % path); - if (st.type != FSAccessor::Type::tRegular) - throw Error(format("path '%1%' is not a regular file") % path); + void cat(ref<FSAccessor> accessor) { + auto st = accessor->stat(path); + if (st.type == FSAccessor::Type::tMissing) + throw Error(format("path '%1%' does not exist") % path); + if (st.type != FSAccessor::Type::tRegular) + throw Error(format("path '%1%' is not a regular file") % path); - std::cout << accessor->readFile(path); - } + std::cout << accessor->readFile(path); + } }; -struct CmdCatStore : StoreCommand, MixCat -{ - CmdCatStore() - { - expectArg("path", &path); - } +struct CmdCatStore : StoreCommand, MixCat { + CmdCatStore() { expectArg("path", &path); } - std::string name() override - { - return "cat-store"; - } + std::string name() override { return "cat-store"; } - std::string description() override - { - return "print the contents of a store file on stdout"; - } + std::string description() override { + return "print the contents of a store file on stdout"; + } - void run(ref<Store> store) override - { - cat(store->getFSAccessor()); - } + void run(ref<Store> store) override { cat(store->getFSAccessor()); } }; -struct CmdCatNar : StoreCommand, MixCat -{ - Path narPath; +struct CmdCatNar : StoreCommand, MixCat { + Path narPath; - CmdCatNar() - { - expectArg("nar", &narPath); - expectArg("path", &path); - } + CmdCatNar() { + expectArg("nar", &narPath); + expectArg("path", &path); + } - std::string name() override - { - return "cat-nar"; - } + std::string name() override { return "cat-nar"; } - std::string description() override - { - return "print the contents of a file inside a NAR file"; - } + std::string description() override { + return "print the contents of a file inside a NAR file"; + } - void run(ref<Store> store) override - { - cat(makeNarAccessor(make_ref<std::string>(readFile(narPath)))); - } + void run(ref<Store> store) override { + cat(makeNarAccessor(make_ref<std::string>(readFile(narPath)))); + } }; static RegisterCommand r1(make_ref<CmdCatStore>()); diff --git a/third_party/nix/src/nix/command.cc b/third_party/nix/src/nix/command.cc index 3d7d582d6f5e..5565e49f56f2 100644 --- a/third_party/nix/src/nix/command.cc +++ b/third_party/nix/src/nix/command.cc @@ -1,61 +1,58 @@ #include "command.hh" -#include "store-api.hh" #include "derivations.hh" +#include "store-api.hh" namespace nix { -Commands * RegisterCommand::commands = 0; +Commands* RegisterCommand::commands = 0; -void Command::printHelp(const string & programName, std::ostream & out) -{ - Args::printHelp(programName, out); +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"; - } + 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 Commands & _commands) - : commands(_commands) -{ - expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) { +MultiCommand::MultiCommand(const Commands& _commands) : commands(_commands) { + expectedArgs.push_back(ExpectedArg{ + "command", 1, true, [=](std::vector<std::string> ss) { assert(!command); auto i = commands.find(ss[0]); if (i == commands.end()) - throw UsageError("'%s' is not a recognised command", ss[0]); + 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; - } +void MultiCommand::printHelp(const string& programName, std::ostream& out) { + if (command) { + command->printHelp(programName + " " + command->name(), out); + return; + } - out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n"; + out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n"; - out << "\n"; - out << "Common flags:\n"; - printFlags(out); + out << "\n"; + out << "Common flags:\n"; + printFlags(out); - out << "\n"; - out << "Available commands:\n"; + 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); + 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"; @@ -63,94 +60,77 @@ void MultiCommand::printHelp(const string & programName, std::ostream & out) #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::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); +bool MultiCommand::processArgs(const Strings& args, bool finish) { + if (command) + return command->processArgs(args, finish); + else + return Args::processArgs(args, finish); } -StoreCommand::StoreCommand() -{ -} +StoreCommand::StoreCommand() {} -ref<Store> StoreCommand::getStore() -{ - if (!_store) - _store = createStore(); - return ref<Store>(_store); +ref<Store> StoreCommand::getStore() { + if (!_store) _store = createStore(); + return ref<Store>(_store); } -ref<Store> StoreCommand::createStore() -{ - return openStore(); -} +ref<Store> StoreCommand::createStore() { return openStore(); } -void StoreCommand::run() -{ - run(getStore()); -} +void StoreCommand::run() { run(getStore()); } -StorePathsCommand::StorePathsCommand(bool recursive) - : recursive(recursive) -{ - if (recursive) - mkFlag() - .longName("no-recursive") - .description("apply operation to specified paths only") - .set(&this->recursive, false); - else - mkFlag() - .longName("recursive") - .shortName('r') - .description("apply operation to closure of the specified paths") - .set(&this->recursive, true); - - mkFlag(0, "all", "apply operation to the entire store", &all); +StorePathsCommand::StorePathsCommand(bool recursive) : recursive(recursive) { + if (recursive) + mkFlag() + .longName("no-recursive") + .description("apply operation to specified paths only") + .set(&this->recursive, false); + else + mkFlag() + .longName("recursive") + .shortName('r') + .description("apply operation to closure of the specified paths") + .set(&this->recursive, true); + + mkFlag(0, "all", "apply operation to the entire store", &all); } -void StorePathsCommand::run(ref<Store> store) -{ - Paths storePaths; +void StorePathsCommand::run(ref<Store> store) { + Paths storePaths; - if (all) { - if (installables.size()) - throw UsageError("'--all' does not expect arguments"); - for (auto & p : store->queryAllValidPaths()) - storePaths.push_back(p); - } + if (all) { + if (installables.size()) + throw UsageError("'--all' does not expect arguments"); + for (auto& p : store->queryAllValidPaths()) storePaths.push_back(p); + } - else { - for (auto & p : toStorePaths(store, NoBuild, installables)) - storePaths.push_back(p); + else { + for (auto& p : toStorePaths(store, NoBuild, installables)) + storePaths.push_back(p); - if (recursive) { - PathSet closure; - store->computeFSClosure(PathSet(storePaths.begin(), storePaths.end()), - closure, false, false); - storePaths = Paths(closure.begin(), closure.end()); - } + if (recursive) { + PathSet closure; + store->computeFSClosure(PathSet(storePaths.begin(), storePaths.end()), + closure, false, false); + storePaths = Paths(closure.begin(), closure.end()); } + } - run(store, storePaths); + run(store, storePaths); } -void StorePathCommand::run(ref<Store> store) -{ - auto storePaths = toStorePaths(store, NoBuild, installables); +void StorePathCommand::run(ref<Store> store) { + auto storePaths = toStorePaths(store, NoBuild, installables); - if (storePaths.size() != 1) - throw UsageError("this command requires exactly one store path"); + if (storePaths.size() != 1) + throw UsageError("this command requires exactly one store path"); - run(store, *storePaths.begin()); + run(store, *storePaths.begin()); } -} +} // namespace nix diff --git a/third_party/nix/src/nix/command.hh b/third_party/nix/src/nix/command.hh index 97a6fee7fd27..cc82c4b1aadd 100644 --- a/third_party/nix/src/nix/command.hh +++ b/third_party/nix/src/nix/command.hh @@ -13,202 +13,177 @@ 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 Command : virtual Args { + virtual std::string name() = 0; + virtual void prepare(){}; + virtual void run() = 0; - struct Example - { - std::string description; - std::string command; - }; + struct Example { + std::string description; + std::string command; + }; - typedef std::list<Example> Examples; + typedef std::list<Example> Examples; - virtual Examples examples() { return Examples(); } + virtual Examples examples() { return Examples(); } - void printHelp(const string & programName, std::ostream & out) override; + void printHelp(const string& programName, std::ostream& out) override; }; class Store; /* A command that require a Nix store. */ -struct StoreCommand : virtual Command -{ - StoreCommand(); - void run() override; - ref<Store> getStore(); - virtual ref<Store> createStore(); - virtual void run(ref<Store>) = 0; - -private: - std::shared_ptr<Store> _store; +struct StoreCommand : virtual Command { + StoreCommand(); + void run() override; + ref<Store> getStore(); + virtual ref<Store> createStore(); + virtual void run(ref<Store>) = 0; + + private: + std::shared_ptr<Store> _store; }; -struct Buildable -{ - Path drvPath; // may be empty - std::map<std::string, Path> outputs; +struct Buildable { + Path drvPath; // may be empty + std::map<std::string, Path> outputs; }; typedef std::vector<Buildable> Buildables; -struct Installable -{ - virtual std::string what() = 0; +struct Installable { + virtual std::string what() = 0; - virtual Buildables toBuildables() - { - throw Error("argument '%s' cannot be built", what()); - } + virtual Buildables toBuildables() { + throw Error("argument '%s' cannot be built", what()); + } - Buildable toBuildable(); + Buildable toBuildable(); - virtual Value * toValue(EvalState & state) - { - throw Error("argument '%s' cannot be evaluated", what()); - } + virtual Value* toValue(EvalState& state) { + throw Error("argument '%s' cannot be evaluated", what()); + } }; -struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs -{ - Path file; +struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs { + Path file; - SourceExprCommand(); + 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); + /* 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<EvalState> getEvalState(); + ref<EvalState> getEvalState(); -private: + private: + std::shared_ptr<EvalState> evalState; - std::shared_ptr<EvalState> evalState; - - Value * vSourceExpr = 0; + Value* vSourceExpr = 0; }; enum RealiseMode { Build, NoBuild, DryRun }; /* A command that operates on a list of "installables", which can be store paths, attribute paths, Nix expressions, etc. */ -struct InstallablesCommand : virtual Args, SourceExprCommand -{ - std::vector<std::shared_ptr<Installable>> installables; - - InstallablesCommand() - { - expectArgs("installables", &_installables); - } +struct InstallablesCommand : virtual Args, SourceExprCommand { + std::vector<std::shared_ptr<Installable>> installables; - void prepare() override; + InstallablesCommand() { expectArgs("installables", &_installables); } - virtual bool useDefaultInstallables() { return true; } + void prepare() override; -private: + virtual bool useDefaultInstallables() { return true; } - std::vector<std::string> _installables; + private: + std::vector<std::string> _installables; }; -struct InstallableCommand : virtual Args, SourceExprCommand -{ - std::shared_ptr<Installable> installable; - - InstallableCommand() - { - expectArg("installable", &_installable); - } +struct InstallableCommand : virtual Args, SourceExprCommand { + std::shared_ptr<Installable> installable; - void prepare() override; + InstallableCommand() { expectArg("installable", &_installable); } -private: + void prepare() override; - std::string _installable; + private: + std::string _installable; }; /* A command that operates on zero or more store paths. */ -struct StorePathsCommand : public InstallablesCommand -{ -private: +struct StorePathsCommand : public InstallablesCommand { + private: + bool recursive = false; + bool all = false; - bool recursive = false; - bool all = false; + public: + StorePathsCommand(bool recursive = false); -public: + using StoreCommand::run; - StorePathsCommand(bool recursive = false); + virtual void run(ref<Store> store, Paths storePaths) = 0; - using StoreCommand::run; + void run(ref<Store> store) override; - virtual void run(ref<Store> store, Paths storePaths) = 0; - - void run(ref<Store> store) override; - - bool useDefaultInstallables() override { return !all; } + bool useDefaultInstallables() override { return !all; } }; /* A command that operates on exactly one store path. */ -struct StorePathCommand : public InstallablesCommand -{ - using StoreCommand::run; +struct StorePathCommand : public InstallablesCommand { + using StoreCommand::run; - virtual void run(ref<Store> store, const Path & storePath) = 0; + virtual void run(ref<Store> store, const Path& storePath) = 0; - void run(ref<Store> store) override; + void run(ref<Store> store) override; }; typedef std::map<std::string, ref<Command>> Commands; /* An argument parser that supports multiple subcommands, i.e. ‘<command> <subcommand>’. */ -class MultiCommand : virtual Args -{ -public: - Commands commands; +class MultiCommand : virtual Args { + public: + Commands commands; - std::shared_ptr<Command> command; + std::shared_ptr<Command> command; - MultiCommand(const Commands & commands); + MultiCommand(const Commands& commands); - void printHelp(const string & programName, std::ostream & out) override; + void printHelp(const string& programName, std::ostream& out) override; - bool processFlag(Strings::iterator & pos, Strings::iterator end) override; + bool processFlag(Strings::iterator& pos, Strings::iterator end) override; - bool processArgs(const Strings & args, bool finish) override; + bool processArgs(const Strings& args, bool finish) override; }; /* A helper class for registering commands globally. */ -struct RegisterCommand -{ - static Commands * commands; - - RegisterCommand(ref<Command> command) - { - if (!commands) commands = new Commands; - commands->emplace(command->name(), command); - } +struct RegisterCommand { + static Commands* commands; + + RegisterCommand(ref<Command> command) { + if (!commands) commands = new Commands; + commands->emplace(command->name(), command); + } }; -std::shared_ptr<Installable> parseInstallable( - SourceExprCommand & cmd, ref<Store> store, const std::string & installable, - bool useDefaultInstallables); +std::shared_ptr<Installable> parseInstallable(SourceExprCommand& cmd, + ref<Store> store, + const std::string& installable, + bool useDefaultInstallables); Buildables build(ref<Store> store, RealiseMode mode, - std::vector<std::shared_ptr<Installable>> installables); + std::vector<std::shared_ptr<Installable>> installables); PathSet toStorePaths(ref<Store> store, RealiseMode mode, - std::vector<std::shared_ptr<Installable>> installables); + std::vector<std::shared_ptr<Installable>> installables); Path toStorePath(ref<Store> store, RealiseMode mode, - std::shared_ptr<Installable> installable); + std::shared_ptr<Installable> installable); PathSet toDerivations(ref<Store> store, - std::vector<std::shared_ptr<Installable>> installables, - bool useDeriver = false); + std::vector<std::shared_ptr<Installable>> installables, + bool useDeriver = false); -} +} // namespace nix diff --git a/third_party/nix/src/nix/copy.cc b/third_party/nix/src/nix/copy.cc index 12a9f9cd3372..030a13a3d019 100644 --- a/third_party/nix/src/nix/copy.cc +++ b/third_party/nix/src/nix/copy.cc @@ -1,100 +1,84 @@ +#include <atomic> #include "command.hh" #include "shared.hh" #include "store-api.hh" #include "sync.hh" #include "thread-pool.hh" -#include <atomic> - using namespace nix; -struct CmdCopy : StorePathsCommand -{ - std::string srcUri, dstUri; - - CheckSigsFlag checkSigs = CheckSigs; - - SubstituteFlag substitute = NoSubstitute; - - CmdCopy() - : StorePathsCommand(true) - { - mkFlag() - .longName("from") - .labels({"store-uri"}) - .description("URI of the source Nix store") - .dest(&srcUri); - mkFlag() - .longName("to") - .labels({"store-uri"}) - .description("URI of the destination Nix store") - .dest(&dstUri); - - mkFlag() - .longName("no-check-sigs") - .description("do not require that paths are signed by trusted keys") - .set(&checkSigs, NoCheckSigs); - - mkFlag() - .longName("substitute-on-destination") - .shortName('s') - .description("whether to try substitutes on the destination store (only supported by SSH)") - .set(&substitute, Substitute); - } - - std::string name() override - { - return "copy"; - } - - std::string description() override - { - return "copy paths between Nix stores"; - } - - Examples examples() override - { - return { - Example{ - "To copy Firefox from the local store to a binary cache in file:///tmp/cache:", - "nix copy --to file:///tmp/cache $(type -p firefox)" - }, - Example{ - "To copy the entire current NixOS system closure to another machine via SSH:", - "nix copy --to ssh://server /run/current-system" - }, - Example{ - "To copy a closure from another machine via SSH:", - "nix copy --from ssh://server /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2" - }, +struct CmdCopy : StorePathsCommand { + std::string srcUri, dstUri; + + CheckSigsFlag checkSigs = CheckSigs; + + SubstituteFlag substitute = NoSubstitute; + + CmdCopy() : StorePathsCommand(true) { + mkFlag() + .longName("from") + .labels({"store-uri"}) + .description("URI of the source Nix store") + .dest(&srcUri); + mkFlag() + .longName("to") + .labels({"store-uri"}) + .description("URI of the destination Nix store") + .dest(&dstUri); + + mkFlag() + .longName("no-check-sigs") + .description("do not require that paths are signed by trusted keys") + .set(&checkSigs, NoCheckSigs); + + mkFlag() + .longName("substitute-on-destination") + .shortName('s') + .description( + "whether to try substitutes on the destination store (only " + "supported by SSH)") + .set(&substitute, Substitute); + } + + std::string name() override { return "copy"; } + + std::string description() override { return "copy paths between Nix stores"; } + + Examples examples() override { + return { + Example{"To copy Firefox from the local store to a binary cache in " + "file:///tmp/cache:", + "nix copy --to file:///tmp/cache $(type -p firefox)"}, + Example{"To copy the entire current NixOS system closure to another " + "machine via SSH:", + "nix copy --to ssh://server /run/current-system"}, + Example{"To copy a closure from another machine via SSH:", + "nix copy --from ssh://server " + "/nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"}, #ifdef ENABLE_S3 - Example{ - "To copy Hello to an S3 binary cache:", - "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" - }, + Example{"To copy Hello to an S3 binary cache:", + "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"}, #endif - }; - } + }; + } - ref<Store> createStore() override - { - return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri); - } + ref<Store> createStore() override { + return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri); + } - void run(ref<Store> srcStore, Paths storePaths) override - { - if (srcUri.empty() && dstUri.empty()) - throw UsageError("you must pass '--from' and/or '--to'"); + void run(ref<Store> srcStore, Paths storePaths) override { + if (srcUri.empty() && dstUri.empty()) + throw UsageError("you must pass '--from' and/or '--to'"); - ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri); + ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri); - copyPaths(srcStore, dstStore, PathSet(storePaths.begin(), storePaths.end()), - NoRepair, checkSigs, substitute); - } + copyPaths(srcStore, dstStore, PathSet(storePaths.begin(), storePaths.end()), + NoRepair, checkSigs, substitute); + } }; static RegisterCommand r1(make_ref<CmdCopy>()); diff --git a/third_party/nix/src/nix/doctor.cc b/third_party/nix/src/nix/doctor.cc index 7b5444619470..93016e007a84 100644 --- a/third_party/nix/src/nix/doctor.cc +++ b/third_party/nix/src/nix/doctor.cc @@ -6,119 +6,121 @@ using namespace nix; -std::string formatProtocol(unsigned int proto) -{ - if (proto) { - auto major = GET_PROTOCOL_MAJOR(proto) >> 8; - auto minor = GET_PROTOCOL_MINOR(proto); - return (format("%1%.%2%") % major % minor).str(); - } - return "unknown"; +std::string formatProtocol(unsigned int proto) { + if (proto) { + auto major = GET_PROTOCOL_MAJOR(proto) >> 8; + auto minor = GET_PROTOCOL_MINOR(proto); + return (format("%1%.%2%") % major % minor).str(); + } + return "unknown"; } -struct CmdDoctor : StoreCommand -{ - bool success = true; +struct CmdDoctor : StoreCommand { + bool success = true; - std::string name() override - { - return "doctor"; - } + std::string name() override { return "doctor"; } - std::string description() override - { - return "check your system for potential problems"; - } + std::string description() override { + return "check your system for potential problems"; + } - void run(ref<Store> store) override - { - std::cout << "Store uri: " << store->getUri() << std::endl; - std::cout << std::endl; + void run(ref<Store> store) override { + std::cout << "Store uri: " << store->getUri() << std::endl; + std::cout << std::endl; - auto type = getStoreType(); + auto type = getStoreType(); - if (type < tOther) { - success &= checkNixInPath(); - success &= checkProfileRoots(store); - } - success &= checkStoreProtocol(store->getProtocol()); - - if (!success) - throw Exit(2); + if (type < tOther) { + success &= checkNixInPath(); + success &= checkProfileRoots(store); } + success &= checkStoreProtocol(store->getProtocol()); - bool checkNixInPath() - { - PathSet dirs; - - for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":")) - if (pathExists(dir + "/nix-env")) - dirs.insert(dirOf(canonPath(dir + "/nix-env", true))); - - if (dirs.size() != 1) { - std::cout << "Warning: multiple versions of nix found in PATH." << std::endl; - std::cout << std::endl; - for (auto & dir : dirs) - std::cout << " " << dir << std::endl; - std::cout << std::endl; - return false; - } + if (!success) throw Exit(2); + } + + bool checkNixInPath() { + PathSet dirs; - return true; + for (auto& dir : tokenizeString<Strings>(getEnv("PATH"), ":")) + if (pathExists(dir + "/nix-env")) + dirs.insert(dirOf(canonPath(dir + "/nix-env", true))); + + if (dirs.size() != 1) { + std::cout << "Warning: multiple versions of nix found in PATH." + << std::endl; + std::cout << std::endl; + for (auto& dir : dirs) std::cout << " " << dir << std::endl; + std::cout << std::endl; + return false; } - bool checkProfileRoots(ref<Store> store) - { - PathSet dirs; + return true; + } - for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":")) { - Path profileDir = dirOf(dir); - try { - Path userEnv = canonPath(profileDir, true); + bool checkProfileRoots(ref<Store> store) { + PathSet dirs; - if (store->isStorePath(userEnv) && hasSuffix(userEnv, "user-environment")) { - while (profileDir.find("/profiles/") == std::string::npos && isLink(profileDir)) - profileDir = absPath(readLink(profileDir), dirOf(profileDir)); + for (auto& dir : tokenizeString<Strings>(getEnv("PATH"), ":")) { + Path profileDir = dirOf(dir); + try { + Path userEnv = canonPath(profileDir, true); - if (profileDir.find("/profiles/") == std::string::npos) - dirs.insert(dir); - } - } catch (SysError &) {} - } + if (store->isStorePath(userEnv) && + hasSuffix(userEnv, "user-environment")) { + while (profileDir.find("/profiles/") == std::string::npos && + isLink(profileDir)) + profileDir = absPath(readLink(profileDir), dirOf(profileDir)); - if (!dirs.empty()) { - std::cout << "Warning: found profiles outside of " << settings.nixStateDir << "/profiles." << std::endl; - std::cout << "The generation this profile points to might not have a gcroot and could be" << std::endl; - std::cout << "garbage collected, resulting in broken symlinks." << std::endl; - std::cout << std::endl; - for (auto & dir : dirs) - std::cout << " " << dir << std::endl; - std::cout << std::endl; - return false; + if (profileDir.find("/profiles/") == std::string::npos) + dirs.insert(dir); } - - return true; + } catch (SysError&) { + } } - bool checkStoreProtocol(unsigned int storeProto) - { - unsigned int clientProto = GET_PROTOCOL_MAJOR(SERVE_PROTOCOL_VERSION) == GET_PROTOCOL_MAJOR(storeProto) - ? SERVE_PROTOCOL_VERSION - : PROTOCOL_VERSION; - - if (clientProto != storeProto) { - std::cout << "Warning: protocol version of this client does not match the store." << std::endl; - std::cout << "While this is not necessarily a problem it's recommended to keep the client in" << std::endl; - std::cout << "sync with the daemon." << std::endl; - std::cout << std::endl; - std::cout << "Client protocol: " << formatProtocol(clientProto) << std::endl; - std::cout << "Store protocol: " << formatProtocol(storeProto) << std::endl; - std::cout << std::endl; - return false; - } + if (!dirs.empty()) { + std::cout << "Warning: found profiles outside of " << settings.nixStateDir + << "/profiles." << std::endl; + std::cout << "The generation this profile points to might not have a " + "gcroot and could be" + << std::endl; + std::cout << "garbage collected, resulting in broken symlinks." + << std::endl; + std::cout << std::endl; + for (auto& dir : dirs) std::cout << " " << dir << std::endl; + std::cout << std::endl; + return false; + } - return true; + return true; + } + + bool checkStoreProtocol(unsigned int storeProto) { + unsigned int clientProto = GET_PROTOCOL_MAJOR(SERVE_PROTOCOL_VERSION) == + GET_PROTOCOL_MAJOR(storeProto) + ? SERVE_PROTOCOL_VERSION + : PROTOCOL_VERSION; + + if (clientProto != storeProto) { + std::cout << "Warning: protocol version of this client does not match " + "the store." + << std::endl; + std::cout << "While this is not necessarily a problem it's recommended " + "to keep the client in" + << std::endl; + std::cout << "sync with the daemon." << std::endl; + std::cout << std::endl; + std::cout << "Client protocol: " << formatProtocol(clientProto) + << std::endl; + std::cout << "Store protocol: " << formatProtocol(storeProto) + << std::endl; + std::cout << std::endl; + return false; } + + return true; + } }; static RegisterCommand r1(make_ref<CmdDoctor>()); diff --git a/third_party/nix/src/nix/dump-path.cc b/third_party/nix/src/nix/dump-path.cc index f411c0cb7c89..5e93a3a44b83 100644 --- a/third_party/nix/src/nix/dump-path.cc +++ b/third_party/nix/src/nix/dump-path.cc @@ -3,34 +3,26 @@ using namespace nix; -struct CmdDumpPath : StorePathCommand -{ - std::string name() override - { - return "dump-path"; - } +struct CmdDumpPath : StorePathCommand { + std::string name() override { return "dump-path"; } - std::string description() override - { - return "dump a store path to stdout (in NAR format)"; - } + std::string description() override { + return "dump a store path to stdout (in NAR format)"; + } - Examples examples() override - { - return { - Example{ - "To get a NAR from the binary cache https://cache.nixos.org/:", - "nix dump-path --store https://cache.nixos.org/ /nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25" - }, - }; - } + Examples examples() override { + return { + Example{"To get a NAR from the binary cache https://cache.nixos.org/:", + "nix dump-path --store https://cache.nixos.org/ " + "/nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25"}, + }; + } - void run(ref<Store> store, const Path & storePath) override - { - FdSink sink(STDOUT_FILENO); - store->narFromPath(storePath, sink); - sink.flush(); - } + void run(ref<Store> store, const Path& storePath) override { + FdSink sink(STDOUT_FILENO); + store->narFromPath(storePath, sink); + sink.flush(); + } }; static RegisterCommand r1(make_ref<CmdDumpPath>()); diff --git a/third_party/nix/src/nix/edit.cc b/third_party/nix/src/nix/edit.cc index a6169f1183cd..8f31e19a1049 100644 --- a/third_party/nix/src/nix/edit.cc +++ b/third_party/nix/src/nix/edit.cc @@ -1,81 +1,72 @@ +#include <unistd.h> +#include "attr-path.hh" #include "command.hh" -#include "shared.hh" #include "eval.hh" -#include "attr-path.hh" #include "progress-bar.hh" - -#include <unistd.h> +#include "shared.hh" using namespace nix; -struct CmdEdit : InstallableCommand -{ - std::string name() override - { - return "edit"; - } +struct CmdEdit : InstallableCommand { + std::string name() override { return "edit"; } - std::string description() override - { - return "open the Nix expression of a Nix package in $EDITOR"; - } + std::string description() override { + return "open the Nix expression of a Nix package in $EDITOR"; + } - Examples examples() override - { - return { - Example{ - "To open the Nix expression of the GNU Hello package:", - "nix edit nixpkgs.hello" - }, - }; - } + Examples examples() override { + return { + Example{"To open the Nix expression of the GNU Hello package:", + "nix edit nixpkgs.hello"}, + }; + } - void run(ref<Store> store) override - { - auto state = getEvalState(); + void run(ref<Store> store) override { + auto state = getEvalState(); - auto v = installable->toValue(*state); + auto v = installable->toValue(*state); - Value * v2; - try { - auto dummyArgs = state->allocBindings(0); - v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v); - } catch (Error &) { - throw Error("package '%s' has no source location information", installable->what()); - } + Value* v2; + try { + auto dummyArgs = state->allocBindings(0); + v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v); + } catch (Error&) { + throw Error("package '%s' has no source location information", + installable->what()); + } - auto pos = state->forceString(*v2); - debug("position is %s", pos); + auto pos = state->forceString(*v2); + debug("position is %s", pos); - auto colon = pos.rfind(':'); - if (colon == std::string::npos) - throw Error("cannot parse meta.position attribute '%s'", pos); + auto colon = pos.rfind(':'); + if (colon == std::string::npos) + throw Error("cannot parse meta.position attribute '%s'", pos); - std::string filename(pos, 0, colon); - int lineno; - try { - lineno = std::stoi(std::string(pos, colon + 1)); - } catch (std::invalid_argument & e) { - throw Error("cannot parse line number '%s'", pos); - } + std::string filename(pos, 0, colon); + int lineno; + try { + lineno = std::stoi(std::string(pos, colon + 1)); + } catch (std::invalid_argument& e) { + throw Error("cannot parse line number '%s'", pos); + } - auto editor = getEnv("EDITOR", "cat"); + auto editor = getEnv("EDITOR", "cat"); - auto args = tokenizeString<Strings>(editor); + auto args = tokenizeString<Strings>(editor); - if (editor.find("emacs") != std::string::npos || - editor.find("nano") != std::string::npos || - editor.find("vim") != std::string::npos) - args.push_back(fmt("+%d", lineno)); + if (editor.find("emacs") != std::string::npos || + editor.find("nano") != std::string::npos || + editor.find("vim") != std::string::npos) + args.push_back(fmt("+%d", lineno)); - args.push_back(filename); + args.push_back(filename); - stopProgressBar(); + stopProgressBar(); - execvp(args.front().c_str(), stringsToCharPtrs(args).data()); + execvp(args.front().c_str(), stringsToCharPtrs(args).data()); - throw SysError("cannot run editor '%s'", editor); - } + throw SysError("cannot run editor '%s'", editor); + } }; static RegisterCommand r1(make_ref<CmdEdit>()); diff --git a/third_party/nix/src/nix/eval.cc b/third_party/nix/src/nix/eval.cc index b7058361cbec..34d91ba1f0a9 100644 --- a/third_party/nix/src/nix/eval.cc +++ b/third_party/nix/src/nix/eval.cc @@ -1,77 +1,57 @@ +#include "eval.hh" #include "command.hh" #include "common-args.hh" +#include "json.hh" +#include "progress-bar.hh" #include "shared.hh" #include "store-api.hh" -#include "eval.hh" -#include "json.hh" #include "value-to-json.hh" -#include "progress-bar.hh" using namespace nix; -struct CmdEval : MixJSON, InstallableCommand -{ - bool raw = false; +struct CmdEval : MixJSON, InstallableCommand { + bool raw = false; - CmdEval() - { - mkFlag(0, "raw", "print strings unquoted", &raw); - } + CmdEval() { mkFlag(0, "raw", "print strings unquoted", &raw); } - std::string name() override - { - return "eval"; - } + std::string name() override { return "eval"; } - std::string description() override - { - return "evaluate a Nix expression"; - } + std::string description() override { return "evaluate a Nix expression"; } - Examples examples() override - { - return { - Example{ - "To evaluate a Nix expression given on the command line:", - "nix eval '(1 + 2)'" - }, - Example{ - "To evaluate a Nix expression from a file or URI:", - "nix eval -f channel:nixos-17.09 hello.name" - }, - Example{ - "To get the current version of Nixpkgs:", - "nix eval --raw nixpkgs.lib.nixpkgsVersion" - }, - Example{ - "To print the store path of the Hello package:", - "nix eval --raw nixpkgs.hello" - }, - }; - } + Examples examples() override { + return { + Example{"To evaluate a Nix expression given on the command line:", + "nix eval '(1 + 2)'"}, + Example{"To evaluate a Nix expression from a file or URI:", + "nix eval -f channel:nixos-17.09 hello.name"}, + Example{"To get the current version of Nixpkgs:", + "nix eval --raw nixpkgs.lib.nixpkgsVersion"}, + Example{"To print the store path of the Hello package:", + "nix eval --raw nixpkgs.hello"}, + }; + } - void run(ref<Store> store) override - { - if (raw && json) - throw UsageError("--raw and --json are mutually exclusive"); + void run(ref<Store> store) override { + if (raw && json) + throw UsageError("--raw and --json are mutually exclusive"); - auto state = getEvalState(); + auto state = getEvalState(); - auto v = installable->toValue(*state); - PathSet context; + auto v = installable->toValue(*state); + PathSet context; - stopProgressBar(); + stopProgressBar(); - if (raw) { - 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"; - } + if (raw) { + 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"; } + } }; static RegisterCommand r1(make_ref<CmdEval>()); diff --git a/third_party/nix/src/nix/hash.cc b/third_party/nix/src/nix/hash.cc index af4105e28904..11fefb7939f1 100644 --- a/third_party/nix/src/nix/hash.cc +++ b/third_party/nix/src/nix/hash.cc @@ -1,94 +1,78 @@ -#include "command.hh" #include "hash.hh" +#include "command.hh" #include "legacy.hh" #include "shared.hh" using namespace nix; -struct CmdHash : Command -{ - enum Mode { mFile, mPath }; - Mode mode; - Base base = SRI; - bool truncate = false; - HashType ht = htSHA256; - std::vector<std::string> paths; - - CmdHash(Mode mode) : mode(mode) - { - mkFlag(0, "sri", "print hash in SRI format", &base, SRI); - mkFlag(0, "base64", "print hash in base-64", &base, Base64); - mkFlag(0, "base32", "print hash in base-32 (Nix-specific)", &base, Base32); - mkFlag(0, "base16", "print hash in base-16", &base, Base16); - mkFlag() - .longName("type") - .mkHashTypeFlag(&ht); - expectArgs("paths", &paths); - } - - std::string name() override - { - return mode == mFile ? "hash-file" : "hash-path"; - } - - std::string description() override - { - return mode == mFile - ? "print cryptographic hash of a regular file" - : "print cryptographic hash of the NAR serialisation of a path"; - } - - void run() override - { - for (auto path : paths) { - Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first; - if (truncate && h.hashSize > 20) h = compressHash(h, 20); - std::cout << format("%1%\n") % - h.to_string(base, base == SRI); - } +struct CmdHash : Command { + enum Mode { mFile, mPath }; + Mode mode; + Base base = SRI; + bool truncate = false; + HashType ht = htSHA256; + std::vector<std::string> paths; + + CmdHash(Mode mode) : mode(mode) { + mkFlag(0, "sri", "print hash in SRI format", &base, SRI); + mkFlag(0, "base64", "print hash in base-64", &base, Base64); + mkFlag(0, "base32", "print hash in base-32 (Nix-specific)", &base, Base32); + mkFlag(0, "base16", "print hash in base-16", &base, Base16); + mkFlag().longName("type").mkHashTypeFlag(&ht); + expectArgs("paths", &paths); + } + + std::string name() override { + return mode == mFile ? "hash-file" : "hash-path"; + } + + std::string description() override { + return mode == mFile + ? "print cryptographic hash of a regular file" + : "print cryptographic hash of the NAR serialisation of a path"; + } + + void run() override { + for (auto path : paths) { + Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first; + if (truncate && h.hashSize > 20) h = compressHash(h, 20); + std::cout << format("%1%\n") % h.to_string(base, base == SRI); } + } }; static RegisterCommand r1(make_ref<CmdHash>(CmdHash::mFile)); static RegisterCommand r2(make_ref<CmdHash>(CmdHash::mPath)); -struct CmdToBase : Command -{ - Base base; - HashType ht = htUnknown; - std::vector<std::string> args; - - CmdToBase(Base base) : base(base) - { - mkFlag() - .longName("type") - .mkHashTypeFlag(&ht); - 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", - base == Base16 ? "base-16" : - base == Base32 ? "base-32" : - base == Base64 ? "base-64" : - "SRI"); - } - - void run() override - { - for (auto s : args) - std::cout << fmt("%s\n", Hash(s, ht).to_string(base, base == SRI)); - } +struct CmdToBase : Command { + Base base; + HashType ht = htUnknown; + std::vector<std::string> args; + + CmdToBase(Base base) : base(base) { + mkFlag().longName("type").mkHashTypeFlag(&ht); + 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", + base == Base16 + ? "base-16" + : base == Base32 ? "base-32" : base == Base64 ? "base-64" : "SRI"); + } + + void run() override { + for (auto s : args) + std::cout << fmt("%s\n", Hash(s, ht).to_string(base, base == SRI)); + } }; static RegisterCommand r3(make_ref<CmdToBase>(Base16)); @@ -97,55 +81,59 @@ static RegisterCommand r5(make_ref<CmdToBase>(Base64)); static RegisterCommand r6(make_ref<CmdToBase>(SRI)); /* Legacy nix-hash command. */ -static int compatNixHash(int argc, char * * argv) -{ - HashType ht = htMD5; - bool flat = false; - bool base32 = false; - bool truncate = false; - enum { opHash, opTo32, opTo16 } op = opHash; - std::vector<std::string> ss; - - parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { - if (*arg == "--help") - showManPage("nix-hash"); - else if (*arg == "--version") - printVersion("nix-hash"); - else if (*arg == "--flat") flat = true; - else if (*arg == "--base32") base32 = true; - else if (*arg == "--truncate") truncate = true; - else if (*arg == "--type") { - string s = getArg(*arg, arg, end); - ht = parseHashType(s); - if (ht == htUnknown) - throw UsageError(format("unknown hash type '%1%'") % s); - } - else if (*arg == "--to-base16") op = opTo16; - else if (*arg == "--to-base32") op = opTo32; - else if (*arg != "" && arg->at(0) == '-') - return false; - else - ss.push_back(*arg); - return true; - }); - - if (op == opHash) { - CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath); - cmd.ht = ht; - cmd.base = base32 ? Base32 : Base16; - cmd.truncate = truncate; - cmd.paths = ss; - cmd.run(); - } - - else { - CmdToBase cmd(op == opTo32 ? Base32 : Base16); - cmd.args = ss; - cmd.ht = ht; - cmd.run(); - } - - return 0; +static int compatNixHash(int argc, char** argv) { + HashType ht = htMD5; + bool flat = false; + bool base32 = false; + bool truncate = false; + enum { opHash, opTo32, opTo16 } op = opHash; + std::vector<std::string> ss; + + parseCmdLine(argc, argv, + [&](Strings::iterator& arg, const Strings::iterator& end) { + if (*arg == "--help") + showManPage("nix-hash"); + else if (*arg == "--version") + printVersion("nix-hash"); + else if (*arg == "--flat") + flat = true; + else if (*arg == "--base32") + base32 = true; + else if (*arg == "--truncate") + truncate = true; + else if (*arg == "--type") { + string s = getArg(*arg, arg, end); + ht = parseHashType(s); + if (ht == htUnknown) + throw UsageError(format("unknown hash type '%1%'") % s); + } else if (*arg == "--to-base16") + op = opTo16; + else if (*arg == "--to-base32") + op = opTo32; + else if (*arg != "" && arg->at(0) == '-') + return false; + else + ss.push_back(*arg); + return true; + }); + + if (op == opHash) { + CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath); + cmd.ht = ht; + cmd.base = base32 ? Base32 : Base16; + cmd.truncate = truncate; + cmd.paths = ss; + cmd.run(); + } + + else { + CmdToBase cmd(op == opTo32 ? Base32 : Base16); + cmd.args = ss; + cmd.ht = ht; + cmd.run(); + } + + return 0; } static RegisterLegacyCommand s1("nix-hash", compatNixHash); diff --git a/third_party/nix/src/nix/installables.cc b/third_party/nix/src/nix/installables.cc index 0e8bba39de20..633b6d58e170 100644 --- a/third_party/nix/src/nix/installables.cc +++ b/third_party/nix/src/nix/installables.cc @@ -1,190 +1,179 @@ -#include "command.hh" +#include <regex> #include "attr-path.hh" +#include "command.hh" #include "common-eval-args.hh" #include "derivations.hh" #include "eval-inline.hh" #include "eval.hh" #include "get-drvs.hh" -#include "store-api.hh" #include "shared.hh" - -#include <regex> +#include "store-api.hh" namespace nix { -SourceExprCommand::SourceExprCommand() -{ - mkFlag() - .shortName('f') - .longName("file") - .label("file") - .description("evaluate FILE rather than the default") - .dest(&file); +SourceExprCommand::SourceExprCommand() { + mkFlag() + .shortName('f') + .longName("file") + .label("file") + .description("evaluate FILE rather than the default") + .dest(&file); } -Value * SourceExprCommand::getSourceExpr(EvalState & state) -{ - if (vSourceExpr) return vSourceExpr; +Value* SourceExprCommand::getSourceExpr(EvalState& state) { + if (vSourceExpr) return vSourceExpr; - auto sToplevel = state.symbols.create("_toplevel"); + auto sToplevel = state.symbols.create("_toplevel"); - vSourceExpr = state.allocValue(); + vSourceExpr = state.allocValue(); - if (file != "") - state.evalFile(lookupFileArg(state, file), *vSourceExpr); + if (file != "") + state.evalFile(lookupFileArg(state, file), *vSourceExpr); - else { + else { + /* Construct the installation source from $NIX_PATH. */ - /* Construct the installation source from $NIX_PATH. */ + auto searchPath = state.getSearchPath(); - auto searchPath = state.getSearchPath(); + state.mkAttrs(*vSourceExpr, 1024); - state.mkAttrs(*vSourceExpr, 1024); + mkBool(*state.allocAttr(*vSourceExpr, sToplevel), true); - mkBool(*state.allocAttr(*vSourceExpr, sToplevel), true); + std::unordered_set<std::string> seen; - std::unordered_set<std::string> seen; + auto addEntry = [&](const std::string& name) { + if (name == "") return; + if (!seen.insert(name).second) return; + Value* v1 = state.allocValue(); + mkPrimOpApp(*v1, state.getBuiltin("findFile"), + state.getBuiltin("nixPath")); + Value* v2 = state.allocValue(); + mkApp(*v2, *v1, mkString(*state.allocValue(), name)); + mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(name)), + state.getBuiltin("import"), *v2); + }; - auto addEntry = [&](const std::string & name) { - if (name == "") return; - if (!seen.insert(name).second) return; - Value * v1 = state.allocValue(); - mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath")); - Value * v2 = state.allocValue(); - mkApp(*v2, *v1, mkString(*state.allocValue(), name)); - mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(name)), - state.getBuiltin("import"), *v2); - }; + for (auto& i : searchPath) /* Hack to handle channels. */ + if (i.first.empty() && pathExists(i.second + "/manifest.nix")) { + for (auto& j : readDirectory(i.second)) + if (j.name != "manifest.nix" && + pathExists(fmt("%s/%s/default.nix", i.second, j.name))) + addEntry(j.name); + } else + addEntry(i.first); - for (auto & i : searchPath) - /* Hack to handle channels. */ - if (i.first.empty() && pathExists(i.second + "/manifest.nix")) { - for (auto & j : readDirectory(i.second)) - if (j.name != "manifest.nix" - && pathExists(fmt("%s/%s/default.nix", i.second, j.name))) - addEntry(j.name); - } else - addEntry(i.first); + vSourceExpr->attrs->sort(); + } - vSourceExpr->attrs->sort(); - } - - return vSourceExpr; + return vSourceExpr; } -ref<EvalState> SourceExprCommand::getEvalState() -{ - if (!evalState) - evalState = std::make_shared<EvalState>(searchPath, getStore()); - return ref<EvalState>(evalState); +ref<EvalState> SourceExprCommand::getEvalState() { + if (!evalState) + evalState = std::make_shared<EvalState>(searchPath, getStore()); + return ref<EvalState>(evalState); } -Buildable Installable::toBuildable() -{ - auto buildables = toBuildables(); - if (buildables.size() != 1) - throw Error("installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size()); - return std::move(buildables[0]); +Buildable Installable::toBuildable() { + auto buildables = toBuildables(); + if (buildables.size() != 1) + throw Error( + "installable '%s' evaluates to %d derivations, where only one is " + "expected", + what(), buildables.size()); + return std::move(buildables[0]); } -struct InstallableStorePath : Installable -{ - Path storePath; +struct InstallableStorePath : Installable { + Path storePath; - InstallableStorePath(const Path & storePath) : storePath(storePath) { } + InstallableStorePath(const Path& storePath) : storePath(storePath) {} - std::string what() override { return storePath; } + std::string what() override { return storePath; } - Buildables toBuildables() override - { - return {{isDerivation(storePath) ? storePath : "", {{"out", storePath}}}}; - } + Buildables toBuildables() override { + return {{isDerivation(storePath) ? storePath : "", {{"out", storePath}}}}; + } }; -struct InstallableValue : Installable -{ - SourceExprCommand & cmd; +struct InstallableValue : Installable { + SourceExprCommand& cmd; - InstallableValue(SourceExprCommand & cmd) : cmd(cmd) { } + InstallableValue(SourceExprCommand& cmd) : cmd(cmd) {} - Buildables toBuildables() override - { - auto state = cmd.getEvalState(); + Buildables toBuildables() override { + auto state = cmd.getEvalState(); - auto v = toValue(*state); + auto v = toValue(*state); - Bindings & autoArgs = *cmd.getAutoArgs(*state); + Bindings& autoArgs = *cmd.getAutoArgs(*state); - DrvInfos drvs; - getDerivations(*state, *v, "", autoArgs, drvs, false); + DrvInfos drvs; + getDerivations(*state, *v, "", autoArgs, drvs, false); - Buildables res; + Buildables res; - PathSet drvPaths; - - for (auto & drv : drvs) { - Buildable b{drv.queryDrvPath()}; - drvPaths.insert(b.drvPath); + PathSet drvPaths; - auto outputName = drv.queryOutputName(); - if (outputName == "") - throw Error("derivation '%s' lacks an 'outputName' attribute", b.drvPath); + for (auto& drv : drvs) { + Buildable b{drv.queryDrvPath()}; + drvPaths.insert(b.drvPath); - b.outputs.emplace(outputName, drv.queryOutPath()); + auto outputName = drv.queryOutputName(); + if (outputName == "") + throw Error("derivation '%s' lacks an 'outputName' attribute", + b.drvPath); - res.push_back(std::move(b)); - } + b.outputs.emplace(outputName, drv.queryOutPath()); - // 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 -{ - std::string text; +struct InstallableExpr : InstallableValue { + std::string text; - InstallableExpr(SourceExprCommand & cmd, const std::string & text) - : InstallableValue(cmd), text(text) { } + InstallableExpr(SourceExprCommand& cmd, const std::string& text) + : InstallableValue(cmd), text(text) {} - std::string what() override { return text; } + std::string what() override { return text; } - Value * toValue(EvalState & state) override - { - auto v = state.allocValue(); - state.eval(state.parseExprFromString(text, absPath(".")), *v); - return v; - } + Value* toValue(EvalState& state) override { + auto v = state.allocValue(); + state.eval(state.parseExprFromString(text, absPath(".")), *v); + return v; + } }; -struct InstallableAttrPath : InstallableValue -{ - std::string attrPath; +struct InstallableAttrPath : InstallableValue { + std::string attrPath; - InstallableAttrPath(SourceExprCommand & cmd, const std::string & attrPath) - : InstallableValue(cmd), attrPath(attrPath) - { } + InstallableAttrPath(SourceExprCommand& cmd, const std::string& attrPath) + : InstallableValue(cmd), attrPath(attrPath) {} - std::string what() override { return attrPath; } + std::string what() override { return attrPath; } - Value * toValue(EvalState & state) override - { - auto source = cmd.getSourceExpr(state); + Value* toValue(EvalState& state) override { + auto source = cmd.getSourceExpr(state); - Bindings & autoArgs = *cmd.getAutoArgs(state); + Bindings& autoArgs = *cmd.getAutoArgs(state); - Value * v = findAlongAttrPath(state, attrPath, autoArgs, *source); - state.forceValue(*v); + Value* v = findAlongAttrPath(state, attrPath, autoArgs, *source); + state.forceValue(*v); - return v; - } + return v; + } }; // FIXME: extend @@ -192,136 +181,127 @@ std::string attrRegex = R"([A-Za-z_][A-Za-z0-9-_+]*)"; static std::regex attrPathRegex(fmt(R"(%1%(\.%1%)*)", attrRegex)); static std::vector<std::shared_ptr<Installable>> parseInstallables( - SourceExprCommand & cmd, ref<Store> store, std::vector<std::string> ss, bool useDefaultInstallables) -{ - std::vector<std::shared_ptr<Installable>> result; - - if (ss.empty() && useDefaultInstallables) { - if (cmd.file == "") - cmd.file = "."; - ss = {""}; - } + SourceExprCommand& cmd, ref<Store> store, std::vector<std::string> ss, + bool useDefaultInstallables) { + std::vector<std::shared_ptr<Installable>> result; - for (auto & s : ss) { + if (ss.empty() && useDefaultInstallables) { + if (cmd.file == "") cmd.file = "."; + ss = {""}; + } - if (s.compare(0, 1, "(") == 0) - result.push_back(std::make_shared<InstallableExpr>(cmd, s)); + for (auto& s : ss) { + if (s.compare(0, 1, "(") == 0) + result.push_back(std::make_shared<InstallableExpr>(cmd, s)); - else if (s.find("/") != std::string::npos) { + else if (s.find("/") != std::string::npos) { + auto path = store->toStorePath(store->followLinksToStore(s)); - auto path = store->toStorePath(store->followLinksToStore(s)); + if (store->isStorePath(path)) + result.push_back(std::make_shared<InstallableStorePath>(path)); + } - if (store->isStorePath(path)) - result.push_back(std::make_shared<InstallableStorePath>(path)); - } + else if (s == "" || std::regex_match(s, attrPathRegex)) + result.push_back(std::make_shared<InstallableAttrPath>(cmd, s)); - else if (s == "" || std::regex_match(s, attrPathRegex)) - result.push_back(std::make_shared<InstallableAttrPath>(cmd, s)); + else + throw UsageError("don't know what to do with argument '%s'", s); + } - else - throw UsageError("don't know what to do with argument '%s'", s); - } - - return result; + return result; } -std::shared_ptr<Installable> parseInstallable( - SourceExprCommand & cmd, ref<Store> store, const std::string & installable, - bool useDefaultInstallables) -{ - auto installables = parseInstallables(cmd, store, {installable}, false); - assert(installables.size() == 1); - return installables.front(); +std::shared_ptr<Installable> parseInstallable(SourceExprCommand& cmd, + ref<Store> store, + const std::string& installable, + bool useDefaultInstallables) { + auto installables = parseInstallables(cmd, store, {installable}, false); + assert(installables.size() == 1); + return installables.front(); } Buildables build(ref<Store> store, RealiseMode mode, - std::vector<std::shared_ptr<Installable>> installables) -{ - if (mode != Build) - settings.readOnlyMode = true; - - Buildables buildables; - - PathSet pathsToBuild; - - for (auto & i : installables) { - for (auto & b : i->toBuildables()) { - if (b.drvPath != "") { - StringSet outputNames; - for (auto & output : b.outputs) - outputNames.insert(output.first); - pathsToBuild.insert( - b.drvPath + "!" + concatStringsSep(",", outputNames)); - } else - for (auto & output : b.outputs) - pathsToBuild.insert(output.second); - buildables.push_back(std::move(b)); - } + std::vector<std::shared_ptr<Installable>> installables) { + if (mode != Build) settings.readOnlyMode = true; + + Buildables buildables; + + PathSet pathsToBuild; + + for (auto& i : installables) { + for (auto& b : i->toBuildables()) { + if (b.drvPath != "") { + StringSet outputNames; + for (auto& output : b.outputs) outputNames.insert(output.first); + pathsToBuild.insert(b.drvPath + "!" + + concatStringsSep(",", outputNames)); + } else + for (auto& output : b.outputs) pathsToBuild.insert(output.second); + buildables.push_back(std::move(b)); } + } - if (mode == DryRun) - printMissing(store, pathsToBuild, lvlError); - else if (mode == Build) - store->buildPaths(pathsToBuild); + if (mode == DryRun) + printMissing(store, pathsToBuild, lvlError); + else if (mode == Build) + store->buildPaths(pathsToBuild); - return buildables; + return buildables; } PathSet toStorePaths(ref<Store> store, RealiseMode mode, - std::vector<std::shared_ptr<Installable>> installables) -{ - PathSet outPaths; + std::vector<std::shared_ptr<Installable>> installables) { + PathSet outPaths; - for (auto & b : build(store, mode, installables)) - for (auto & output : b.outputs) - outPaths.insert(output.second); + for (auto& b : build(store, mode, installables)) + for (auto& output : b.outputs) outPaths.insert(output.second); - return outPaths; + return outPaths; } Path toStorePath(ref<Store> store, RealiseMode mode, - std::shared_ptr<Installable> installable) -{ - auto paths = toStorePaths(store, mode, {installable}); + std::shared_ptr<Installable> installable) { + auto paths = toStorePaths(store, mode, {installable}); - if (paths.size() != 1) - throw Error("argument '%s' should evaluate to one store path", installable->what()); + if (paths.size() != 1) + throw Error("argument '%s' should evaluate to one store path", + installable->what()); - return *paths.begin(); + return *paths.begin(); } PathSet toDerivations(ref<Store> store, - std::vector<std::shared_ptr<Installable>> installables, bool useDeriver) -{ - PathSet drvPaths; - - for (auto & i : installables) - for (auto & b : i->toBuildables()) { - if (b.drvPath.empty()) { - if (!useDeriver) - throw Error("argument '%s' did not evaluate to a derivation", i->what()); - for (auto & output : b.outputs) { - auto derivers = store->queryValidDerivers(output.second); - if (derivers.empty()) - throw Error("'%s' does not have a known deriver", i->what()); - // FIXME: use all derivers? - drvPaths.insert(*derivers.begin()); - } - } else - drvPaths.insert(b.drvPath); + std::vector<std::shared_ptr<Installable>> installables, + bool useDeriver) { + PathSet drvPaths; + + for (auto& i : installables) + for (auto& b : i->toBuildables()) { + if (b.drvPath.empty()) { + if (!useDeriver) + throw Error("argument '%s' did not evaluate to a derivation", + i->what()); + for (auto& output : b.outputs) { + auto derivers = store->queryValidDerivers(output.second); + if (derivers.empty()) + throw Error("'%s' does not have a known deriver", i->what()); + // FIXME: use all derivers? + drvPaths.insert(*derivers.begin()); } + } else + drvPaths.insert(b.drvPath); + } - return drvPaths; + return drvPaths; } -void InstallablesCommand::prepare() -{ - installables = parseInstallables(*this, getStore(), _installables, useDefaultInstallables()); +void InstallablesCommand::prepare() { + installables = parseInstallables(*this, getStore(), _installables, + useDefaultInstallables()); } -void InstallableCommand::prepare() -{ - installable = parseInstallable(*this, getStore(), _installable, false); +void InstallableCommand::prepare() { + installable = parseInstallable(*this, getStore(), _installable, false); } -} +} // namespace nix diff --git a/third_party/nix/src/nix/legacy.cc b/third_party/nix/src/nix/legacy.cc index 6df09ee37a5e..2860afe53a49 100644 --- a/third_party/nix/src/nix/legacy.cc +++ b/third_party/nix/src/nix/legacy.cc @@ -2,6 +2,6 @@ namespace nix { -RegisterLegacyCommand::Commands * RegisterLegacyCommand::commands = 0; +RegisterLegacyCommand::Commands* RegisterLegacyCommand::commands = 0; } diff --git a/third_party/nix/src/nix/legacy.hh b/third_party/nix/src/nix/legacy.hh index f503b0da3e1a..09a102218132 100644 --- a/third_party/nix/src/nix/legacy.hh +++ b/third_party/nix/src/nix/legacy.hh @@ -6,18 +6,16 @@ namespace nix { -typedef std::function<void(int, char * *)> MainFunction; +typedef std::function<void(int, char**)> MainFunction; -struct RegisterLegacyCommand -{ - typedef std::map<std::string, MainFunction> Commands; - static Commands * commands; +struct RegisterLegacyCommand { + typedef std::map<std::string, MainFunction> Commands; + static Commands* commands; - RegisterLegacyCommand(const std::string & name, MainFunction fun) - { - if (!commands) commands = new Commands; - (*commands)[name] = fun; - } + RegisterLegacyCommand(const std::string& name, MainFunction fun) { + if (!commands) commands = new Commands; + (*commands)[name] = fun; + } }; -} +} // namespace nix diff --git a/third_party/nix/src/nix/log.cc b/third_party/nix/src/nix/log.cc index f07ec4e93a16..350d5c4a4999 100644 --- a/third_party/nix/src/nix/log.cc +++ b/third_party/nix/src/nix/log.cc @@ -1,71 +1,59 @@ #include "command.hh" #include "common-args.hh" +#include "progress-bar.hh" #include "shared.hh" #include "store-api.hh" -#include "progress-bar.hh" 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"; - } - - Examples examples() override - { - return { - Example{ - "To get the build log of GNU Hello:", - "nix log nixpkgs.hello" - }, - Example{ - "To get the build log of a specific path:", - "nix log /nix/store/lmngj4wcm9rkv3w4dfhzhcyij3195hiq-thunderbird-52.2.1" - }, - Example{ - "To get a build log from a specific binary cache:", - "nix log --store https://cache.nixos.org nixpkgs.hello" - }, - }; +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"; + } + + Examples examples() override { + return { + Example{"To get the build log of GNU Hello:", "nix log nixpkgs.hello"}, + Example{ + "To get the build log of a specific path:", + "nix log " + "/nix/store/lmngj4wcm9rkv3w4dfhzhcyij3195hiq-thunderbird-52.2.1"}, + Example{"To get a build log from a specific binary cache:", + "nix log --store https://cache.nixos.org nixpkgs.hello"}, + }; + } + + void run(ref<Store> store) override { + settings.readOnlyMode = true; + + auto subs = getDefaultSubstituters(); + + subs.push_front(store); + + auto b = installable->toBuildable(); + + RunPager pager; + for (auto& sub : subs) { + auto log = b.drvPath != "" ? sub->getBuildLog(b.drvPath) : nullptr; + for (auto& output : b.outputs) { + if (log) break; + log = sub->getBuildLog(output.second); + } + if (!log) continue; + stopProgressBar(); + printInfo("got build log for '%s' from '%s'", installable->what(), + sub->getUri()); + std::cout << *log; + return; } - void run(ref<Store> store) override - { - settings.readOnlyMode = true; - - auto subs = getDefaultSubstituters(); - - subs.push_front(store); - - auto b = installable->toBuildable(); - - RunPager pager; - for (auto & sub : subs) { - auto log = b.drvPath != "" ? sub->getBuildLog(b.drvPath) : nullptr; - for (auto & output : b.outputs) { - if (log) break; - log = sub->getBuildLog(output.second); - } - if (!log) continue; - stopProgressBar(); - printInfo("got build log for '%s' from '%s'", installable->what(), sub->getUri()); - std::cout << *log; - return; - } - - throw Error("build log of '%s' is not available", installable->what()); - } + throw Error("build log of '%s' is not available", installable->what()); + } }; static RegisterCommand r1(make_ref<CmdLog>()); diff --git a/third_party/nix/src/nix/ls.cc b/third_party/nix/src/nix/ls.cc index d089be42fb20..b23f83eb7824 100644 --- a/third_party/nix/src/nix/ls.cc +++ b/third_party/nix/src/nix/ls.cc @@ -1,155 +1,128 @@ #include "command.hh" -#include "store-api.hh" -#include "fs-accessor.hh" -#include "nar-accessor.hh" #include "common-args.hh" +#include "fs-accessor.hh" #include "json.hh" +#include "nar-accessor.hh" +#include "store-api.hh" using namespace nix; -struct MixLs : virtual Args, MixJSON -{ - std::string path; - - bool recursive = false; - bool verbose = false; - bool showDirectory = false; - - MixLs() - { - mkFlag('R', "recursive", "list subdirectories recursively", &recursive); - mkFlag('l', "long", "show more file information", &verbose); - mkFlag('d', "directory", "show directories rather than their contents", &showDirectory); - } - - void listText(ref<FSAccessor> accessor) - { - std::function<void(const FSAccessor::Stat &, const Path &, const std::string &, bool)> doPath; - - auto showFile = [&](const Path & curPath, const std::string & relPath) { - if (verbose) { - auto st = accessor->stat(curPath); - std::string tp = - st.type == FSAccessor::Type::tRegular ? - (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); - if (st.type == FSAccessor::Type::tSymlink) - std::cout << " -> " << accessor->readLink(curPath) - ; - std::cout << "\n"; - if (recursive && st.type == FSAccessor::Type::tDirectory) - doPath(st, curPath, relPath, false); - } else { - std::cout << relPath << "\n"; - if (recursive) { - auto st = accessor->stat(curPath); - if (st.type == FSAccessor::Type::tDirectory) - doPath(st, curPath, relPath, false); - } - } - }; - - doPath = [&](const FSAccessor::Stat & st, const Path & curPath, - const std::string & relPath, bool showDirectory) - { - if (st.type == FSAccessor::Type::tDirectory && !showDirectory) { - auto names = accessor->readDirectory(curPath); - for (auto & name : names) - showFile(curPath + "/" + name, relPath + "/" + name); - } else - showFile(curPath, relPath); - }; - - auto st = accessor->stat(path); - if (st.type == FSAccessor::Type::tMissing) - throw Error(format("path '%1%' does not exist") % path); - doPath(st, path, - st.type == FSAccessor::Type::tDirectory ? "." : baseNameOf(path), - showDirectory); - } - - void list(ref<FSAccessor> accessor) - { - if (path == "/") path = ""; - - if (json) { - JSONPlaceholder jsonRoot(std::cout); - listNar(jsonRoot, accessor, path, recursive); - } else - listText(accessor); - } +struct MixLs : virtual Args, MixJSON { + std::string path; + + bool recursive = false; + bool verbose = false; + bool showDirectory = false; + + MixLs() { + mkFlag('R', "recursive", "list subdirectories recursively", &recursive); + mkFlag('l', "long", "show more file information", &verbose); + mkFlag('d', "directory", "show directories rather than their contents", + &showDirectory); + } + + void listText(ref<FSAccessor> accessor) { + std::function<void(const FSAccessor::Stat&, const Path&, const std::string&, + bool)> + doPath; + + auto showFile = [&](const Path& curPath, const std::string& relPath) { + if (verbose) { + auto st = accessor->stat(curPath); + std::string tp = st.type == FSAccessor::Type::tRegular + ? (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); + if (st.type == FSAccessor::Type::tSymlink) + std::cout << " -> " << accessor->readLink(curPath); + std::cout << "\n"; + if (recursive && st.type == FSAccessor::Type::tDirectory) + doPath(st, curPath, relPath, false); + } else { + std::cout << relPath << "\n"; + if (recursive) { + auto st = accessor->stat(curPath); + if (st.type == FSAccessor::Type::tDirectory) + doPath(st, curPath, relPath, false); + } + } + }; + + doPath = [&](const FSAccessor::Stat& st, const Path& curPath, + const std::string& relPath, bool showDirectory) { + if (st.type == FSAccessor::Type::tDirectory && !showDirectory) { + auto names = accessor->readDirectory(curPath); + for (auto& name : names) + showFile(curPath + "/" + name, relPath + "/" + name); + } else + showFile(curPath, relPath); + }; + + auto st = accessor->stat(path); + if (st.type == FSAccessor::Type::tMissing) + throw Error(format("path '%1%' does not exist") % path); + doPath(st, path, + st.type == FSAccessor::Type::tDirectory ? "." : baseNameOf(path), + showDirectory); + } + + void list(ref<FSAccessor> accessor) { + if (path == "/") path = ""; + + if (json) { + JSONPlaceholder jsonRoot(std::cout); + listNar(jsonRoot, accessor, path, recursive); + } else + listText(accessor); + } }; -struct CmdLsStore : StoreCommand, MixLs -{ - CmdLsStore() - { - expectArg("path", &path); - } - - Examples examples() override - { - return { - Example{ - "To list the contents of a store path in a binary cache:", - "nix ls-store --store https://cache.nixos.org/ -lR /nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10" - }, - }; - } - - std::string name() override - { - return "ls-store"; - } - - std::string description() override - { - return "show information about a store path"; - } - - void run(ref<Store> store) override - { - list(store->getFSAccessor()); - } +struct CmdLsStore : StoreCommand, MixLs { + CmdLsStore() { expectArg("path", &path); } + + Examples examples() override { + return { + Example{"To list the contents of a store path in a binary cache:", + "nix ls-store --store https://cache.nixos.org/ -lR " + "/nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10"}, + }; + } + + std::string name() override { return "ls-store"; } + + std::string description() override { + return "show information about a store path"; + } + + void run(ref<Store> store) override { list(store->getFSAccessor()); } }; -struct CmdLsNar : Command, MixLs -{ - Path narPath; - - CmdLsNar() - { - expectArg("nar", &narPath); - expectArg("path", &path); - } - - Examples examples() override - { - return { - Example{ - "To list a specific file in a NAR:", - "nix ls-nar -l hello.nar /bin/hello" - }, - }; - } - - std::string name() override - { - return "ls-nar"; - } - - std::string description() override - { - return "show information about the contents of a NAR file"; - } - - void run() override - { - list(makeNarAccessor(make_ref<std::string>(readFile(narPath, true)))); - } +struct CmdLsNar : Command, MixLs { + Path narPath; + + CmdLsNar() { + expectArg("nar", &narPath); + expectArg("path", &path); + } + + Examples examples() override { + return { + Example{"To list a specific file in a NAR:", + "nix ls-nar -l hello.nar /bin/hello"}, + }; + } + + std::string name() override { return "ls-nar"; } + + std::string description() override { + return "show information about the contents of a NAR file"; + } + + void run() override { + list(makeNarAccessor(make_ref<std::string>(readFile(narPath, true)))); + } }; static RegisterCommand r1(make_ref<CmdLsStore>()); diff --git a/third_party/nix/src/nix/main.cc b/third_party/nix/src/nix/main.cc index bfc0a3def042..79730c58b994 100644 --- a/third_party/nix/src/nix/main.cc +++ b/third_party/nix/src/nix/main.cc @@ -1,181 +1,172 @@ +#include <ifaddrs.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> #include <algorithm> - #include "command.hh" #include "common-args.hh" +#include "download.hh" #include "eval.hh" +#include "finally.hh" #include "globals.hh" #include "legacy.hh" +#include "progress-bar.hh" #include "shared.hh" #include "store-api.hh" -#include "progress-bar.hh" -#include "download.hh" -#include "finally.hh" - -#include <sys/types.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <ifaddrs.h> -#include <netdb.h> -#include <netinet/in.h> extern std::string chrootHelperName; -void chrootHelper(int argc, char * * argv); +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; +static bool haveInternet() { + struct ifaddrs* addrs; - if (getifaddrs(&addrs)) - return true; + if (getifaddrs(&addrs)) return true; + + Finally free([&]() { freeifaddrs(addrs); }); - 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) && - !IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr)) - return true; - } + 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) && + !IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6*)i->ifa_addr)->sin6_addr)) + return true; } + } - return false; + return false; } std::string programPath; -struct NixArgs : virtual MultiCommand, virtual MixCommonArgs -{ - bool printBuildLogs = false; - bool useNet = true; - - NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix") - { - mkFlag() - .longName("help") - .description("show usage information") - .handler([&]() { showHelpAndExit(); }); - - mkFlag() - .longName("help-config") - .description("show configuration options") - .handler([&]() { - std::cout << "The following configuration options are available:\n\n"; - Table2 tbl; - std::map<std::string, Config::SettingInfo> settings; - globalConfig.getSettings(settings); - for (const auto & s : settings) - tbl.emplace_back(s.first, s.second.description); - printTable(std::cout, tbl); - throw Exit(); - }); - - mkFlag() - .longName("print-build-logs") - .shortName('L') - .description("print full build logs on stderr") - .set(&printBuildLogs, true); - - mkFlag() - .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 - { - Args::printFlags(out); - std::cout << - "\n" - "In addition, most configuration settings can be overriden using '--<name> <value>'.\n" - "Boolean settings can be overriden using '--<name>' or '--no-<name>'. See 'nix\n" - "--help-config' for a list of configuration settings.\n"; - } - - void showHelpAndExit() - { - printHelp(programName, std::cout); - std::cout << "\nNote: this program is EXPERIMENTAL and subject to change.\n"; - throw Exit(); - } +struct NixArgs : virtual MultiCommand, virtual MixCommonArgs { + bool printBuildLogs = false; + bool useNet = true; + + NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix") { + mkFlag() + .longName("help") + .description("show usage information") + .handler([&]() { showHelpAndExit(); }); + + mkFlag() + .longName("help-config") + .description("show configuration options") + .handler([&]() { + std::cout << "The following configuration options are available:\n\n"; + Table2 tbl; + std::map<std::string, Config::SettingInfo> settings; + globalConfig.getSettings(settings); + for (const auto& s : settings) + tbl.emplace_back(s.first, s.second.description); + printTable(std::cout, tbl); + throw Exit(); + }); + + mkFlag() + .longName("print-build-logs") + .shortName('L') + .description("print full build logs on stderr") + .set(&printBuildLogs, true); + + mkFlag() + .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 { + Args::printFlags(out); + std::cout << "\n" + "In addition, most configuration settings can be overriden " + "using '--<name> <value>'.\n" + "Boolean settings can be overriden using '--<name>' or " + "'--no-<name>'. See 'nix\n" + "--help-config' for a list of configuration settings.\n"; + } + + void showHelpAndExit() { + printHelp(programName, std::cout); + std::cout + << "\nNote: this program is EXPERIMENTAL and subject to change.\n"; + throw Exit(); + } }; -void mainWrapped(int argc, char * * argv) -{ - /* The chroot helper needs to be run before any threads have been - started. */ - if (argc > 0 && argv[0] == chrootHelperName) { - chrootHelper(argc, argv); - return; - } +void mainWrapped(int argc, char** argv) { + /* The chroot helper needs to be run before any threads have been + started. */ + if (argc > 0 && argv[0] == chrootHelperName) { + chrootHelper(argc, argv); + return; + } - initNix(); - initGC(); + initNix(); + initGC(); - programPath = argv[0]; - string programName = baseNameOf(programPath); + programPath = argv[0]; + string programName = baseNameOf(programPath); - { - auto legacy = (*RegisterLegacyCommand::commands)[programName]; - if (legacy) return legacy(argc, argv); - } + { + auto legacy = (*RegisterLegacyCommand::commands)[programName]; + if (legacy) return legacy(argc, argv); + } - verbosity = lvlWarn; - settings.verboseBuild = false; + verbosity = lvlWarn; + settings.verboseBuild = false; - NixArgs args; + NixArgs args; - args.parseCmdline(argvToStrings(argc, argv)); + args.parseCmdline(argvToStrings(argc, argv)); - initPlugins(); + initPlugins(); - if (!args.command) args.showHelpAndExit(); + if (!args.command) args.showHelpAndExit(); - Finally f([]() { stopProgressBar(); }); + Finally f([]() { stopProgressBar(); }); - startProgressBar(args.printBuildLogs); + 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 && !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<unsigned int>::max(); - if (!downloadSettings.tries.overriden) - downloadSettings.tries = 0; - if (!downloadSettings.connectTimeout.overriden) - downloadSettings.connectTimeout = 1; - } + 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<unsigned int>::max(); + if (!downloadSettings.tries.overriden) downloadSettings.tries = 0; + if (!downloadSettings.connectTimeout.overriden) + downloadSettings.connectTimeout = 1; + } - args.command->prepare(); - args.command->run(); + args.command->prepare(); + args.command->run(); } -} +} // namespace nix -int main(int argc, char * * argv) -{ - return nix::handleExceptions(argv[0], [&]() { - nix::mainWrapped(argc, argv); - }); +int main(int argc, char** argv) { + return nix::handleExceptions(argv[0], + [&]() { nix::mainWrapped(argc, argv); }); } diff --git a/third_party/nix/src/nix/optimise-store.cc b/third_party/nix/src/nix/optimise-store.cc index 725fb75a19ec..49871df680bc 100644 --- a/third_party/nix/src/nix/optimise-store.cc +++ b/third_party/nix/src/nix/optimise-store.cc @@ -1,41 +1,26 @@ +#include <atomic> #include "command.hh" #include "shared.hh" #include "store-api.hh" -#include <atomic> - using namespace nix; -struct CmdOptimiseStore : StoreCommand -{ - CmdOptimiseStore() - { - } +struct CmdOptimiseStore : StoreCommand { + CmdOptimiseStore() {} - std::string name() override - { - return "optimise-store"; - } + std::string name() override { return "optimise-store"; } - std::string description() override - { - return "replace identical files in the store by hard links"; - } + std::string description() override { + return "replace identical files in the store by hard links"; + } - Examples examples() override - { - return { - Example{ - "To optimise the Nix store:", - "nix optimise-store" - }, - }; - } + Examples examples() override { + return { + Example{"To optimise the Nix store:", "nix optimise-store"}, + }; + } - void run(ref<Store> store) override - { - store->optimiseStore(); - } + void run(ref<Store> store) override { store->optimiseStore(); } }; static RegisterCommand r1(make_ref<CmdOptimiseStore>()); diff --git a/third_party/nix/src/nix/path-info.cc b/third_party/nix/src/nix/path-info.cc index dea5f0557b81..bd12be5c2311 100644 --- a/third_party/nix/src/nix/path-info.cc +++ b/third_party/nix/src/nix/path-info.cc @@ -1,133 +1,118 @@ +#include <algorithm> +#include <array> #include "command.hh" +#include "common-args.hh" +#include "json.hh" #include "shared.hh" #include "store-api.hh" -#include "json.hh" -#include "common-args.hh" - -#include <algorithm> -#include <array> using namespace nix; -struct CmdPathInfo : StorePathsCommand, MixJSON -{ - bool showSize = false; - bool showClosureSize = false; - bool humanReadable = false; - bool showSigs = false; - - CmdPathInfo() - { - mkFlag('s', "size", "print size of the NAR dump of each path", &showSize); - mkFlag('S', "closure-size", "print sum size of the NAR dumps of the closure of each path", &showClosureSize); - mkFlag('h', "human-readable", "with -s and -S, print sizes like 1K 234M 5.67G etc.", &humanReadable); - mkFlag(0, "sigs", "show signatures", &showSigs); +struct CmdPathInfo : StorePathsCommand, MixJSON { + bool showSize = false; + bool showClosureSize = false; + bool humanReadable = false; + bool showSigs = false; + + CmdPathInfo() { + mkFlag('s', "size", "print size of the NAR dump of each path", &showSize); + mkFlag('S', "closure-size", + "print sum size of the NAR dumps of the closure of each path", + &showClosureSize); + mkFlag('h', "human-readable", + "with -s and -S, print sizes like 1K 234M 5.67G etc.", + &humanReadable); + mkFlag(0, "sigs", "show signatures", &showSigs); + } + + std::string name() override { return "path-info"; } + + std::string description() override { + return "query information about store paths"; + } + + Examples examples() override { + return { + Example{"To show the closure sizes of every path in the current NixOS " + "system closure, sorted by size:", + "nix path-info -rS /run/current-system | sort -nk2"}, + Example{"To show a package's closure size and all its dependencies " + "with human readable sizes:", + "nix path-info -rsSh nixpkgs.rust"}, + Example{"To check the existence of a path in a binary cache:", + "nix path-info -r /nix/store/7qvk5c91...-geeqie-1.1 --store " + "https://cache.nixos.org/"}, + Example{"To print the 10 most recently added paths (using --json and " + "the jq(1) command):", + "nix path-info --json --all | jq -r " + "'sort_by(.registrationTime)[-11:-1][].path'"}, + Example{"To show the size of the entire Nix store:", + "nix path-info --json --all | jq 'map(.narSize) | add'"}, + Example{"To show every path whose closure is bigger than 1 GB, sorted " + "by closure size:", + "nix path-info --json --all -S | jq 'map(select(.closureSize > " + "1e9)) | sort_by(.closureSize) | map([.path, .closureSize])'"}, + }; + } + + void printSize(unsigned long long value) { + if (!humanReadable) { + std::cout << fmt("\t%11d", value); + return; } - std::string name() override - { - return "path-info"; + static const std::array<char, 9> idents{ + {' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}}; + size_t power = 0; + double res = value; + while (res > 1024 && power < idents.size()) { + ++power; + res /= 1024; } - - std::string description() override - { - return "query information about store paths"; + std::cout << fmt("\t%6.1f%c", res, idents.at(power)); + } + + void run(ref<Store> store, Paths storePaths) override { + size_t pathLen = 0; + for (auto& storePath : storePaths) + pathLen = std::max(pathLen, storePath.size()); + + if (json) { + JSONPlaceholder jsonRoot(std::cout); + store->pathInfoToJSON(jsonRoot, + // FIXME: preserve order? + PathSet(storePaths.begin(), storePaths.end()), true, + showClosureSize, AllowInvalid); } - Examples examples() override - { - return { - Example{ - "To show the closure sizes of every path in the current NixOS system closure, sorted by size:", - "nix path-info -rS /run/current-system | sort -nk2" - }, - Example{ - "To show a package's closure size and all its dependencies with human readable sizes:", - "nix path-info -rsSh nixpkgs.rust" - }, - Example{ - "To check the existence of a path in a binary cache:", - "nix path-info -r /nix/store/7qvk5c91...-geeqie-1.1 --store https://cache.nixos.org/" - }, - Example{ - "To print the 10 most recently added paths (using --json and the jq(1) command):", - "nix path-info --json --all | jq -r 'sort_by(.registrationTime)[-11:-1][].path'" - }, - Example{ - "To show the size of the entire Nix store:", - "nix path-info --json --all | jq 'map(.narSize) | add'" - }, - Example{ - "To show every path whose closure is bigger than 1 GB, sorted by closure size:", - "nix path-info --json --all -S | jq 'map(select(.closureSize > 1e9)) | sort_by(.closureSize) | map([.path, .closureSize])'" - }, - }; - } + else { + for (auto storePath : storePaths) { + auto info = store->queryPathInfo(storePath); + storePath = info->path; // FIXME: screws up padding - void printSize(unsigned long long value) - { - if (!humanReadable) { - std::cout << fmt("\t%11d", value); - return; - } + std::cout << storePath; - static const std::array<char, 9> idents{{ - ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' - }}; - size_t power = 0; - double res = value; - while (res > 1024 && power < idents.size()) { - ++power; - res /= 1024; - } - std::cout << fmt("\t%6.1f%c", res, idents.at(power)); - } - - void run(ref<Store> store, Paths storePaths) override - { - size_t pathLen = 0; - for (auto & storePath : storePaths) - pathLen = std::max(pathLen, storePath.size()); - - if (json) { - JSONPlaceholder jsonRoot(std::cout); - store->pathInfoToJSON(jsonRoot, - // FIXME: preserve order? - PathSet(storePaths.begin(), storePaths.end()), - true, showClosureSize, AllowInvalid); - } - - else { - - for (auto storePath : storePaths) { - auto info = store->queryPathInfo(storePath); - storePath = info->path; // FIXME: screws up padding + if (showSize || showClosureSize || showSigs) + std::cout << std::string( + std::max(0, (int)pathLen - (int)storePath.size()), ' '); - std::cout << storePath; + if (showSize) printSize(info->narSize); - if (showSize || showClosureSize || showSigs) - std::cout << std::string(std::max(0, (int) pathLen - (int) storePath.size()), ' '); - - if (showSize) - printSize(info->narSize); - - if (showClosureSize) - printSize(store->getClosureSize(storePath).first); - - if (showSigs) { - std::cout << '\t'; - Strings ss; - if (info->ultimate) ss.push_back("ultimate"); - if (info->ca != "") ss.push_back("ca:" + info->ca); - for (auto & sig : info->sigs) ss.push_back(sig); - std::cout << concatStringsSep(" ", ss); - } - - std::cout << std::endl; - } + if (showClosureSize) printSize(store->getClosureSize(storePath).first); + if (showSigs) { + std::cout << '\t'; + Strings ss; + if (info->ultimate) ss.push_back("ultimate"); + if (info->ca != "") ss.push_back("ca:" + info->ca); + for (auto& sig : info->sigs) ss.push_back(sig); + std::cout << concatStringsSep(" ", ss); } + + std::cout << std::endl; + } } + } }; static RegisterCommand r1(make_ref<CmdPathInfo>()); diff --git a/third_party/nix/src/nix/ping-store.cc b/third_party/nix/src/nix/ping-store.cc index 310942574a2a..9e9ee15c8966 100644 --- a/third_party/nix/src/nix/ping-store.cc +++ b/third_party/nix/src/nix/ping-store.cc @@ -4,32 +4,22 @@ using namespace nix; -struct CmdPingStore : StoreCommand -{ - std::string name() override - { - return "ping-store"; - } +struct CmdPingStore : StoreCommand { + std::string name() override { return "ping-store"; } - std::string description() override - { - return "test whether a store can be opened"; - } + std::string description() override { + return "test whether a store can be opened"; + } - Examples examples() override - { - return { - Example{ - "To test whether connecting to a remote Nix store via SSH works:", - "nix ping-store --store ssh://mac1" - }, - }; - } + Examples examples() override { + return { + Example{ + "To test whether connecting to a remote Nix store via SSH works:", + "nix ping-store --store ssh://mac1"}, + }; + } - void run(ref<Store> store) override - { - store->connect(); - } + void run(ref<Store> store) override { store->connect(); } }; static RegisterCommand r1(make_ref<CmdPingStore>()); diff --git a/third_party/nix/src/nix/progress-bar.cc b/third_party/nix/src/nix/progress-bar.cc index 98049b5daf9c..d3fca01c38a1 100644 --- a/third_party/nix/src/nix/progress-bar.cc +++ b/third_party/nix/src/nix/progress-bar.cc @@ -1,454 +1,444 @@ #include "progress-bar.hh" -#include "util.hh" -#include "sync.hh" -#include "store-api.hh" -#include "names.hh" - #include <atomic> #include <map> #include <thread> +#include "names.hh" +#include "store-api.hh" +#include "sync.hh" +#include "util.hh" namespace nix { -static std::string getS(const std::vector<Logger::Field> & fields, size_t n) -{ - assert(n < fields.size()); - assert(fields[n].type == Logger::Field::tString); - return fields[n].s; +static std::string getS(const std::vector<Logger::Field>& fields, size_t n) { + assert(n < fields.size()); + assert(fields[n].type == Logger::Field::tString); + return fields[n].s; } -static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n) -{ - assert(n < fields.size()); - assert(fields[n].type == Logger::Field::tInt); - return fields[n].i; +static uint64_t getI(const std::vector<Logger::Field>& fields, size_t n) { + assert(n < fields.size()); + assert(fields[n].type == Logger::Field::tInt); + return fields[n].i; } -class ProgressBar : public Logger -{ -private: - - struct ActInfo - { - std::string s, lastLine, phase; - ActivityType type = actUnknown; - uint64_t done = 0; - uint64_t expected = 0; - uint64_t running = 0; - uint64_t failed = 0; - std::map<ActivityType, uint64_t> expectedByType; - bool visible = true; - ActivityId parent; - std::optional<std::string> name; - }; - - struct ActivitiesByType - { - std::map<ActivityId, std::list<ActInfo>::iterator> its; - uint64_t done = 0; - uint64_t expected = 0; - uint64_t failed = 0; - }; - - struct State - { - std::list<ActInfo> activities; - std::map<ActivityId, std::list<ActInfo>::iterator> its; - - std::map<ActivityType, ActivitiesByType> activitiesByType; - - uint64_t filesLinked = 0, bytesLinked = 0; +class ProgressBar : public Logger { + private: + struct ActInfo { + std::string s, lastLine, phase; + ActivityType type = actUnknown; + uint64_t done = 0; + uint64_t expected = 0; + uint64_t running = 0; + uint64_t failed = 0; + std::map<ActivityType, uint64_t> expectedByType; + bool visible = true; + ActivityId parent; + std::optional<std::string> name; + }; + + struct ActivitiesByType { + std::map<ActivityId, std::list<ActInfo>::iterator> its; + uint64_t done = 0; + uint64_t expected = 0; + uint64_t failed = 0; + }; + + struct State { + std::list<ActInfo> activities; + std::map<ActivityId, std::list<ActInfo>::iterator> its; + + std::map<ActivityType, ActivitiesByType> activitiesByType; + + uint64_t filesLinked = 0, bytesLinked = 0; + + uint64_t corruptedPaths = 0, untrustedPaths = 0; + + bool active = true; + bool haveUpdate = true; + }; + + Sync<State> state_; + + std::thread updateThread; + + std::condition_variable quitCV, updateCV; + + bool printBuildLogs; + bool isTTY; + + public: + ProgressBar(bool printBuildLogs, bool isTTY) + : printBuildLogs(printBuildLogs), isTTY(isTTY) { + state_.lock()->active = isTTY; + updateThread = std::thread([&]() { + auto state(state_.lock()); + while (state->active) { + if (!state->haveUpdate) state.wait(updateCV); + draw(*state); + state.wait_for(quitCV, std::chrono::milliseconds(50)); + } + }); + } + + ~ProgressBar() { + stop(); + updateThread.join(); + } + + void stop() { + auto state(state_.lock()); + if (!state->active) return; + state->active = false; + std::string status = getStatus(*state); + writeToStderr("\r\e[K"); + if (status != "") writeToStderr("[" + status + "]\n"); + updateCV.notify_one(); + quitCV.notify_one(); + } + + void log(Verbosity lvl, const FormatOrString& fs) override { + auto state(state_.lock()); + log(*state, lvl, fs.s); + } + + void log(State& state, Verbosity lvl, const std::string& s) { + if (state.active) { + writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); + draw(state); + } else { + auto s2 = s + ANSI_NORMAL "\n"; + if (!isTTY) s2 = filterANSIEscapes(s2, true); + writeToStderr(s2); + } + } + + void startActivity(ActivityId act, Verbosity lvl, ActivityType type, + const std::string& s, const Fields& fields, + ActivityId parent) override { + auto state(state_.lock()); + + if (lvl <= verbosity && !s.empty()) log(*state, lvl, s + "..."); + + state->activities.emplace_back(ActInfo()); + auto i = std::prev(state->activities.end()); + i->s = s; + i->type = type; + i->parent = parent; + state->its.emplace(act, i); + state->activitiesByType[type].its.emplace(act, i); + + if (type == actBuild) { + auto name = storePathToName(getS(fields, 0)); + if (hasSuffix(name, ".drv")) name.resize(name.size() - 4); + i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name); + auto machineName = getS(fields, 1); + if (machineName != "") + i->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName); + auto curRound = getI(fields, 2); + auto nrRounds = getI(fields, 3); + if (nrRounds != 1) i->s += fmt(" (round %d/%d)", curRound, nrRounds); + i->name = DrvName(name).name; + } - uint64_t corruptedPaths = 0, untrustedPaths = 0; + if (type == actSubstitute) { + auto name = storePathToName(getS(fields, 0)); + auto sub = getS(fields, 1); + i->s = fmt(hasPrefix(sub, "local") + ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s" + : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", + name, sub); + } - bool active = true; - bool haveUpdate = true; - }; + if (type == actPostBuildHook) { + auto name = storePathToName(getS(fields, 0)); + if (hasSuffix(name, ".drv")) name.resize(name.size() - 4); + i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name); + i->name = DrvName(name).name; + } - Sync<State> state_; + if (type == actQueryPathInfo) { + auto name = storePathToName(getS(fields, 0)); + i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, + getS(fields, 1)); + } - std::thread updateThread; + if ((type == actDownload && hasAncestor(*state, actCopyPath, parent)) || + (type == actDownload && + hasAncestor(*state, actQueryPathInfo, parent)) || + (type == actCopyPath && hasAncestor(*state, actSubstitute, parent))) + i->visible = false; + + update(*state); + } + + /* Check whether an activity has an ancestore with the specified + type. */ + bool hasAncestor(State& state, ActivityType type, ActivityId act) { + while (act != 0) { + auto i = state.its.find(act); + if (i == state.its.end()) break; + if (i->second->type == type) return true; + act = i->second->parent; + } + return false; + } - std::condition_variable quitCV, updateCV; + void stopActivity(ActivityId act) override { + auto state(state_.lock()); - bool printBuildLogs; - bool isTTY; + auto i = state->its.find(act); + if (i != state->its.end()) { + auto& actByType = state->activitiesByType[i->second->type]; + actByType.done += i->second->done; + actByType.failed += i->second->failed; -public: + for (auto& j : i->second->expectedByType) + state->activitiesByType[j.first].expected -= j.second; - ProgressBar(bool printBuildLogs, bool isTTY) - : printBuildLogs(printBuildLogs) - , isTTY(isTTY) - { - state_.lock()->active = isTTY; - updateThread = std::thread([&]() { - auto state(state_.lock()); - while (state->active) { - if (!state->haveUpdate) - state.wait(updateCV); - draw(*state); - state.wait_for(quitCV, std::chrono::milliseconds(50)); - } - }); + actByType.its.erase(act); + state->activities.erase(i->second); + state->its.erase(i); } - ~ProgressBar() - { - stop(); - updateThread.join(); - } + update(*state); + } - void stop() - { - auto state(state_.lock()); - if (!state->active) return; - state->active = false; - std::string status = getStatus(*state); - writeToStderr("\r\e[K"); - if (status != "") - writeToStderr("[" + status + "]\n"); - updateCV.notify_one(); - quitCV.notify_one(); - } + void result(ActivityId act, ResultType type, + const std::vector<Field>& fields) override { + auto state(state_.lock()); - void log(Verbosity lvl, const FormatOrString & fs) override - { - auto state(state_.lock()); - log(*state, lvl, fs.s); + if (type == resFileLinked) { + state->filesLinked++; + state->bytesLinked += getI(fields, 0); + update(*state); } - void log(State & state, Verbosity lvl, const std::string & s) - { - if (state.active) { - writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); - draw(state); + else if (type == resBuildLogLine || type == resPostBuildLogLine) { + auto lastLine = trim(getS(fields, 0)); + if (!lastLine.empty()) { + auto i = state->its.find(act); + assert(i != state->its.end()); + ActInfo info = *i->second; + if (printBuildLogs) { + auto suffix = "> "; + if (type == resPostBuildLogLine) { + suffix = " (post)> "; + } + log(*state, lvlInfo, + ANSI_FAINT + info.name.value_or("unnamed") + suffix + + ANSI_NORMAL + lastLine); } else { - auto s2 = s + ANSI_NORMAL "\n"; - if (!isTTY) s2 = filterANSIEscapes(s2, true); - writeToStderr(s2); + state->activities.erase(i->second); + info.lastLine = lastLine; + state->activities.emplace_back(info); + i->second = std::prev(state->activities.end()); + update(*state); } + } } - void startActivity(ActivityId act, Verbosity lvl, ActivityType type, - const std::string & s, const Fields & fields, ActivityId parent) override - { - auto state(state_.lock()); - - if (lvl <= verbosity && !s.empty()) - log(*state, lvl, s + "..."); - - state->activities.emplace_back(ActInfo()); - auto i = std::prev(state->activities.end()); - i->s = s; - i->type = type; - i->parent = parent; - state->its.emplace(act, i); - state->activitiesByType[type].its.emplace(act, i); - - if (type == actBuild) { - auto name = storePathToName(getS(fields, 0)); - if (hasSuffix(name, ".drv")) - name.resize(name.size() - 4); - i->s = fmt("building " ANSI_BOLD "%s" ANSI_NORMAL, name); - auto machineName = getS(fields, 1); - if (machineName != "") - i->s += fmt(" on " ANSI_BOLD "%s" ANSI_NORMAL, machineName); - auto curRound = getI(fields, 2); - auto nrRounds = getI(fields, 3); - if (nrRounds != 1) - i->s += fmt(" (round %d/%d)", curRound, nrRounds); - i->name = DrvName(name).name; - } - - if (type == actSubstitute) { - auto name = storePathToName(getS(fields, 0)); - auto sub = getS(fields, 1); - i->s = fmt( - hasPrefix(sub, "local") - ? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s" - : "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", - name, sub); - } - - if (type == actPostBuildHook) { - auto name = storePathToName(getS(fields, 0)); - if (hasSuffix(name, ".drv")) - name.resize(name.size() - 4); - i->s = fmt("post-build " ANSI_BOLD "%s" ANSI_NORMAL, name); - i->name = DrvName(name).name; - } - - if (type == actQueryPathInfo) { - auto name = storePathToName(getS(fields, 0)); - i->s = fmt("querying " ANSI_BOLD "%s" ANSI_NORMAL " on %s", name, getS(fields, 1)); - } - - if ((type == actDownload && hasAncestor(*state, actCopyPath, parent)) - || (type == actDownload && hasAncestor(*state, actQueryPathInfo, parent)) - || (type == actCopyPath && hasAncestor(*state, actSubstitute, parent))) - i->visible = false; + else if (type == resUntrustedPath) { + state->untrustedPaths++; + update(*state); + } - update(*state); + else if (type == resCorruptedPath) { + state->corruptedPaths++; + update(*state); } - /* Check whether an activity has an ancestore with the specified - type. */ - bool hasAncestor(State & state, ActivityType type, ActivityId act) - { - while (act != 0) { - auto i = state.its.find(act); - if (i == state.its.end()) break; - if (i->second->type == type) return true; - act = i->second->parent; - } - return false; + else if (type == resSetPhase) { + auto i = state->its.find(act); + assert(i != state->its.end()); + i->second->phase = getS(fields, 0); + update(*state); } - void stopActivity(ActivityId act) override - { - auto state(state_.lock()); + else if (type == resProgress) { + auto i = state->its.find(act); + assert(i != state->its.end()); + ActInfo& actInfo = *i->second; + actInfo.done = getI(fields, 0); + actInfo.expected = getI(fields, 1); + actInfo.running = getI(fields, 2); + actInfo.failed = getI(fields, 3); + update(*state); + } - auto i = state->its.find(act); - if (i != state->its.end()) { + else if (type == resSetExpected) { + auto i = state->its.find(act); + assert(i != state->its.end()); + ActInfo& actInfo = *i->second; + auto type = (ActivityType)getI(fields, 0); + auto& j = actInfo.expectedByType[type]; + state->activitiesByType[type].expected -= j; + j = getI(fields, 1); + state->activitiesByType[type].expected += j; + update(*state); + } + } - auto & actByType = state->activitiesByType[i->second->type]; - actByType.done += i->second->done; - actByType.failed += i->second->failed; + void update(State& state) { + state.haveUpdate = true; + updateCV.notify_one(); + } - for (auto & j : i->second->expectedByType) - state->activitiesByType[j.first].expected -= j.second; + void draw(State& state) { + state.haveUpdate = false; + if (!state.active) return; - actByType.its.erase(act); - state->activities.erase(i->second); - state->its.erase(i); - } + std::string line; - update(*state); + std::string status = getStatus(state); + if (!status.empty()) { + line += '['; + line += status; + line += "]"; } - void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override - { - auto state(state_.lock()); + if (!state.activities.empty()) { + if (!status.empty()) line += " "; + auto i = state.activities.rbegin(); - if (type == resFileLinked) { - state->filesLinked++; - state->bytesLinked += getI(fields, 0); - update(*state); - } + while (i != state.activities.rend() && + (!i->visible || (i->s.empty() && i->lastLine.empty()))) + ++i; - else if (type == resBuildLogLine || type == resPostBuildLogLine) { - auto lastLine = trim(getS(fields, 0)); - if (!lastLine.empty()) { - auto i = state->its.find(act); - assert(i != state->its.end()); - ActInfo info = *i->second; - if (printBuildLogs) { - auto suffix = "> "; - if (type == resPostBuildLogLine) { - suffix = " (post)> "; - } - log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine); - } else { - state->activities.erase(i->second); - info.lastLine = lastLine; - state->activities.emplace_back(info); - i->second = std::prev(state->activities.end()); - update(*state); - } - } + if (i != state.activities.rend()) { + line += i->s; + if (!i->phase.empty()) { + line += " ("; + line += i->phase; + line += ")"; } - - else if (type == resUntrustedPath) { - state->untrustedPaths++; - update(*state); - } - - else if (type == resCorruptedPath) { - state->corruptedPaths++; - update(*state); + if (!i->lastLine.empty()) { + if (!i->s.empty()) line += ": "; + line += i->lastLine; } + } + } - else if (type == resSetPhase) { - auto i = state->its.find(act); - assert(i != state->its.end()); - i->second->phase = getS(fields, 0); - update(*state); - } + auto width = getWindowSize().second; + if (width <= 0) width = std::numeric_limits<decltype(width)>::max(); + + writeToStderr("\r" + filterANSIEscapes(line, false, width) + "\e[K"); + } + + std::string getStatus(State& state) { + auto MiB = 1024.0 * 1024.0; + + std::string res; + + auto renderActivity = [&](ActivityType type, const std::string& itemFmt, + const std::string& numberFmt = "%d", + double unit = 1) { + auto& act = state.activitiesByType[type]; + uint64_t done = act.done, expected = act.done, running = 0, + failed = act.failed; + for (auto& j : act.its) { + done += j.second->done; + expected += j.second->expected; + running += j.second->running; + failed += j.second->failed; + } + + expected = std::max(expected, act.expected); + + std::string s; + + if (running || done || expected || failed) { + if (running) + if (expected != 0) + s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + + numberFmt + ANSI_NORMAL "/" + numberFmt, + running / unit, done / unit, expected / unit); + else + s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + + numberFmt + ANSI_NORMAL, + running / unit, done / unit); + else if (expected != done) + if (expected != 0) + s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, + done / unit, expected / unit); + else + s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit); + else + s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, + done / unit); + s = fmt(itemFmt, s); + + if (failed) + s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit); + } + + return s; + }; - else if (type == resProgress) { - auto i = state->its.find(act); - assert(i != state->its.end()); - ActInfo & actInfo = *i->second; - actInfo.done = getI(fields, 0); - actInfo.expected = getI(fields, 1); - actInfo.running = getI(fields, 2); - actInfo.failed = getI(fields, 3); - update(*state); - } + auto showActivity = [&](ActivityType type, const std::string& itemFmt, + const std::string& numberFmt = "%d", + double unit = 1) { + auto s = renderActivity(type, itemFmt, numberFmt, unit); + if (s.empty()) return; + if (!res.empty()) res += ", "; + res += s; + }; - else if (type == resSetExpected) { - auto i = state->its.find(act); - assert(i != state->its.end()); - ActInfo & actInfo = *i->second; - auto type = (ActivityType) getI(fields, 0); - auto & j = actInfo.expectedByType[type]; - state->activitiesByType[type].expected -= j; - j = getI(fields, 1); - state->activitiesByType[type].expected += j; - update(*state); - } + showActivity(actBuilds, "%s built"); + + auto s1 = renderActivity(actCopyPaths, "%s copied"); + auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB); + + if (!s1.empty() || !s2.empty()) { + if (!res.empty()) res += ", "; + if (s1.empty()) + res += "0 copied"; + else + res += s1; + if (!s2.empty()) { + res += " ("; + res += s2; + res += ')'; + } } - void update(State & state) - { - state.haveUpdate = true; - updateCV.notify_one(); - } + showActivity(actDownload, "%s MiB DL", "%.1f", MiB); - void draw(State & state) { - state.haveUpdate = false; - if (!state.active) return; - - std::string line; - - std::string status = getStatus(state); - if (!status.empty()) { - line += '['; - line += status; - line += "]"; - } - - if (!state.activities.empty()) { - if (!status.empty()) line += " "; - auto i = state.activities.rbegin(); - - while (i != state.activities.rend() && (!i->visible || (i->s.empty() && i->lastLine.empty()))) - ++i; - - if (i != state.activities.rend()) { - line += i->s; - if (!i->phase.empty()) { - line += " ("; - line += i->phase; - line += ")"; - } - if (!i->lastLine.empty()) { - if (!i->s.empty()) line += ": "; - line += i->lastLine; - } - } - } - - auto width = getWindowSize().second; - if (width <= 0) width = std::numeric_limits<decltype(width)>::max(); - - writeToStderr("\r" + filterANSIEscapes(line, false, width) + "\e[K"); + auto s = renderActivity(actOptimiseStore, "%s paths optimised"); + if (s != "") { + s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, + state.filesLinked); + if (!res.empty()) res += ", "; + res += s; + } } - std::string getStatus(State & state) - { - auto MiB = 1024.0 * 1024.0; - - std::string res; - - auto renderActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { - auto & act = state.activitiesByType[type]; - uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed; - for (auto & j : act.its) { - done += j.second->done; - expected += j.second->expected; - running += j.second->running; - failed += j.second->failed; - } - - expected = std::max(expected, act.expected); - - std::string s; - - if (running || done || expected || failed) { - if (running) - if (expected != 0) - s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, - running / unit, done / unit, expected / unit); - else - s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL, - running / unit, done / unit); - else if (expected != done) - if (expected != 0) - s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, - done / unit, expected / unit); - else - s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit); - else - s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit); - s = fmt(itemFmt, s); - - if (failed) - s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit); - } - - return s; - }; - - auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { - auto s = renderActivity(type, itemFmt, numberFmt, unit); - if (s.empty()) return; - if (!res.empty()) res += ", "; - res += s; - }; - - showActivity(actBuilds, "%s built"); - - auto s1 = renderActivity(actCopyPaths, "%s copied"); - auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB); - - if (!s1.empty() || !s2.empty()) { - if (!res.empty()) res += ", "; - if (s1.empty()) res += "0 copied"; else res += s1; - if (!s2.empty()) { res += " ("; res += s2; res += ')'; } - } - - showActivity(actDownload, "%s MiB DL", "%.1f", MiB); + // FIXME: don't show "done" paths in green. + showActivity(actVerifyPaths, "%s paths verified"); - { - auto s = renderActivity(actOptimiseStore, "%s paths optimised"); - if (s != "") { - s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked); - if (!res.empty()) res += ", "; - res += s; - } - } - - // FIXME: don't show "done" paths in green. - showActivity(actVerifyPaths, "%s paths verified"); - - if (state.corruptedPaths) { - if (!res.empty()) res += ", "; - res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths); - } - - if (state.untrustedPaths) { - if (!res.empty()) res += ", "; - res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths); - } + if (state.corruptedPaths) { + if (!res.empty()) res += ", "; + res += fmt(ANSI_RED "%d corrupted" ANSI_NORMAL, state.corruptedPaths); + } - return res; + if (state.untrustedPaths) { + if (!res.empty()) res += ", "; + res += fmt(ANSI_RED "%d untrusted" ANSI_NORMAL, state.untrustedPaths); } + + return res; + } }; -void startProgressBar(bool printBuildLogs) -{ - logger = new ProgressBar( - printBuildLogs, - isatty(STDERR_FILENO) && getEnv("TERM", "dumb") != "dumb"); +void startProgressBar(bool printBuildLogs) { + logger = + new ProgressBar(printBuildLogs, isatty(STDERR_FILENO) && + getEnv("TERM", "dumb") != "dumb"); } -void stopProgressBar() -{ - auto progressBar = dynamic_cast<ProgressBar *>(logger); - if (progressBar) progressBar->stop(); - +void stopProgressBar() { + auto progressBar = dynamic_cast<ProgressBar*>(logger); + if (progressBar) progressBar->stop(); } -} +} // namespace nix diff --git a/third_party/nix/src/nix/progress-bar.hh b/third_party/nix/src/nix/progress-bar.hh index 4d61175c24e4..d9963f9d9456 100644 --- a/third_party/nix/src/nix/progress-bar.hh +++ b/third_party/nix/src/nix/progress-bar.hh @@ -8,4 +8,4 @@ void startProgressBar(bool printBuildLogs = false); void stopProgressBar(); -} +} // namespace nix diff --git a/third_party/nix/src/nix/repl.cc b/third_party/nix/src/nix/repl.cc index f857b2e89c29..72609fb16401 100644 --- a/third_party/nix/src/nix/repl.cc +++ b/third_party/nix/src/nix/repl.cc @@ -1,16 +1,16 @@ -#include <iostream> +#include <setjmp.h> +#include <climits> #include <cstdlib> #include <cstring> -#include <climits> - -#include <setjmp.h> +#include <iostream> #ifdef READLINE #include <readline/history.h> #include <readline/readline.h> #else // editline < 1.15.2 don't wrap their API for C++ usage -// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). +// (added in +// https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). // This results in linker errors due to to name-mangling of editline C symbols. // For compatibility with these versions, we wrap the API here // (wrapping multiple times on newer versions is no problem). @@ -19,17 +19,17 @@ extern "C" { } #endif -#include "shared.hh" -#include "eval.hh" -#include "eval-inline.hh" -#include "store-api.hh" -#include "common-eval-args.hh" -#include "get-drvs.hh" -#include "derivations.hh" #include "affinity.hh" -#include "globals.hh" #include "command.hh" +#include "common-eval-args.hh" +#include "derivations.hh" +#include "eval-inline.hh" +#include "eval.hh" #include "finally.hh" +#include "get-drvs.hh" +#include "globals.hh" +#include "shared.hh" +#include "store-api.hh" namespace nix { @@ -41,115 +41,113 @@ namespace nix { #define ESC_CYA "\033[36m" #define ESC_END "\033[0m" -struct NixRepl -{ - string curDir; - EvalState state; - Bindings * autoArgs; - - Strings loadedFiles; - - const static int envSize = 32768; - StaticEnv staticEnv; - Env * env; - int displ; - StringSet varNames; - - const Path historyFile; - - NixRepl(const Strings & searchPath, nix::ref<Store> store); - ~NixRepl(); - void mainLoop(const std::vector<std::string> & files); - StringSet completePrefix(string prefix); - bool getLine(string & input, const std::string &prompt); - Path getDerivationPath(Value & v); - bool processLine(string line); - void loadFile(const Path & path); - void initEnv(); - void reloadFiles(); - void addAttrsToScope(Value & attrs); - void addVarToScope(const Symbol & name, Value & v); - Expr * parseString(string s); - void evalString(string s, Value & v); - - typedef set<Value *> ValuesSeen; - std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth); - std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen); +struct NixRepl { + string curDir; + EvalState state; + Bindings* autoArgs; + + Strings loadedFiles; + + const static int envSize = 32768; + StaticEnv staticEnv; + Env* env; + int displ; + StringSet varNames; + + const Path historyFile; + + NixRepl(const Strings& searchPath, nix::ref<Store> store); + ~NixRepl(); + void mainLoop(const std::vector<std::string>& files); + StringSet completePrefix(string prefix); + bool getLine(string& input, const std::string& prompt); + Path getDerivationPath(Value& v); + bool processLine(string line); + void loadFile(const Path& path); + void initEnv(); + void reloadFiles(); + void addAttrsToScope(Value& attrs); + void addVarToScope(const Symbol& name, Value& v); + Expr* parseString(string s); + void evalString(string s, Value& v); + + typedef set<Value*> ValuesSeen; + std::ostream& printValue(std::ostream& str, Value& v, unsigned int maxDepth); + std::ostream& printValue(std::ostream& str, Value& v, unsigned int maxDepth, + ValuesSeen& seen); }; - -void printHelp() -{ - std::cout - << "Usage: nix-repl [--help] [--version] [-I path] paths...\n" - << "\n" - << "nix-repl is a simple read-eval-print loop (REPL) for the Nix package manager.\n" - << "\n" - << "Options:\n" - << " --help\n" - << " Prints out a summary of the command syntax and exits.\n" - << "\n" - << " --version\n" - << " Prints out the Nix version number on standard output and exits.\n" - << "\n" - << " -I path\n" - << " Add a path to the Nix expression search path. This option may be given\n" - << " multiple times. See the NIX_PATH environment variable for information on\n" - << " the semantics of the Nix search path. Paths added through -I take\n" - << " precedence over NIX_PATH.\n" - << "\n" - << " paths...\n" - << " A list of paths to files containing Nix expressions which nix-repl will\n" - << " load and add to its scope.\n" - << "\n" - << " A path surrounded in < and > will be looked up in the Nix expression search\n" - << " path, as in the Nix language itself.\n" - << "\n" - << " If an element of paths starts with http:// or https://, it is interpreted\n" - << " as the URL of a tarball that will be downloaded and unpacked to a temporary\n" - << " location. The tarball must include a single top-level directory containing\n" - << " at least a file named default.nix.\n"; +void printHelp() { + std::cout << "Usage: nix-repl [--help] [--version] [-I path] paths...\n" + << "\n" + << "nix-repl is a simple read-eval-print loop (REPL) for the Nix " + "package manager.\n" + << "\n" + << "Options:\n" + << " --help\n" + << " Prints out a summary of the command syntax and exits.\n" + << "\n" + << " --version\n" + << " Prints out the Nix version number on standard output " + "and exits.\n" + << "\n" + << " -I path\n" + << " Add a path to the Nix expression search path. This " + "option may be given\n" + << " multiple times. See the NIX_PATH environment variable " + "for information on\n" + << " the semantics of the Nix search path. Paths added " + "through -I take\n" + << " precedence over NIX_PATH.\n" + << "\n" + << " paths...\n" + << " A list of paths to files containing Nix expressions " + "which nix-repl will\n" + << " load and add to its scope.\n" + << "\n" + << " A path surrounded in < and > will be looked up in the " + "Nix expression search\n" + << " path, as in the Nix language itself.\n" + << "\n" + << " If an element of paths starts with http:// or " + "https://, it is interpreted\n" + << " as the URL of a tarball that will be downloaded and " + "unpacked to a temporary\n" + << " location. The tarball must include a single top-level " + "directory containing\n" + << " at least a file named default.nix.\n"; } - -string removeWhitespace(string s) -{ - s = chomp(s); - size_t n = s.find_first_not_of(" \n\r\t"); - if (n != string::npos) s = string(s, n); - return s; +string removeWhitespace(string s) { + s = chomp(s); + size_t n = s.find_first_not_of(" \n\r\t"); + if (n != string::npos) s = string(s, n); + return s; } - -NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store) - : state(searchPath, store) - , staticEnv(false, &state.staticBaseEnv) - , historyFile(getDataDir() + "/nix/repl-history") -{ - curDir = absPath("."); +NixRepl::NixRepl(const Strings& searchPath, nix::ref<Store> store) + : state(searchPath, store), + staticEnv(false, &state.staticBaseEnv), + historyFile(getDataDir() + "/nix/repl-history") { + curDir = absPath("."); } +NixRepl::~NixRepl() { write_history(historyFile.c_str()); } -NixRepl::~NixRepl() -{ - write_history(historyFile.c_str()); -} - -static NixRepl * curRepl; // ugly +static NixRepl* curRepl; // ugly -static char * completionCallback(char * s, int *match) { +static char* completionCallback(char* s, int* match) { auto possible = curRepl->completePrefix(s); if (possible.size() == 1) { *match = 1; - auto *res = strdup(possible.begin()->c_str() + strlen(s)); + auto* res = strdup(possible.begin()->c_str() + strlen(s)); if (!res) throw Error("allocation failure"); return res; } else if (possible.size() > 1) { auto checkAllHaveSameAt = [&](size_t pos) { - auto &first = *possible.begin(); - for (auto &p : possible) { - if (p.size() <= pos || p[pos] != first[pos]) - return false; + auto& first = *possible.begin(); + for (auto& p : possible) { + if (p.size() <= pos || p[pos] != first[pos]) return false; } return true; }; @@ -158,7 +156,7 @@ static char * completionCallback(char * s, int *match) { while (checkAllHaveSameAt(start + len)) ++len; if (len > 0) { *match = 1; - auto *res = strdup(std::string(*possible.begin(), start, len).c_str()); + auto* res = strdup(std::string(*possible.begin(), start, len).c_str()); if (!res) throw Error("allocation failure"); return res; } @@ -168,20 +166,19 @@ static char * completionCallback(char * s, int *match) { return nullptr; } -static int listPossibleCallback(char *s, char ***avp) { +static int listPossibleCallback(char* s, char*** avp) { auto possible = curRepl->completePrefix(s); if (possible.size() > (INT_MAX / sizeof(char*))) throw Error("too many completions"); int ac = 0; - char **vp = nullptr; + char** vp = nullptr; - auto check = [&](auto *p) { + auto check = [&](auto* p) { if (!p) { if (vp) { - while (--ac >= 0) - free(vp[ac]); + while (--ac >= 0) free(vp[ac]); free(vp); } throw Error("allocation failure"); @@ -189,10 +186,9 @@ static int listPossibleCallback(char *s, char ***avp) { return p; }; - vp = check((char **)malloc(possible.size() * sizeof(char*))); + vp = check((char**)malloc(possible.size() * sizeof(char*))); - for (auto & p : possible) - vp[ac++] = check(strdup(p.c_str())); + for (auto& p : possible) vp[ac++] = check(strdup(p.c_str())); *avp = vp; @@ -200,592 +196,567 @@ static int listPossibleCallback(char *s, char ***avp) { } namespace { - // Used to communicate to NixRepl::getLine whether a signal occurred in ::readline. - volatile sig_atomic_t g_signal_received = 0; +// Used to communicate to NixRepl::getLine whether a signal occurred in +// ::readline. +volatile sig_atomic_t g_signal_received = 0; - void sigintHandler(int signo) { - g_signal_received = signo; - } -} +void sigintHandler(int signo) { g_signal_received = signo; } +} // namespace -void NixRepl::mainLoop(const std::vector<std::string> & files) -{ - string error = ANSI_RED "error:" ANSI_NORMAL " "; - std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." << std::endl << std::endl; +void NixRepl::mainLoop(const std::vector<std::string>& files) { + string error = ANSI_RED "error:" ANSI_NORMAL " "; + std::cout << "Welcome to Nix version " << nixVersion << ". Type :? for help." + << std::endl + << std::endl; - for (auto & i : files) - loadedFiles.push_back(i); + for (auto& i : files) loadedFiles.push_back(i); - reloadFiles(); - if (!loadedFiles.empty()) std::cout << std::endl; + reloadFiles(); + if (!loadedFiles.empty()) std::cout << std::endl; - // Allow nix-repl specific settings in .inputrc - rl_readline_name = "nix-repl"; - createDirs(dirOf(historyFile)); + // Allow nix-repl specific settings in .inputrc + rl_readline_name = "nix-repl"; + createDirs(dirOf(historyFile)); #ifndef READLINE - el_hist_size = 1000; + el_hist_size = 1000; #endif - read_history(historyFile.c_str()); - curRepl = this; + read_history(historyFile.c_str()); + curRepl = this; #ifndef READLINE - rl_set_complete_func(completionCallback); - rl_set_list_possib_func(listPossibleCallback); + rl_set_complete_func(completionCallback); + rl_set_list_possib_func(listPossibleCallback); #endif - std::string input; - - while (true) { - // When continuing input from previous lines, don't print a prompt, just align to the same - // number of chars as the prompt. - if (!getLine(input, input.empty() ? "nix-repl> " : " ")) - break; - - try { - if (!removeWhitespace(input).empty() && !processLine(input)) return; - } catch (ParseError & e) { - if (e.msg().find("unexpected $end") != std::string::npos) { - // For parse errors on incomplete input, we continue waiting for the next line of - // input without clearing the input so far. - continue; - } else { - printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); - } - } catch (Error & e) { - printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); - } catch (Interrupted & e) { - printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg()); - } - - // We handled the current input fully, so we should clear it - // and read brand new input. - input.clear(); - std::cout << std::endl; + std::string input; + + while (true) { + // When continuing input from previous lines, don't print a prompt, just + // align to the same number of chars as the prompt. + if (!getLine(input, input.empty() ? "nix-repl> " : " ")) break; + + try { + if (!removeWhitespace(input).empty() && !processLine(input)) return; + } catch (ParseError& e) { + if (e.msg().find("unexpected $end") != std::string::npos) { + // For parse errors on incomplete input, we continue waiting for the + // next line of input without clearing the input so far. + continue; + } else { + printMsg(lvlError, format(error + "%1%%2%") % + (settings.showTrace ? e.prefix() : "") % + e.msg()); + } + } catch (Error& e) { + printMsg(lvlError, format(error + "%1%%2%") % + (settings.showTrace ? e.prefix() : "") % e.msg()); + } catch (Interrupted& e) { + printMsg(lvlError, format(error + "%1%%2%") % + (settings.showTrace ? e.prefix() : "") % e.msg()); } -} + // We handled the current input fully, so we should clear it + // and read brand new input. + input.clear(); + std::cout << std::endl; + } +} -bool NixRepl::getLine(string & input, const std::string &prompt) -{ - struct sigaction act, old; - sigset_t savedSignalMask, set; - - auto setupSignals = [&]() { - act.sa_handler = sigintHandler; - sigfillset(&act.sa_mask); - act.sa_flags = 0; - if (sigaction(SIGINT, &act, &old)) - throw SysError("installing handler for SIGINT"); - - sigemptyset(&set); - sigaddset(&set, SIGINT); - if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask)) - throw SysError("unblocking SIGINT"); - }; - auto restoreSignals = [&]() { - if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) - throw SysError("restoring signals"); - - if (sigaction(SIGINT, &old, 0)) - throw SysError("restoring handler for SIGINT"); - }; +bool NixRepl::getLine(string& input, const std::string& prompt) { + struct sigaction act, old; + sigset_t savedSignalMask, set; + + auto setupSignals = [&]() { + act.sa_handler = sigintHandler; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGINT, &act, &old)) + throw SysError("installing handler for SIGINT"); + + sigemptyset(&set); + sigaddset(&set, SIGINT); + if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask)) + throw SysError("unblocking SIGINT"); + }; + auto restoreSignals = [&]() { + if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) + throw SysError("restoring signals"); - setupSignals(); - char * s = readline(prompt.c_str()); - Finally doFree([&]() { free(s); }); - restoreSignals(); + if (sigaction(SIGINT, &old, 0)) + throw SysError("restoring handler for SIGINT"); + }; - if (g_signal_received) { - g_signal_received = 0; - input.clear(); - return true; - } + setupSignals(); + char* s = readline(prompt.c_str()); + Finally doFree([&]() { free(s); }); + restoreSignals(); - if (!s) - return false; - input += s; - input += '\n'; + if (g_signal_received) { + g_signal_received = 0; + input.clear(); return true; + } + + if (!s) return false; + input += s; + input += '\n'; + return true; } +StringSet NixRepl::completePrefix(string prefix) { + StringSet completions; + + size_t start = prefix.find_last_of(" \n\r\t(){}[]"); + std::string prev, cur; + if (start == std::string::npos) { + prev = ""; + cur = prefix; + } else { + prev = std::string(prefix, 0, start + 1); + cur = std::string(prefix, start + 1); + } -StringSet NixRepl::completePrefix(string prefix) -{ - StringSet completions; + size_t slash, dot; - size_t start = prefix.find_last_of(" \n\r\t(){}[]"); - std::string prev, cur; - if (start == std::string::npos) { - prev = ""; - cur = prefix; - } else { - prev = std::string(prefix, 0, start + 1); - cur = std::string(prefix, start + 1); + if ((slash = cur.rfind('/')) != string::npos) { + try { + auto dir = std::string(cur, 0, slash); + auto prefix2 = std::string(cur, slash + 1); + for (auto& entry : readDirectory(dir == "" ? "/" : dir)) { + if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2)) + completions.insert(prev + dir + "/" + entry.name); + } + } catch (Error&) { } - - size_t slash, dot; - - if ((slash = cur.rfind('/')) != string::npos) { - try { - auto dir = std::string(cur, 0, slash); - auto prefix2 = std::string(cur, slash + 1); - for (auto & entry : readDirectory(dir == "" ? "/" : dir)) { - if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2)) - completions.insert(prev + dir + "/" + entry.name); - } - } catch (Error &) { - } - } else if ((dot = cur.rfind('.')) == string::npos) { - /* This is a variable name; look it up in the current scope. */ - StringSet::iterator i = varNames.lower_bound(cur); - while (i != varNames.end()) { - if (string(*i, 0, cur.size()) != cur) break; - completions.insert(prev + *i); - i++; - } - } else { - try { - /* This is an expression that should evaluate to an - attribute set. Evaluate it to get the names of the - attributes. */ - string expr(cur, 0, dot); - string cur2 = string(cur, dot + 1); - - Expr * e = parseString(expr); - Value v; - e->eval(state, *env, v); - state.forceAttrs(v); - - for (auto & i : *v.attrs) { - string name = i.name; - if (string(name, 0, cur2.size()) != cur2) continue; - completions.insert(prev + expr + "." + name); - } - - } catch (ParseError & e) { - // Quietly ignore parse errors. - } catch (EvalError & e) { - // Quietly ignore evaluation errors. - } catch (UndefinedVarError & e) { - // Quietly ignore undefined variable errors. - } + } else if ((dot = cur.rfind('.')) == string::npos) { + /* This is a variable name; look it up in the current scope. */ + StringSet::iterator i = varNames.lower_bound(cur); + while (i != varNames.end()) { + if (string(*i, 0, cur.size()) != cur) break; + completions.insert(prev + *i); + i++; } + } else { + try { + /* This is an expression that should evaluate to an + attribute set. Evaluate it to get the names of the + attributes. */ + string expr(cur, 0, dot); + string cur2 = string(cur, dot + 1); + + Expr* e = parseString(expr); + Value v; + e->eval(state, *env, v); + state.forceAttrs(v); + + for (auto& i : *v.attrs) { + string name = i.name; + if (string(name, 0, cur2.size()) != cur2) continue; + completions.insert(prev + expr + "." + name); + } - return completions; -} - - -static int runProgram(const string & program, const Strings & args) -{ - Strings args2(args); - args2.push_front(program); - - Pid pid; - pid = fork(); - if (pid == -1) throw SysError("forking"); - if (pid == 0) { - restoreAffinity(); - execvp(program.c_str(), stringsToCharPtrs(args2).data()); - _exit(1); + } catch (ParseError& e) { + // Quietly ignore parse errors. + } catch (EvalError& e) { + // Quietly ignore evaluation errors. + } catch (UndefinedVarError& e) { + // Quietly ignore undefined variable errors. } + } - return pid.wait(); + return completions; } +static int runProgram(const string& program, const Strings& args) { + Strings args2(args); + args2.push_front(program); + + Pid pid; + pid = fork(); + if (pid == -1) throw SysError("forking"); + if (pid == 0) { + restoreAffinity(); + execvp(program.c_str(), stringsToCharPtrs(args2).data()); + _exit(1); + } -bool isVarName(const string & s) -{ - if (s.size() == 0) return false; - char c = s[0]; - if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false; - for (auto & i : s) - if (!((i >= 'a' && i <= 'z') || - (i >= 'A' && i <= 'Z') || - (i >= '0' && i <= '9') || - i == '_' || i == '-' || i == '\'')) - return false; - return true; + return pid.wait(); } - -Path NixRepl::getDerivationPath(Value & v) { - auto drvInfo = getDerivation(state, v, false); - if (!drvInfo) - throw Error("expression does not evaluate to a derivation, so I can't build it"); - Path drvPath = drvInfo->queryDrvPath(); - if (drvPath == "" || !state.store->isValidPath(drvPath)) - throw Error("expression did not evaluate to a valid derivation"); - return drvPath; +bool isVarName(const string& s) { + if (s.size() == 0) return false; + char c = s[0]; + if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false; + for (auto& i : s) + if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || + (i >= '0' && i <= '9') || i == '_' || i == '-' || i == '\'')) + return false; + return true; } +Path NixRepl::getDerivationPath(Value& v) { + auto drvInfo = getDerivation(state, v, false); + if (!drvInfo) + throw Error( + "expression does not evaluate to a derivation, so I can't build it"); + Path drvPath = drvInfo->queryDrvPath(); + if (drvPath == "" || !state.store->isValidPath(drvPath)) + throw Error("expression did not evaluate to a valid derivation"); + return drvPath; +} -bool NixRepl::processLine(string line) -{ - if (line == "") return true; - - string command, arg; - - if (line[0] == ':') { - size_t p = line.find_first_of(" \n\r\t"); - command = string(line, 0, p); - if (p != string::npos) arg = removeWhitespace(string(line, p)); - } else { - arg = line; - } +bool NixRepl::processLine(string line) { + if (line == "") return true; - if (command == ":?" || command == ":help") { - std::cout - << "The following commands are available:\n" - << "\n" - << " <expr> Evaluate and print expression\n" - << " <x> = <expr> Bind expression to variable\n" - << " :a <expr> Add attributes from resulting set to scope\n" - << " :b <expr> Build derivation\n" - << " :i <expr> Build derivation, then install result into current profile\n" - << " :l <path> Load Nix expression and add it to scope\n" - << " :p <expr> Evaluate and print expression recursively\n" - << " :q Exit nix-repl\n" - << " :r Reload all files\n" - << " :s <expr> Build dependencies of derivation, then start nix-shell\n" - << " :t <expr> Describe result of evaluation\n" - << " :u <expr> Build derivation, then start nix-shell\n"; - } + string command, arg; - else if (command == ":a" || command == ":add") { - Value v; - evalString(arg, v); - addAttrsToScope(v); - } + if (line[0] == ':') { + size_t p = line.find_first_of(" \n\r\t"); + command = string(line, 0, p); + if (p != string::npos) arg = removeWhitespace(string(line, p)); + } else { + arg = line; + } - else if (command == ":l" || command == ":load") { - state.resetFileCache(); - loadFile(arg); - } + if (command == ":?" || command == ":help") { + std::cout << "The following commands are available:\n" + << "\n" + << " <expr> Evaluate and print expression\n" + << " <x> = <expr> Bind expression to variable\n" + << " :a <expr> Add attributes from resulting set to scope\n" + << " :b <expr> Build derivation\n" + << " :i <expr> Build derivation, then install result into " + "current profile\n" + << " :l <path> Load Nix expression and add it to scope\n" + << " :p <expr> Evaluate and print expression recursively\n" + << " :q Exit nix-repl\n" + << " :r Reload all files\n" + << " :s <expr> Build dependencies of derivation, then start " + "nix-shell\n" + << " :t <expr> Describe result of evaluation\n" + << " :u <expr> Build derivation, then start nix-shell\n"; + } - else if (command == ":r" || command == ":reload") { - state.resetFileCache(); - reloadFiles(); - } + else if (command == ":a" || command == ":add") { + Value v; + evalString(arg, v); + addAttrsToScope(v); + } - else if (command == ":t") { - Value v; - evalString(arg, v); - std::cout << showType(v) << std::endl; + else if (command == ":l" || command == ":load") { + state.resetFileCache(); + loadFile(arg); + } - } else if (command == ":u") { - Value v, f, result; - evalString(arg, v); - evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f); - state.callFunction(f, v, result, Pos()); + else if (command == ":r" || command == ":reload") { + state.resetFileCache(); + reloadFiles(); + } - Path drvPath = getDerivationPath(result); - runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath}); - } + else if (command == ":t") { + Value v; + evalString(arg, v); + std::cout << showType(v) << std::endl; + + } else if (command == ":u") { + Value v, f, result; + evalString(arg, v); + evalString( + "drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv " + "]; } \"\"", + f); + state.callFunction(f, v, result, Pos()); + + Path drvPath = getDerivationPath(result); + runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath}); + } - else if (command == ":b" || command == ":i" || command == ":s") { - Value v; - evalString(arg, v); - Path drvPath = getDerivationPath(v); - - if (command == ":b") { - /* We could do the build in this process using buildPaths(), - but doing it in a child makes it easier to recover from - problems / SIGINT. */ - if (runProgram(settings.nixBinDir + "/nix", Strings{"build", "--no-link", drvPath}) == 0) { - Derivation drv = readDerivation(drvPath); - std::cout << std::endl << "this derivation produced the following outputs:" << std::endl; - for (auto & i : drv.outputs) - std::cout << format(" %1% -> %2%") % i.first % i.second.path << std::endl; - } - } else if (command == ":i") { - runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPath}); - } else { - runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath}); - } + else if (command == ":b" || command == ":i" || command == ":s") { + Value v; + evalString(arg, v); + Path drvPath = getDerivationPath(v); + + if (command == ":b") { + /* We could do the build in this process using buildPaths(), + but doing it in a child makes it easier to recover from + problems / SIGINT. */ + if (runProgram(settings.nixBinDir + "/nix", + Strings{"build", "--no-link", drvPath}) == 0) { + Derivation drv = readDerivation(drvPath); + std::cout << std::endl + << "this derivation produced the following outputs:" + << std::endl; + for (auto& i : drv.outputs) + std::cout << format(" %1% -> %2%") % i.first % i.second.path + << std::endl; + } + } else if (command == ":i") { + runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPath}); + } else { + runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath}); } + } - else if (command == ":p" || command == ":print") { - Value v; - evalString(arg, v); - printValue(std::cout, v, 1000000000) << std::endl; - } + else if (command == ":p" || command == ":print") { + Value v; + evalString(arg, v); + printValue(std::cout, v, 1000000000) << std::endl; + } - else if (command == ":q" || command == ":quit") - return false; - - else if (command != "") - throw Error(format("unknown command '%1%'") % command); - - else { - size_t p = line.find('='); - string name; - if (p != string::npos && - p < line.size() && - line[p + 1] != '=' && - isVarName(name = removeWhitespace(string(line, 0, p)))) - { - Expr * e = parseString(string(line, p + 1)); - Value & v(*state.allocValue()); - v.type = tThunk; - v.thunk.env = env; - v.thunk.expr = e; - addVarToScope(state.symbols.create(name), v); - } else { - Value v; - evalString(line, v); - printValue(std::cout, v, 1) << std::endl; - } + else if (command == ":q" || command == ":quit") + return false; + + else if (command != "") + throw Error(format("unknown command '%1%'") % command); + + else { + size_t p = line.find('='); + string name; + if (p != string::npos && p < line.size() && line[p + 1] != '=' && + isVarName(name = removeWhitespace(string(line, 0, p)))) { + Expr* e = parseString(string(line, p + 1)); + Value& v(*state.allocValue()); + v.type = tThunk; + v.thunk.env = env; + v.thunk.expr = e; + addVarToScope(state.symbols.create(name), v); + } else { + Value v; + evalString(line, v); + printValue(std::cout, v, 1) << std::endl; } + } - return true; + return true; } - -void NixRepl::loadFile(const Path & path) -{ - loadedFiles.remove(path); - loadedFiles.push_back(path); - Value v, v2; - state.evalFile(lookupFileArg(state, path), v); - state.autoCallFunction(*autoArgs, v, v2); - addAttrsToScope(v2); +void NixRepl::loadFile(const Path& path) { + loadedFiles.remove(path); + loadedFiles.push_back(path); + Value v, v2; + state.evalFile(lookupFileArg(state, path), v); + state.autoCallFunction(*autoArgs, v, v2); + addAttrsToScope(v2); } +void NixRepl::initEnv() { + env = &state.allocEnv(envSize); + env->up = &state.baseEnv; + displ = 0; + staticEnv.vars.clear(); -void NixRepl::initEnv() -{ - env = &state.allocEnv(envSize); - env->up = &state.baseEnv; - displ = 0; - staticEnv.vars.clear(); - - varNames.clear(); - for (auto & i : state.staticBaseEnv.vars) - varNames.insert(i.first); + varNames.clear(); + for (auto& i : state.staticBaseEnv.vars) varNames.insert(i.first); } +void NixRepl::reloadFiles() { + initEnv(); -void NixRepl::reloadFiles() -{ - initEnv(); + Strings old = loadedFiles; + loadedFiles.clear(); - Strings old = loadedFiles; - loadedFiles.clear(); - - bool first = true; - for (auto & i : old) { - if (!first) std::cout << std::endl; - first = false; - std::cout << format("Loading '%1%'...") % i << std::endl; - loadFile(i); - } + bool first = true; + for (auto& i : old) { + if (!first) std::cout << std::endl; + first = false; + std::cout << format("Loading '%1%'...") % i << std::endl; + loadFile(i); + } } - -void NixRepl::addAttrsToScope(Value & attrs) -{ - state.forceAttrs(attrs); - for (auto & i : *attrs.attrs) - addVarToScope(i.name, *i.value); - std::cout << format("Added %1% variables.") % attrs.attrs->size() << std::endl; +void NixRepl::addAttrsToScope(Value& attrs) { + state.forceAttrs(attrs); + for (auto& i : *attrs.attrs) addVarToScope(i.name, *i.value); + std::cout << format("Added %1% variables.") % attrs.attrs->size() + << std::endl; } - -void NixRepl::addVarToScope(const Symbol & name, Value & v) -{ - if (displ >= envSize) - throw Error("environment full; cannot add more variables"); - staticEnv.vars[name] = displ; - env->values[displ++] = &v; - varNames.insert((string) name); +void NixRepl::addVarToScope(const Symbol& name, Value& v) { + if (displ >= envSize) + throw Error("environment full; cannot add more variables"); + staticEnv.vars[name] = displ; + env->values[displ++] = &v; + varNames.insert((string)name); } - -Expr * NixRepl::parseString(string s) -{ - Expr * e = state.parseExprFromString(s, curDir, staticEnv); - return e; +Expr* NixRepl::parseString(string s) { + Expr* e = state.parseExprFromString(s, curDir, staticEnv); + return e; } - -void NixRepl::evalString(string s, Value & v) -{ - Expr * e = parseString(s); - e->eval(state, *env, v); - state.forceValue(v); +void NixRepl::evalString(string s, Value& v) { + Expr* e = parseString(s); + e->eval(state, *env, v); + state.forceValue(v); } - -std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth) -{ - ValuesSeen seen; - return printValue(str, v, maxDepth, seen); +std::ostream& NixRepl::printValue(std::ostream& str, Value& v, + unsigned int maxDepth) { + ValuesSeen seen; + return printValue(str, v, maxDepth, seen); } - -std::ostream & printStringValue(std::ostream & str, const char * string) { - str << "\""; - for (const char * i = string; *i; i++) - if (*i == '\"' || *i == '\\') str << "\\" << *i; - else if (*i == '\n') str << "\\n"; - else if (*i == '\r') str << "\\r"; - else if (*i == '\t') str << "\\t"; - else str << *i; - str << "\""; - return str; +std::ostream& printStringValue(std::ostream& str, const char* string) { + str << "\""; + for (const char* i = string; *i; i++) + if (*i == '\"' || *i == '\\') + str << "\\" << *i; + else if (*i == '\n') + str << "\\n"; + else if (*i == '\r') + str << "\\r"; + else if (*i == '\t') + str << "\\t"; + else + str << *i; + str << "\""; + return str; } - // FIXME: lot of cut&paste from Nix's eval.cc. -std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen) -{ - str.flush(); - checkInterrupt(); +std::ostream& NixRepl::printValue(std::ostream& str, Value& v, + unsigned int maxDepth, ValuesSeen& seen) { + str.flush(); + checkInterrupt(); - state.forceValue(v); - - switch (v.type) { + state.forceValue(v); + switch (v.type) { case tInt: - str << ESC_CYA << v.integer << ESC_END; - break; + str << ESC_CYA << v.integer << ESC_END; + break; case tBool: - str << ESC_CYA << (v.boolean ? "true" : "false") << ESC_END; - break; + str << ESC_CYA << (v.boolean ? "true" : "false") << ESC_END; + break; case tString: - str << ESC_YEL; - printStringValue(str, v.string.s); - str << ESC_END; - break; + str << ESC_YEL; + printStringValue(str, v.string.s); + str << ESC_END; + break; case tPath: - str << ESC_GRE << v.path << ESC_END; // !!! escaping? - break; + str << ESC_GRE << v.path << ESC_END; // !!! escaping? + break; case tNull: - str << ESC_CYA "null" ESC_END; - break; + str << ESC_CYA "null" ESC_END; + break; case tAttrs: { - seen.insert(&v); - - bool isDrv = state.isDerivation(v); - - if (isDrv) { - str << "«derivation "; - Bindings::iterator i = v.attrs->find(state.sDrvPath); - PathSet context; - Path drvPath = i != v.attrs->end() ? state.coerceToPath(*i->pos, *i->value, context) : "???"; - str << drvPath << "»"; - } + seen.insert(&v); + + bool isDrv = state.isDerivation(v); + + if (isDrv) { + str << "«derivation "; + Bindings::iterator i = v.attrs->find(state.sDrvPath); + PathSet context; + Path drvPath = i != v.attrs->end() + ? state.coerceToPath(*i->pos, *i->value, context) + : "???"; + str << drvPath << "»"; + } - else if (maxDepth > 0) { - str << "{ "; - - typedef std::map<string, Value *> Sorted; - Sorted sorted; - for (auto & i : *v.attrs) - sorted[i.name] = i.value; - - for (auto & i : sorted) { - if (isVarName(i.first)) - str << i.first; - else - printStringValue(str, i.first.c_str()); - str << " = "; - if (seen.find(i.second) != seen.end()) - str << "«repeated»"; - else - try { - printValue(str, *i.second, maxDepth - 1, seen); - } catch (AssertionError & e) { - str << ESC_RED "«error: " << e.msg() << "»" ESC_END; - } - str << "; "; + else if (maxDepth > 0) { + str << "{ "; + + typedef std::map<string, Value*> Sorted; + Sorted sorted; + for (auto& i : *v.attrs) sorted[i.name] = i.value; + + for (auto& i : sorted) { + if (isVarName(i.first)) + str << i.first; + else + printStringValue(str, i.first.c_str()); + str << " = "; + if (seen.find(i.second) != seen.end()) + str << "«repeated»"; + else + try { + printValue(str, *i.second, maxDepth - 1, seen); + } catch (AssertionError& e) { + str << ESC_RED "«error: " << e.msg() << "»" ESC_END; } + str << "; "; + } - str << "}"; - } else - str << "{ ... }"; + str << "}"; + } else + str << "{ ... }"; - break; + break; } case tList1: case tList2: case tListN: - seen.insert(&v); - - str << "[ "; - if (maxDepth > 0) - for (unsigned int n = 0; n < v.listSize(); ++n) { - if (seen.find(v.listElems()[n]) != seen.end()) - str << "«repeated»"; - else - try { - printValue(str, *v.listElems()[n], maxDepth - 1, seen); - } catch (AssertionError & e) { - str << ESC_RED "«error: " << e.msg() << "»" ESC_END; - } - str << " "; + seen.insert(&v); + + str << "[ "; + if (maxDepth > 0) + for (unsigned int n = 0; n < v.listSize(); ++n) { + if (seen.find(v.listElems()[n]) != seen.end()) + str << "«repeated»"; + else + try { + printValue(str, *v.listElems()[n], maxDepth - 1, seen); + } catch (AssertionError& e) { + str << ESC_RED "«error: " << e.msg() << "»" ESC_END; } - else - str << "... "; - str << "]"; - break; + str << " "; + } + else + str << "... "; + str << "]"; + break; case tLambda: { - std::ostringstream s; - s << v.lambda.fun->pos; - str << ESC_BLU "«lambda @ " << filterANSIEscapes(s.str()) << "»" ESC_END; - break; + std::ostringstream s; + s << v.lambda.fun->pos; + str << ESC_BLU "«lambda @ " << filterANSIEscapes(s.str()) << "»" ESC_END; + break; } case tPrimOp: - str << ESC_MAG "«primop»" ESC_END; - break; + str << ESC_MAG "«primop»" ESC_END; + break; case tPrimOpApp: - str << ESC_BLU "«primop-app»" ESC_END; - break; + str << ESC_BLU "«primop-app»" ESC_END; + break; case tFloat: - str << v.fpoint; - break; + str << v.fpoint; + break; default: - str << ESC_RED "«unknown»" ESC_END; - break; - } + str << ESC_RED "«unknown»" ESC_END; + break; + } - return str; + return str; } -struct CmdRepl : StoreCommand, MixEvalArgs -{ - std::vector<std::string> files; +struct CmdRepl : StoreCommand, MixEvalArgs { + std::vector<std::string> files; - CmdRepl() - { - expectArgs("files", &files); - } + CmdRepl() { expectArgs("files", &files); } - std::string name() override { return "repl"; } + std::string name() override { return "repl"; } - std::string description() override - { - return "start an interactive environment for evaluating Nix expressions"; - } + std::string description() override { + return "start an interactive environment for evaluating Nix expressions"; + } - void run(ref<Store> store) override - { - auto repl = std::make_unique<NixRepl>(searchPath, openStore()); - repl->autoArgs = getAutoArgs(repl->state); - repl->mainLoop(files); - } + void run(ref<Store> store) override { + auto repl = std::make_unique<NixRepl>(searchPath, openStore()); + repl->autoArgs = getAutoArgs(repl->state); + repl->mainLoop(files); + } }; static RegisterCommand r1(make_ref<CmdRepl>()); -} +} // namespace nix diff --git a/third_party/nix/src/nix/run.cc b/third_party/nix/src/nix/run.cc index 90b76d6663e9..d78589172c1b 100644 --- a/third_party/nix/src/nix/run.cc +++ b/third_party/nix/src/nix/run.cc @@ -1,13 +1,13 @@ +#include "affinity.hh" #include "command.hh" #include "common-args.hh" -#include "shared.hh" -#include "store-api.hh" #include "derivations.hh" -#include "local-store.hh" #include "finally.hh" #include "fs-accessor.hh" +#include "local-store.hh" #include "progress-bar.hh" -#include "affinity.hh" +#include "shared.hh" +#include "store-api.hh" #if __linux__ #include <sys/mount.h> @@ -19,242 +19,229 @@ using namespace nix; std::string chrootHelperName = "__run_in_chroot"; -struct CmdRun : InstallablesCommand -{ - std::vector<std::string> command = { "bash" }; - StringSet keep, unset; - bool ignoreEnvironment = false; - - CmdRun() - { - mkFlag() - .longName("command") - .shortName('c') - .description("command and arguments to be executed; defaults to 'bash'") - .labels({"command", "args"}) - .arity(ArityAny) - .handler([&](std::vector<std::string> ss) { - 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<std::string> ss) { keep.insert(ss.front()); }); - - mkFlag() - .longName("unset") - .shortName('u') - .description("unset specified environment variable") - .arity(1) - .labels({"name"}) - .handler([&](std::vector<std::string> 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"; - } - - Examples examples() override - { - return { - Example{ - "To start a shell providing GNU Hello from NixOS 17.03:", - "nix run -f channel:nixos-17.03 hello" - }, - Example{ - "To start a shell providing youtube-dl from your 'nixpkgs' channel:", - "nix run nixpkgs.youtube-dl" - }, - Example{ - "To run GNU Hello:", - "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" - }, - }; +struct CmdRun : InstallablesCommand { + std::vector<std::string> command = {"bash"}; + StringSet keep, unset; + bool ignoreEnvironment = false; + + CmdRun() { + mkFlag() + .longName("command") + .shortName('c') + .description("command and arguments to be executed; defaults to 'bash'") + .labels({"command", "args"}) + .arity(ArityAny) + .handler([&](std::vector<std::string> ss) { + 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<std::string> ss) { keep.insert(ss.front()); }); + + mkFlag() + .longName("unset") + .shortName('u') + .description("unset specified environment variable") + .arity(1) + .labels({"name"}) + .handler( + [&](std::vector<std::string> 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"; + } + + Examples examples() override { + return { + Example{"To start a shell providing GNU Hello from NixOS 17.03:", + "nix run -f channel:nixos-17.03 hello"}, + Example{"To start a shell providing youtube-dl from your 'nixpkgs' " + "channel:", + "nix run nixpkgs.youtube-dl"}, + Example{"To run GNU Hello:", + "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"}, + }; + } + + void run(ref<Store> store) override { + auto outPaths = toStorePaths(store, Build, installables); + + auto accessor = store->getFSAccessor(); + + if (ignoreEnvironment) { + if (!unset.empty()) + throw UsageError( + "--unset does not make sense with --ignore-environment"); + + std::map<std::string, std::string> kept; + for (auto& var : keep) { + auto s = getenv(var.c_str()); + if (s) kept[var] = s; + } + + clearEnv(); + + for (auto& var : kept) setenv(var.first.c_str(), var.second.c_str(), 1); + + } else { + if (!keep.empty()) + throw UsageError( + "--keep does not make sense without --ignore-environment"); + + for (auto& var : unset) unsetenv(var.c_str()); } - void run(ref<Store> store) override - { - auto outPaths = toStorePaths(store, Build, installables); - - auto accessor = store->getFSAccessor(); - - if (ignoreEnvironment) { - - if (!unset.empty()) - throw UsageError("--unset does not make sense with --ignore-environment"); - - std::map<std::string, std::string> kept; - for (auto & var : keep) { - auto s = getenv(var.c_str()); - if (s) kept[var] = s; - } + std::unordered_set<Path> done; + std::queue<Path> todo; + for (auto& path : outPaths) todo.push(path); - clearEnv(); + auto unixPath = tokenizeString<Strings>(getEnv("PATH"), ":"); - for (auto & var : kept) - setenv(var.first.c_str(), var.second.c_str(), 1); + while (!todo.empty()) { + Path path = todo.front(); + todo.pop(); + if (!done.insert(path).second) continue; - } else { + if (true) unixPath.push_front(path + "/bin"); - if (!keep.empty()) - throw UsageError("--keep does not make sense without --ignore-environment"); - - for (auto & var : unset) - unsetenv(var.c_str()); - } - - std::unordered_set<Path> done; - std::queue<Path> todo; - for (auto & path : outPaths) todo.push(path); - - auto unixPath = tokenizeString<Strings>(getEnv("PATH"), ":"); + auto propPath = path + "/nix-support/propagated-user-env-packages"; + if (accessor->stat(propPath).type == FSAccessor::tRegular) { + for (auto& p : tokenizeString<Paths>(readFile(propPath))) todo.push(p); + } + } - while (!todo.empty()) { - Path path = todo.front(); - todo.pop(); - if (!done.insert(path).second) continue; + setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); - if (true) - unixPath.push_front(path + "/bin"); + std::string cmd = *command.begin(); + Strings args; + for (auto& arg : command) args.push_back(arg); - auto propPath = path + "/nix-support/propagated-user-env-packages"; - if (accessor->stat(propPath).type == FSAccessor::tRegular) { - for (auto & p : tokenizeString<Paths>(readFile(propPath))) - todo.push(p); - } - } + stopProgressBar(); - setenv("PATH", concatStringsSep(":", unixPath).c_str(), 1); + restoreSignals(); - std::string cmd = *command.begin(); - Strings args; - for (auto & arg : command) args.push_back(arg); + restoreAffinity(); - stopProgressBar(); + /* 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<LocalStore>(); - restoreSignals(); + if (store2 && store->storeDir != store2->realStoreDir) { + Strings helperArgs = {chrootHelperName, store->storeDir, + store2->realStoreDir, cmd}; + for (auto& arg : args) helperArgs.push_back(arg); - restoreAffinity(); + execv(readLink("/proc/self/exe").c_str(), + stringsToCharPtrs(helperArgs).data()); - /* 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<LocalStore>(); + throw SysError("could not execute chroot helper"); + } - if (store2 && store->storeDir != store2->realStoreDir) { - Strings helperArgs = { chrootHelperName, store->storeDir, store2->realStoreDir, cmd }; - for (auto & arg : args) helperArgs.push_back(arg); + execvp(cmd.c_str(), stringsToCharPtrs(args).data()); - execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data()); + throw SysError("unable to exec '%s'", cmd); + } +}; - throw SysError("could not execute chroot helper"); - } +static RegisterCommand r1(make_ref<CmdRun>()); - execvp(cmd.c_str(), stringsToCharPtrs(args).data()); +void chrootHelper(int argc, char** argv) { + int p = 1; + std::string storeDir = argv[p++]; + std::string realStoreDir = argv[p++]; + std::string cmd = argv[p++]; + Strings args; + while (p < argc) args.push_back(argv[p++]); - throw SysError("unable to exec '%s'", cmd); +#if __linux__ + uid_t uid = getuid(); + uid_t gid = getgid(); + + if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == -1) + /* Try with just CLONE_NEWNS in case user namespaces are + specifically disabled. */ + if (unshare(CLONE_NEWNS) == -1) + throw SysError("setting up a private mount namespace"); + + /* Bind-mount realStoreDir on /nix/store. If the latter mount + point doesn't already exists, we have to create a chroot + environment containing the mount point and bind mounts for the + children of /. Would be nice if we could use overlayfs here, + but that doesn't work in a user namespace yet (Ubuntu has a + patch for this: + https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1478578). */ + if (!pathExists(storeDir)) { + // FIXME: Use overlayfs? + + Path tmpDir = createTempDir(); + + createDirs(tmpDir + storeDir); + + if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, + 0) == -1) + throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); + + for (auto entry : readDirectory("/")) { + auto src = "/" + entry.name; + auto st = lstat(src); + if (!S_ISDIR(st.st_mode)) continue; + Path dst = tmpDir + "/" + entry.name; + if (pathExists(dst)) continue; + if (mkdir(dst.c_str(), 0700) == -1) + throw SysError("creating directory '%s'", dst); + if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == -1) + throw SysError("mounting '%s' on '%s'", src, dst); } -}; -static RegisterCommand r1(make_ref<CmdRun>()); + char* cwd = getcwd(0, 0); + if (!cwd) throw SysError("getting current directory"); + Finally freeCwd([&]() { free(cwd); }); -void chrootHelper(int argc, char * * argv) -{ - int p = 1; - std::string storeDir = argv[p++]; - std::string realStoreDir = argv[p++]; - std::string cmd = argv[p++]; - Strings args; - while (p < argc) - args.push_back(argv[p++]); + if (chroot(tmpDir.c_str()) == -1) + throw SysError(format("chrooting into '%s'") % tmpDir); -#if __linux__ - uid_t uid = getuid(); - uid_t gid = getgid(); - - if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == -1) - /* Try with just CLONE_NEWNS in case user namespaces are - specifically disabled. */ - if (unshare(CLONE_NEWNS) == -1) - throw SysError("setting up a private mount namespace"); - - /* Bind-mount realStoreDir on /nix/store. If the latter mount - point doesn't already exists, we have to create a chroot - environment containing the mount point and bind mounts for the - children of /. Would be nice if we could use overlayfs here, - but that doesn't work in a user namespace yet (Ubuntu has a - patch for this: - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1478578). */ - if (!pathExists(storeDir)) { - // FIXME: Use overlayfs? - - Path tmpDir = createTempDir(); - - createDirs(tmpDir + storeDir); - - if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, 0) == -1) - throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - - for (auto entry : readDirectory("/")) { - auto src = "/" + entry.name; - auto st = lstat(src); - if (!S_ISDIR(st.st_mode)) continue; - Path dst = tmpDir + "/" + entry.name; - if (pathExists(dst)) continue; - if (mkdir(dst.c_str(), 0700) == -1) - throw SysError("creating directory '%s'", dst); - if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == -1) - throw SysError("mounting '%s' on '%s'", src, dst); - } - - char * cwd = getcwd(0, 0); - if (!cwd) throw SysError("getting current directory"); - Finally freeCwd([&]() { free(cwd); }); - - if (chroot(tmpDir.c_str()) == -1) - throw SysError(format("chrooting into '%s'") % tmpDir); - - if (chdir(cwd) == -1) - throw SysError(format("chdir to '%s' in chroot") % cwd); - } else - if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == -1) - throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - - writeFile("/proc/self/setgroups", "deny"); - writeFile("/proc/self/uid_map", fmt("%d %d %d", uid, uid, 1)); - writeFile("/proc/self/gid_map", fmt("%d %d %d", gid, gid, 1)); + if (chdir(cwd) == -1) + throw SysError(format("chdir to '%s' in chroot") % cwd); + } else if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == + -1) + throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - execvp(cmd.c_str(), stringsToCharPtrs(args).data()); + writeFile("/proc/self/setgroups", "deny"); + writeFile("/proc/self/uid_map", fmt("%d %d %d", uid, uid, 1)); + writeFile("/proc/self/gid_map", fmt("%d %d %d", gid, gid, 1)); - throw SysError("unable to exec '%s'", cmd); + execvp(cmd.c_str(), stringsToCharPtrs(args).data()); + + throw SysError("unable to exec '%s'", cmd); #else - throw Error("mounting the Nix store on '%s' is not supported on this platform", storeDir); + throw Error( + "mounting the Nix store on '%s' is not supported on this platform", + storeDir); #endif } diff --git a/third_party/nix/src/nix/search.cc b/third_party/nix/src/nix/search.cc index eb75493e4759..fab8bba6283a 100644 --- a/third_party/nix/src/nix/search.cc +++ b/third_party/nix/src/nix/search.cc @@ -1,280 +1,262 @@ +#include <fstream> +#include <regex> #include "command.hh" -#include "globals.hh" -#include "eval.hh" +#include "common-args.hh" #include "eval-inline.hh" -#include "names.hh" +#include "eval.hh" #include "get-drvs.hh" -#include "common-args.hh" -#include "json.hh" +#include "globals.hh" #include "json-to-value.hh" +#include "json.hh" +#include "names.hh" #include "shared.hh" -#include <regex> -#include <fstream> - using namespace nix; -std::string wrap(std::string prefix, std::string s) -{ - return prefix + s + ANSI_NORMAL; +std::string wrap(std::string prefix, std::string s) { + return prefix + s + ANSI_NORMAL; } -std::string hilite(const std::string & s, const std::smatch & m, std::string postfix) -{ - return - m.empty() - ? s - : std::string(m.prefix()) - + ANSI_RED + std::string(m.str()) + postfix - + std::string(m.suffix()); +std::string hilite(const std::string& s, const std::smatch& m, + std::string postfix) { + return m.empty() ? s + : std::string(m.prefix()) + ANSI_RED + std::string(m.str()) + + postfix + std::string(m.suffix()); } -struct CmdSearch : SourceExprCommand, MixJSON -{ - std::vector<std::string> res; +struct CmdSearch : SourceExprCommand, MixJSON { + std::vector<std::string> 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 name() override { return "search"; } + + std::string description() override { return "query available packages"; } + + Examples examples() override { + return {Example{"To show all available packages:", "nix search"}, + Example{"To show any packages containing 'blender' in its name or " + "description:", + "nix search blender"}, + Example{"To search for Firefox or Chromium:", + "nix search 'firefox|chromium'"}, + Example{"To search for git and frontend or gui:", + "nix search git 'frontend|gui'"}}; + } + + void run(ref<Store> store) override { + settings.readOnlyMode = true; + + // 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()) { + res.push_back("^"); + } - bool writeCache = true; - bool useCache = true; + std::vector<std::regex> regexes; + regexes.reserve(res.size()); - CmdSearch() - { - expectArgs("regex", &res); + for (auto& re : res) { + regexes.push_back( + std::regex(re, std::regex::extended | std::regex::icase)); + } - mkFlag() - .longName("update-cache") - .shortName('u') - .description("update the package search cache") - .handler([&]() { writeCache = true; useCache = false; }); + auto state = getEvalState(); - mkFlag() - .longName("no-cache") - .description("do not use or update the package search cache") - .handler([&]() { writeCache = false; useCache = false; }); - } + auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr; - std::string name() override - { - return "search"; - } + auto sToplevel = state->symbols.create("_toplevel"); + auto sRecurse = state->symbols.create("recurseForDerivations"); - std::string description() override - { - return "query available packages"; - } + bool fromCache = false; - Examples examples() override - { - return { - Example{ - "To show all available packages:", - "nix search" - }, - Example{ - "To show any packages containing 'blender' in its name or description:", - "nix search blender" - }, - Example{ - "To search for Firefox or Chromium:", - "nix search 'firefox|chromium'" - }, - Example{ - "To search for git and frontend or gui:", - "nix search git 'frontend|gui'" - } - }; - } + std::map<std::string, std::string> results; - void run(ref<Store> store) override - { - settings.readOnlyMode = true; + std::function<void(Value*, std::string, bool, JSONObject*)> doExpr; - // 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()) { - res.push_back("^"); - } + doExpr = [&](Value* v, std::string attrPath, bool toplevel, + JSONObject* cache) { + debug("at attribute '%s'", attrPath); + + try { + uint found = 0; - std::vector<std::regex> regexes; - regexes.reserve(res.size()); + state->forceValue(*v); - for (auto &re : res) { - regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); + if (v->type == tLambda && toplevel) { + Value* v2 = state->allocValue(); + state->autoCallFunction(*state->allocBindings(1), *v, *v2); + v = v2; + state->forceValue(*v); } - auto state = getEvalState(); + if (state->isDerivation(*v)) { + DrvInfo drv(*state, attrPath, v->attrs); + std::string description; + std::smatch attrPathMatch; + std::smatch descriptionMatch; + std::smatch nameMatch; + std::string name; - auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr; - - auto sToplevel = state->symbols.create("_toplevel"); - auto sRecurse = state->symbols.create("recurseForDerivations"); - - bool fromCache = false; - - std::map<std::string, std::string> results; - - std::function<void(Value *, std::string, bool, JSONObject *)> doExpr; - - doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) { - debug("at attribute '%s'", attrPath); - - try { - uint found = 0; - - state->forceValue(*v); - - if (v->type == tLambda && toplevel) { - Value * v2 = state->allocValue(); - state->autoCallFunction(*state->allocBindings(1), *v, *v2); - v = v2; - state->forceValue(*v); - } - - if (state->isDerivation(*v)) { - - 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', ' '); - std::regex_search(description, descriptionMatch, regex); - - if (!attrPathMatch.empty() - || !nameMatch.empty() - || !descriptionMatch.empty()) - { - found++; - } - } - - if (found == res.size()) { - if (json) { - - auto jsonElem = jsonOut->object(attrPath); - - jsonElem.attr("pkgName", parsed.name); - jsonElem.attr("version", parsed.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); - } - } - } - - else if (v->type == tAttrs) { - - 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<JSONObject>(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)); - throw; - } - } - }; + DrvName parsed(drv.queryName()); - Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json"; + for (auto& regex : regexes) { + std::regex_search(attrPath, attrPathMatch, regex); - if (useCache && pathExists(jsonCacheFileName)) { + name = parsed.name; + std::regex_search(name, nameMatch, regex); - warn("using cached results; pass '-u' to update the cache"); + description = drv.queryMetaString("description"); + std::replace(description.begin(), description.end(), '\n', ' '); + std::regex_search(description, descriptionMatch, regex); - Value vRoot; - parseJSON(*state, readFile(jsonCacheFileName), vRoot); + if (!attrPathMatch.empty() || !nameMatch.empty() || + !descriptionMatch.empty()) { + found++; + } + } + + if (found == res.size()) { + if (json) { + auto jsonElem = jsonOut->object(attrPath); + + jsonElem.attr("pkgName", parsed.name); + jsonElem.attr("version", parsed.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); + } + } + } - fromCache = true; + else if (v->type == tAttrs) { + 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<JSONObject>(cache->object(i.name)) + : nullptr; + doExpr(i.value, + attrPath == "" ? (std::string)i.name + : attrPath + "." + (std::string)i.name, + toplevel2 || fromCache, cache2 ? cache2.get() : nullptr); + } + } - doExpr(&vRoot, "", true, nullptr); + } catch (AssertionError& e) { + } catch (Error& e) { + if (!toplevel) { + e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath)); + throw; } + } + }; - else { - createDirs(dirOf(jsonCacheFileName)); + Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json"; - Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid()); + if (useCache && pathExists(jsonCacheFileName)) { + warn("using cached results; pass '-u' to update the cache"); - std::ofstream jsonCacheFile; + Value vRoot; + parseJSON(*state, readFile(jsonCacheFileName), vRoot); - try { - // iostream considered harmful - jsonCacheFile.exceptions(std::ofstream::failbit); - jsonCacheFile.open(tmpFile); + fromCache = true; - auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr; + doExpr(&vRoot, "", true, nullptr); + } - doExpr(getSourceExpr(*state), "", true, cache.get()); + else { + createDirs(dirOf(jsonCacheFileName)); - } 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; - } + Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid()); - if (writeCache && rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1) - throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName); - } + std::ofstream jsonCacheFile; - if (results.size() == 0) - throw Error("no results for the given search term(s)!"); + try { + // iostream considered harmful + jsonCacheFile.exceptions(std::ofstream::failbit); + jsonCacheFile.open(tmpFile); - RunPager pager; - for (auto el : results) std::cout << el.second << "\n"; + auto cache = writeCache + ? std::make_unique<JSONObject>(jsonCacheFile, false) + : nullptr; + 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) + throw Error("no results for the given search term(s)!"); + + RunPager pager; + for (auto el : results) std::cout << el.second << "\n"; + } }; static RegisterCommand r1(make_ref<CmdSearch>()); diff --git a/third_party/nix/src/nix/show-config.cc b/third_party/nix/src/nix/show-config.cc index 86638b50d2c6..dba94732a9c3 100644 --- a/third_party/nix/src/nix/show-config.cc +++ b/third_party/nix/src/nix/show-config.cc @@ -1,40 +1,30 @@ #include "command.hh" #include "common-args.hh" +#include "json.hh" #include "shared.hh" #include "store-api.hh" -#include "json.hh" using namespace nix; -struct CmdShowConfig : Command, MixJSON -{ - CmdShowConfig() - { - } +struct CmdShowConfig : Command, MixJSON { + CmdShowConfig() {} - std::string name() override - { - return "show-config"; - } + std::string name() override { return "show-config"; } - std::string description() override - { - return "show the Nix configuration"; - } + std::string description() override { return "show the Nix configuration"; } - void run() override - { - if (json) { - // FIXME: use appropriate JSON types (bool, ints, etc). - JSONObject jsonObj(std::cout); - globalConfig.toJSON(jsonObj); - } else { - std::map<std::string, Config::SettingInfo> settings; - globalConfig.getSettings(settings); - for (auto & s : settings) - std::cout << s.first + " = " + s.second.value + "\n"; - } + void run() override { + if (json) { + // FIXME: use appropriate JSON types (bool, ints, etc). + JSONObject jsonObj(std::cout); + globalConfig.toJSON(jsonObj); + } else { + std::map<std::string, Config::SettingInfo> settings; + globalConfig.getSettings(settings); + for (auto& s : settings) + std::cout << s.first + " = " + s.second.value + "\n"; } + } }; static RegisterCommand r1(make_ref<CmdShowConfig>()); diff --git a/third_party/nix/src/nix/show-derivation.cc b/third_party/nix/src/nix/show-derivation.cc index ee94fded364f..44e33cef4c98 100644 --- a/third_party/nix/src/nix/show-derivation.cc +++ b/third_party/nix/src/nix/show-derivation.cc @@ -1,119 +1,103 @@ // FIXME: integrate this with nix path-info? +#include "archive.hh" #include "command.hh" #include "common-args.hh" -#include "store-api.hh" -#include "archive.hh" -#include "json.hh" #include "derivations.hh" +#include "json.hh" +#include "store-api.hh" using namespace nix; -struct CmdShowDerivation : InstallablesCommand -{ - bool recursive = false; - - CmdShowDerivation() - { - mkFlag() - .longName("recursive") - .shortName('r') - .description("include the dependencies of the specified derivations") - .set(&recursive, true); - } - - std::string name() override - { - return "show-derivation"; +struct CmdShowDerivation : InstallablesCommand { + bool recursive = false; + + CmdShowDerivation() { + mkFlag() + .longName("recursive") + .shortName('r') + .description("include the dependencies of the specified derivations") + .set(&recursive, true); + } + + std::string name() override { return "show-derivation"; } + + std::string description() override { + return "show the contents of a store derivation"; + } + + Examples examples() override { + return { + Example{"To show the store derivation that results from evaluating the " + "Hello package:", + "nix show-derivation nixpkgs.hello"}, + Example{"To show the full derivation graph (if available) that " + "produced your NixOS system:", + "nix show-derivation -r /run/current-system"}, + }; + } + + void run(ref<Store> store) override { + auto drvPaths = toDerivations(store, installables, true); + + if (recursive) { + PathSet closure; + store->computeFSClosure(drvPaths, closure); + drvPaths = closure; } - std::string description() override { - return "show the contents of a store derivation"; - } + JSONObject jsonRoot(std::cout, true); - Examples examples() override - { - return { - Example{ - "To show the store derivation that results from evaluating the Hello package:", - "nix show-derivation nixpkgs.hello" - }, - Example{ - "To show the full derivation graph (if available) that produced your NixOS system:", - "nix show-derivation -r /run/current-system" - }, - }; - } + for (auto& drvPath : drvPaths) { + if (!isDerivation(drvPath)) continue; - void run(ref<Store> store) override - { - auto drvPaths = toDerivations(store, installables, true); + auto drvObj(jsonRoot.object(drvPath)); - if (recursive) { - PathSet closure; - store->computeFSClosure(drvPaths, closure); - drvPaths = closure; - } + auto drv = readDerivation(drvPath); { - - JSONObject jsonRoot(std::cout, true); - - for (auto & drvPath : drvPaths) { - if (!isDerivation(drvPath)) continue; - - auto drvObj(jsonRoot.object(drvPath)); - - auto drv = readDerivation(drvPath); - - { - auto outputsObj(drvObj.object("outputs")); - for (auto & output : drv.outputs) { - auto outputObj(outputsObj.object(output.first)); - outputObj.attr("path", output.second.path); - if (output.second.hash != "") { - outputObj.attr("hashAlgo", output.second.hashAlgo); - outputObj.attr("hash", output.second.hash); - } - } - } - - { - auto inputsList(drvObj.list("inputSrcs")); - for (auto & input : drv.inputSrcs) - inputsList.elem(input); + auto outputsObj(drvObj.object("outputs")); + for (auto& output : drv.outputs) { + auto outputObj(outputsObj.object(output.first)); + outputObj.attr("path", output.second.path); + if (output.second.hash != "") { + outputObj.attr("hashAlgo", output.second.hashAlgo); + outputObj.attr("hash", output.second.hash); } + } + } - { - auto inputDrvsObj(drvObj.object("inputDrvs")); - for (auto & input : drv.inputDrvs) { - auto inputList(inputDrvsObj.list(input.first)); - for (auto & outputId : input.second) - inputList.elem(outputId); - } - } + { + auto inputsList(drvObj.list("inputSrcs")); + for (auto& input : drv.inputSrcs) inputsList.elem(input); + } - drvObj.attr("platform", drv.platform); - drvObj.attr("builder", drv.builder); + { + auto inputDrvsObj(drvObj.object("inputDrvs")); + for (auto& input : drv.inputDrvs) { + auto inputList(inputDrvsObj.list(input.first)); + for (auto& outputId : input.second) inputList.elem(outputId); + } + } - { - auto argsList(drvObj.list("args")); - for (auto & arg : drv.args) - argsList.elem(arg); - } + drvObj.attr("platform", drv.platform); + drvObj.attr("builder", drv.builder); - { - auto envObj(drvObj.object("env")); - for (auto & var : drv.env) - envObj.attr(var.first, var.second); - } + { + auto argsList(drvObj.list("args")); + for (auto& arg : drv.args) argsList.elem(arg); } + { + auto envObj(drvObj.object("env")); + for (auto& var : drv.env) envObj.attr(var.first, var.second); } - - std::cout << "\n"; + } } + + std::cout << "\n"; + } }; static RegisterCommand r1(make_ref<CmdShowDerivation>()); diff --git a/third_party/nix/src/nix/sigs.cc b/third_party/nix/src/nix/sigs.cc index b1825c412c2d..da453d1486fe 100644 --- a/third_party/nix/src/nix/sigs.cc +++ b/third_party/nix/src/nix/sigs.cc @@ -1,149 +1,133 @@ +#include <atomic> #include "command.hh" #include "shared.hh" #include "store-api.hh" #include "thread-pool.hh" -#include <atomic> - using namespace nix; -struct CmdCopySigs : StorePathsCommand -{ - Strings substituterUris; - - CmdCopySigs() - { - mkFlag() - .longName("substituter") - .shortName('s') - .labels({"store-uri"}) - .description("use signatures from specified store") - .arity(1) - .handler([&](std::vector<std::string> ss) { substituterUris.push_back(ss[0]); }); - } +struct CmdCopySigs : StorePathsCommand { + Strings substituterUris; - std::string name() override - { - return "copy-sigs"; - } + CmdCopySigs() { + mkFlag() + .longName("substituter") + .shortName('s') + .labels({"store-uri"}) + .description("use signatures from specified store") + .arity(1) + .handler([&](std::vector<std::string> ss) { + substituterUris.push_back(ss[0]); + }); + } - std::string description() override - { - return "copy path signatures from substituters (like binary caches)"; - } + std::string name() override { return "copy-sigs"; } + + std::string description() override { + return "copy path signatures from substituters (like binary caches)"; + } - void run(ref<Store> store, Paths storePaths) override - { - if (substituterUris.empty()) - throw UsageError("you must specify at least one substituter using '-s'"); + void run(ref<Store> store, Paths storePaths) override { + if (substituterUris.empty()) + throw UsageError("you must specify at least one substituter using '-s'"); - // FIXME: factor out commonality with MixVerify. - std::vector<ref<Store>> substituters; - for (auto & s : substituterUris) - substituters.push_back(openStore(s)); + // FIXME: factor out commonality with MixVerify. + std::vector<ref<Store>> substituters; + for (auto& s : substituterUris) substituters.push_back(openStore(s)); - ThreadPool pool; + ThreadPool pool; - std::string doneLabel = "done"; - std::atomic<size_t> added{0}; + std::string doneLabel = "done"; + std::atomic<size_t> added{0}; - //logger->setExpected(doneLabel, storePaths.size()); + // logger->setExpected(doneLabel, storePaths.size()); - auto doPath = [&](const Path & storePath) { - //Activity act(*logger, lvlInfo, format("getting signatures for '%s'") % storePath); + auto doPath = [&](const Path& storePath) { + // Activity act(*logger, lvlInfo, format("getting signatures for '%s'") % + // storePath); - checkInterrupt(); + checkInterrupt(); - auto info = store->queryPathInfo(storePath); + auto info = store->queryPathInfo(storePath); - StringSet newSigs; + StringSet newSigs; - for (auto & store2 : substituters) { - try { - auto info2 = store2->queryPathInfo(storePath); + for (auto& store2 : substituters) { + try { + auto info2 = store2->queryPathInfo(storePath); - /* Don't import signatures that don't match this - binary. */ - if (info->narHash != info2->narHash || - info->narSize != info2->narSize || - info->references != info2->references) - continue; + /* Don't import signatures that don't match this + binary. */ + if (info->narHash != info2->narHash || + info->narSize != info2->narSize || + info->references != info2->references) + continue; - for (auto & sig : info2->sigs) - if (!info->sigs.count(sig)) - newSigs.insert(sig); - } catch (InvalidPath &) { - } - } + for (auto& sig : info2->sigs) + if (!info->sigs.count(sig)) newSigs.insert(sig); + } catch (InvalidPath&) { + } + } - if (!newSigs.empty()) { - store->addSignatures(storePath, newSigs); - added += newSigs.size(); - } + if (!newSigs.empty()) { + store->addSignatures(storePath, newSigs); + added += newSigs.size(); + } - //logger->incProgress(doneLabel); - }; + // logger->incProgress(doneLabel); + }; - for (auto & storePath : storePaths) - pool.enqueue(std::bind(doPath, storePath)); + for (auto& storePath : storePaths) + pool.enqueue(std::bind(doPath, storePath)); - pool.process(); + pool.process(); - printInfo(format("imported %d signatures") % added); - } + printInfo(format("imported %d signatures") % added); + } }; static RegisterCommand r1(make_ref<CmdCopySigs>()); -struct CmdSignPaths : StorePathsCommand -{ - Path secretKeyFile; - - CmdSignPaths() - { - mkFlag() - .shortName('k') - .longName("key-file") - .label("file") - .description("file containing the secret signing key") - .dest(&secretKeyFile); - } +struct CmdSignPaths : StorePathsCommand { + Path secretKeyFile; - std::string name() override - { - return "sign-paths"; - } + CmdSignPaths() { + mkFlag() + .shortName('k') + .longName("key-file") + .label("file") + .description("file containing the secret signing key") + .dest(&secretKeyFile); + } - std::string description() override - { - return "sign the specified paths"; - } + std::string name() override { return "sign-paths"; } - void run(ref<Store> store, Paths storePaths) override - { - if (secretKeyFile.empty()) - throw UsageError("you must specify a secret key file using '-k'"); + std::string description() override { return "sign the specified paths"; } - SecretKey secretKey(readFile(secretKeyFile)); + void run(ref<Store> store, Paths storePaths) override { + if (secretKeyFile.empty()) + throw UsageError("you must specify a secret key file using '-k'"); - size_t added{0}; + SecretKey secretKey(readFile(secretKeyFile)); - for (auto & storePath : storePaths) { - auto info = store->queryPathInfo(storePath); + size_t added{0}; - auto info2(*info); - info2.sigs.clear(); - info2.sign(secretKey); - assert(!info2.sigs.empty()); + for (auto& storePath : storePaths) { + auto info = store->queryPathInfo(storePath); - if (!info->sigs.count(*info2.sigs.begin())) { - store->addSignatures(storePath, info2.sigs); - added++; - } - } + auto info2(*info); + info2.sigs.clear(); + info2.sign(secretKey); + assert(!info2.sigs.empty()); - printInfo(format("added %d signatures") % added); + if (!info->sigs.count(*info2.sigs.begin())) { + store->addSignatures(storePath, info2.sigs); + added++; + } } + + printInfo(format("added %d signatures") % added); + } }; static RegisterCommand r3(make_ref<CmdSignPaths>()); diff --git a/third_party/nix/src/nix/upgrade-nix.cc b/third_party/nix/src/nix/upgrade-nix.cc index 35c44a70cf52..66230a9ddf4f 100644 --- a/third_party/nix/src/nix/upgrade-nix.cc +++ b/third_party/nix/src/nix/upgrade-nix.cc @@ -1,160 +1,156 @@ +#include "attr-path.hh" #include "command.hh" #include "common-args.hh" -#include "store-api.hh" #include "download.hh" #include "eval.hh" -#include "attr-path.hh" #include "names.hh" #include "progress-bar.hh" +#include "store-api.hh" using namespace nix; -struct CmdUpgradeNix : MixDryRun, StoreCommand -{ - Path profileDir; - std::string storePathsUrl = "https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/tools/nix-fallback-paths.nix"; - - CmdUpgradeNix() +struct CmdUpgradeNix : MixDryRun, StoreCommand { + Path profileDir; + std::string storePathsUrl = + "https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/" + "tools/nix-fallback-paths.nix"; + + CmdUpgradeNix() { + mkFlag() + .longName("profile") + .shortName('p') + .labels({"profile-dir"}) + .description("the Nix profile to upgrade") + .dest(&profileDir); + + mkFlag() + .longName("nix-store-paths-url") + .labels({"url"}) + .description( + "URL of the file that contains the store paths of the latest Nix " + "release") + .dest(&storePathsUrl); + } + + std::string name() override { return "upgrade-nix"; } + + std::string description() override { + return "upgrade Nix to the latest stable version"; + } + + Examples examples() override { + return { + Example{"To upgrade Nix to the latest stable version:", + "nix upgrade-nix"}, + Example{ + "To upgrade Nix in a specific profile:", + "nix upgrade-nix -p /nix/var/nix/profiles/per-user/alice/profile"}, + }; + } + + void run(ref<Store> store) override { + evalSettings.pureEval = true; + + if (profileDir == "") profileDir = getProfileDir(store); + + printInfo("upgrading Nix in profile '%s'", profileDir); + + Path storePath; { - mkFlag() - .longName("profile") - .shortName('p') - .labels({"profile-dir"}) - .description("the Nix profile to upgrade") - .dest(&profileDir); - - mkFlag() - .longName("nix-store-paths-url") - .labels({"url"}) - .description("URL of the file that contains the store paths of the latest Nix release") - .dest(&storePathsUrl); + Activity act(*logger, lvlInfo, actUnknown, "querying latest Nix version"); + storePath = getLatestNix(store); } - std::string name() override - { - return "upgrade-nix"; - } + auto version = DrvName(storePathToName(storePath)).version; - std::string description() override - { - return "upgrade Nix to the latest stable version"; + if (dryRun) { + stopProgressBar(); + printError("would upgrade to version %s", version); + return; } - Examples examples() override { - return { - Example{ - "To upgrade Nix to the latest stable version:", - "nix upgrade-nix" - }, - Example{ - "To upgrade Nix in a specific profile:", - "nix upgrade-nix -p /nix/var/nix/profiles/per-user/alice/profile" - }, - }; + Activity act(*logger, lvlInfo, actUnknown, + fmt("downloading '%s'...", storePath)); + store->ensurePath(storePath); } - void run(ref<Store> store) override { - evalSettings.pureEval = true; - - if (profileDir == "") - profileDir = getProfileDir(store); - - printInfo("upgrading Nix in profile '%s'", profileDir); - - Path storePath; - { - Activity act(*logger, lvlInfo, actUnknown, "querying latest Nix version"); - storePath = getLatestNix(store); - } - - auto version = DrvName(storePathToName(storePath)).version; - - if (dryRun) { - stopProgressBar(); - printError("would upgrade to version %s", version); - return; - } - - { - Activity act(*logger, lvlInfo, actUnknown, fmt("downloading '%s'...", storePath)); - store->ensurePath(storePath); - } - - { - Activity act(*logger, lvlInfo, actUnknown, fmt("verifying that '%s' works...", storePath)); - auto program = storePath + "/bin/nix-env"; - auto s = runProgram(program, false, {"--version"}); - if (s.find("Nix") == std::string::npos) - throw Error("could not verify that '%s' works", program); - } - - stopProgressBar(); + Activity act(*logger, lvlInfo, actUnknown, + fmt("verifying that '%s' works...", storePath)); + auto program = storePath + "/bin/nix-env"; + auto s = runProgram(program, false, {"--version"}); + if (s.find("Nix") == std::string::npos) + throw Error("could not verify that '%s' works", program); + } - { - Activity act(*logger, lvlInfo, actUnknown, fmt("installing '%s' into profile '%s'...", storePath, profileDir)); - runProgram(settings.nixBinDir + "/nix-env", false, - {"--profile", profileDir, "-i", storePath, "--no-sandbox"}); - } + stopProgressBar(); - printError(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version); + { + Activity act( + *logger, lvlInfo, actUnknown, + fmt("installing '%s' into profile '%s'...", storePath, profileDir)); + runProgram(settings.nixBinDir + "/nix-env", false, + {"--profile", profileDir, "-i", storePath, "--no-sandbox"}); } - /* Return the profile in which Nix is installed. */ - Path getProfileDir(ref<Store> store) - { - Path where; + printError(ANSI_GREEN "upgrade to version %s done" ANSI_NORMAL, version); + } - for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":")) - if (pathExists(dir + "/nix-env")) { - where = dir; - break; - } + /* Return the profile in which Nix is installed. */ + Path getProfileDir(ref<Store> store) { + Path where; - if (where == "") - throw Error("couldn't figure out how Nix is installed, so I can't upgrade it"); + for (auto& dir : tokenizeString<Strings>(getEnv("PATH"), ":")) + if (pathExists(dir + "/nix-env")) { + where = dir; + break; + } - printInfo("found Nix in '%s'", where); + if (where == "") + throw Error( + "couldn't figure out how Nix is installed, so I can't upgrade it"); - if (hasPrefix(where, "/run/current-system")) - throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'"); + printInfo("found Nix in '%s'", where); - Path profileDir = dirOf(where); + if (hasPrefix(where, "/run/current-system")) + throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'"); - // Resolve profile to /nix/var/nix/profiles/<name> link. - while (canonPath(profileDir).find("/profiles/") == std::string::npos && isLink(profileDir)) - profileDir = readLink(profileDir); + Path profileDir = dirOf(where); - printInfo("found profile '%s'", profileDir); + // Resolve profile to /nix/var/nix/profiles/<name> link. + while (canonPath(profileDir).find("/profiles/") == std::string::npos && + isLink(profileDir)) + profileDir = readLink(profileDir); - Path userEnv = canonPath(profileDir, true); + printInfo("found profile '%s'", profileDir); - if (baseNameOf(where) != "bin" || - !hasSuffix(userEnv, "user-environment")) - throw Error("directory '%s' does not appear to be part of a Nix profile", where); + Path userEnv = canonPath(profileDir, true); - if (!store->isValidPath(userEnv)) - throw Error("directory '%s' is not in the Nix store", userEnv); + if (baseNameOf(where) != "bin" || !hasSuffix(userEnv, "user-environment")) + throw Error("directory '%s' does not appear to be part of a Nix profile", + where); - return profileDir; - } + if (!store->isValidPath(userEnv)) + throw Error("directory '%s' is not in the Nix store", userEnv); - /* Return the store path of the latest stable Nix. */ - Path getLatestNix(ref<Store> store) - { - // FIXME: use nixos.org? - auto req = DownloadRequest(storePathsUrl); - auto res = getDownloader()->download(req); + return profileDir; + } - auto state = std::make_unique<EvalState>(Strings(), store); - 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); + /* Return the store path of the latest stable Nix. */ + Path getLatestNix(ref<Store> store) { + // FIXME: use nixos.org? + auto req = DownloadRequest(storePathsUrl); + auto res = getDownloader()->download(req); - return state->forceString(*v2); - } + auto state = std::make_unique<EvalState>(Strings(), store); + 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); + + return state->forceString(*v2); + } }; static RegisterCommand r1(make_ref<CmdUpgradeNix>()); diff --git a/third_party/nix/src/nix/verify.cc b/third_party/nix/src/nix/verify.cc index 8893fded5ed1..87405b7b37d1 100644 --- a/third_party/nix/src/nix/verify.cc +++ b/third_party/nix/src/nix/verify.cc @@ -1,178 +1,167 @@ +#include <atomic> #include "command.hh" #include "shared.hh" #include "store-api.hh" #include "sync.hh" #include "thread-pool.hh" -#include <atomic> - using namespace nix; -struct CmdVerify : StorePathsCommand -{ - bool noContents = false; - bool noTrust = false; - Strings substituterUris; - size_t sigsNeeded = 0; - - CmdVerify() - { - mkFlag(0, "no-contents", "do not verify the contents of each store path", &noContents); - mkFlag(0, "no-trust", "do not verify whether each store path is trusted", &noTrust); - mkFlag() - .longName("substituter") - .shortName('s') - .labels({"store-uri"}) - .description("use signatures from specified store") - .arity(1) - .handler([&](std::vector<std::string> ss) { substituterUris.push_back(ss[0]); }); - 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"; - } - - Examples examples() override - { - return { - Example{ - "To verify the entire Nix store:", - "nix verify --all" - }, - Example{ - "To check whether each path in the closure of Firefox has at least 2 signatures:", - "nix verify -r -n2 --no-contents $(type -p firefox)" - }, - }; - } - - void run(ref<Store> store, Paths storePaths) override - { - std::vector<ref<Store>> substituters; - for (auto & s : substituterUris) - substituters.push_back(openStore(s)); - - auto publicKeys = getDefaultPublicKeys(); - - Activity act(*logger, actVerifyPaths); - - std::atomic<size_t> done{0}; - std::atomic<size_t> untrusted{0}; - std::atomic<size_t> corrupted{0}; - std::atomic<size_t> failed{0}; - std::atomic<size_t> active{0}; - - auto update = [&]() { - act.progress(done, storePaths.size(), active, failed); - }; - - ThreadPool pool; - - auto doPath = [&](const Path & storePath) { - try { - checkInterrupt(); - - Activity act2(*logger, lvlInfo, actUnknown, fmt("checking '%s'", storePath)); - - MaintainCount<std::atomic<size_t>> mcActive(active); - update(); - - auto info = store->queryPathInfo(storePath); - - if (!noContents) { - - HashSink sink(info->narHash.type); - store->narFromPath(info->path, sink); - - auto hash = sink.finish(); - - if (hash.first != info->narHash) { - corrupted++; - act2.result(resCorruptedPath, info->path); - printError( - format("path '%s' was modified! expected hash '%s', got '%s'") - % info->path % info->narHash.to_string() % hash.first.to_string()); - } - - } - - if (!noTrust) { - - bool good = false; - - if (info->ultimate && !sigsNeeded) - good = true; - - else { - - StringSet sigsSeen; - size_t actualSigsNeeded = std::max(sigsNeeded, (size_t) 1); - size_t validSigs = 0; - - auto doSigs = [&](StringSet sigs) { - for (auto sig : sigs) { - if (sigsSeen.count(sig)) continue; - sigsSeen.insert(sig); - if (validSigs < ValidPathInfo::maxSigs && info->checkSignature(publicKeys, sig)) - validSigs++; - } - }; - - if (info->isContentAddressed(*store)) validSigs = ValidPathInfo::maxSigs; - - doSigs(info->sigs); - - for (auto & store2 : substituters) { - if (validSigs >= actualSigsNeeded) break; - try { - auto info2 = store2->queryPathInfo(info->path); - if (info2->isContentAddressed(*store)) validSigs = ValidPathInfo::maxSigs; - doSigs(info2->sigs); - } catch (InvalidPath &) { - } catch (Error & e) { - printError(format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); - } - } - - if (validSigs >= actualSigsNeeded) - good = true; - } +struct CmdVerify : StorePathsCommand { + bool noContents = false; + bool noTrust = false; + Strings substituterUris; + size_t sigsNeeded = 0; + + CmdVerify() { + mkFlag(0, "no-contents", "do not verify the contents of each store path", + &noContents); + mkFlag(0, "no-trust", "do not verify whether each store path is trusted", + &noTrust); + mkFlag() + .longName("substituter") + .shortName('s') + .labels({"store-uri"}) + .description("use signatures from specified store") + .arity(1) + .handler([&](std::vector<std::string> ss) { + substituterUris.push_back(ss[0]); + }); + 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"; + } + + Examples examples() override { + return { + Example{"To verify the entire Nix store:", "nix verify --all"}, + Example{"To check whether each path in the closure of Firefox has at " + "least 2 signatures:", + "nix verify -r -n2 --no-contents $(type -p firefox)"}, + }; + } + + void run(ref<Store> store, Paths storePaths) override { + std::vector<ref<Store>> substituters; + for (auto& s : substituterUris) substituters.push_back(openStore(s)); + + auto publicKeys = getDefaultPublicKeys(); + + Activity act(*logger, actVerifyPaths); + + std::atomic<size_t> done{0}; + std::atomic<size_t> untrusted{0}; + std::atomic<size_t> corrupted{0}; + std::atomic<size_t> failed{0}; + std::atomic<size_t> active{0}; + + auto update = [&]() { + act.progress(done, storePaths.size(), active, failed); + }; + + ThreadPool pool; + + auto doPath = [&](const Path& storePath) { + try { + checkInterrupt(); + + Activity act2(*logger, lvlInfo, actUnknown, + fmt("checking '%s'", storePath)); + + MaintainCount<std::atomic<size_t>> mcActive(active); + update(); + + auto info = store->queryPathInfo(storePath); + + if (!noContents) { + HashSink sink(info->narHash.type); + store->narFromPath(info->path, sink); + + auto hash = sink.finish(); + + if (hash.first != info->narHash) { + corrupted++; + act2.result(resCorruptedPath, info->path); + printError( + format("path '%s' was modified! expected hash '%s', got '%s'") % + info->path % info->narHash.to_string() % + hash.first.to_string()); + } + } + + if (!noTrust) { + bool good = false; + + if (info->ultimate && !sigsNeeded) + good = true; + + else { + StringSet sigsSeen; + size_t actualSigsNeeded = std::max(sigsNeeded, (size_t)1); + size_t validSigs = 0; + + auto doSigs = [&](StringSet sigs) { + for (auto sig : sigs) { + if (sigsSeen.count(sig)) continue; + sigsSeen.insert(sig); + if (validSigs < ValidPathInfo::maxSigs && + info->checkSignature(publicKeys, sig)) + validSigs++; + } + }; + + if (info->isContentAddressed(*store)) + validSigs = ValidPathInfo::maxSigs; + + doSigs(info->sigs); + + for (auto& store2 : substituters) { + if (validSigs >= actualSigsNeeded) break; + try { + auto info2 = store2->queryPathInfo(info->path); + if (info2->isContentAddressed(*store)) + validSigs = ValidPathInfo::maxSigs; + doSigs(info2->sigs); + } catch (InvalidPath&) { + } catch (Error& e) { + printError(format(ANSI_RED "error:" ANSI_NORMAL " %s") % + e.what()); + } + } - if (!good) { - untrusted++; - act2.result(resUntrustedPath, info->path); - printError(format("path '%s' is untrusted") % info->path); - } + if (validSigs >= actualSigsNeeded) good = true; + } - } + if (!good) { + untrusted++; + act2.result(resUntrustedPath, info->path); + printError(format("path '%s' is untrusted") % info->path); + } + } - done++; + done++; - } catch (Error & e) { - printError(format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); - failed++; - } + } catch (Error& e) { + printError(format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what()); + failed++; + } - update(); - }; + update(); + }; - for (auto & storePath : storePaths) - pool.enqueue(std::bind(doPath, storePath)); + for (auto& storePath : storePaths) + pool.enqueue(std::bind(doPath, storePath)); - pool.process(); + pool.process(); - throw Exit( - (corrupted ? 1 : 0) | - (untrusted ? 2 : 0) | - (failed ? 4 : 0)); - } + throw Exit((corrupted ? 1 : 0) | (untrusted ? 2 : 0) | (failed ? 4 : 0)); + } }; static RegisterCommand r1(make_ref<CmdVerify>()); diff --git a/third_party/nix/src/nix/why-depends.cc b/third_party/nix/src/nix/why-depends.cc index 325a2be0a793..7b8650a4e8eb 100644 --- a/third_party/nix/src/nix/why-depends.cc +++ b/third_party/nix/src/nix/why-depends.cc @@ -1,267 +1,246 @@ +#include <queue> #include "command.hh" -#include "store-api.hh" -#include "progress-bar.hh" #include "fs-accessor.hh" +#include "progress-bar.hh" #include "shared.hh" - -#include <queue> +#include "store-api.hh" using namespace nix; -static std::string hilite(const std::string & s, size_t pos, size_t len, - const std::string & colour = ANSI_RED) -{ - return - std::string(s, 0, pos) - + colour - + std::string(s, pos, len) - + ANSI_NORMAL - + std::string(s, pos + len); +static std::string hilite(const std::string& s, size_t pos, size_t len, + const std::string& colour = ANSI_RED) { + return std::string(s, 0, pos) + colour + std::string(s, pos, len) + + ANSI_NORMAL + std::string(s, pos + len); } -static std::string filterPrintable(const std::string & s) -{ - std::string res; - for (char c : s) - res += isprint(c) ? c : '.'; - return res; +static std::string filterPrintable(const std::string& s) { + std::string res; + for (char c : s) res += isprint(c) ? c : '.'; + return res; } -struct CmdWhyDepends : SourceExprCommand -{ - std::string _package, _dependency; - bool all = false; - - CmdWhyDepends() - { - expectArg("package", &_package); - expectArg("dependency", &_dependency); - - mkFlag() - .longName("all") - .shortName('a') - .description("show all edges in the dependency graph leading from 'package' to 'dependency', rather than just a shortest path") - .set(&all, true); +struct CmdWhyDepends : SourceExprCommand { + std::string _package, _dependency; + bool all = false; + + CmdWhyDepends() { + expectArg("package", &_package); + expectArg("dependency", &_dependency); + + mkFlag() + .longName("all") + .shortName('a') + .description( + "show all edges in the dependency graph leading from 'package' to " + "'dependency', rather than just a shortest path") + .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"; + } + + Examples examples() override { + return { + Example{"To show one path through the dependency graph leading from " + "Hello to 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"}, + Example{"To show why Glibc depends on itself:", + "nix why-depends nixpkgs.glibc nixpkgs.glibc"}, + }; + } + + void run(ref<Store> store) override { + auto package = parseInstallable(*this, store, _package, false); + auto packagePath = toStorePath(store, Build, package); + auto dependency = parseInstallable(*this, store, _dependency, false); + auto dependencyPath = toStorePath(store, NoBuild, dependency); + auto dependencyPathHash = storePathToHash(dependencyPath); + + PathSet closure; + store->computeFSClosure({packagePath}, closure, false, false); + + if (!closure.count(dependencyPath)) { + printError("'%s' does not depend on '%s'", package->what(), + dependency->what()); + return; } - std::string name() override - { - return "why-depends"; - } + stopProgressBar(); // FIXME - std::string description() override - { - return "show why a package has another package in its closure"; - } + auto accessor = store->getFSAccessor(); - Examples examples() override - { - return { - Example{ - "To show one path through the dependency graph leading from Hello to 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" - }, - Example{ - "To show why Glibc depends on itself:", - "nix why-depends nixpkgs.glibc nixpkgs.glibc" - }, - }; - } + auto const inf = std::numeric_limits<size_t>::max(); + + struct Node { + Path path; + PathSet refs; + PathSet rrefs; + size_t dist = inf; + Node* prev = nullptr; + bool queued = false; + bool visited = false; + }; + + std::map<Path, Node> graph; + + for (auto& path : closure) + graph.emplace(path, Node{path, store->queryPathInfo(path)->references}); + + // Transpose the graph. + for (auto& node : graph) + for (auto& ref : node.second.refs) graph[ref].rrefs.insert(node.first); + + /* Run Dijkstra's shortest path algorithm to get the distance + of every path in the closure to 'dependency'. */ + graph[dependencyPath].dist = 0; + + std::priority_queue<Node*> queue; - void run(ref<Store> store) override - { - auto package = parseInstallable(*this, store, _package, false); - auto packagePath = toStorePath(store, Build, package); - auto dependency = parseInstallable(*this, store, _dependency, false); - auto dependencyPath = toStorePath(store, NoBuild, dependency); - auto dependencyPathHash = storePathToHash(dependencyPath); + queue.push(&graph.at(dependencyPath)); - PathSet closure; - store->computeFSClosure({packagePath}, closure, false, false); + while (!queue.empty()) { + auto& node = *queue.top(); + queue.pop(); - if (!closure.count(dependencyPath)) { - printError("'%s' does not depend on '%s'", package->what(), dependency->what()); - return; + for (auto& rref : node.rrefs) { + auto& node2 = graph.at(rref); + auto dist = node.dist + 1; + if (dist < node2.dist) { + node2.dist = dist; + node2.prev = &node; + if (!node2.queued) { + node2.queued = true; + queue.push(&node2); + } } + } + } - stopProgressBar(); // FIXME + /* Print the subgraph of nodes that have 'dependency' in their + closure (i.e., that have a non-infinite distance to + 'dependency'). Print every edge on a path between `package` + and `dependency`. */ + std::function<void(Node&, const string&, const string&)> printNode; - auto accessor = store->getFSAccessor(); + const string treeConn = "╠═══"; + const string treeLast = "╚═══"; + const string treeLine = "║ "; + const string treeNull = " "; - auto const inf = std::numeric_limits<size_t>::max(); + struct BailOut {}; - struct Node - { - Path path; - PathSet refs; - PathSet rrefs; - size_t dist = inf; - Node * prev = nullptr; - bool queued = false; - bool visited = false; - }; + printNode = [&](Node& node, const string& firstPad, const string& tailPad) { + assert(node.dist != inf); + std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n", firstPad, + node.visited ? "\e[38;5;244m" : "", + firstPad != "" ? "=> " : "", node.path); - std::map<Path, Node> graph; + if (node.path == dependencyPath && !all && packagePath != dependencyPath) + throw BailOut(); - for (auto & path : closure) - graph.emplace(path, Node{path, store->queryPathInfo(path)->references}); + if (node.visited) return; + node.visited = true; - // Transpose the graph. - for (auto & node : graph) - for (auto & ref : node.second.refs) - graph[ref].rrefs.insert(node.first); + /* Sort the references by distance to `dependency` to + ensure that the shortest path is printed first. */ + std::multimap<size_t, Node*> refs; + std::set<std::string> hashes; - /* Run Dijkstra's shortest path algorithm to get the distance - of every path in the closure to 'dependency'. */ - graph[dependencyPath].dist = 0; + for (auto& ref : node.refs) { + if (ref == node.path && packagePath != dependencyPath) continue; + auto& node2 = graph.at(ref); + if (node2.dist == inf) continue; + refs.emplace(node2.dist, &node2); + hashes.insert(storePathToHash(node2.path)); + } - std::priority_queue<Node *> queue; + /* For each reference, find the files and symlinks that + contain the reference. */ + std::map<std::string, Strings> hits; - queue.push(&graph.at(dependencyPath)); + std::function<void(const Path&)> visitPath; - while (!queue.empty()) { - auto & node = *queue.top(); - queue.pop(); + visitPath = [&](const Path& p) { + auto st = accessor->stat(p); - for (auto & rref : node.rrefs) { - auto & node2 = graph.at(rref); - auto dist = node.dist + 1; - if (dist < node2.dist) { - node2.dist = dist; - node2.prev = &node; - if (!node2.queued) { - node2.queued = true; - queue.push(&node2); - } - } + auto p2 = p == node.path ? "/" : std::string(p, node.path.size() + 1); - } + auto getColour = [&](const std::string& hash) { + return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE; + }; + + if (st.type == FSAccessor::Type::tDirectory) { + auto names = accessor->readDirectory(p); + for (auto& name : names) visitPath(p + "/" + name); } - /* Print the subgraph of nodes that have 'dependency' in their - closure (i.e., that have a non-infinite distance to - 'dependency'). Print every edge on a path between `package` - and `dependency`. */ - std::function<void(Node &, const string &, const string &)> printNode; - - const string treeConn = "╠═══"; - const string treeLast = "╚═══"; - const string treeLine = "║ "; - const string treeNull = " "; - - struct BailOut { }; - - printNode = [&](Node & node, const string & firstPad, const string & tailPad) { - assert(node.dist != inf); - std::cout << fmt("%s%s%s%s" ANSI_NORMAL "\n", - firstPad, - node.visited ? "\e[38;5;244m" : "", - firstPad != "" ? "=> " : "", - node.path); - - if (node.path == dependencyPath && !all - && packagePath != dependencyPath) - throw BailOut(); - - if (node.visited) return; - node.visited = true; - - /* Sort the references by distance to `dependency` to - ensure that the shortest path is printed first. */ - std::multimap<size_t, Node *> refs; - std::set<std::string> hashes; - - for (auto & ref : node.refs) { - if (ref == node.path && packagePath != dependencyPath) continue; - auto & node2 = graph.at(ref); - if (node2.dist == inf) continue; - refs.emplace(node2.dist, &node2); - hashes.insert(storePathToHash(node2.path)); + else if (st.type == FSAccessor::Type::tRegular) { + auto contents = accessor->readFile(p); + + for (auto& hash : hashes) { + auto pos = contents.find(hash); + if (pos != std::string::npos) { + size_t margin = 32; + auto pos2 = pos >= margin ? pos - margin : 0; + hits[hash].emplace_back(fmt( + "%s: …%s…\n", p2, + hilite( + filterPrintable(std::string( + contents, pos2, pos - pos2 + hash.size() + margin)), + pos - pos2, storePathHashLen, getColour(hash)))); } + } + } - /* For each reference, find the files and symlinks that - contain the reference. */ - std::map<std::string, Strings> hits; - - std::function<void(const Path &)> visitPath; - - visitPath = [&](const Path & p) { - auto st = accessor->stat(p); - - auto p2 = p == node.path ? "/" : std::string(p, node.path.size() + 1); - - auto getColour = [&](const std::string & hash) { - return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE; - }; - - if (st.type == FSAccessor::Type::tDirectory) { - auto names = accessor->readDirectory(p); - for (auto & name : names) - visitPath(p + "/" + name); - } - - else if (st.type == FSAccessor::Type::tRegular) { - auto contents = accessor->readFile(p); - - for (auto & hash : hashes) { - auto pos = contents.find(hash); - if (pos != std::string::npos) { - size_t margin = 32; - auto pos2 = pos >= margin ? pos - margin : 0; - hits[hash].emplace_back(fmt("%s: …%s…\n", - p2, - hilite(filterPrintable( - std::string(contents, pos2, pos - pos2 + hash.size() + margin)), - pos - pos2, storePathHashLen, - getColour(hash)))); - } - } - } - - else if (st.type == FSAccessor::Type::tSymlink) { - auto target = accessor->readLink(p); - - for (auto & hash : hashes) { - auto pos = target.find(hash); - if (pos != std::string::npos) - hits[hash].emplace_back(fmt("%s -> %s\n", p2, - hilite(target, pos, storePathHashLen, getColour(hash)))); - } - } - }; - - // FIXME: should use scanForReferences(). - - visitPath(node.path); - - RunPager pager; - for (auto & ref : refs) { - auto hash = storePathToHash(ref.second->path); - - bool last = all ? ref == *refs.rbegin() : true; - - for (auto & hit : hits[hash]) { - bool first = hit == *hits[hash].begin(); - std::cout << tailPad - << (first ? (last ? treeLast : treeConn) : (last ? treeNull : treeLine)) - << hit; - if (!all) break; - } - - printNode(*ref.second, - tailPad + (last ? treeNull : treeLine), - tailPad + (last ? treeNull : treeLine)); - } - }; + else if (st.type == FSAccessor::Type::tSymlink) { + auto target = accessor->readLink(p); + + for (auto& hash : hashes) { + auto pos = target.find(hash); + if (pos != std::string::npos) + hits[hash].emplace_back( + fmt("%s -> %s\n", p2, + hilite(target, pos, storePathHashLen, getColour(hash)))); + } + } + }; + + // FIXME: should use scanForReferences(). + + visitPath(node.path); + + RunPager pager; + for (auto& ref : refs) { + auto hash = storePathToHash(ref.second->path); + + bool last = all ? ref == *refs.rbegin() : true; + + for (auto& hit : hits[hash]) { + bool first = hit == *hits[hash].begin(); + std::cout << tailPad + << (first ? (last ? treeLast : treeConn) + : (last ? treeNull : treeLine)) + << hit; + if (!all) break; + } + + printNode(*ref.second, tailPad + (last ? treeNull : treeLine), + tailPad + (last ? treeNull : treeLine)); + } + }; - try { - printNode(graph.at(packagePath), "", ""); - } catch (BailOut & ) { } + try { + printNode(graph.at(packagePath), "", ""); + } catch (BailOut&) { } + } }; static RegisterCommand r1(make_ref<CmdWhyDepends>()); |