forked from lix-project/lix
Merge pull request #7748 from obsidiansystems/split-other-installables
Keep splitting libcmd headers & files
This commit is contained in:
commit
924ef6761b
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -103,6 +103,7 @@ outputs/
|
||||||
|
|
||||||
*.a
|
*.a
|
||||||
*.o
|
*.o
|
||||||
|
*.o.tmp
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
|
#include "repl.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ ref<EvalState> EvalCommand::getEvalState()
|
||||||
;
|
;
|
||||||
|
|
||||||
if (startReplOnEvalErrors) {
|
if (startReplOnEvalErrors) {
|
||||||
evalState->debugRepl = &runRepl;
|
evalState->debugRepl = &AbstractNixRepl::runSimple;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return ref<EvalState>(evalState);
|
return ref<EvalState>(evalState);
|
||||||
|
@ -218,20 +219,6 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
|
||||||
run(store, *storePaths.begin());
|
run(store, *storePaths.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings editorFor(const Path & file, uint32_t line)
|
|
||||||
{
|
|
||||||
auto editor = getEnv("EDITOR").value_or("cat");
|
|
||||||
auto args = tokenizeString<Strings>(editor);
|
|
||||||
if (line > 0 && (
|
|
||||||
editor.find("emacs") != std::string::npos ||
|
|
||||||
editor.find("nano") != std::string::npos ||
|
|
||||||
editor.find("vim") != std::string::npos ||
|
|
||||||
editor.find("kak") != std::string::npos))
|
|
||||||
args.push_back(fmt("+%d", line));
|
|
||||||
args.push_back(file);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
MixProfile::MixProfile()
|
MixProfile::MixProfile()
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
|
|
|
@ -231,10 +231,6 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||||
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
|
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper function to generate args that invoke $EDITOR on
|
|
||||||
filename:lineno. */
|
|
||||||
Strings editorFor(const Path & file, uint32_t line);
|
|
||||||
|
|
||||||
struct MixProfile : virtual StoreCommand
|
struct MixProfile : virtual StoreCommand
|
||||||
{
|
{
|
||||||
std::optional<Path> profile;
|
std::optional<Path> profile;
|
||||||
|
@ -284,8 +280,4 @@ void printClosureDiff(
|
||||||
const StorePath & afterPath,
|
const StorePath & afterPath,
|
||||||
std::string_view indent);
|
std::string_view indent);
|
||||||
|
|
||||||
|
|
||||||
void runRepl(
|
|
||||||
ref<EvalState> evalState,
|
|
||||||
const ValMap & extraEnv);
|
|
||||||
}
|
}
|
||||||
|
|
20
src/libcmd/editor-for.cc
Normal file
20
src/libcmd/editor-for.cc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "util.hh"
|
||||||
|
#include "editor-for.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
Strings editorFor(const Path & file, uint32_t line)
|
||||||
|
{
|
||||||
|
auto editor = getEnv("EDITOR").value_or("cat");
|
||||||
|
auto args = tokenizeString<Strings>(editor);
|
||||||
|
if (line > 0 && (
|
||||||
|
editor.find("emacs") != std::string::npos ||
|
||||||
|
editor.find("nano") != std::string::npos ||
|
||||||
|
editor.find("vim") != std::string::npos ||
|
||||||
|
editor.find("kak") != std::string::npos))
|
||||||
|
args.push_back(fmt("+%d", line));
|
||||||
|
args.push_back(file);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/libcmd/editor-for.hh
Normal file
11
src/libcmd/editor-for.hh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Helper function to generate args that invoke $EDITOR on
|
||||||
|
filename:lineno. */
|
||||||
|
Strings editorFor(const Path & file, uint32_t line);
|
||||||
|
|
||||||
|
}
|
109
src/libcmd/installable-attr-path.cc
Normal file
109
src/libcmd/installable-attr-path.cc
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "installable-attr-path.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "util.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 "build-result.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
InstallableAttrPath::InstallableAttrPath(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
const std::string & attrPath,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
|
: InstallableValue(state)
|
||||||
|
, cmd(cmd)
|
||||||
|
, v(allocRootValue(v))
|
||||||
|
, attrPath(attrPath)
|
||||||
|
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
|
||||||
|
{
|
||||||
|
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
||||||
|
state.forceValue(*vRes, pos);
|
||||||
|
return {vRes, pos};
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
||||||
|
{
|
||||||
|
auto v = toValue(*state).first;
|
||||||
|
|
||||||
|
Bindings & autoArgs = *cmd.getAutoArgs(*state);
|
||||||
|
|
||||||
|
DrvInfos drvInfos;
|
||||||
|
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
|
||||||
|
|
||||||
|
// Backward compatibility hack: group results by drvPath. This
|
||||||
|
// helps keep .all output together.
|
||||||
|
std::map<StorePath, OutputsSpec> byDrvPath;
|
||||||
|
|
||||||
|
for (auto & drvInfo : drvInfos) {
|
||||||
|
auto drvPath = drvInfo.queryDrvPath();
|
||||||
|
if (!drvPath)
|
||||||
|
throw Error("'%s' is not a derivation", what());
|
||||||
|
|
||||||
|
auto newOutputs = std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||||
|
std::set<std::string> outputsToInstall;
|
||||||
|
for (auto & output : drvInfo.queryOutputs(false, true))
|
||||||
|
outputsToInstall.insert(output.first);
|
||||||
|
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw());
|
||||||
|
|
||||||
|
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
|
||||||
|
|
||||||
|
if (!didInsert)
|
||||||
|
iter->second = iter->second.union_(newOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo res;
|
||||||
|
for (auto & [drvPath, outputs] : byDrvPath)
|
||||||
|
res.push_back({
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = outputs,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallableAttrPath InstallableAttrPath::parse(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
state, cmd, v,
|
||||||
|
prefix == "." ? "" : std::string { prefix },
|
||||||
|
extendedOutputsSpec
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
src/libcmd/installable-attr-path.hh
Normal file
56
src/libcmd/installable-attr-path.hh
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "installable-value.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "util.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 "eval-cache.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
#include "registry.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class InstallableAttrPath : public InstallableValue
|
||||||
|
{
|
||||||
|
SourceExprCommand & cmd;
|
||||||
|
RootValue v;
|
||||||
|
std::string attrPath;
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec;
|
||||||
|
|
||||||
|
InstallableAttrPath(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
const std::string & attrPath,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec);
|
||||||
|
|
||||||
|
std::string what() const override { return attrPath; };
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
|
DerivedPathsWithInfo toDerivedPaths() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static InstallableAttrPath parse(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
236
src/libcmd/installable-flake.cc
Normal file
236
src/libcmd/installable-flake.cc
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "util.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 "build-result.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
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->determinePos(noPos); });
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallableFlake::InstallableFlake(
|
||||||
|
SourceExprCommand * cmd,
|
||||||
|
ref<EvalState> state,
|
||||||
|
FlakeRef && flakeRef,
|
||||||
|
std::string_view fragment,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec,
|
||||||
|
Strings attrPaths,
|
||||||
|
Strings prefixes,
|
||||||
|
const flake::LockFlags & lockFlags)
|
||||||
|
: InstallableValue(state),
|
||||||
|
flakeRef(flakeRef),
|
||||||
|
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
||||||
|
prefixes(fragment == "" ? Strings{} : prefixes),
|
||||||
|
extendedOutputsSpec(std::move(extendedOutputsSpec)),
|
||||||
|
lockFlags(lockFlags)
|
||||||
|
{
|
||||||
|
if (cmd && cmd->getAutoArgs(*state)->size())
|
||||||
|
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
||||||
|
{
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
|
||||||
|
|
||||||
|
auto attr = getCursor(*state);
|
||||||
|
|
||||||
|
auto attrPath = attr->getAttrPathStr();
|
||||||
|
|
||||||
|
if (!attr->isDerivation()) {
|
||||||
|
|
||||||
|
// FIXME: use eval cache?
|
||||||
|
auto v = attr->forceValue();
|
||||||
|
|
||||||
|
if (v.type() == nPath) {
|
||||||
|
PathSet context;
|
||||||
|
auto storePath = state->copyPathToStore(context, Path(v.path));
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Opaque {
|
||||||
|
.path = std::move(storePath),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (v.type() == nString) {
|
||||||
|
PathSet context;
|
||||||
|
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
|
||||||
|
auto storePath = state->store->maybeParseStorePath(s);
|
||||||
|
if (storePath && context.count(std::string(s))) {
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Opaque {
|
||||||
|
.path = std::move(*storePath),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
} else
|
||||||
|
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto drvPath = attr->forceDerivation();
|
||||||
|
|
||||||
|
std::optional<NixInt> priority;
|
||||||
|
|
||||||
|
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||||
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||||
|
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
||||||
|
priority = aPriority->getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = std::move(drvPath),
|
||||||
|
.outputs = std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||||
|
std::set<std::string> outputsToInstall;
|
||||||
|
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||||
|
if (aOutputSpecified->getBool()) {
|
||||||
|
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
||||||
|
outputsToInstall = { aOutputName->getString() };
|
||||||
|
}
|
||||||
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||||
|
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
||||||
|
for (auto & s : aOutputsToInstall->getListOfStrings())
|
||||||
|
outputsToInstall.insert(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputsToInstall.empty())
|
||||||
|
outputsToInstall.insert("out");
|
||||||
|
|
||||||
|
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw()),
|
||||||
|
},
|
||||||
|
.info = {
|
||||||
|
.priority = priority,
|
||||||
|
.originalRef = flakeRef,
|
||||||
|
.resolvedRef = getLockedFlake()->flake.lockedRef,
|
||||||
|
.attrPath = attrPath,
|
||||||
|
.extendedOutputsSpec = extendedOutputsSpec,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||||
|
{
|
||||||
|
return {&getCursor(state)->forceValue(), noPos};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
|
InstallableFlake::getCursors(EvalState & state)
|
||||||
|
{
|
||||||
|
auto evalCache = openEvalCache(state,
|
||||||
|
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
|
||||||
|
|
||||||
|
auto root = evalCache->getRoot();
|
||||||
|
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>> res;
|
||||||
|
|
||||||
|
Suggestions suggestions;
|
||||||
|
auto attrPaths = getActualAttrPaths();
|
||||||
|
|
||||||
|
for (auto & attrPath : attrPaths) {
|
||||||
|
debug("trying flake output attribute '%s'", attrPath);
|
||||||
|
|
||||||
|
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||||
|
if (attr) {
|
||||||
|
res.push_back(ref(*attr));
|
||||||
|
} else {
|
||||||
|
suggestions += attr.getSuggestions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.size() == 0)
|
||||||
|
throw Error(
|
||||||
|
suggestions,
|
||||||
|
"flake '%s' does not provide attribute %s",
|
||||||
|
flakeRef,
|
||||||
|
showAttrPaths(attrPaths));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||||
|
{
|
||||||
|
if (!_lockedFlake) {
|
||||||
|
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||||
|
lockFlagsApplyConfig.applyNixConfig = true;
|
||||||
|
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
src/libcmd/installable-flake.hh
Normal file
50
src/libcmd/installable-flake.hh
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "installable-value.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct InstallableFlake : InstallableValue
|
||||||
|
{
|
||||||
|
FlakeRef flakeRef;
|
||||||
|
Strings attrPaths;
|
||||||
|
Strings prefixes;
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec;
|
||||||
|
const flake::LockFlags & lockFlags;
|
||||||
|
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
|
||||||
|
|
||||||
|
InstallableFlake(
|
||||||
|
SourceExprCommand * cmd,
|
||||||
|
ref<EvalState> state,
|
||||||
|
FlakeRef && flakeRef,
|
||||||
|
std::string_view fragment,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec,
|
||||||
|
Strings attrPaths,
|
||||||
|
Strings prefixes,
|
||||||
|
const flake::LockFlags & lockFlags);
|
||||||
|
|
||||||
|
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||||
|
|
||||||
|
std::vector<std::string> getActualAttrPaths();
|
||||||
|
|
||||||
|
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
|
||||||
|
|
||||||
|
DerivedPathsWithInfo toDerivedPaths() override;
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
|
/* Get a cursor to every attrpath in getActualAttrPaths()
|
||||||
|
that exists. However if none exists, throw an exception. */
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
14
src/libcmd/installable-value.hh
Normal file
14
src/libcmd/installable-value.hh
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "installables.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct InstallableValue : Installable
|
||||||
|
{
|
||||||
|
ref<EvalState> state;
|
||||||
|
|
||||||
|
InstallableValue(ref<EvalState> state) : state(state) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "installables.hh"
|
#include "installables.hh"
|
||||||
#include "installable-derived-path.hh"
|
#include "installable-derived-path.hh"
|
||||||
|
#include "installable-attr-path.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
@ -390,111 +392,6 @@ static StorePath getDeriver(
|
||||||
return *derivers.begin();
|
return *derivers.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InstallableAttrPath : InstallableValue
|
|
||||||
{
|
|
||||||
SourceExprCommand & cmd;
|
|
||||||
RootValue v;
|
|
||||||
std::string attrPath;
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec;
|
|
||||||
|
|
||||||
InstallableAttrPath(
|
|
||||||
ref<EvalState> state,
|
|
||||||
SourceExprCommand & cmd,
|
|
||||||
Value * v,
|
|
||||||
const std::string & attrPath,
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec)
|
|
||||||
: InstallableValue(state)
|
|
||||||
, cmd(cmd)
|
|
||||||
, v(allocRootValue(v))
|
|
||||||
, attrPath(attrPath)
|
|
||||||
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
std::string what() const override { return attrPath; }
|
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override
|
|
||||||
{
|
|
||||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
|
||||||
state.forceValue(*vRes, pos);
|
|
||||||
return {vRes, pos};
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override
|
|
||||||
{
|
|
||||||
auto v = toValue(*state).first;
|
|
||||||
|
|
||||||
Bindings & autoArgs = *cmd.getAutoArgs(*state);
|
|
||||||
|
|
||||||
DrvInfos drvInfos;
|
|
||||||
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
|
|
||||||
|
|
||||||
// Backward compatibility hack: group results by drvPath. This
|
|
||||||
// helps keep .all output together.
|
|
||||||
std::map<StorePath, OutputsSpec> byDrvPath;
|
|
||||||
|
|
||||||
for (auto & drvInfo : drvInfos) {
|
|
||||||
auto drvPath = drvInfo.queryDrvPath();
|
|
||||||
if (!drvPath)
|
|
||||||
throw Error("'%s' is not a derivation", what());
|
|
||||||
|
|
||||||
auto newOutputs = std::visit(overloaded {
|
|
||||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
|
||||||
std::set<std::string> outputsToInstall;
|
|
||||||
for (auto & output : drvInfo.queryOutputs(false, true))
|
|
||||||
outputsToInstall.insert(output.first);
|
|
||||||
return OutputsSpec::Names { std::move(outputsToInstall) };
|
|
||||||
},
|
|
||||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
|
||||||
return e;
|
|
||||||
},
|
|
||||||
}, extendedOutputsSpec.raw());
|
|
||||||
|
|
||||||
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
|
|
||||||
|
|
||||||
if (!didInsert)
|
|
||||||
iter->second = iter->second.union_(newOutputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo res;
|
|
||||||
for (auto & [drvPath, outputs] : byDrvPath)
|
|
||||||
res.push_back({
|
|
||||||
.path = DerivedPath::Built {
|
|
||||||
.drvPath = drvPath,
|
|
||||||
.outputs = outputs,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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->determinePos(noPos); });
|
|
||||||
|
|
||||||
return aOutputs->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref<eval_cache::EvalCache> openEvalCache(
|
ref<eval_cache::EvalCache> openEvalCache(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
||||||
|
@ -524,187 +421,6 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstallableFlake::InstallableFlake(
|
|
||||||
SourceExprCommand * cmd,
|
|
||||||
ref<EvalState> state,
|
|
||||||
FlakeRef && flakeRef,
|
|
||||||
std::string_view fragment,
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec,
|
|
||||||
Strings attrPaths,
|
|
||||||
Strings prefixes,
|
|
||||||
const flake::LockFlags & lockFlags)
|
|
||||||
: InstallableValue(state),
|
|
||||||
flakeRef(flakeRef),
|
|
||||||
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
|
||||||
prefixes(fragment == "" ? Strings{} : prefixes),
|
|
||||||
extendedOutputsSpec(std::move(extendedOutputsSpec)),
|
|
||||||
lockFlags(lockFlags)
|
|
||||||
{
|
|
||||||
if (cmd && cmd->getAutoArgs(*state)->size())
|
|
||||||
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
|
||||||
{
|
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
|
|
||||||
|
|
||||||
auto attr = getCursor(*state);
|
|
||||||
|
|
||||||
auto attrPath = attr->getAttrPathStr();
|
|
||||||
|
|
||||||
if (!attr->isDerivation()) {
|
|
||||||
|
|
||||||
// FIXME: use eval cache?
|
|
||||||
auto v = attr->forceValue();
|
|
||||||
|
|
||||||
if (v.type() == nPath) {
|
|
||||||
PathSet context;
|
|
||||||
auto storePath = state->copyPathToStore(context, Path(v.path));
|
|
||||||
return {{
|
|
||||||
.path = DerivedPath::Opaque {
|
|
||||||
.path = std::move(storePath),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (v.type() == nString) {
|
|
||||||
PathSet context;
|
|
||||||
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
|
|
||||||
auto storePath = state->store->maybeParseStorePath(s);
|
|
||||||
if (storePath && context.count(std::string(s))) {
|
|
||||||
return {{
|
|
||||||
.path = DerivedPath::Opaque {
|
|
||||||
.path = std::move(*storePath),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
} else
|
|
||||||
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto drvPath = attr->forceDerivation();
|
|
||||||
|
|
||||||
std::optional<NixInt> priority;
|
|
||||||
|
|
||||||
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
|
||||||
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
|
||||||
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
|
||||||
priority = aPriority->getInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {{
|
|
||||||
.path = DerivedPath::Built {
|
|
||||||
.drvPath = std::move(drvPath),
|
|
||||||
.outputs = std::visit(overloaded {
|
|
||||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
|
||||||
std::set<std::string> outputsToInstall;
|
|
||||||
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
|
||||||
if (aOutputSpecified->getBool()) {
|
|
||||||
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
|
||||||
outputsToInstall = { aOutputName->getString() };
|
|
||||||
}
|
|
||||||
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
|
||||||
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
|
||||||
for (auto & s : aOutputsToInstall->getListOfStrings())
|
|
||||||
outputsToInstall.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputsToInstall.empty())
|
|
||||||
outputsToInstall.insert("out");
|
|
||||||
|
|
||||||
return OutputsSpec::Names { std::move(outputsToInstall) };
|
|
||||||
},
|
|
||||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
|
||||||
return e;
|
|
||||||
},
|
|
||||||
}, extendedOutputsSpec.raw()),
|
|
||||||
},
|
|
||||||
.info = {
|
|
||||||
.priority = priority,
|
|
||||||
.originalRef = flakeRef,
|
|
||||||
.resolvedRef = getLockedFlake()->flake.lockedRef,
|
|
||||||
.attrPath = attrPath,
|
|
||||||
.extendedOutputsSpec = extendedOutputsSpec,
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
|
||||||
{
|
|
||||||
return {&getCursor(state)->forceValue(), noPos};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>>
|
|
||||||
InstallableFlake::getCursors(EvalState & state)
|
|
||||||
{
|
|
||||||
auto evalCache = openEvalCache(state,
|
|
||||||
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
|
|
||||||
|
|
||||||
auto root = evalCache->getRoot();
|
|
||||||
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>> res;
|
|
||||||
|
|
||||||
Suggestions suggestions;
|
|
||||||
auto attrPaths = getActualAttrPaths();
|
|
||||||
|
|
||||||
for (auto & attrPath : attrPaths) {
|
|
||||||
debug("trying flake output attribute '%s'", attrPath);
|
|
||||||
|
|
||||||
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
|
||||||
if (attr) {
|
|
||||||
res.push_back(ref(*attr));
|
|
||||||
} else {
|
|
||||||
suggestions += attr.getSuggestions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.size() == 0)
|
|
||||||
throw Error(
|
|
||||||
suggestions,
|
|
||||||
"flake '%s' does not provide attribute %s",
|
|
||||||
flakeRef,
|
|
||||||
showAttrPaths(attrPaths));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
|
||||||
{
|
|
||||||
if (!_lockedFlake) {
|
|
||||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
|
||||||
lockFlagsApplyConfig.applyNixConfig = true;
|
|
||||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
|
||||||
}
|
|
||||||
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(
|
std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
ref<Store> store, std::vector<std::string> ss)
|
ref<Store> store, std::vector<std::string> ss)
|
||||||
{
|
{
|
||||||
|
@ -739,9 +455,8 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||||
result.push_back(
|
result.push_back(
|
||||||
std::make_shared<InstallableAttrPath>(
|
std::make_shared<InstallableAttrPath>(
|
||||||
state, *this, vFile,
|
InstallableAttrPath::parse(
|
||||||
prefix == "." ? "" : std::string { prefix },
|
state, *this, vFile, prefix, extendedOutputsSpec)));
|
||||||
extendedOutputsSpec));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -993,8 +708,8 @@ void InstallablesCommand::prepare()
|
||||||
installables = load();
|
installables = load();
|
||||||
}
|
}
|
||||||
|
|
||||||
Installables InstallablesCommand::load() {
|
Installables InstallablesCommand::load()
|
||||||
Installables installables;
|
{
|
||||||
if (_installables.empty() && useDefaultInstallables())
|
if (_installables.empty() && useDefaultInstallables())
|
||||||
// FIXME: commands like "nix profile install" should not have a
|
// FIXME: commands like "nix profile install" should not have a
|
||||||
// default, probably.
|
// default, probably.
|
||||||
|
@ -1004,11 +719,8 @@ Installables InstallablesCommand::load() {
|
||||||
|
|
||||||
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
|
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
|
||||||
{
|
{
|
||||||
if (_installables.empty()) {
|
if (_installables.empty() && useDefaultInstallables())
|
||||||
if (useDefaultInstallables())
|
|
||||||
return {"."};
|
return {"."};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return _installables;
|
return _installables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,54 +161,4 @@ struct Installable
|
||||||
|
|
||||||
typedef std::vector<std::shared_ptr<Installable>> Installables;
|
typedef std::vector<std::shared_ptr<Installable>> Installables;
|
||||||
|
|
||||||
struct InstallableValue : Installable
|
|
||||||
{
|
|
||||||
ref<EvalState> state;
|
|
||||||
|
|
||||||
InstallableValue(ref<EvalState> state) : state(state) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstallableFlake : InstallableValue
|
|
||||||
{
|
|
||||||
FlakeRef flakeRef;
|
|
||||||
Strings attrPaths;
|
|
||||||
Strings prefixes;
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec;
|
|
||||||
const flake::LockFlags & lockFlags;
|
|
||||||
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
|
|
||||||
|
|
||||||
InstallableFlake(
|
|
||||||
SourceExprCommand * cmd,
|
|
||||||
ref<EvalState> state,
|
|
||||||
FlakeRef && flakeRef,
|
|
||||||
std::string_view fragment,
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec,
|
|
||||||
Strings attrPaths,
|
|
||||||
Strings prefixes,
|
|
||||||
const flake::LockFlags & lockFlags);
|
|
||||||
|
|
||||||
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
|
||||||
|
|
||||||
std::vector<std::string> getActualAttrPaths();
|
|
||||||
|
|
||||||
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override;
|
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
|
||||||
|
|
||||||
/* Get a cursor to every attrpath in getActualAttrPaths()
|
|
||||||
that exists. However if none exists, throw an exception. */
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>>
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ libcmd_DIR := $(d)
|
||||||
|
|
||||||
libcmd_SOURCES := $(wildcard $(d)/*.cc)
|
libcmd_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers -I src/nix
|
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
|
||||||
|
|
||||||
libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread
|
libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ extern "C" {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "repl.hh"
|
||||||
|
|
||||||
#include "ansicolor.hh"
|
#include "ansicolor.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
@ -31,7 +33,9 @@ extern "C" {
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "command.hh"
|
#include "flake/flake.hh"
|
||||||
|
#include "flake/lockfile.hh"
|
||||||
|
#include "editor-for.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "markdown.hh"
|
#include "markdown.hh"
|
||||||
#include "local-fs-store.hh"
|
#include "local-fs-store.hh"
|
||||||
|
@ -45,18 +49,16 @@ extern "C" {
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct NixRepl
|
struct NixRepl
|
||||||
|
: AbstractNixRepl,
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
: gc
|
gc
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
std::string curDir;
|
std::string curDir;
|
||||||
ref<EvalState> state;
|
|
||||||
Bindings * autoArgs;
|
|
||||||
|
|
||||||
size_t debugTraceIndex;
|
size_t debugTraceIndex;
|
||||||
|
|
||||||
Strings loadedFiles;
|
Strings loadedFiles;
|
||||||
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
|
||||||
std::function<AnnotatedValues()> getValues;
|
std::function<AnnotatedValues()> getValues;
|
||||||
|
|
||||||
const static int envSize = 32768;
|
const static int envSize = 32768;
|
||||||
|
@ -69,8 +71,11 @@ struct NixRepl
|
||||||
|
|
||||||
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
||||||
std::function<AnnotatedValues()> getValues);
|
std::function<AnnotatedValues()> getValues);
|
||||||
~NixRepl();
|
virtual ~NixRepl();
|
||||||
void mainLoop();
|
|
||||||
|
void mainLoop() override;
|
||||||
|
void initEnv() override;
|
||||||
|
|
||||||
StringSet completePrefix(const std::string & prefix);
|
StringSet completePrefix(const std::string & prefix);
|
||||||
bool getLine(std::string & input, const std::string & prompt);
|
bool getLine(std::string & input, const std::string & prompt);
|
||||||
StorePath getDerivationPath(Value & v);
|
StorePath getDerivationPath(Value & v);
|
||||||
|
@ -78,7 +83,6 @@ struct NixRepl
|
||||||
|
|
||||||
void loadFile(const Path & path);
|
void loadFile(const Path & path);
|
||||||
void loadFlake(const std::string & flakeRef);
|
void loadFlake(const std::string & flakeRef);
|
||||||
void initEnv();
|
|
||||||
void loadFiles();
|
void loadFiles();
|
||||||
void reloadFiles();
|
void reloadFiles();
|
||||||
void addAttrsToScope(Value & attrs);
|
void addAttrsToScope(Value & attrs);
|
||||||
|
@ -92,7 +96,6 @@ struct NixRepl
|
||||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
std::string removeWhitespace(std::string s)
|
std::string removeWhitespace(std::string s)
|
||||||
{
|
{
|
||||||
s = chomp(s);
|
s = chomp(s);
|
||||||
|
@ -104,7 +107,7 @@ std::string removeWhitespace(std::string s)
|
||||||
|
|
||||||
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
std::function<NixRepl::AnnotatedValues()> getValues)
|
std::function<NixRepl::AnnotatedValues()> getValues)
|
||||||
: state(state)
|
: AbstractNixRepl(state)
|
||||||
, debugTraceIndex(0)
|
, debugTraceIndex(0)
|
||||||
, getValues(getValues)
|
, getValues(getValues)
|
||||||
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
|
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
|
||||||
|
@ -1029,8 +1032,22 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void runRepl(
|
|
||||||
ref<EvalState>evalState,
|
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||||
|
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
|
std::function<AnnotatedValues()> getValues)
|
||||||
|
{
|
||||||
|
return std::make_unique<NixRepl>(
|
||||||
|
searchPath,
|
||||||
|
openStore(),
|
||||||
|
state,
|
||||||
|
getValues
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AbstractNixRepl::runSimple(
|
||||||
|
ref<EvalState> evalState,
|
||||||
const ValMap & extraEnv)
|
const ValMap & extraEnv)
|
||||||
{
|
{
|
||||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
||||||
|
@ -1054,91 +1071,4 @@ void runRepl(
|
||||||
repl->mainLoop();
|
repl->mainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CmdRepl : InstallablesCommand
|
|
||||||
{
|
|
||||||
CmdRepl() {
|
|
||||||
evalSettings.pureEval = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void prepare() override
|
|
||||||
{
|
|
||||||
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
|
|
||||||
warn("future versions of Nix will require using `--file` to load a file");
|
|
||||||
if (this->_installables.size() > 1)
|
|
||||||
warn("more than one input file is not currently supported");
|
|
||||||
auto filePath = this->_installables[0].data();
|
|
||||||
file = std::optional(filePath);
|
|
||||||
_installables.front() = _installables.back();
|
|
||||||
_installables.pop_back();
|
|
||||||
}
|
|
||||||
installables = InstallablesCommand::load();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> files;
|
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPaths() override
|
|
||||||
{
|
|
||||||
return {""};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool useDefaultInstallables() override
|
|
||||||
{
|
|
||||||
return file.has_value() or expr.has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool forceImpureByDefault() override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string description() override
|
|
||||||
{
|
|
||||||
return "start an interactive environment for evaluating Nix expressions";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string doc() override
|
|
||||||
{
|
|
||||||
return
|
|
||||||
#include "repl.md"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
|
||||||
{
|
|
||||||
auto state = getEvalState();
|
|
||||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
|
||||||
auto installables = load();
|
|
||||||
NixRepl::AnnotatedValues values;
|
|
||||||
for (auto & installable: installables){
|
|
||||||
auto what = installable->what();
|
|
||||||
if (file){
|
|
||||||
auto [val, pos] = installable->toValue(*state);
|
|
||||||
auto what = installable->what();
|
|
||||||
state->forceValue(*val, pos);
|
|
||||||
auto autoArgs = getAutoArgs(*state);
|
|
||||||
auto valPost = state->allocValue();
|
|
||||||
state->autoCallFunction(*autoArgs, *val, *valPost);
|
|
||||||
state->forceValue(*valPost, pos);
|
|
||||||
values.push_back( {valPost, what });
|
|
||||||
} else {
|
|
||||||
auto [val, pos] = installable->toValue(*state);
|
|
||||||
values.push_back( {val, what} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
};
|
|
||||||
auto repl = std::make_unique<NixRepl>(
|
|
||||||
searchPath,
|
|
||||||
openStore(),
|
|
||||||
state,
|
|
||||||
getValues
|
|
||||||
);
|
|
||||||
repl->autoArgs = getAutoArgs(*repl->state);
|
|
||||||
repl->initEnv();
|
|
||||||
repl->mainLoop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto rCmdRepl = registerCommand<CmdRepl>("repl");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
39
src/libcmd/repl.hh
Normal file
39
src/libcmd/repl.hh
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "eval.hh"
|
||||||
|
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
|
#define GC_INCLUDE_NEW
|
||||||
|
#include <gc/gc_cpp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct AbstractNixRepl
|
||||||
|
{
|
||||||
|
ref<EvalState> state;
|
||||||
|
Bindings * autoArgs;
|
||||||
|
|
||||||
|
AbstractNixRepl(ref<EvalState> state)
|
||||||
|
: state(state)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual ~AbstractNixRepl()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
||||||
|
|
||||||
|
static std::unique_ptr<AbstractNixRepl> create(
|
||||||
|
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
|
std::function<AnnotatedValues()> getValues);
|
||||||
|
|
||||||
|
static void runSimple(
|
||||||
|
ref<EvalState> evalState,
|
||||||
|
const ValMap & extraEnv);
|
||||||
|
|
||||||
|
virtual void initEnv() = 0;
|
||||||
|
|
||||||
|
virtual void mainLoop() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
|
#include "editor-for.hh"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
|
95
src/nix/repl.cc
Normal file
95
src/nix/repl.cc
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#include "eval.hh"
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "command.hh"
|
||||||
|
#include "repl.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct CmdRepl : InstallablesCommand
|
||||||
|
{
|
||||||
|
CmdRepl() {
|
||||||
|
evalSettings.pureEval = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare() override
|
||||||
|
{
|
||||||
|
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
|
||||||
|
warn("future versions of Nix will require using `--file` to load a file");
|
||||||
|
if (this->_installables.size() > 1)
|
||||||
|
warn("more than one input file is not currently supported");
|
||||||
|
auto filePath = this->_installables[0].data();
|
||||||
|
file = std::optional(filePath);
|
||||||
|
_installables.front() = _installables.back();
|
||||||
|
_installables.pop_back();
|
||||||
|
}
|
||||||
|
installables = InstallablesCommand::load();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> files;
|
||||||
|
|
||||||
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
|
{
|
||||||
|
return {""};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useDefaultInstallables() override
|
||||||
|
{
|
||||||
|
return file.has_value() or expr.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool forceImpureByDefault() override
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "start an interactive environment for evaluating Nix expressions";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "repl.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto state = getEvalState();
|
||||||
|
auto getValues = [&]()->AbstractNixRepl::AnnotatedValues{
|
||||||
|
auto installables = load();
|
||||||
|
AbstractNixRepl::AnnotatedValues values;
|
||||||
|
for (auto & installable: installables){
|
||||||
|
auto what = installable->what();
|
||||||
|
if (file){
|
||||||
|
auto [val, pos] = installable->toValue(*state);
|
||||||
|
auto what = installable->what();
|
||||||
|
state->forceValue(*val, pos);
|
||||||
|
auto autoArgs = getAutoArgs(*state);
|
||||||
|
auto valPost = state->allocValue();
|
||||||
|
state->autoCallFunction(*autoArgs, *val, *valPost);
|
||||||
|
state->forceValue(*valPost, pos);
|
||||||
|
values.push_back( {valPost, what });
|
||||||
|
} else {
|
||||||
|
auto [val, pos] = installable->toValue(*state);
|
||||||
|
values.push_back( {val, what} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
auto repl = AbstractNixRepl::create(
|
||||||
|
searchPath,
|
||||||
|
openStore(),
|
||||||
|
state,
|
||||||
|
getValues
|
||||||
|
);
|
||||||
|
repl->autoArgs = getAutoArgs(*repl->state);
|
||||||
|
repl->initEnv();
|
||||||
|
repl->mainLoop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdRepl = registerCommand<CmdRepl>("repl");
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue