CMake 系列(十八):将 CMake 源码分成模块

项目通常从单个 CMakeLists.txt 文件开始,随着时间的推移,这个文件会逐渐增长。本篇演示一种将 CMakeLists.txt 拆分成更小单元的机制。将 CMakeLists.txt 拆分为模块有几个动机,这些模块可以包含在主 CMakeLists.txt 或其他模块中:

  • 主 CMakeLists.txt 更易于阅读。
  • CMake 模块可以在其他项目中重用。
  • 与函数相结合,模块可以帮助我们限制变量的作用范围。

本篇将演示如何定义和包含一个宏,该宏让我们能够获得 CMake 的彩色输出(用于重要的状态消息或警告)。

注意:本示例在 CMake 3.5 或更高版本中有效,并且已经在 GNU/Linux、macOS 和 Windows 上进行过测试。

准备工作

本例中使用两个文件:主 CMakeLists.txt 和 cmake/colors.cmake:

.
├── cmake
│     └── colors.cmake
└── CMakeLists.txt

cmake/colors.cmake 文件包含彩色输出的定义:

# colorize CMake output
# code adapted from stackoverflow
# from post authored by fraser
macro(define_colors)
  if(WIN32)
    # has no effect on WIN32
    set(ColourReset "")
    set(ColourBold "")
    set(Red "")
    set(Green "")
    set(Yellow "")
    set(Blue "")
    set(Magenta "")
    set(Cyan "")
    set(White "")
    set(BoldRed "")
    set(BoldGreen "")
    set(BoldYellow "")
    set(BoldBlue "")
    set(BoldMagenta "")
    set(BoldCyan "")
    set(BoldWhite "")
  else()
    string(ASCII 27 Esc)
    set(ColourReset "${Esc}[m")
    set(ColourBold "${Esc}[1m")
    set(Red "${Esc}[31m")
    set(Green "${Esc}[32m")
    set(Yellow "${Esc}[33m")
    set(Blue "${Esc}[34m")
    set(Magenta "${Esc}[35m")
    set(Cyan "${Esc}[36m")
    set(White "${Esc}[37m")
    set(BoldRed "${Esc}[1;31m")
    set(BoldGreen "${Esc}[1;32m")
    set(BoldYellow "${Esc}[1;33m")
    set(BoldBlue "${Esc}[1;34m")
    set(BoldMagenta "${Esc}[1;35m")
    set(BoldCyan "${Esc}[1;36m")
    set(BoldWhite "${Esc}[1;37m")
  endif()
endmacro()

具体实施

来看下我们如何使用颜色定义,生成彩色的状态消息。

从一个熟悉的头部开始:

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

project(recipe-02 LANGUAGES NONE)

然后,将 cmake 子目录添加到 CMake 模块搜索路径列表中:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

引入 colors.cmake 模块,并调用其中定义的宏:

include(colors)

define_colors()

最后,打印不同颜色的消息。在 macOS 或 Linux 终端下配置项目时,屏幕上就会出现彩色的状态输出:

message(STATUS "This is a normal message")
message(STATUS "${Red}This is a red${ColourReset}")
message(STATUS "${BoldRed}This is a bold red${ColourReset}")
message(STATUS "${Green}This is a green${ColourReset}")
message(STATUS "${BoldMagenta}This is bold${ColourReset}")

工作原理

这个例子中不需要编译代码,也不需要语言支持,我们已经用 LANGUAGES NONE 明确了这一点:

project(recipe-02 LANGUAGES NONE)

我们定义了 define_colors 宏,并将其放在 cmake/colors.cmake 中。由于仍然希望在调用宏后使用其中定义的变量来更改消息的颜色,所以我们选择使用宏而不是函数。我们使用以下两行引入宏并调用 define_colors:

include(colors)
define_colors()

我们还需要告诉 CMake 去哪里查找宏:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

include(colors) 命令指示 CMake 在 ${CMAKE_MODULE_PATH} 中搜索名称为 colors.cmake 的模块。

本例中,我们没有按以下方式进行:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(colors)

而是使用了一个显式包含的方式:

include(cmake/colors.cmake)

更多信息

推荐的做法是在模块中定义宏或函数,然后调用宏或函数。把包含模块当作函数调用来使用并不是一个好的方式。除了定义函数和宏以及查找程序、库和路径之外,包含模块不应该做更多的事情。实际的 include 命令不应该定义或修改变量,其原因是重复的 include(可能是偶然发生的)不应该引入任何不想要的副作用。后续示例中,我们将创建一个防止多次包含的保护机制。

系列导航


   转载规则


《CMake 系列(十八):将 CMake 源码分成模块》 由 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
CMake 系列(十九):add_subdirectory 的作用域 CMake 系列(十九):add_subdirectory 的作用域
随着项目规模的增长,把所有构建逻辑都集中在一个 CMakeLists.txt 文件里,会让项目越来越难维护。接下来几篇我们讨论构建项目的策略,并限制变量的作用范围和副作用,目的是降低代码的复杂性、简化项目的维护。本文将把一个项目分割成几个范
2022-07-19
下一篇 
CMake 系列(十七):用函数和宏重用代码 CMake 系列(十七):用函数和宏重用代码
任何编程语言中,函数都允许我们抽象(隐藏)细节并避免代码重复,CMake 也不例外。本篇将以宏和函数为例进行讨论,并介绍一个宏,用于方便地定义测试并设置测试的顺序。我们的目标是定义一个宏,用来替换 add_test 和 set_tests_
2022-07-05
  目录