NullCompressor.cpp
NullDecompressor.cpp
ProgressBar.cpp
+ Result.cpp
+ ResultDumper.cpp
+ ResultRetriever.cpp
SignalHandler.cpp
Stat.cpp
ThreadPool.cpp
legacy_util.cpp
logging.cpp
manifest.cpp
- result.cpp
stats.cpp
version.cpp)
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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()
+{
+}
--- /dev/null
+// 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;
+};
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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;
+};
#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"
#include "language.hpp"
#include "logging.hpp"
#include "manifest.hpp"
-#include "result.hpp"
#include "stats.hpp"
#include "third_party/fmt/core.h"
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) {
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");
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");
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();
#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"
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>(
+++ /dev/null
-// 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;
-}
+++ /dev/null
-// 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);