1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-17 07:52:43 +01:00

Apply clang-format universally.

* 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.
This commit is contained in:
Graham Christensen 2025-07-18 12:47:27 -04:00
parent 41bf87ec70
commit e4f62e4608
587 changed files with 23258 additions and 23135 deletions

View file

@ -5,10 +5,11 @@
namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
v.mkString(*s);
}
@ -21,18 +22,17 @@ static RegisterPrimOp primop_unsafeDiscardStringContext({
.fun = prim_unsafeDiscardStringContext,
});
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
v.mkBool(!context.empty());
}
static RegisterPrimOp primop_hasContext({
.name = "__hasContext",
.args = {"s"},
.doc = R"(
static RegisterPrimOp primop_hasContext(
{.name = "__hasContext",
.args = {"s"},
.doc = R"(
Return `true` if string *s* has a non-empty context.
The context can be obtained with
[`getContext`](#builtins-getContext).
@ -50,21 +50,18 @@ static RegisterPrimOp primop_hasContext({
> else { ${name} = meta; }
> ```
)",
.fun = prim_hasContext
});
.fun = prim_hasContext});
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
NixStringContext context2;
for (auto && c : context) {
if (auto * ptr = std::get_if<NixStringContextElem::DrvDeep>(&c.raw)) {
context2.emplace(NixStringContextElem::Opaque {
.path = ptr->drvPath
});
context2.emplace(NixStringContextElem::Opaque{.path = ptr->drvPath});
} else {
/* Can reuse original item */
context2.emplace(std::move(c).raw);
@ -74,10 +71,10 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
v.mkString(*s, context2);
}
static RegisterPrimOp primop_unsafeDiscardOutputDependency({
.name = "__unsafeDiscardOutputDependency",
.args = {"s"},
.doc = R"(
static RegisterPrimOp primop_unsafeDiscardOutputDependency(
{.name = "__unsafeDiscardOutputDependency",
.args = {"s"},
.doc = R"(
Create a copy of the given string where every
[derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep)
string context element is turned into a
@ -94,58 +91,58 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency({
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
)",
.fun = prim_unsafeDiscardOutputDependency
});
.fun = prim_unsafeDiscardOutputDependency});
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies");
auto s = state.coerceToString(
pos, *args[0], context, "while evaluating the argument passed to builtins.addDrvOutputDependencies");
auto contextSize = context.size();
auto contextSize = context.size();
if (contextSize != 1) {
state.error<EvalError>(
"context of string '%s' must have exactly one element, but has %d",
*s,
contextSize
).atPos(pos).debugThrow();
state.error<EvalError>("context of string '%s' must have exactly one element, but has %d", *s, contextSize)
.atPos(pos)
.debugThrow();
}
NixStringContext context2 {
(NixStringContextElem { std::visit(overloaded {
[&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep {
if (!c.path.isDerivation()) {
state.error<EvalError>(
"path '%s' is not a derivation",
state.store->printStorePath(c.path)
).atPos(pos).debugThrow();
}
return NixStringContextElem::DrvDeep {
.drvPath = c.path,
};
NixStringContext context2{
(NixStringContextElem{std::visit(
overloaded{
[&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep {
if (!c.path.isDerivation()) {
state.error<EvalError>("path '%s' is not a derivation", state.store->printStorePath(c.path))
.atPos(pos)
.debugThrow();
}
return NixStringContextElem::DrvDeep{
.drvPath = c.path,
};
},
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep {
state
.error<EvalError>(
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
c.output)
.atPos(pos)
.debugThrow();
},
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
/* Reuse original item because we want this to be idempotent. */
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
above does not make much sense. */
return std::move(c);
},
},
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep {
state.error<EvalError>(
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
c.output
).atPos(pos).debugThrow();
},
[&](const NixStringContextElem::DrvDeep & c) -> NixStringContextElem::DrvDeep {
/* Reuse original item because we want this to be idempotent. */
/* FIXME: Suspicious move out of const. This is actually a copy, so the comment
above does not make much sense. */
return std::move(c);
},
}, context.begin()->raw) }),
context.begin()->raw)}),
};
v.mkString(*s, context2);
}
static RegisterPrimOp primop_addDrvOutputDependencies({
.name = "__addDrvOutputDependencies",
.args = {"s"},
.doc = R"(
static RegisterPrimOp primop_addDrvOutputDependencies(
{.name = "__addDrvOutputDependencies",
.args = {"s"},
.doc = R"(
Create a copy of the given string where a single
[constant](@docroot@/language/string-context.md#string-context-element-constant)
string context element is turned into a
@ -159,9 +156,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies({
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
)",
.fun = prim_addDrvOutputDependencies
});
.fun = prim_addDrvOutputDependencies});
/* Extract the context of a string as a structured Nix value.
@ -182,31 +177,31 @@ static RegisterPrimOp primop_addDrvOutputDependencies({
Note that for a given path any combination of the above attributes
may be present.
*/
static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_getContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
struct ContextInfo {
struct ContextInfo
{
bool path = false;
bool allOutputs = false;
Strings outputs;
};
NixStringContext context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
auto contextInfos = std::map<StorePath, ContextInfo>();
for (auto && i : context) {
std::visit(overloaded {
[&](NixStringContextElem::DrvDeep && d) {
contextInfos[std::move(d.drvPath)].allOutputs = true;
std::visit(
overloaded{
[&](NixStringContextElem::DrvDeep && d) { contextInfos[std::move(d.drvPath)].allOutputs = true; },
[&](NixStringContextElem::Built && b) {
// FIXME should eventually show string context as is, no
// resolving here.
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
},
[&](NixStringContextElem::Opaque && o) { contextInfos[std::move(o.path)].path = true; },
},
[&](NixStringContextElem::Built && b) {
// FIXME should eventually show string context as is, no
// resolving here.
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
},
[&](NixStringContextElem::Opaque && o) {
contextInfos[std::move(o.path)].path = true;
},
}, ((NixStringContextElem &&) i).raw);
((NixStringContextElem &&) i).raw);
}
auto attrs = state.buildBindings(contextInfos.size());
@ -231,10 +226,10 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
v.mkAttrs(attrs);
}
static RegisterPrimOp primop_getContext({
.name = "__getContext",
.args = {"s"},
.doc = R"(
static RegisterPrimOp primop_getContext(
{.name = "__getContext",
.args = {"s"},
.doc = R"(
Return the string context of *s*.
The string context tracks references to derivations within a string.
@ -253,19 +248,18 @@ static RegisterPrimOp primop_getContext({
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
```
)",
.fun = prim_getContext
});
.fun = prim_getContext});
/* Append the given context to a given string.
See the commentary above getContext for details of the
context representation.
*/
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
NixStringContext context;
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
auto orig = state.forceString(
*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
@ -274,10 +268,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
for (auto & i : *args[1]->attrs()) {
const auto & name = state.symbols[i.name];
if (!state.store->isStorePath(name))
state.error<EvalError>(
"context key '%s' is not a store path",
name
).atPos(i.pos).debugThrow();
state.error<EvalError>("context key '%s' is not a store path", name).atPos(i.pos).debugThrow();
auto namePath = state.store->parseStorePath(name);
if (!settings.readOnlyMode)
state.store->ensurePath(namePath);
@ -285,39 +276,46 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
if (auto attr = i.value->attrs()->get(sPath)) {
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context"))
context.emplace(NixStringContextElem::Opaque {
.path = namePath,
});
context.emplace(
NixStringContextElem::Opaque{
.path = namePath,
});
}
if (auto attr = i.value->attrs()->get(sAllOutputs)) {
if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
if (state.forceBool(
*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
if (!isDerivation(name)) {
state.error<EvalError>(
"tried to add all-outputs context of %s, which is not a derivation, to a string",
name
).atPos(i.pos).debugThrow();
state
.error<EvalError>(
"tried to add all-outputs context of %s, which is not a derivation, to a string", name)
.atPos(i.pos)
.debugThrow();
}
context.emplace(NixStringContextElem::DrvDeep {
.drvPath = namePath,
});
context.emplace(
NixStringContextElem::DrvDeep{
.drvPath = namePath,
});
}
}
if (auto attr = i.value->attrs()->get(state.sOutputs)) {
state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context");
if (attr->value->listSize() && !isDerivation(name)) {
state.error<EvalError>(
"tried to add derivation output context of %s, which is not a derivation, to a string",
name
).atPos(i.pos).debugThrow();
state
.error<EvalError>(
"tried to add derivation output context of %s, which is not a derivation, to a string", name)
.atPos(i.pos)
.debugThrow();
}
for (auto elem : attr->value->listView()) {
auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
context.emplace(NixStringContextElem::Built {
.drvPath = makeConstantStorePathRef(namePath),
.output = std::string { outputName },
});
auto outputName =
state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
context.emplace(
NixStringContextElem::Built{
.drvPath = makeConstantStorePathRef(namePath),
.output = std::string{outputName},
});
}
}
}
@ -325,10 +323,6 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
v.mkString(orig, context);
}
static RegisterPrimOp primop_appendContext({
.name = "__appendContext",
.arity = 2,
.fun = prim_appendContext
});
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});
}
} // namespace nix

View file

@ -15,29 +15,35 @@ namespace nix {
* @param toPathMaybe Path to write the rewritten path to. If empty, the error shows the actual path.
* @param v Return `Value`
*/
static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, const std::optional<StorePath> & toPathMaybe, Value &v) {
static void runFetchClosureWithRewrite(
EvalState & state,
const PosIdx pos,
Store & fromStore,
const StorePath & fromPath,
const std::optional<StorePath> & toPathMaybe,
Value & v)
{
// establish toPath or throw
if (!toPathMaybe || !state.store->isValidPath(*toPathMaybe)) {
auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath);
if (toPathMaybe && *toPathMaybe != rewrittenPath)
throw Error({
.msg = HintFmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath),
state.store->printStorePath(*toPathMaybe)),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt(
"rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath),
state.store->printStorePath(*toPathMaybe)),
.pos = state.positions[pos]});
if (!toPathMaybe)
throw Error({
.msg = HintFmt(
"rewriting '%s' to content-addressed form yielded '%s'\n"
"Use this value for the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath)),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt(
"rewriting '%s' to content-addressed form yielded '%s'\n"
"Use this value for the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(fromPath),
state.store->printStorePath(rewrittenPath)),
.pos = state.positions[pos]});
}
const auto & toPath = *toPathMaybe;
@ -49,13 +55,12 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
if (!resultInfo->isContentAddressed(*state.store)) {
// We don't perform the rewriting when outPath already exists, as an optimisation.
// However, we can quickly detect a mistake if the toPath is input addressed.
throw Error({
.msg = HintFmt(
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
state.store->printStorePath(toPath)),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt(
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
"Set 'toPath' to an empty string to make Nix report the correct content-addressed path.",
state.store->printStorePath(toPath)),
.pos = state.positions[pos]});
}
state.mkStorePathString(toPath, v);
@ -64,24 +69,25 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
/**
* Fetch the closure and make sure it's content addressed.
*/
static void runFetchClosureWithContentAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
static void runFetchClosureWithContentAddressedPath(
EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v)
{
if (!state.store->isValidPath(fromPath))
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
copyClosure(fromStore, *state.store, RealisedPath::Set{fromPath});
auto info = state.store->queryPathInfo(fromPath);
if (!info->isContentAddressed(*state.store)) {
throw Error({
.msg = HintFmt(
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
"If you do intend to fetch an input-addressed store path, add\n\n"
" inputAddressed = true;\n\n"
"to the 'fetchClosure' arguments.\n\n"
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
state.store->printStorePath(fromPath)),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt(
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
"If you do intend to fetch an input-addressed store path, add\n\n"
" inputAddressed = true;\n\n"
"to the 'fetchClosure' arguments.\n\n"
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
state.store->printStorePath(fromPath)),
.pos = state.positions[pos]});
}
state.mkStorePathString(fromPath, v);
@ -90,21 +96,22 @@ static void runFetchClosureWithContentAddressedPath(EvalState & state, const Pos
/**
* Fetch the closure and make sure it's input addressed.
*/
static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
static void runFetchClosureWithInputAddressedPath(
EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v)
{
if (!state.store->isValidPath(fromPath))
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
copyClosure(fromStore, *state.store, RealisedPath::Set{fromPath});
auto info = state.store->queryPathInfo(fromPath);
if (info->isContentAddressed(*state.store)) {
throw Error({
.msg = HintFmt(
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
state.store->printStorePath(fromPath)),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt(
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
state.store->printStorePath(fromPath)),
.pos = state.positions[pos]});
}
state.mkStorePathString(fromPath, v);
@ -112,7 +119,7 @@ static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosId
typedef std::optional<StorePath> StorePathOrGap;
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure");
@ -136,67 +143,58 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
state.forceValue(*attr.value, attr.pos);
bool isEmptyString = attr.value->type() == nString && attr.value->string_view() == "";
if (isEmptyString) {
toPath = StorePathOrGap {};
}
else {
toPath = StorePathOrGap{};
} else {
NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
}
}
else if (attrName == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos,
attrHint());
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos, attrHint());
else if (attrName == "inputAddressed")
inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint());
else
throw Error({
.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
.pos = state.positions[pos]});
}
if (!fromPath)
throw Error({
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
.pos = state.positions[pos]});
bool inputAddressed = inputAddressedMaybe.value_or(false);
if (inputAddressed) {
if (toPath)
throw Error({
.msg = HintFmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
"inputAddressed",
"toPath"),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt(
"attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
"inputAddressed",
"toPath"),
.pos = state.positions[pos]});
}
if (!fromStoreUrl)
throw Error({
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
.pos = state.positions[pos]});
auto parsedURL = parseURL(*fromStoreUrl);
if (parsedURL.scheme != "http" &&
parsedURL.scheme != "https" &&
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
throw Error({
.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"),
.pos = state.positions[pos]
});
if (parsedURL.scheme != "http" && parsedURL.scheme != "https"
&& !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
throw Error(
{.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"), .pos = state.positions[pos]});
if (!parsedURL.query.empty())
throw Error({
.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
.pos = state.positions[pos]
});
throw Error(
{.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
.pos = state.positions[pos]});
auto fromStore = openStore(parsedURL.to_string());
@ -284,4 +282,4 @@ static RegisterPrimOp primop_fetchClosure({
.experimentalFeature = Xp::FetchClosure,
});
}
} // namespace nix

View file

@ -8,7 +8,7 @@
namespace nix {
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
std::string url;
std::optional<Hash> rev;
@ -23,31 +23,46 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
for (auto & attr : *args[0]->attrs()) {
std::string_view n(state.symbols[attr.name]);
if (n == "url")
url = state.coerceToString(attr.pos, *attr.value, context,
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
false, false).toOwned();
url = state
.coerceToString(
attr.pos,
*attr.value,
context,
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
false,
false)
.toOwned();
else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
auto value = state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
else
ref = value;
}
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
} else if (n == "name")
name = state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
else
state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]).atPos(attr.pos).debugThrow();
state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name])
.atPos(attr.pos)
.debugThrow();
}
if (url.empty())
state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
} else
url = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.fetchMercurial",
false, false).toOwned();
url = state
.coerceToString(
pos,
*args[0],
context,
"while evaluating the first argument passed to builtins.fetchMercurial",
false,
false)
.toOwned();
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.
@ -60,8 +75,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
attrs.insert_or_assign("type", "hg");
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
attrs.insert_or_assign("name", std::string(name));
if (ref) attrs.insert_or_assign("ref", *ref);
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
if (ref)
attrs.insert_or_assign("ref", *ref);
if (rev)
attrs.insert_or_assign("rev", rev->gitRev());
auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
auto [storePath, input2] = input.fetchToStore(state.store);
@ -82,10 +99,6 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
state.allowPath(storePath);
}
static RegisterPrimOp r_fetchMercurial({
.name = "fetchMercurial",
.arity = 1,
.fun = prim_fetchMercurial
});
static RegisterPrimOp r_fetchMercurial({.name = "fetchMercurial", .arity = 1, .fun = prim_fetchMercurial});
}
} // namespace nix

View file

@ -37,8 +37,7 @@ void emitTreeAttrs(
attrs.alloc("narHash").mkString(narHash->to_string(HashFormat::SRI, true));
if (input.getType() == "git")
attrs.alloc("submodules").mkBool(
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
attrs.alloc("submodules").mkBool(fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
if (!forceDirty) {
@ -56,7 +55,6 @@ void emitTreeAttrs(
attrs.alloc("revCount").mkInt(*revCount);
else if (emptyRevFallback)
attrs.alloc("revCount").mkInt(0);
}
if (auto dirtyRev = fetchers::maybeGetStrAttr(input.attrs, "dirtyRev")) {
@ -66,14 +64,14 @@ void emitTreeAttrs(
if (auto lastModified = input.getLastModified()) {
attrs.alloc("lastModified").mkInt(*lastModified);
attrs.alloc("lastModifiedDate").mkString(
fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
attrs.alloc("lastModifiedDate").mkString(fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
}
v.mkAttrs(attrs);
}
struct FetchTreeParams {
struct FetchTreeParams
{
bool emptyRevFallback = false;
bool allowNameArgument = false;
bool isFetchGit = false;
@ -81,17 +79,14 @@ struct FetchTreeParams {
};
static void fetchTree(
EvalState & state,
const PosIdx pos,
Value * * args,
Value & v,
const FetchTreeParams & params = FetchTreeParams{}
) {
fetchers::Input input { state.fetchSettings };
EvalState & state, const PosIdx pos, Value ** args, Value & v, const FetchTreeParams & params = FetchTreeParams{})
{
fetchers::Input input{state.fetchSettings};
NixStringContext context;
std::optional<std::string> type;
auto fetcher = params.isFetchGit ? "fetchGit" : "fetchTree";
if (params.isFetchGit) type = "git";
if (params.isFetchGit)
type = "git";
state.forceValue(*args[0], pos);
@ -102,47 +97,55 @@ static void fetchTree(
if (auto aType = args[0]->attrs()->get(state.sType)) {
if (type)
state.error<EvalError>(
"unexpected argument 'type'"
).atPos(pos).debugThrow();
type = state.forceStringNoCtx(*aType->value, aType->pos,
fmt("while evaluating the `type` argument passed to '%s'", fetcher));
state.error<EvalError>("unexpected argument 'type'").atPos(pos).debugThrow();
type = state.forceStringNoCtx(
*aType->value, aType->pos, fmt("while evaluating the `type` argument passed to '%s'", fetcher));
} else if (!type)
state.error<EvalError>(
"argument 'type' is missing in call to '%s'", fetcher
).atPos(pos).debugThrow();
state.error<EvalError>("argument 'type' is missing in call to '%s'", fetcher).atPos(pos).debugThrow();
attrs.emplace("type", type.value());
for (auto & attr : *args[0]->attrs()) {
if (attr.name == state.sType) continue;
if (attr.name == state.sType)
continue;
state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
attrs.emplace(state.symbols[attr.name],
params.isFetchGit && state.symbols[attr.name] == "url"
? fixGitURL(s)
: s);
}
else if (attr.value->type() == nBool)
attrs.emplace(
state.symbols[attr.name],
params.isFetchGit && state.symbols[attr.name] == "url" ? fixGitURL(s) : s);
} else if (attr.value->type() == nBool)
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
else if (attr.value->type() == nInt) {
auto intValue = attr.value->integer().value;
if (intValue < 0)
state.error<EvalError>("negative value given for '%s' argument '%s': %d", fetcher, state.symbols[attr.name], intValue).atPos(pos).debugThrow();
state
.error<EvalError>(
"negative value given for '%s' argument '%s': %d",
fetcher,
state.symbols[attr.name],
intValue)
.atPos(pos)
.debugThrow();
attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
} else if (state.symbols[attr.name] == "publicKeys") {
experimentalFeatureSettings.require(Xp::VerifiedFetches);
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
}
else
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
state.symbols[attr.name], fetcher, showType(*attr.value)).debugThrow();
attrs.emplace(
state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
} else
state
.error<TypeError>(
"argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
state.symbols[attr.name],
fetcher,
showType(*attr.value))
.debugThrow();
}
if (params.isFetchGit && !attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
if (params.isFetchGit && !attrs.contains("exportIgnore")
&& (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
attrs.emplace("exportIgnore", Explicit<bool>{true});
}
@ -153,29 +156,38 @@ static void fetchTree(
if (!params.allowNameArgument)
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
state.error<EvalError>(
"argument 'name' isnt supported in call to '%s'", fetcher
).atPos(pos).debugThrow();
state.error<EvalError>("argument 'name' isnt supported in call to '%s'", fetcher)
.atPos(pos)
.debugThrow();
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else {
auto url = state.coerceToString(pos, *args[0], context,
fmt("while evaluating the first argument passed to '%s'", fetcher),
false, false).toOwned();
auto url = state
.coerceToString(
pos,
*args[0],
context,
fmt("while evaluating the first argument passed to '%s'", fetcher),
false,
false)
.toOwned();
if (params.isFetchGit) {
fetchers::Attrs attrs;
attrs.emplace("type", "git");
attrs.emplace("url", fixGitURL(url));
if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
if (!attrs.contains("exportIgnore")
&& (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) {
attrs.emplace("exportIgnore", Explicit<bool>{true});
}
input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs));
} else {
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
state.error<EvalError>(
"passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher
).atPos(pos).debugThrow();
state
.error<EvalError>(
"passing a string argument to '%s' requires the 'flakes' experimental feature", fetcher)
.atPos(pos)
.debugThrow();
input = fetchers::Input::fromURL(state.fetchSettings, url);
}
}
@ -190,9 +202,11 @@ static void fetchTree(
"This is deprecated since such inputs are verifiable but may not be reproducible.",
input.to_string());
else
state.error<EvalError>(
"in pure evaluation mode, '%s' doesn't fetch unlocked input '%s'",
fetcher, input.to_string()).atPos(pos).debugThrow();
state
.error<EvalError>(
"in pure evaluation mode, '%s' doesn't fetch unlocked input '%s'", fetcher, input.to_string())
.atPos(pos)
.debugThrow();
}
state.checkURI(input.toURLString());
@ -211,9 +225,9 @@ static void fetchTree(
emitTreeAttrs(state, storePath, input2, v, params.emptyRevFallback, false);
}
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
fetchTree(state, pos, args, v, { });
fetchTree(state, pos, args, v, {});
}
static RegisterPrimOp primop_fetchTree({
@ -446,7 +460,7 @@ static RegisterPrimOp primop_fetchTree({
.experimentalFeature = Xp::FetchTree,
});
void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
fetchTree(state, pos, args, v, {.isFinal = true});
}
@ -458,8 +472,14 @@ static RegisterPrimOp primop_fetchFinalTree({
.internal = true,
});
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
const std::string & who, bool unpack, std::string name)
static void fetch(
EvalState & state,
const PosIdx pos,
Value ** args,
Value & v,
const std::string & who,
bool unpack,
std::string name)
{
std::optional<std::string> url;
std::optional<Hash> expectedHash;
@ -476,19 +496,20 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
if (n == "url")
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), HashAlgorithm::SHA256);
expectedHash = newHashAllowEmpty(
state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"),
HashAlgorithm::SHA256);
else if (n == "name") {
nameAttrPassed = true;
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
}
else
state.error<EvalError>("unsupported argument '%s' to '%s'", n, who)
.atPos(pos).debugThrow();
name = state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
} else
state.error<EvalError>("unsupported argument '%s' to '%s'", n, who).atPos(pos).debugThrow();
}
if (!url)
state.error<EvalError>(
"'url' argument required").atPos(pos).debugThrow();
state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
} else
url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch");
@ -504,27 +525,41 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
checkName(name);
} catch (BadStorePathName & e) {
auto resolution =
nameAttrPassed ? HintFmt("Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.", who) :
isArgAttrs ? HintFmt("Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.", who) :
HintFmt("Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.", who);
nameAttrPassed
? HintFmt(
"Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.",
who)
: isArgAttrs
? HintFmt(
"Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.",
who)
: HintFmt(
"Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.",
who);
state.error<EvalError>(
std::string("invalid store path name when fetching URL '%s': %s. %s"), *url, Uncolored(e.message()), Uncolored(resolution.str()))
.atPos(pos).debugThrow();
state
.error<EvalError>(
std::string("invalid store path name when fetching URL '%s': %s. %s"),
*url,
Uncolored(e.message()),
Uncolored(resolution.str()))
.atPos(pos)
.debugThrow();
}
if (state.settings.pureEval && !expectedHash)
state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow();
state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who)
.atPos(pos)
.debugThrow();
// early exit if pinned and already in the store
if (expectedHash && expectedHash->algo == HashAlgorithm::SHA256) {
auto expectedPath = state.store->makeFixedOutputPath(
name,
FixedOutputInfo {
FixedOutputInfo{
.method = unpack ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat,
.hash = *expectedHash,
.references = {}
});
.references = {}});
if (state.store->isValidPath(expectedPath)) {
state.allowAndSetStorePathString(expectedPath, v);
@ -534,35 +569,33 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
// TODO: fetching may fail, yet the path may be substitutable.
// https://github.com/NixOS/nix/issues/4313
auto storePath =
unpack
? fetchToStore(
state.fetchSettings,
*state.store,
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
FetchMode::Copy,
name)
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath;
auto storePath = unpack ? fetchToStore(
state.fetchSettings,
*state.store,
fetchers::downloadTarball(state.store, state.fetchSettings, *url),
FetchMode::Copy,
name)
: fetchers::downloadFile(state.store, state.fetchSettings, *url, name).storePath;
if (expectedHash) {
auto hash = unpack
? state.store->queryPathInfo(storePath)->narHash
: hashFile(HashAlgorithm::SHA256, state.store->toRealPath(storePath));
auto hash = unpack ? state.store->queryPathInfo(storePath)->narHash
: hashFile(HashAlgorithm::SHA256, state.store->toRealPath(storePath));
if (hash != *expectedHash) {
state.error<EvalError>(
"hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
*url,
expectedHash->to_string(HashFormat::Nix32, true),
hash.to_string(HashFormat::Nix32, true)
).withExitStatus(102)
.debugThrow();
state
.error<EvalError>(
"hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
*url,
expectedHash->to_string(HashFormat::Nix32, true),
hash.to_string(HashFormat::Nix32, true))
.withExitStatus(102)
.debugThrow();
}
}
state.allowAndSetStorePathString(storePath, v);
}
static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_fetchurl(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
fetch(state, pos, args, v, "fetchurl", false, "");
}
@ -588,7 +621,7 @@ static RegisterPrimOp primop_fetchurl({
.fun = prim_fetchurl,
});
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
fetch(state, pos, args, v, "fetchTarball", true, "source");
}
@ -638,14 +671,10 @@ static RegisterPrimOp primop_fetchTarball({
.fun = prim_fetchTarball,
});
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v)
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
fetchTree(state, pos, args, v,
FetchTreeParams {
.emptyRevFallback = true,
.allowNameArgument = true,
.isFetchGit = true
});
fetchTree(
state, pos, args, v, FetchTreeParams{.emptyRevFallback = true, .allowNameArgument = true, .isFetchGit = true});
}
static RegisterPrimOp primop_fetchGit({
@ -858,4 +887,4 @@ static RegisterPrimOp primop_fetchGit({
.fun = prim_fetchGit,
});
}
} // namespace nix

View file

@ -7,7 +7,7 @@
namespace nix {
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Value & val)
{
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
@ -16,75 +16,75 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
std::function<void(Value &, toml::value)> visit;
visit = [&](Value & v, toml::value t) {
switch (t.type()) {
case toml::value_t::table: {
auto table = toml::get<toml::table>(t);
switch(t.type())
{
case toml::value_t::table:
{
auto table = toml::get<toml::table>(t);
size_t size = 0;
for (auto & i : table) {
(void) i;
size++;
}
size_t size = 0;
for (auto & i : table) { (void) i; size++; }
auto attrs = state.buildBindings(size);
auto attrs = state.buildBindings(size);
for (auto & elem : table) {
forceNoNullByte(elem.first);
visit(attrs.alloc(elem.first), elem.second);
}
for(auto & elem : table) {
forceNoNullByte(elem.first);
visit(attrs.alloc(elem.first), elem.second);
}
v.mkAttrs(attrs);
}
break;;
case toml::value_t::array:
{
auto array = toml::get<std::vector<toml::value>>(t);
auto list = state.buildList(array.size());
for (const auto & [n, v] : enumerate(list))
visit(*(v = state.allocValue()), array[n]);
v.mkList(list);
}
break;;
case toml::value_t::boolean:
v.mkBool(toml::get<bool>(t));
break;;
case toml::value_t::integer:
v.mkInt(toml::get<int64_t>(t));
break;;
case toml::value_t::floating:
v.mkFloat(toml::get<NixFloat>(t));
break;;
case toml::value_t::string:
{
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s);
}
break;;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
case toml::value_t::local_date:
case toml::value_t::local_time:
{
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
auto str = toView(s);
forceNoNullByte(str);
attrs.alloc("value").mkString(str);
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");
}
}
break;;
case toml::value_t::empty:
v.mkNull();
break;;
v.mkAttrs(attrs);
} break;
;
case toml::value_t::array: {
auto array = toml::get<std::vector<toml::value>>(t);
auto list = state.buildList(array.size());
for (const auto & [n, v] : enumerate(list))
visit(*(v = state.allocValue()), array[n]);
v.mkList(list);
} break;
;
case toml::value_t::boolean:
v.mkBool(toml::get<bool>(t));
break;
;
case toml::value_t::integer:
v.mkInt(toml::get<int64_t>(t));
break;
;
case toml::value_t::floating:
v.mkFloat(toml::get<NixFloat>(t));
break;
;
case toml::value_t::string: {
auto s = toml::get<std::string_view>(t);
forceNoNullByte(s);
v.mkString(s);
} break;
;
case toml::value_t::local_datetime:
case toml::value_t::offset_datetime:
case toml::value_t::local_date:
case toml::value_t::local_time: {
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
auto attrs = state.buildBindings(2);
attrs.alloc("_type").mkString("timestamp");
std::ostringstream s;
s << t;
auto str = toView(s);
forceNoNullByte(str);
attrs.alloc("value").mkString(str);
v.mkAttrs(attrs);
} else {
throw std::runtime_error("Dates and times are not supported");
}
} break;
;
case toml::value_t::empty:
v.mkNull();
break;
;
}
};
@ -95,10 +95,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
}
}
static RegisterPrimOp primop_fromTOML({
.name = "fromTOML",
.args = {"e"},
.doc = R"(
static RegisterPrimOp primop_fromTOML(
{.name = "fromTOML",
.args = {"e"},
.doc = R"(
Convert a TOML string to a Nix value. For example,
```nix
@ -112,7 +112,6 @@ static RegisterPrimOp primop_fromTOML({
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
)",
.fun = prim_fromTOML
});
.fun = prim_fromTOML});
}
} // namespace nix