Add builtins.patch

This replaces the 'patches' argument to builtins.fetchTree with
something more generic. So instead of 'builtins.fetchTree { patches =
... }' you can do 'builtins.patch { src = builtins.fetchTree { ... };
patchFiles = ... }'.
This commit is contained in:
Eelco Dolstra 2022-06-10 13:10:22 +02:00
parent 2b30df7b46
commit 5f1340219b
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
3 changed files with 125 additions and 20 deletions

View file

@ -110,7 +110,6 @@ static void fetchTree(
) { ) {
fetchers::Input input; fetchers::Input input;
PathSet context; PathSet context;
std::vector<std::string> patches;
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
@ -137,19 +136,6 @@ static void fetchTree(
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
if (attr.name == state.sType) continue; if (attr.name == state.sType) continue;
if (state.symbols[attr.name] == "patches") {
state.forceList(*attr.value, attr.pos);
for (auto elem : attr.value->listItems()) {
// FIXME: use realisePath
PathSet context;
auto patchFile = state.coerceToPath(pos, *elem, context);
patches.push_back(patchFile.readFile());
}
continue;
}
state.forceValue(*attr.value, attr.pos); state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
@ -200,9 +186,6 @@ static void fetchTree(
if (params.returnPath) { if (params.returnPath) {
auto [accessor, input2] = input.lazyFetch(state.store); auto [accessor, input2] = input.lazyFetch(state.store);
if (!patches.empty())
accessor = makePatchingInputAccessor(accessor, patches);
emitTreeAttrs( emitTreeAttrs(
state, state,
{ state.registerAccessor(accessor), CanonPath::root }, { state.registerAccessor(accessor), CanonPath::root },
@ -211,8 +194,6 @@ static void fetchTree(
params.emptyRevFallback, params.emptyRevFallback,
false); false);
} else { } else {
assert(patches.empty());
auto [tree, input2] = input.fetch(state.store); auto [tree, input2] = input.fetch(state.store);
auto storePath = state.store->printStorePath(tree.storePath); auto storePath = state.store->printStorePath(tree.storePath);

View file

@ -0,0 +1,124 @@
#include "primops.hh"
namespace nix {
static void prim_patch(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::vector<std::string> patches;
std::optional<SourcePath> src;
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
std::string_view n(state.symbols[attr.name]);
auto check = [&]()
{
if (!patches.empty())
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'builtins.patch' does not support both 'patches' and 'patchFiles'"),
.errPos = state.positions[attr.pos]
}));
};
if (n == "src") {
PathSet context;
src.emplace(state.coerceToPath(pos, *attr.value, context));
}
else if (n == "patchFiles") {
check();
state.forceList(*attr.value, attr.pos);
for (auto elem : attr.value->listItems()) {
// FIXME: use realisePath
PathSet context;
auto patchFile = state.coerceToPath(attr.pos, *elem, context);
patches.push_back(patchFile.readFile());
}
}
else if (n == "patches") {
check();
state.forceList(*attr.value, attr.pos);
for (auto elem : attr.value->listItems())
patches.push_back(std::string(state.forceStringNoCtx(*elem, attr.pos)));
}
else
throw Error({
.msg = hintfmt("attribute '%s' isn't supported in call to 'builtins.patch'", n),
.errPos = state.positions[pos]
});
}
if (!src)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("attribute 'src' is missing in call to 'builtins.patch'"),
.errPos = state.positions[pos]
}));
if (!src->path.isRoot())
throw UnimplementedError("applying patches to a non-root path ('%s') is not yet supported", src->path);
auto accessor = makePatchingInputAccessor(ref(src->accessor.shared_from_this()), patches);
v.mkPath(SourcePath { state.registerAccessor(accessor), src->path });
}
static RegisterPrimOp primop_patch({
.name = "__patch",
.args = {"args"},
.doc = R"(
Apply patches to a source tree. This function has the following required argument:
- src\
The input source tree.
It also takes one of the following:
- patchFiles\
A list of patch files to be applied to `src`.
- patches\
A list of patches (i.e. strings) to be applied to `src`.
It returns a source tree that lazily and non-destructively
applies the specified patches to `src`.
Example:
```nix
let
tree = builtins.patch {
src = fetchTree {
type = "github";
owner = "NixOS";
repo = "patchelf";
rev = "be0cc30a59b2755844bcd48823f6fbc8d97b93a7";
};
patches = [
''
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 6882b28..28f511c 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -1844,6 +1844,8 @@ void showHelp(const std::string & progName)
int mainWrapped(int argc, char * * argv)
{
+ printf("Hello!");
+
if (argc <= 1) {
showHelp(argv[0]);
return 1;
''
];
};
in builtins.readFile (tree + "/src/patchelf.cc")
```
)",
.fun = prim_patch,
});
}

View file

@ -9,7 +9,7 @@ namespace nix {
MakeError(RestrictedPathError, Error); MakeError(RestrictedPathError, Error);
struct InputAccessor struct InputAccessor : public std::enable_shared_from_this<InputAccessor>
{ {
const size_t number; const size_t number;