CMake教程-第 4 步:添加生成器表达式

该文档是基于CMake的官方教程翻译而来,并稍微添加了自己的理解:

cmake的官方网站为:CMake Tutorial

1 CMake教程介绍

The CMake tutorial provides a step-by-step guide that covers common build system issues that CMake helps address. Seeing how various topics all work together in an example project can be very helpful.
CMake 教程提供了一个循序渐进的指南,涵盖了 CMake 可帮助解决的常见构建系统问题。在一个示例项目中了解各个主题是如何协同工作的,会非常有帮助。

2 学习步骤

The tutorial source code examples are available in this archive. Each step has its own subdirectory containing code that may be used as a starting point. The tutorial examples are progressive so that each step provides the complete solution for the previous step.
本文档中提供了教程源代码示例。每个步骤都有自己的子目录,其中包含可用作起点的代码。教程示例是循序渐进的,因此每一步都提供了前一步的完整解决方案。

Step 1: A Basic Starting Point

  • Exercise 1 - Building a Basic Project
  • Exercise 2 - Specifying the C++ Standard
  • Exercise 3 - Adding a Version Number and Configured Header File

Step 2: Adding a Library

  • Exercise 1 - Creating a Library
  • Exercise 2 - Adding an Option

Step 3: Adding Usage Requirements for a Library

  • Exercise 1 - Adding Usage Requirements for a Library
  • Exercise 2 - Setting the C++ Standard with Interface Libraries

Step 4: Adding Generator Expressions

  • Exercise 1 - Adding Compiler Warning Flags with Generator Expressions

Step 5: Installing and Testing

  • Exercise 1 - Install Rules
  • Exercise 2 - Testing Support

Step 6: Adding Support for a Testing Dashboard

  • Exercise 1 - Send Results to a Testing Dashboard

Step 7: Adding System Introspection

  • Exercise 1 - Assessing Dependency Availability

Step 8: Adding a Custom Command and Generated File

Step 9: Packaging an Installer

Step 10: Selecting Static or Shared Libraries

Step 11: Adding Export Configuration

Step 12: Packaging Debug and Release

3 Step 4: Adding Generator Expressions

Generator expressions are evaluated during build system generation to produce information specific to each build configuration.
生成器表达式在构建系统生成期间进行评估,以生成特定于每个构建配置的信息。

Generator expressions are allowed in the context of many target properties, such as LINK_LIBRARIES, INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS and others. They may also be used when using commands to populate those properties, such as target_link_libraries(), target_include_directories(), target_compile_definitions() and others.
在多目标属性(如 LINK_LIBRARIESINCLUDE_DIRECTORIESCOMPILE_DEFINITIONS 等)的上下文中都允许使用生成器表达式。在使用 target_link_libraries()target_include_directories()target_compile_definitions() 等命令填充这些属性时,也可以使用这些表达式。

Generator expressions may be used to enable conditional linking, conditional definitions used when compiling, conditional include directories and more. The conditions may be based on the build configuration, target properties, platform information or any other queryable information.
生成器表达式可用于启用条件链接、编译时使用的条件定义、条件包含目录等。这些条件可以基于编译配置、目标属性、平台信息或任何其他可查询的信息。

There are different types of generator expressions including Logical, Informational, and Output expressions.
生成器表达式有多种类型,包括逻辑表达式、信息表达式和输出表达式。

Logical expressions are used to create conditional output. The basic expressions are the 0 and 1 expressions. A < 0 : . . . > r e s u l t s i n t h e e m p t y s t r i n g , a n d < 1 : . . . > r e s u l t s i n t h e c o n t e n t o f . . . . T h e y c a n a l s o b e n e s t e d . 逻辑表达式用于创建条件输出。基本表达式是 0 和 1 表达式。 <0:...> results in the empty string, and <1:...> results in the content of .... They can also be nested. 逻辑表达式用于创建条件输出。基本表达式是 0 和 1 表达式。 <0:...>resultsintheemptystring,and<1:...>resultsinthecontentof....Theycanalsobenested.逻辑表达式用于创建条件输出。基本表达式是01表达式。<0:…>的结果是空字符串,<1:…>的结果是 … 的内容。它们还可以嵌套。

3.1 Exercise 1 - Adding Compiler Warning Flags with Generator Expressions

A common usage of generator expressions is to conditionally add compiler flags, such as those for language levels or warnings. A nice pattern is to associate this information to an INTERFACE target allowing this information to propagate.
生成器表达式的一个常见用法是有条件地添加编译器标志,如语言级别或警告标志。一种不错的模式是将这些信息与 INTERFACE 目标相关联,从而使这些信息得以传播。

3.1.1 目标

Add compiler warning flags when building but not for installed versions.
编译时添加编译器警告标志,但不针对已安装的版本。

3.1.2 Helpful Resources(有用的资源)

3.1.3 Files to Edit(需编辑的文件)

  • CMakeLists.txt

3.1.4 Getting Started(入门指南)

