CMake Tutorial 巡礼(10)_ 添加生成器表达式
这是本系列的第十一篇。
上一篇我们学习了如何选择静态库和动态库。这一篇我们来学习如何添加生成表达式。
本章导读
第十步 添加生成器表达式
Generator expressions
are evaluated during build system generation to produce information specific to each build configuration.
Generator expressions
(生成器表达式)在生成系统期间进行计算,以生成特定于每个生成配置的信息。
Generator expressions
are allowed in the context of many target properties, such asLINK_LIBRARIES
,INCLUDE_DIRECTORIES
,COMPILE_DEFINITIONS
and others. They may also be used when using commands to populate those properties, such astarget_link_libraries()
,target_include_directories()
,target_compile_definitions()
and others.
Generator expressions
允许出现在很多目标属性的文本中,例如LINK_LIBRARIES
, INCLUDE_DIRECTORIES
, COMPILE_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.
Generator expressions
(生成器表达式)可用作使能条件链接时,编译时使用条件编译时,条件包含目录时及更多情况下。这些情况均是基于生成参数,目标属性,平台信息以及其他可查询信息。
There are different types of
generator expressions
including Logical, Informational, and Output expressions.
Generator expressions
(生成器表达式)有多种不同的类型,包含逻辑型、信息型,以及输出表达式。
Logical expressions are used to create conditional output. The basic expressions are the
0
and1
expressions. A$<0:...>
results in the empty string, and<1:...>
results in the content of...
. They can also be nested.
逻辑型表达式用于创建条件输出。基本表达式是0 1表达式。一个$<0:...>
将表示一个空字符串,<1:...>
则表示一个...
串,它们也可以被嵌套。
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 anINTERFACE
target allowing this information to propagate. Let’s start by constructing anINTERFACE
target and specifying the required C++ standard level of11
instead of usingCMAKE_CXX_STANDARD
.
一个常见的生成器表达式应用是条件地添加编译标志位,例如那些语言级别或警告。一种非常好的范式是将此信息关联到一个 INTERFACE
(接口)目标,以允许这个信息进行传播。让我们从建立一个接口目标开始,并且替换使用CMAKE_CXX_STANDARD
的方式来指定C++11标准。
So the following code:
所以代码如下:
CMakeLists.txt
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
将会被替换成:
CMakeLists.txt
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
Note: This upcoming section will require a change to the
cmake_minimum_required()
usage in the code. The Generator Expression that is about to be used was introduced in 3.15. Update the call to require that more recent version:
注意:下一节将要求更改代码中的 cmake_minimum_required()
用法。将要使用的生成器表达式是在版本3.15中引入的。请更新调用此函数所需要的版本。
cmake_minimum_required(VERSION 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 as seen below:
接下来,我们为项目添加所需的编译器警告标志。由于警告标志因编译器而异,我们使用 COMPILE_LANG_AND_ID
生成器表达式来控制在给定语言和一组编译器ID的情况下应用哪些标志,如下所示:
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
Looking at this we see that the warning flags are encapsulated inside a
BUILD_INTERFACE
condition. This is done so that consumers of our installed project will not inherit our warning flags.
我们看到,警告标志封装在BUILD_INTERFACE
条件中。这样做是为了使已安装项目的使用者不会继承我们的警告标志。
小白按: CMakeLists.txt
修改后的全部代码如下所示:
cmake_minimum_required(VERSION 3.15)
# 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)
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
# control where the static and shared libraries are built so that on windows
# we don't need to tinker with the path to run the executable
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
# configure a header file to pass the version number only
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)
# 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}"
)
# add the install targets
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
# enable testing
include(CTest)
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)
# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# define a function to simplify adding tests
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction()
# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
Exercise: Modify
MathFunctions/CMakeLists.txt
so that all targets have atarget_link_libraries()
call totutorial_compiler_flags
.
练习: 修改 MathFunctions/CMakeLists.txt
令所有的目标都有一个针对tutorial_compiler_flags
的target_link_libraries()
调用。
小白按:对于这个小练习,小白也是没弄清楚该如何去弄。说句实在话,到了这个阶段,小白也不是特别理解这些功能的真正目的和使用场景。只能留待以后学习时再度体会。
下一篇我们将学习如何添加导出参数。
【水平所限,错漏难免,创作不易,轻喷勿骂】