mirror of
https://github.com/NixOS/nix
synced 2025-06-30 15:48:00 +02:00
fetchTree: Support applying patches
You can now write fetchTree { type = "github"; owner = "NixOS"; repo = "nixpkgs"; rev = "0f316e4d72daed659233817ffe52bf08e081b5de"; patches = [ ./thunderbird-1.patch ./thunderbird-2.patch ]; }; to apply a list of patches to a tree. These are applied lazily - the patched tree is not materialized unless you do something that causes the entire tree to be copied to the store (like 'src = fetchTree { ... }'). The equivalent of '-p1' is implied. File additions/deletions/renames are not yet handled. Issue #3920.
This commit is contained in:
parent
9075644631
commit
4b313ceb9e
6 changed files with 195 additions and 0 deletions
115
src/libfetchers/patching-input-accessor.cc
Normal file
115
src/libfetchers/patching-input-accessor.cc
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include "input-accessor.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
// TODO: handle file creation / deletion.
|
||||
struct PatchingInputAccessor : InputAccessor
|
||||
{
|
||||
ref<InputAccessor> next;
|
||||
|
||||
std::map<Path, std::vector<std::string>> patchesPerFile;
|
||||
|
||||
PatchingInputAccessor(
|
||||
ref<InputAccessor> next,
|
||||
const std::vector<std::string> & patches)
|
||||
: next(next)
|
||||
{
|
||||
/* Extract the patches for each file. */
|
||||
for (auto & patch : patches) {
|
||||
std::string_view p = patch;
|
||||
std::string_view start;
|
||||
std::string_view fileName;
|
||||
|
||||
auto flush = [&]()
|
||||
{
|
||||
if (start.empty()) return;
|
||||
auto contents = start.substr(0, p.data() - start.data());
|
||||
start = "";
|
||||
auto slash = fileName.find('/');
|
||||
if (slash == fileName.npos) return;
|
||||
fileName = fileName.substr(slash);
|
||||
debug("found patch for '%s'", fileName);
|
||||
patchesPerFile.emplace(Path(fileName), std::vector<std::string>())
|
||||
.first->second.push_back(std::string(contents));
|
||||
};
|
||||
|
||||
while (!p.empty()) {
|
||||
auto [line, rest] = getLine(p);
|
||||
|
||||
if (hasPrefix(line, "--- ")) {
|
||||
flush();
|
||||
start = p;
|
||||
fileName = line.substr(4);
|
||||
}
|
||||
|
||||
if (!start.empty()) {
|
||||
if (!(hasPrefix(line, "+++ ")
|
||||
|| hasPrefix(line, "@@")
|
||||
|| hasPrefix(line, "+")
|
||||
|| hasPrefix(line, "-")
|
||||
|| hasPrefix(line, " ")))
|
||||
{
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
p = rest;
|
||||
}
|
||||
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
std::string readFile(PathView path) override
|
||||
{
|
||||
auto contents = next->readFile(path);
|
||||
|
||||
auto i = patchesPerFile.find((Path) path);
|
||||
if (i != patchesPerFile.end()) {
|
||||
for (auto & patch : i->second) {
|
||||
auto tempDir = createTempDir();
|
||||
AutoDelete del(tempDir);
|
||||
auto sourceFile = tempDir + "/source";
|
||||
auto rejFile = tempDir + "/source.rej";
|
||||
writeFile(sourceFile, contents);
|
||||
try {
|
||||
contents = runProgram("patch", true, {"--quiet", sourceFile, "--output=-", "-r", rejFile}, patch);
|
||||
} catch (ExecError & e) {
|
||||
del.cancel();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
bool pathExists(PathView path) override
|
||||
{
|
||||
return next->pathExists(path);
|
||||
}
|
||||
|
||||
Stat lstat(PathView path) override
|
||||
{
|
||||
return next->lstat(path);
|
||||
}
|
||||
|
||||
DirEntries readDirectory(PathView path) override
|
||||
{
|
||||
return next->readDirectory(path);
|
||||
}
|
||||
|
||||
std::string readLink(PathView path) override
|
||||
{
|
||||
return next->readLink(path);
|
||||
}
|
||||
};
|
||||
|
||||
ref<InputAccessor> makePatchingInputAccessor(
|
||||
ref<InputAccessor> next,
|
||||
const std::vector<std::string> & patches)
|
||||
{
|
||||
return make_ref<PatchingInputAccessor>(next, std::move(patches));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue