cleanup and slightly refactor DrvInfo::queryOutputs
Change-Id: I00d9ff707fe61995737b86af6d2eaa1e4d8116ff
This commit is contained in:
parent
c347d3df8f
commit
eb18dcb0ea
2 changed files with 118 additions and 48 deletions
|
@ -101,66 +101,134 @@ StorePath DrvInfo::queryOutPath()
|
|||
return *outPath;
|
||||
}
|
||||
|
||||
void DrvInfo::fillOutputs(bool withPaths)
|
||||
{
|
||||
auto fillDefault = [&]() {
|
||||
std::optional<StorePath> outPath = std::nullopt;
|
||||
if (withPaths) {
|
||||
outPath.emplace(this->queryOutPath());
|
||||
}
|
||||
this->outputs.emplace("out", outPath);
|
||||
};
|
||||
|
||||
// lol. lmao even.
|
||||
if (this->attrs == nullptr) {
|
||||
fillDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
Attr * outputs = this->attrs->get(this->state->sOutputs);
|
||||
if (outputs == nullptr) {
|
||||
fillDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(Qyriad): I don't think there is any codepath that can cause this to error.
|
||||
this->state->forceList(
|
||||
*outputs->value,
|
||||
outputs->pos,
|
||||
"while evaluating the 'outputs' attribute of a derivation"
|
||||
);
|
||||
|
||||
for (auto [idx, elem] : enumerate(outputs->value->listItems())) {
|
||||
// NOTE(Qyriad): This error should be *extremely* rare in practice.
|
||||
// It is impossible to construct with `stdenv.mkDerivation`,
|
||||
// `builtins.derivation`, or even `derivationStrict`. As far as we can tell,
|
||||
// it is only possible by overriding a derivation attrset already created by
|
||||
// one of those with `//` to introduce the failing `outputs` entry.
|
||||
auto errMsg = fmt("while evaluating output %d of a derivation", idx);
|
||||
std::string_view outputName = state->forceStringNoCtx(
|
||||
*elem,
|
||||
outputs->pos,
|
||||
errMsg
|
||||
);
|
||||
|
||||
if (withPaths) {
|
||||
// Find the attr with this output's name...
|
||||
Attr * out = this->attrs->get(this->state->symbols.create(outputName));
|
||||
if (out == nullptr) {
|
||||
// FIXME: throw error?
|
||||
continue;
|
||||
}
|
||||
|
||||
// Meanwhile we couldn't figure out any circumstances
|
||||
// that cause this to error.
|
||||
state->forceAttrs(*out->value, outputs->pos, errMsg);
|
||||
|
||||
// ...and evaluate its `outPath` attribute.
|
||||
Attr * outPath = out->value->attrs->get(this->state->sOutPath);
|
||||
if (outPath == nullptr) {
|
||||
continue;
|
||||
// FIXME: throw error?
|
||||
}
|
||||
|
||||
NixStringContext context;
|
||||
// And idk what could possibly cause this one to error
|
||||
// that wouldn't error before here.
|
||||
auto storePath = state->coerceToStorePath(
|
||||
outPath->pos,
|
||||
*outPath->value,
|
||||
context,
|
||||
errMsg
|
||||
);
|
||||
this->outputs.emplace(outputName, storePath);
|
||||
} else {
|
||||
this->outputs.emplace(outputName, std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
|
||||
{
|
||||
// If we haven't already cached the outputs set, then do so now.
|
||||
if (outputs.empty()) {
|
||||
/* Get the ‘outputs’ list. */
|
||||
Bindings::iterator i;
|
||||
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
||||
state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
|
||||
|
||||
/* For each output... */
|
||||
for (auto elem : i->value->listItems()) {
|
||||
std::string output(state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
|
||||
|
||||
if (withPaths) {
|
||||
/* Evaluate the corresponding set. */
|
||||
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||
NixStringContext context;
|
||||
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
|
||||
} else
|
||||
outputs.emplace(output, std::nullopt);
|
||||
}
|
||||
} else
|
||||
outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt);
|
||||
// FIXME: this behavior seems kind of busted, since whether or not this
|
||||
// DrvInfo will have paths is forever determined by the *first* call to
|
||||
// this function??
|
||||
fillOutputs(withPaths);
|
||||
}
|
||||
|
||||
// Things that operate on derivations like packages, like `nix-env` and `nix build`,
|
||||
// allow derivations to specify which outputs should be used in those user-facing
|
||||
// cases if the user didn't specify an output explicitly.
|
||||
// If the caller just wanted all the outputs for this derivation, though,
|
||||
// then we're done here.
|
||||
if (!onlyOutputsToInstall || !attrs)
|
||||
return outputs;
|
||||
|
||||
Bindings::iterator i;
|
||||
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
|
||||
Outputs result;
|
||||
auto out = outputs.find(queryOutputName());
|
||||
if (out == outputs.end())
|
||||
throw Error("derivation does not have output '%s'", queryOutputName());
|
||||
result.insert(*out);
|
||||
return result;
|
||||
// Regardless of `meta.outputsToInstall`, though, you can select into a derivation
|
||||
// output by its attribute, e.g. `pkgs.lix.dev`, which (lol?) sets the magic
|
||||
// attribute `outputSpecified = true`, and changes the `outputName` attr to the
|
||||
// explicitly selected-into output.
|
||||
if (Attr * outSpecAttr = attrs->get(state->sOutputSpecified)) {
|
||||
bool outputSpecified = this->state->forceBool(
|
||||
*outSpecAttr->value,
|
||||
outSpecAttr->pos,
|
||||
"while evaluating the 'outputSpecified' attribute of a derivation"
|
||||
);
|
||||
if (outputSpecified) {
|
||||
auto maybeOut = outputs.find(queryOutputName());
|
||||
if (maybeOut == outputs.end()) {
|
||||
throw Error("derivation does not have output '%s'", queryOutputName());
|
||||
}
|
||||
return Outputs{*maybeOut};
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
|
||||
const Value * outTI = queryMeta("outputsToInstall");
|
||||
if (!outTI) return outputs;
|
||||
auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw errMsg;
|
||||
Outputs result;
|
||||
for (auto elem : outTI->listItems()) {
|
||||
if (elem->type() != nString) throw errMsg;
|
||||
auto out = outputs.find(elem->string.s);
|
||||
if (out == outputs.end()) throw errMsg;
|
||||
result.insert(*out);
|
||||
}
|
||||
return result;
|
||||
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
|
||||
const Value * outTI = queryMeta("outputsToInstall");
|
||||
if (!outTI) return outputs;
|
||||
auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw errMsg;
|
||||
Outputs result;
|
||||
for (auto elem : outTI->listItems()) {
|
||||
if (elem->type() != nString) throw errMsg;
|
||||
auto out = outputs.find(elem->string.s);
|
||||
if (out == outputs.end()) throw errMsg;
|
||||
result.insert(*out);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ private:
|
|||
|
||||
bool checkMeta(Value & v);
|
||||
|
||||
void fillOutputs(bool withPaths = true);
|
||||
|
||||
public:
|
||||
/**
|
||||
* path towards the derivation
|
||||
|
|
Loading…
Reference in a new issue