diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2021-01-27 17:17:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-27 17:17:58 +0100 |
commit | b8f345b29a65669f4d6966ba3837fda2341c5ec2 (patch) | |
tree | 7bfb6b59ad8e48454e4311307277017ab6b392de /src/nix | |
parent | 12de0466fea6558ccb0dd5b98b72d7a068c9b5e8 (diff) | |
parent | 6af6e41df06f0a8a3b919b4052b41d09f0a97678 (diff) |
Merge pull request #4483 from shlevy/libcmd
Move command plugin interface to libnixcmd
Diffstat (limited to 'src/nix')
-rw-r--r-- | src/nix/command.cc | 237 | ||||
-rw-r--r-- | src/nix/command.hh | 274 | ||||
-rw-r--r-- | src/nix/daemon.cc | 2 | ||||
-rw-r--r-- | src/nix/installables.cc | 824 | ||||
-rw-r--r-- | src/nix/installables.hh | 137 | ||||
-rw-r--r-- | src/nix/legacy.cc | 7 | ||||
-rw-r--r-- | src/nix/legacy.hh | 23 | ||||
-rw-r--r-- | src/nix/local.mk | 4 | ||||
-rw-r--r-- | src/nix/markdown.cc | 50 | ||||
-rw-r--r-- | src/nix/markdown.hh | 7 |
10 files changed, 3 insertions, 1562 deletions
diff --git a/src/nix/command.cc b/src/nix/command.cc deleted file mode 100644 index 614dee788..000000000 --- a/src/nix/command.cc +++ /dev/null @@ -1,237 +0,0 @@ -#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()); - } -} - -} diff --git a/src/nix/command.hh b/src/nix/command.hh deleted file mode 100644 index ed6980075..000000000 --- a/src/nix/command.hh +++ /dev/null @@ -1,274 +0,0 @@ -#pragma once - -#include "installables.hh" -#include "args.hh" -#include "common-eval-args.hh" -#include "path.hh" -#include "flake/lockfile.hh" -#include "store-api.hh" - -#include <optional> - -namespace nix { - -extern std::string programPath; - -extern char * * savedArgv; - -class EvalState; -struct Pos; -class Store; - -static constexpr Command::Category catSecondary = 100; -static constexpr Command::Category catUtility = 101; -static constexpr Command::Category catNixInstallation = 102; - -static constexpr auto installablesCategory = "Options that change the interpretation of installables"; - -struct NixMultiCommand : virtual MultiCommand, virtual Command -{ - nlohmann::json toJSON() override; -}; - -/* A command that requires 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 EvalCommand : virtual StoreCommand, MixEvalArgs -{ - ref<EvalState> getEvalState(); - - std::shared_ptr<EvalState> evalState; -}; - -struct MixFlakeOptions : virtual Args, EvalCommand -{ - flake::LockFlags lockFlags; - - MixFlakeOptions(); - - virtual std::optional<FlakeRef> getFlakeRefForCompletion() - { return {}; } -}; - -/* How to handle derivations in commands that operate on store paths. */ -enum class OperateOn { - /* Operate on the output path. */ - Output, - /* Operate on the .drv path. */ - Derivation -}; - -struct SourceExprCommand : virtual Args, MixFlakeOptions -{ - std::optional<Path> file; - std::optional<std::string> expr; - - // FIXME: move this; not all commands (e.g. 'nix run') use it. - OperateOn operateOn = OperateOn::Output; - - SourceExprCommand(); - - std::vector<std::shared_ptr<Installable>> parseInstallables( - ref<Store> store, std::vector<std::string> ss); - - std::shared_ptr<Installable> parseInstallable( - ref<Store> store, const std::string & installable); - - virtual Strings getDefaultFlakeAttrPaths(); - - virtual Strings getDefaultFlakeAttrPathPrefixes(); - - void completeInstallable(std::string_view prefix); -}; - -enum class Realise { - /* Build the derivation. Postcondition: the - derivation outputs exist. */ - Outputs, - /* Don't build the derivation. Postcondition: the store derivation - exists. */ - Derivation, - /* Evaluate in dry-run mode. Postcondition: nothing. */ - Nothing -}; - -/* A command that operates on a list of "installables", which can be - store paths, attribute paths, Nix expressions, etc. */ -struct InstallablesCommand : virtual Args, SourceExprCommand -{ - std::vector<std::shared_ptr<Installable>> installables; - - InstallablesCommand(); - - void prepare() override; - - virtual bool useDefaultInstallables() { return true; } - - std::optional<FlakeRef> getFlakeRefForCompletion() override; - -private: - - std::vector<std::string> _installables; -}; - -/* A command that operates on exactly one "installable" */ -struct InstallableCommand : virtual Args, SourceExprCommand -{ - std::shared_ptr<Installable> installable; - - InstallableCommand(); - - void prepare() override; - - std::optional<FlakeRef> getFlakeRefForCompletion() override - { - return parseFlakeRef(_installable, absPath(".")); - } - -private: - - std::string _installable{"."}; -}; - -/* A command that operates on zero or more store paths. */ -struct StorePathsCommand : public InstallablesCommand -{ -private: - - bool recursive = false; - bool all = false; - -protected: - - Realise realiseMode = Realise::Derivation; - -public: - - StorePathsCommand(bool recursive = false); - - using StoreCommand::run; - - virtual void run(ref<Store> store, std::vector<StorePath> storePaths) = 0; - - void run(ref<Store> store) override; - - bool useDefaultInstallables() override { return !all; } -}; - -/* A command that operates on exactly one store path. */ -struct StorePathCommand : public InstallablesCommand -{ - using StoreCommand::run; - - virtual void run(ref<Store> store, const StorePath & storePath) = 0; - - void run(ref<Store> store) override; -}; - -/* A helper class for registering commands globally. */ -struct RegisterCommand -{ - typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands; - static Commands * commands; - - RegisterCommand(std::vector<std::string> && name, - std::function<ref<Command>()> command) - { - if (!commands) commands = new Commands; - commands->emplace(name, command); - } - - static nix::Commands getCommandsFor(const std::vector<std::string> & prefix); -}; - -template<class T> -static RegisterCommand registerCommand(const std::string & name) -{ - return RegisterCommand({name}, [](){ return make_ref<T>(); }); -} - -template<class T> -static RegisterCommand registerCommand2(std::vector<std::string> && name) -{ - return RegisterCommand(std::move(name), [](){ return make_ref<T>(); }); -} - -Buildables build(ref<Store> store, Realise mode, - std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode = bmNormal); - -std::set<StorePath> toStorePaths(ref<Store> store, - Realise mode, OperateOn operateOn, - std::vector<std::shared_ptr<Installable>> installables); - -StorePath toStorePath(ref<Store> store, - Realise mode, OperateOn operateOn, - std::shared_ptr<Installable> installable); - -std::set<StorePath> toDerivations(ref<Store> store, - std::vector<std::shared_ptr<Installable>> installables, - bool useDeriver = false); - -/* Helper function to generate args that invoke $EDITOR on - filename:lineno. */ -Strings editorFor(const Pos & pos); - -struct MixProfile : virtual StoreCommand -{ - std::optional<Path> profile; - - MixProfile(); - - /* If 'profile' is set, make it point at 'storePath'. */ - void updateProfile(const StorePath & storePath); - - /* If 'profile' is set, make it point at the store path produced - by 'buildables'. */ - void updateProfile(const Buildables & buildables); -}; - -struct MixDefaultProfile : MixProfile -{ - MixDefaultProfile(); -}; - -struct MixEnvironment : virtual Args { - - StringSet keep, unset; - Strings stringsEnv; - std::vector<char*> vectorEnv; - bool ignoreEnvironment; - - MixEnvironment(); - - /* Modify global environ based on ignoreEnvironment, keep, and unset. It's expected that exec will be called before this class goes out of scope, otherwise environ will become invalid. */ - void setEnviron(); -}; - -void completeFlakeRef(ref<Store> store, std::string_view prefix); - -void completeFlakeRefWithFragment( - ref<EvalState> evalState, - flake::LockFlags lockFlags, - Strings attrPathPrefixes, - const Strings & defaultFlakeAttrPaths, - std::string_view prefix); - -std::string showVersions(const std::set<std::string> & versions); - -void printClosureDiff( - ref<Store> store, - const StorePath & beforePath, - const StorePath & afterPath, - std::string_view indent); - -} diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index a358cb0d9..26006167d 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -8,7 +8,7 @@ #include "globals.hh" #include "derivations.hh" #include "finally.hh" -#include "../nix/legacy.hh" +#include "legacy.hh" #include "daemon.hh" #include <algorithm> diff --git a/src/nix/installables.cc b/src/nix/installables.cc deleted file mode 100644 index 4e6bf4a9a..000000000 --- a/src/nix/installables.cc +++ /dev/null @@ -1,824 +0,0 @@ -#include "installables.hh" -#include "command.hh" -#include "attr-path.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 "flake/flake.hh" -#include "eval-cache.hh" -#include "url.hh" -#include "registry.hh" - -#include <regex> -#include <queue> - -#include <nlohmann/json.hpp> - -namespace nix { - -nlohmann::json BuildableOpaque::toJSON(ref<Store> store) const { - nlohmann::json res; - res["path"] = store->printStorePath(path); - return res; -} - -nlohmann::json BuildableFromDrv::toJSON(ref<Store> store) const { - nlohmann::json res; - res["drvPath"] = store->printStorePath(drvPath); - for (const auto& [output, path] : outputs) { - res["outputs"][output] = path ? store->printStorePath(*path) : ""; - } - return res; -} - -nlohmann::json buildablesToJSON(const Buildables & buildables, ref<Store> store) { - auto res = nlohmann::json::array(); - for (const Buildable & buildable : buildables) { - std::visit([&res, store](const auto & buildable) { - res.push_back(buildable.toJSON(store)); - }, buildable); - } - return res; -} - -void completeFlakeInputPath( - ref<EvalState> evalState, - const FlakeRef & flakeRef, - std::string_view prefix) -{ - auto flake = flake::getFlake(*evalState, flakeRef, true); - for (auto & input : flake.inputs) - if (hasPrefix(input.first, prefix)) - completions->add(input.first); -} - -MixFlakeOptions::MixFlakeOptions() -{ - auto category = "Common flake-related options"; - - addFlag({ - .longName = "recreate-lock-file", - .description = "Recreate the flake's lock file from scratch.", - .category = category, - .handler = {&lockFlags.recreateLockFile, true} - }); - - addFlag({ - .longName = "no-update-lock-file", - .description = "Do not allow any updates to the flake's lock file.", - .category = category, - .handler = {&lockFlags.updateLockFile, false} - }); - - addFlag({ - .longName = "no-write-lock-file", - .description = "Do not write the flake's newly generated lock file.", - .category = category, - .handler = {&lockFlags.writeLockFile, false} - }); - - addFlag({ - .longName = "no-registries", - .description = "Don't allow lookups in the flake registries.", - .category = category, - .handler = {&lockFlags.useRegistries, false} - }); - - addFlag({ - .longName = "commit-lock-file", - .description = "Commit changes to the flake's lock file.", - .category = category, - .handler = {&lockFlags.commitLockFile, true} - }); - - addFlag({ - .longName = "update-input", - .description = "Update a specific flake input (ignoring its previous entry in the lock file).", - .category = category, - .labels = {"input-path"}, - .handler = {[&](std::string s) { - lockFlags.inputUpdates.insert(flake::parseInputPath(s)); - }}, - .completer = {[&](size_t, std::string_view prefix) { - if (auto flakeRef = getFlakeRefForCompletion()) - completeFlakeInputPath(getEvalState(), *flakeRef, prefix); - }} - }); - - addFlag({ - .longName = "override-input", - .description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`).", - .category = category, - .labels = {"input-path", "flake-url"}, - .handler = {[&](std::string inputPath, std::string flakeRef) { - lockFlags.inputOverrides.insert_or_assign( - flake::parseInputPath(inputPath), - parseFlakeRef(flakeRef, absPath("."))); - }} - }); - - addFlag({ - .longName = "inputs-from", - .description = "Use the inputs of the specified flake as registry entries.", - .category = category, - .labels = {"flake-url"}, - .handler = {[&](std::string flakeRef) { - auto evalState = getEvalState(); - auto flake = flake::lockFlake( - *evalState, - parseFlakeRef(flakeRef, absPath(".")), - { .writeLockFile = false }); - for (auto & [inputName, input] : flake.lockFile.root->inputs) { - auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes - if (auto input3 = std::dynamic_pointer_cast<const flake::LockedNode>(input2)) { - overrideRegistry( - fetchers::Input::fromAttrs({{"type","indirect"}, {"id", inputName}}), - input3->lockedRef.input, - {}); - } - } - }}, - .completer = {[&](size_t, std::string_view prefix) { - completeFlakeRef(getEvalState()->store, prefix); - }} - }); -} - -SourceExprCommand::SourceExprCommand() -{ - addFlag({ - .longName = "file", - .shortName = 'f', - .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.", - .category = installablesCategory, - .labels = {"file"}, - .handler = {&file}, - .completer = completePath - }); - - addFlag({ - .longName = "expr", - .description = "Interpret installables as attribute paths relative to the Nix expression *expr*.", - .category = installablesCategory, - .labels = {"expr"}, - .handler = {&expr} - }); - - addFlag({ - .longName = "derivation", - .description = "Operate on the store derivation rather than its outputs.", - .category = installablesCategory, - .handler = {&operateOn, OperateOn::Derivation}, - }); -} - -Strings SourceExprCommand::getDefaultFlakeAttrPaths() -{ - return {"defaultPackage." + settings.thisSystem.get()}; -} - -Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() -{ - return { - // As a convenience, look for the attribute in - // 'outputs.packages'. - "packages." + settings.thisSystem.get() + ".", - // As a temporary hack until Nixpkgs is properly converted - // to provide a clean 'packages' set, look in 'legacyPackages'. - "legacyPackages." + settings.thisSystem.get() + "." - }; -} - -void SourceExprCommand::completeInstallable(std::string_view prefix) -{ - if (file) return; // FIXME - - completeFlakeRefWithFragment( - getEvalState(), - lockFlags, - getDefaultFlakeAttrPathPrefixes(), - getDefaultFlakeAttrPaths(), - prefix); -} - -void completeFlakeRefWithFragment( - ref<EvalState> evalState, - flake::LockFlags lockFlags, - Strings attrPathPrefixes, - const Strings & defaultFlakeAttrPaths, - std::string_view prefix) -{ - /* Look for flake output attributes that match the - prefix. */ - try { - auto hash = prefix.find('#'); - if (hash != std::string::npos) { - auto fragment = prefix.substr(hash + 1); - auto flakeRefS = std::string(prefix.substr(0, hash)); - // FIXME: do tilde expansion. - auto flakeRef = parseFlakeRef(flakeRefS, absPath(".")); - - auto evalCache = openEvalCache(*evalState, - std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags))); - - auto root = evalCache->getRoot(); - - /* Complete 'fragment' relative to all the - attrpath prefixes as well as the root of the - flake. */ - attrPathPrefixes.push_back(""); - - for (auto & attrPathPrefixS : attrPathPrefixes) { - auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS); - auto attrPathS = attrPathPrefixS + std::string(fragment); - auto attrPath = parseAttrPath(*evalState, attrPathS); - - std::string lastAttr; - if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) { - lastAttr = attrPath.back(); - attrPath.pop_back(); - } - - auto attr = root->findAlongAttrPath(attrPath); - if (!attr) continue; - - for (auto & attr2 : attr->getAttrs()) { - if (hasPrefix(attr2, lastAttr)) { - auto attrPath2 = attr->getAttrPath(attr2); - /* Strip the attrpath prefix. */ - attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions->add(flakeRefS + "#" + concatStringsSep(".", attrPath2)); - } - } - } - - /* And add an empty completion for the default - attrpaths. */ - if (fragment.empty()) { - for (auto & attrPath : defaultFlakeAttrPaths) { - auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); - if (!attr) continue; - completions->add(flakeRefS + "#"); - } - } - } - } catch (Error & e) { - warn(e.msg()); - } - - completeFlakeRef(evalState->store, prefix); -} - -ref<EvalState> EvalCommand::getEvalState() -{ - if (!evalState) - evalState = std::make_shared<EvalState>(searchPath, getStore()); - return ref<EvalState>(evalState); -} - -void completeFlakeRef(ref<Store> store, std::string_view prefix) -{ - if (prefix == "") - completions->add("."); - - completeDir(0, prefix); - - /* Look for registry entries that match the prefix. */ - for (auto & registry : fetchers::getRegistries(store)) { - for (auto & entry : registry->entries) { - auto from = entry.from.to_string(); - if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { - std::string from2(from, 6); - if (hasPrefix(from2, prefix)) - completions->add(from2); - } else { - if (hasPrefix(from, prefix)) - completions->add(from); - } - } - } -} - -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]); -} - -std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> -Installable::getCursors(EvalState & state) -{ - auto evalCache = - std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state, - [&]() { return toValue(state).first; }); - return {{evalCache->getRoot(), ""}}; -} - -std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string> -Installable::getCursor(EvalState & state) -{ - auto cursors = getCursors(state); - if (cursors.empty()) - throw Error("cannot find flake attribute '%s'", what()); - return cursors[0]; -} - -struct InstallableStorePath : Installable -{ - ref<Store> store; - StorePath storePath; - - InstallableStorePath(ref<Store> store, StorePath && storePath) - : store(store), storePath(std::move(storePath)) { } - - std::string what() override { return store->printStorePath(storePath); } - - Buildables toBuildables() override - { - if (storePath.isDerivation()) { - std::map<std::string, std::optional<StorePath>> outputs; - auto drv = store->readDerivation(storePath); - for (auto & [name, output] : drv.outputsAndOptPaths(*store)) - outputs.emplace(name, output.second); - return { - BuildableFromDrv { - .drvPath = storePath, - .outputs = std::move(outputs) - } - }; - } else { - return { - BuildableOpaque { - .path = storePath, - } - }; - } - } - - std::optional<StorePath> getStorePath() override - { - return storePath; - } -}; - -Buildables InstallableValue::toBuildables() -{ - Buildables res; - - std::map<StorePath, std::map<std::string, std::optional<StorePath>>> drvsToOutputs; - - // Group by derivation, helps with .all in particular - for (auto & drv : toDerivations()) { - auto outputName = drv.outputName; - if (outputName == "") - throw Error("derivation '%s' lacks an 'outputName' attribute", state->store->printStorePath(drv.drvPath)); - drvsToOutputs[drv.drvPath].insert_or_assign(outputName, drv.outPath); - } - - for (auto & i : drvsToOutputs) - res.push_back(BuildableFromDrv { i.first, i.second }); - - return res; -} - -struct InstallableAttrPath : InstallableValue -{ - SourceExprCommand & cmd; - RootValue v; - std::string attrPath; - - InstallableAttrPath(ref<EvalState> state, SourceExprCommand & cmd, Value * v, const std::string & attrPath) - : InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath) - { } - - std::string what() override { return attrPath; } - - std::pair<Value *, Pos> toValue(EvalState & state) override - { - auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); - state.forceValue(*vRes); - return {vRes, pos}; - } - - virtual std::vector<InstallableValue::DerivationInfo> toDerivations() override; -}; - -std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations() -{ - auto v = toValue(*state).first; - - Bindings & autoArgs = *cmd.getAutoArgs(*state); - - DrvInfos drvInfos; - getDerivations(*state, *v, "", autoArgs, drvInfos, false); - - std::vector<DerivationInfo> res; - for (auto & drvInfo : drvInfos) { - res.push_back({ - state->store->parseStorePath(drvInfo.queryDrvPath()), - state->store->maybeParseStorePath(drvInfo.queryOutPath()), - drvInfo.queryOutputName() - }); - } - - return res; -} - -std::vector<std::string> InstallableFlake::getActualAttrPaths() -{ - std::vector<std::string> res; - - for (auto & prefix : prefixes) - res.push_back(prefix + *attrPaths.begin()); - - for (auto & s : attrPaths) - res.push_back(s); - - return res; -} - -Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake) -{ - auto vFlake = state.allocValue(); - - callFlake(state, lockedFlake, *vFlake); - - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); - assert(aOutputs); - - state.forceValue(*aOutputs->value); - - return aOutputs->value; -} - -ref<eval_cache::EvalCache> openEvalCache( - EvalState & state, - std::shared_ptr<flake::LockedFlake> lockedFlake) -{ - auto fingerprint = lockedFlake->getFingerprint(); - return make_ref<nix::eval_cache::EvalCache>( - evalSettings.useEvalCache && evalSettings.pureEval - ? std::optional { std::cref(fingerprint) } - : std::nullopt, - state, - [&state, lockedFlake]() - { - /* For testing whether the evaluation cache is - complete. */ - if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0") - throw Error("not everything is cached, but evaluation is not allowed"); - - auto vFlake = state.allocValue(); - flake::callFlake(state, *lockedFlake, *vFlake); - - state.forceAttrs(*vFlake); - - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); - assert(aOutputs); - - return aOutputs->value; - }); -} - -static std::string showAttrPaths(const std::vector<std::string> & paths) -{ - std::string s; - for (const auto & [n, i] : enumerate(paths)) { - if (n > 0) s += n + 1 == paths.size() ? " or " : ", "; - s += '\''; s += i; s += '\''; - } - return s; -} - -std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation() -{ - auto lockedFlake = getLockedFlake(); - - auto cache = openEvalCache(*state, lockedFlake); - auto root = cache->getRoot(); - - for (auto & attrPath : getActualAttrPaths()) { - auto attr = root->findAlongAttrPath(parseAttrPath(*state, attrPath)); - if (!attr) continue; - - if (!attr->isDerivation()) - throw Error("flake output attribute '%s' is not a derivation", attrPath); - - auto drvPath = attr->forceDerivation(); - - auto drvInfo = DerivationInfo{ - std::move(drvPath), - state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()), - attr->getAttr(state->sOutputName)->getString() - }; - - return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)}; - } - - throw Error("flake '%s' does not provide attribute %s", - flakeRef, showAttrPaths(getActualAttrPaths())); -} - -std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations() -{ - std::vector<DerivationInfo> res; - res.push_back(std::get<2>(toDerivation())); - return res; -} - -std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state) -{ - auto lockedFlake = getLockedFlake(); - - auto vOutputs = getFlakeOutputs(state, *lockedFlake); - - auto emptyArgs = state.allocBindings(0); - - for (auto & attrPath : getActualAttrPaths()) { - try { - auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); - state.forceValue(*v); - return {v, pos}; - } catch (AttrPathNotFound & e) { - } - } - - throw Error("flake '%s' does not provide attribute %s", - flakeRef, showAttrPaths(getActualAttrPaths())); -} - -std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> -InstallableFlake::getCursors(EvalState & state) -{ - auto evalCache = openEvalCache(state, - std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags))); - - auto root = evalCache->getRoot(); - - std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> res; - - for (auto & attrPath : getActualAttrPaths()) { - auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath)); - if (attr) res.push_back({attr, attrPath}); - } - - return res; -} - -std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const -{ - if (!_lockedFlake) { - _lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags)); - _lockedFlake->flake.config.apply(); - // FIXME: send new config to the daemon. - } - return _lockedFlake; -} - -FlakeRef InstallableFlake::nixpkgsFlakeRef() const -{ - auto lockedFlake = getLockedFlake(); - - if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) { - if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) { - debug("using nixpkgs flake '%s'", lockedNode->lockedRef); - return std::move(lockedNode->lockedRef); - } - } - - return Installable::nixpkgsFlakeRef(); -} - -std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables( - ref<Store> store, std::vector<std::string> ss) -{ - std::vector<std::shared_ptr<Installable>> result; - - if (file || expr) { - if (file && expr) - throw UsageError("'--file' and '--expr' are exclusive"); - - // FIXME: backward compatibility hack - if (file) evalSettings.pureEval = false; - - auto state = getEvalState(); - auto vFile = state->allocValue(); - - if (file) - state->evalFile(lookupFileArg(*state, *file), *vFile); - else { - auto e = state->parseExprFromString(*expr, absPath(".")); - state->eval(e, *vFile); - } - - for (auto & s : ss) - result.push_back(std::make_shared<InstallableAttrPath>(state, *this, vFile, s == "." ? "" : s)); - - } else { - - for (auto & s : ss) { - std::exception_ptr ex; - - try { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); - result.push_back(std::make_shared<InstallableFlake>( - getEvalState(), std::move(flakeRef), - fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, - getDefaultFlakeAttrPathPrefixes(), lockFlags)); - continue; - } catch (...) { - ex = std::current_exception(); - } - - if (s.find('/') != std::string::npos) { - try { - result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s))); - continue; - } catch (BadStorePath &) { - } catch (...) { - if (!ex) - ex = std::current_exception(); - } - } - - std::rethrow_exception(ex); - - /* - throw Error( - pathExists(s) - ? "path '%s' is not a flake or a store path" - : "don't know how to handle argument '%s'", s); - */ - } - } - - return result; -} - -std::shared_ptr<Installable> SourceExprCommand::parseInstallable( - ref<Store> store, const std::string & installable) -{ - auto installables = parseInstallables(store, {installable}); - assert(installables.size() == 1); - return installables.front(); -} - -Buildables build(ref<Store> store, Realise mode, - std::vector<std::shared_ptr<Installable>> installables, BuildMode bMode) -{ - if (mode == Realise::Nothing) - settings.readOnlyMode = true; - - Buildables buildables; - - std::vector<StorePathWithOutputs> pathsToBuild; - - for (auto & i : installables) { - for (auto & b : i->toBuildables()) { - std::visit(overloaded { - [&](BuildableOpaque bo) { - pathsToBuild.push_back({bo.path}); - }, - [&](BuildableFromDrv bfd) { - StringSet outputNames; - for (auto & output : bfd.outputs) - outputNames.insert(output.first); - pathsToBuild.push_back({bfd.drvPath, outputNames}); - }, - }, b); - buildables.push_back(std::move(b)); - } - } - - if (mode == Realise::Nothing) - printMissing(store, pathsToBuild, lvlError); - else if (mode == Realise::Outputs) - store->buildPaths(pathsToBuild, bMode); - - return buildables; -} - -StorePathSet toStorePaths(ref<Store> store, - Realise mode, OperateOn operateOn, - std::vector<std::shared_ptr<Installable>> installables) -{ - StorePathSet outPaths; - - if (operateOn == OperateOn::Output) { - for (auto & b : build(store, mode, installables)) - std::visit(overloaded { - [&](BuildableOpaque bo) { - outPaths.insert(bo.path); - }, - [&](BuildableFromDrv bfd) { - for (auto & output : bfd.outputs) { - if (!output.second) - throw Error("Cannot operate on output of unbuilt CA drv"); - outPaths.insert(*output.second); - } - }, - }, b); - } else { - if (mode == Realise::Nothing) - settings.readOnlyMode = true; - - for (auto & i : installables) - for (auto & b : i->toBuildables()) - if (auto bfd = std::get_if<BuildableFromDrv>(&b)) - outPaths.insert(bfd->drvPath); - } - - return outPaths; -} - -StorePath toStorePath(ref<Store> store, - Realise mode, OperateOn operateOn, - std::shared_ptr<Installable> installable) -{ - auto paths = toStorePaths(store, mode, operateOn, {installable}); - - if (paths.size() != 1) - throw Error("argument '%s' should evaluate to one store path", installable->what()); - - return *paths.begin(); -} - -StorePathSet toDerivations(ref<Store> store, - std::vector<std::shared_ptr<Installable>> installables, bool useDeriver) -{ - StorePathSet drvPaths; - - for (auto & i : installables) - for (auto & b : i->toBuildables()) - std::visit(overloaded { - [&](BuildableOpaque bo) { - if (!useDeriver) - throw Error("argument '%s' did not evaluate to a derivation", i->what()); - auto derivers = store->queryValidDerivers(bo.path); - if (derivers.empty()) - throw Error("'%s' does not have a known deriver", i->what()); - // FIXME: use all derivers? - drvPaths.insert(*derivers.begin()); - }, - [&](BuildableFromDrv bfd) { - drvPaths.insert(bfd.drvPath); - }, - }, b); - - return drvPaths; -} - -InstallablesCommand::InstallablesCommand() -{ - expectArgs({ - .label = "installables", - .handler = {&_installables}, - .completer = {[&](size_t, std::string_view prefix) { - completeInstallable(prefix); - }} - }); -} - -void InstallablesCommand::prepare() -{ - if (_installables.empty() && useDefaultInstallables()) - // FIXME: commands like "nix install" should not have a - // default, probably. - _installables.push_back("."); - installables = parseInstallables(getStore(), _installables); -} - -std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion() -{ - if (_installables.empty()) { - if (useDefaultInstallables()) - return parseFlakeRef(".", absPath(".")); - return {}; - } - return parseFlakeRef(_installables.front(), absPath(".")); -} - -InstallableCommand::InstallableCommand() -{ - expectArgs({ - .label = "installable", - .optional = true, - .handler = {&_installable}, - .completer = {[&](size_t, std::string_view prefix) { - completeInstallable(prefix); - }} - }); -} - -void InstallableCommand::prepare() -{ - installable = parseInstallable(getStore(), _installable); -} - -} diff --git a/src/nix/installables.hh b/src/nix/installables.hh deleted file mode 100644 index f37b3f829..000000000 --- a/src/nix/installables.hh +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include "util.hh" -#include "path.hh" -#include "eval.hh" -#include "flake/flake.hh" - -#include <optional> - -#include <nlohmann/json_fwd.hpp> - -namespace nix { - -struct DrvInfo; -struct SourceExprCommand; - -namespace eval_cache { class EvalCache; class AttrCursor; } - -struct BuildableOpaque { - StorePath path; - nlohmann::json toJSON(ref<Store> store) const; -}; - -struct BuildableFromDrv { - StorePath drvPath; - std::map<std::string, std::optional<StorePath>> outputs; - nlohmann::json toJSON(ref<Store> store) const; -}; - -typedef std::variant< - BuildableOpaque, - BuildableFromDrv -> Buildable; - -typedef std::vector<Buildable> Buildables; -nlohmann::json buildablesToJSON(const Buildables & buildables, ref<Store> store); - -struct App -{ - std::vector<StorePathWithOutputs> context; - Path program; - // FIXME: add args, sandbox settings, metadata, ... -}; - -struct Installable -{ - virtual ~Installable() { } - - virtual std::string what() = 0; - - virtual Buildables toBuildables() = 0; - - Buildable toBuildable(); - - App toApp(EvalState & state); - - virtual std::pair<Value *, Pos> toValue(EvalState & state) - { - throw Error("argument '%s' cannot be evaluated", what()); - } - - /* Return a value only if this installable is a store path or a - symlink to it. */ - virtual std::optional<StorePath> getStorePath() - { - return {}; - } - - virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> - getCursors(EvalState & state); - - std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string> - getCursor(EvalState & state); - - virtual FlakeRef nixpkgsFlakeRef() const - { - return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); - } -}; - -struct InstallableValue : Installable -{ - ref<EvalState> state; - - InstallableValue(ref<EvalState> state) : state(state) {} - - struct DerivationInfo - { - StorePath drvPath; - std::optional<StorePath> outPath; - std::string outputName; - }; - - virtual std::vector<DerivationInfo> toDerivations() = 0; - - Buildables toBuildables() override; -}; - -struct InstallableFlake : InstallableValue -{ - FlakeRef flakeRef; - Strings attrPaths; - Strings prefixes; - const flake::LockFlags & lockFlags; - mutable std::shared_ptr<flake::LockedFlake> _lockedFlake; - - InstallableFlake(ref<EvalState> state, FlakeRef && flakeRef, - Strings && attrPaths, Strings && prefixes, const flake::LockFlags & lockFlags) - : InstallableValue(state), flakeRef(flakeRef), attrPaths(attrPaths), - prefixes(prefixes), lockFlags(lockFlags) - { } - - std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); } - - std::vector<std::string> getActualAttrPaths(); - - Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); - - std::tuple<std::string, FlakeRef, DerivationInfo> toDerivation(); - - std::vector<DerivationInfo> toDerivations() override; - - std::pair<Value *, Pos> toValue(EvalState & state) override; - - std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> - getCursors(EvalState & state) override; - - std::shared_ptr<flake::LockedFlake> getLockedFlake() const; - - FlakeRef nixpkgsFlakeRef() const override; -}; - -ref<eval_cache::EvalCache> openEvalCache( - EvalState & state, - std::shared_ptr<flake::LockedFlake> lockedFlake); - -} diff --git a/src/nix/legacy.cc b/src/nix/legacy.cc deleted file mode 100644 index 6df09ee37..000000000 --- a/src/nix/legacy.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include "legacy.hh" - -namespace nix { - -RegisterLegacyCommand::Commands * RegisterLegacyCommand::commands = 0; - -} diff --git a/src/nix/legacy.hh b/src/nix/legacy.hh deleted file mode 100644 index f503b0da3..000000000 --- a/src/nix/legacy.hh +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include <functional> -#include <map> -#include <string> - -namespace nix { - -typedef std::function<void(int, char * *)> MainFunction; - -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; - } -}; - -} diff --git a/src/nix/local.mk b/src/nix/local.mk index 23c08fc86..83b6dd08b 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -14,9 +14,9 @@ nix_SOURCES := \ $(wildcard src/nix-instantiate/*.cc) \ $(wildcard src/nix-store/*.cc) \ -nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain +nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -I src/libcmd -nix_LIBS = libexpr libmain libfetchers libstore libutil +nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd nix_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -llowdown diff --git a/src/nix/markdown.cc b/src/nix/markdown.cc deleted file mode 100644 index 40788a42f..000000000 --- a/src/nix/markdown.cc +++ /dev/null @@ -1,50 +0,0 @@ -#include "markdown.hh" -#include "util.hh" -#include "finally.hh" - -#include <sys/queue.h> -extern "C" { -#include <lowdown.h> -} - -namespace nix { - -std::string renderMarkdownToTerminal(std::string_view markdown) -{ - struct lowdown_opts opts { - .type = LOWDOWN_TERM, - .maxdepth = 20, - .cols = std::min(getWindowSize().second, (unsigned short) 80), - .hmargin = 0, - .vmargin = 0, - .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, - .oflags = 0, - }; - - auto doc = lowdown_doc_new(&opts); - if (!doc) - throw Error("cannot allocate Markdown document"); - Finally freeDoc([&]() { lowdown_doc_free(doc); }); - - size_t maxn = 0; - auto node = lowdown_doc_parse(doc, &maxn, markdown.data(), markdown.size()); - if (!node) - throw Error("cannot parse Markdown document"); - Finally freeNode([&]() { lowdown_node_free(node); }); - - auto renderer = lowdown_term_new(&opts); - if (!renderer) - throw Error("cannot allocate Markdown renderer"); - Finally freeRenderer([&]() { lowdown_term_free(renderer); }); - - auto buf = lowdown_buf_new(16384); - if (!buf) - throw Error("cannot allocate Markdown output buffer"); - Finally freeBuffer([&]() { lowdown_buf_free(buf); }); - - lowdown_term_rndr(buf, nullptr, renderer, node); - - return std::string(buf->data, buf->size); -} - -} diff --git a/src/nix/markdown.hh b/src/nix/markdown.hh deleted file mode 100644 index 78320fcf5..000000000 --- a/src/nix/markdown.hh +++ /dev/null @@ -1,7 +0,0 @@ -#include "types.hh" - -namespace nix { - -std::string renderMarkdownToTerminal(std::string_view markdown); - -} |