CMake 秘籍(八)(5)

简介: CMake 秘籍(八)

CMake 秘籍(八)(4)https://developer.aliyun.com/article/1525077

实现多步骤测试

src/testdir/Makefile中的目标表明 Vim 代码以多步骤测试运行:首先,vim可执行文件处理一个脚本并生成一个输出文件,然后在第二步中,输出文件与参考文件进行比较,如果这些文件没有差异,则测试成功。临时文件随后在第三步中被删除。这可能无法以可移植的方式适应单个add_test命令,因为add_test只能执行一个命令。一个解决方案是将测试步骤定义在一个 Python 脚本中,并用一些参数执行该 Python 脚本。我们将在这里介绍的另一种替代方案也是跨平台的,即将测试步骤定义在一个单独的 CMake 脚本中,并从add_test执行该脚本。我们将在src/testdir/test.cmake中定义测试步骤:

function(execute_test _vim_executable _working_dir _test_script)
  # generates test.out
  execute_process(
    COMMAND ${_vim_executable} -f -u unix.vim -U NONE --noplugin --not-a-term -s dotest.in ${_test_script}.in
    WORKING_DIRECTORY ${_working_dir}
    )
  # compares test*.ok and test.out
  execute_process(
    COMMAND ${CMAKE_COMMAND} -E compare_files ${_test_script}.ok test.out
    WORKING_DIRECTORY ${_working_dir}
    RESULT_VARIABLE files_differ
    OUTPUT_QUIET
    ERROR_QUIET
    )
  # removes leftovers
  file(REMOVE ${_working_dir}/Xdotest)
  # we let the test fail if the files differ
  if(files_differ)
    message(SEND_ERROR "test ${_test_script} failed")
  endif()
endfunction()
execute_test(${VIM_EXECUTABLE} ${WORKING_DIR} ${TEST_SCRIPT})

再次,我们选择函数而非宏来确保变量不会逃逸函数作用域。我们将处理这个脚本,该脚本将调用execute_test函数。然而,我们必须确保从外部定义了${VIM_EXECUTABLE}${WORKING_DIR}${TEST_SCRIPT}。这些在src/testdir/CMakeLists.txt中定义:

add_test(
  NAME
    test1
  COMMAND
    ${CMAKE_COMMAND} -D VIM_EXECUTABLE=$<TARGET_FILE:vim>
                     -D WORKING_DIR=${CMAKE_CURRENT_LIST_DIR}
                     -D TEST_SCRIPT=test1
                     -P ${CMAKE_CURRENT_LIST_DIR}/test.cmake
  WORKING_DIRECTORY
    ${PROJECT_BINARY_DIR}
  )

Vim 项目有许多测试,但在本例中,我们只移植了一个(test1)作为概念验证。

测试建议

我们至少可以给出两个关于移植测试的建议。首先,确保测试不会总是报告成功,如果代码被破坏或参考数据被更改,请验证测试是否失败。其次,为测试添加COST估计,以便在并行运行时,较长的测试首先启动,以最小化总测试时间(参见第四章,创建和运行测试,第 8 个配方,并行运行测试)。

移植安装目标

我们现在可以配置、编译、链接和测试代码,但我们缺少安装目标,我们将在本节中添加它。

这是 Autotools 构建和安装代码的方法:

$ ./configure --prefix=/some/install/path
$ make
$ make install

这就是 CMake 的方式:

$ mkdir -p build
$ cd build
$ cmake -D CMAKE_INSTALL_PREFIX=/some/install/path ..
$ cmake --build .
$ cmake --build . --target install

要添加安装目标,我们需在src/CMakeLists.txt中添加以下代码片段:

install(
  TARGETS
    vim
  RUNTIME DESTINATION
    ${CMAKE_INSTALL_BINDIR}
  )

在本例中,我们只安装了可执行文件。Vim 项目在安装二进制文件的同时安装了大量文件(符号链接和文档文件)。为了使本节易于理解,我们没有在本例迁移中安装所有其他文件。对于你自己的项目,你应该验证安装步骤的结果是否与遗留构建框架的安装目标相匹配。

进一步的步骤

成功移植到 CMake 后,下一步应该是进一步限定目标和变量的范围:考虑将选项、目标和变量移动到它们被使用和修改的位置附近。避免全局变量,因为它们会强制 CMake 命令的顺序,而这个顺序可能不明显,会导致脆弱的 CMake 代码。一种强制分离变量范围的方法是将大型项目划分为 CMake 项目,这些项目使用超级构建模式(参见第八章,超级构建模式)。考虑将大型CMakeLists.txt文件拆分为较小的模块。

接下来的步骤可能是在其他平台和操作系统上测试配置和编译,以便使 CMake 代码更加通用和防弹,并使其更具可移植性。

最后,在将项目迁移到新的构建框架时,开发社区也需要适应它。通过培训、文档和代码审查帮助你的同事。在将代码移植到 CMake 时,最难的部分可能是改变人的习惯。

转换项目到 CMake 时的总结和常见陷阱

让我们总结一下本章我们取得了哪些成就以及我们学到了什么。

代码变更总结

在本章中,我们讨论了如何将项目移植到 CMake。我们以 Vim 项目为例,并添加了以下文件:

.
├── CMakeLists.txt
└── src
    ├── autogenerate.cmake
    ├── CMakeLists.txt
    ├── config.h.cmake.in
    ├── libvterm
    │   └── CMakeLists.txt
    ├── pathdef.c.in
    └── testdir
        ├── CMakeLists.txt
        └── test.cmake

可以在线浏览变更:github.com/dev-cafe/vim/compare/b476cb7...cmake-support

这是一个不完整的 CMake 移植概念证明,我们省略了许多选项和调整以简化,并试图专注于最突出的特性和步骤。

常见陷阱

我们希望通过指出转向 CMake 时的一些常见陷阱来结束这次讨论。

  • 全局变量是代码异味:这在任何编程语言中都是如此,CMake 也不例外。跨越 CMake 文件的变量,特别是从叶子到父级CMakeLists.txt文件“向上”传递的变量,表明代码存在问题。通常有更好的方式来传递依赖。理想情况下,依赖应该通过目标来导入。不要将一系列库组合成一个变量并在文件之间传递该变量,而是将库一个接一个地链接到它们定义的位置附近。不要将源文件组合成变量,而是使用target_sources添加源文件。在链接库时,如果可用,使用导入的目标而不是变量。
  • 最小化顺序影响:CMake 不是一种声明式语言,但我们也不应该用命令式范式来处理它。强制严格顺序的 CMake 源码往往比较脆弱。这也与变量的讨论有关(见前一段)。某些语句和模块的顺序是必要的,但为了得到稳健的 CMake 框架,我们应该避免不必要的顺序强制。使用target_sourcestarget_compile_definitionstarget_include_directoriestarget_link_libraries。避免全局范围的语句,如add_definitionsinclude_directorieslink_libraries。避免全局定义编译标志。如果可能,为每个目标定义编译标志。
  • 不要将生成的文件放置在构建目录之外:强烈建议永远不要将生成的文件放置在构建目录之外。这样做的原因是,生成的文件通常依赖于所选的选项、编译器或构建类型,而将文件写入源代码树中,我们放弃了维护多个具有相同源代码的构建的可能性,并且使构建步骤的可重复性变得复杂。
  • 优先使用函数而非宏:它们具有不同的作用域,函数作用域是有限的。所有变量修改都需要明确标记,这也向读者表明了变量重定义。当你必须使用宏时使用,但如果你能使用函数,则优先使用函数。
  • 避免 shell 命令:它们可能不兼容其他平台(如 Windows)。优先使用 CMake 的等效命令。如果没有可用的 CMake 等效命令,考虑调用 Python 脚本。
  • 在 Fortran 项目中,注意后缀大小写:需要预处理的 Fortran 源文件应具有大写的.F90后缀。不需要预处理的源文件应具有小写的.f90后缀。
  • 避免显式路径:无论是在定义目标时还是在引用文件时都是如此。使用CMAKE_CURRENT_LIST_DIR来引用当前路径。这样做的好处是,当你移动或重命名目录时,它仍然有效。
  • 模块包含不应是函数调用:将 CMake 代码模块化是一个好的策略,但包含模块理想情况下不应执行 CMake 代码。相反,应将 CMake 代码封装到函数和宏中,并在包含模块后显式调用这些函数和宏。这可以防止无意中多次包含模块时产生的不良后果,并使执行 CMake 代码模块的动作对读者更加明确。
相关文章
|
5月前
|
编译器 Shell
CMake 秘籍(八)(3)
CMake 秘籍(八)
33 2
|
5月前
|
编译器 Linux C语言
CMake 秘籍(二)(2)
CMake 秘籍(二)
45 2
|
5月前
|
Linux 编译器 C++
CMake 秘籍(七)(2)
CMake 秘籍(七)
36 1
|
5月前
|
Linux API iOS开发
CMake 秘籍(六)(1)
CMake 秘籍(六)
38 1
|
5月前
|
Linux C++ iOS开发
CMake 秘籍(三)(4)
CMake 秘籍(三)
31 1
|
5月前
|
XML 监控 Linux
CMake 秘籍(七)(4)
CMake 秘籍(七)
45 0
|
5月前
|
Linux C++ iOS开发
CMake 秘籍(七)(1)
CMake 秘籍(七)
27 0
|
5月前
|
并行计算 Unix 编译器
CMake 秘籍(七)(5)
CMake 秘籍(七)
57 0
|
5月前
|
编译器 测试技术 开发工具
CMake 秘籍(八)(4)
CMake 秘籍(八)
25 0
|
5月前
|
并行计算 编译器 Linux
CMake 秘籍(二)(3)
CMake 秘籍(二)
26 0
下一篇
无影云桌面