如何将CMake CTest测试用例提升至超级项目顶层统一执行?
I've run into this exact issue with nested CMake projects before—here's how to fix it so you can run all tests from the superproject root without hopping into each subdirectory:
The Root Cause
Your current setup has each subproject calling enable_testing() on its own, which creates a separate CTestTestfile.cmake for every subdirectory. CTest only looks at the test file in the current working directory by default, so running ctest from the root doesn't pick up the subproject tests.
Recommended Fix: Centralize CTest Setup
The cleanest solution is to handle the CTest initialization once at the superproject level, then let all subprojects register their tests to the top-level test suite.
Step 1: Update the Superproject's CMakeLists.txt
Replace any existing enable_testing() call in the root CMakeLists.txt with include(CTest) (this is the preferred approach because it loads the full CTest module with extra features). Make sure this comes before adding your subprojects:
cmake_minimum_required(VERSION 3.10) project(superproject) # Initialize CTest at the top level—this creates the root CTestTestfile.cmake include(CTest) # Add all your subprojects as usual add_subdirectory(project1) add_subdirectory(project2) # ... add projectN and any other subprojects
Step 2: Modify Subproject CMakeLists.txt Files
Remove the enable_testing() line from every subproject (and sub-subproject) CMakeLists.txt. Since the top level already initialized CTest, any add_test() calls in subprojects will automatically register to the top-level test suite.
Your subproject test code should look like this:
# ${subprojectName} was set earlier in this file # REMOVE the enable_testing() line here! file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test/*.cpp) foreach(testSrc ${TEST_SRCS}) get_filename_component(testName ${testSrc} NAME_WE) set(testName ${subprojectName}_${testName}) add_executable(${testName} ${testSrc}) target_link_libraries(${testName} ${subprojectName} Boost::unit_test_framework Boost::dynamic_linking) add_test(NAME ${testName} COMMAND ${testName}) endforeach(testSrc)
How It Works
When you call include(CTest) at the root, CMake enables testing globally and creates a single CTestTestfile.cmake in the root build directory. Every subsequent add_test() (regardless of which subdirectory it's in) appends its test definition to this root file. Now, running ctest from the superproject build directory will discover and execute all tests from all subprojects.
Alternative: Auto-Include Subproject Test Files (If You Can't Modify Subprojects)
If you can't edit the subproject CMakeLists.txt files (e.g., they're external dependencies), you can manually include all subproject CTestTestfile.cmake files in the root. Add this to the end of your superproject's CMakeLists.txt:
# Recursively find all CTestTestfile.cmake files in the build directory file(GLOB_RECURSE SUB_CTEST_FILES "${CMAKE_BINARY_DIR}/**/CTestTestfile.cmake") foreach(TEST_FILE ${SUB_CTEST_FILES}) # Skip the root's own test file to avoid duplication if(NOT TEST_FILE STREQUAL "${CMAKE_BINARY_DIR}/CTestTestfile.cmake") include(${TEST_FILE}) endif() endforeach()
This will pull all subproject tests into the root test suite. Note that this approach can have edge cases (e.g., tests that rely on subdirectory-specific environment variables), but it works for most standard setups.
Verify It Works
After making these changes, run your usual build commands from the superproject root:
mkdir build && cd build cmake .. cmake --build . ctest
You should see all tests from every subproject listed and executed in the output.
内容的提问来源于stack exchange,提问作者Stewart

