From: Evgeniy Isaev Date: Sun, 19 Oct 2025 17:59:15 +0000 (+0300) Subject: feat: Add support for -fcoverage-compilation-dir/-fcoverage-prefix-map (#1644) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=99cdbeea27348a6a1b39945056fbde97127f247b;p=thirdparty%2Fccache.git feat: Add support for -fcoverage-compilation-dir/-fcoverage-prefix-map (#1644) --- diff --git a/src/ccache/argprocessing.cpp b/src/ccache/argprocessing.cpp index 8d431960..063bac34 100644 --- a/src/ccache/argprocessing.cpp +++ b/src/ccache/argprocessing.cpp @@ -733,6 +733,13 @@ process_option_arg(const Context& ctx, return Statistic::none; } + if (util::starts_with(arg, "-fcoverage-prefix-map=")) { + std::string map = arg.substr(arg.find('=') + 1); + args_info.coverage_prefix_maps.push_back(map); + state.add_common_arg(args[i]); + return Statistic::none; + } + if (util::starts_with(arg, "-fdebug-compilation-dir") || util::starts_with(arg, "-ffile-compilation-dir")) { std::string compilation_dir; @@ -756,6 +763,13 @@ process_option_arg(const Context& ctx, return Statistic::none; } + if (std::string_view prefix{"-fcoverage-compilation-dir="}; + util::starts_with(arg, prefix)) { + args_info.coverage_compilation_dir = arg.substr(prefix.length()); + state.add_common_arg(args[i]); + return Statistic::none; + } + // Debugging is handled specially, so that we know if we can strip line // number info. if (util::starts_with(arg, "-g")) { @@ -1348,6 +1362,11 @@ process_args(Context& ctx) } } + std::reverse(args_info.debug_prefix_maps.begin(), + args_info.debug_prefix_maps.end()); + std::reverse(args_info.coverage_prefix_maps.begin(), + args_info.coverage_prefix_maps.end()); + const bool is_link = !(state.found_c_opt || state.found_dc_opt || state.found_S_opt || state.found_syntax_only || state.found_analyze_opt); diff --git a/src/ccache/argsinfo.hpp b/src/ccache/argsinfo.hpp index a1ba647b..283ad38a 100644 --- a/src/ccache/argsinfo.hpp +++ b/src/ccache/argsinfo.hpp @@ -163,10 +163,16 @@ struct ArgsInfo // Relocating debuginfo in the format old=new. std::vector debug_prefix_maps; + // Relocating coverage info in the format old=new. + std::vector coverage_prefix_maps; + // Compilation directory as passed in -ffile-compilation-dir or // -fdebug-compilation-dir. std::string compilation_dir; + // Coverage compilation directory as passed in -fcoverage-compilation-dir. + std::string coverage_compilation_dir; + // Build session file as passed in -fbuild-session-file. std::filesystem::path build_session_file; }; diff --git a/src/ccache/ccache.cpp b/src/ccache/ccache.cpp index e95ac6b7..6e503d31 100644 --- a/src/ccache/ccache.cpp +++ b/src/ccache/ccache.cpp @@ -1563,6 +1563,32 @@ hash_nvcc_host_compiler(const Context& ctx, return {}; } +static void +apply_prefix_remapping(const std::vector& maps, fs::path& path) +{ + for (const auto& map : maps) { + const size_t sep_pos{map.find('=')}; + if (sep_pos == std::string::npos) { + continue; + } + + const std::string old_prefix{map.substr(0, sep_pos)}; + const std::string new_prefix{map.substr(sep_pos + 1)}; + if (!util::starts_with(util::pstr(path).str(), old_prefix)) { + continue; + } + + LOG("Relocating from '{}' to '{}' (original path: '{}')", + old_prefix, + new_prefix, + path); + fs::path suffix{util::pstr(path).str().substr(old_prefix.size())}; + path = new_prefix / suffix; + + return; + } +} + // update a hash with information common for the direct and preprocessor modes. static tl::expected hash_common_info(const Context& ctx, const util::Args& args, Hash& hash) @@ -1641,26 +1667,12 @@ hash_common_info(const Context& ctx, const util::Args& args, Hash& hash) // Possibly hash the current working directory. if (ctx.args_info.generating_debuginfo && ctx.config.hash_dir()) { - std::string dir_to_hash = util::pstr(ctx.apparent_cwd); + fs::path dir_to_hash{ctx.apparent_cwd}; if (!ctx.args_info.compilation_dir.empty()) { dir_to_hash = ctx.args_info.compilation_dir; } else { - for (const auto& map : ctx.args_info.debug_prefix_maps) { - size_t sep_pos = map.find('='); - if (sep_pos != std::string::npos) { - std::string old_path = map.substr(0, sep_pos); - std::string new_path = map.substr(sep_pos + 1); - LOG("Relocating debuginfo from {} to {} (CWD: {})", - old_path, - new_path, - ctx.apparent_cwd); - if (util::starts_with(util::pstr(ctx.apparent_cwd).str(), old_path)) { - dir_to_hash = - new_path - + util::pstr(ctx.apparent_cwd).str().substr(old_path.size()); - } - } - } + LOG("Applying debug prefix maps to CWD path '{}'", dir_to_hash); + apply_prefix_remapping(ctx.args_info.debug_prefix_maps, dir_to_hash); } LOG("Hashing CWD {}", dir_to_hash); hash.hash_delimiter("cwd"); @@ -1949,6 +1961,11 @@ hash_argument(const Context& ctx, hash.hash("-fprofile-prefix-path="); return {}; } + if (util::starts_with(args[i], "-fcoverage-prefix-map=")) { + hash.hash_delimiter("arg"); + hash.hash("-fcoverage-prefix-map="); + return {}; + } if (util::starts_with(args[i], "-frandom-seed=") && ctx.config.sloppiness().contains(core::Sloppy::random_seed)) { @@ -2284,11 +2301,17 @@ hash_profiling_related_data(const Context& ctx, Hash& hash) // For a relative profile directory D the compiler stores $PWD/D as part of // the profile filename so we need to include the same information in the // hash. - const fs::path profile_path = - ctx.args_info.profile_path.is_absolute() - ? ctx.args_info.profile_path - : ctx.apparent_cwd / ctx.args_info.profile_path; - LOG("Adding profile directory {} to our hash", profile_path); + fs::path profile_path = ctx.args_info.profile_path.is_absolute() + ? ctx.args_info.profile_path + : ctx.apparent_cwd / ctx.args_info.profile_path; + + if (!ctx.args_info.coverage_compilation_dir.empty()) { + profile_path = ctx.args_info.coverage_compilation_dir; + } else if (!ctx.args_info.coverage_prefix_maps.empty()) { + LOG("Applying coverage prefix maps to profile path '{}'", profile_path); + apply_prefix_remapping(ctx.args_info.coverage_prefix_maps, profile_path); + } + LOG("Adding profile directory '{}' to our hash", profile_path); hash.hash_delimiter("-fprofile-dir"); hash.hash(profile_path); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ab3c3b9f..c8225dc1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,8 @@ addtest(clang_cu_direct) addtest(cleanup) addtest(color_diagnostics) addtest(config) +addtest(coverage_compilation_dir) +addtest(coverage_prefix_map) addtest(debug_compilation_dir) addtest(debug_prefix_map) addtest(depend) diff --git a/test/suites/coverage_compilation_dir.bash b/test/suites/coverage_compilation_dir.bash new file mode 100644 index 00000000..0d8775fd --- /dev/null +++ b/test/suites/coverage_compilation_dir.bash @@ -0,0 +1,88 @@ +SUITE_coverage_compilation_dir_PROBE() { + touch test.c + if ! $COMPILER -c -fcoverage-compilation-dir=dir test.c 2>/dev/null; then + echo "-fcoverage-compilation-dir not supported by compiler" + fi + + if ! $RUN_WIN_XFAIL; then + echo "coverage-compilation-dir tests are broken on Windows." + return + fi +} + +SUITE_coverage_compilation_dir_SETUP() { + unset CCACHE_NODIRECT + + mkdir -p dir1/src dir1/include + cat <dir1/src/test.c +#include +#include +EOF + cat <dir1/include/test.h +int test; +EOF + cp -r dir1 dir2 + backdate dir1/include/test.h dir2/include/test.h +} + +SUITE_coverage_compilation_dir() { + # ------------------------------------------------------------------------- + TEST "Cache misses without a configured coverage compilation directory" + + cd dir1 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + cd ../dir2 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 2 + expect_stat files_in_cache 4 + + # ------------------------------------------------------------------------- + TEST "Cache hits with a configured coverage compilation directory" + + cd dir1 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-compilation-dir=some_other_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + cd ../dir2 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-compilation-dir=some_other_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 1 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 +} diff --git a/test/suites/coverage_prefix_map.bash b/test/suites/coverage_prefix_map.bash new file mode 100644 index 00000000..b9ef45aa --- /dev/null +++ b/test/suites/coverage_prefix_map.bash @@ -0,0 +1,154 @@ +SUITE_coverage_prefix_map_PROBE() { + touch test.c + if ! $COMPILER -c -fcoverage-prefix-map=old=new test.c 2>/dev/null; then + echo "-fcoverage-prefix-map not supported by compiler" + fi + + if ! $RUN_WIN_XFAIL; then + echo "coverage_prefix_map tests are broken on Windows." + return + fi +} + +SUITE_coverage_prefix_map_SETUP() { + unset CCACHE_NODIRECT + + mkdir -p dir1/src dir1/include + cat <dir1/src/test.c +#include +#include +EOF + cat <dir1/include/test.h +int test; +EOF + cp -r dir1 dir2 + backdate dir1/include/test.h dir2/include/test.h +} + +SUITE_coverage_prefix_map() { + # ------------------------------------------------------------------------- + TEST "Cache misses without a configured coverage prefix map" + + cd dir1 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + cd ../dir2 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 2 + expect_stat files_in_cache 4 + + # ------------------------------------------------------------------------- + TEST "Cache hits with a configured coverage prefix map" + + cd dir1 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=$(pwd)=some_other_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + cd ../dir2 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=$(pwd)=some_other_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 1 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + # ------------------------------------------------------------------------- + TEST "Cache hits with multiple configured coverage prefix maps" + + cd dir1 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=$(pwd)=some_other_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=foo=bar \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + cd ../dir2 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=$(pwd)=some_other_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=foo=bar \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 1 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + # ------------------------------------------------------------------------- + TEST "Cache hits with multiple configured coverage prefix maps (different order)" + + cd dir1 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=foo=bar \ + -fcoverage-prefix-map=$(pwd)=some_other_name_not_likely_to_exist_in_path \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 0 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 + + cd ../dir2 + CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE \ + -I$(pwd)/include \ + -g \ + -fprofile-instr-generate \ + -fdebug-compilation-dir=some_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=$(pwd)=some_other_name_not_likely_to_exist_in_path \ + -fcoverage-prefix-map=foo=bar \ + -c $(pwd)/src/test.c \ + -o $(pwd)/test.o + expect_stat direct_cache_hit 1 + expect_stat preprocessed_cache_hit 0 + expect_stat cache_miss 1 + expect_stat files_in_cache 2 +}