forked from lix-project/lix
7ad66cb3ef
We use the same nested map representation we used for goals, again in order to save space. We might someday want to combine with `inputDrvs`, by doing `V = bool` instead of `V = std::set<OutputName>`, but we are not doing that yet for sake of a smaller diff. The ATerm format for Derivations also needs to be extended, in addition to the in-memory format. To accomodate this, we added a new basic versioning scheme, so old versions of Nix will get nice errors. (And going forward, if the ATerm format changes again the errors will be even better.) `parsedStrings`, an internal function used as part of parsing derivations in A-Term format, used to consume the final `]` but expect the initial `[` to already be consumed. This made for what looked like unbalanced brackets at callsites, which was confusing. Now it consumes both which is hopefully less confusing. As part of testing, we also created a unit test for the A-Term format for regular non-experimental derivations too. Co-authored-by: Robert Hensing <roberth@users.noreply.github.com> Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> Apply suggestions from code review Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
148 lines
5 KiB
C++
148 lines
5 KiB
C++
#include "installables.hh"
|
|
#include "installable-derived-path.hh"
|
|
#include "installable-value.hh"
|
|
#include "store-api.hh"
|
|
#include "eval-inline.hh"
|
|
#include "eval-cache.hh"
|
|
#include "names.hh"
|
|
#include "command.hh"
|
|
#include "derivations.hh"
|
|
#include "downstream-placeholder.hh"
|
|
|
|
namespace nix {
|
|
|
|
/**
|
|
* Return the rewrites that are needed to resolve a string whose context is
|
|
* included in `dependencies`.
|
|
*/
|
|
StringPairs resolveRewrites(
|
|
Store & store,
|
|
const std::vector<BuiltPathWithResult> & dependencies)
|
|
{
|
|
StringPairs res;
|
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
|
for (auto & dep : dependencies) {
|
|
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path)) {
|
|
for (auto & [ outputName, outputPath ] : drvDep->outputs) {
|
|
res.emplace(
|
|
DownstreamPlaceholder::fromSingleDerivedPathBuilt(
|
|
SingleDerivedPath::Built {
|
|
.drvPath = make_ref<SingleDerivedPath>(drvDep->drvPath->discardOutputPath()),
|
|
.output = outputName,
|
|
}).render(),
|
|
store.printStorePath(outputPath)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Resolve the given string assuming the given context.
|
|
*/
|
|
std::string resolveString(
|
|
Store & store,
|
|
const std::string & toResolve,
|
|
const std::vector<BuiltPathWithResult> & dependencies)
|
|
{
|
|
auto rewrites = resolveRewrites(store, dependencies);
|
|
return rewriteStrings(toResolve, rewrites);
|
|
}
|
|
|
|
UnresolvedApp InstallableValue::toApp(EvalState & state)
|
|
{
|
|
auto cursor = getCursor(state);
|
|
auto attrPath = cursor->getAttrPath();
|
|
|
|
auto type = cursor->getAttr("type")->getString();
|
|
|
|
std::string expected = !attrPath.empty() &&
|
|
(state.symbols[attrPath[0]] == "apps" || state.symbols[attrPath[0]] == "defaultApp")
|
|
? "app" : "derivation";
|
|
if (type != expected)
|
|
throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected);
|
|
|
|
if (type == "app") {
|
|
auto [program, context] = cursor->getAttr("program")->getStringWithContext();
|
|
|
|
std::vector<DerivedPath> context2;
|
|
for (auto & c : context) {
|
|
context2.emplace_back(std::visit(overloaded {
|
|
[&](const NixStringContextElem::DrvDeep & d) -> DerivedPath {
|
|
/* We want all outputs of the drv */
|
|
return DerivedPath::Built {
|
|
.drvPath = makeConstantStorePathRef(d.drvPath),
|
|
.outputs = OutputsSpec::All {},
|
|
};
|
|
},
|
|
[&](const NixStringContextElem::Built & b) -> DerivedPath {
|
|
return DerivedPath::Built {
|
|
.drvPath = b.drvPath,
|
|
.outputs = OutputsSpec::Names { b.output },
|
|
};
|
|
},
|
|
[&](const NixStringContextElem::Opaque & o) -> DerivedPath {
|
|
return DerivedPath::Opaque {
|
|
.path = o.path,
|
|
};
|
|
},
|
|
}, c.raw));
|
|
}
|
|
|
|
return UnresolvedApp{App {
|
|
.context = std::move(context2),
|
|
.program = program,
|
|
}};
|
|
}
|
|
|
|
else if (type == "derivation") {
|
|
auto drvPath = cursor->forceDerivation();
|
|
auto outPath = cursor->getAttr(state.sOutPath)->getString();
|
|
auto outputName = cursor->getAttr(state.sOutputName)->getString();
|
|
auto name = cursor->getAttr(state.sName)->getString();
|
|
auto aPname = cursor->maybeGetAttr("pname");
|
|
auto aMeta = cursor->maybeGetAttr(state.sMeta);
|
|
auto aMainProgram = aMeta ? aMeta->maybeGetAttr("mainProgram") : nullptr;
|
|
auto mainProgram =
|
|
aMainProgram
|
|
? aMainProgram->getString()
|
|
: aPname
|
|
? aPname->getString()
|
|
: DrvName(name).name;
|
|
auto program = outPath + "/bin/" + mainProgram;
|
|
return UnresolvedApp { App {
|
|
.context = { DerivedPath::Built {
|
|
.drvPath = makeConstantStorePathRef(drvPath),
|
|
.outputs = OutputsSpec::Names { outputName },
|
|
} },
|
|
.program = program,
|
|
}};
|
|
}
|
|
|
|
else
|
|
throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type);
|
|
}
|
|
|
|
// FIXME: move to libcmd
|
|
App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
|
|
{
|
|
auto res = unresolved;
|
|
|
|
Installables installableContext;
|
|
|
|
for (auto & ctxElt : unresolved.context)
|
|
installableContext.push_back(
|
|
make_ref<InstallableDerivedPath>(store, DerivedPath { ctxElt }));
|
|
|
|
auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext);
|
|
res.program = resolveString(*store, unresolved.program, builtContext);
|
|
if (!store->isInStore(res.program))
|
|
throw Error("app program '%s' is not in the Nix store", res.program);
|
|
|
|
return res;
|
|
}
|
|
|
|
}
|