]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
refactor: Move Util::send_to_fd to core
authorJoel Rosdahl <joel@rosdahl.net>
Tue, 25 Jul 2023 19:43:15 +0000 (21:43 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 2 Aug 2023 11:36:34 +0000 (13:36 +0200)
src/Util.cpp
src/Util.hpp
src/ccache.cpp
src/core/ResultRetriever.cpp
src/core/common.cpp
src/core/common.hpp
unittest/test_Util.cpp
unittest/test_core_common.cpp

index da87144b630aaf008c03bda9079a2e7f0296ef48..d2c8b5cc83f127764b7d215c1bfcbe044a65f656 100644 (file)
 
 #include <fcntl.h>
 
-using IncludeDelimiter = util::Tokenizer::IncludeDelimiter;
-
 namespace fs = util::filesystem;
 
-namespace {
-
-// Search for the first match of the following regular expression:
-//
-//   \x1b\[[\x30-\x3f]*[\x20-\x2f]*[Km]
-//
-// The primary reason for not using std::regex is that it's not available for
-// GCC 4.8. It's also a bit bloated. The reason for not using POSIX regex
-// functionality is that it's are not available in MinGW.
-std::string_view
-find_first_ansi_csi_seq(std::string_view string)
-{
-  size_t pos = 0;
-  while (pos < string.length() && string[pos] != 0x1b) {
-    ++pos;
-  }
-  if (pos + 1 >= string.length() || string[pos + 1] != '[') {
-    return {};
-  }
-  size_t start = pos;
-  pos += 2;
-  while (pos < string.length()
-         && (string[pos] >= 0x30 && string[pos] <= 0x3f)) {
-    ++pos;
-  }
-  while (pos < string.length()
-         && (string[pos] >= 0x20 && string[pos] <= 0x2f)) {
-    ++pos;
-  }
-  if (pos < string.length() && (string[pos] == 'K' || string[pos] == 'm')) {
-    return string.substr(start, pos + 1 - start);
-  } else {
-    return {};
-  }
-}
-
-std::string
-rewrite_stderr_to_absolute_paths(std::string_view text)
-{
-  static const std::string in_file_included_from = "In file included from ";
-
-  std::string result;
-  using util::Tokenizer;
-  for (auto line : Tokenizer(text,
-                             "\n",
-                             Tokenizer::Mode::include_empty,
-                             Tokenizer::IncludeDelimiter::yes)) {
-    // Rewrite <path> to <absolute path> in the following two cases, where X may
-    // be optional ANSI CSI sequences:
-    //
-    // In file included from X<path>X:1:
-    // X<path>X:1:2: ...
-
-    if (util::starts_with(line, in_file_included_from)) {
-      result += in_file_included_from;
-      line = line.substr(in_file_included_from.length());
-    }
-    while (!line.empty() && line[0] == 0x1b) {
-      auto csi_seq = find_first_ansi_csi_seq(line);
-      result.append(csi_seq.data(), csi_seq.length());
-      line = line.substr(csi_seq.length());
-    }
-    size_t path_end = line.find(':');
-    if (path_end == std::string_view::npos) {
-      result.append(line.data(), line.length());
-    } else {
-      std::string path(line.substr(0, path_end));
-      if (Stat::stat(path)) {
-        result += util::real_path(path);
-        auto tail = line.substr(path_end);
-        result.append(tail.data(), tail.length());
-      } else {
-        result.append(line.data(), line.length());
-      }
-    }
-  }
-  return result;
-}
-
-} // namespace
-
 namespace Util {
 
 std::string_view
@@ -468,61 +385,6 @@ remove_extension(std::string_view path)
   return path.substr(0, path.length() - get_extension(path).length());
 }
 
-void
-send_to_fd(const Context& ctx, std::string_view text, int fd)
-{
-  std::string_view text_to_send = text;
-  std::string modified_text;
-
-#ifdef _WIN32
-  // stdout/stderr are normally opened in text mode, which would convert
-  // newlines a second time since we treat output as binary data. Make sure to
-  // switch to binary mode.
-  int oldmode = _setmode(fd, _O_BINARY);
-  Finalizer binary_mode_restorer([=] { _setmode(fd, oldmode); });
-#endif
-
-  if (ctx.args_info.strip_diagnostics_colors) {
-    try {
-      modified_text = strip_ansi_csi_seqs(text);
-      text_to_send = modified_text;
-    } catch (const core::Error&) {
-      // Ignore.
-    }
-  }
-
-  if (ctx.config.absolute_paths_in_stderr()) {
-    modified_text = rewrite_stderr_to_absolute_paths(text_to_send);
-    text_to_send = modified_text;
-  }
-
-  util::throw_on_error<core::Error>(
-    util::write_fd(fd, text_to_send.data(), text_to_send.length()),
-    FMT("Failed to write to fd {}: ", fd));
-}
-
-std::string
-strip_ansi_csi_seqs(std::string_view string)
-{
-  size_t pos = 0;
-  std::string result;
-
-  while (true) {
-    auto seq_span = find_first_ansi_csi_seq(string.substr(pos));
-    auto data_start = string.data() + pos;
-    auto data_length =
-      seq_span.empty() ? string.length() - pos : seq_span.data() - data_start;
-    result.append(data_start, data_length);
-    if (seq_span.empty()) {
-      // Reached tail.
-      break;
-    }
-    pos += data_length + seq_span.length();
-  }
-
-  return result;
-}
-
 #ifdef HAVE_DIRENT_H
 
 void
index 9dc99a8f777cd6f60c7ac6e399aa9d2ee142ed89..717b371b574557a229c2cb3c8d5e9903aa90d0d4 100644 (file)
@@ -123,15 +123,6 @@ std::string normalize_concrete_absolute_path(const std::string& path);
 // extension as determined by `get_extension()`.
 std::string_view remove_extension(std::string_view path);
 
-// Send `text` to file descriptor `fd`, optionally stripping ANSI color
-// sequences if `ctx.args_info.strip_diagnostics_colors` is true and rewriting
-// paths to absolute if `ctx.config.absolute_paths_in_stderr` is true. Throws
-// `core::Error` on error.
-void send_to_fd(const Context& ctx, std::string_view text, int fd);
-
-// Returns a copy of string with the specified ANSI CSI sequences removed.
-[[nodiscard]] std::string strip_ansi_csi_seqs(std::string_view string);
-
 // Traverse `path` recursively (postorder, i.e. files are visited before their
 // parent directory).
 //
index 48aac63e6c26b0ff4d53c6e865dccd1386c25fdc..c9e6ed4312d3ae4890f182ed0e2b4aa568c75c0c 100644 (file)
@@ -48,6 +48,7 @@
 #include <core/ResultRetriever.hpp>
 #include <core/Statistics.hpp>
 #include <core/StatsLog.hpp>
+#include <core/common.hpp>
 #include <core/exceptions.hpp>
 #include <core/mainoptions.hpp>
 #include <core/types.hpp>
@@ -1018,7 +1019,7 @@ rewrite_stdout_from_compiler(const Context& ctx, util::Bytes&& stdout_data)
                                      Mode::include_empty,
                                      IncludeDelimiter::yes)) {
       if (util::starts_with(line, "__________")) {
-        Util::send_to_fd(ctx, line, STDOUT_FILENO);
+        core::send_to_console(ctx, line, STDOUT_FILENO);
       }
       // Ninja uses the lines with 'Note: including file: ' to determine the
       // used headers. Headers within basedir need to be changed into relative
@@ -1138,9 +1139,9 @@ to_cache(Context& ctx,
     LOG("Compiler gave exit status {}", result->exit_status);
 
     // We can output stderr immediately instead of rerunning the compiler.
-    Util::send_to_fd(
+    core::send_to_console(
       ctx, util::to_string_view(result->stderr_data), STDERR_FILENO);
-    Util::send_to_fd(
+    core::send_to_console(
       ctx,
       util::to_string_view(core::MsvcShowIncludesOutput::strip_includes(
         ctx, std::move(result->stdout_data))),
@@ -1201,10 +1202,10 @@ to_cache(Context& ctx,
   MTR_END("result", "result_put");
 
   // Everything OK.
-  Util::send_to_fd(
+  core::send_to_console(
     ctx, util::to_string_view(result->stderr_data), STDERR_FILENO);
   // Send stdout after stderr, it makes the output clearer with MSVC.
-  Util::send_to_fd(
+  core::send_to_console(
     ctx,
     util::to_string_view(core::MsvcShowIncludesOutput::strip_includes(
       ctx, std::move(result->stdout_data))),
index eefb6226490814120bcbc8a709c5462328bbcff7..5e5b3a9b975a9045eed285d91d15134d8ed25d78 100644 (file)
@@ -26,6 +26,7 @@
 #include <Stat.hpp>
 #include <Util.hpp>
 #include <core/MsvcShowIncludesOutput.hpp>
+#include <core/common.hpp>
 #include <core/exceptions.hpp>
 #include <core/wincompat.hpp>
 #include <fmtmacros.hpp>
@@ -63,12 +64,12 @@ ResultRetriever::on_embedded_file(uint8_t file_number,
       data.size());
 
   if (file_type == FileType::stdout_output) {
-    Util::send_to_fd(
+    core::send_to_console(
       m_ctx,
       util::to_string_view(MsvcShowIncludesOutput::strip_includes(m_ctx, data)),
       STDOUT_FILENO);
   } else if (file_type == FileType::stderr_output) {
-    Util::send_to_fd(m_ctx, util::to_string_view(data), STDERR_FILENO);
+    core::send_to_console(m_ctx, util::to_string_view(data), STDERR_FILENO);
   } else {
     const auto dest_path = get_dest_path(file_type);
     if (dest_path.empty()) {
index c1eebaa646079b4c3d51c2b91c1f7db4e2c9c561..5fd0ba59a6fc10b340a891288406748a66eace08 100644 (file)
 
 #include "common.hpp"
 
+#include <Context.hpp>
+#include <Finalizer.hpp>
 #include <core/exceptions.hpp>
 #include <fmtmacros.hpp>
+#include <util/Tokenizer.hpp>
+#include <util/expected.hpp>
 #include <util/filesystem.hpp>
+#include <util/path.hpp>
+
+using IncludeDelimiter = util::Tokenizer::IncludeDelimiter;
 
 namespace fs = util::filesystem;
 
+namespace {
+
+// Search for the first match of the following regular expression:
+//
+//   \x1b\[[\x30-\x3f]*[\x20-\x2f]*[Km]
+//
+// The primary reason for not using std::regex is that it's not available for
+// GCC 4.8. It's also a bit bloated. The reason for not using POSIX regex
+// functionality is that it's are not available in MinGW.
+std::string_view
+find_first_ansi_csi_seq(std::string_view string)
+{
+  size_t pos = 0;
+  while (pos < string.length() && string[pos] != 0x1b) {
+    ++pos;
+  }
+  if (pos + 1 >= string.length() || string[pos + 1] != '[') {
+    return {};
+  }
+  size_t start = pos;
+  pos += 2;
+  while (pos < string.length() && string[pos] >= 0x30 && string[pos] <= 0x3f) {
+    ++pos;
+  }
+  while (pos < string.length() && string[pos] >= 0x20 && string[pos] <= 0x2f) {
+    ++pos;
+  }
+  if (pos < string.length() && (string[pos] == 'K' || string[pos] == 'm')) {
+    return string.substr(start, pos + 1 - start);
+  } else {
+    return {};
+  }
+}
+
+std::string
+rewrite_stderr_to_absolute_paths(std::string_view text)
+{
+  const std::string_view in_file_included_from = "In file included from ";
+
+  std::string result;
+  using util::Tokenizer;
+  for (auto line : Tokenizer(text,
+                             "\n",
+                             Tokenizer::Mode::include_empty,
+                             Tokenizer::IncludeDelimiter::yes)) {
+    // Rewrite <path> to <absolute path> in the following two cases, where X may
+    // be optional ANSI CSI sequences:
+    //
+    // In file included from X<path>X:1:
+    // X<path>X:1:2: ...
+
+    if (util::starts_with(line, in_file_included_from)) {
+      result += in_file_included_from;
+      line = line.substr(in_file_included_from.length());
+    }
+    while (!line.empty() && line[0] == 0x1b) {
+      auto csi_seq = find_first_ansi_csi_seq(line);
+      result.append(csi_seq.data(), csi_seq.length());
+      line = line.substr(csi_seq.length());
+    }
+    size_t path_end = line.find(':');
+    if (path_end == std::string_view::npos) {
+      result.append(line.data(), line.length());
+    } else {
+      std::string path(line.substr(0, path_end));
+      if (Stat::stat(path)) {
+        result += util::real_path(path);
+        auto tail = line.substr(path_end);
+        result.append(tail.data(), tail.length());
+      } else {
+        result.append(line.data(), line.length());
+      }
+    }
+  }
+  return result;
+}
+
+} // namespace
+
 namespace core {
 
 void
@@ -35,4 +121,55 @@ ensure_dir_exists(std::string_view dir)
   }
 }
 
+void
+send_to_console(const Context& ctx, std::string_view text, int fd)
+{
+  std::string_view text_to_send = text;
+  std::string modified_text;
+
+#ifdef _WIN32
+  // stdout/stderr are normally opened in text mode, which would convert
+  // newlines a second time since we treat output as binary data. Make sure to
+  // switch to binary mode.
+  int oldmode = _setmode(fd, _O_BINARY);
+  Finalizer binary_mode_restorer([=] { _setmode(fd, oldmode); });
+#endif
+
+  if (ctx.args_info.strip_diagnostics_colors) {
+    modified_text = strip_ansi_csi_seqs(text);
+    text_to_send = modified_text;
+  }
+
+  if (ctx.config.absolute_paths_in_stderr()) {
+    modified_text = rewrite_stderr_to_absolute_paths(text_to_send);
+    text_to_send = modified_text;
+  }
+
+  util::throw_on_error<core::Error>(
+    util::write_fd(fd, text_to_send.data(), text_to_send.length()),
+    FMT("Failed to write to fd {}: ", fd));
+}
+
+std::string
+strip_ansi_csi_seqs(std::string_view string)
+{
+  size_t pos = 0;
+  std::string result;
+
+  while (true) {
+    auto seq_span = find_first_ansi_csi_seq(string.substr(pos));
+    auto data_start = string.data() + pos;
+    auto data_length =
+      seq_span.empty() ? string.length() - pos : seq_span.data() - data_start;
+    result.append(data_start, data_length);
+    if (seq_span.empty()) {
+      // Reached tail.
+      break;
+    }
+    pos += data_length + seq_span.length();
+  }
+
+  return result;
+}
+
 } // namespace core
index 695238e9e0ab81666f3d613283727ceb0b61f2d4..4247d77ccb278439126af44c7e7a05a3338ef631 100644 (file)
 
 #pragma once
 
+#include <string>
 #include <string_view>
 
+class Context;
+
 namespace core {
 
 // Like std::filesystem::create_directories but throws core::Fatal on error.
 void ensure_dir_exists(std::string_view dir);
 
+// Send `text` to file descriptor `fd` (typically stdout or stderr, which
+// potentially is connected to a console), optionally stripping ANSI color
+// sequences if `ctx.args_info.strip_diagnostics_colors` is true and rewriting
+// paths to absolute if `ctx.config.absolute_paths_in_stderr()` is true. Throws
+// `core::Error` on error.
+void send_to_console(const Context& ctx, std::string_view text, int fd);
+
+// Returns a copy of string with the specified ANSI CSI sequences removed.
+[[nodiscard]] std::string strip_ansi_csi_seqs(std::string_view string);
+
 } // namespace core
index 498e6d3ffe60172e5fc808ab67e8d9b1156fc252..43a64870047c02e0d3e8bfc6d954f360b6f3113e 100644 (file)
@@ -113,17 +113,6 @@ TEST_CASE("Util::dir_name")
 #endif
 }
 
-TEST_CASE("Util::strip_ansi_csi_seqs")
-{
-  const char input[] =
-    "Normal,"
-    " \x1B[K\x1B[1mbold\x1B[m,"
-    " \x1B[31mred\x1B[m,"
-    " \x1B[1;32mbold green\x1B[m.\n";
-
-  CHECK(Util::strip_ansi_csi_seqs(input) == "Normal, bold, red, bold green.\n");
-}
-
 TEST_CASE("Util::get_extension")
 {
   CHECK(Util::get_extension("") == "");
index 66cd6e031a658b2a411513913cab08ce3a7625e8..9c8e27ec27f238019b1996f25d926c2e22be32ab 100644 (file)
@@ -43,4 +43,15 @@ TEST_CASE("core::ensure_dir_exists")
     doctest::Contains("Failed to create directory create/dir/file:"));
 }
 
+TEST_CASE("core::strip_ansi_csi_seqs")
+{
+  const char input[] =
+    "Normal,"
+    " \x1B[K\x1B[1mbold\x1B[m,"
+    " \x1B[31mred\x1B[m,"
+    " \x1B[1;32mbold green\x1B[m.\n";
+
+  CHECK(core::strip_ansi_csi_seqs(input) == "Normal, bold, red, bold green.\n");
+}
+
 TEST_SUITE_END();