mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-11 00:36:20 +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
|
@ -105,6 +105,7 @@ 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);
|
||||||
|
|
||||||
|
@ -130,7 +131,22 @@ 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 (attr.name == "patches") {
|
||||||
|
state.forceList(*attr.value, *attr.pos);
|
||||||
|
|
||||||
|
for (auto elem : attr.value->listItems()) {
|
||||||
|
// FIXME: use realisePath
|
||||||
|
PathSet context;
|
||||||
|
auto patchFile = state.unpackPath(state.coerceToPath(pos, *elem, context));
|
||||||
|
patches.push_back(patchFile.accessor->readFile(patchFile.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
||||||
attrs.emplace(attr.name,
|
attrs.emplace(attr.name,
|
||||||
|
@ -178,6 +194,9 @@ static void fetchTree(
|
||||||
|
|
||||||
auto [accessor, input2] = input.lazyFetch(state.store);
|
auto [accessor, input2] = input.lazyFetch(state.store);
|
||||||
|
|
||||||
|
if (!patches.empty())
|
||||||
|
accessor = makePatchingInputAccessor(accessor, patches);
|
||||||
|
|
||||||
//state.allowPath(tree.storePath);
|
//state.allowPath(tree.storePath);
|
||||||
|
|
||||||
emitTreeAttrs(
|
emitTreeAttrs(
|
||||||
|
|
|
@ -57,6 +57,10 @@ ref<MemoryInputAccessor> makeMemoryInputAccessor();
|
||||||
|
|
||||||
ref<InputAccessor> makeZipInputAccessor(PathView path);
|
ref<InputAccessor> makeZipInputAccessor(PathView path);
|
||||||
|
|
||||||
|
ref<InputAccessor> makePatchingInputAccessor(
|
||||||
|
ref<InputAccessor> next,
|
||||||
|
const std::vector<std::string> & patches);
|
||||||
|
|
||||||
struct SourcePath
|
struct SourcePath
|
||||||
{
|
{
|
||||||
ref<InputAccessor> accessor;
|
ref<InputAccessor> accessor;
|
||||||
|
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -311,6 +311,42 @@ namespace nix {
|
||||||
ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error);
|
ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* getLine
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(getLine, all) {
|
||||||
|
{
|
||||||
|
auto [line, rest] = getLine("foo\nbar\nxyzzy");
|
||||||
|
ASSERT_EQ(line, "foo");
|
||||||
|
ASSERT_EQ(rest, "bar\nxyzzy");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto [line, rest] = getLine("foo\r\nbar\r\nxyzzy");
|
||||||
|
ASSERT_EQ(line, "foo");
|
||||||
|
ASSERT_EQ(rest, "bar\r\nxyzzy");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto [line, rest] = getLine("foo\n");
|
||||||
|
ASSERT_EQ(line, "foo");
|
||||||
|
ASSERT_EQ(rest, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto [line, rest] = getLine("foo");
|
||||||
|
ASSERT_EQ(line, "foo");
|
||||||
|
ASSERT_EQ(rest, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto [line, rest] = getLine("");
|
||||||
|
ASSERT_EQ(line, "");
|
||||||
|
ASSERT_EQ(rest, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* toLower
|
* toLower
|
||||||
* --------------------------------------------------------------------------*/
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -1564,6 +1564,21 @@ std::string stripIndentation(std::string_view s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::pair<std::string_view, std::string_view> getLine(std::string_view s)
|
||||||
|
{
|
||||||
|
auto newline = s.find('\n');
|
||||||
|
|
||||||
|
if (newline == s.npos) {
|
||||||
|
return {s, ""};
|
||||||
|
} else {
|
||||||
|
auto line = s.substr(0, newline);
|
||||||
|
if (!line.empty() && line[line.size() - 1] == '\r')
|
||||||
|
line = line.substr(0, line.size() - 1);
|
||||||
|
return {line, s.substr(newline + 1)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -537,6 +537,12 @@ std::string base64Decode(std::string_view s);
|
||||||
std::string stripIndentation(std::string_view s);
|
std::string stripIndentation(std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
|
/* Get the prefix of 's' up to and excluding the next line break (LF
|
||||||
|
optionally preceded by CR), and the remainder following the line
|
||||||
|
break. */
|
||||||
|
std::pair<std::string_view, std::string_view> getLine(std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
/* Get a value for the specified key from an associate container. */
|
/* Get a value for the specified key from an associate container. */
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
|
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
|
||||||
|
|
Loading…
Reference in a new issue