CMake能否在测试定义目录外设置其属性?求因及解决方案
CMake跨目录设置测试属性失败的问题解答
问题背景
我想通过ctest -L实现结构化的测试执行,需要给单元测试设置标签,但在声明测试时无法确定要分配的标签。发现一个问题:CMake在非测试定义的目录中无法找到该测试,但是用相同逻辑修改目标属性却可以成功。
目录结构
. ├── CMakeLists.txt └── a ├── CMakeLists.txt ├── a.cpp └── test_a.cpp
根目录./CMakeLists.txt代码
cmake_minimum_required(VERSION 3.16) project(cmaketest LANGUAGES CXX) enable_testing() add_subdirectory(a) # 修改目标属性成功 set_target_properties(a PROPERTIES LABELS "bla") # 修改测试属性失败 set_tests_properties(mytest_a PROPERTIES LABELS "bla")
子目录./a/CMakeLists.txt代码
add_library(a src/a.cpp) # 添加测试 add_executable(test_a test/test_a.cpp) target_link_libraries(test_a a) add_test(NAME mytest_a COMMAND ./test_a)
错误输出
-- The CXX compiler identification is GNU 9.4.0 -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done CMake Error at CMakeLists.txt:9 (set_tests_properties): set_tests_properties Can not find test to add properties to: mytest_a -- Configuring incomplete, errors occurred!
在子目录a/CMakeLists.txt中调用set_tests_properties(mytest_a PROPERTIES LABELS "bla")可以成功,但在父目录中执行则失败。请问该行为是否是预期的?若为预期,原因是什么?是否有解决办法?
行为是否符合预期?
是,这是CMake的预期行为。
原因分析
CMake中目标(如库、可执行文件)是全局可见的,所以父目录可以直接修改子目录定义的目标属性;但测试的作用域是目录级的,默认情况下父目录无法直接引用子目录中定义的测试。
具体来说:当你在子目录调用add_test时,测试会被注册到子目录的专属测试集合中,父目录的CMake上下文无法直接访问该集合中的测试名称,因此set_tests_properties会报错找不到测试。
解决办法
针对“父目录统一设置测试标签”的需求,提供三种可行方案:
方案1:全局变量传递测试名称
在子目录中将测试名称存入全局缓存变量,父目录通过该变量批量设置属性:
- 修改子目录
a/CMakeLists.txt:
add_library(a src/a.cpp) add_executable(test_a test/test_a.cpp) target_link_libraries(test_a a) add_test(NAME mytest_a COMMAND ./test_a) # 将测试名称加入全局缓存变量 list(APPEND ALL_TESTS mytest_a) set(ALL_TESTS "${ALL_TESTS}" CACHE INTERNAL "全局测试列表")
- 修改根目录
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16) project(cmaketest LANGUAGES CXX) enable_testing() add_subdirectory(a) set_target_properties(a PROPERTIES LABELS "bla") # 通过全局变量设置所有测试的标签 if(ALL_TESTS) set_tests_properties(${ALL_TESTS} PROPERTIES LABELS "bla") endif()
方案2:子目录预留属性设置函数
在子目录定义专门的函数,父目录调用该函数完成属性设置:
- 修改子目录
a/CMakeLists.txt:
add_library(a src/a.cpp) add_executable(test_a test/test_a.cpp) target_link_libraries(test_a a) add_test(NAME mytest_a COMMAND ./test_a) # 定义设置测试属性的函数 function(set_a_test_properties) set_tests_properties(mytest_a PROPERTIES ${ARGV}) endfunction()
- 修改根目录
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16) project(cmaketest LANGUAGES CXX) enable_testing() add_subdirectory(a) set_target_properties(a PROPERTIES LABELS "bla") # 调用子目录函数设置测试属性 set_a_test_properties(LABELS "bla")
方案3:全局属性传递测试信息
利用CMake全局属性存储测试列表,父目录读取后设置属性:
- 修改子目录
a/CMakeLists.txt:
add_library(a src/a.cpp) add_executable(test_a test/test_a.cpp) target_link_libraries(test_a a) add_test(NAME mytest_a COMMAND ./test_a) # 将测试名称添加到全局属性 get_property(GLOBAL_TESTS GLOBAL PROPERTY ALL_GLOBAL_TESTS) list(APPEND GLOBAL_TESTS mytest_a) set_property(GLOBAL PROPERTY ALL_GLOBAL_TESTS "${GLOBAL_TESTS}")
- 修改根目录
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16) project(cmaketest LANGUAGES CXX) enable_testing() add_subdirectory(a) set_target_properties(a PROPERTIES LABELS "bla") # 获取全局属性中的测试列表并设置属性 get_property(GLOBAL_TESTS GLOBAL PROPERTY ALL_GLOBAL_TESTS) if(GLOBAL_TESTS) set_tests_properties(${GLOBAL_TESTS} PROPERTIES LABELS "bla") endif()
内容的提问来源于stack exchange,提问作者saragossa
相关产品推荐
相关产品推荐

