]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Refactor result code into a Result::Reader/Writer classes
authorJoel Rosdahl <joel@rosdahl.net>
Sat, 4 Jul 2020 11:52:16 +0000 (13:52 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 6 Jul 2020 17:59:58 +0000 (19:59 +0200)
Introduced a Result::Reader class which remove knowledge about what to
do with the individual file parts of a result. That’s instead defined by
a Result::Reader::Consumer instance passed to Result::Reader::read. This
means a ResultFileMap is no longer used and that the consumer is free to
write data whereever it wants.

The new ResultRetriever class implements retrieval by writing to local
files like before, except that the stderr data is written directly to
STDERR_FILENO instead of landing in a temporary file.

The new ResultDumper class implements “ccache --dump-result”.

Also introduced a Result::Writer class, mostly for symmetry with the
Result::Reader class.

src/CMakeLists.txt
src/Result.cpp [new file with mode: 0644]
src/Result.hpp [new file with mode: 0644]
src/ResultDumper.cpp [new file with mode: 0644]
src/ResultDumper.hpp [new file with mode: 0644]
src/ResultRetriever.cpp [new file with mode: 0644]
src/ResultRetriever.hpp [new file with mode: 0644]
src/ccache.cpp
src/compress.cpp
src/result.cpp [deleted file]
src/result.hpp [deleted file]

index 5e1ece50898c7991e202114f3d801e7f5e083af6..9e50e135574251af59cc9adb87ff4295a8e8750a 100644 (file)
@@ -16,6 +16,9 @@ set(
   NullCompressor.cpp
   NullDecompressor.cpp
   ProgressBar.cpp
+  Result.cpp
+  ResultDumper.cpp
+  ResultRetriever.cpp
   SignalHandler.cpp
   Stat.cpp
   ThreadPool.cpp
@@ -34,7 +37,6 @@ set(
   legacy_util.cpp
   logging.cpp
   manifest.cpp
-  result.cpp
   stats.cpp
   version.cpp)
 
diff --git a/src/Result.cpp b/src/Result.cpp
new file mode 100644 (file)
index 0000000..6661084
--- /dev/null
@@ -0,0 +1,409 @@
+// Copyright (C) 2019-2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "Result.hpp"
+
+#include "AtomicFile.hpp"
+#include "CacheEntryReader.hpp"
+#include "CacheEntryWriter.hpp"
+#include "Config.hpp"
+#include "Context.hpp"
+#include "Fd.hpp"
+#include "File.hpp"
+#include "Stat.hpp"
+#include "Util.hpp"
+#include "exceptions.hpp"
+#include "logging.hpp"
+#include "stats.hpp"
+
+// Result data format
+// ==================
+//
+// Integers are big-endian.
+//
+// <result>               ::= <header> <body> <epilogue>
+// <header>               ::= <magic> <version> <compr_type> <compr_level>
+//                            <content_len>
+// <magic>                ::= 4 bytes ("cCrS")
+// <version>              ::= uint8_t
+// <compr_type>           ::= <compr_none> | <compr_zstd>
+// <compr_none>           ::= 0 (uint8_t)
+// <compr_zstd>           ::= 1 (uint8_t)
+// <compr_level>          ::= int8_t
+// <content_len>          ::= uint64_t ; size of file if stored uncompressed
+// <body>                 ::= <n_entries> <entry>* ; potentially compressed
+// <n_entries>            ::= uint8_t
+// <entry>                ::= <embedded_file_entry> | <raw_file_entry>
+// <embedded_file_entry>  ::= <embedded_file_marker> <suffix_len> <suffix>
+//                            <data_len> <data>
+// <embedded_file_marker> ::= 0 (uint8_t)
+// <embedded_file_type>   ::= uint8_t
+// <data_len>             ::= uint64_t
+// <data>                 ::= data_len bytes
+// <raw_file_entry>       ::= <raw_file_marker> <suffix_len> <suffix> <file_len>
+// <raw_file_marker>      ::= 1 (uint8_t)
+// <file_len>             ::= uint64_t
+// <epilogue>             ::= <checksum>
+// <checksum>             ::= uint64_t ; XXH64 of content bytes
+//
+// Sketch of concrete layout:
+//
+// <magic>                4 bytes
+// <version>              1 byte
+// <compr_type>           1 byte
+// <compr_level>          1 byte
+// <content_len>          8 bytes
+// --- [potentially compressed from here] -------------------------------------
+// <n_entries>            1 byte
+// <embedded_file_marker> 1 byte
+// <embedded_file_type>   1 byte
+// <data_len>             8 bytes
+// <data>                 data_len bytes
+// ...
+// <ref_marker>           1 byte
+// <key_len>              1 byte
+// <key>                  key_len bytes
+// ...
+// checksum               8 bytes
+//
+//
+// Version history
+// ===============
+//
+// 1: Introduced in ccache 4.0.
+
+using nonstd::nullopt;
+using nonstd::optional;
+
+namespace {
+
+// File data stored inside the result file.
+const uint8_t k_embedded_file_marker = 0;
+
+// File stored as-is in the file system.
+const uint8_t k_raw_file_marker = 1;
+
+std::string
+get_raw_file_path(const std::string& result_path_in_cache,
+                  uint32_t entry_number)
+{
+  return fmt::format("{:{}}_{}.raw",
+                     result_path_in_cache.c_str(),
+                     result_path_in_cache.length() - 7, // .result
+                     entry_number);
+}
+
+bool
+should_store_raw_file(const Config& config, Result::FileType type)
+{
+  if (!config.file_clone() && !config.hard_link()) {
+    return false;
+  }
+
+  // Only store object files as raw files since there are several problems with
+  // storing other file types:
+  //
+  // 1. The compiler unlinks object files before writing to them but it doesn't
+  //    unlink .d files, so just it's possible to corrupt .d files just by
+  //    running the compiler (see ccache issue 599).
+  // 2. .d files cause trouble for automake if hard-linked (see ccache issue
+  //    378).
+  // 3. It's unknown how the compiler treats other file types, so better safe
+  //    than sorry.
+  //
+  // It would be possible to store all files in raw form for the file_clone case
+  // and only hard link object files. However, most likely it's only object
+  // files that become large enough that it's of interest to clone or hard link
+  // them, so we keep things simple for now. This will also save i-nodes in the
+  // cache.
+  return type == Result::FileType::object;
+}
+
+} // namespace
+
+namespace Result {
+
+const uint8_t k_magic[4] = {'c', 'C', 'r', 'S'};
+const uint8_t k_version = 1;
+
+const char*
+file_type_to_string(FileType type)
+{
+  switch (type) {
+  case FileType::object:
+    return ".o";
+
+  case FileType::dependency:
+    return ".d";
+
+  case FileType::stderr_output:
+    return "<stderr>";
+
+  case FileType::coverage:
+    return ".cov";
+
+  case FileType::stackusage:
+    return ".su";
+
+  case FileType::diagnostic:
+    return ".dia";
+
+  case FileType::dwarf_object:
+    return ".dwo";
+  }
+
+  return "<unknown type>";
+}
+
+Result::Reader::Reader(const std::string& result_path)
+  : m_result_path(result_path)
+{
+}
+
+optional<std::string>
+Result::Reader::read(Consumer& consumer)
+{
+  cc_log("Reading result %s", m_result_path.c_str());
+
+  try {
+    if (read_result(consumer)) {
+      return nullopt;
+    } else {
+      return "No such result file";
+    }
+  } catch (const Error& e) {
+    return e.what();
+  }
+}
+
+bool
+Reader::read_result(Consumer& consumer)
+{
+  File file(m_result_path, "rb");
+  if (!file) {
+    // Cache miss.
+    return false;
+  }
+
+  CacheEntryReader cache_entry_reader(file.get(), k_magic, k_version);
+
+  consumer.on_header(cache_entry_reader);
+
+  uint8_t n_entries;
+  cache_entry_reader.read(n_entries);
+
+  uint32_t i;
+  for (i = 0; i < n_entries; ++i) {
+    read_entry(cache_entry_reader, i, consumer);
+  }
+
+  if (i != n_entries) {
+    throw Error(
+      fmt::format("Too few entries (read {}, expected {})", i, n_entries));
+  }
+
+  cache_entry_reader.finalize();
+  return true;
+}
+
+void
+Reader::read_entry(CacheEntryReader& cache_entry_reader,
+                   uint32_t entry_number,
+                   Reader::Consumer& consumer)
+{
+  uint8_t marker;
+  cache_entry_reader.read(marker);
+
+  switch (marker) {
+  case k_embedded_file_marker:
+  case k_raw_file_marker:
+    break;
+
+  default:
+    throw Error(fmt::format("Unknown entry type: {}", marker));
+  }
+
+  UnderlyingFileTypeInt type;
+  cache_entry_reader.read(type);
+  FileType file_type = FileType(type);
+
+  uint64_t file_len;
+  cache_entry_reader.read(file_len);
+
+  if (marker == k_embedded_file_marker) {
+    consumer.on_entry_start(entry_number, file_type, file_len, nullopt);
+
+    uint8_t buf[READ_BUFFER_SIZE];
+    size_t remain = file_len;
+    while (remain > 0) {
+      size_t n = std::min(remain, sizeof(buf));
+      cache_entry_reader.read(buf, n);
+      consumer.on_entry_data(buf, n);
+      remain -= n;
+    }
+  } else {
+    assert(marker == k_raw_file_marker);
+
+    auto raw_path = get_raw_file_path(m_result_path, entry_number);
+    auto st = Stat::stat(raw_path, Stat::OnError::throw_error);
+    if (st.size() != file_len) {
+      throw Error(
+        fmt::format("Bad file size of {} (actual {} bytes, expected {} bytes)",
+                    raw_path,
+                    st.size(),
+                    file_len));
+    }
+
+    consumer.on_entry_start(entry_number, file_type, file_len, raw_path);
+  }
+
+  consumer.on_entry_end();
+}
+
+Writer::Writer(Context& ctx, const std::string& result_path)
+  : m_ctx(ctx), m_result_path(result_path)
+{
+}
+
+Writer::~Writer()
+{
+  if (!m_finalized) {
+    finalize();
+  }
+}
+
+void
+Writer::write(FileType file_type, const std::string& file_path)
+{
+  m_entries_to_write.emplace_back(file_type, file_path);
+}
+
+optional<std::string>
+Writer::finalize()
+{
+  m_finalized = true;
+  try {
+    do_finalize();
+    return nullopt;
+  } catch (const Error& e) {
+    return e.what();
+  }
+}
+
+void
+Writer::do_finalize()
+{
+  uint64_t payload_size = 0;
+  payload_size += 1; // n_entries
+  for (const auto& pair : m_entries_to_write) {
+    const auto& path = pair.second;
+    auto st = Stat::stat(path, Stat::OnError::throw_error);
+
+    payload_size += 1;         // embedded_file_marker
+    payload_size += 1;         // embedded_file_type
+    payload_size += 8;         // data_len
+    payload_size += st.size(); // data
+  }
+
+  AtomicFile atomic_result_file(m_result_path, AtomicFile::Mode::binary);
+  CacheEntryWriter writer(atomic_result_file.stream(),
+                          k_magic,
+                          k_version,
+                          Compression::type_from_config(m_ctx.config),
+                          Compression::level_from_config(m_ctx.config),
+                          payload_size);
+
+  writer.write<uint8_t>(m_entries_to_write.size());
+
+  uint32_t entry_number = 0;
+  for (const auto& pair : m_entries_to_write) {
+    const auto file_type = pair.first;
+    const auto& path = pair.second;
+    cc_log("Storing result %s", path.c_str());
+
+    const bool store_raw = should_store_raw_file(m_ctx.config, file_type);
+    uint64_t file_size = Stat::stat(path, Stat::OnError::throw_error).size();
+
+    cc_log("Storing %s file #%u %s (%llu bytes) from %s",
+           store_raw ? "raw" : "embedded",
+           entry_number,
+           file_type_to_string(file_type),
+           (unsigned long long)file_size,
+           path.c_str());
+
+    writer.write<uint8_t>(store_raw ? k_raw_file_marker
+                                    : k_embedded_file_marker);
+    writer.write(UnderlyingFileTypeInt(file_type));
+    writer.write(file_size);
+
+    if (store_raw) {
+      write_raw_file_entry(path, entry_number);
+    } else {
+      write_embedded_file_entry(writer, path, file_size);
+    }
+
+    ++entry_number;
+  }
+
+  writer.finalize();
+  atomic_result_file.commit();
+}
+
+void
+Result::Writer::write_embedded_file_entry(CacheEntryWriter& writer,
+                                          const std::string& path,
+                                          uint64_t file_size)
+{
+  Fd file(open(path.c_str(), O_RDONLY | O_BINARY));
+  if (!file) {
+    throw Error(fmt::format("Failed to open {} for reading", path));
+  }
+
+  uint64_t remain = file_size;
+  while (remain > 0) {
+    uint8_t buf[READ_BUFFER_SIZE];
+    size_t n = std::min(remain, static_cast<uint64_t>(sizeof(buf)));
+    ssize_t bytes_read = read(*file, buf, n);
+    if (bytes_read < 1) {
+      throw Error(
+        fmt::format("Error reading from {}: {}", path, strerror(errno)));
+    } else if (bytes_read == 0) {
+      throw Error(fmt::format("Error reading from {}: end of file", path));
+    }
+    writer.write(buf, n);
+    remain -= n;
+  }
+}
+
+void
+Result::Writer::write_raw_file_entry(const std::string& path,
+                                     uint32_t entry_number)
+{
+  auto raw_file = get_raw_file_path(m_result_path, entry_number);
+  auto old_stat = Stat::stat(raw_file);
+  if (!Util::clone_hard_link_or_copy_file(m_ctx, path, raw_file, true)) {
+    throw Error(
+      fmt::format("Failed to store {} as raw file {}", path, raw_file));
+  }
+  auto new_stat = Stat::stat(raw_file);
+  stats_update_size(m_ctx.counter_updates,
+                    new_stat.size_on_disk() - old_stat.size_on_disk(),
+                    (new_stat ? 1 : 0) - (old_stat ? 1 : 0));
+}
+
+} // namespace Result
diff --git a/src/Result.hpp b/src/Result.hpp
new file mode 100644 (file)
index 0000000..8918996
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright (C) 2019-2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include "system.hpp"
+
+#include "third_party/nonstd/optional.hpp"
+
+#include <map>
+#include <string>
+#include <vector>
+
+class CacheEntryReader;
+class CacheEntryWriter;
+class Context;
+
+namespace Result {
+
+extern const uint8_t k_magic[4];
+extern const uint8_t k_version;
+
+using UnderlyingFileTypeInt = uint8_t;
+enum class FileType : UnderlyingFileTypeInt {
+  // These values are written into the cache result file. This means they must
+  // never be changed or removed unless the result file version is incremented.
+  // Adding new values is OK.
+  object = 0,
+  dependency = 1,
+  stderr_output = 2,
+  coverage = 3,
+  stackusage = 4,
+  diagnostic = 5,
+  dwarf_object = 6,
+};
+
+const char* file_type_to_string(FileType type);
+
+// This class knows how to read a result cache entry.
+class Reader
+{
+public:
+  Reader(const std::string& result_path);
+
+  class Consumer
+  {
+  public:
+    virtual ~Consumer() = default;
+
+    virtual void on_header(CacheEntryReader& cache_entry_reader) = 0;
+    virtual void on_entry_start(uint32_t entry_number,
+                                FileType file_type,
+                                uint64_t file_len,
+                                nonstd::optional<std::string> raw_file) = 0;
+    virtual void on_entry_data(const uint8_t* data, size_t size) = 0;
+    virtual void on_entry_end() = 0;
+  };
+
+  // Returns error message on error, otherwise nonstd::nullopt.
+  nonstd::optional<std::string> read(Consumer& consumer);
+
+private:
+  const std::string m_result_path;
+
+  bool read_result(Consumer& consumer);
+  void read_entry(CacheEntryReader& cache_entry_reader,
+                  uint32_t entry_number,
+                  Reader::Consumer& consumer);
+};
+
+// This class knows how to write a result cache entry.
+class Writer
+{
+public:
+  Writer(Context& ctx, const std::string& result_path);
+  ~Writer();
+
+  // Register a file to include in the result. Does not throw.
+  void write(FileType file_type, const std::string& file_path);
+
+  // Write registered files to the result. Returns an error message on error.
+  nonstd::optional<std::string> finalize();
+
+private:
+  Context& m_ctx;
+  const std::string m_result_path;
+  bool m_finalized = false;
+  std::vector<std::pair<FileType, std::string>> m_entries_to_write;
+
+  void do_finalize();
+  void write_embedded_file_entry(CacheEntryWriter& writer,
+                                 const std::string& path,
+                                 uint64_t file_size);
+  void write_raw_file_entry(const std::string& path, uint32_t entry_number);
+};
+
+} // namespace Result
diff --git a/src/ResultDumper.cpp b/src/ResultDumper.cpp
new file mode 100644 (file)
index 0000000..e6cd112
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "ResultDumper.hpp"
+
+#include "CacheEntryReader.hpp"
+#include "Context.hpp"
+#include "logging.hpp"
+
+using nonstd::optional;
+
+ResultDumper::ResultDumper(FILE* stream) : m_stream(stream)
+{
+}
+
+void
+ResultDumper::on_header(CacheEntryReader& cache_entry_reader)
+{
+  cache_entry_reader.dump_header(m_stream);
+}
+
+void
+ResultDumper::on_entry_start(uint32_t entry_number,
+                             Result::FileType file_type,
+                             uint64_t file_len,
+                             optional<std::string> raw_file)
+{
+  fmt::print(m_stream,
+             "{} file #{}: {} ({} bytes)\n",
+             raw_file ? "Raw" : "Embedded",
+             entry_number,
+             Result::file_type_to_string(file_type),
+             file_len);
+}
+
+void
+ResultDumper::on_entry_data(const uint8_t* /*data*/, size_t /*size*/)
+{
+}
+
+void
+ResultDumper::on_entry_end()
+{
+}
diff --git a/src/ResultDumper.hpp b/src/ResultDumper.hpp
new file mode 100644 (file)
index 0000000..767a834
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include "system.hpp"
+
+#include "Result.hpp"
+
+// This class dumps information about the result entry to `stream`.
+class ResultDumper : public Result::Reader::Consumer
+{
+public:
+  ResultDumper(FILE* stream);
+
+  virtual void on_header(CacheEntryReader& cache_entry_reader);
+  virtual void on_entry_start(uint32_t entry_number,
+                              Result::FileType file_type,
+                              uint64_t file_len,
+                              nonstd::optional<std::string> raw_file);
+  virtual void on_entry_data(const uint8_t* data, size_t size);
+  virtual void on_entry_end();
+
+private:
+  FILE* m_stream;
+};
diff --git a/src/ResultRetriever.cpp b/src/ResultRetriever.cpp
new file mode 100644 (file)
index 0000000..1e91351
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "ResultRetriever.hpp"
+
+#include "Context.hpp"
+#include "logging.hpp"
+
+#include "third_party/nonstd/string_view.hpp"
+
+using string_view = nonstd::string_view;
+
+ResultRetriever::ResultRetriever(Context& ctx) : m_ctx(ctx)
+{
+}
+
+void
+ResultRetriever::on_header(CacheEntryReader& /*cache_entry_reader*/)
+{
+}
+
+void
+ResultRetriever::on_entry_start(uint32_t entry_number,
+                                Result::FileType file_type,
+                                uint64_t file_len,
+                                nonstd::optional<std::string> raw_file)
+{
+  std::string dest_path;
+
+  m_dest_file_type = file_type;
+
+  switch (file_type) {
+  case Result::FileType::object:
+    dest_path = m_ctx.args_info.output_obj;
+    break;
+
+  case Result::FileType::dependency:
+    if (m_ctx.args_info.generating_dependencies) {
+      dest_path = m_ctx.args_info.output_dep;
+    }
+    break;
+
+  case Result::FileType::stderr_output:
+    m_dest_stderr = true;
+    return;
+
+  case Result::FileType::coverage:
+    if (m_ctx.args_info.generating_coverage) {
+      dest_path = m_ctx.args_info.output_cov;
+    }
+    break;
+
+  case Result::FileType::stackusage:
+    if (m_ctx.args_info.generating_stackusage) {
+      dest_path = m_ctx.args_info.output_su;
+    }
+    break;
+
+  case Result::FileType::diagnostic:
+    if (m_ctx.args_info.generating_diagnostics) {
+      dest_path = m_ctx.args_info.output_dia;
+    }
+    break;
+
+  case Result::FileType::dwarf_object:
+    if (m_ctx.args_info.seen_split_dwarf
+        && m_ctx.args_info.output_obj != "/dev/null") {
+      dest_path = m_ctx.args_info.output_dwo;
+    }
+    break;
+  }
+
+  m_dest_stderr = false;
+
+  if (dest_path.empty()) {
+    cc_log("Not copying");
+  } else if (dest_path == "/dev/null") {
+    cc_log("Not copying to /dev/null");
+  } else {
+    cc_log("Retrieving %s file #%u %s (%llu bytes)",
+           raw_file ? "raw" : "embedded",
+           entry_number,
+           Result::file_type_to_string(file_type),
+           (unsigned long long)file_len);
+
+    if (raw_file) {
+      Util::clone_hard_link_or_copy_file(m_ctx, *raw_file, dest_path, false);
+
+      // Update modification timestamp to save the file from LRU cleanup (and,
+      // if hard-linked, to make the object file newer than the source file).
+      update_mtime(raw_file->c_str());
+    } else {
+      cc_log("Copying to %s", dest_path.c_str());
+      m_dest_fd = Fd(
+        open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
+      if (!m_dest_fd) {
+        throw Error(fmt::format(
+          "Failed to open {} for writing: {}", dest_path, strerror(errno)));
+      }
+      m_dest_path = dest_path;
+    }
+  }
+}
+
+void
+ResultRetriever::on_entry_data(const uint8_t* data, size_t size)
+{
+  assert((m_dest_stderr && !m_dest_fd) || (!m_dest_stderr && m_dest_fd));
+
+  if (m_dest_stderr) {
+    m_stderr_text.append(reinterpret_cast<const char*>(data), size);
+  } else if (!write_fd(*m_dest_fd, data, size)) {
+    throw Error(fmt::format("Failed to write to {}", m_dest_path));
+  }
+}
+
+void
+ResultRetriever::on_entry_end()
+{
+  if (m_dest_stderr) {
+    Util::send_to_stderr(m_stderr_text,
+                         m_ctx.args_info.strip_diagnostics_colors);
+  } else if (m_dest_fd) {
+    m_dest_fd.close();
+  }
+
+  m_dest_path.clear();
+}
diff --git a/src/ResultRetriever.hpp b/src/ResultRetriever.hpp
new file mode 100644 (file)
index 0000000..48cb84b
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include "system.hpp"
+
+#include "Fd.hpp"
+#include "Result.hpp"
+
+class Context;
+
+// This class retrieves an result entry to the local file system.
+class ResultRetriever : public Result::Reader::Consumer
+{
+public:
+  ResultRetriever(Context& ctx);
+
+  virtual void on_header(CacheEntryReader& cache_entry_reader);
+  virtual void on_entry_start(uint32_t entry_number,
+                              Result::FileType file_type,
+                              uint64_t file_len,
+                              nonstd::optional<std::string> raw_file);
+  virtual void on_entry_data(const uint8_t* data, size_t size);
+  virtual void on_entry_end();
+
+private:
+  Context& m_ctx;
+  bool m_dest_stderr = false;
+  Result::FileType m_dest_file_type;
+  Fd m_dest_fd;
+  std::string m_dest_path;
+  std::string m_stderr_text;
+};
index cd6ff77fff5e1c1a4c7f71fac35ca229a26bf48e..685dbc468456b2fb4c0235c8b53e0eb11f3b5fd1 100644 (file)
@@ -28,6 +28,9 @@
 #include "FormatNonstdStringView.hpp"
 #include "MiniTrace.hpp"
 #include "ProgressBar.hpp"
+#include "Result.hpp"
+#include "ResultDumper.hpp"
+#include "ResultRetriever.hpp"
 #include "SignalHandler.hpp"
 #include "StdMakeUnique.hpp"
 #include "Util.hpp"
@@ -42,7 +45,6 @@
 #include "language.hpp"
 #include "logging.hpp"
 #include "manifest.hpp"
-#include "result.hpp"
 #include "stats.hpp"
 
 #include "third_party/fmt/core.h"
@@ -955,33 +957,39 @@ to_cache(Context& ctx,
   if (!st) {
     failed(STATS_ERROR);
   }
-  ResultFileMap result_file_map;
+
+  auto orig_dest_stat = Stat::stat(ctx.result_path());
+  Result::Writer result_writer(ctx, ctx.result_path());
+
   if (st.size() > 0) {
-    result_file_map.emplace(FileType::stderr_output, tmp_stderr);
+    result_writer.write(Result::FileType::stderr_output, tmp_stderr);
   }
-  result_file_map.emplace(FileType::object, ctx.args_info.output_obj);
+  result_writer.write(Result::FileType::object, ctx.args_info.output_obj);
   if (ctx.args_info.generating_dependencies) {
-    result_file_map.emplace(FileType::dependency, ctx.args_info.output_dep);
+    result_writer.write(Result::FileType::dependency, ctx.args_info.output_dep);
   }
   if (ctx.args_info.generating_coverage) {
-    result_file_map.emplace(FileType::coverage, ctx.args_info.output_cov);
+    result_writer.write(Result::FileType::coverage, ctx.args_info.output_cov);
   }
   if (ctx.args_info.generating_stackusage) {
-    result_file_map.emplace(FileType::stackusage, ctx.args_info.output_su);
+    result_writer.write(Result::FileType::stackusage, ctx.args_info.output_su);
   }
   if (ctx.args_info.generating_diagnostics) {
-    result_file_map.emplace(FileType::diagnostic, ctx.args_info.output_dia);
+    result_writer.write(Result::FileType::diagnostic, ctx.args_info.output_dia);
   }
   if (ctx.args_info.seen_split_dwarf && Stat::stat(ctx.args_info.output_dwo)) {
-    // Only copy .dwo file if it was created by the compiler (GCC and Clang
+    // Only store .dwo file if it was created by the compiler (GCC and Clang
     // behave differently e.g. for "-gsplit-dwarf -g1").
-    result_file_map.emplace(FileType::dwarf_object, ctx.args_info.output_dwo);
+    result_writer.write(Result::FileType::dwarf_object,
+                        ctx.args_info.output_dwo);
   }
 
-  auto orig_dest_stat = Stat::stat(ctx.result_path());
-  result_put(ctx, ctx.result_path(), result_file_map);
-
-  cc_log("Stored in cache: %s", ctx.result_path().c_str());
+  auto error = result_writer.finalize();
+  if (error) {
+    cc_log("Error: %s", error->c_str());
+  } else {
+    cc_log("Stored in cache: %s", ctx.result_path().c_str());
+  }
 
   auto new_dest_stat = Stat::stat(ctx.result_path(), Stat::OnError::log);
   if (!new_dest_stat) {
@@ -1376,7 +1384,7 @@ calculate_result_name(Context& ctx,
   bool found_ccbin = false;
 
   hash_delimiter(hash, "result version");
-  hash_int(hash, k_result_version);
+  hash_int(hash, Result::k_version);
 
   if (direct_mode) {
     hash_delimiter(hash, "manifest version");
@@ -1676,49 +1684,19 @@ from_cache(Context& ctx, enum fromcache_call_mode mode)
 
   MTR_BEGIN("cache", "from_cache");
 
-  bool produce_dep_file = ctx.args_info.generating_dependencies
-                          && ctx.args_info.output_dep != "/dev/null";
-
-  MTR_BEGIN("file", "file_get");
-
   // Get result from cache.
-  const auto tmp_stderr_fd_and_path = Util::create_temp_fd(
-    fmt::format("{}/tmp.stderr", ctx.config.temporary_dir()));
-  close(tmp_stderr_fd_and_path.first);
-  const std::string& tmp_stderr = tmp_stderr_fd_and_path.second;
-  ctx.register_pending_tmp_file(tmp_stderr);
+  Result::Reader result_reader(ctx.result_path());
+  ResultRetriever result_retriever(ctx);
 
-  ResultFileMap result_file_map;
-  if (ctx.args_info.output_obj != "/dev/null") {
-    result_file_map.emplace(FileType::object, ctx.args_info.output_obj);
-    if (ctx.args_info.seen_split_dwarf) {
-      result_file_map.emplace(FileType::dwarf_object, ctx.args_info.output_dwo);
-    }
-  }
-  result_file_map.emplace(FileType::stderr_output, tmp_stderr);
-  if (produce_dep_file) {
-    result_file_map.emplace(FileType::dependency, ctx.args_info.output_dep);
-  }
-  if (ctx.args_info.generating_coverage) {
-    result_file_map.emplace(FileType::coverage, ctx.args_info.output_cov);
-  }
-  if (ctx.args_info.generating_stackusage) {
-    result_file_map.emplace(FileType::stackusage, ctx.args_info.output_su);
-  }
-  if (ctx.args_info.generating_diagnostics) {
-    result_file_map.emplace(FileType::diagnostic, ctx.args_info.output_dia);
-  }
-  bool ok = result_get(ctx, ctx.result_path(), result_file_map);
-  if (!ok) {
-    cc_log("Failed to get result from cache");
+  auto error = result_reader.read(result_retriever);
+  if (error) {
+    cc_log("Failed to get result from cache: %s", error->c_str());
     return nullopt;
+  } else {
+    // Update modification timestamp to save file from LRU cleanup.
+    update_mtime(ctx.result_path().c_str());
   }
 
-  MTR_END("file", "file_get");
-
-  Util::send_to_stderr(Util::read_file(tmp_stderr),
-                       ctx.args_info.strip_diagnostics_colors);
-
   cc_log("Succeeded getting cached result");
 
   MTR_END("cache", "from_cache");
@@ -2248,8 +2226,15 @@ handle_main_options(int argc, const char* const* argv)
     case DUMP_MANIFEST:
       return manifest_dump(optarg, stdout) ? 0 : 1;
 
-    case DUMP_RESULT:
-      return result_dump(ctx, optarg, stdout) ? 0 : 1;
+    case DUMP_RESULT: {
+      ResultDumper result_dumper(stdout);
+      Result::Reader result_reader(optarg);
+      auto error = result_reader.read(result_dumper);
+      if (error) {
+        fmt::print(stderr, "Error: {}\n", *error);
+      }
+      return error ? EXIT_FAILURE : EXIT_SUCCESS;
+    }
 
     case HASH_FILE: {
       struct hash* hash = hash_init();
index cef4b06a48a8e1d46790d49290e11208509c3778..065c58791f50597aab4183a1b0a5f6e167c792fe 100644 (file)
 #include "CacheEntryWriter.hpp"
 #include "Context.hpp"
 #include "File.hpp"
+#include "Result.hpp"
 #include "StdMakeUnique.hpp"
 #include "ThreadPool.hpp"
+#include "logging.hpp"
 #include "manifest.hpp"
-#include "result.hpp"
 #include "stats.hpp"
 
 #include "third_party/fmt/core.h"
@@ -55,7 +56,7 @@ create_reader(const CacheFile& cache_file, FILE* stream)
   switch (cache_file.type()) {
   case CacheFile::Type::result:
     return std::make_unique<CacheEntryReader>(
-      stream, k_result_magic, k_result_version);
+      stream, Result::k_magic, Result::k_version);
 
   case CacheFile::Type::manifest:
     return std::make_unique<CacheEntryReader>(
diff --git a/src/result.cpp b/src/result.cpp
deleted file mode 100644 (file)
index da90d75..0000000
+++ /dev/null
@@ -1,527 +0,0 @@
-// Copyright (C) 2019-2020 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "result.hpp"
-
-#include "AtomicFile.hpp"
-#include "CacheEntryReader.hpp"
-#include "CacheEntryWriter.hpp"
-#include "Config.hpp"
-#include "Context.hpp"
-#include "File.hpp"
-#include "Stat.hpp"
-#include "Util.hpp"
-#include "exceptions.hpp"
-#include "stats.hpp"
-
-// Result data format
-// ==================
-//
-// Integers are big-endian.
-//
-// <result>               ::= <header> <body> <epilogue>
-// <header>               ::= <magic> <version> <compr_type> <compr_level>
-//                            <content_len>
-// <magic>                ::= 4 bytes ("cCrS")
-// <version>              ::= uint8_t
-// <compr_type>           ::= <compr_none> | <compr_zstd>
-// <compr_none>           ::= 0 (uint8_t)
-// <compr_zstd>           ::= 1 (uint8_t)
-// <compr_level>          ::= int8_t
-// <content_len>          ::= uint64_t ; size of file if stored uncompressed
-// <body>                 ::= <n_entries> <entry>* ; potentially compressed
-// <n_entries>            ::= uint8_t
-// <entry>                ::= <embedded_file_entry> | <raw_file_entry>
-// <embedded_file_entry>  ::= <embedded_file_marker> <suffix_len> <suffix>
-//                            <data_len> <data>
-// <embedded_file_marker> ::= 0 (uint8_t)
-// <embedded_file_type>   ::= uint8_t
-// <data_len>             ::= uint64_t
-// <data>                 ::= data_len bytes
-// <raw_file_entry>       ::= <raw_file_marker> <suffix_len> <suffix> <file_len>
-// <raw_file_marker>      ::= 1 (uint8_t)
-// <file_len>             ::= uint64_t
-// <epilogue>             ::= <checksum>
-// <checksum>             ::= uint64_t ; XXH64 of content bytes
-//
-// Sketch of concrete layout:
-//
-// <magic>                4 bytes
-// <version>              1 byte
-// <compr_type>           1 byte
-// <compr_level>          1 byte
-// <content_len>          8 bytes
-// --- [potentially compressed from here] -------------------------------------
-// <n_entries>            1 byte
-// <embedded_file_marker> 1 byte
-// <embedded_file_type>   1 byte
-// <data_len>             8 bytes
-// <data>                 data_len bytes
-// ...
-// <ref_marker>           1 byte
-// <key_len>              1 byte
-// <key>                  key_len bytes
-// ...
-// checksum               8 bytes
-//
-//
-// Version history
-// ===============
-//
-// 1: Introduced in ccache 4.0.
-
-const uint8_t k_result_magic[4] = {'c', 'C', 'r', 'S'};
-const uint8_t k_result_version = 1;
-
-// File data stored inside the result file.
-const uint8_t k_embedded_file_marker = 0;
-
-// File stored as-is in the file system.
-const uint8_t k_raw_file_marker = 1;
-
-using ReadEntryFunction = void (*)(const Context& ctx,
-                                   CacheEntryReader& reader,
-                                   const std::string& result_path_in_cache,
-                                   uint32_t entry_number,
-                                   const ResultFileMap* result_file_map,
-                                   FILE* dump_stream);
-
-using WriteEntryFunction =
-  void (*)(Context& ctx,
-           CacheEntryWriter& writer,
-           const std::string& result_path_in_cache,
-           uint32_t entry_number,
-           const ResultFileMap::value_type& suffix_and_path);
-
-static const char*
-UnderlyingFileTypeIntToString(UnderlyingFileTypeInt underlying_type)
-{
-  switch (FileType(underlying_type)) {
-  case FileType::object:
-    return ".o";
-
-  case FileType::dependency:
-    return ".d";
-
-  case FileType::stderr_output:
-    return "<stderr>";
-
-  case FileType::coverage:
-    return ".cov";
-
-  case FileType::stackusage:
-    return ".su";
-
-  case FileType::diagnostic:
-    return ".dia";
-
-  case FileType::dwarf_object:
-    return ".dwo";
-  }
-
-  return "<unknown type>";
-}
-
-static void
-read_embedded_file_entry(const Context& /*ctx*/,
-                         CacheEntryReader& reader,
-                         const std::string& /*result_path_in_cache*/,
-                         uint32_t entry_number,
-                         const ResultFileMap* result_file_map,
-                         FILE* dump_stream)
-{
-  UnderlyingFileTypeInt type;
-  reader.read(type);
-
-  uint64_t file_len;
-  reader.read(file_len);
-
-  bool content_read = false;
-  if (dump_stream) {
-    fmt::print(dump_stream,
-               "Embedded file #{}: {} ({} bytes)\n",
-               entry_number,
-               UnderlyingFileTypeIntToString(type),
-               file_len);
-  } else {
-    cc_log("Retrieving embedded file #%u %s (%llu bytes)",
-           entry_number,
-           UnderlyingFileTypeIntToString(type),
-           (unsigned long long)file_len);
-
-    const auto it = result_file_map->find(FileType(type));
-    if (it == result_file_map->end()) {
-      cc_log("Not copying");
-    } else if (it->second == "/dev/null") {
-      cc_log("Not copying to /dev/null");
-    } else {
-      content_read = true;
-
-      const auto& path = it->second;
-      cc_log("Copying to %s", path.c_str());
-
-      File subfile(path, "wb");
-      if (!subfile) {
-        throw Error(fmt::format(
-          "Failed to open {} for writing: {}", path, strerror(errno)));
-      }
-      int subfile_fd = fileno(subfile.get());
-
-      uint8_t buf[READ_BUFFER_SIZE];
-      size_t remain = file_len;
-      while (remain > 0) {
-        size_t n = std::min(remain, sizeof(buf));
-        reader.read(buf, n);
-
-        // Write directly to the file descriptor to avoid stdio caching.
-        if (!write_fd(subfile_fd, buf, n)) {
-          throw Error(fmt::format("Failed to write to {}", path));
-        }
-        remain -= n;
-      }
-    }
-  }
-
-  if (!content_read) {
-    // Discard the file data.
-    uint8_t buf[READ_BUFFER_SIZE];
-    size_t remain = file_len;
-    while (remain > 0) {
-      size_t n = std::min(remain, sizeof(buf));
-      reader.read(buf, n);
-      remain -= n;
-    }
-  }
-}
-
-static std::string
-get_raw_file_path(const std::string& result_path_in_cache,
-                  uint32_t entry_number)
-{
-  return fmt::format("{:{}}_{}.raw",
-                     result_path_in_cache.c_str(),
-                     result_path_in_cache.length() - 7, // .result
-                     entry_number);
-}
-
-static void
-read_raw_file_entry(const Context& ctx,
-                    CacheEntryReader& reader,
-                    const std::string& result_path_in_cache,
-                    uint32_t entry_number,
-                    const ResultFileMap* result_file_map,
-                    std::FILE* dump_stream)
-{
-  UnderlyingFileTypeInt type;
-  reader.read(type);
-
-  uint64_t file_len;
-  reader.read(file_len);
-
-  if (dump_stream) {
-    fmt::print(dump_stream,
-               "Raw file #{}: {} ({} bytes)\n",
-               entry_number,
-               UnderlyingFileTypeIntToString(type),
-               file_len);
-  } else {
-    cc_log("Retrieving raw file #%u %s (%llu bytes)",
-           entry_number,
-           UnderlyingFileTypeIntToString(type),
-           (unsigned long long)file_len);
-
-    auto raw_path = get_raw_file_path(result_path_in_cache, entry_number);
-    auto st = Stat::stat(raw_path, Stat::OnError::throw_error);
-    if (st.size() != file_len) {
-      throw Error(
-        fmt::format("Bad file size of {} (actual {} bytes, expected {} bytes)",
-                    raw_path,
-                    st.size(),
-                    file_len));
-    }
-
-    const auto it = result_file_map->find(FileType(type));
-    if (it == result_file_map->end()) {
-      cc_log("Not copying");
-    } else if (it->second == "/dev/null") {
-      cc_log("Not copying to /dev/null");
-    } else {
-      const auto& dest_path = it->second;
-      if (!Util::clone_hard_link_or_copy_file(
-            ctx, raw_path, dest_path, false)) {
-        throw Error(
-          fmt::format("Failed to copy raw file {} to {}", raw_path, dest_path));
-      }
-      // Update modification timestamp to save the file from LRU cleanup
-      // (and, if hard-linked, to make the object file newer than the source
-      // file).
-      update_mtime(raw_path.c_str());
-    }
-  }
-}
-
-static bool
-read_result(const Context& ctx,
-            const std::string& path,
-            const ResultFileMap* result_file_map,
-            FILE* dump_stream)
-{
-  File file(path, "rb");
-  if (!file) {
-    // Cache miss.
-    return false;
-  }
-
-  CacheEntryReader reader(file.get(), k_result_magic, k_result_version);
-
-  if (dump_stream) {
-    reader.dump_header(dump_stream);
-  }
-
-  uint8_t n_entries;
-  reader.read(n_entries);
-
-  uint32_t i;
-  for (i = 0; i < n_entries; ++i) {
-    uint8_t marker;
-    reader.read(marker);
-
-    ReadEntryFunction read_entry;
-
-    switch (marker) {
-    case k_embedded_file_marker:
-      read_entry = read_embedded_file_entry;
-      break;
-
-    case k_raw_file_marker:
-      read_entry = read_raw_file_entry;
-      break;
-
-    default:
-      throw Error(fmt::format("Unknown entry type: {}", marker));
-    }
-
-    read_entry(ctx, reader, path, i, result_file_map, dump_stream);
-  }
-
-  if (i != n_entries) {
-    throw Error(
-      fmt::format("Too few entries (read {}, expected {})", i, n_entries));
-  }
-
-  reader.finalize();
-  return true;
-}
-
-static void
-write_embedded_file_entry(Context& /*ctx*/,
-                          CacheEntryWriter& writer,
-                          const std::string& /*result_path_in_cache*/,
-                          uint32_t entry_number,
-                          const ResultFileMap::value_type& suffix_and_path)
-{
-  auto type = UnderlyingFileTypeInt(suffix_and_path.first);
-  const auto& source_path = suffix_and_path.second;
-
-  uint64_t source_file_size =
-    Stat::stat(source_path, Stat::OnError::throw_error).size();
-
-  cc_log("Storing embedded file #%u %s (%llu bytes) from %s",
-         entry_number,
-         UnderlyingFileTypeIntToString(type),
-         (unsigned long long)source_file_size,
-         source_path.c_str());
-
-  writer.write<uint8_t>(k_embedded_file_marker);
-  writer.write(type);
-  writer.write(source_file_size);
-
-  File file(source_path, "rb");
-  if (!file) {
-    throw Error(fmt::format("Failed to open {} for reading", source_path));
-  }
-
-  uint64_t remain = source_file_size;
-  while (remain > 0) {
-    uint8_t buf[READ_BUFFER_SIZE];
-    size_t n = std::min(remain, static_cast<uint64_t>(sizeof(buf)));
-    if (fread(buf, n, 1, file.get()) != 1) {
-      throw Error(fmt::format("Error reading from {}", source_path));
-    }
-    writer.write(buf, n);
-    remain -= n;
-  }
-}
-
-static void
-write_raw_file_entry(Context& ctx,
-                     CacheEntryWriter& writer,
-                     const std::string& result_path_in_cache,
-                     uint32_t entry_number,
-                     const ResultFileMap::value_type& suffix_and_path)
-{
-  auto type = UnderlyingFileTypeInt(suffix_and_path.first);
-  const auto& source_path = suffix_and_path.second;
-
-  uint64_t source_file_size =
-    Stat::stat(source_path, Stat::OnError::throw_error).size();
-
-  cc_log("Storing raw file #%u %s (%llu bytes) from %s",
-         entry_number,
-         UnderlyingFileTypeIntToString(type),
-         (unsigned long long)source_file_size,
-         source_path.c_str());
-
-  writer.write<uint8_t>(k_raw_file_marker);
-  writer.write(type);
-  writer.write(source_file_size);
-
-  auto raw_file = get_raw_file_path(result_path_in_cache, entry_number);
-  auto old_stat = Stat::stat(raw_file);
-  if (!Util::clone_hard_link_or_copy_file(ctx, source_path, raw_file, true)) {
-    throw Error(
-      fmt::format("Failed to store {} as raw file {}", source_path, raw_file));
-  }
-  auto new_stat = Stat::stat(raw_file);
-
-  stats_update_size(ctx.counter_updates,
-                    new_stat.size_on_disk() - old_stat.size_on_disk(),
-                    (new_stat ? 1 : 0) - (old_stat ? 1 : 0));
-}
-
-static bool
-should_store_raw_file(const Config& config, FileType type)
-{
-  if (!config.file_clone() && !config.hard_link()) {
-    return false;
-  }
-
-  // Only store object files as raw files since there are several problems with
-  // storing other file types:
-  //
-  // 1. The compiler unlinks object files before writing to them but it doesn't
-  //    unlink .d files, so just it's possible to corrupt .d files just by
-  //    running the compiler (see ccache issue 599).
-  // 2. .d files cause trouble for automake if hard-linked (see ccache issue
-  //    378).
-  // 3. It's unknown how the compiler treats other file types, so better safe
-  //    than sorry.
-  //
-  // It would be possible to store all files in raw form for the file_clone case
-  // and only hard link object files. However, most likely it's only object
-  // files that become large enough that it's of interest to clone or hard link
-  // them, so we keep things simple for now. This will also save i-nodes in the
-  // cache.
-  return type == FileType::object;
-}
-
-static void
-write_result(Context& ctx,
-             const std::string& path,
-             const ResultFileMap& result_file_map)
-{
-  uint64_t payload_size = 0;
-  payload_size += 1; // n_entries
-  for (const auto& pair : result_file_map) {
-    const auto& result_file = pair.second;
-    auto st = Stat::stat(result_file, Stat::OnError::throw_error);
-    payload_size += 1;         // embedded_file_marker
-    payload_size += 1;         // embedded_file_type
-    payload_size += 8;         // data_len
-    payload_size += st.size(); // data
-  }
-
-  AtomicFile atomic_result_file(path, AtomicFile::Mode::binary);
-  CacheEntryWriter writer(atomic_result_file.stream(),
-                          k_result_magic,
-                          k_result_version,
-                          Compression::type_from_config(ctx.config),
-                          Compression::level_from_config(ctx.config),
-                          payload_size);
-
-  writer.write<uint8_t>(result_file_map.size());
-
-  size_t entry_number = 0;
-  for (const auto& pair : result_file_map) {
-    const auto& suffix = pair.first;
-    WriteEntryFunction write_entry = should_store_raw_file(ctx.config, suffix)
-                                       ? write_raw_file_entry
-                                       : write_embedded_file_entry;
-    write_entry(ctx, writer, path, entry_number, pair);
-    ++entry_number;
-  }
-
-  writer.finalize();
-  atomic_result_file.commit();
-}
-
-bool
-result_get(const Context& ctx,
-           const std::string& path,
-           const ResultFileMap& result_file_map)
-{
-  cc_log("Getting result %s", path.c_str());
-
-  try {
-    bool cache_hit = read_result(ctx, path, &result_file_map, nullptr);
-    if (cache_hit) {
-      // Update modification timestamp to save files from LRU cleanup.
-      update_mtime(path.c_str());
-    } else {
-      cc_log("No such result file");
-    }
-    return cache_hit;
-  } catch (const Error& e) {
-    cc_log("Error: %s", e.what());
-    return false;
-  }
-}
-
-bool
-result_put(Context& ctx,
-           const std::string& path,
-           const ResultFileMap& result_file_map)
-{
-  cc_log("Storing result %s", path.c_str());
-
-  try {
-    write_result(ctx, path, result_file_map);
-    return true;
-  } catch (const Error& e) {
-    cc_log("Error: %s", e.what());
-    return false;
-  }
-}
-
-bool
-result_dump(const Context& ctx, const std::string& path, FILE* stream)
-{
-  assert(stream);
-
-  try {
-    if (read_result(ctx, path, nullptr, stream)) {
-      return true;
-    } else {
-      fmt::print(stream, "Error: No such file: {}\n", path);
-    }
-  } catch (const Error& e) {
-    fmt::print(stream, "Error: {}\n", e.what());
-  }
-
-  return false;
-}
diff --git a/src/result.hpp b/src/result.hpp
deleted file mode 100644 (file)
index 02351c6..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2019 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include "system.hpp"
-
-#include "logging.hpp"
-
-#include <map>
-#include <string>
-
-class Context;
-
-extern const uint8_t k_result_magic[4];
-extern const uint8_t k_result_version;
-
-using UnderlyingFileTypeInt = uint8_t;
-enum class FileType : UnderlyingFileTypeInt {
-  // These values are written into the cache result file. This means they must
-  // never be changed or removed unless the result file version is incremented.
-  // Adding new values is OK.
-  object = 0,
-  dependency = 1,
-  stderr_output = 2,
-  coverage = 3,
-  stackusage = 4,
-  diagnostic = 5,
-  dwarf_object = 6,
-};
-
-using ResultFileMap = std::map<FileType, std::string /*path*/>;
-
-bool result_get(const Context& ctx,
-                const std::string& path,
-                const ResultFileMap& result_file_map);
-bool result_put(Context& ctx,
-                const std::string& path,
-                const ResultFileMap& result_file_map);
-bool result_dump(const Context& ctx, const std::string& path, FILE* stream);