diff options
Diffstat (limited to 'src/libcmd/command.cc')
-rw-r--r-- | src/libcmd/command.cc | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc new file mode 100644 index 000000000..614dee788 --- /dev/null +++ b/src/libcmd/command.cc @@ -0,0 +1,237 @@ +#include "command.hh" +#include "store-api.hh" +#include "local-fs-store.hh" +#include "derivations.hh" +#include "nixexpr.hh" +#include "profiles.hh" + +#include <nlohmann/json.hpp> + +extern char * * environ __attribute__((weak)); + +namespace nix { + +RegisterCommand::Commands * RegisterCommand::commands = nullptr; + +nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix) +{ + nix::Commands res; + for (auto & [name, command] : *RegisterCommand::commands) + if (name.size() == prefix.size() + 1) { + bool equal = true; + for (size_t i = 0; i < prefix.size(); ++i) + if (name[i] != prefix[i]) equal = false; + if (equal) + res.insert_or_assign(name[prefix.size()], command); + } + return res; +} + +nlohmann::json NixMultiCommand::toJSON() +{ + // FIXME: use Command::toJSON() as well. + return MultiCommand::toJSON(); +} + +StoreCommand::StoreCommand() +{ +} + +ref<Store> StoreCommand::getStore() +{ + if (!_store) + _store = createStore(); + return ref<Store>(_store); +} + +ref<Store> StoreCommand::createStore() +{ + return openStore(); +} + +void StoreCommand::run() +{ + run(getStore()); +} + +StorePathsCommand::StorePathsCommand(bool recursive) + : recursive(recursive) +{ + if (recursive) + addFlag({ + .longName = "no-recursive", + .description = "Apply operation to specified paths only.", + .category = installablesCategory, + .handler = {&this->recursive, false}, + }); + else + addFlag({ + .longName = "recursive", + .shortName = 'r', + .description = "Apply operation to closure of the specified paths.", + .category = installablesCategory, + .handler = {&this->recursive, true}, + }); + + addFlag({ + .longName = "all", + .description = "Apply the operation to every store path.", + .category = installablesCategory, + .handler = {&all, true}, + }); +} + +void StorePathsCommand::run(ref<Store> store) +{ + StorePaths storePaths; + + 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, realiseMode, operateOn, installables)) + storePaths.push_back(p); + + if (recursive) { + StorePathSet closure; + store->computeFSClosure(StorePathSet(storePaths.begin(), storePaths.end()), closure, false, false); + storePaths.clear(); + for (auto & p : closure) + storePaths.push_back(p); + } + } + + run(store, std::move(storePaths)); +} + +void StorePathCommand::run(ref<Store> store) +{ + auto storePaths = toStorePaths(store, Realise::Nothing, operateOn, installables); + + if (storePaths.size() != 1) + throw UsageError("this command requires exactly one store path"); + + run(store, *storePaths.begin()); +} + +Strings editorFor(const Pos & pos) +{ + auto editor = getEnv("EDITOR").value_or("cat"); + auto args = tokenizeString<Strings>(editor); + if (pos.line > 0 && ( + editor.find("emacs") != std::string::npos || + editor.find("nano") != std::string::npos || + editor.find("vim") != std::string::npos)) + args.push_back(fmt("+%d", pos.line)); + args.push_back(pos.file); + return args; +} + +MixProfile::MixProfile() +{ + addFlag({ + .longName = "profile", + .description = "The profile to update.", + .labels = {"path"}, + .handler = {&profile}, + .completer = completePath + }); +} + +void MixProfile::updateProfile(const StorePath & storePath) +{ + if (!profile) return; + auto store = getStore().dynamic_pointer_cast<LocalFSStore>(); + if (!store) throw Error("'--profile' is not supported for this Nix store"); + auto profile2 = absPath(*profile); + switchLink(profile2, + createGeneration( + ref<LocalFSStore>(store), + profile2, storePath)); +} + +void MixProfile::updateProfile(const Buildables & buildables) +{ + if (!profile) return; + + std::vector<StorePath> result; + + for (auto & buildable : buildables) { + std::visit(overloaded { + [&](BuildableOpaque bo) { + result.push_back(bo.path); + }, + [&](BuildableFromDrv bfd) { + for (auto & output : bfd.outputs) { + /* Output path should be known because we just tried to + build it. */ + assert(output.second); + result.push_back(*output.second); + } + }, + }, buildable); + } + + if (result.size() != 1) + throw Error("'--profile' requires that the arguments produce a single store path, but there are %d", result.size()); + + updateProfile(result[0]); +} + +MixDefaultProfile::MixDefaultProfile() +{ + profile = getDefaultProfile(); +} + +MixEnvironment::MixEnvironment() : ignoreEnvironment(false) +{ + addFlag({ + .longName = "ignore-environment", + .shortName = 'i', + .description = "Clear the entire environment (except those specified with `--keep`).", + .handler = {&ignoreEnvironment, true}, + }); + + addFlag({ + .longName = "keep", + .shortName = 'k', + .description = "Keep the environment variable *name*.", + .labels = {"name"}, + .handler = {[&](std::string s) { keep.insert(s); }}, + }); + + addFlag({ + .longName = "unset", + .shortName = 'u', + .description = "Unset the environment variable *name*.", + .labels = {"name"}, + .handler = {[&](std::string s) { unset.insert(s); }}, + }); +} + +void MixEnvironment::setEnviron() { + if (ignoreEnvironment) { + if (!unset.empty()) + throw UsageError("--unset does not make sense with --ignore-environment"); + + for (const auto & var : keep) { + auto val = getenv(var.c_str()); + if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val)); + } + + vectorEnv = stringsToCharPtrs(stringsEnv); + environ = vectorEnv.data(); + } else { + if (!keep.empty()) + throw UsageError("--keep does not make sense without --ignore-environment"); + + for (const auto & var : unset) + unsetenv(var.c_str()); + } +} + +} |