]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Context: move g_config
authorThomas Otto <thomas.otto@pdv-fs.de>
Sun, 26 Jan 2020 20:34:40 +0000 (21:34 +0100)
committerThomas Otto <thomas.otto@pdv-fs.de>
Sun, 16 Feb 2020 12:20:15 +0000 (13:20 +0100)
23 files changed:
src/Compression.cpp
src/Compression.hpp
src/Config.cpp
src/Config.hpp
src/Context.hpp
src/ccache.cpp
src/ccache.hpp
src/compress.cpp
src/compress.hpp
src/execute.cpp
src/execute.hpp
src/hashutil.cpp
src/hashutil.hpp
src/manifest.cpp
src/manifest.hpp
src/result.cpp
src/result.hpp
src/stats.cpp
src/stats.hpp
unittest/framework.cpp
unittest/test_Compression.cpp
unittest/test_argument_processing.cpp
unittest/test_hashutil.cpp

index ecf93fc370393c82a189751a6c573462e3335123..81e3cce409dad3fd724cda5bb9c4fa2e91163b26 100644 (file)
 #include "Compression.hpp"
 
 #include "Config.hpp"
+#include "Context.hpp"
 #include "exceptions.hpp"
 
 namespace Compression {
 
 int8_t
-level_from_config()
+level_from_config(const Config& config)
 {
-  return g_config.compression() ? g_config.compression_level() : 0;
+  return config.compression() ? config.compression_level() : 0;
 }
 
 Type
-type_from_config()
+type_from_config(const Config& config)
 {
-  return g_config.compression() ? Type::zstd : Type::none;
+  return config.compression() ? Type::zstd : Type::none;
 }
 
 Type
index ac973d999adb6ac0d8e05d1be7ae6179174aacaf..905173dbee66b75adbe5a9d14a0ddfeb9bd133be 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <string>
 
+class Config;
+
 namespace Compression {
 
 enum class Type : uint8_t {
@@ -29,9 +31,9 @@ enum class Type : uint8_t {
   zstd = 1,
 };
 
-int8_t level_from_config();
+int8_t level_from_config(const Config& config);
 
-Type type_from_config();
+Type type_from_config(const Config& config);
 
 Type type_from_int(uint8_t type);
 
index a759adee8ad46638c42022e3c5adb2b6f78bf03c..d3bead7d48a1b061b16380179bc2035ef4ef2a46 100644 (file)
@@ -34,8 +34,6 @@
 using nonstd::nullopt;
 using nonstd::optional;
 
-Config g_config;
-
 namespace {
 
 enum class ConfigItem {
index dabf238c6d2713fe8045f0352459bd53155f69da..ba16f10e43358687dcf8dec949e847b29a16128f 100644 (file)
@@ -31,7 +31,6 @@
 #include <unordered_map>
 
 class Config;
-extern Config g_config;
 
 class Config
 {
index 770c257b7a03296f22afb6c24193925f264d520b..4fd8187eafe86c4b42b5b4940deb8eedd2d6834c 100644 (file)
@@ -21,6 +21,7 @@
 #include "system.hpp"
 
 #include "ArgsInfo.hpp"
+#include "Config.hpp"
 #include "NonCopyable.hpp"
 
 struct Context : NonCopyable
@@ -29,4 +30,5 @@ struct Context : NonCopyable
   ~Context();
 
   ArgsInfo args_info;
+  Config config;
 };
index d83c05e2d972b0d365375c4565bb0dbb7160193c..5a55b7446b9a82322d2c428e4a7743aadee73640 100644 (file)
@@ -157,7 +157,7 @@ static pid_t compiler_pid = 0;
 static const char HASH_PREFIX[] = "3";
 
 static void
-add_prefix(struct args* args, const char* prefix_command)
+add_prefix(Context& ctx, struct args* args, const char* prefix_command)
 {
   if (str_eq(prefix_command, "")) {
     return;
@@ -170,7 +170,7 @@ add_prefix(struct args* args, const char* prefix_command)
        tok = strtok_r(NULL, " ", &saveptr)) {
     char* p;
 
-    p = find_executable(tok, MYNAME);
+    p = find_executable(ctx, tok, MYNAME);
     if (!p) {
       fatal("%s: %s", tok, strerror(errno));
     }
@@ -197,15 +197,15 @@ failed(enum stats stat)
 }
 
 static const char*
-temp_dir()
+temp_dir(Context& ctx)
 {
   static const char* path = NULL;
   if (path) {
     return path; // Memoize
   }
-  path = g_config.temporary_dir().c_str();
+  path = ctx.config.temporary_dir().c_str();
   if (str_eq(path, "")) {
-    path = format("%s/tmp", g_config.cache_dir().c_str());
+    path = format("%s/tmp", ctx.config.cache_dir().c_str());
   }
   return path;
 }
@@ -325,18 +325,18 @@ set_up_signal_handlers(void)
 #endif // _WIN32
 
 static void
-clean_up_internal_tempdir(void)
+clean_up_internal_tempdir(Context& ctx)
 {
   time_t now = time(NULL);
-  auto st = Stat::stat(g_config.cache_dir(), Stat::OnError::log);
+  auto st = Stat::stat(ctx.config.cache_dir(), Stat::OnError::log);
   if (!st || st.mtime() + k_tempdir_cleanup_interval >= now) {
     // No cleanup needed.
     return;
   }
 
-  update_mtime(g_config.cache_dir().c_str());
+  update_mtime(ctx.config.cache_dir().c_str());
 
-  DIR* dir = opendir(temp_dir());
+  DIR* dir = opendir(temp_dir(ctx));
   if (!dir) {
     return;
   }
@@ -347,7 +347,7 @@ clean_up_internal_tempdir(void)
       continue;
     }
 
-    char* path = format("%s/%s", temp_dir(), entry->d_name);
+    char* path = format("%s/%s", temp_dir(ctx), entry->d_name);
     st = Stat::lstat(path, Stat::OnError::log);
     if (st && st.mtime() + k_tempdir_cleanup_interval < now) {
       tmp_unlink(path);
@@ -368,7 +368,7 @@ static void
 dump_debug_log_buffer_exitfn(void* context)
 {
   Context& ctx = *static_cast<Context*>(context);
-  if (!g_config.debug()) {
+  if (!ctx.config.debug()) {
     return;
   }
 
@@ -377,13 +377,14 @@ dump_debug_log_buffer_exitfn(void* context)
 }
 
 static void
-init_hash_debug(struct hash* hash,
+init_hash_debug(Context& ctx,
+                struct hash* hash,
                 const char* obj_path,
                 char type,
                 const char* section_name,
                 FILE* debug_text_file)
 {
-  if (!g_config.debug()) {
+  if (!ctx.config.debug()) {
     return;
   }
 
@@ -454,7 +455,7 @@ do_remember_include_file(Context& ctx,
     return true;
   }
 
-  if (system && (g_config.sloppiness() & SLOPPY_SYSTEM_HEADERS)) {
+  if (system && (ctx.config.sloppiness() & SLOPPY_SYSTEM_HEADERS)) {
     // Don't remember this system header.
     return true;
   }
@@ -516,14 +517,14 @@ do_remember_include_file(Context& ctx,
   // The comparison using >= is intentional, due to a possible race between
   // starting compilation and writing the include file. See also the notes
   // under "Performance" in doc/MANUAL.adoc.
-  if (!(g_config.sloppiness() & SLOPPY_INCLUDE_FILE_MTIME)
+  if (!(ctx.config.sloppiness() & SLOPPY_INCLUDE_FILE_MTIME)
       && st.mtime() >= time_of_compilation) {
     cc_log("Include file %s too new", path.c_str());
     return false;
   }
 
   // The same >= logic as above applies to the change time of the file.
-  if (!(g_config.sloppiness() & SLOPPY_INCLUDE_FILE_CTIME)
+  if (!(ctx.config.sloppiness() & SLOPPY_INCLUDE_FILE_CTIME)
       && st.ctime() >= time_of_compilation) {
     cc_log("Include file %s ctime too new", path.c_str());
     return false;
@@ -540,7 +541,7 @@ do_remember_include_file(Context& ctx,
       cc_log("Detected use of precompiled header: %s", path.c_str());
     }
     bool using_pch_sum = false;
-    if (g_config.pch_external_checksum()) {
+    if (ctx.config.pch_external_checksum()) {
       // hash pch.sum instead of pch when it exists
       // to prevent hashing a very large .pch file every time
       std::string pch_sum_path = fmt::format("{}.sum", path);
@@ -560,7 +561,7 @@ do_remember_include_file(Context& ctx,
     hash_string(cpp_hash, pch_digest);
   }
 
-  if (g_config.direct_mode()) {
+  if (ctx.config.direct_mode()) {
     if (!is_pch) { // else: the file has already been hashed.
       char* source = NULL;
       size_t size;
@@ -574,7 +575,7 @@ do_remember_include_file(Context& ctx,
       }
 
       int result =
-        hash_source_code_string(g_config, fhash, source, size, path.c_str());
+        hash_source_code_string(ctx.config, fhash, source, size, path.c_str());
       free(source);
       if (result & HASH_SOURCE_CODE_ERROR
           || result & HASH_SOURCE_CODE_FOUND_TIME) {
@@ -608,9 +609,9 @@ remember_include_file(Context& ctx,
                       struct hash* depend_mode_hash)
 {
   if (!do_remember_include_file(ctx, path, cpp_hash, system, depend_mode_hash)
-      && g_config.direct_mode()) {
+      && ctx.config.direct_mode()) {
     cc_log("Disabling direct mode");
-    g_config.set_direct_mode(false);
+    ctx.config.set_direct_mode(false);
   }
 }
 
@@ -625,10 +626,10 @@ print_included_files(FILE* fp)
 // Make a relative path from current working directory to path if path is under
 // the base directory.
 static std::string
-make_relative_path(const char* path)
+make_relative_path(Context& ctx, const char* path)
 {
-  if (g_config.base_dir().empty()
-      || !str_startswith(path, g_config.base_dir().c_str())) {
+  if (ctx.config.base_dir().empty()
+      || !str_startswith(path, ctx.config.base_dir().c_str())) {
     return path;
   }
 
@@ -719,9 +720,9 @@ process_preprocessed_file(Context& ctx,
 
   ignore_headers = NULL;
   ignore_headers_len = 0;
-  if (!g_config.ignore_headers_in_manifest().empty()) {
+  if (!ctx.config.ignore_headers_in_manifest().empty()) {
     char *header, *p, *q, *saveptr = NULL;
-    p = x_strdup(g_config.ignore_headers_in_manifest().c_str());
+    p = x_strdup(ctx.config.ignore_headers_in_manifest().c_str());
     q = p;
     while ((header = strtok_r(q, PATH_DELIM, &saveptr))) {
       ignore_headers = static_cast<char**>(
@@ -831,11 +832,11 @@ process_preprocessed_file(Context& ctx,
         has_absolute_include_headers = is_absolute_path(inc_path);
       }
       char* saved_inc_path = inc_path;
-      inc_path = x_strdup(make_relative_path(inc_path).c_str());
+      inc_path = x_strdup(make_relative_path(ctx, inc_path).c_str());
       free(saved_inc_path);
 
       bool should_hash_inc_path = true;
-      if (!g_config.hash_dir()) {
+      if (!ctx.config.hash_dir()) {
         if (str_startswith(inc_path, cwd) && str_endswith(inc_path, "//")) {
           // When compiling with -g or similar, GCC adds the absolute path to
           // CWD like this:
@@ -890,7 +891,7 @@ process_preprocessed_file(Context& ctx,
   // Explicitly check the .gch/.pch/.pth file as Clang does not include any
   // mention of it in the preprocessed output.
   if (included_pch_file) {
-    std::string pch_path = make_relative_path(included_pch_file);
+    std::string pch_path = make_relative_path(ctx, included_pch_file);
     hash_string(hash, pch_path);
     remember_include_file(ctx, pch_path, hash, false, NULL);
   }
@@ -909,7 +910,7 @@ use_relative_paths_in_depfile(Context& ctx)
 {
   const char* depfile = ctx.args_info.output_dep.c_str();
 
-  if (g_config.base_dir().empty()) {
+  if (ctx.config.base_dir().empty()) {
     cc_log("Base dir not set, skip using relative paths");
     return; // nothing to do
   }
@@ -938,8 +939,8 @@ use_relative_paths_in_depfile(Context& ctx)
     while (token) {
       char* relpath = nullptr;
       if (is_absolute_path(token)
-          && str_startswith(token, g_config.base_dir().c_str())) {
-        relpath = x_strdup(make_relative_path(token).c_str());
+          && str_startswith(token, ctx.config.base_dir().c_str())) {
+        relpath = x_strdup(make_relative_path(ctx, token).c_str());
         result = true;
       } else {
         relpath = token;
@@ -1018,7 +1019,7 @@ result_name_from_depfile(Context& ctx, struct hash* hash)
       if (!has_absolute_include_headers) {
         has_absolute_include_headers = is_absolute_path(token);
       }
-      std::string path = make_relative_path(token);
+      std::string path = make_relative_path(ctx, token);
       remember_include_file(ctx, path, hash, false, hash);
     }
   }
@@ -1028,7 +1029,7 @@ result_name_from_depfile(Context& ctx, struct hash* hash)
   // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the
   // dependencies output.
   if (included_pch_file) {
-    std::string pch_path = make_relative_path(included_pch_file);
+    std::string pch_path = make_relative_path(ctx, included_pch_file);
     hash_string(hash, pch_path);
     remember_include_file(ctx, pch_path, hash, false, NULL);
   }
@@ -1056,10 +1057,10 @@ send_cached_stderr(const char* path_stderr)
 
 // Create or update the manifest file.
 static void
-update_manifest_file(void)
+update_manifest_file(Context& ctx)
 {
-  if (!g_config.direct_mode() || g_config.read_only()
-      || g_config.read_only_direct()) {
+  if (!ctx.config.direct_mode() || ctx.config.read_only()
+      || ctx.config.read_only_direct()) {
     return;
   }
 
@@ -1067,17 +1068,21 @@ update_manifest_file(void)
 
   // See comment in get_file_hash_index for why saving of timestamps is forced
   // for precompiled headers.
-  bool save_timestamp = (g_config.sloppiness() & SLOPPY_FILE_STAT_MATCHES)
+  bool save_timestamp = (ctx.config.sloppiness() & SLOPPY_FILE_STAT_MATCHES)
                         || output_is_precompiled_header;
 
   MTR_BEGIN("manifest", "manifest_put");
   cc_log("Adding result name to %s", manifest_path);
-  if (!manifest_put(
-        manifest_path, *cached_result_name, g_included_files, save_timestamp)) {
+  if (!manifest_put(ctx.config,
+                    manifest_path,
+                    *cached_result_name,
+                    g_included_files,
+                    save_timestamp)) {
     cc_log("Failed to add result name to %s", manifest_path);
   } else {
     auto st = Stat::stat(manifest_path, Stat::OnError::log);
-    stats_update_size(manifest_stats_file,
+    stats_update_size(ctx,
+                      manifest_stats_file,
                       st.size_on_disk() - old_st.size_on_disk(),
                       !old_st && st ? 1 : 0);
   }
@@ -1085,19 +1090,19 @@ update_manifest_file(void)
 }
 
 static void
-update_cached_result_globals(struct digest* result_name)
+update_cached_result_globals(Context& ctx, struct digest* result_name)
 {
   char result_name_string[DIGEST_STRING_BUFFER_SIZE];
   digest_as_string(result_name, result_name_string);
   cached_result_name = result_name;
   cached_result_path =
-    x_strdup(Util::get_path_in_cache(g_config.cache_dir(),
-                                     g_config.cache_dir_levels(),
+    x_strdup(Util::get_path_in_cache(ctx.config.cache_dir(),
+                                     ctx.config.cache_dir_levels(),
                                      result_name_string,
                                      ".result")
                .c_str());
-  stats_file =
-    format("%s/%c/stats", g_config.cache_dir().c_str(), result_name_string[0]);
+  stats_file = format(
+    "%s/%c/stats", ctx.config.cache_dir().c_str(), result_name_string[0]);
 }
 
 static bool
@@ -1142,7 +1147,7 @@ to_cache(Context& ctx,
   args_add(args, "-o");
   args_add(args, ctx.args_info.output_obj.c_str());
 
-  if (g_config.hard_link()) {
+  if (ctx.config.hard_link()) {
     // Workaround for Clang bug where it overwrites an existing object file
     // when it's compiling an assembler file, see
     // <https://bugs.llvm.org/show_bug.cgi?id=39782>.
@@ -1161,7 +1166,7 @@ to_cache(Context& ctx,
   x_unsetenv("DEPENDENCIES_OUTPUT");
   x_unsetenv("SUNPRO_DEPENDENCIES");
 
-  if (g_config.run_second_cpp()) {
+  if (ctx.config.run_second_cpp()) {
     args_add(args, ctx.args_info.input_file.c_str());
   } else {
     args_add(args, i_tmpfile);
@@ -1187,18 +1192,18 @@ to_cache(Context& ctx,
   char* tmp_stderr;
   int tmp_stderr_fd;
   int status;
-  if (!g_config.depend_mode()) {
-    tmp_stdout = format("%s/tmp.stdout", temp_dir());
+  if (!ctx.config.depend_mode()) {
+    tmp_stdout = format("%s/tmp.stdout", temp_dir(ctx));
     tmp_stdout_fd = create_tmp_fd(&tmp_stdout);
-    tmp_stderr = format("%s/tmp.stderr", temp_dir());
+    tmp_stderr = format("%s/tmp.stderr", temp_dir(ctx));
     tmp_stderr_fd = create_tmp_fd(&tmp_stderr);
     status = execute(args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid);
     args_pop(args, 3);
   } else {
     // The cached result path is not known yet, use temporary files.
-    tmp_stdout = format("%s/tmp.stdout", temp_dir());
+    tmp_stdout = format("%s/tmp.stdout", temp_dir(ctx));
     tmp_stdout_fd = create_tmp_fd(&tmp_stdout);
-    tmp_stderr = format("%s/tmp.stderr", temp_dir());
+    tmp_stderr = format("%s/tmp.stderr", temp_dir(ctx));
     tmp_stderr_fd = create_tmp_fd(&tmp_stderr);
 
     // Use the original arguments (including dependency options) in depend
@@ -1209,7 +1214,7 @@ to_cache(Context& ctx,
     if (depend_extra_args) {
       args_extend(depend_mode_args, depend_extra_args);
     }
-    add_prefix(depend_mode_args, g_config.prefix_command().c_str());
+    add_prefix(ctx, depend_mode_args, ctx.config.prefix_command().c_str());
 
     time_of_compilation = time(NULL);
     status = execute(
@@ -1300,14 +1305,14 @@ to_cache(Context& ctx,
     failed();
   }
 
-  if (g_config.depend_mode()) {
+  if (ctx.config.depend_mode()) {
     struct digest* result_name =
       result_name_from_depfile(ctx, depend_mode_hash);
     if (!result_name) {
       stats_update(STATS_ERROR);
       failed();
     }
-    update_cached_result_globals(result_name);
+    update_cached_result_globals(ctx, result_name);
   }
 
   bool produce_dep_file = ctx.args_info.generating_dependencies
@@ -1358,7 +1363,7 @@ to_cache(Context& ctx,
   }
 
   auto orig_dest_stat = Stat::stat(cached_result_path);
-  result_put(cached_result_path, result_file_map);
+  result_put(ctx, cached_result_path, result_file_map);
 
   cc_log("Stored in cache: %s", cached_result_path);
 
@@ -1367,7 +1372,8 @@ to_cache(Context& ctx,
     stats_update(STATS_ERROR);
     failed();
   }
-  stats_update_size(stats_file,
+  stats_update_size(ctx,
+                    stats_file,
                     new_dest_stat.size_on_disk()
                       - orig_dest_stat.size_on_disk(),
                     orig_dest_stat ? 0 : 1);
@@ -1391,7 +1397,7 @@ to_cache(Context& ctx,
     // Remove any CACHEDIR.TAG on the cache_dir level where it was located in
     // previous ccache versions.
     if (getpid() % 1000 == 0) {
-      char* path = format("%s/CACHEDIR.TAG", g_config.cache_dir().c_str());
+      char* path = format("%s/CACHEDIR.TAG", ctx.config.cache_dir().c_str());
       x_unlink(path);
       free(path);
     }
@@ -1401,7 +1407,7 @@ to_cache(Context& ctx,
   send_cached_stderr(tmp_stderr);
   tmp_unlink(tmp_stderr);
 
-  update_manifest_file();
+  update_manifest_file(ctx);
 
   free(tmp_stderr);
   free(tmp_stdout);
@@ -1430,22 +1436,22 @@ get_result_name_from_cpp(Context& ctx, struct args* args, struct hash* hash)
     string_view input_base =
       Util::get_truncated_base_name(ctx.args_info.input_file, 10);
     path_stdout =
-      x_strdup(fmt::format("{}/{}.stdout", temp_dir(), input_base).c_str());
+      x_strdup(fmt::format("{}/{}.stdout", temp_dir(ctx), input_base).c_str());
     int path_stdout_fd = create_tmp_fd(&path_stdout);
     add_pending_tmp_file(path_stdout);
 
-    path_stderr = format("%s/tmp.cpp_stderr", temp_dir());
+    path_stderr = format("%s/tmp.cpp_stderr", temp_dir(ctx));
     int path_stderr_fd = create_tmp_fd(&path_stderr);
     add_pending_tmp_file(path_stderr);
 
     int args_added = 2;
     args_add(args, "-E");
-    if (g_config.keep_comments_cpp()) {
+    if (ctx.config.keep_comments_cpp()) {
       args_add(args, "-C");
       args_added = 3;
     }
     args_add(args, ctx.args_info.input_file.c_str());
-    add_prefix(args, g_config.prefix_command_cpp().c_str());
+    add_prefix(ctx, args, ctx.config.prefix_command_cpp().c_str());
     cc_log("Running preprocessor");
     MTR_BEGIN("execute", "preprocessor");
     status = execute(args->argv, path_stdout_fd, path_stderr_fd, &compiler_pid);
@@ -1479,12 +1485,13 @@ get_result_name_from_cpp(Context& ctx, struct args* args, struct hash* hash)
   } else {
     // i_tmpfile needs the proper cpp_extension for the compiler to do its
     // thing correctly
-    i_tmpfile = format("%s.%s", path_stdout, g_config.cpp_extension().c_str());
+    i_tmpfile =
+      format("%s.%s", path_stdout, ctx.config.cpp_extension().c_str());
     x_rename(path_stdout, i_tmpfile);
     add_pending_tmp_file(i_tmpfile);
   }
 
-  if (g_config.run_second_cpp()) {
+  if (ctx.config.run_second_cpp()) {
     free(path_stderr);
   } else {
     // If we are using the CPP trick, we need to remember this stderr data and
@@ -1502,29 +1509,30 @@ get_result_name_from_cpp(Context& ctx, struct args* args, struct hash* hash)
 // Hash mtime or content of a file, or the output of a command, according to
 // the CCACHE_COMPILERCHECK setting.
 static void
-hash_compiler(struct hash* hash,
+hash_compiler(Context& ctx,
+              struct hash* hash,
               const Stat& st,
               const char* path,
               bool allow_command)
 {
-  if (g_config.compiler_check() == "none") {
+  if (ctx.config.compiler_check() == "none") {
     // Do nothing.
-  } else if (g_config.compiler_check() == "mtime") {
+  } else if (ctx.config.compiler_check() == "mtime") {
     hash_delimiter(hash, "cc_mtime");
     hash_int(hash, st.size());
     hash_int(hash, st.mtime());
-  } else if (Util::starts_with(g_config.compiler_check(), "string:")) {
+  } else if (Util::starts_with(ctx.config.compiler_check(), "string:")) {
     hash_delimiter(hash, "cc_hash");
-    hash_string(hash, g_config.compiler_check().c_str() + strlen("string:"));
-  } else if (g_config.compiler_check() == "content" || !allow_command) {
+    hash_string(hash, ctx.config.compiler_check().c_str() + strlen("string:"));
+  } else if (ctx.config.compiler_check() == "content" || !allow_command) {
     hash_delimiter(hash, "cc_content");
     hash_file(hash, path);
   } else { // command string
     bool ok = hash_multicommand_output(
-      hash, g_config.compiler_check().c_str(), orig_args->argv[0]);
+      ctx, hash, ctx.config.compiler_check().c_str(), orig_args->argv[0]);
     if (!ok) {
       fatal("Failure running compiler check command: %s",
-            g_config.compiler_check().c_str());
+            ctx.config.compiler_check().c_str());
     }
   }
 }
@@ -1535,7 +1543,8 @@ hash_compiler(struct hash* hash,
 // with -ccbin/--compiler-bindir. If they are NULL, the compilers are looked up
 // in PATH instead.
 static void
-hash_nvcc_host_compiler(struct hash* hash,
+hash_nvcc_host_compiler(Context& ctx,
+                        struct hash* hash,
                         const Stat* ccbin_st,
                         const char* ccbin)
 {
@@ -1564,20 +1573,20 @@ hash_nvcc_host_compiler(struct hash* hash,
         char* path = format("%s/%s", ccbin, compilers[i]);
         auto st = Stat::stat(path);
         if (st) {
-          hash_compiler(hash, st, path, false);
+          hash_compiler(ctx, hash, st, path, false);
         }
         free(path);
       } else {
-        char* path = find_executable(compilers[i], MYNAME);
+        char* path = find_executable(ctx, compilers[i], MYNAME);
         if (path) {
           auto st = Stat::stat(path, Stat::OnError::log);
-          hash_compiler(hash, st, ccbin, false);
+          hash_compiler(ctx, hash, st, ccbin, false);
           free(path);
         }
       }
     }
   } else {
-    hash_compiler(hash, *ccbin_st, ccbin, false);
+    hash_compiler(ctx, hash, *ccbin_st, ccbin, false);
   }
 }
 
@@ -1593,7 +1602,7 @@ hash_common_info(Context& ctx,
   // We have to hash the extension, as a .i file isn't treated the same by the
   // compiler as a .ii file.
   hash_delimiter(hash, "ext");
-  hash_string(hash, g_config.cpp_extension().c_str());
+  hash_string(hash, ctx.config.cpp_extension().c_str());
 
 #ifdef _WIN32
   const char* ext = strrchr(args->argv[0], '.');
@@ -1612,7 +1621,7 @@ hash_common_info(Context& ctx,
   }
 
   // Hash information about the compiler.
-  hash_compiler(hash, st, args->argv[0], true);
+  hash_compiler(ctx, hash, st, args->argv[0], true);
 
   // Also hash the compiler name as some compilers use hard links and behave
   // differently depending on the real name.
@@ -1620,7 +1629,7 @@ hash_common_info(Context& ctx,
   string_view base = Util::base_name(args->argv[0]);
   hash_string_view(hash, base);
 
-  if (!(g_config.sloppiness() & SLOPPY_LOCALE)) {
+  if (!(ctx.config.sloppiness() & SLOPPY_LOCALE)) {
     // Hash environment variables that may affect localization of compiler
     // warning messages.
     const char* envvars[] = {"LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", NULL};
@@ -1634,7 +1643,7 @@ hash_common_info(Context& ctx,
   }
 
   // Possibly hash the current working directory.
-  if (args_info.generating_debuginfo && g_config.hash_dir()) {
+  if (args_info.generating_debuginfo && ctx.config.hash_dir()) {
     char* cwd = get_cwd();
     for (size_t i = 0; i < args_info.debug_prefix_maps_len; i++) {
       char* map = args_info.debug_prefix_maps[i];
@@ -1701,8 +1710,8 @@ hash_common_info(Context& ctx,
     }
   }
 
-  if (!g_config.extra_files_to_hash().empty()) {
-    char* p = x_strdup(g_config.extra_files_to_hash().c_str());
+  if (!ctx.config.extra_files_to_hash().empty()) {
+    char* p = x_strdup(ctx.config.extra_files_to_hash().c_str());
     char* q = p;
     char* path;
     char* saveptr = NULL;
@@ -1848,7 +1857,7 @@ calculate_result_name(Context& ctx,
         // If given an explicit specs file, then hash that file, but don't
         // include the path to it in the hash.
         hash_delimiter(hash, "specs");
-        hash_compiler(hash, st, p, false);
+        hash_compiler(ctx, hash, st, p, false);
         continue;
       }
     }
@@ -1857,7 +1866,7 @@ calculate_result_name(Context& ctx,
       auto st = Stat::stat(args->argv[i] + 9, Stat::OnError::log);
       if (st) {
         hash_delimiter(hash, "plugin");
-        hash_compiler(hash, st, args->argv[i] + 9, false);
+        hash_compiler(ctx, hash, st, args->argv[i] + 9, false);
         continue;
       }
     }
@@ -1868,7 +1877,7 @@ calculate_result_name(Context& ctx,
       auto st = Stat::stat(args->argv[i + 3], Stat::OnError::log);
       if (st) {
         hash_delimiter(hash, "plugin");
-        hash_compiler(hash, st, args->argv[i + 3], false);
+        hash_compiler(ctx, hash, st, args->argv[i + 3], false);
         i += 3;
         continue;
       }
@@ -1881,7 +1890,7 @@ calculate_result_name(Context& ctx,
       if (st) {
         found_ccbin = true;
         hash_delimiter(hash, "ccbin");
-        hash_nvcc_host_compiler(hash, &st, args->argv[i + 1]);
+        hash_nvcc_host_compiler(ctx, hash, &st, args->argv[i + 1]);
         i++;
         continue;
       }
@@ -1905,7 +1914,7 @@ calculate_result_name(Context& ctx,
   }
 
   if (!found_ccbin && ctx.args_info.actual_language == "cu") {
-    hash_nvcc_host_compiler(hash, NULL, NULL);
+    hash_nvcc_host_compiler(ctx, hash, NULL, NULL);
   }
 
   // For profile generation (-fprofile-arcs, -fprofile-generate):
@@ -1982,31 +1991,31 @@ calculate_result_name(Context& ctx,
 
     hash_delimiter(hash, "sourcecode");
     int result =
-      hash_source_code_file(g_config, hash, ctx.args_info.input_file.c_str());
+      hash_source_code_file(ctx.config, hash, ctx.args_info.input_file.c_str());
     if (result & HASH_SOURCE_CODE_ERROR) {
       stats_update(STATS_ERROR);
       failed();
     }
     if (result & HASH_SOURCE_CODE_FOUND_TIME) {
       cc_log("Disabling direct mode");
-      g_config.set_direct_mode(false);
+      ctx.config.set_direct_mode(false);
       return NULL;
     }
 
     char manifest_name_string[DIGEST_STRING_BUFFER_SIZE];
     hash_result_as_string(hash, manifest_name_string);
     manifest_path =
-      x_strdup(Util::get_path_in_cache(g_config.cache_dir(),
-                                       g_config.cache_dir_levels(),
+      x_strdup(Util::get_path_in_cache(ctx.config.cache_dir(),
+                                       ctx.config.cache_dir_levels(),
                                        manifest_name_string,
                                        ".manifest")
                  .c_str());
     manifest_stats_file = format(
-      "%s/%c/stats", g_config.cache_dir().c_str(), manifest_name_string[0]);
+      "%s/%c/stats", ctx.config.cache_dir().c_str(), manifest_name_string[0]);
 
     cc_log("Looking for result name in %s", manifest_path);
     MTR_BEGIN("manifest", "manifest_get");
-    result_name = manifest_get(g_config, manifest_path);
+    result_name = manifest_get(ctx, manifest_path);
     MTR_END("manifest", "manifest_get");
     if (result_name) {
       cc_log("Got result name from manifest");
@@ -2045,7 +2054,7 @@ from_cache(Context& ctx,
            bool put_result_in_manifest)
 {
   // The user might be disabling cache hits.
-  if (g_config.recache()) {
+  if (ctx.config.recache()) {
     return;
   }
 
@@ -2070,7 +2079,7 @@ from_cache(Context& ctx,
   MTR_BEGIN("file", "file_get");
 
   // Get result from cache.
-  char* tmp_stderr = format("%s/tmp.stderr", temp_dir());
+  char* tmp_stderr = format("%s/tmp.stderr", temp_dir(ctx));
   int tmp_stderr_fd = create_tmp_fd(&tmp_stderr);
   close(tmp_stderr_fd);
 
@@ -2094,7 +2103,7 @@ from_cache(Context& ctx,
   if (ctx.args_info.generating_diagnostics) {
     result_file_map.emplace(FileType::diagnostic, ctx.args_info.output_dia);
   }
-  bool ok = result_get(cached_result_path, result_file_map);
+  bool ok = result_get(ctx, cached_result_path, result_file_map);
   if (!ok) {
     cc_log("Failed to get result from cache");
     tmp_unlink(tmp_stderr);
@@ -2107,7 +2116,7 @@ from_cache(Context& ctx,
   send_cached_stderr(tmp_stderr);
 
   if (put_result_in_manifest) {
-    update_manifest_file();
+    update_manifest_file(ctx);
   }
 
   tmp_unlink(tmp_stderr);
@@ -2135,7 +2144,7 @@ from_cache(Context& ctx,
 // Find the real compiler. We just search the PATH to find an executable of the
 // same name that isn't a link to ourselves.
 static void
-find_compiler(char** argv)
+find_compiler(Context& ctx, char** argv)
 {
   // We might be being invoked like "ccache gcc -c foo.c".
   std::string base(Util::base_name(argv[0]));
@@ -2149,11 +2158,11 @@ find_compiler(char** argv)
   }
 
   // Support user override of the compiler.
-  if (!g_config.compiler().empty()) {
-    base = g_config.compiler();
+  if (!ctx.config.compiler().empty()) {
+    base = ctx.config.compiler();
   }
 
-  char* compiler = find_executable(base.c_str(), MYNAME);
+  char* compiler = find_executable(ctx, base.c_str(), MYNAME);
   if (!compiler) {
     stats_update(STATS_COMPILER);
     fatal("Could not find compiler \"%s\" in PATH", base.c_str());
@@ -2240,13 +2249,15 @@ detect_pch(const char* option, const char* arg, bool* found_pch)
 //
 // Returns true on success, otherwise false.
 bool
-cc_process_args(ArgsInfo& args_info,
-                Config& config,
+cc_process_args(Context& ctx,
                 struct args* args,
                 struct args** preprocessor_args,
                 struct args** extra_args_to_hash,
                 struct args** compiler_args)
 {
+  ArgsInfo& args_info = ctx.args_info;
+  Config& config = ctx.config;
+
   bool found_c_opt = false;
   bool found_dc_opt = false;
   bool found_S_opt = false;
@@ -2475,7 +2486,7 @@ cc_process_args(ArgsInfo& args_info,
                argv[i]);
         stats_update(STATS_CANTUSEMODULES);
         return false;
-      } else if (!(g_config.sloppiness() & SLOPPY_MODULES)) {
+      } else if (!(ctx.config.sloppiness() & SLOPPY_MODULES)) {
         cc_log(
           "You have to specify \"modules\" sloppiness when using"
           " -fmodules to get hits");
@@ -2532,14 +2543,14 @@ cc_process_args(ArgsInfo& args_info,
         stats_update(STATS_ARGS);
         return false;
       }
-      args_info.output_obj = make_relative_path(argv[i + 1]);
+      args_info.output_obj = make_relative_path(ctx, argv[i + 1]);
       i++;
       continue;
     }
 
     // Alternate form of -o with no space. Nvcc does not support this.
     if (str_startswith(argv[i], "-o") && guessed_compiler != GUESSED_NVCC) {
-      args_info.output_obj = make_relative_path(&argv[i][2]);
+      args_info.output_obj = make_relative_path(ctx, &argv[i][2]);
       continue;
     }
 
@@ -2616,7 +2627,7 @@ cc_process_args(ArgsInfo& args_info,
           ++arg;
         }
       }
-      args_info.output_dep = make_relative_path(arg);
+      args_info.output_dep = make_relative_path(ctx, arg);
       // Keep the format of the args the same.
       if (separate_argument) {
         args_add(dep_args, "-MF");
@@ -2639,12 +2650,12 @@ cc_process_args(ArgsInfo& args_info,
           return false;
         }
         args_add(dep_args, argv[i]);
-        std::string relpath = make_relative_path(argv[i + 1]);
+        std::string relpath = make_relative_path(ctx, argv[i + 1]);
         args_add(dep_args, relpath.c_str());
         i++;
       } else {
         char* arg_opt = x_strndup(argv[i], 3);
-        std::string relpath = make_relative_path(argv[i] + 3);
+        std::string relpath = make_relative_path(ctx, argv[i] + 3);
         char* option = format("%s%s", arg_opt, relpath.c_str());
         args_add(dep_args, option);
         free(arg_opt);
@@ -2689,7 +2700,7 @@ cc_process_args(ArgsInfo& args_info,
       continue;
     }
     if (str_startswith(argv[i], "--sysroot=")) {
-      std::string relpath = make_relative_path(argv[i] + 10);
+      std::string relpath = make_relative_path(ctx, argv[i] + 10);
       std::string option = fmt::format("--sysroot={}", relpath);
       args_add(common_args, option.c_str());
       continue;
@@ -2702,7 +2713,7 @@ cc_process_args(ArgsInfo& args_info,
         return false;
       }
       args_add(common_args, argv[i]);
-      std::string relpath = make_relative_path(argv[i + 1]);
+      std::string relpath = make_relative_path(ctx, argv[i + 1]);
       args_add(common_args, relpath.c_str());
       i++;
       continue;
@@ -2732,14 +2743,14 @@ cc_process_args(ArgsInfo& args_info,
                  && !strchr(argv[i] + 8, ',')) {
         args_info.generating_dependencies = true;
         dependency_filename_specified = true;
-        args_info.output_dep = make_relative_path(argv[i] + 8);
+        args_info.output_dep = make_relative_path(ctx, argv[i] + 8);
         args_add(dep_args, argv[i]);
         continue;
       } else if (str_startswith(argv[i], "-Wp,-MMD,")
                  && !strchr(argv[i] + 9, ',')) {
         args_info.generating_dependencies = true;
         dependency_filename_specified = true;
-        args_info.output_dep = make_relative_path(argv[i] + 9);
+        args_info.output_dep = make_relative_path(ctx, argv[i] + 9);
         args_add(dep_args, argv[i]);
         continue;
       } else if (str_startswith(argv[i], "-Wp,-D")
@@ -2785,7 +2796,7 @@ cc_process_args(ArgsInfo& args_info,
         return false;
       }
       args_info.generating_diagnostics = true;
-      args_info.output_dia = make_relative_path(argv[i + 1]);
+      args_info.output_dia = make_relative_path(ctx, argv[i + 1]);
       i++;
       continue;
     }
@@ -2898,7 +2909,7 @@ cc_process_args(ArgsInfo& args_info,
         return false;
       }
 
-      std::string relpath = make_relative_path(argv[i + 1]);
+      std::string relpath = make_relative_path(ctx, argv[i + 1]);
       if (compopt_affects_cpp(argv[i])) {
         args_add(cpp_args, argv[i]);
         args_add(cpp_args, relpath.c_str());
@@ -2918,7 +2929,7 @@ cc_process_args(ArgsInfo& args_info,
       if (slash_pos) {
         char* option = x_strndup(argv[i], slash_pos - argv[i]);
         if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) {
-          std::string relpath = make_relative_path(slash_pos);
+          std::string relpath = make_relative_path(ctx, slash_pos);
           char* new_option = format("%s%s", option, relpath.c_str());
           if (compopt_affects_cpp(option)) {
             args_add(cpp_args, new_option);
@@ -3013,7 +3024,7 @@ cc_process_args(ArgsInfo& args_info,
       args_info.input_file = from_cstr(argv[i]);
     } else {
       // Rewrite to relative to increase hit rate.
-      args_info.input_file = make_relative_path(argv[i]);
+      args_info.input_file = make_relative_path(ctx, argv[i]);
     }
   } // for
 
@@ -3040,7 +3051,7 @@ cc_process_args(ArgsInfo& args_info,
       char* saveptr = nullptr;
       char* abspath_file = strtok_r(dependencies_env, " ", &saveptr);
 
-      args_info.output_dep = make_relative_path(abspath_file);
+      args_info.output_dep = make_relative_path(ctx, abspath_file);
 
       // specifying target object is optional.
       char* abspath_obj = strtok_r(nullptr, " ", &saveptr);
@@ -3048,7 +3059,7 @@ cc_process_args(ArgsInfo& args_info,
         // it's the "file target" form.
 
         dependency_target_specified = true;
-        std::string relpath_obj = make_relative_path(abspath_obj);
+        std::string relpath_obj = make_relative_path(ctx, abspath_obj);
         // ensure compiler gets relative path.
         std::string relpath_both =
           fmt::format("{} {}", args_info.output_dep, relpath_obj);
@@ -3250,8 +3261,9 @@ cc_process_args(ArgsInfo& args_info,
     if (!dependency_filename_specified) {
       std::string default_depfile_name =
         Util::change_extension(args_info.output_obj, ".d");
-      args_info.output_dep = make_relative_path(default_depfile_name.c_str());
-      if (!g_config.run_second_cpp()) {
+      args_info.output_dep =
+        make_relative_path(ctx, default_depfile_name.c_str());
+      if (!ctx.config.run_second_cpp()) {
         // If we're compiling preprocessed code we're sending dep_args to the
         // preprocessor so we need to use -MF to write to the correct .d file
         // location since the preprocessor doesn't know the final object path.
@@ -3261,7 +3273,7 @@ cc_process_args(ArgsInfo& args_info,
     }
 
     if (!dependency_target_specified && !dependency_implicit_target_specified
-        && !g_config.run_second_cpp()) {
+        && !ctx.config.run_second_cpp()) {
       // If we're compiling preprocessed code we're sending dep_args to the
       // preprocessor so we need to use -MQ to get the correct target object
       // file in the .d file.
@@ -3272,12 +3284,12 @@ cc_process_args(ArgsInfo& args_info,
   if (args_info.generating_coverage) {
     std::string gcda_path =
       Util::change_extension(args_info.output_obj, ".gcno");
-    args_info.output_cov = make_relative_path(gcda_path.c_str());
+    args_info.output_cov = make_relative_path(ctx, gcda_path.c_str());
   }
   if (args_info.generating_stackusage) {
     std::string default_sufile_name =
       Util::change_extension(args_info.output_obj, ".su");
-    args_info.output_su = make_relative_path(default_sufile_name.c_str());
+    args_info.output_su = make_relative_path(ctx, default_sufile_name.c_str());
   }
 
   *compiler_args = args_copy(common_args);
@@ -3324,7 +3336,7 @@ cc_process_args(ArgsInfo& args_info,
   *preprocessor_args = args_copy(common_args);
   args_extend(*preprocessor_args, cpp_args);
 
-  if (g_config.run_second_cpp()) {
+  if (ctx.config.run_second_cpp()) {
     // When not compiling the preprocessed source code, only pass dependency
     // arguments to the compiler to avoid having to add -MQ, supporting e.g.
     // EDG-based compilers which don't support -MQ.
@@ -3337,7 +3349,7 @@ cc_process_args(ArgsInfo& args_info,
   }
 
   *extra_args_to_hash = compiler_only_args;
-  if (g_config.run_second_cpp()) {
+  if (ctx.config.run_second_cpp()) {
     args_extend(*extra_args_to_hash, dep_args);
   }
 
@@ -3345,15 +3357,15 @@ cc_process_args(ArgsInfo& args_info,
 }
 
 static void
-create_initial_config_file(const std::string& path)
+create_initial_config_file(Config& config)
 {
-  if (!Util::create_dir(Util::dir_name(path))) {
+  if (!Util::create_dir(Util::dir_name(config.primary_config_path()))) {
     return;
   }
 
   unsigned max_files;
   uint64_t max_size;
-  char* stats_dir = format("%s/0", g_config.cache_dir().c_str());
+  char* stats_dir = format("%s/0", config.cache_dir().c_str());
   if (Stat::stat(stats_dir)) {
     stats_get_obsolete_limits(stats_dir, &max_files, &max_size);
     // STATS_MAXFILES and STATS_MAXSIZE was stored for each top directory.
@@ -3361,23 +3373,23 @@ create_initial_config_file(const std::string& path)
     max_size *= 16;
   } else {
     max_files = 0;
-    max_size = g_config.max_size();
+    max_size = config.max_size();
   }
   free(stats_dir);
 
-  FILE* f = fopen(path.c_str(), "w");
+  FILE* f = fopen(config.primary_config_path().c_str(), "w");
   if (!f) {
     return;
   }
   if (max_files != 0) {
     fprintf(f, "max_files = %u\n", max_files);
-    g_config.set_max_files(max_files);
+    config.set_max_files(max_files);
   }
   if (max_size != 0) {
     char* size = format_parsable_size_with_suffix(max_size);
     fprintf(f, "max_size = %s\n", size);
     free(size);
-    g_config.set_max_size(max_size);
+    config.set_max_size(max_size);
   }
   fclose(f);
 }
@@ -3478,11 +3490,11 @@ set_up_config(Config& config)
   MTR_END("config", "conf_update_from_environment");
 
   if (should_create_initial_config) {
-    create_initial_config_file(config.primary_config_path());
+    create_initial_config_file(config);
   }
 
-  if (g_config.umask() != std::numeric_limits<uint32_t>::max()) {
-    umask(g_config.umask());
+  if (config.umask() != std::numeric_limits<uint32_t>::max()) {
+    umask(config.umask());
   }
 }
 
@@ -3494,13 +3506,13 @@ initialize()
   // which run after main(). It is cleaned up by the last exit function.
   Context* ctx = new Context{};
 
-  set_up_config(g_config);
+  set_up_config(ctx->config);
 
-  init_log(g_config);
+  init_log(ctx->config);
 
   exitfn_init();
   exitfn_delete_context(ctx);
-  exitfn_add_nullary(stats_flush);
+  exitfn_add(stats_flush, ctx);
   exitfn_add_nullary(clean_up_pending_tmp_files);
 
   bool enable_internal_trace = getenv("CCACHE_INTERNAL_TRACE");
@@ -3536,9 +3548,9 @@ free_and_nullify(T*& ptr)
 
 // Reset the global state. Used by the test suite.
 void
-cc_reset(void)
+cc_reset(Config& config)
 {
-  g_config.clear_and_reset();
+  config.clear_and_reset();
 
   free_and_nullify(current_working_dir);
   free_and_nullify(included_pch_file);
@@ -3618,7 +3630,7 @@ cache_compilation(int argc, char* argv[])
   Context& ctx = initialize();
 
   MTR_BEGIN("main", "find_compiler");
-  find_compiler(argv);
+  find_compiler(ctx, argv);
   MTR_END("main", "find_compiler");
 
   try {
@@ -3631,7 +3643,7 @@ cache_compilation(int argc, char* argv[])
     assert(orig_args);
 
     args_strip(orig_args, "--ccache-");
-    add_prefix(orig_args, g_config.prefix_command().c_str());
+    add_prefix(ctx, orig_args, ctx.config.prefix_command().c_str());
 
     cc_log("Failed; falling back to running the real compiler");
     cc_log_argv("Executing ", orig_args->argv);
@@ -3645,16 +3657,16 @@ static void
 do_cache_compilation(Context& ctx, char* argv[])
 {
   MTR_BEGIN("main", "clean_up_internal_tempdir");
-  if (g_config.temporary_dir().empty()) {
-    clean_up_internal_tempdir();
+  if (ctx.config.temporary_dir().empty()) {
+    clean_up_internal_tempdir(ctx);
   }
   MTR_END("main", "clean_up_internal_tempdir");
 
-  if (!g_config.log_file().empty() || g_config.debug()) {
-    g_config.visit_items(configuration_logger);
+  if (!ctx.config.log_file().empty() || ctx.config.debug()) {
+    ctx.config.visit_items(configuration_logger);
   }
 
-  if (g_config.disable()) {
+  if (ctx.config.disable()) {
     cc_log("ccache is disabled");
     stats_update(STATS_CACHEMISS); // Dummy to trigger stats_flush.
     failed();
@@ -3668,8 +3680,8 @@ do_cache_compilation(Context& ctx, char* argv[])
   cc_log("Hostname: %s", get_hostname());
   cc_log("Working directory: %s", get_current_working_dir());
 
-  g_config.set_limit_multiple(
-    std::min(std::max(g_config.limit_multiple(), 0.0), 1.0));
+  ctx.config.set_limit_multiple(
+    std::min(std::max(ctx.config.limit_multiple(), 0.0), 1.0));
 
   MTR_BEGIN("main", "guess_compiler");
   guessed_compiler = guess_compiler(orig_args->argv[0]);
@@ -3684,8 +3696,7 @@ do_cache_compilation(Context& ctx, char* argv[])
   struct args* compiler_args;
   MTR_BEGIN("main", "process_args");
 
-  if (!cc_process_args(ctx.args_info,
-                       g_config,
+  if (!cc_process_args(ctx,
                        orig_args,
                        &preprocessor_args,
                        &extra_args_to_hash,
@@ -3702,12 +3713,12 @@ do_cache_compilation(Context& ctx, char* argv[])
 
   MTR_END("main", "process_args");
 
-  if (g_config.depend_mode()
+  if (ctx.config.depend_mode()
       && (!ctx.args_info.generating_dependencies
           || ctx.args_info.output_dep == "/dev/null"
-          || !g_config.run_second_cpp())) {
+          || !ctx.config.run_second_cpp())) {
     cc_log("Disabling depend mode");
-    g_config.set_depend_mode(false);
+    ctx.config.set_depend_mode(false);
   }
 
   cc_log("Source file: %s", ctx.args_info.input_file.c_str());
@@ -3734,7 +3745,7 @@ do_cache_compilation(Context& ctx, char* argv[])
   exitfn_add_last(dump_debug_log_buffer_exitfn, &ctx);
 
   FILE* debug_text_file = NULL;
-  if (g_config.debug()) {
+  if (ctx.config.debug()) {
     std::string path =
       fmt::format("{}.ccache-input-text", ctx.args_info.output_obj);
     debug_text_file = fopen(path.c_str(), "w");
@@ -3746,7 +3757,8 @@ do_cache_compilation(Context& ctx, char* argv[])
   }
 
   struct hash* common_hash = hash_init();
-  init_hash_debug(common_hash,
+  init_hash_debug(ctx,
+                  common_hash,
                   ctx.args_info.output_obj.c_str(),
                   'c',
                   "COMMON",
@@ -3758,7 +3770,8 @@ do_cache_compilation(Context& ctx, char* argv[])
 
   // Try to find the hash using the manifest.
   struct hash* direct_hash = hash_copy(common_hash);
-  init_hash_debug(direct_hash,
+  init_hash_debug(ctx,
+                  direct_hash,
                   ctx.args_info.output_obj.c_str(),
                   'd',
                   "DIRECT MODE",
@@ -3770,14 +3783,14 @@ do_cache_compilation(Context& ctx, char* argv[])
   bool put_result_in_manifest = false;
   struct digest* result_name = NULL;
   struct digest* result_name_from_manifest = NULL;
-  if (g_config.direct_mode()) {
+  if (ctx.config.direct_mode()) {
     cc_log("Trying direct lookup");
     MTR_BEGIN("hash", "direct_hash");
     result_name =
       calculate_result_name(ctx, args_to_hash, nullptr, direct_hash, true);
     MTR_END("hash", "direct_hash");
     if (result_name) {
-      update_cached_result_globals(result_name);
+      update_cached_result_globals(ctx, result_name);
 
       // If we can return from cache at this point then do so.
       from_cache(ctx, FROMCACHE_DIRECT_MODE, 0);
@@ -3793,17 +3806,18 @@ do_cache_compilation(Context& ctx, char* argv[])
     }
   }
 
-  if (g_config.read_only_direct()) {
+  if (ctx.config.read_only_direct()) {
     cc_log("Read-only direct mode; running real compiler");
     stats_update(STATS_CACHEMISS);
     failed();
   }
 
-  if (!g_config.depend_mode()) {
+  if (!ctx.config.depend_mode()) {
     // Find the hash using the preprocessed output. Also updates
     // g_included_files.
     struct hash* cpp_hash = hash_copy(common_hash);
-    init_hash_debug(cpp_hash,
+    init_hash_debug(ctx,
+                    cpp_hash,
                     ctx.args_info.output_obj.c_str(),
                     'p',
                     "PREPROCESSOR MODE",
@@ -3816,7 +3830,7 @@ do_cache_compilation(Context& ctx, char* argv[])
     if (!result_name) {
       fatal("internal error: calculate_result_name returned NULL for cpp");
     }
-    update_cached_result_globals(result_name);
+    update_cached_result_globals(ctx, result_name);
 
     if (result_name_from_manifest
         && !digests_equal(result_name_from_manifest, result_name)) {
@@ -3844,16 +3858,16 @@ do_cache_compilation(Context& ctx, char* argv[])
     from_cache(ctx, FROMCACHE_CPP_MODE, put_result_in_manifest);
   }
 
-  if (g_config.read_only()) {
+  if (ctx.config.read_only()) {
     cc_log("Read-only mode; running real compiler");
     stats_update(STATS_CACHEMISS);
     failed();
   }
 
-  add_prefix(compiler_args, g_config.prefix_command().c_str());
+  add_prefix(ctx, compiler_args, ctx.config.prefix_command().c_str());
 
   // In depend_mode, extend the direct hash.
-  struct hash* depend_mode_hash = g_config.depend_mode() ? direct_hash : NULL;
+  struct hash* depend_mode_hash = ctx.config.depend_mode() ? direct_hash : NULL;
 
   // Run real compiler, sending output to cache.
   MTR_BEGIN("cache", "to_cache");
@@ -3905,7 +3919,7 @@ handle_main_options(int argc, char* argv[])
       return manifest_dump(optarg, stdout) ? 0 : 1;
 
     case DUMP_RESULT:
-      return result_dump(optarg, stdout) ? 0 : 1;
+      return result_dump(ctx, optarg, stdout) ? 0 : 1;
 
     case HASH_FILE: {
       struct hash* hash = hash_init();
@@ -3922,13 +3936,13 @@ handle_main_options(int argc, char* argv[])
     }
 
     case PRINT_STATS:
-      stats_print();
+      stats_print(ctx.config);
       break;
 
     case 'c': // --cleanup
     {
       ProgressBar progress_bar("Cleaning...");
-      clean_up_all(g_config,
+      clean_up_all(ctx.config,
                    [&](double progress) { progress_bar.update(progress); });
       if (isatty(STDOUT_FILENO)) {
         printf("\n");
@@ -3939,7 +3953,7 @@ handle_main_options(int argc, char* argv[])
     case 'C': // --clear
     {
       ProgressBar progress_bar("Clearing...");
-      wipe_all(g_config,
+      wipe_all(ctx.config,
                [&](double progress) { progress_bar.update(progress); });
       if (isatty(STDOUT_FILENO)) {
         printf("\n");
@@ -3952,12 +3966,12 @@ handle_main_options(int argc, char* argv[])
       x_exit(0);
 
     case 'k': // --get-config
-      fmt::print("{}\n", g_config.get_string_value(optarg));
+      fmt::print("{}\n", ctx.config.get_string_value(optarg));
       break;
 
     case 'F': { // --max-files
-      g_config.set_value_in_file(
-        g_config.primary_config_path(), "max_files", optarg);
+      ctx.config.set_value_in_file(
+        ctx.config.primary_config_path(), "max_files", optarg);
       unsigned files = atoi(optarg);
       if (files == 0) {
         printf("Unset cache file limit\n");
@@ -3972,8 +3986,8 @@ handle_main_options(int argc, char* argv[])
       if (!parse_size_with_suffix(optarg, &size)) {
         fatal("invalid size: %s", optarg);
       }
-      g_config.set_value_in_file(
-        g_config.primary_config_path(), "max_size", optarg);
+      ctx.config.set_value_in_file(
+        ctx.config.primary_config_path(), "max_size", optarg);
       if (size == 0) {
         printf("Unset cache size limit\n");
       } else {
@@ -3991,17 +4005,18 @@ handle_main_options(int argc, char* argv[])
       }
       char* key = x_strndup(optarg, p - optarg);
       char* value = p + 1;
-      g_config.set_value_in_file(g_config.primary_config_path(), key, value);
+      ctx.config.set_value_in_file(
+        ctx.config.primary_config_path(), key, value);
       free(key);
       break;
     }
 
     case 'p': // --show-config
-      g_config.visit_items(configuration_printer);
+      ctx.config.visit_items(configuration_printer);
       break;
 
     case 's': // --show-stats
-      stats_summary();
+      stats_summary(ctx.config);
       break;
 
     case 'V': // --version
@@ -4011,7 +4026,7 @@ handle_main_options(int argc, char* argv[])
     case 'x': // --show-compression
     {
       ProgressBar progress_bar("Scanning...");
-      compress_stats(g_config,
+      compress_stats(ctx.config,
                      [&](double progress) { progress_bar.update(progress); });
       break;
     }
@@ -4027,19 +4042,18 @@ handle_main_options(int argc, char* argv[])
           throw Error("compression level must be between -128 and 127");
         }
         if (level == 0) {
-          level = g_config.compression_level();
+          level = ctx.config.compression_level();
         }
       }
 
       ProgressBar progress_bar("Recompressing...");
-      compress_recompress(g_config, level, [&](double progress) {
-        progress_bar.update(progress);
-      });
+      compress_recompress(
+        ctx, level, [&](double progress) { progress_bar.update(progress); });
       break;
     }
 
     case 'z': // --zero-stats
-      stats_zero();
+      stats_zero(ctx.config);
       printf("Statistics zeroed\n");
       break;
 
@@ -4050,7 +4064,7 @@ handle_main_options(int argc, char* argv[])
 
     // Some of the above switches might have changed config settings, so run the
     // setup again.
-    set_up_config(g_config);
+    set_up_config(ctx.config);
   }
 
   return 0;
index 4abc326b29c27513d447e3d1d7e00142fcbb55f9..fbe779bc5253dc146a643cc29e450bd0d78a9d29 100644 (file)
@@ -24,6 +24,7 @@
 #include "counters.hpp"
 
 struct ArgsInfo;
+struct Context;
 class Config;
 
 #ifndef MYNAME
@@ -64,11 +65,10 @@ extern time_t time_of_compilation;
 extern bool output_is_precompiled_header;
 void block_signals();
 void unblock_signals();
-bool cc_process_args(ArgsInfo& args_info,
-                     Config& config,
+bool cc_process_args(Context& ctx,
                      struct args* args,
                      struct args** preprocessor_args,
                      struct args** extra_args_to_hash,
                      struct args** compiler_args);
-void cc_reset();
+void cc_reset(Config& config);
 bool is_precompiled_header(const char* path);
index 724a5122cbad5a631fcef5d8a66a7312e4ed290a..d127e8610ce2f27eab1bcaaea5a46a0ca827211d 100644 (file)
@@ -21,6 +21,7 @@
 #include "AtomicFile.hpp"
 #include "CacheEntryReader.hpp"
 #include "CacheEntryWriter.hpp"
+#include "Context.hpp"
 #include "File.hpp"
 #include "StdMakeUnique.hpp"
 #include "ThreadPool.hpp"
@@ -84,7 +85,8 @@ create_writer(FILE* stream,
 }
 
 static void
-recompress_file(const std::string& stats_file,
+recompress_file(Context& ctx,
+                const std::string& stats_file,
                 const CacheFile& cache_file,
                 int8_t level)
 {
@@ -125,7 +127,7 @@ recompress_file(const std::string& stats_file,
   uint64_t new_size =
     Stat::stat(cache_file.path(), Stat::OnError::log).size_on_disk();
 
-  stats_update_size(stats_file.c_str(), new_size - old_size, 0);
+  stats_update_size(ctx, stats_file.c_str(), new_size - old_size, 0);
   cc_log("Recompression of %s done", cache_file.path().c_str());
 }
 
@@ -198,7 +200,7 @@ compress_stats(const Config& config,
 }
 
 void
-compress_recompress(const Config& config,
+compress_recompress(Context& ctx,
                     int8_t level,
                     const Util::ProgressReceiver& progress_receiver)
 {
@@ -207,7 +209,7 @@ compress_recompress(const Config& config,
   ThreadPool thread_pool(threads, read_ahead);
 
   Util::for_each_level_1_subdir(
-    config.cache_dir(),
+    ctx.config.cache_dir(),
     [&](const std::string& subdir,
         const Util::ProgressReceiver& sub_progress_receiver) {
       std::vector<std::shared_ptr<CacheFile>> files;
@@ -222,9 +224,9 @@ compress_recompress(const Config& config,
         const auto& file = files[i];
 
         if (file->type() != CacheFile::Type::unknown) {
-          thread_pool.enqueue([=] {
+          thread_pool.enqueue([&ctx, stats_file, file, level] {
             try {
-              recompress_file(stats_file, *file, level);
+              recompress_file(ctx, stats_file, *file, level);
             } catch (Error&) {
               // Ignore for now.
             }
index 72423be1c5e79fffccb3a23844055cb47ac74a0c..118bab51741768774ef37df6bb0fdde99392e0af 100644 (file)
 
 #include "system.hpp"
 
-#include "Config.hpp"
 #include "Util.hpp"
 
+struct Confix;
+struct Context;
+
 void compress_stats(const Config& config,
                     const Util::ProgressReceiver& progress_receiver);
 
 // Recompress the cache.
 //
 // Arguments:
-// - config: The config.
+// - ctx: The context.
 // - level: Target compression level (positive or negative value), or 0 for no
 //   compression.
 // - progress_receiver: Function that will be called for progress updates.
-void compress_recompress(const Config& config,
+void compress_recompress(Context& ctx,
                          int8_t level,
                          const Util::ProgressReceiver& progress_receiver);
index 3d5045e1140cf2eb13108e8504c60f2b373d2450..d23bef65418d0c20f948a9aad987b1abba4bffca 100644 (file)
@@ -20,6 +20,7 @@
 #include "execute.hpp"
 
 #include "Config.hpp"
+#include "Context.hpp"
 #include "Stat.hpp"
 #include "Util.hpp"
 #include "ccache.hpp"
@@ -305,13 +306,13 @@ execute(char** argv, int fd_out, int fd_err, pid_t* pid)
 // Find an executable by name in $PATH. Exclude any that are links to
 // exclude_name.
 char*
-find_executable(const char* name, const char* exclude_name)
+find_executable(Context& ctx, const char* name, const char* exclude_name)
 {
   if (is_absolute_path(name)) {
     return x_strdup(name);
   }
 
-  const char* path = g_config.path().c_str();
+  const char* path = ctx.config.path().c_str();
   if (str_eq(path, "")) {
     path = getenv("PATH");
   }
index 4e31f38848a1e6709672b70f21d6fca4acb0545c..73ffa13105fd843ecc29f62411b9454a55be9b0f 100644 (file)
@@ -20,7 +20,9 @@
 
 #include "system.hpp"
 
+struct Context;
+
 int execute(char** argv, int fd_out, int fd_err, pid_t* pid);
-char* find_executable(const char* name, const char* exclude_name);
+char* find_executable(Context& ctx, const char* name, const char* exclude_name);
 void print_command(FILE* fp, char** argv);
 char* format_command(const char* const* argv);
index 53e7f1e45d6b00cfb8bbf13928149eed6a3a4fa0..1168f4998c360143d96a3b2712537588c40b4045 100644 (file)
@@ -18,6 +18,8 @@
 
 #include "hashutil.hpp"
 
+#include "Config.hpp"
+#include "Context.hpp"
 #include "Stat.hpp"
 #include "args.hpp"
 #include "ccache.hpp"
@@ -283,7 +285,8 @@ hash_source_code_file(const Config& config, struct hash* hash, const char* path)
 }
 
 bool
-hash_command_output(struct hash* hash,
+hash_command_output(Context& ctx,
+                    struct hash* hash,
                     const char* command,
                     const char* compiler)
 {
@@ -322,7 +325,7 @@ hash_command_output(struct hash* hash,
   STARTUPINFO si;
   memset(&si, 0x00, sizeof(si));
 
-  char* path = find_executable(args->argv[0], NULL);
+  char* path = find_executable(ctx, args->argv[0], nullptr);
   if (!path) {
     path = args->argv[0];
   }
@@ -380,6 +383,8 @@ hash_command_output(struct hash* hash,
   }
   return ok;
 #else
+  (void)ctx;
+
   int pipefd[2];
   if (pipe(pipefd) == -1) {
     fatal("pipe failed");
@@ -426,7 +431,8 @@ hash_command_output(struct hash* hash,
 }
 
 bool
-hash_multicommand_output(struct hash* hash,
+hash_multicommand_output(Context& ctx,
+                         struct hash* hash,
                          const char* commands,
                          const char* compiler)
 {
@@ -436,7 +442,7 @@ hash_multicommand_output(struct hash* hash,
   char* saveptr = NULL;
   bool ok = true;
   while ((command = strtok_r(p, ";", &saveptr))) {
-    if (!hash_command_output(hash, command, compiler)) {
+    if (!hash_command_output(ctx, hash, command, compiler)) {
       ok = false;
     }
     p = NULL;
index ef3bb03ffdd90970c9f9ab7a0317b7c42dfd9589..560fae20681113e2b9a2918c5a560f6ebfc0af39 100644 (file)
 
 #include "system.hpp"
 
-#include "Config.hpp"
 #include "hash.hpp"
 
 #include <inttypes.h>
 
+class Config;
+struct Context;
+
 unsigned hash_from_int(int i);
 
 #define HASH_SOURCE_CODE_OK 0
@@ -42,9 +44,11 @@ int hash_source_code_string(const Config& config,
 int hash_source_code_file(const Config& config,
                           struct hash* hash,
                           const char* path);
-bool hash_command_output(struct hash* hash,
+bool hash_command_output(Context& ctx,
+                         struct hash* hash,
                          const char* command,
                          const char* compiler);
-bool hash_multicommand_output(struct hash* hash,
+bool hash_multicommand_output(Context& ctx,
+                              struct hash* hash,
                               const char* command,
                               const char* compiler);
index 3a969549031bcb7dc1c7f16b1359d3c93914a2a5..07d416b427f52bfcb81d247ba4a840e97f66ebdd 100644 (file)
@@ -23,6 +23,7 @@
 #include "CacheEntryWriter.hpp"
 #include "Checksum.hpp"
 #include "Config.hpp"
+#include "Context.hpp"
 #include "File.hpp"
 #include "StdMakeUnique.hpp"
 #include "ccache.hpp"
@@ -325,7 +326,9 @@ read_manifest(const std::string& path, FILE* dump_stream = nullptr)
 }
 
 static bool
-write_manifest(const std::string& path, const ManifestData& mf)
+write_manifest(const Config& config,
+               const std::string& path,
+               const ManifestData& mf)
 {
   uint64_t payload_size = 0;
   payload_size += 4; // n_files
@@ -345,8 +348,8 @@ write_manifest(const std::string& path, const ManifestData& mf)
   CacheEntryWriter writer(atomic_manifest_file.stream(),
                           k_manifest_magic,
                           k_manifest_version,
-                          Compression::type_from_config(),
-                          Compression::level_from_config(),
+                          Compression::type_from_config(config),
+                          Compression::level_from_config(config),
                           payload_size);
   writer.write<uint32_t>(mf.files.size());
   for (uint32_t i = 0; i < mf.files.size(); ++i) {
@@ -378,7 +381,7 @@ write_manifest(const std::string& path, const ManifestData& mf)
 }
 
 static bool
-verify_result(const Config& config,
+verify_result(const Context& ctx,
               const ManifestData& mf,
               const ResultEntry& result,
               std::unordered_map<std::string, FileStats>& stated_files,
@@ -416,8 +419,8 @@ verify_result(const Config& config,
       return false;
     }
 
-    if (config.sloppiness() & SLOPPY_FILE_STAT_MATCHES) {
-      if (!(config.sloppiness() & SLOPPY_FILE_STAT_MATCHES_CTIME)) {
+    if (ctx.config.sloppiness() & SLOPPY_FILE_STAT_MATCHES) {
+      if (!(ctx.config.sloppiness() & SLOPPY_FILE_STAT_MATCHES_CTIME)) {
         if (fi.mtime == fs.mtime && fi.ctime == fs.ctime) {
           cc_log("mtime/ctime hit for %s", path.c_str());
           continue;
@@ -437,7 +440,7 @@ verify_result(const Config& config,
     auto hashed_files_iter = hashed_files.find(path);
     if (hashed_files_iter == hashed_files.end()) {
       struct hash* hash = hash_init();
-      int ret = hash_source_code_file(config, hash, path.c_str());
+      int ret = hash_source_code_file(ctx.config, hash, path.c_str());
       if (ret & HASH_SOURCE_CODE_ERROR) {
         cc_log("Failed hashing %s", path.c_str());
         hash_free(hash);
@@ -465,7 +468,7 @@ verify_result(const Config& config,
 // Try to get the result name from a manifest file. Caller frees. Returns NULL
 // on failure.
 struct digest*
-manifest_get(const Config& config, const std::string& path)
+manifest_get(const Context& ctx, const std::string& path)
 {
   std::unique_ptr<ManifestData> mf;
   try {
@@ -489,7 +492,7 @@ manifest_get(const Config& config, const std::string& path)
   struct digest* name = NULL;
   for (uint32_t i = mf->results.size(); i > 0; i--) {
     if (verify_result(
-          config, *mf, mf->results[i - 1], stated_files, hashed_files)) {
+          ctx, *mf, mf->results[i - 1], stated_files, hashed_files)) {
       name = static_cast<digest*>(x_malloc(sizeof(digest)));
       *name = mf->results[i - 1].name;
       break;
@@ -502,7 +505,8 @@ manifest_get(const Config& config, const std::string& path)
 // Put the result name into a manifest file given a set of included files.
 // Returns true on success, otherwise false.
 bool
-manifest_put(const std::string& path,
+manifest_put(const Config& config,
+             const std::string& path,
              const struct digest& result_name,
              const std::unordered_map<std::string, digest>& included_files,
              bool save_timestamp)
@@ -550,7 +554,7 @@ manifest_put(const std::string& path,
   mf->add_result_entry(result_name, included_files, save_timestamp);
 
   try {
-    write_manifest(path, *mf);
+    write_manifest(config, path, *mf);
     return true;
   } catch (const Error& e) {
     cc_log("Error: %s", e.what());
index 62c3f3975d770a73aea890b6028884e35a3af1fe..1a574d54f810e2928beb615be4eb2f01aaa5631a 100644 (file)
 #include <unordered_map>
 
 class Config;
+struct Context;
 struct digest;
 
 extern const uint8_t k_manifest_magic[4];
 extern const uint8_t k_manifest_version;
 
-struct digest* manifest_get(const Config& config, const std::string& path);
-bool manifest_put(const std::string& path,
+struct digest* manifest_get(const Context& ctx, const std::string& path);
+bool manifest_put(const Config& config,
+                  const std::string& path,
                   const struct digest& result_name,
                   const std::unordered_map<std::string, digest>& included_files,
                   bool save_timestamp);
index 1985a867ab7c2698c5e3ef928ca893b1594a3f52..3685447c4bff2639d6096f0c1548259623ca4e21 100644 (file)
@@ -22,6 +22,7 @@
 #include "CacheEntryReader.hpp"
 #include "CacheEntryWriter.hpp"
 #include "Config.hpp"
+#include "Context.hpp"
 #include "File.hpp"
 #include "Stat.hpp"
 #include "Util.hpp"
@@ -95,14 +96,16 @@ 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 (*)(CacheEntryReader& reader,
+using ReadEntryFunction = void (*)(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 (*)(CacheEntryWriter& writer,
+  void (*)(Context& ctx,
+           CacheEntryWriter& writer,
            const std::string& result_path_in_cache,
            uint32_t entry_number,
            const ResultFileMap::value_type& suffix_and_path);
@@ -137,7 +140,8 @@ UnderlyingFileTypeIntToString(UnderlyingFileTypeInt underlying_type)
 }
 
 static void
-read_embedded_file_entry(CacheEntryReader& reader,
+read_embedded_file_entry(Context&,
+                         CacheEntryReader& reader,
                          const std::string& /*result_path_in_cache*/,
                          uint32_t entry_number,
                          const ResultFileMap* result_file_map,
@@ -210,16 +214,19 @@ get_raw_file_path(const std::string& result_path_in_cache,
 }
 
 static bool
-copy_raw_file(const std::string& source, const std::string& dest, bool to_cache)
+copy_raw_file(Context& ctx,
+              const std::string& source,
+              const std::string& dest,
+              bool to_cache)
 {
-  if (g_config.file_clone()) {
+  if (ctx.config.file_clone()) {
     cc_log("Cloning %s to %s", source.c_str(), dest.c_str());
     if (clone_file(source.c_str(), dest.c_str(), to_cache)) {
       return true;
     }
     cc_log("Failed to clone: %s", strerror(errno));
   }
-  if (g_config.hard_link()) {
+  if (ctx.config.hard_link()) {
     x_try_unlink(dest.c_str());
     cc_log("Hard linking %s to %s", source.c_str(), dest.c_str());
     int ret = link(source.c_str(), dest.c_str());
@@ -234,7 +241,8 @@ copy_raw_file(const std::string& source, const std::string& dest, bool to_cache)
 }
 
 static void
-read_raw_file_entry(CacheEntryReader& reader,
+read_raw_file_entry(Context& ctx,
+                    CacheEntryReader& reader,
                     const std::string& result_path_in_cache,
                     uint32_t entry_number,
                     const ResultFileMap* result_file_map,
@@ -271,7 +279,7 @@ read_raw_file_entry(CacheEntryReader& reader,
     const auto it = result_file_map->find(FileType(type));
     if (it != result_file_map->end()) {
       const auto& dest_path = it->second;
-      if (!copy_raw_file(raw_path, dest_path, false)) {
+      if (!copy_raw_file(ctx, raw_path, dest_path, false)) {
         throw Error(
           fmt::format("Failed to copy raw file {} to {}", raw_path, dest_path));
       }
@@ -284,7 +292,8 @@ read_raw_file_entry(CacheEntryReader& reader,
 }
 
 static bool
-read_result(const std::string& path,
+read_result(Context& ctx,
+            const std::string& path,
             const ResultFileMap* result_file_map,
             FILE* dump_stream)
 {
@@ -323,7 +332,7 @@ read_result(const std::string& path,
       throw Error(fmt::format("Unknown entry type: {}", marker));
     }
 
-    read_entry(reader, path, i, result_file_map, dump_stream);
+    read_entry(ctx, reader, path, i, result_file_map, dump_stream);
   }
 
   if (i != n_entries) {
@@ -336,7 +345,8 @@ read_result(const std::string& path,
 }
 
 static void
-write_embedded_file_entry(CacheEntryWriter& writer,
+write_embedded_file_entry(Context&,
+                          CacheEntryWriter& writer,
                           const std::string& /*result_path_in_cache*/,
                           uint32_t entry_number,
                           const ResultFileMap::value_type& suffix_and_path)
@@ -375,7 +385,8 @@ write_embedded_file_entry(CacheEntryWriter& writer,
 }
 
 static void
-write_raw_file_entry(CacheEntryWriter& writer,
+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)
@@ -398,21 +409,22 @@ write_raw_file_entry(CacheEntryWriter& writer,
 
   auto raw_file = get_raw_file_path(result_path_in_cache, entry_number);
   auto old_stat = Stat::stat(raw_file);
-  if (!copy_raw_file(source_path, raw_file, true)) {
+  if (!copy_raw_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(stats_file,
+  stats_update_size(ctx,
+                    stats_file,
                     new_stat.size_on_disk() - old_stat.size_on_disk(),
                     (new_stat ? 1 : 0) - (old_stat ? 1 : 0));
 }
 
 static bool
-should_store_raw_file(FileType type)
+should_store_raw_file(const Config& config, FileType type)
 {
-  if (!g_config.file_clone() && !g_config.hard_link()) {
+  if (!config.file_clone() && !config.hard_link()) {
     return false;
   }
 
@@ -434,7 +446,9 @@ should_store_raw_file(FileType type)
 }
 
 static void
-write_result(const std::string& path, const ResultFileMap& result_file_map)
+write_result(Context& ctx,
+             const std::string& path,
+             const ResultFileMap& result_file_map)
 {
   uint64_t payload_size = 0;
   payload_size += 1; // n_entries
@@ -451,8 +465,8 @@ write_result(const std::string& path, const ResultFileMap& result_file_map)
   CacheEntryWriter writer(atomic_result_file.stream(),
                           k_result_magic,
                           k_result_version,
-                          Compression::type_from_config(),
-                          Compression::level_from_config(),
+                          Compression::type_from_config(ctx.config),
+                          Compression::level_from_config(ctx.config),
                           payload_size);
 
   writer.write<uint8_t>(result_file_map.size());
@@ -460,10 +474,10 @@ write_result(const std::string& path, const ResultFileMap& result_file_map)
   size_t entry_number = 0;
   for (const auto& pair : result_file_map) {
     const auto& suffix = pair.first;
-    WriteEntryFunction write_entry = should_store_raw_file(suffix)
+    WriteEntryFunction write_entry = should_store_raw_file(ctx.config, suffix)
                                        ? write_raw_file_entry
                                        : write_embedded_file_entry;
-    write_entry(writer, path, entry_number, pair);
+    write_entry(ctx, writer, path, entry_number, pair);
     ++entry_number;
   }
 
@@ -472,12 +486,14 @@ write_result(const std::string& path, const ResultFileMap& result_file_map)
 }
 
 bool
-result_get(const std::string& path, const ResultFileMap& result_file_map)
+result_get(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(path, &result_file_map, nullptr);
+    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());
@@ -492,12 +508,14 @@ result_get(const std::string& path, const ResultFileMap& result_file_map)
 }
 
 bool
-result_put(const std::string& path, const ResultFileMap& result_file_map)
+result_put(Context& ctx,
+           const std::string& path,
+           const ResultFileMap& result_file_map)
 {
   cc_log("Storing result %s", path.c_str());
 
   try {
-    write_result(path, result_file_map);
+    write_result(ctx, path, result_file_map);
     return true;
   } catch (const Error& e) {
     cc_log("Error: %s", e.what());
@@ -506,12 +524,12 @@ result_put(const std::string& path, const ResultFileMap& result_file_map)
 }
 
 bool
-result_dump(const std::string& path, FILE* stream)
+result_dump(Context& ctx, const std::string& path, FILE* stream)
 {
   assert(stream);
 
   try {
-    if (read_result(path, nullptr, stream)) {
+    if (read_result(ctx, path, nullptr, stream)) {
       return true;
     } else {
       fmt::print(stream, "Error: No such file: {}\n", path);
index 535573b9034e995975d7803cecffecb4396c399c..7ccb5edcae24032be470edac5ad3d0d79c38415a 100644 (file)
@@ -25,6 +25,8 @@
 #include <map>
 #include <string>
 
+struct Context;
+
 extern const uint8_t k_result_magic[4];
 extern const uint8_t k_result_version;
 
@@ -44,6 +46,10 @@ enum class FileType : UnderlyingFileTypeInt {
 
 using ResultFileMap = std::map<FileType, std::string /*path*/>;
 
-bool result_get(const std::string& path, const ResultFileMap& result_file_map);
-bool result_put(const std::string& path, const ResultFileMap& result_file_map);
-bool result_dump(const std::string& path, FILE* stream);
+bool result_get(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(Context& ctx, const std::string& path, FILE* stream);
index eaa7c3a423694c700563f18fd0acb0d54eb53ecb..446d58e596d19ea11d2328f2c44a41e8915116b5 100644 (file)
@@ -23,6 +23,7 @@
 #include "stats.hpp"
 
 #include "AtomicFile.hpp"
+#include "Context.hpp"
 #include "cleanup.hpp"
 #include "counters.hpp"
 #include "hashutil.hpp"
@@ -53,7 +54,9 @@ typedef char* (*format_fn)(uint64_t value);
 
 static char* format_size_times_1024(uint64_t size);
 static char* format_timestamp(uint64_t timestamp);
-static void stats_flush_to_file(const char* sfile, struct counters* updates);
+static void stats_flush_to_file(const Config& config,
+                                const char* sfile,
+                                struct counters* updates);
 
 // Statistics fields in display order.
 static struct
@@ -260,7 +263,9 @@ stats_hit_rate(struct counters* counters)
 }
 
 static void
-stats_collect(struct counters* counters, time_t* last_updated)
+stats_collect(const Config& config,
+              struct counters* counters,
+              time_t* last_updated)
 {
   unsigned zero_timestamp = 0;
 
@@ -271,9 +276,9 @@ stats_collect(struct counters* counters, time_t* last_updated)
     char* fname;
 
     if (dir == -1) {
-      fname = format("%s/stats", g_config.cache_dir().c_str());
+      fname = format("%s/stats", config.cache_dir().c_str());
     } else {
-      fname = format("%s/%1x/stats", g_config.cache_dir().c_str(), dir);
+      fname = format("%s/%1x/stats", config.cache_dir().c_str(), dir);
     }
 
     counters->data[STATS_ZEROTIMESTAMP] = 0; // Don't add
@@ -293,7 +298,7 @@ stats_collect(struct counters* counters, time_t* last_updated)
 // Record that a number of bytes and files have been added to the cache. Size
 // is in bytes.
 void
-stats_update_size(const char* sfile, int64_t size, int files)
+stats_update_size(Context& ctx, const char* sfile, int64_t size, int files)
 {
   if (size == 0 && files == 0) {
     return;
@@ -309,7 +314,7 @@ stats_update_size(const char* sfile, int64_t size, int files)
   updates->data[STATS_NUMFILES] += files;
   updates->data[STATS_TOTALSIZE] += size / 1024;
   if (sfile != stats_file) {
-    stats_flush_to_file(sfile, updates);
+    stats_flush_to_file(ctx.config, sfile, updates);
     counters_free(updates);
   }
 }
@@ -327,19 +332,21 @@ stats_read(const char* sfile, struct counters* counters)
 
 // Write counter updates in updates to sfile.
 static void
-stats_flush_to_file(const char* sfile, struct counters* updates)
+stats_flush_to_file(const Config& config,
+                    const char* sfile,
+                    struct counters* updates)
 {
   if (!updates) {
     return;
   }
 
-  if (g_config.disable()) {
+  if (config.disable()) {
     // Just log result, don't update statistics.
     cc_log("Result: disabled");
     return;
   }
 
-  if (!g_config.log_file().empty() || g_config.debug()) {
+  if (!config.log_file().empty() || config.debug()) {
     for (int i = 0; i < STATS_END; ++i) {
       if (updates->data[stats_info[i].stat] != 0
           && !(stats_info[i].flags & FLAG_NOZERO)) {
@@ -348,7 +355,7 @@ stats_flush_to_file(const char* sfile, struct counters* updates)
     }
   }
 
-  if (!g_config.stats()) {
+  if (!config.stats()) {
     return;
   }
 
@@ -368,8 +375,8 @@ stats_flush_to_file(const char* sfile, struct counters* updates)
 
     // A NULL sfile means that we didn't get past calculate_object_hash(), so
     // we just choose one of stats files in the 16 subdirectories.
-    stats_dir = format(
-      "%s/%x", g_config.cache_dir().c_str(), hash_from_int(getpid()) % 16);
+    stats_dir =
+      format("%s/%x", config.cache_dir().c_str(), hash_from_int(getpid()) % 16);
     sfile = format("%s/stats", stats_dir);
     free(stats_dir);
   }
@@ -389,27 +396,27 @@ stats_flush_to_file(const char* sfile, struct counters* updates)
   char* subdir = x_dirname(sfile);
   bool need_cleanup = false;
 
-  if (g_config.max_files() != 0
-      && counters->data[STATS_NUMFILES] > g_config.max_files() / 16) {
+  if (config.max_files() != 0
+      && counters->data[STATS_NUMFILES] > config.max_files() / 16) {
     cc_log("Need to clean up %s since it holds %u files (limit: %u files)",
            subdir,
            counters->data[STATS_NUMFILES],
-           g_config.max_files() / 16);
+           config.max_files() / 16);
     need_cleanup = true;
   }
-  if (g_config.max_size() != 0
-      && counters->data[STATS_TOTALSIZE] > g_config.max_size() / 1024 / 16) {
+  if (config.max_size() != 0
+      && counters->data[STATS_TOTALSIZE] > config.max_size() / 1024 / 16) {
     cc_log("Need to clean up %s since it holds %u KiB (limit: %lu KiB)",
            subdir,
            counters->data[STATS_TOTALSIZE],
-           (unsigned long)g_config.max_size() / 1024 / 16);
+           (unsigned long)config.max_size() / 1024 / 16);
     need_cleanup = true;
   }
 
   if (need_cleanup) {
-    double factor = g_config.limit_multiple() / 16;
-    uint64_t max_size = round(g_config.max_size() * factor);
-    uint32_t max_files = round(g_config.max_files() * factor);
+    double factor = config.limit_multiple() / 16;
+    uint64_t max_size = round(config.max_size() * factor);
+    uint32_t max_files = round(config.max_files() * factor);
     clean_up_dir(subdir, max_size, max_files, [](double) {});
   }
 
@@ -419,9 +426,10 @@ stats_flush_to_file(const char* sfile, struct counters* updates)
 
 // Write counter updates in counter_updates to disk.
 void
-stats_flush(void)
+stats_flush(void* context)
 {
-  stats_flush_to_file(stats_file, counter_updates);
+  Context& ctx = *static_cast<Context*>(context);
+  stats_flush_to_file(ctx.config, stats_file, counter_updates);
   counters_free(counter_updates);
   counter_updates = NULL;
 }
@@ -445,17 +453,17 @@ stats_get_pending(enum stats stat)
 
 // Sum and display the total stats for all cache dirs.
 void
-stats_summary(void)
+stats_summary(const Config& config)
 {
   struct counters* counters = counters_init(STATS_END);
   time_t last_updated;
-  stats_collect(counters, &last_updated);
+  stats_collect(config, counters, &last_updated);
 
-  fmt::print("cache directory                     {}\n", g_config.cache_dir());
+  fmt::print("cache directory                     {}\n", config.cache_dir());
   fmt::print("primary config                      {}\n",
-             g_config.primary_config_path());
+             config.primary_config_path());
   fmt::print("secondary config (readonly)         {}\n",
-             g_config.secondary_config_path());
+             config.secondary_config_path());
   if (last_updated > 0) {
     struct tm tm;
     localtime_r(&last_updated, &tm);
@@ -492,11 +500,11 @@ stats_summary(void)
     }
   }
 
-  if (g_config.max_files() != 0) {
-    printf("max files                       %8u\n", g_config.max_files());
+  if (config.max_files() != 0) {
+    printf("max files                       %8u\n", config.max_files());
   }
-  if (g_config.max_size() != 0) {
-    char* value = format_size(g_config.max_size());
+  if (config.max_size() != 0) {
+    char* value = format_size(config.max_size());
     printf("max cache size                  %s\n", value);
     free(value);
   }
@@ -506,11 +514,11 @@ stats_summary(void)
 
 // Print machine-parsable (tab-separated) statistics counters.
 void
-stats_print(void)
+stats_print(const Config& config)
 {
   struct counters* counters = counters_init(STATS_END);
   time_t last_updated;
-  stats_collect(counters, &last_updated);
+  stats_collect(config, counters, &last_updated);
 
   printf("stats_updated_timestamp\t%llu\n", (unsigned long long)last_updated);
 
@@ -525,9 +533,9 @@ stats_print(void)
 
 // Zero all the stats structures.
 void
-stats_zero(void)
+stats_zero(const Config& config)
 {
-  char* fname = format("%s/stats", g_config.cache_dir().c_str());
+  char* fname = format("%s/stats", config.cache_dir().c_str());
   x_unlink(fname);
   free(fname);
 
@@ -535,7 +543,7 @@ stats_zero(void)
 
   for (int dir = 0; dir <= 0xF; dir++) {
     struct counters* counters = counters_init(STATS_END);
-    fname = format("%s/%1x/stats", g_config.cache_dir().c_str(), dir);
+    fname = format("%s/%1x/stats", config.cache_dir().c_str(), dir);
     if (!Stat::stat(fname)) {
       // No point in trying to reset the stats file if it doesn't exist.
       free(fname);
index 385ad3486393a39f5c68d0e7a22834a66511418e..27950619a6977aee83bc4eef8f57749b6b55ec26 100644 (file)
@@ -20,6 +20,9 @@
 
 #include "system.hpp"
 
+class Config;
+struct Context;
+
 // Statistics fields in storage order.
 enum stats {
   STATS_NONE = 0,
@@ -60,12 +63,13 @@ enum stats {
 };
 
 void stats_update(enum stats stat);
-void stats_flush();
+void stats_flush(void* context);
 unsigned stats_get_pending(enum stats stat);
-void stats_zero();
-void stats_summary();
-void stats_print();
-void stats_update_size(const char* sfile, int64_t size, int files);
+void stats_zero(const Config& config);
+void stats_summary(const Config& config);
+void stats_print(const Config& config);
+void
+stats_update_size(Context& ctx, const char* sfile, int64_t size, int files);
 void stats_get_obsolete_limits(const char* dir,
                                unsigned* maxfiles,
                                uint64_t* maxsize);
index 64e1c6ab5d389aeb24b22fe688bff9bd83e6b2e1..04f21d2a1f1b0bcfff25ee69ffea02a0c8d3f236 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "framework.hpp"
 
+#include "../src/Config.hpp"
 #include "../src/Util.hpp"
 #include "../src/args.hpp"
 #include "../src/ccache.hpp"
@@ -128,6 +129,7 @@ cct_suite_end()
 void
 cct_test_begin(const char* name)
 {
+  Config config;
   ++total_tests;
   if (verbose) {
     printf("--- TEST: %s ---\n", name);
@@ -138,7 +140,7 @@ cct_test_begin(const char* name)
   current_test = name;
 
   x_setenv("CCACHE_CONFIG_PATH", "/dev/null");
-  cc_reset();
+  cc_reset(config);
 }
 
 void
index e2ccf7085bc38a260d15dc9ee49374a4c64fedcb..8ea2f2441979fcb31bf28187fe318710762a7e11 100644 (file)
@@ -17,6 +17,7 @@
 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "../src/Compression.hpp"
+#include "../src/Config.hpp"
 
 #include "third_party/catch.hpp"
 
@@ -24,12 +25,14 @@ using Catch::Equals;
 
 TEST_CASE("Compression::level_from_config")
 {
-  CHECK(Compression::level_from_config() == 0);
+  Config config;
+  CHECK(Compression::level_from_config(config) == 0);
 }
 
 TEST_CASE("Compression::type_from_config")
 {
-  CHECK(Compression::type_from_config() == Compression::Type::zstd);
+  Config config;
+  CHECK(Compression::type_from_config(config) == Compression::Type::zstd);
 }
 
 TEST_CASE("Compression::type_from_int")
index ce8d9d9b04d513b9eebd3c79cff34575862ea2e0..a98da1ace0956a7477b4d83e7ceb33f59a89b67f 100644 (file)
@@ -65,31 +65,6 @@ get_posix_path(char* path)
 #endif
 }
 
-bool cc_process_args(Context& ctx,
-                     struct args* args,
-                     struct args** preprocessor_args,
-                     struct args** extra_args_to_hash,
-                     struct args** compiler_args);
-
-bool
-cc_process_args(Context& ctx,
-                struct args* args,
-                struct args** preprocessor_args,
-                struct args** extra_args_to_hash,
-                struct args** compiler_args)
-{
-  bool success = cc_process_args(ctx.args_info,
-                                 g_config,
-                                 args,
-                                 preprocessor_args,
-                                 extra_args_to_hash,
-                                 compiler_args);
-
-  output_is_precompiled_header = ctx.args_info.output_is_precompiled_header;
-
-  return success;
-}
-
 TEST_SUITE(argument_processing)
 
 TEST(dash_E_should_result_in_called_for_preprocessing)
@@ -138,7 +113,7 @@ TEST(dependency_args_to_preprocessor_if_run_second_cpp_is_false)
   struct args* act_cc = NULL;
   create_file("foo.c", "");
 
-  g_config.set_run_second_cpp(false);
+  ctx.config.set_run_second_cpp(false);
   CHECK(cc_process_args(ctx, orig, &act_cpp, &act_extra, &act_cc));
   CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
   CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
@@ -197,7 +172,7 @@ TEST(cpp_only_args_to_preprocessor_if_run_second_cpp_is_false)
   struct args* act_cc = NULL;
   create_file("foo.c", "");
 
-  g_config.set_run_second_cpp(false);
+  ctx.config.set_run_second_cpp(false);
   CHECK(cc_process_args(ctx, orig, &act_cpp, &act_extra, &act_cc));
   CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
   CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
@@ -298,7 +273,7 @@ TEST(MQ_flag_should_be_added_if_run_second_cpp_is_false)
   struct args* act_cc = NULL;
   create_file("foo.c", "");
 
-  g_config.set_run_second_cpp(false);
+  ctx.config.set_run_second_cpp(false);
   CHECK(cc_process_args(ctx, orig, &act_cpp, &act_extra, &act_cc));
   CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
   CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
@@ -321,7 +296,7 @@ TEST(MF_should_be_added_if_run_second_cpp_is_false)
 
   create_file("foo.c", "");
 
-  g_config.set_run_second_cpp(false);
+  ctx.config.set_run_second_cpp(false);
   CHECK(cc_process_args(ctx, orig, &act_cpp, &act_extra, &act_cc));
   CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
   CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
@@ -386,7 +361,7 @@ TEST(sysroot_should_be_rewritten_if_basedir_is_used)
   struct args* act_cc = NULL;
 
   create_file("foo.c", "");
-  g_config.set_base_dir(get_root());
+  ctx.config.set_base_dir(get_root());
   current_working_dir = get_cwd();
   arg_string = format("cc --sysroot=%s/foo/bar -c foo.c", current_working_dir);
   orig = args_init_from_string(arg_string);
@@ -412,7 +387,7 @@ TEST(sysroot_with_separate_argument_should_be_rewritten_if_basedir_is_used)
   struct args* act_cc = NULL;
 
   create_file("foo.c", "");
-  g_config.set_base_dir(get_root());
+  ctx.config.set_base_dir(get_root());
   current_working_dir = get_cwd();
   arg_string = format("cc --sysroot %s/foo -c foo.c", current_working_dir);
   orig = args_init_from_string(arg_string);
@@ -666,7 +641,7 @@ TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used)
   struct args* act_cc = NULL;
 
   create_file("foo.c", "");
-  g_config.set_base_dir(get_root());
+  ctx.config.set_base_dir(get_root());
   current_working_dir = get_cwd();
   arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir);
   orig = args_init_from_string(arg_string);
@@ -693,7 +668,7 @@ TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used)
   struct args* act_cc = NULL;
 
   create_file("foo.c", "");
-  g_config.set_base_dir("/"); // posix
+  ctx.config.set_base_dir("/"); // posix
   current_working_dir = get_cwd();
   // Windows path doesn't work concatenated.
   cwd = get_posix_path(current_working_dir);
@@ -723,7 +698,7 @@ TEST(I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used)
   struct args* act_cc = NULL;
 
   create_file("foo.c", "");
-  g_config.set_base_dir(x_strdup("/")); // posix
+  ctx.config.set_base_dir(x_strdup("/")); // posix
   current_working_dir = get_cwd();
   // Windows path doesn't work concatenated.
   cwd = get_posix_path(current_working_dir);
index db41f090f72c5fee893d089f19ff20aa4fc3b1af..2abac0ab2ef09dd4de584234b4b85f07df4fe57f 100644 (file)
@@ -18,6 +18,7 @@
 
 // This file contains tests for functions in hashutil.c.
 
+#include "../src/Context.hpp"
 #include "../src/hashutil.hpp"
 #include "framework.hpp"
 #include "util.hpp"
@@ -26,14 +27,16 @@ TEST_SUITE(hashutil)
 
 TEST(hash_command_output_simple)
 {
+  Context ctx;
+
   char d1[DIGEST_STRING_BUFFER_SIZE];
   char d2[DIGEST_STRING_BUFFER_SIZE];
 
   struct hash* h1 = hash_init();
   struct hash* h2 = hash_init();
 
-  CHECK(hash_command_output(h1, "echo", "not used"));
-  CHECK(hash_command_output(h2, "echo", "not used"));
+  CHECK(hash_command_output(ctx, h1, "echo", "not used"));
+  CHECK(hash_command_output(ctx, h2, "echo", "not used"));
   hash_result_as_string(h1, d1);
   hash_result_as_string(h2, d2);
   CHECK_STR_EQ(d1, d2);
@@ -44,14 +47,16 @@ TEST(hash_command_output_simple)
 
 TEST(hash_command_output_space_removal)
 {
+  Context ctx;
+
   char d1[DIGEST_STRING_BUFFER_SIZE];
   char d2[DIGEST_STRING_BUFFER_SIZE];
 
   struct hash* h1 = hash_init();
   struct hash* h2 = hash_init();
 
-  CHECK(hash_command_output(h1, "echo", "not used"));
-  CHECK(hash_command_output(h2, " echo ", "not used"));
+  CHECK(hash_command_output(ctx, h1, "echo", "not used"));
+  CHECK(hash_command_output(ctx, h2, " echo ", "not used"));
   hash_result_as_string(h1, d1);
   hash_result_as_string(h2, d2);
   CHECK_STR_EQ(d1, d2);
@@ -62,14 +67,16 @@ TEST(hash_command_output_space_removal)
 
 TEST(hash_command_output_hash_inequality)
 {
+  Context ctx;
+
   char d1[DIGEST_STRING_BUFFER_SIZE];
   char d2[DIGEST_STRING_BUFFER_SIZE];
 
   struct hash* h1 = hash_init();
   struct hash* h2 = hash_init();
 
-  CHECK(hash_command_output(h1, "echo foo", "not used"));
-  CHECK(hash_command_output(h2, "echo bar", "not used"));
+  CHECK(hash_command_output(ctx, h1, "echo foo", "not used"));
+  CHECK(hash_command_output(ctx, h2, "echo bar", "not used"));
   hash_result_as_string(h1, d1);
   hash_result_as_string(h2, d2);
   CHECK(!str_eq(d1, d2));
@@ -80,14 +87,16 @@ TEST(hash_command_output_hash_inequality)
 
 TEST(hash_command_output_compiler_substitution)
 {
+  Context ctx;
+
   char d1[DIGEST_STRING_BUFFER_SIZE];
   char d2[DIGEST_STRING_BUFFER_SIZE];
 
   struct hash* h1 = hash_init();
   struct hash* h2 = hash_init();
 
-  CHECK(hash_command_output(h1, "echo foo", "not used"));
-  CHECK(hash_command_output(h2, "%compiler% foo", "echo"));
+  CHECK(hash_command_output(ctx, h1, "echo foo", "not used"));
+  CHECK(hash_command_output(ctx, h2, "%compiler% foo", "echo"));
   hash_result_as_string(h1, d1);
   hash_result_as_string(h2, d2);
   CHECK_STR_EQ(d1, d2);
@@ -98,6 +107,8 @@ TEST(hash_command_output_compiler_substitution)
 
 TEST(hash_command_output_stdout_versus_stderr)
 {
+  Context ctx;
+
   char d1[DIGEST_STRING_BUFFER_SIZE];
   char d2[DIGEST_STRING_BUFFER_SIZE];
 
@@ -107,12 +118,12 @@ TEST(hash_command_output_stdout_versus_stderr)
 #ifndef _WIN32
   create_file("stderr.sh", "#!/bin/sh\necho foo >&2\n");
   chmod("stderr.sh", 0555);
-  CHECK(hash_command_output(h1, "echo foo", "not used"));
-  CHECK(hash_command_output(h2, "./stderr.sh", "not used"));
+  CHECK(hash_command_output(ctx, h1, "echo foo", "not used"));
+  CHECK(hash_command_output(ctx, h2, "./stderr.sh", "not used"));
 #else
   create_file("stderr.bat", "@echo off\r\necho foo>&2\r\n");
-  CHECK(hash_command_output(h1, "echo foo", "not used"));
-  CHECK(hash_command_output(h2, "stderr.bat", "not used"));
+  CHECK(hash_command_output(ctx, h1, "echo foo", "not used"));
+  CHECK(hash_command_output(ctx, h2, "stderr.bat", "not used"));
 #endif
   hash_result_as_string(h1, d1);
   hash_result_as_string(h2, d2);
@@ -124,6 +135,8 @@ TEST(hash_command_output_stdout_versus_stderr)
 
 TEST(hash_multicommand_output)
 {
+  Context ctx;
+
   char d1[DIGEST_STRING_BUFFER_SIZE];
   char d2[DIGEST_STRING_BUFFER_SIZE];
 
@@ -133,12 +146,12 @@ TEST(hash_multicommand_output)
 #ifndef _WIN32
   create_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n");
   chmod("foo.sh", 0555);
-  CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
-  CHECK(hash_multicommand_output(h1, "./foo.sh", "not used"));
+  CHECK(hash_multicommand_output(ctx, h2, "echo foo; echo bar", "not used"));
+  CHECK(hash_multicommand_output(ctx, h1, "./foo.sh", "not used"));
 #else
   create_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n");
-  CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
-  CHECK(hash_multicommand_output(h1, "foo.bat", "not used"));
+  CHECK(hash_multicommand_output(ctx, h2, "echo foo; echo bar", "not used"));
+  CHECK(hash_multicommand_output(ctx, h1, "foo.bat", "not used"));
 #endif
   hash_result_as_string(h1, d1);
   hash_result_as_string(h2, d2);
@@ -150,10 +163,12 @@ TEST(hash_multicommand_output)
 
 TEST(hash_multicommand_output_error_handling)
 {
+  Context ctx;
+
   struct hash* h1 = hash_init();
   struct hash* h2 = hash_init();
 
-  CHECK(!hash_multicommand_output(h2, "false; true", "not used"));
+  CHECK(!hash_multicommand_output(ctx, h2, "false; true", "not used"));
 
   hash_free(h2);
   hash_free(h1);