Split out InstallableFlake
and InstallableAttrPath
This commit is contained in:
parent
de714833f1
commit
fa4733fce5
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -103,6 +103,7 @@ outputs/
|
||||||
|
|
||||||
*.a
|
*.a
|
||||||
*.o
|
*.o
|
||||||
|
*.o.tmp
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
|
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 {
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue