From: Joel Rosdahl Date: Thu, 22 Oct 2020 17:30:41 +0000 (+0200) Subject: Detect errors in fmt::format format strings at compile time X-Git-Tag: v4.1~44 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=83b50b30711f36b84f2666a27db1cc92d8bf1d95;p=thirdparty%2Fccache.git Detect errors in fmt::format format strings at compile time See also 4413d842e23c6fa52ec411951a4ab442f42227de. --- diff --git a/src/Config.cpp b/src/Config.cpp index c7168e06d..36c7608df 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -24,6 +24,7 @@ #include "assertions.hpp" #include "ccache.hpp" #include "exceptions.hpp" +#include "fmtmacros.hpp" #include "third_party/fmt/core.h" @@ -317,7 +318,7 @@ format_umask(uint32_t umask) if (umask == std::numeric_limits::max()) { return {}; } else { - return fmt::format("{:03o}", umask); + return FMT("{:03o}", umask); } } @@ -490,7 +491,7 @@ Config::get_string_value(const std::string& key) const return format_bool(m_compression); case ConfigItem::compression_level: - return fmt::format("{}", m_compression_level); + return FMT("{}", m_compression_level); case ConfigItem::cpp_extension: return m_cpp_extension; @@ -532,13 +533,13 @@ Config::get_string_value(const std::string& key) const return format_bool(m_keep_comments_cpp); case ConfigItem::limit_multiple: - return fmt::format("{:.1f}", m_limit_multiple); + return FMT("{:.1f}", m_limit_multiple); case ConfigItem::log_file: return m_log_file; case ConfigItem::max_files: - return fmt::format("{}", m_max_files); + return FMT("{}", m_max_files); case ConfigItem::max_size: return format_cache_size(m_max_size); @@ -615,17 +616,17 @@ Config::set_value_in_file(const std::string& path, const std::string& c_key, const std::string& /*c_value*/) { if (c_key == key) { - output.write(fmt::format("{} = {}\n", key, value)); + output.write(FMT("{} = {}\n", key, value)); found = true; } else { - output.write(fmt::format("{}\n", c_line)); + output.write(FMT("{}\n", c_line)); } })) { throw Error("failed to open {}: {}", path, strerror(errno)); } if (!found) { - output.write(fmt::format("{} = {}\n", key, value)); + output.write(FMT("{} = {}\n", key, value)); } output.commit(); @@ -834,7 +835,7 @@ std::string Config::default_temporary_dir(const std::string& cache_dir) { #ifdef HAVE_GETEUID - std::string user_tmp_dir = fmt::format("/run/user/{}", geteuid()); + std::string user_tmp_dir = FMT("/run/user/{}", geteuid()); if (Stat::stat(user_tmp_dir).is_directory()) { return user_tmp_dir + "/ccache-tmp"; } diff --git a/src/Hash.cpp b/src/Hash.cpp index 3db2b54c3..ccc6f7bb3 100644 --- a/src/Hash.cpp +++ b/src/Hash.cpp @@ -20,6 +20,7 @@ #include "Fd.hpp" #include "Logging.hpp" +#include "fmtmacros.hpp" using nonstd::string_view; @@ -97,7 +98,7 @@ Hash& Hash::hash(int64_t x) { hash_buffer(string_view(reinterpret_cast(&x), sizeof(x))); - add_debug_text(fmt::format("{}\n", x)); + add_debug_text(FMT("{}\n", x)); return *this; } diff --git a/src/InodeCache.cpp b/src/InodeCache.cpp index a0f6f76c5..5c77f08bd 100644 --- a/src/InodeCache.cpp +++ b/src/InodeCache.cpp @@ -26,6 +26,7 @@ #include "Stat.hpp" #include "TemporaryFile.hpp" #include "Util.hpp" +#include "fmtmacros.hpp" #include #include @@ -464,7 +465,7 @@ InodeCache::drop() std::string InodeCache::get_file() { - return fmt::format("{}/inode-cache.v{}", m_config.temporary_dir(), k_version); + return FMT("{}/inode-cache.v{}", m_config.temporary_dir(), k_version); } int64_t diff --git a/src/Lockfile.cpp b/src/Lockfile.cpp index e37c8dc71..715ffa4f8 100644 --- a/src/Lockfile.cpp +++ b/src/Lockfile.cpp @@ -20,6 +20,7 @@ #include "Logging.hpp" #include "Util.hpp" +#include "fmtmacros.hpp" #ifdef _WIN32 # include "Win32Util.hpp" @@ -49,7 +50,7 @@ do_acquire_posix(const std::string& lockfile, uint32_t staleness_limit) const auto content_prefix = ss.str(); while (true) { - auto my_content = fmt::format("{}:{}", content_prefix, time(nullptr)); + auto my_content = FMT("{}:{}", content_prefix, time(nullptr)); if (symlink(my_content.c_str(), lockfile.c_str()) == 0) { // We got the lock. diff --git a/src/MiniTrace.cpp b/src/MiniTrace.cpp index 788d31606..bf17397b9 100644 --- a/src/MiniTrace.cpp +++ b/src/MiniTrace.cpp @@ -24,6 +24,7 @@ # include "MiniTrace.hpp" # include "TemporaryFile.hpp" # include "Util.hpp" +# include "fmtmacros.hpp" # ifdef HAVE_SYS_TIME_H # include @@ -70,7 +71,7 @@ MiniTrace::MiniTrace(const ArgsInfo& args_info) m_tmp_trace_file = tmp_file.path; mtr_init(m_tmp_trace_file.c_str()); - MTR_INSTANT_C("", "", "time", fmt::format("{:f}", time_seconds()).c_str()); + MTR_INSTANT_C("", "", "time", FMT("{:f}", time_seconds()).c_str()); MTR_META_PROCESS_NAME("ccache"); MTR_START("program", "ccache", m_trace_id); } diff --git a/src/Result.cpp b/src/Result.cpp index 09d704c0b..ef8ac747e 100644 --- a/src/Result.cpp +++ b/src/Result.cpp @@ -30,6 +30,7 @@ #include "Statistics.hpp" #include "Util.hpp" #include "exceptions.hpp" +#include "fmtmacros.hpp" #include @@ -106,7 +107,7 @@ get_raw_file_path(string_view result_path, uint32_t entry_number) { const auto prefix = result_path.substr( 0, result_path.length() - Result::k_file_suffix.length()); - return fmt::format("{}{}W", prefix, entry_number); + return FMT("{}{}W", prefix, entry_number); } bool @@ -183,7 +184,7 @@ gcno_file_in_mangled_form(const Context& ctx) const std::string abs_output_obj = Util::is_absolute_path(output_obj) ? output_obj - : fmt::format("{}/{}", ctx.apparent_cwd, output_obj); + : FMT("{}/{}", ctx.apparent_cwd, output_obj); std::string hashified_obj = abs_output_obj; std::replace(hashified_obj.begin(), hashified_obj.end(), '/', '#'); return Util::change_extension(hashified_obj, ".gcno"); diff --git a/src/ResultExtractor.cpp b/src/ResultExtractor.cpp index 10b8189c8..e18ae5160 100644 --- a/src/ResultExtractor.cpp +++ b/src/ResultExtractor.cpp @@ -19,6 +19,7 @@ #include "ResultExtractor.hpp" #include "Util.hpp" +#include "fmtmacros.hpp" ResultExtractor::ResultExtractor(const std::string& directory) : m_directory(directory) @@ -38,13 +39,13 @@ ResultExtractor::on_entry_start(uint32_t /*entry_number*/, { std::string suffix = Result::file_type_to_string(file_type); if (suffix == Result::k_unknown_file_type) { - suffix = fmt::format(".type_{}", file_type); + suffix = FMT(".type_{}", file_type); } else if (suffix[0] == '<') { suffix[0] = '.'; suffix.resize(suffix.length() - 1); } - m_dest_path = fmt::format("{}/ccache-result{}", m_directory, suffix); + m_dest_path = FMT("{}/ccache-result{}", m_directory, suffix); if (!raw_file) { m_dest_fd = Fd( diff --git a/src/Statistics.cpp b/src/Statistics.cpp index b8dccf873..08ad8928c 100644 --- a/src/Statistics.cpp +++ b/src/Statistics.cpp @@ -24,6 +24,7 @@ #include "Logging.hpp" #include "Util.hpp" #include "exceptions.hpp" +#include "fmtmacros.hpp" const unsigned FLAG_NOZERO = 1; // don't zero with the -z option const unsigned FLAG_ALWAYS = 2; // always show, even if zero @@ -39,7 +40,7 @@ using FormatFunction = std::string (*)(uint64_t value); static std::string format_size(uint64_t size) { - return fmt::format("{:>11}", Util::format_human_readable_size(size)); + return FMT("{:>11}", Util::format_human_readable_size(size)); } static std::string @@ -80,9 +81,9 @@ for_each_level_1_and_2_stats_file( const std::function function) { for (size_t level_1 = 0; level_1 <= 0xF; ++level_1) { - function(fmt::format("{}/{:x}/stats", cache_dir, level_1)); + function(FMT("{}/{:x}/stats", cache_dir, level_1)); for (size_t level_2 = 0; level_2 <= 0xF; ++level_2) { - function(fmt::format("{}/{:x}/{:x}/stats", cache_dir, level_1, level_2)); + function(FMT("{}/{:x}/{:x}/stats", cache_dir, level_1, level_2)); } } } @@ -229,7 +230,7 @@ update(const std::string& path, AtomicFile file(path, AtomicFile::Mode::text); for (size_t i = 0; i < counters.size(); ++i) { - file.write(fmt::format("{}\n", counters.get_raw(i))); + file.write(FMT("{}\n", counters.get_raw(i))); } try { file.commit(); @@ -280,10 +281,9 @@ format_human_readable(const Config& config) std::tie(counters, last_updated) = collect_counters(config); std::string result; - result += fmt::format("{:36}{}\n", "cache directory", config.cache_dir()); - result += - fmt::format("{:36}{}\n", "primary config", config.primary_config_path()); - result += fmt::format( + result += FMT("{:36}{}\n", "cache directory", config.cache_dir()); + result += FMT("{:36}{}\n", "primary config", config.primary_config_path()); + result += FMT( "{:36}{}\n", "secondary config (readonly)", config.secondary_config_path()); if (last_updated > 0) { const auto tm = Util::localtime(last_updated); @@ -291,7 +291,7 @@ format_human_readable(const Config& config) if (tm) { strftime(timestamp, sizeof(timestamp), "%c", &*tm); } - result += fmt::format("{:36}{}\n", "stats updated", timestamp); + result += FMT("{:36}{}\n", "stats updated", timestamp); } // ...and display them. @@ -309,23 +309,23 @@ format_human_readable(const Config& config) const std::string value = k_statistics_fields[i].format ? k_statistics_fields[i].format(counters.get(statistic)) - : fmt::format("{:8}", counters.get(statistic)); + : FMT("{:8}", counters.get(statistic)); if (!value.empty()) { - result += fmt::format("{:32}{}\n", k_statistics_fields[i].message, value); + result += FMT("{:32}{}\n", k_statistics_fields[i].message, value); } if (statistic == Statistic::cache_miss) { double percent = hit_rate(counters); - result += fmt::format("{:34}{:6.2f} %\n", "cache hit rate", percent); + result += FMT("{:34}{:6.2f} %\n", "cache hit rate", percent); } } if (config.max_files() != 0) { - result += fmt::format("{:32}{:8}\n", "max files", config.max_files()); + result += FMT("{:32}{:8}\n", "max files", config.max_files()); } if (config.max_size() != 0) { - result += fmt::format( - "{:32}{}\n", "max cache size", format_size(config.max_size())); + result += + FMT("{:32}{}\n", "max cache size", format_size(config.max_size())); } return result; @@ -339,13 +339,13 @@ format_machine_readable(const Config& config) std::tie(counters, last_updated) = collect_counters(config); std::string result; - result += fmt::format("stats_updated_timestamp\t{}\n", last_updated); + result += FMT("stats_updated_timestamp\t{}\n", last_updated); for (size_t i = 0; k_statistics_fields[i].message; i++) { if (!(k_statistics_fields[i].flags & FLAG_NEVER)) { - result += fmt::format("{}\t{}\n", - k_statistics_fields[i].id, - counters.get(k_statistics_fields[i].statistic)); + result += FMT("{}\t{}\n", + k_statistics_fields[i].id, + counters.get(k_statistics_fields[i].statistic)); } } diff --git a/src/Util.cpp b/src/Util.cpp index cd2966643..bac6b7d12 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -24,6 +24,7 @@ #include "FormatNonstdStringView.hpp" #include "Logging.hpp" #include "TemporaryFile.hpp" +#include "fmtmacros.hpp" extern "C" { #include "third_party/base32hex.h" @@ -503,7 +504,7 @@ for_each_level_1_subdir(const std::string& cache_dir, for (int i = 0; i <= 0xF; i++) { double progress = 1.0 * i / 16; progress_receiver(progress); - std::string subdir_path = fmt::format("{}/{:x}", cache_dir, i); + std::string subdir_path = FMT("{}/{:x}", cache_dir, i); visitor(subdir_path, [&](double inner_progress) { progress_receiver(progress + inner_progress / 16); }); @@ -553,11 +554,11 @@ std::string format_human_readable_size(uint64_t size) { if (size >= 1000 * 1000 * 1000) { - return fmt::format("{:.1f} GB", size / ((double)(1000 * 1000 * 1000))); + return FMT("{:.1f} GB", size / ((double)(1000 * 1000 * 1000))); } else if (size >= 1000 * 1000) { - return fmt::format("{:.1f} MB", size / ((double)(1000 * 1000))); + return FMT("{:.1f} MB", size / ((double)(1000 * 1000))); } else { - return fmt::format("{:.1f} kB", size / 1000.0); + return FMT("{:.1f} kB", size / 1000.0); } } @@ -565,11 +566,11 @@ std::string format_parsable_size_with_suffix(uint64_t size) { if (size >= 1000 * 1000 * 1000) { - return fmt::format("{:.1f}G", size / ((double)(1000 * 1000 * 1000))); + return FMT("{:.1f}G", size / ((double)(1000 * 1000 * 1000))); } else if (size >= 1000 * 1000) { - return fmt::format("{:.1f}M", size / ((double)(1000 * 1000))); + return FMT("{:.1f}M", size / ((double)(1000 * 1000))); } else { - return fmt::format("{}", size); + return FMT("{}", size); } } @@ -844,7 +845,7 @@ make_relative_path(const Context& ctx, string_view path) if (path.length() >= 3 && path[0] == '/') { if (isalpha(path[1]) && path[2] == '/') { // Transform /c/path... to c:/path... - winpath = fmt::format("{}:/{}", path[1], path.substr(3)); + winpath = FMT("{}:/{}", path[1], path.substr(3)); path = winpath; } else if (path[2] == ':') { // Transform /c:/path to c:/path @@ -1249,8 +1250,7 @@ same_program_name(nonstd::string_view program_name, #ifdef _WIN32 std::string lowercase_program_name = Util::to_lowercase(program_name); return lowercase_program_name == canonical_program_name - || lowercase_program_name - == fmt::format("{}.exe", canonical_program_name); + || lowercase_program_name == FMT("{}.exe", canonical_program_name); #else return program_name == canonical_program_name; #endif diff --git a/src/argprocessing.cpp b/src/argprocessing.cpp index ccb7d8e86..fc61655bc 100644 --- a/src/argprocessing.cpp +++ b/src/argprocessing.cpp @@ -23,6 +23,7 @@ #include "Logging.hpp" #include "assertions.hpp" #include "compopt.hpp" +#include "fmtmacros.hpp" #include "language.hpp" #include @@ -534,7 +535,7 @@ process_arg(Context& ctx, auto arg_opt = string_view(args[i]).substr(0, 3); auto option = string_view(args[i]).substr(3); auto relpath = Util::make_relative_path(ctx, option); - state.dep_args.push_back(fmt::format("{}{}", arg_opt, relpath)); + state.dep_args.push_back(FMT("{}{}", arg_opt, relpath)); } return nullopt; } @@ -902,8 +903,7 @@ handle_dependency_environment_variables(Context& ctx, string_view abspath_obj = dependencies[1]; std::string relpath_obj = Util::make_relative_path(ctx, abspath_obj); // Ensure that the compiler gets a relative path. - std::string relpath_both = - fmt::format("{} {}", args_info.output_dep, relpath_obj); + std::string relpath_both = FMT("{} {}", args_info.output_dep, relpath_obj); if (using_sunpro_dependencies) { Util::setenv("SUNPRO_DEPENDENCIES", relpath_both); } else { diff --git a/src/ccache.cpp b/src/ccache.cpp index a57dd8a8e..2eef57837 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -240,7 +240,7 @@ init_hash_debug(Context& ctx, return; } - std::string path = fmt::format("{}.ccache-input-{}", obj_path, type); + std::string path = FMT("{}.ccache-input-{}", obj_path, type); File debug_binary_file(path, "wb"); if (debug_binary_file) { hash.enable_debug(section_name, debug_binary_file.get(), debug_text_file); @@ -361,7 +361,7 @@ do_remember_include_file(Context& ctx, 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); + std::string pch_sum_path = FMT("{}.sum", path); if (Stat::stat(pch_sum_path, Stat::OnError::log)) { path = std::move(pch_sum_path); using_pch_sum = true; @@ -787,7 +787,7 @@ look_up_cache_file(const std::string& cache_dir, const Digest& name, nonstd::string_view suffix) { - const auto name_string = fmt::format("{}{}", name.to_string(), suffix); + const auto name_string = FMT("{}{}", name.to_string(), suffix); for (uint8_t level = k_min_cache_levels; level <= k_max_cache_levels; ++level) { @@ -853,9 +853,9 @@ create_cachedir_tag(const Context& ctx) "# For information about cache directory tags, see:\n" "#\thttp://www.brynosaurus.com/cachedir/\n"; - const std::string path = fmt::format("{}/{}/CACHEDIR.TAG", - ctx.config.cache_dir(), - ctx.result_name()->to_string()[0]); + const std::string path = FMT("{}/{}/CACHEDIR.TAG", + ctx.config.cache_dir(), + ctx.result_name()->to_string()[0]); const auto stat = Stat::stat(path); if (stat) { return; @@ -955,13 +955,11 @@ to_cache(Context& ctx, LOG_RAW("Running real compiler"); MTR_BEGIN("execute", "compiler"); - TemporaryFile tmp_stdout( - fmt::format("{}/tmp.stdout", ctx.config.temporary_dir())); + TemporaryFile tmp_stdout(FMT("{}/tmp.stdout", ctx.config.temporary_dir())); ctx.register_pending_tmp_file(tmp_stdout.path); std::string tmp_stdout_path = tmp_stdout.path; - TemporaryFile tmp_stderr( - fmt::format("{}/tmp.stderr", ctx.config.temporary_dir())); + TemporaryFile tmp_stderr(FMT("{}/tmp.stderr", ctx.config.temporary_dir())); ctx.register_pending_tmp_file(tmp_stderr.path); std::string tmp_stderr_path = tmp_stderr.path; @@ -1127,12 +1125,12 @@ get_result_name_from_cpp(Context& ctx, Args& args, Hash& hash) // Run cpp on the input file to obtain the .i. TemporaryFile tmp_stdout( - fmt::format("{}/tmp.cpp_stdout", ctx.config.temporary_dir())); + FMT("{}/tmp.cpp_stdout", ctx.config.temporary_dir())); stdout_path = tmp_stdout.path; ctx.register_pending_tmp_file(stdout_path); TemporaryFile tmp_stderr( - fmt::format("{}/tmp.cpp_stderr", ctx.config.temporary_dir())); + FMT("{}/tmp.cpp_stderr", ctx.config.temporary_dir())); stderr_path = tmp_stderr.path; ctx.register_pending_tmp_file(stderr_path); @@ -1180,8 +1178,7 @@ get_result_name_from_cpp(Context& ctx, Args& args, Hash& hash) } else { // i_tmpfile needs the proper cpp_extension for the compiler to do its // thing correctly - ctx.i_tmpfile = - fmt::format("{}.{}", stdout_path, ctx.config.cpp_extension()); + ctx.i_tmpfile = FMT("{}.{}", stdout_path, ctx.config.cpp_extension()); Util::rename(stdout_path, ctx.i_tmpfile); ctx.register_pending_tmp_file(ctx.i_tmpfile); } @@ -1261,7 +1258,7 @@ hash_nvcc_host_compiler(const Context& ctx, #endif for (const char* compiler : compilers) { if (!ccbin.empty()) { - std::string path = fmt::format("{}/{}", ccbin, compiler); + std::string path = FMT("{}/{}", ccbin, compiler); auto st = Stat::stat(path); if (st) { hash_compiler(ctx, hash, st, path, false); @@ -1395,7 +1392,7 @@ hash_common_info(const Context& ctx, } string_view stem = Util::remove_extension(Util::base_name(ctx.args_info.output_obj)); - std::string gcda_path = fmt::format("{}/{}.gcda", dir, stem); + std::string gcda_path = FMT("{}/{}.gcda", dir, stem); LOG("Hashing coverage path {}", gcda_path); hash.hash_delimiter("gcda"); hash.hash(gcda_path); @@ -1441,13 +1438,13 @@ hash_profile_data_file(const Context& ctx, Hash& hash) std::vector paths_to_try{ // -fprofile-use[=dir]/-fbranch-probabilities (GCC <9) - fmt::format("{}/{}.gcda", profile_path, base_name), + FMT("{}/{}.gcda", profile_path, base_name), // -fprofile-use[=dir]/-fbranch-probabilities (GCC >=9) - fmt::format("{}/{}#{}.gcda", profile_path, hashified_cwd, base_name), + FMT("{}/{}#{}.gcda", profile_path, hashified_cwd, base_name), // -fprofile(-instr|-sample)-use=file (Clang), -fauto-profile=file (GCC >=5) profile_path, // -fprofile(-instr|-sample)-use=dir (Clang) - fmt::format("{}/default.profdata", profile_path), + FMT("{}/default.profdata", profile_path), // -fauto-profile (GCC >=5) "fbdata.afdo", // -fprofile-dir is not used }; @@ -1927,9 +1924,9 @@ set_up_config(Config& config) // Only used for ccache tests: const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2"); - config.set_secondary_config_path( - env_ccache_configpath2 ? env_ccache_configpath2 - : fmt::format("{}/ccache.conf", SYSCONFDIR)); + config.set_secondary_config_path(env_ccache_configpath2 + ? env_ccache_configpath2 + : FMT("{}/ccache.conf", SYSCONFDIR)); MTR_BEGIN("config", "conf_read_secondary"); // A missing config file in SYSCONFDIR is OK so don't check return value. config.update_from_file(config.secondary_config_path()); @@ -1944,7 +1941,7 @@ set_up_config(Config& config) } else if (legacy_ccache_dir_exists) { primary_config_dir = legacy_ccache_dir; } else if (env_xdg_config_home) { - primary_config_dir = fmt::format("{}/ccache", env_xdg_config_home); + primary_config_dir = FMT("{}/ccache", env_xdg_config_home); } else { primary_config_dir = default_config_dir(home_dir); } @@ -1969,7 +1966,7 @@ set_up_config(Config& config) if (legacy_ccache_dir_exists) { config.set_cache_dir(legacy_ccache_dir); } else if (env_xdg_cache_home) { - config.set_cache_dir(fmt::format("{}/ccache", env_xdg_cache_home)); + config.set_cache_dir(FMT("{}/ccache", env_xdg_cache_home)); } else { config.set_cache_dir(default_cache_dir(home_dir)); } @@ -2032,7 +2029,7 @@ set_up_uncached_err() throw Failure(Statistic::internal_error); } - Util::setenv("UNCACHED_ERR_FD", fmt::format("{}", uncached_fd)); + Util::setenv("UNCACHED_ERR_FD", FMT("{}", uncached_fd)); } static void @@ -2084,12 +2081,12 @@ update_stats_and_maybe_move_cache_file(const Context& ctx, const bool use_stats_on_level_1 = counter_updates.get(Statistic::cache_size_kibibyte) != 0 || counter_updates.get(Statistic::files_in_cache) != 0; - std::string level_string = fmt::format("{:x}", name.bytes()[0] >> 4); + std::string level_string = FMT("{:x}", name.bytes()[0] >> 4); if (!use_stats_on_level_1) { - level_string += fmt::format("/{:x}", name.bytes()[0] & 0xF); + level_string += FMT("/{:x}", name.bytes()[0] & 0xF); } const auto stats_file = - fmt::format("{}/{}/stats", ctx.config.cache_dir(), level_string); + FMT("{}/{}/stats", ctx.config.cache_dir(), level_string); auto counters = Statistics::update(stats_file, [&counter_updates](Counters& cs) { @@ -2150,8 +2147,8 @@ finalize_stats_and_trigger_cleanup(Context& ctx) // Context::set_result_path hasn't been called yet, so we just choose one of // the stats files in the 256 level 2 directories. const auto bucket = getpid() % 256; - const auto stats_file = fmt::format( - "{}/{:x}/{:x}/stats", config.cache_dir(), bucket / 16, bucket % 16); + const auto stats_file = + FMT("{}/{:x}/{:x}/stats", config.cache_dir(), bucket / 16, bucket % 16); Statistics::update( stats_file, [&ctx](Counters& cs) { cs.increment(ctx.counter_updates); }); return; @@ -2175,8 +2172,8 @@ finalize_stats_and_trigger_cleanup(Context& ctx) return; } - const auto subdir = fmt::format( - "{}/{:x}", config.cache_dir(), ctx.result_name()->bytes()[0] >> 4); + const auto subdir = + FMT("{}/{:x}", config.cache_dir(), ctx.result_name()->bytes()[0] >> 4); bool need_cleanup = false; if (config.max_files() != 0 @@ -2219,7 +2216,7 @@ finalize_at_exit(Context& ctx) // Dump log buffer last to not lose any logs. if (ctx.config.debug() && !ctx.args_info.output_obj.empty()) { - const auto path = fmt::format("{}.ccache-log", ctx.args_info.output_obj); + const auto path = FMT("{}.ccache-log", ctx.args_info.output_obj); Logging::dump_log(path); } } @@ -2364,8 +2361,7 @@ do_cache_compilation(Context& ctx, const char* const* argv) MTR_META_THREAD_NAME(ctx.args_info.output_obj.c_str()); if (ctx.config.debug()) { - std::string path = - fmt::format("{}.ccache-input-text", ctx.args_info.output_obj); + std::string path = FMT("{}.ccache-input-text", ctx.args_info.output_obj); File debug_text_file(path, "w"); if (debug_text_file) { ctx.hash_debug_files.push_back(std::move(debug_text_file)); diff --git a/src/compress.cpp b/src/compress.cpp index 307eb65e5..42e0179cd 100644 --- a/src/compress.cpp +++ b/src/compress.cpp @@ -172,7 +172,7 @@ recompress_file(RecompressionStatistics& statistics, LOG("Recompressing {} to {}", cache_file.path(), - level ? fmt::format("level {}", wanted_level) : "uncompressed"); + level ? FMT("level {}", wanted_level) : "uncompressed"); AtomicFile atomic_new_file(cache_file.path(), AtomicFile::Mode::binary); auto writer = create_writer(atomic_new_file.stream(), @@ -350,10 +350,10 @@ compress_recompress(Context& ctx, std::string incompr_size_str = Util::format_human_readable_size(statistics.incompressible_size()); std::string size_difference_str = - fmt::format("{}{}", - size_difference < 0 ? "-" : (size_difference > 0 ? "+" : " "), - Util::format_human_readable_size( - size_difference < 0 ? -size_difference : size_difference)); + FMT("{}{}", + size_difference < 0 ? "-" : (size_difference > 0 ? "+" : " "), + Util::format_human_readable_size( + size_difference < 0 ? -size_difference : size_difference)); PRINT(stdout, "Original data: {:>8s}\n", content_size_str); PRINT(stdout, diff --git a/src/execute.cpp b/src/execute.cpp index 32164929a..ec8703391 100644 --- a/src/execute.cpp +++ b/src/execute.cpp @@ -27,6 +27,7 @@ #include "Stat.hpp" #include "TemporaryFile.hpp" #include "Util.hpp" +#include "fmtmacros.hpp" #ifdef _WIN32 # include "Win32Util.hpp" @@ -110,7 +111,7 @@ win32execute(const char* path, if (args.length() > 8192) { TemporaryFile tmp_file(path); Util::write_fd(*tmp_file.fd, args.data(), args.length()); - args = fmt::format("\"@{}\"", tmp_file.path); + args = FMT("\"@{}\"", tmp_file.path); tmp_file_path = tmp_file.path; } BOOL ret = CreateProcess(full_path.c_str(), @@ -241,7 +242,7 @@ find_executable_in_path(const std::string& name, int ret = SearchPath( dir.c_str(), name.c_str(), nullptr, sizeof(namebuf), namebuf, nullptr); if (!ret) { - std::string exename = fmt::format("{}.exe", name); + std::string exename = FMT("{}.exe", name); ret = SearchPath(dir.c_str(), exename.c_str(), nullptr, @@ -255,7 +256,7 @@ find_executable_in_path(const std::string& name, } #else ASSERT(!exclude_name.empty()); - std::string fname = fmt::format("{}/{}", dir, name); + std::string fname = FMT("{}/{}", dir, name); auto st1 = Stat::lstat(fname); auto st2 = Stat::stat(fname); // Look for a normal executable file. diff --git a/src/fmtmacros.hpp b/src/fmtmacros.hpp index 27e63f7f0..9a017fae9 100644 --- a/src/fmtmacros.hpp +++ b/src/fmtmacros.hpp @@ -21,6 +21,10 @@ #include "third_party/fmt/core.h" #include "third_party/fmt/format.h" +// Convenience macro for calling `fmt::format` with `FMT_STRING` around the +// format string literal. +#define FMT(format_, ...) fmt::format(FMT_STRING(format_), __VA_ARGS__) + // Convenience macro for calling `fmt::print` with `FMT_STRING` around the // format string literal. #define PRINT(stream_, format_, ...) \ diff --git a/src/hashutil.cpp b/src/hashutil.cpp index db5ad3723..072d821d5 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -26,6 +26,7 @@ #include "Stat.hpp" #include "ccache.hpp" #include "execute.hpp" +#include "fmtmacros.hpp" #include "macroskip.hpp" #include "third_party/blake3/blake3_cpu_supports_avx2.h" @@ -361,12 +362,12 @@ hash_command_output(Hash& hash, // Add "echo" command. bool using_cmd_exe; if (Util::starts_with(adjusted_command, "echo")) { - adjusted_command = fmt::format("cmd.exe /c \"{}\"", adjusted_command); + adjusted_command = FMT("cmd.exe /c \"{}\"", adjusted_command); using_cmd_exe = true; } else if (Util::starts_with(adjusted_command, "%compiler%") && compiler == "echo") { adjusted_command = - fmt::format("cmd.exe /c \"{}{}\"", compiler, adjusted_command.substr(10)); + FMT("cmd.exe /c \"{}{}\"", compiler, adjusted_command.substr(10)); using_cmd_exe = true; } else { using_cmd_exe = false; diff --git a/unittest/TestUtil.cpp b/unittest/TestUtil.cpp index 04c4fe96e..5f53efeb1 100644 --- a/unittest/TestUtil.cpp +++ b/unittest/TestUtil.cpp @@ -20,6 +20,7 @@ #include "../src/Util.hpp" #include "../src/exceptions.hpp" +#include "../src/fmtmacros.hpp" namespace TestUtil { @@ -31,8 +32,7 @@ TestContext::TestContext() : m_test_dir(Util::get_actual_cwd()) throw Error("TestContext instantiated outside test directory"); } ++m_subdir_counter; - std::string subtest_dir = - fmt::format("{}/test_{}", m_test_dir, m_subdir_counter); + std::string subtest_dir = FMT("{}/test_{}", m_test_dir, m_subdir_counter); Util::create_dir(subtest_dir); if (chdir(subtest_dir.c_str()) != 0) { abort(); diff --git a/unittest/main.cpp b/unittest/main.cpp index 7f9b78ce2..6e9d02e1d 100644 --- a/unittest/main.cpp +++ b/unittest/main.cpp @@ -35,7 +35,7 @@ main(int argc, char** argv) Util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests. std::string dir_before = Util::get_actual_cwd(); - std::string testdir = fmt::format("testdir.{}", getpid()); + std::string testdir = FMT("testdir.{}", getpid()); Util::wipe_path(testdir); Util::create_dir(testdir); TestUtil::check_chdir(testdir); diff --git a/unittest/test_Config.cpp b/unittest/test_Config.cpp index 95787dbac..7543129ab 100644 --- a/unittest/test_Config.cpp +++ b/unittest/test_Config.cpp @@ -20,6 +20,7 @@ #include "../src/Util.hpp" #include "../src/ccache.hpp" #include "../src/exceptions.hpp" +#include "../src/fmtmacros.hpp" #include "TestUtil.hpp" #include "third_party/doctest.h" @@ -82,9 +83,9 @@ TEST_CASE("Config::update_from_file") Util::setenv("USER", user); #ifndef _WIN32 - std::string base_dir = fmt::format("/{0}/foo/{0}", user); + std::string base_dir = FMT("/{0}/foo/{0}", user); #else - std::string base_dir = fmt::format("C:/{0}/foo/{0}", user); + std::string base_dir = FMT("C:/{0}/foo/{0}", user); #endif Util::write_file( @@ -132,7 +133,7 @@ TEST_CASE("Config::update_from_file") Config config; REQUIRE(config.update_from_file("ccache.conf")); CHECK(config.base_dir() == base_dir); - CHECK(config.cache_dir() == fmt::format("{0}$/{0}/.ccache", user)); + CHECK(config.cache_dir() == FMT("{0}$/{0}/.ccache", user)); CHECK(config.compiler() == "foo"); CHECK(config.compiler_check() == "none"); CHECK_FALSE(config.compression()); @@ -141,7 +142,7 @@ TEST_CASE("Config::update_from_file") CHECK(config.depend_mode()); CHECK_FALSE(config.direct_mode()); CHECK(config.disable()); - CHECK(config.extra_files_to_hash() == fmt::format("a:b c:{}", user)); + CHECK(config.extra_files_to_hash() == FMT("a:b c:{}", user)); CHECK(config.file_clone()); CHECK(config.hard_link()); CHECK_FALSE(config.hash_dir()); @@ -149,12 +150,12 @@ TEST_CASE("Config::update_from_file") CHECK(config.ignore_options() == "-a=* -b"); CHECK(config.keep_comments_cpp()); CHECK(config.limit_multiple() == Approx(1.0)); - CHECK(config.log_file() == fmt::format("{0}{0}", user)); + CHECK(config.log_file() == FMT("{0}{0}", user)); CHECK(config.max_files() == 17); CHECK(config.max_size() == 123 * 1000 * 1000); - CHECK(config.path() == fmt::format("{}.x", user)); + CHECK(config.path() == FMT("{}.x", user)); CHECK(config.pch_external_checksum()); - CHECK(config.prefix_command() == fmt::format("x{}", user)); + CHECK(config.prefix_command() == FMT("x{}", user)); CHECK(config.prefix_command_cpp() == "y"); CHECK(config.read_only()); CHECK(config.read_only_direct()); @@ -166,7 +167,7 @@ TEST_CASE("Config::update_from_file") | SLOPPY_FILE_STAT_MATCHES_CTIME | SLOPPY_SYSTEM_HEADERS | SLOPPY_PCH_DEFINES | SLOPPY_CLANG_INDEX_STORE)); CHECK_FALSE(config.stats()); - CHECK(config.temporary_dir() == fmt::format("{}_foo", user)); + CHECK(config.temporary_dir() == FMT("{}_foo", user)); CHECK(config.umask() == 0777); } @@ -408,7 +409,7 @@ TEST_CASE("Config::visit_items") config.visit_items([&](const std::string& key, const std::string& value, const std::string& origin) { - received_items.push_back(fmt::format("({}) {} = {}", origin, key, value)); + received_items.push_back(FMT("({}) {} = {}", origin, key, value)); }); std::vector expected = { diff --git a/unittest/test_Statistics.cpp b/unittest/test_Statistics.cpp index d945848f2..5d6892c81 100644 --- a/unittest/test_Statistics.cpp +++ b/unittest/test_Statistics.cpp @@ -18,6 +18,7 @@ #include "../src/Statistics.hpp" #include "../src/Util.hpp" +#include "../src/fmtmacros.hpp" #include "TestUtil.hpp" #include "third_party/doctest.h" @@ -66,7 +67,7 @@ TEST_CASE("Read future counters") std::string content; size_t count = static_cast(Statistic::END) + 1; for (size_t i = 0; i < count; ++i) { - content += fmt::format("{}\n", i); + content += FMT("{}\n", i); } Util::write_file("test", content); diff --git a/unittest/test_Util.cpp b/unittest/test_Util.cpp index 5633c0220..44054f35e 100644 --- a/unittest/test_Util.cpp +++ b/unittest/test_Util.cpp @@ -19,6 +19,7 @@ #include "../src/Config.hpp" #include "../src/Fd.hpp" #include "../src/Util.hpp" +#include "../src/fmtmacros.hpp" #include "TestUtil.hpp" #include "third_party/doctest.h" @@ -910,7 +911,7 @@ TEST_CASE("Util::traverse") std::vector visited; auto visitor = [&visited](const std::string& path, bool is_dir) { - visited.push_back(fmt::format("[{}] {}", is_dir ? 'd' : 'f', path)); + visited.push_back(FMT("[{}] {}", is_dir ? 'd' : 'f', path)); }; SUBCASE("traverse nonexistent path") diff --git a/unittest/test_argprocessing.cpp b/unittest/test_argprocessing.cpp index c0f349e35..089d8495f 100644 --- a/unittest/test_argprocessing.cpp +++ b/unittest/test_argprocessing.cpp @@ -21,6 +21,7 @@ #include "../src/Context.hpp" #include "../src/Statistics.hpp" #include "../src/Util.hpp" +#include "../src/fmtmacros.hpp" #include "TestUtil.hpp" #include "argprocessing.hpp" @@ -283,7 +284,7 @@ TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used") Util::write_file("foo.c", ""); ctx.config.set_base_dir(get_root()); std::string arg_string = - fmt::format("cc --sysroot={}/foo/bar -c foo.c", ctx.actual_cwd); + FMT("cc --sysroot={}/foo/bar -c foo.c", ctx.actual_cwd); ctx.orig_args = Args::from_string(arg_string); const ProcessArgsResult result = process_args(ctx); @@ -300,8 +301,7 @@ TEST_CASE( Util::write_file("foo.c", ""); ctx.config.set_base_dir(get_root()); - std::string arg_string = - fmt::format("cc --sysroot {}/foo -c foo.c", ctx.actual_cwd); + std::string arg_string = FMT("cc --sysroot {}/foo -c foo.c", ctx.actual_cwd); ctx.orig_args = Args::from_string(arg_string); const ProcessArgsResult result = process_args(ctx); @@ -436,8 +436,7 @@ TEST_CASE( Util::write_file("foo.c", ""); ctx.config.set_base_dir(get_root()); - std::string arg_string = - fmt::format("cc -isystem {}/foo -c foo.c", ctx.actual_cwd); + std::string arg_string = FMT("cc -isystem {}/foo -c foo.c", ctx.actual_cwd); ctx.orig_args = Args::from_string(arg_string); const ProcessArgsResult result = process_args(ctx); @@ -455,7 +454,7 @@ TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used") ctx.config.set_base_dir("/"); // posix // Windows path doesn't work concatenated. std::string cwd = get_posix_path(ctx.actual_cwd); - std::string arg_string = fmt::format("cc -isystem{}/foo -c foo.c", cwd); + std::string arg_string = FMT("cc -isystem{}/foo -c foo.c", cwd); ctx.orig_args = Args::from_string(arg_string); const ProcessArgsResult result = process_args(ctx); @@ -473,7 +472,7 @@ TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used") ctx.config.set_base_dir("/"); // posix // Windows path doesn't work concatenated. std::string cwd = get_posix_path(ctx.actual_cwd); - std::string arg_string = fmt::format("cc -I{}/foo -c foo.c", cwd); + std::string arg_string = FMT("cc -I{}/foo -c foo.c", cwd); ctx.orig_args = Args::from_string(arg_string); const ProcessArgsResult result = process_args(ctx); diff --git a/unittest/test_ccache.cpp b/unittest/test_ccache.cpp index f204a74f5..209094ed2 100644 --- a/unittest/test_ccache.cpp +++ b/unittest/test_ccache.cpp @@ -16,9 +16,10 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "Context.hpp" +#include "../src/Context.hpp" +#include "../src/ccache.hpp" +#include "../src/fmtmacros.hpp" #include "TestUtil.hpp" -#include "ccache.hpp" #include "third_party/doctest.h" #include "third_party/nonstd/optional.hpp" @@ -161,10 +162,9 @@ TEST_CASE("rewrite_dep_file_paths") const auto cwd = ctx.actual_cwd; ctx.has_absolute_include_headers = true; - const auto content = - fmt::format("foo.o: bar.c {0}/bar.h \\\n {1}/fie.h {0}/fum.h\n", - cwd, - Util::dir_name(cwd)); + const auto content = FMT("foo.o: bar.c {0}/bar.h \\\n {1}/fie.h {0}/fum.h\n", + cwd, + Util::dir_name(cwd)); SUBCASE("Base directory not in dep file content") { @@ -175,7 +175,7 @@ TEST_CASE("rewrite_dep_file_paths") SUBCASE("Base directory in dep file content but not matching") { - ctx.config.set_base_dir(fmt::format("{}/other", Util::dir_name(cwd))); + ctx.config.set_base_dir(FMT("{}/other", Util::dir_name(cwd))); CHECK(!rewrite_dep_file_paths(ctx, "")); CHECK(!rewrite_dep_file_paths(ctx, content)); } @@ -184,8 +184,8 @@ TEST_CASE("rewrite_dep_file_paths") { ctx.config.set_base_dir(cwd); const auto actual = rewrite_dep_file_paths(ctx, content); - const auto expected = fmt::format( - "foo.o: bar.c ./bar.h \\\n {}/fie.h ./fum.h\n", Util::dir_name(cwd)); + const auto expected = + FMT("foo.o: bar.c ./bar.h \\\n {}/fie.h ./fum.h\n", Util::dir_name(cwd)); REQUIRE(actual); CHECK(*actual == expected); }