mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 08:44:58 +00:00
patch+LibDiff: Add support for applying patches with preprocessor macro
This commit is contained in:
parent
ee643b6417
commit
4bce61e508
4 changed files with 101 additions and 7 deletions
|
@ -276,3 +276,40 @@ TEST_CASE(patch_with_timestamp_separated_by_tab)
|
|||
run_patch(ExpectSuccess::Yes, {}, patch, "patching file 1\n"sv);
|
||||
EXPECT_FILE_EQ(ByteString::formatted("{}/1", s_test_dir), "a\n"sv);
|
||||
}
|
||||
|
||||
TEST_CASE(patch_defines_add_remove)
|
||||
{
|
||||
PatchSetup setup;
|
||||
|
||||
StringView patch = R"(
|
||||
--- file.cpp
|
||||
+++ file.cpp
|
||||
@@ -1,4 +1,4 @@
|
||||
int main()
|
||||
{
|
||||
- return 0;
|
||||
+ return 1;
|
||||
}
|
||||
)"sv;
|
||||
|
||||
auto file = R"(int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
)"sv;
|
||||
|
||||
auto input = MUST(Core::File::open(ByteString::formatted("{}/file.cpp", s_test_dir), Core::File::OpenMode::Write));
|
||||
MUST(input->write_until_depleted(file.bytes()));
|
||||
|
||||
run_patch(ExpectSuccess::Yes, { "--ifdef", "TEST_PATCH" }, patch);
|
||||
|
||||
EXPECT_FILE_EQ(ByteString::formatted("{}/file.cpp", s_test_dir), R"(int main()
|
||||
{
|
||||
#ifndef TEST_PATCH
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
|
|
@ -130,7 +130,59 @@ static ErrorOr<size_t> write_hunk(Stream& out, Hunk const& hunk, Location const&
|
|||
return line_number;
|
||||
}
|
||||
|
||||
ErrorOr<void> apply_patch(Stream& out, Vector<StringView> const& lines, Patch const& patch)
|
||||
static ErrorOr<size_t> write_define_hunk(Stream& out, Hunk const& hunk, Location const& location, Vector<StringView> const& lines, StringView define)
|
||||
{
|
||||
enum class State {
|
||||
Outside,
|
||||
InsideIFNDEF,
|
||||
InsideIFDEF,
|
||||
InsideELSE,
|
||||
};
|
||||
|
||||
auto state = State::Outside;
|
||||
|
||||
auto line_number = location.line_number;
|
||||
|
||||
for (auto const& patch_line : hunk.lines) {
|
||||
if (patch_line.operation == Diff::Line::Operation::Context) {
|
||||
auto const& line = lines.at(line_number);
|
||||
++line_number;
|
||||
if (state != State::Outside) {
|
||||
TRY(out.write_formatted("#endif\n"));
|
||||
state = State::Outside;
|
||||
}
|
||||
TRY(out.write_formatted("{}\n", line));
|
||||
} else if (patch_line.operation == Diff::Line::Operation::Addition) {
|
||||
if (state == State::Outside) {
|
||||
state = State::InsideIFDEF;
|
||||
TRY(out.write_formatted("#ifdef {}\n", define));
|
||||
} else if (state == State::InsideIFNDEF) {
|
||||
state = State::InsideELSE;
|
||||
TRY(out.write_formatted("#else\n"));
|
||||
}
|
||||
TRY(out.write_formatted("{}\n", patch_line.content));
|
||||
} else if (patch_line.operation == Diff::Line::Operation::Removal) {
|
||||
auto const& line = lines.at(line_number);
|
||||
++line_number;
|
||||
|
||||
if (state == State::Outside) {
|
||||
state = State::InsideIFNDEF;
|
||||
TRY(out.write_formatted("#ifndef {}\n", define));
|
||||
} else if (state == State::InsideIFDEF) {
|
||||
state = State::InsideELSE;
|
||||
TRY(out.write_formatted("#else\n"));
|
||||
}
|
||||
TRY(out.write_formatted("{}\n", line));
|
||||
}
|
||||
}
|
||||
|
||||
if (state != State::Outside)
|
||||
TRY(out.write_formatted("#endif\n"));
|
||||
|
||||
return line_number;
|
||||
}
|
||||
|
||||
ErrorOr<void> apply_patch(Stream& out, Vector<StringView> const& lines, Patch const& patch, Optional<StringView> const& define)
|
||||
{
|
||||
size_t line_number = 0; // NOTE: relative to 'old' file.
|
||||
ssize_t offset_error = 0;
|
||||
|
@ -150,7 +202,10 @@ ErrorOr<void> apply_patch(Stream& out, Vector<StringView> const& lines, Patch co
|
|||
TRY(out.write_formatted("{}\n", lines.at(line_number)));
|
||||
|
||||
// Then output the hunk to what we hope is the correct location in the file.
|
||||
line_number = TRY(write_hunk(out, hunk, location, lines));
|
||||
if (define.has_value())
|
||||
line_number = TRY(write_define_hunk(out, hunk, location, lines, define.value()));
|
||||
else
|
||||
line_number = TRY(write_hunk(out, hunk, location, lines));
|
||||
}
|
||||
|
||||
// We've finished applying all hunks, write out anything from the old file we haven't already.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -11,6 +11,6 @@
|
|||
|
||||
namespace Diff {
|
||||
|
||||
ErrorOr<void> apply_patch(Stream& out, Vector<StringView> const& lines, Patch const& patch);
|
||||
ErrorOr<void> apply_patch(Stream& out, Vector<StringView> const& lines, Patch const& patch, Optional<StringView> const& define = {});
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ static ErrorOr<ByteBuffer> read_content(StringView path_of_file_to_patch, Diff::
|
|||
return ByteBuffer {};
|
||||
}
|
||||
|
||||
static ErrorOr<void> do_patch(StringView path_of_file_to_patch, Diff::Patch const& patch)
|
||||
static ErrorOr<void> do_patch(StringView path_of_file_to_patch, Diff::Patch const& patch, Optional<StringView> const& define = {})
|
||||
{
|
||||
ByteBuffer content = TRY(read_content(path_of_file_to_patch, patch));
|
||||
auto lines = StringView(content).lines();
|
||||
|
@ -49,7 +49,7 @@ static ErrorOr<void> do_patch(StringView path_of_file_to_patch, Diff::Patch cons
|
|||
auto tmp_file = TRY(Core::File::adopt_fd(TRY(Core::System::mkstemp(tmp_output)), Core::File::OpenMode::ReadWrite));
|
||||
StringView tmp_path { tmp_output, sizeof(tmp_output) };
|
||||
|
||||
TRY(Diff::apply_patch(*tmp_file, lines, patch));
|
||||
TRY(Diff::apply_patch(*tmp_file, lines, patch, define));
|
||||
|
||||
// If the patched file ends up being empty, remove it, as the patch was a removal.
|
||||
// Note that we cannot simply rely on the patch successfully applying and the patch claiming it is removing the file
|
||||
|
@ -67,11 +67,13 @@ static ErrorOr<void> do_patch(StringView path_of_file_to_patch, Diff::Patch cons
|
|||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
StringView directory;
|
||||
Optional<StringView> define;
|
||||
Optional<size_t> strip_count;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(directory, "Change the working directory to <directory> before applying the patch file", "directory", 'd', "directory");
|
||||
args_parser.add_option(strip_count, "Strip given number of leading path components from file names (defaults as basename)", "strip", 'p', "count");
|
||||
args_parser.add_option(define, "Apply merged patch content separated by C preprocessor macros", "ifdef", 'D', "define");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (!directory.is_null())
|
||||
|
@ -100,7 +102,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
}
|
||||
|
||||
outln("patching file {}", to_patch);
|
||||
TRY(do_patch(to_patch, patch));
|
||||
TRY(do_patch(to_patch, patch, define));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue