如何让CMake Ninja生成器将gcno文件放入对应目标目录?
Okay, let's tackle this issue—you’re exactly right that the root cause is how Make vs. Ninja handles working directories during compilation. Make switches to the target’s specific build directory before running the compiler, so GCC drops .gcno files right where they belong. Ninja runs all commands from the root build directory, which dumps those files in the top level and causes conflicts when you have duplicate source filenames across targets.
Here are the most reliable ways to fix this:
1. Explicitly Set the Profile Output Directory with -fprofile-dir
The simplest and most maintainable fix is to tell GCC exactly where to generate .gcno files using the -fprofile-dir flag, tied to each target’s build directory via CMake generator expressions.
Instead of global compile options, use a target-specific function to ensure coverage settings are applied correctly per project:
function(target_enable_coverage target) # Set compile flags for coverage target_compile_options(${target} PRIVATE "$<$<CONFIG:COVERAGE>:-g;-O0;-ftest-coverage;-fprofile-arcs;-fno-builtin>" # Direct gcno files to the target's CMakeFiles directory "$<$<CONFIG:COVERAGE>:-fprofile-dir=$<TARGET_FILE_DIR:${target}>/../CMakeFiles/${target}.dir>" ) # Don't forget link flags for coverage target_link_options(${target} PRIVATE "$<$<CONFIG:COVERAGE>:-ftest-coverage;-fprofile-arcs>" ) endfunction() # Apply to your targets (repeat for each target needing coverage) target_enable_coverage(libblake2)
This ensures every target’s .gcno files land in its own CMakeFiles/<target>.dir subdirectory, matching the behavior you get with Makefiles.
2. Force Ninja to Compile in the Target’s Build Directory
If you prefer to mimic Make’s working directory behavior directly, you can override CMake’s compile command variables to make Ninja switch to the target’s build directory before running the compiler.
For C projects:
if(CMAKE_GENERATOR STREQUAL "Ninja" AND CMAKE_BUILD_TYPE STREQUAL "COVERAGE") set(CMAKE_C_COMPILE_OBJECT "cd ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/$<TARGET_NAME>.dir && <CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>" ) endif()
For C++ projects, replace CMAKE_C_COMPILE_OBJECT with CMAKE_CXX_COMPILE_OBJECT. The generator expression $<TARGET_NAME> dynamically inserts the target’s name, so this works across all your projects without hardcoding paths.
Why This Works
When using -ftest-coverage without -fprofile-dir, GCC generates .gcno files in the directory where the compiler was executed (the working directory). Make handles this by changing directories first; with either method above, we either explicitly tell GCC where to put the files or make Ninja replicate Make’s directory-switching behavior.
3. Verify the Fix
After applying either solution, rebuild your project with coverage enabled to confirm:
# Clean previous build artifacts cmake --build . --config COVERAGE --clean-first # Rebuild with coverage cmake --build . --config COVERAGE
Check the CMakeFiles/<your-target>.dir subdirectories—your .gcno files should now be there instead of cluttering the root build directory.
内容的提问来源于stack exchange,提问作者Marnix

