2023-02-03 21:53:40 +02:00
|
|
|
#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;
|
2023-08-20 00:03:31 +03:00
|
|
|
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")){
|
|
|
|
attrPaths.front().erase(0,1);
|
|
|
|
res.push_back(attrPaths.front());
|
|
|
|
return res;
|
|
|
|
}
|
2023-02-03 21:53:40 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2023-12-11 16:48:24 +02:00
|
|
|
state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos));
|
2023-02-03 21:53:40 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
Make more string values work as installables
As discussed in #7417, it would be good to make more string values work
as installables. That is to say, if an installable refers to a value,
and the value is a string, it used to not work at all, since #7484, it
works somewhat, and this PR make it work some more.
The new cases that are added for `BuiltPath` contexts:
- Fixed input- or content-addressed derivation:
```
nix-repl> hello.out.outPath
"/nix/store/jppfl2bp1zhx8sgs2mgifmsx6dv16mv2-hello-2.12"
nix-repl> :p builtins.getContext hello.out.outPath
{ "/nix/store/c7jrxqjhdda93lhbkanqfs07x2bzazbm-hello-2.12.drv" = { outputs = [ "out" ]; }; }
The string matches the specified single output of that derivation, so
it should also be valid.
- Floating content-addressed derivation:
```
nix-repl> (hello.overrideAttrs (_: { __contentAddressed = true; })).out.outPath
"/1a08j26xqc0zm8agps8anxpjji410yvsx4pcgyn4bfan1ddkx2g0"
nix-repl> :p builtins.getContext (hello.overrideAttrs (_: { __contentAddressed = true; })).out.outPath
{ "/nix/store/qc645pyf9wl37c6qvqzaqkwsm1gp48al-hello-2.12.drv" = { outputs = [ "out" ]; }; }
```
The string is not a path but a placeholder, however it also matches
the context, and because it is a CA derivation we have no better
option. This should also be valid.
We may also want to think about richer attrset based values (also
discussed in that issue and #6507), but this change "completes" our
string-based building blocks, from which the others can be desugared
into or at least described/document/taught in terms of.
Progress towards #7417
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2023-01-11 00:18:59 +02:00
|
|
|
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
|
|
|
|
v,
|
|
|
|
noPos,
|
|
|
|
fmt("while evaluating the flake output attribute '%s'", attrPath)))
|
|
|
|
{
|
|
|
|
return { *derivedPathWithInfo };
|
2023-02-03 21:53:40 +02:00
|
|
|
}
|
|
|
|
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 {
|
Make the Derived Path family of types inductive for dynamic derivations
We want to be able to write down `foo.drv^bar.drv^baz`:
`foo.drv^bar.drv` is the dynamic derivation (since it is itself a
derivation output, `bar.drv` from `foo.drv`).
To that end, we create `Single{Derivation,BuiltPath}` types, that are
very similar except instead of having multiple outputs (in a set or
map), they have a single one. This is for everything to the left of the
rightmost `^`.
`NixStringContextElem` has an analogous change, and now can reuse
`SingleDerivedPath` at the top level. In fact, if we ever get rid of
`DrvDeep`, `NixStringContextElem` could be replaced with
`SingleDerivedPath` entirely!
Important note: some JSON formats have changed.
We already can *produce* dynamic derivations, but we can't refer to them
directly. Today, we can merely express building or example at the top
imperatively over time by building `foo.drv^bar.drv`, and then with a
second nix invocation doing `<result-from-first>^baz`, but this is not
declarative. The ethos of Nix of being able to write down the full plan
everything you want to do, and then execute than plan with a single
command, and for that we need the new inductive form of these types.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-01-16 00:39:04 +02:00
|
|
|
.drvPath = makeConstantStorePathRef(std::move(drvPath)),
|
2023-02-03 21:53:40 +02:00
|
|
|
.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;
|
|
|
|
},
|
2023-08-16 19:29:23 +03:00
|
|
|
}, extendedOutputsSpec.raw),
|
2023-02-03 21:53:40 +02:00
|
|
|
},
|
2023-02-06 06:28:18 +02:00
|
|
|
.info = make_ref<ExtraPathInfoFlake>(
|
|
|
|
ExtraPathInfoValue::Value {
|
|
|
|
.priority = priority,
|
|
|
|
.attrPath = attrPath,
|
|
|
|
.extendedOutputsSpec = extendedOutputsSpec,
|
|
|
|
},
|
|
|
|
ExtraPathInfoFlake::Flake {
|
|
|
|
.originalRef = flakeRef,
|
2022-05-04 11:35:28 +03:00
|
|
|
.lockedRef = getLockedFlake()->flake.lockedRef,
|
2023-02-06 06:28:18 +02:00
|
|
|
}),
|
2023-02-03 21:53:40 +02:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
|
|
|
{
|
|
|
|
return {&getCursor(state)->forceValue(), noPos};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<ref<eval_cache::AttrCursor>>
|
|
|
|
InstallableFlake::getCursors(EvalState & state)
|
|
|
|
{
|
2023-03-04 13:39:19 +02:00
|
|
|
auto evalCache = openEvalCache(state, getLockedFlake());
|
2023-02-03 21:53:40 +02:00
|
|
|
|
|
|
|
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;
|
2023-02-06 06:28:18 +02:00
|
|
|
// FIXME why this side effect?
|
2023-02-03 21:53:40 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-09 18:50:22 +03:00
|
|
|
return defaultNixpkgsFlakeRef();
|
2023-02-03 21:53:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|