mirror of
https://github.com/NixOS/nix.git
synced 2025-11-09 03:56:01 +01:00
* It is tough to contribute to a project that doesn't use a formatter, * It is extra hard to contribute to a project which has configured the formatter, but ignores it for some files * Code formatting makes it harder to hide obscure / weird bugs by accident or on purpose, Let's rip the bandaid off? Note that PRs currently in flight should be able to be merged relatively easily by applying `clang-format` to their tip prior to merge. Co-authored-by: Graham Christensen <graham@grahamc.com>
214 lines
6.9 KiB
C++
214 lines
6.9 KiB
C++
#include "nix/store/globals.hh"
|
|
#include "nix/cmd/installable-flake.hh"
|
|
#include "nix/cmd/installable-derived-path.hh"
|
|
#include "nix/store/outputs-spec.hh"
|
|
#include "nix/util/util.hh"
|
|
#include "nix/cmd/command.hh"
|
|
#include "nix/expr/attr-path.hh"
|
|
#include "nix/cmd/common-eval-args.hh"
|
|
#include "nix/store/derivations.hh"
|
|
#include "nix/expr/eval-inline.hh"
|
|
#include "nix/expr/eval.hh"
|
|
#include "nix/expr/get-drvs.hh"
|
|
#include "nix/store/store-api.hh"
|
|
#include "nix/main/shared.hh"
|
|
#include "nix/flake/flake.hh"
|
|
#include "nix/expr/eval-cache.hh"
|
|
#include "nix/util/url.hh"
|
|
#include "nix/fetchers/registry.hh"
|
|
#include "nix/store/build-result.hh"
|
|
|
|
#include <regex>
|
|
#include <queue>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
namespace nix {
|
|
|
|
std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
|
{
|
|
std::vector<std::string> res;
|
|
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")) {
|
|
attrPaths.front().erase(0, 1);
|
|
res.push_back(attrPaths.front());
|
|
return res;
|
|
}
|
|
|
|
for (auto & prefix : prefixes)
|
|
res.push_back(prefix + *attrPaths.begin());
|
|
|
|
for (auto & s : attrPaths)
|
|
res.push_back(s);
|
|
|
|
return res;
|
|
}
|
|
|
|
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 (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
|
|
v, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath))) {
|
|
return {*derivedPathWithInfo};
|
|
} else {
|
|
throw Error(
|
|
"expected flake output attribute '%s' to be a derivation or path but found %s: %s",
|
|
attrPath,
|
|
showType(v),
|
|
ValuePrinter(*this->state, v, errorPrintOptions));
|
|
}
|
|
}
|
|
|
|
auto drvPath = attr->forceDerivation();
|
|
|
|
std::optional<NixInt::Inner> priority;
|
|
|
|
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
|
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
|
priority = aPriority->getInt().value;
|
|
}
|
|
|
|
return {{
|
|
.path =
|
|
DerivedPath::Built{
|
|
.drvPath = makeConstantStorePathRef(std::move(drvPath)),
|
|
.outputs = std::visit(
|
|
overloaded{
|
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
|
StringSet 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 = make_ref<ExtraPathInfoFlake>(
|
|
ExtraPathInfoValue::Value{
|
|
.priority = priority,
|
|
.attrPath = attrPath,
|
|
.extendedOutputsSpec = extendedOutputsSpec,
|
|
},
|
|
ExtraPathInfoFlake::Flake{
|
|
.originalRef = flakeRef,
|
|
.lockedRef = getLockedFlake()->flake.lockedRef,
|
|
}),
|
|
}};
|
|
}
|
|
|
|
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, getLockedFlake());
|
|
|
|
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;
|
|
// FIXME why this side effect?
|
|
lockFlagsApplyConfig.applyNixConfig = true;
|
|
_lockedFlake =
|
|
std::make_shared<flake::LockedFlake>(lockFlake(flakeSettings, *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 defaultNixpkgsFlakeRef();
|
|
}
|
|
|
|
} // namespace nix
|