- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
本指南围绕 CMake 仓库中ctest_read_custom_files命令展开,讲解如何在 CTest 脚本模式下手动加载CTestCustom.cmake/CTestCustom.ctest自定义配置文件,剖析其底层文件搜索规则、默认行为与源码实现,并结合仓库测试用例给出可复用的实战示例。读完本文,你将掌握 CTest 自定义配置的加载机制,并能自主编写、调试自定义 CTest 配置文件。
命令概述:在脚本模式中主动读取 CTestCustom 文件
ctest_read_custom_files是 CTest 内置的脚本命令,用于从指定目录读取 CTest 自定义配置文件。官方命令文档(Help/command/ctest_read_custom_files.rst)给出的定义非常简洁:
Read all the
CTestCustom.ctestorCTestCustom.cmakefiles from the given directory.By default, invoking
ctest(1)without a script will read custom files from the binary directory.
即:该命令从给定目录读取CTestCustom.ctest或CTestCustom.cmake文件;默认情况下,不带脚本调用ctest(1)时会从二进制目录(binary directory)读取这些自定义文件。
它的典型应用场景是在ctest -S script.cmake脚本模式(CTest 脚本模式)下,当自定义配置文件不在默认的二进制目录、或者需要从多个位置加载配置时,用该命令显式指定配置来源。
命令语法与参数语义
命令签名如下:
ctest_read_custom_files(<directory>...)<directory>...表示可以同时传入一个或多个目录参数;- 命令会依次在每个目录中查找并读取自定义配置文件;
- 参数不能为空。源码 Source/CTest/cmCTestReadCustomFilesCommand.cxx 在
InitialPass中首先检查:
if (args.empty()) { status.SetError("called with incorrect number of arguments"); return false; }因此不带任何参数调用时,会直接报错 "called with incorrect number of arguments" 并以失败状态返回。
文件查找规则:扩展名优先级与目录语义
命令的实际文件查找逻辑位于 Source/cmCTest.cxx 的cmCTest::ReadCustomConfigurationFileTree方法中,其核心行为如下:
auto const fname = [this, &dir]() -> std::string { for (char const* ext : { ".cmake", ".ctest" }) { std::string path = cmStrCat(dir, "/CTestCustom", ext); cmCTestLog(this, DEBUG, "* Check for file: ", path, '\n'); if (cmSystemTools::FileExists(path)) { return path; } } return ""; }();从这段实现可以提炼出几条明确的规则:
- 文件名固定:只查找名为
CTestCustom的文件,扩展名为.cmake或.ctest; - 扩展名优先级:按
CTestCustom.cmake、CTestCustom.ctest的顺序逐个检查,.cmake扩展名优先——只有CTestCustom.cmake不存在时才回退到CTestCustom.ctest; - 目录非递归:只检查给定目录直接路径下的文件(
<dir>/CTestCustom.<ext>),不会递归搜索子目录; - 每个目录最多读取一个文件:文档中 "Read all the CTestCustom.ctest or CTestCustom.cmake files" 指的是对每个传入目录的完整处理(依次尝试两种扩展名),而非把一个目录下所有同名文件全部读入;
- 文件不存在时静默跳过:若两种扩展名均不存在,
fname为空字符串,命令不做任何操作、不报错,便于对目录统一执行而无需逐一判断。
读取与错误处理机制
找到文件后,实现通过cmMakefile::ReadListFile以 CMake 脚本方式解析执行该文件,并做了细致的错误隔离:
bool erroroc = cmSystemTools::GetErrorOccurredFlag(); cmSystemTools::ResetErrorOccurredFlag(); if (!mf->ReadListFile(fname) || cmSystemTools::GetErrorOccurredFlag()) { cmCTestLog(this, ERROR_MESSAGE, "Problem reading custom configuration: ", fname, '\n'); } if (erroroc) { cmSystemTools::SetErrorOccurred(); }要点解读:
- 自定义配置文件本质上是一段被
include进当前 makefile 上下文的 CMake 脚本,因此文件内可以执行任意 CMake 命令、设置变量、调用message()等; - 读取失败(文件解析错误或执行出错)时,会输出错误消息
Problem reading custom configuration: <file>,但会先保存并恢复原有的错误标志位,避免污染外层脚本的错误状态; - 调试模式下(
--debug)会打印 "* Check for file: ..." 与 "* Read custom CTest configuration file: ..." 等日志,可用于排查文件为何未被加载。
默认行为:二进制目录与 ctest_start 的隐式调用
命令文档明确指出:不带脚本调用ctest(1)时,默认从二进制目录读取自定义文件。这一点在源码中有两处印证:
- CTest 启动阶段会自动处理二进制目录下的自定义配置;
ctest_start命令内部同样会触发读取——见 Source/CTest/cmCTestStartCommand.cxx:
this->CTest->ReadCustomConfigurationFileTree(bld_dir, &mf);也就是说,在ctest_start()被调用时,CTest 会自动对构建目录执行与ctest_read_custom_files(<binary_dir>)等价的读取动作。这解释了为什么大多数项目只需把CTestCustom.cmake放进构建目录即可生效,无需手动调用本命令。
该命令在 CTest 脚本模式下的注册位置为 Source/CTest/cmCTestScriptHandler.cxx,属于 CTest 脚本处理器内置命令集合:
state->AddBuiltinCommand("ctest_read_custom_files", cmCTestReadCustomFilesCommand(this->CTest));实战:编写并加载 CTestCustom 配置文件
何时需要手动调用本命令
- 项目使用
ctest -S script.cmake脚本模式,且自定义配置文件位于非二进制目录的路径; - 需要在同一个脚本中从多个目录加载自定义配置;
- 自定义配置文件名或位置不符合默认约定,需要显式指定来源目录。
最小示例
假设项目的测试自定义配置位于源码树下的cmake/CTestCustom.cmake:
# 文件:cmake/CTestCustom.cmake message("Loading project custom CTest configuration") set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS 100) set(CTEST_CUSTOM_WARNING_EXCEPTION "C4996;LNK4221" # 示例:忽略的警告模式 )在 CTest 脚本(ctest -S my_driver.cmake)中加载它:
cmake_minimum_required(VERSION 3.20) # 显式从源码目录加载自定义配置 ctest_read_custom_files( "${CMAKE_CURRENT_LIST_DIR}/cmake" "${CMAKE_CURRENT_LIST_DIR}/tests" ) ctest_start(...) ctest_configure(...) ctest_build(...) ctest_test(...)执行后,脚本会把第一个存在的CTestCustom.cmake/CTestCustom.ctest读入当前上下文,后续ctest_*步骤即可使用其中定义的变量。
CTestCustom 文件中的变量约定
自定义配置文件的核心用途是覆写 CTest 的自定义变量。CTest 的CTEST_CUSTOM_XXX系列变量由ctest(1)初始化,但可以在 CTestCustom 文件中编辑,这一点在 Help/variable/include/CTEST_CUSTOM_XXX.rst 中有明确说明。
从源码看,CTest 在配置阶段会通过 Source/cmCTest.cxx 的PopulateCustomVector与PopulateCustomInteger从当前 makefile 定义中取出这些变量,分别填充为字符串列表与整数,用于控制 CTest 的各环节行为(例如警告/错误扫描、测试命令、覆盖率工具参数等):
void cmCTest::PopulateCustomVector(cmMakefile* mf, std::string const& def, std::vector<std::string>& vec) { cmValue dval = mf->GetDefinition(def); if (!dval) { return; } ... cmList::assign(vec, *dval); }因此,在 CTestCustom 文件中set(CTEST_CUSTOM_XXX ...)的写法,会直接影响后续测试、构建与覆盖率处理逻辑。
同时使用 .ctest 与 .cmake 的注意点
由于同一目录下.cmake扩展名优先,若同时存在CTestCustom.cmake与CTestCustom.ctest,只会读取前者。若两种文件需要同时生效,应分别放到不同目录并分两次传入目录参数(不同目录之间互不影响)。
递归与边界场景:仓库测试的验证
仓库在 Tests/RunCMake/MaxRecursionDepth/ 下提供了针对该命令的回归测试,用于验证其递归深度与错误处理行为:
- 测试脚本 Tests/RunCMake/MaxRecursionDepth/test.cmake.in 中,当
TEST_NAME为ctest_read_custom_files时,先设置变量x为 2,再调用ctest_read_custom_files读取测试源目录下的配置; - 被读取的 Tests/RunCMake/MaxRecursionDepth/CTestCustom.cmake 内容为:
message("${x}") math(EXPR x "${x} + 1") ctest_read_custom_files("${CMAKE_CURRENT_LIST_DIR}")即该自定义文件会在每次读取时打印当前x值、递增x,并再次调用ctest_read_custom_files指向自身目录,形成自我递归加载;
- 测试框架 Tests/RunCMake/MaxRecursionDepth/RunCMakeTest.cmake 通过
run_ctest_recursive(ctest_read_custom_files)覆盖了默认、变量设置CMAKE_MAXIMUM_RECURSION_DEPTH及环境变量版本等多种场景,并配合ctest_read_custom_files-*-stderr.txt基准输出验证错误信息与递归上限行为。
该测试同时揭示了两个重要事实:
ctest_read_custom_files读取的文件内可以再次调用该命令本身(以及嵌套include等其他递归加载手段),因此受CMAKE_MAXIMUM_RECURSION_DEPTH递归深度限制约束(参见 Help/variable/CMAKE_MAXIMUM_RECURSION_DEPTH.rst);- 该命令的读取行为与普通 CMake 脚本执行一致,可在自定义文件中定义变量、执行命令,乃至继续加载其他配置文件。
常见问题与排查要点
- 命令报 "called with incorrect number of arguments":检查是否漏掉了目录参数,
ctest_read_custom_files至少需要一个目录; - 文件未生效:确认文件名严格为
CTestCustom.cmake或CTestCustom.ctest(大小写敏感),且位于传入目录的直接路径下;可使用ctest --debug -S script.cmake观察 "* Check for file: ..." 日志确认查找过程; - 同目录双扩展名:记住
.cmake优先,必要时拆分目录或用两次调用分别指向不同目录; - 读取报 "Problem reading custom configuration":说明文件被找到但解析/执行失败,按普通 CMake 脚本语法错误排查文件内容。
小结
ctest_read_custom_files是 CTest 自定义配置体系中的显式加载入口:它以目录为参数、按.cmake优先于.ctest的顺序查找CTestCustom文件,并在 CTest 脚本上下文中执行之。理解其默认行为(无脚本时读取二进制目录、ctest_start隐式触发)、文件查找规则与错误处理细节,能帮助你在复杂测试驱动脚本中可靠地组织、加载和调试自定义 CTest 配置。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
CMake CTest `ctest_run_script` 命令详解:在 CTest 脚本中嵌套执行其他脚本
CMake CTest ctest_run_script 命令详解:在 CTest 脚本中嵌套执行其他脚本 ctest_run_script 是 CTest 脚
构建工具开发工具CLIF´ 组件命令字典(Component Dictionary)深度解读:从 XML 命令定义到自动生成的命令表
F´ 组件命令字典(Component Dictionary)深度解读:从 XML 命令定义到自动生成的命令表 导读 本文以 F´(Flight Softwar
嵌入式系统编程CMake CTest 的 ctest_update 命令:从源码树更新到 Update.xml 提交流程全解析
CMake CTest 的 ctest_update 命令:从源码树更新到 Update.xml 提交流程全解析 导读 ctest_update 是 CTest
构建工具开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考