2019-01-13 19:43:46 +02:00
|
|
|
#include "primops.hh"
|
|
|
|
#include "eval-inline.hh"
|
2022-03-18 00:29:15 +02:00
|
|
|
#include "derivations.hh"
|
2020-03-21 17:07:16 +02:00
|
|
|
#include "store-api.hh"
|
2019-01-13 19:43:46 +02:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2022-03-04 20:31:59 +02:00
|
|
|
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
2019-01-13 19:43:46 +02:00
|
|
|
{
|
|
|
|
PathSet context;
|
2022-04-28 13:54:14 +03:00
|
|
|
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
|
2022-01-21 17:20:54 +02:00
|
|
|
v.mkString(*s);
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
2019-01-13 19:43:46 +02:00
|
|
|
|
|
|
|
|
2022-03-04 20:31:59 +02:00
|
|
|
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
2019-01-13 19:43:46 +02:00
|
|
|
{
|
|
|
|
PathSet context;
|
2022-04-28 13:54:14 +03:00
|
|
|
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
|
2022-01-04 19:40:39 +02:00
|
|
|
v.mkBool(!context.empty());
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
|
2019-01-13 19:43:46 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
|
|
|
|
builder without causing the derivation to be built (for instance,
|
|
|
|
in the derivation that builds NARs in nix-push, when doing
|
|
|
|
source-only deployment). This primop marks the string context so
|
|
|
|
that builtins.derivation adds the path to drv.inputSrcs rather than
|
|
|
|
drv.inputDrvs. */
|
2022-03-04 20:31:59 +02:00
|
|
|
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
2019-01-13 19:43:46 +02:00
|
|
|
{
|
|
|
|
PathSet context;
|
2022-04-28 13:54:14 +03:00
|
|
|
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
|
2019-01-13 19:43:46 +02:00
|
|
|
|
|
|
|
PathSet context2;
|
|
|
|
for (auto & p : context)
|
2022-02-25 17:00:00 +02:00
|
|
|
context2.insert(p.at(0) == '=' ? std::string(p, 1) : p);
|
2019-01-13 19:43:46 +02:00
|
|
|
|
2022-01-21 17:20:54 +02:00
|
|
|
v.mkString(*s, context2);
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
2019-01-13 19:43:46 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* Extract the context of a string as a structured Nix value.
|
|
|
|
|
|
|
|
The context is represented as an attribute set whose keys are the
|
|
|
|
paths in the context set and whose values are attribute sets with
|
|
|
|
the following keys:
|
|
|
|
path: True if the relevant path is in the context as a plain store
|
|
|
|
path (i.e. the kind of context you get when interpolating
|
|
|
|
a Nix path (e.g. ./.) into a string). False if missing.
|
|
|
|
allOutputs: True if the relevant path is a derivation and it is
|
|
|
|
in the context as a drv file with all of its outputs
|
|
|
|
(i.e. the kind of context you get when referencing
|
|
|
|
.drvPath of some derivation). False if missing.
|
|
|
|
outputs: If a non-empty list, the relevant path is a derivation
|
|
|
|
and the provided outputs are referenced in the context
|
|
|
|
(i.e. the kind of context you get when referencing
|
|
|
|
.outPath of some derivation). Empty list if missing.
|
|
|
|
Note that for a given path any combination of the above attributes
|
2019-01-14 18:34:54 +02:00
|
|
|
may be present.
|
2019-01-13 19:43:46 +02:00
|
|
|
*/
|
2022-03-04 20:31:59 +02:00
|
|
|
static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
2019-01-13 19:43:46 +02:00
|
|
|
{
|
|
|
|
struct ContextInfo {
|
|
|
|
bool path = false;
|
|
|
|
bool allOutputs = false;
|
|
|
|
Strings outputs;
|
|
|
|
};
|
|
|
|
PathSet context;
|
2022-04-28 13:54:14 +03:00
|
|
|
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
|
2019-01-13 19:43:46 +02:00
|
|
|
auto contextInfos = std::map<Path, ContextInfo>();
|
|
|
|
for (const auto & p : context) {
|
|
|
|
Path drv;
|
2022-02-25 17:00:00 +02:00
|
|
|
std::string output;
|
2019-01-13 19:43:46 +02:00
|
|
|
const Path * path = &p;
|
|
|
|
if (p.at(0) == '=') {
|
2022-02-25 17:00:00 +02:00
|
|
|
drv = std::string(p, 1);
|
2019-01-13 19:43:46 +02:00
|
|
|
path = &drv;
|
|
|
|
} else if (p.at(0) == '!') {
|
2022-03-12 02:28:00 +02:00
|
|
|
NixStringContextElem ctx = decodeContext(*state.store, p);
|
|
|
|
drv = state.store->printStorePath(ctx.first);
|
2019-01-13 19:43:46 +02:00
|
|
|
output = ctx.second;
|
|
|
|
path = &drv;
|
|
|
|
}
|
|
|
|
auto isPath = drv.empty();
|
|
|
|
auto isAllOutputs = (!drv.empty()) && output.empty();
|
|
|
|
|
|
|
|
auto iter = contextInfos.find(*path);
|
|
|
|
if (iter == contextInfos.end()) {
|
|
|
|
contextInfos.emplace(*path, ContextInfo{isPath, isAllOutputs, output.empty() ? Strings{} : Strings{std::move(output)}});
|
|
|
|
} else {
|
|
|
|
if (isPath)
|
|
|
|
iter->second.path = true;
|
|
|
|
else if (isAllOutputs)
|
|
|
|
iter->second.allOutputs = true;
|
|
|
|
else
|
|
|
|
iter->second.outputs.emplace_back(std::move(output));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 18:39:16 +02:00
|
|
|
auto attrs = state.buildBindings(contextInfos.size());
|
2019-01-13 19:43:46 +02:00
|
|
|
|
|
|
|
auto sPath = state.symbols.create("path");
|
|
|
|
auto sAllOutputs = state.symbols.create("allOutputs");
|
|
|
|
for (const auto & info : contextInfos) {
|
2022-01-04 18:39:16 +02:00
|
|
|
auto infoAttrs = state.buildBindings(3);
|
2019-01-13 19:43:46 +02:00
|
|
|
if (info.second.path)
|
2022-01-04 19:40:39 +02:00
|
|
|
infoAttrs.alloc(sPath).mkBool(true);
|
2019-01-13 19:43:46 +02:00
|
|
|
if (info.second.allOutputs)
|
2022-01-04 19:40:39 +02:00
|
|
|
infoAttrs.alloc(sAllOutputs).mkBool(true);
|
2019-01-13 19:43:46 +02:00
|
|
|
if (!info.second.outputs.empty()) {
|
2022-01-04 18:39:16 +02:00
|
|
|
auto & outputsVal = infoAttrs.alloc(state.sOutputs);
|
2019-01-13 19:43:46 +02:00
|
|
|
state.mkList(outputsVal, info.second.outputs.size());
|
2022-01-04 18:39:16 +02:00
|
|
|
for (const auto & [i, output] : enumerate(info.second.outputs))
|
|
|
|
(outputsVal.listElems()[i] = state.allocValue())->mkString(output);
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|
2022-01-04 18:39:16 +02:00
|
|
|
attrs.alloc(info.first).mkAttrs(infoAttrs);
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|
2022-01-04 18:39:16 +02:00
|
|
|
|
|
|
|
v.mkAttrs(attrs);
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
|
2019-01-13 19:43:46 +02:00
|
|
|
|
2019-01-14 18:34:54 +02:00
|
|
|
|
|
|
|
/* Append the given context to a given string.
|
|
|
|
|
|
|
|
See the commentary above unsafeGetContext for details of the
|
|
|
|
context representation.
|
|
|
|
*/
|
2022-03-04 20:31:59 +02:00
|
|
|
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
2019-01-14 18:34:54 +02:00
|
|
|
{
|
|
|
|
PathSet context;
|
2022-04-29 01:12:25 +03:00
|
|
|
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
|
2019-01-14 18:34:54 +02:00
|
|
|
|
2022-04-28 13:54:14 +03:00
|
|
|
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
|
2019-01-14 18:34:54 +02:00
|
|
|
|
|
|
|
auto sPath = state.symbols.create("path");
|
|
|
|
auto sAllOutputs = state.symbols.create("allOutputs");
|
|
|
|
for (auto & i : *args[1]->attrs) {
|
2022-03-05 15:40:24 +02:00
|
|
|
const auto & name = state.symbols[i.name];
|
|
|
|
if (!state.store->isStorePath(name))
|
2020-06-15 15:06:58 +03:00
|
|
|
throw EvalError({
|
2022-04-29 01:12:25 +03:00
|
|
|
.msg = hintfmt("context key '%s' is not a store path", name),
|
2022-03-04 20:31:59 +02:00
|
|
|
.errPos = state.positions[i.pos]
|
2020-06-15 15:06:58 +03:00
|
|
|
});
|
2019-01-14 18:34:54 +02:00
|
|
|
if (!settings.readOnlyMode)
|
2022-03-05 15:40:24 +02:00
|
|
|
state.store->ensurePath(state.store->parseStorePath(name));
|
2022-04-29 01:12:25 +03:00
|
|
|
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
|
2019-01-14 18:34:54 +02:00
|
|
|
auto iter = i.value->attrs->find(sPath);
|
|
|
|
if (iter != i.value->attrs->end()) {
|
2022-04-29 01:12:25 +03:00
|
|
|
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
|
2022-03-05 15:40:24 +02:00
|
|
|
context.emplace(name);
|
2019-01-14 18:34:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
iter = i.value->attrs->find(sAllOutputs);
|
|
|
|
if (iter != i.value->attrs->end()) {
|
2022-04-29 01:12:25 +03:00
|
|
|
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
2022-03-05 15:40:24 +02:00
|
|
|
if (!isDerivation(name)) {
|
2020-06-15 15:06:58 +03:00
|
|
|
throw EvalError({
|
2022-04-29 01:12:25 +03:00
|
|
|
.msg = hintfmt("tried to add all-outputs context of %s, which is not a derivation, to a string", name),
|
2022-03-04 20:31:59 +02:00
|
|
|
.errPos = state.positions[i.pos]
|
2020-06-15 15:06:58 +03:00
|
|
|
});
|
2019-01-14 18:34:54 +02:00
|
|
|
}
|
2022-03-05 15:40:24 +02:00
|
|
|
context.insert(concatStrings("=", name));
|
2019-01-14 18:34:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
iter = i.value->attrs->find(state.sOutputs);
|
|
|
|
if (iter != i.value->attrs->end()) {
|
2022-04-29 01:12:25 +03:00
|
|
|
state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context");
|
2022-03-05 15:40:24 +02:00
|
|
|
if (iter->value->listSize() && !isDerivation(name)) {
|
2020-06-15 15:06:58 +03:00
|
|
|
throw EvalError({
|
2022-04-29 01:12:25 +03:00
|
|
|
.msg = hintfmt("tried to add derivation output context of %s, which is not a derivation, to a string", name),
|
2022-03-04 20:31:59 +02:00
|
|
|
.errPos = state.positions[i.pos]
|
2020-06-15 15:06:58 +03:00
|
|
|
});
|
2019-01-14 18:34:54 +02:00
|
|
|
}
|
2021-11-24 21:21:34 +02:00
|
|
|
for (auto elem : iter->value->listItems()) {
|
2022-04-29 01:12:25 +03:00
|
|
|
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
|
2022-03-05 15:40:24 +02:00
|
|
|
context.insert(concatStrings("!", outputName, "!", name));
|
2019-01-14 18:34:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 19:24:42 +02:00
|
|
|
v.mkString(orig, context);
|
2019-01-14 18:34:54 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext);
|
2019-01-14 18:34:54 +02:00
|
|
|
|
2019-01-13 19:43:46 +02:00
|
|
|
}
|