CMake 系列(八):脚本控制流

本章前面的示例中,已经使用过 if-else-endif。CMake 还提供了创建循环的语言工具:foreach-endforeach 和 while-endwhile。两者都可以与 break() 结合使用,以便尽早从循环中跳出。本示例将展示如何使用 foreach 来循环源文件列表。我们将应用这样的循环,在引入新目标的前提下,为一组源文件进行优化降级。

提示:本示例包含一个 C++ 示例,在 CMake 3.5 或更高版本中有效,并已在 GNU/Linux、macOS 和 Windows 上测试通过。

准备工作

将重用设置编译器选项示例中的几何示例代码,目标是通过将一些源代码汇集到一个列表中,从而微调编译器的优化。

具体实施

下面是 CMakeLists.txt 中的详细步骤。

与设置编译器选项示例中一样,指定 CMake 的最低版本、项目名称和语言,并声明几何库目标:

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

project(recipe-10 LANGUAGES CXX)

add_library(geometry
  STATIC
  geometry_circle.cpp
  geometry_circle.hpp
  geometry_polygon.cpp
  geometry_polygon.hpp
  geometry_rhombus.cpp
  geometry_rhombus.hpp
  geometry_square.cpp
  geometry_square.hpp
)

使用 -O3 编译器优化级别编译库,对目标设置一个私有编译器选项:

target_compile_options(geometry
  PRIVATE
  -O3
)

然后,生成一个源文件列表,以较低的优化选项进行编译:

list(
  APPEND sources_with_lower_optimization
  geometry_circle.cpp
  geometry_rhombus.cpp
)

循环这些源文件,将它们的优化级别调到 -O2,使用源文件属性完成:

message(STATUS "Setting source properties using IN LISTS syntax:")

foreach(_source IN LISTS sources_with_lower_optimization)
  set_source_files_properties(${_source} PROPERTIES COMPILE_FLAGS -O2)
  message(STATUS "Appending -O2 flag for ${_source}")
endforeach()

为了确保设置了属性,再次循环并打印每个源文件的 COMPILE_FLAGS 属性:

message(STATUS "Querying sources properties using plain syntax:")

foreach(_source ${sources_with_lower_optimization})
  get_source_file_property(_flags ${_source} COMPILE_FLAGS)
  message(STATUS "Source ${_source} has the following extra COMPILE_FLAGS: ${_flags}")
endforeach()

最后,添加 compute-areas 可执行目标,并将 geometry 库链接上去:

add_executable(compute-areas compute-areas.cpp)

target_link_libraries(compute-areas geometry)

验证在配置步骤中正确设置了标志:

$ mkdir -p build
$ cd build
$ cmake ..
...
-- Setting source properties using IN LISTS syntax:
-- Appending -O2 flag for geometry_circle.cpp
-- Appending -O2 flag for geometry_rhombus.cpp
-- Querying sources properties using plain syntax:
-- Source geometry_circle.cpp has the following extra COMPILE_FLAGS: -O2
-- Source geometry_rhombus.cpp has the following extra COMPILE_FLAGS: -O2

还可以使用 VERBOSE=1 检查构建步骤,将看到 -O2 标志被追加在 -O3 标志之后,而最后一个优化级别标志(本例中是 -O2)会生效:

$ cmake --build . -- VERBOSE=1

工作原理

foreach-endforeach 语法可用于在变量列表上重复特定任务。本示例中,使用它来操作、设置和获取项目中特定文件的编译器标志。CMake 代码片段中引入了另外两个新命令:

  • set_source_files_properties(file PROPERTIES property value):将属性设置为给定文件传递的值。与目标非常相似,CMake 中的文件也有属性,允许对构建系统进行非常细粒度的控制;
  • get_source_file_property(VAR file property):检索给定文件所需属性的值,并将其存储在 CMake 变量 VAR 中。

注意:CMake 中,列表是用分号分隔的字符串组。列表可以由 list 或 set 命令创建。例如,set(var a b c d e) 和 list(APPEND a b c d e) 都创建了列表 a;b;c;d;e。

提示:为了对一组文件降低优化,将它们收集到一个单独的目标(库)中,并为这个目标显式地设置优化级别,而不是附加一个标志,这样可能会更简洁。不过在本示例中,我们的重点是 foreach-endforeach。

更多信息

foreach() 有四种使用方式:

  • foreach(loop_var arg1 arg2 ...):提供循环变量和显式项列表。当为 sources_with_lower_optimization 中的项打印编译器标志集时,使用此形式。注意,如果项列表位于变量中,则必须显式展开它,也就是说,${sources_with_lower_optimization} 必须作为参数传递;
  • 通过指定一个范围,可以对整数进行循环,例如 foreach(loop_var range total) 或 foreach(loop_var range start stop [step]);
  • 对列表值变量的循环,例如 foreach(loop_var IN LISTS list1 [...])。参数被解释为列表,其内容会自动展开;
  • 对变量的循环,例如 foreach(loop_var IN ITEMS item1 [...])。参数的内容不会被展开。

系列导航


   转载规则


《CMake 系列(八):脚本控制流》 由 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
CMake 系列(九):检测操作系统 CMake 系列(九):检测操作系统
CMake 是一组跨平台工具。不过,了解操作系统(OS)上执行的配置或构建步骤也很重要,与操作系统相关的 CMake 代码会根据操作系统启用条件编译,或者在可用或必要时使用特定于编译器的扩展。本示例中,我们将通过一个不需要编译任何源代码的示
2022-05-10
下一篇 
CMake 系列(七):设置编译器选项 CMake 系列(七):设置编译器选项
前面的示例展示了如何探测 CMake,从而获得关于编译器的信息,以及如何切换项目中的编译器。本篇的任务是控制项目的编译器标志。CMake 为调整或扩展编译器标志提供了很大的灵活性,可以选择下面两种方法: CMake 将编译选项视为目标属性
2022-04-26
  目录