Open the file Step4/CMakeLists.txt and complete TODO 1 through TODO 4.
打开文件 Step4/CMakeLists.txt,并完成 TODO 1 至 TODO 4。

First, in the top level CMakeLists.txt file, we need to set the cmake_minimum_required() to 3.15. In this exercise we are going to use a generator expression which was introduced in CMake 3.15.
首先,在顶层 CMakeLists.txt 文件中,我们需要将 cmake_minimum_required() 设置为 3.15。在本练习中,我们将使用 CMake 3.15 中引入的生成器表达式。

Next we add the desired compiler warning flags that we want for our project. As warning flags vary based on the compiler, we use the COMPILE_LANG_AND_ID generator expression to control which flags to apply given a language and a set of compiler ids.
接下来,我们为项目添加所需的编译器警告标志。由于警告标志因编译器而异,我们使用 COMPILE_LANG_AND_ID 生成器表达式来控制在给定语言和编译器 ID 的情况下应用哪些标志。

3.1.5 Build and Run(构建并运行)

Make a new directory called Step4_build, run the cmake executable or the cmake-gui to configure the project and then build it with your chosen build tool or by using cmake --build . from the build directory.
创建一个名为 Step4_build 的目录,运行 cmake 可执行文件或 cmake-gui 配置项目,然后使用你选择的构建工具或从构建目录中使用 cmake --build .构建项目。

mkdir Step4_build
cd Step4_build
cmake ../Step4
cmake --build .

3.1.6 解决方案

Update the cmake_minimum_required() to require at least CMake version 3.15:
更新 cmake_minimum_required() 以至少需要 CMake 3.15版本 :

TODO 1: Click to show/hide answer

TODO 1: CMakeLists.txt
cmake_minimum_required(VERSION 3.15)

Next we determine which compiler our system is currently using to build since warning flags vary based on the compiler we use. This is done with the COMPILE_LANG_AND_ID generator expression. We set the result in the variables gcc_like_cxx and msvc_cxx as follows:
接下来,我们要确定系统当前使用的编译器,因为警告标志会根据编译器的不同而变化。这是通过 COMPILE_LANG_AND_ID 生成器表达式完成的。我们在变量 gcc_like_cxxmsvc_cxx 中设置结果如下:

TODO 2: Click to show/hide answer

TODO 2: CMakeLists.txt
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")

Next we add the desired compiler warning flags that we want for our project. Using our variables gcc_like_cxx and msvc_cxx, we can use another generator expression to apply the respective flags only when the variables are true. We use target_compile_options() to apply these flags to our interface library.
接下来,我们为项目添加所需的编译器警告标志。使用我们的变量 gcc_like_cxxmsvc_cxx,我们可以使用另一个生成器表达式,只有当变量为 true 时才应用相应的标记。我们使用 target_compile_options() 将这些标志应用到我们的接口库中。

TODO 3: Click to show/hide answer

TODO 3: CMakeLists.txt
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
  "$<${msvc_cxx}:-W3>"
)

Lastly, we only want these warning flags to be used during builds. Consumers of our installed project should not inherit our warning flags. To specify this, we wrap our flags in a generator expression using the BUILD_INTERFACE condition. The resulting full code looks like the following:
最后,我们只希望在构建过程中使用这些警告标志。已安装项目的用户不应继承我们的警告标志。为此,我们使用 BUILD_INTERFACE 条件在生成器表达式中封装了警告标志。生成的完整代码如下

TODO 4: Click to show/hide answer

TODO 4: CMakeLists.txt
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)

3.1.7 CMakeLists.txt

# TODO 1: Update the minimum required version to 3.15
cmake_minimum_required(VERSION 3.15)

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# specify the C++ standard
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

# TODO 2: Create helper variables to determine which compiler we are using:
# * Create a new variable gcc_like_cxx that is true if we are using CXX and
#   any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
# * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
# Hint: Use set() and COMPILE_LANG_AND_ID
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")

# TODO 3: Add warning flag compile options to the interface library
# tutorial_compiler_flags.
# * For gcc_like_cxx, add flags -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused
# * For msvc_cxx, add flags -W3
# Hint: Use target_compile_options()
# target_compile_options(tutorial_compiler_flags INTERFACE
#   "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
#   "$<${msvc_cxx}:-W3>"
# )

# TODO 4: With nested generator expressions, only use the flags for the
# build-tree
# Hint: Use BUILD_INTERFACE                                                                                                                                                                                                                                                     
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# add the MathFunctions library
add_subdirectory(MathFunctions)

# add the executable
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

3.1.8 运行结果

执行Tutorial程序的运行结果如下所示:

$ ./Tutorial 25
Computing sqrt of 25 to be 13
Computing sqrt of 25 to be 7.46154
Computing sqrt of 25 to be 5.40603
Computing sqrt of 25 to be 5.01525
Computing sqrt of 25 to be 5.00002
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
Computing sqrt of 25 to be 5
The square root of 25 is 5
$ 
09-26 23:54