#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 & dependencies) { StringPairs res; if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { for (auto & dep : dependencies) { if (auto drvDep = std::get_if(&dep.path)) { for (auto & [ outputName, outputPath ] : drvDep->outputs) { res.emplace( DownstreamPlaceholder::fromSingleDerivedPathBuilt( SingleDerivedPath::Built { .drvPath = make_ref(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 & 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 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 evalStore, ref store) { auto res = unresolved; Installables installableContext; for (auto & ctxElt : unresolved.context) installableContext.push_back( make_ref(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; } }