mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 08:24: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);
|
run_patch(ExpectSuccess::Yes, {}, patch, "patching file 1\n"sv);
|
||||||
EXPECT_FILE_EQ(ByteString::formatted("{}/1", s_test_dir), "a\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;
|
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.
|
size_t line_number = 0; // NOTE: relative to 'old' file.
|
||||||
ssize_t offset_error = 0;
|
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)));
|
TRY(out.write_formatted("{}\n", lines.at(line_number)));
|
||||||
|
|
||||||
// Then output the hunk to what we hope is the correct location in the file.
|
// 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.
|
// 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
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -11,6 +11,6 @@
|
||||||
|
|
||||||
namespace Diff {
|
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 {};
|
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));
|
ByteBuffer content = TRY(read_content(path_of_file_to_patch, patch));
|
||||||
auto lines = StringView(content).lines();
|
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));
|
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) };
|
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.
|
// 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
|
// 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)
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
StringView directory;
|
StringView directory;
|
||||||
|
Optional<StringView> define;
|
||||||
Optional<size_t> strip_count;
|
Optional<size_t> strip_count;
|
||||||
|
|
||||||
Core::ArgsParser args_parser;
|
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(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(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);
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
if (!directory.is_null())
|
if (!directory.is_null())
|
||||||
|
@ -100,7 +102,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
outln("patching file {}", to_patch);
|
outln("patching file {}", to_patch);
|
||||||
TRY(do_patch(to_patch, patch));
|
TRY(do_patch(to_patch, patch, define));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue