From 5f1340219b83f15a4354aad94467ae642a1196ed Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 10 Jun 2022 13:10:22 +0200 Subject: [PATCH] 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 = ... }'. --- src/libexpr/primops/fetchTree.cc | 19 ----- src/libexpr/primops/patch.cc | 124 ++++++++++++++++++++++++++++++ src/libfetchers/input-accessor.hh | 2 +- 3 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 src/libexpr/primops/patch.cc diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index efdc19882..292525c09 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -110,7 +110,6 @@ static void fetchTree( ) { fetchers::Input input; PathSet context; - std::vector patches; state.forceValue(*args[0], pos); @@ -137,19 +136,6 @@ static void fetchTree( for (auto & attr : *args[0]->attrs) { 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); if (attr.value->type() == nPath || attr.value->type() == nString) { @@ -200,9 +186,6 @@ static void fetchTree( if (params.returnPath) { auto [accessor, input2] = input.lazyFetch(state.store); - if (!patches.empty()) - accessor = makePatchingInputAccessor(accessor, patches); - emitTreeAttrs( state, { state.registerAccessor(accessor), CanonPath::root }, @@ -211,8 +194,6 @@ static void fetchTree( params.emptyRevFallback, false); } else { - assert(patches.empty()); - auto [tree, input2] = input.fetch(state.store); auto storePath = state.store->printStorePath(tree.storePath); diff --git a/src/libexpr/primops/patch.cc b/src/libexpr/primops/patch.cc new file mode 100644 index 000000000..a4c0d451b --- /dev/null +++ b/src/libexpr/primops/patch.cc @@ -0,0 +1,124 @@ +#include "primops.hh" + +namespace nix { + +static void prim_patch(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + std::vector patches; + std::optional 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, +}); + +} diff --git a/src/libfetchers/input-accessor.hh b/src/libfetchers/input-accessor.hh index 8e88a6d2c..ceac4f356 100644 --- a/src/libfetchers/input-accessor.hh +++ b/src/libfetchers/input-accessor.hh @@ -9,7 +9,7 @@ namespace nix { MakeError(RestrictedPathError, Error); -struct InputAccessor +struct InputAccessor : public std::enable_shared_from_this { const size_t number;