cmake 生成器表达式 (7)

介绍

生成器表达式在构建系统生成期间进行评估,以生成特定于每个构建配置的信息。它们的形式为 $<...>。例如:

target_include_directories(tgt PRIVATE /opt/include/$<CXX_COMPILER_ID>)

这将扩展为“/opt/include/GNU”、“/opt/include/Clang”等,具体取决于所使用的 C++ 编译器。

许多目标属性的上下文中允许使用生成器表达式,例如:prop_tgt:LINK_LIBRARIESINCLUDE_DIRECTORIESCOMPILE_DEFINITIONS 等。它们也可以在使用命令填充这些属性时使用,例如:command:target_link_librariestarget_include_directories()target_compile_definitions() 等。它们启用条件链接、编译时使用的条件定义、条件包含目录等。这些条件可能基于构建配置、目标属性、平台信息或任何其他可查询信息。

生成器表达式可以嵌套:

target_compile_definitions(tgt PRIVATE
  $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,4.2.0>:OLD_COMPILER>
)

如果 CMAKE_CXX_COMPILER_VERSION 小于 4.2.0,则以上将扩展为 OLD_COMPILER

空格和引号

生成器表达式通常在命令参数之后进行解析。如果生成器表达式包含空格、换行符、分号或其他可能被解释为命令参数分隔符的字符,则在传递给命令时,整个表达式应该用引号括起来。如果不这样做可能会导致表达式被拆分并且它可能不再被识别为生成器表达式。

使用 add_custom_command()add_custom_target() 时,请使用 VERBATIMCOMMAND_EXPAND_LISTS 选项以获得可靠的参数拆分和引用。

# WRONG: Embedded space will be treated as an argument separator.
# This ends up not being seen as a generator expression at all.
add_custom_target(run_some_tool
  COMMAND some_tool -I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>
  VERBATIM
)
# Better, but still not robust. Quotes prevent the space from splitting the
# expression. However, the tool will receive the expanded value as a single
# argument.
add_custom_target(run_some_tool
  COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>"
  VERBATIM
)
# Nearly correct. Using a semicolon to separate arguments and adding the
# COMMAND_EXPAND_LISTS option means that paths with spaces will be handled
# correctly. Quoting the whole expression ensures it is seen as a generator
# expression. But if the target property is empty, we will get a bare -I
# with nothing after it.
add_custom_target(run_some_tool
  COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>,;-I>"
  COMMAND_EXPAND_LISTS
  VERBATIM
)

使用变量构建更复杂的生成器表达式也是减少错误和提高可读性的好方法。上面的例子可以像这样进一步改进:

# The $<BOOL:...> check prevents adding anything if the property is empty,
# assuming the property value cannot be one of CMake's false constants.
set(prop "$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>")
add_custom_target(run_some_tool
  COMMAND some_tool "$<$<BOOL:${prop}>:-I$<JOIN:${prop},;-I>>"
  COMMAND_EXPAND_LISTS
  VERBATIM
)

一个常见的错误是尝试通过缩进将生成器表达式拆分为多行:

# WRONG: New lines and spaces all treated as argument separators, so the
# generator expression is split and not recognized correctly.
target_compile_definitions(tgt PRIVATE
  $<$<AND:
      $<CXX_COMPILER_ID:GNU>,
      $<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>
    >:HAVE_5_OR_LATER>
)

同样,使用具有精心选择的名称的辅助变量来构建一个可读的表达式:

set(is_gnu "$<CXX_COMPILER_ID:GNU>")
set(v5_or_later "$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>")
set(meet_requirements "$<AND:${is_gnu},${v5_or_later}>")
target_compile_definitions(tgt PRIVATE
  "$<${meet_requirements}:HAVE_5_OR_LATER>"
)

调试

由于生成器表达式是在构建系统生成期间计算的,而不是在处理 CMakeLists.txt 文件期间计算的,因此无法使用 message() 命令检查它们的结果。生成调试消息的一种可能方法是添加自定义目标:

add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<...>")

运行 cmake 后,您可以构建 genexdebug 目标以打印 $<...> 表达式的结果(即运行命令:option:cmake --build ... --target genexdebug <cmake--build --target>)。

另一种方法是使用 file(GENERATE) 将调试消息写入文件:

file(GENERATE OUTPUT filename CONTENT "$<...>")

生成器表达式参考

备注

此参考与大多数 CMake 文档不同,因为它省略了占位符周围的尖括号 <...>,例如 conditionstringtarget 等。这是为了防止这些占位符被误解为生成器表达式的机会。

条件表达式

生成器表达式的一个基本类别与条件逻辑有关。支持两种形式的条件生成器表达式:

$<condition:true_string>

如果“条件”为“1”,则评估为“true_string”,如果“条件”评估为“0”,则评估为空字符串。 condition 的任何其他值都会导致错误。

$<IF:condition,true_string,false_string>

在 3.8 版本加入.

如果“条件”为“1”,则评估为“true_string”,如果“条件”为“0”,则评估为“false_string”。 condition 的任何其他值都会导致错误。

通常,“条件”本身就是一个生成器表达式。例如,当使用 Debug 配置时,以下表达式扩展为 DEBUG_MODE,所有其他配置为空字符串:

$<$<CONFIG:Debug>:DEBUG_MODE>

除了 10 之外的类似布尔的 condition 值可以通过用 $<BOOL:...> 生成器表达式包装它们来处理:

$<BOOL:string>

string 转换为 01。如果以下任何一项为真,则评估为“0”:

  • string 为空,

  • string 是不区分大小写的等于 0FALSEOFFNNOIGNORE 或 ` NOTFOUND`

  • string 以后缀 -NOTFOUND 结尾(区分大小写)。

否则计算为“1”。

当 CMake 变量提供“条件”时,经常使用“$<BOOL:...>”生成器表达式:

$<$<BOOL:${HAVE_SOME_FEATURE}>:-DENABLE_SOME_FEATURE>

逻辑运算符

支持常见的布尔逻辑运算符:

$<AND:conditions>

其中 conditions 是以逗号分隔的布尔表达式列表,所有这些表达式的计算结果必须为 10。如果所有条件都为“1”,则整个表达式的计算结果为“1”。如果任何条件为“0”,则整个表达式的计算结果为“0”。

$<OR:conditions>

其中 conditions 是以逗号分隔的布尔表达式列表。所有这些都必须评估为“1”或“0”。如果“条件”中的至少一个为“1”,则整个表达式的计算结果为“1”。如果所有“条件”的计算结果为“0”,则整个表达式的计算结果为“0”。

$<NOT:condition>

condition 必须是 01。如果“条件”为“1”,则表达式的结果为“0”,否则为“1”。

初级比较表达式

CMake 支持各种比较事物的生成器表达式。本节涵盖主要和最广泛使用的比较类型。其他更具体的比较类型在下面的单独部分中有记录。

字符串比较

$<STREQUAL:string1,string2>

1 如果 string1string2 相等,否则为 0。比较区分大小写。对于不区分大小写的比较,结合 string transforming generator expression。例如,如果 ${foo}BARBarbar 等中的任何一个,则以下计算结果为 1

$<STREQUAL:$<UPPER_CASE:${foo}>,BAR>
$<EQUAL:value1,value2>

1 如果``value1`` 和``value2`` 在数值上相等,否则为``0``。

版本比较

$<VERSION_LESS:v1,v2>

1 如果``v1`` 的版本小于``v2``,否则为``0``。

$<VERSION_GREATER:v1,v2>

1 如果``v1`` 是大于``v2`` 的版本,否则为``0``。

$<VERSION_EQUAL:v1,v2>

1 如果``v1`` 与``v2`` 的版本相同,否则为``0``。

$<VERSION_LESS_EQUAL:v1,v2>

在 3.7 版本加入.

1 如果``v1`` 是小于或等于``v2`` 的版本,否则为``0``。

$<VERSION_GREATER_EQUAL:v1,v2>

在 3.7 版本加入.

1 如果``v1`` 是大于或等于``v2`` 的版本,否则为``0``。

字符串转换

$<LOWER_CASE:string>

string 的内容转换为小写。

$<UPPER_CASE:string>

string 的内容转换为大写。

$<MAKE_C_IDENTIFIER:...>

... 的内容转换为 C 标识符。转换遵循与 string(MAKE_C_IDENTIFIER) 相同的行为。

列表表达式

$<IN_LIST:string,list>

在 3.12 版本加入.

1 如果``string`` 是分号分隔的``list`` 中的一项,否则为``0``。它使用区分大小写的比较。

$<JOIN:list,string>

加入列表,在每个项目之间插入 string 的内容。

$<REMOVE_DUPLICATES:list>

在 3.15 版本加入.

删除给定“列表”中的重复项目。保留项目的相对顺序,但如果遇到重复项,则仅保留第一个实例。

$<FILTER:list,INCLUDE|EXCLUDE,regex>

在 3.15 版本加入.

list 中包含或删除与正则表达式 regex 匹配的项目。

路径表达式

本节中的大多数表达式与 cmake_path() 命令密切相关,提供相同的功能,但采用生成器表达式的形式。

对于本节中的所有生成器表达式,路径应采用 cmake 样式格式。 $<PATH:CMAKE_PATH> 生成器表达式可用于将本机路径转换为 ​​cmake 样式的路径。

路径比较

$<PATH_EQUAL:path1,path2>

在 3.24 版本加入.

比较两条路径的词法表示。在任何一条路径上都不执行规范化。如果路径相等,则返回“1”,否则返回“0”。

有关详细信息,请参见:ref:cmake_path(COMPARE) <Path COMPARE>

路径查询

这些表达式提供的生成时功能等同于 cmake_path 命令的 Query 选项。所有路径都应采用 cmake 样式格式。

$<PATH:HAS_*,path>

在 3.24 版本加入.

如果存在特定路径组件,则以下操作返回“1”,否则返回“0”。有关每个路径组件的含义,请参阅 路径结构和术语

$<PATH:HAS_ROOT_NAME,path>
$<PATH:HAS_ROOT_DIRECTORY,path>
$<PATH:HAS_ROOT_PATH,path>
$<PATH:HAS_FILENAME,path>
$<PATH:HAS_EXTENSION,path>
$<PATH:HAS_STEM,path>
$<PATH:HAS_RELATIVE_PART,path>
$<PATH:HAS_PARENT_PATH,path>

注意以下特殊情况:

  • 对于 HAS_ROOT_PATH,仅当 root-nameroot-directory 中至少有一个非空时,才会返回 true 结果。

  • 对于``HAS_PARENT_PATH``,根目录也被认为有一个父目录,就是它自己。结果为真,除非路径仅包含 filename

$<PATH:IS_ABSOLUTE,path>

在 3.24 版本加入.

如果路径是 absolute <IS_ABSOLUTE>`,则返回 1,否则返回 0

$<PATH:IS_RELATIVE,path>

在 3.24 版本加入.

这将返回 IS_ABSOLUTE 的反义词。

$<PATH:IS_PREFIX[,NORMALIZE],path,input>

在 3.24 版本加入.

如果 pathinput 的前缀,则返回 1,否则返回 0

当指定了 NORMALIZE 选项时,pathinput 在检查之前被规范化 <Normalization>`。

路径分解

这些表达式提供的生成时功能等同于 cmake_path 命令的 Decomposition <Path Decomposition> 选项。所有路径都应采用 cmake 样式格式。

$<PATH:GET_*,...>

在 3.24 版本加入.

以下操作从路径中检索不同的组件或组件组。有关每个路径组件的含义,请参阅 路径结构和术语

$<PATH:GET_ROOT_NAME,path>
$<PATH:GET_ROOT_DIRECTORY,path>
$<PATH:GET_ROOT_PATH,path>
$<PATH:GET_FILENAME,path>
$<PATH:GET_EXTENSION[,LAST_ONLY],path>
$<PATH:GET_STEM[,LAST_ONLY],path>
$<PATH:GET_RELATIVE_PART,path>
$<PATH:GET_PARENT_PATH,path>

如果路径中不存在请求的组件,则返回空字符串。

路径转换

这些表达式提供的生成时功能等效于 cmake_path 命令的 ModificationGeneration 选项。所有路径都应采用 cmake 样式格式。

$<PATH:CMAKE_PATH[,NORMALIZE],path>

在 3.24 版本加入.

返回“路径”。如果 path 是本机路径,它将转换为带有正斜杠 (/) 的 cmake 样式路径。在 Windows 上,长文件名标记被考虑在内。

当指定了 NORMALIZE 选项时,转换后的路径是 normalized

$<PATH:APPEND,path,input,...>

在 3.24 版本加入.

使用“/”作为“目录分隔符”返回所有附加到“路径”的“输入”参数。根据“输入”,“路径”的值可能会被丢弃。

有关更多详细信息,请参阅:ref:cmake_path(APPEND)<APPEND>

$<PATH:REMOVE_FILENAME,path>

在 3.24 版本加入.

返回带有文件名组件(由 $<PATH:GET_FILENAME>`` 返回)的 path 被移除。删除后,任何尾随的“目录分隔符”(如果存在)将保持不变。

有关更多详细信息,请参阅 cmake_path(REMOVE_FILENAME)

$<PATH:REPLACE_FILENAME,path,input>

在 3.24 版本加入.

返回 path,文件名部分替换为 input。如果 path 没有文件名组件(即 $<PATH:HAS_FILENAME> 返回 0),path 不变。

有关详细信息,请参见:ref:cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>

$<PATH:REMOVE_EXTENSION[,LAST_ONLY],path>

在 3.24 版本加入.

返回带有 extensionpath,如果有的话。

有关详细信息,请参见:ref:cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>

$<PATH:REPLACE_EXTENSION[,LAST_ONLY],path,input>

在 3.24 版本加入.

返回 path,其中 extension 替换为 ``input``(如果有)。

有关详细信息,请参见:ref:cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>

$<PATH:NORMAL_PATH,path>

在 3.24 版本加入.

返回根据 Normalization 中描述的步骤规范化的 path

$<PATH:RELATIVE_PATH,path,base_directory>

在 3.24 版本加入.

返回 path,修改后使其相对于 base_directory 参数。

有关详细信息,请参见:ref:cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>

$<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory>

在 3.24 版本加入.

以绝对方式返回“路径”。如果 path 是相对路径($<PATH:IS_RELATIVE> 返回 1),则相对于 base_directory 参数指定的给定基目录进行评估。

当指定 NORMALIZE 选项时,路径在路径计算之后是 normalized

有关详细信息,请参阅:ref:cmake_path(ABSOLUTE_PATH) <ABSOLUTE_PATH>

外壳路径

$<SHELL_PATH:...>

在 3.4 版本加入.

... 的内容转换为 shell 路径样式。例如,斜杠在 Windows shell 中被转换为反斜杠,驱动器号在 MSYS shell 中被转换为 posix 路径。 ... 必须是绝对路径。

在 3.14 版本加入: ... 可能是:ref:分号分隔的路径列表 <CMake Language Lists>,在这种情况下,每个路径都会单独转换,并使用 shell 路径分隔符生成结果列表(`` :`` 在 POSIX 上和 ; 在 Windows 上)。请务必在 CMake 源代码中用双引号将包含此 genex 的参数括起来,以便 ; 不会拆分参数。

配置表达式

$<CONFIG>

配置名称。使用它代替已弃用的 CONFIGURATION 生成器表达式。

$<CONFIG:cfgs>

1 如果配置是逗号分隔列表``cfgs`` 中的任何一个条目,否则为``0``。这是不区分大小写的比较。 MAP_IMPORTED_CONFIG_<CONFIG> 中的映射在对 IMPORTED 目标的属性进行评估时也被此表达式考虑。

在 3.19 版本发生变更: 可以为 cfgs 指定多个配置。 CMake 3.18 及更早版本仅接受单一配置。

$<OUTPUT_CONFIG:...>

在 3.20 版本加入.

仅在 add_custom_command()add_custom_target() 作为参数中最外层的生成器表达式有效。使用 Ninja Multi-Config 生成器,... 中的生成器表达式使用自定义命令的“输出配置”进行评估。对于其他生成器,... 的内容被正常评估。

$<COMMAND_CONFIG:...>

在 3.20 版本加入.

仅在 add_custom_command()add_custom_target() 作为参数中最外层的生成器表达式有效。使用 Ninja Multi-Config 生成器,... 中的生成器表达式使用自定义命令的“命令配置”进行评估。对于其他生成器,... 的内容被正常评估。

工具链和语言表达式

平台

$<PLATFORM_ID>

当前系统的 CMake 平台 id。另请参阅 CMAKE_SYSTEM_NAME 变量。

$<PLATFORM_ID:platform_ids>

1 如果 CMake 的平台 ID 与逗号分隔列表``platform_ids`` 中的任何一个条目匹配,否则为``0``。另请参阅 CMAKE_SYSTEM_NAME 变量。

编译器版本

另请参阅 CMAKE_<LANG>_COMPILER_VERSION 变量,它与本小节中的表达式密切相关。

$<C_COMPILER_VERSION>

使用的 C 编译器的版本。

$<C_COMPILER_VERSION:version>

1 如果 C 编译器的版本与 version 匹配,否则为 0

$<CXX_COMPILER_VERSION>

使用的 CXX 编译器的版本。

$<CXX_COMPILER_VERSION:version>

1 如果 CXX 编译器的版本与 version 匹配,否则为 0

$<CUDA_COMPILER_VERSION>

在 3.15 版本加入.

使用的 CUDA 编译器的版本。

$<CUDA_COMPILER_VERSION:version>

在 3.15 版本加入.

1 如果 CXX 编译器的版本与 version 匹配,否则为 0

$<OBJC_COMPILER_VERSION>

在 3.16 版本加入.

使用的 OBJC 编译器的版本。

$<OBJC_COMPILER_VERSION:version>

在 3.16 版本加入.

1 如果 OBJC 编译器的版本匹配``version``,否则为``0``。

$<OBJCXX_COMPILER_VERSION>

在 3.16 版本加入.

使用的 OBJCXX 编译器的版本。

$<OBJCXX_COMPILER_VERSION:version>

在 3.16 版本加入.

1 如果 OBJCXX 编译器的版本匹配``version``,否则为``0``。

$<Fortran_COMPILER_VERSION>

使用的 Fortran 编译器的版本。

$<Fortran_COMPILER_VERSION:version>

1 如果 Fortran 编译器的版本与 version 匹配,否则为 0

$<HIP_COMPILER_VERSION>

在 3.21 版本加入.

使用的 HIP 编译器的版本。

$<HIP_COMPILER_VERSION:version>

在 3.21 版本加入.

如果 HIP 编译器的版本匹配“版本”,则为“1”,否则为“0”。

$<ISPC_COMPILER_VERSION>

在 3.19 版本加入.

使用的 ISPC 编译器的版本。

$<ISPC_COMPILER_VERSION:version>

在 3.19 版本加入.

如果 ISPC 编译器的版本匹配“版本”,则为“1”,否则为“0”。

编译器语言和 ID

另请参阅 CMAKE_<LANG>_COMPILER_ID 变量,它与本小节中的大多数表达式密切相关。

$<C_COMPILER_ID>

使用的 C 编译器的 CMake 编译器 ID。

$<C_COMPILER_ID:compiler_ids>

其中 compiler_ids 是以逗号分隔的列表。 1 如果 C 编译器的 CMake 编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<CXX_COMPILER_ID>

使用的 CXX 编译器的 CMake 编译器 ID。

$<CXX_COMPILER_ID:compiler_ids>

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CXX 编译器的 CMake 编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<CUDA_COMPILER_ID>

在 3.15 版本加入.

使用的 CUDA 编译器的 CMake 编译器 ID。

$<CUDA_COMPILER_ID:compiler_ids>

在 3.15 版本加入.

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CMake 的 CUDA 编译器编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<OBJC_COMPILER_ID>

在 3.16 版本加入.

使用的 OBJC 编译器的 CMake 编译器 ID。

$<OBJC_COMPILER_ID:compiler_ids>

在 3.16 版本加入.

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CMake 的 Objective-C 编译器的编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<OBJCXX_COMPILER_ID>

在 3.16 版本加入.

使用的 OBJCXX 编译器的 CMake 编译器 ID。

$<OBJCXX_COMPILER_ID:compiler_ids>

在 3.16 版本加入.

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CMake 的 Objective-C++ 编译器的编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<Fortran_COMPILER_ID>

使用的 Fortran 编译器的 CMake 编译器 ID。

$<Fortran_COMPILER_ID:compiler_ids>

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CMake 的 Fortran 编译器的编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<HIP_COMPILER_ID>

在 3.21 版本加入.

使用的 HIP 编译器的 CMake 编译器 ID。

$<HIP_COMPILER_ID:compiler_ids>

在 3.21 版本加入.

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CMake 的 HIP 编译器的编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<ISPC_COMPILER_ID>

在 3.19 版本加入.

使用的 ISPC 编译器的 CMake 编译器 ID。

$<ISPC_COMPILER_ID:compiler_ids>

在 3.19 版本加入.

其中 compiler_ids 是以逗号分隔的列表。 1 如果 CMake 的 ISPC 编译器的编译器 ID 与 compiler_ids 中的任何一个条目匹配,否则为 0

$<COMPILE_LANGUAGE>

在 3.3 版本加入.

评估编译选项时源文件的编译语言。有关此生成器表达式的可移植性的注释,请参阅 相关的布尔表达式 <Boolean COMPILE_LANGUAGE Generator Expression>``$<COMPILE_LANGUAGE:language>`

$<COMPILE_LANGUAGE:languages>

在 3.3 版本加入.

在 3.15 版本发生变更: 可以为 languages 指定多种语言。 CMake 3.14 及更早版本仅接受一种语言。

1 当用于编译单元的语言与 languages 中任何逗号分隔的条目匹配时,否则为 0。此表达式可用于指定编译选项、编译定义以及在目标中包含特定语言的源文件的目录。例如:

add_executable(myapp main.cpp foo.c bar.cpp zot.cu)
target_compile_options(myapp
  PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
)
target_compile_definitions(myapp
  PRIVATE $<$<COMPILE_LANGUAGE:CXX>:COMPILING_CXX>
          $<$<COMPILE_LANGUAGE:CUDA>:COMPILING_CUDA>
)
target_include_directories(myapp
  PRIVATE $<$<COMPILE_LANGUAGE:CXX,CUDA>:/opt/foo/headers>
)

这指定使用``-fno-exceptions`` 编译选项、COMPILING_CXX 编译定义和``cxx_headers`` 仅包含用于 C++ 的目录(省略了编译器 ID 检查)。它还为 CUDA 指定了一个 COMPILING_CUDA 编译定义。

请注意,使用 Visual Studio 生成器Xcode 无法表示目标范围的编译定义,也无法为 CCXX 语言单独包含目录。此外,使用 Visual Studio 生成器 无法分别为 CCXX 语言表示目标范围内的标志。在这些生成器下,如果有任何 C++ 源,则将使用 CXX 计算 C 和 C++ 源的表达式,否则使用 C。解决方法是为每种源文件语言创建单独的库:

add_library(myapp_c foo.c)
add_library(myapp_cxx bar.cpp)
target_compile_options(myapp_cxx PUBLIC -fno-exceptions)
add_executable(myapp main.cpp)
target_link_libraries(myapp myapp_c myapp_cxx)
$<COMPILE_LANG_AND_ID:language,compiler_ids>

在 3.15 版本加入.

1 当用于编译单元的语言与 language 匹配并且 language 编译器的 CMake 编译器 id 与 compiler_ids 中任何一个以逗号分隔的条目匹配时,否则为 ``0 ``。此表达式是“$<COMPILE_LANGUAGE:language>”和“$<LANG_COMPILER_ID:compiler_ids>”组合的缩写形式。此表达式可用于指定编译选项、编译定义,以及在目标中包含特定语言和编译器组合的源文件的目录。例如:

add_executable(myapp main.cpp foo.c bar.cpp zot.cu)
target_compile_definitions(myapp
  PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:COMPILING_CXX_WITH_CLANG>
          $<$<COMPILE_LANG_AND_ID:CXX,Intel>:COMPILING_CXX_WITH_INTEL>
          $<$<COMPILE_LANG_AND_ID:C,Clang>:COMPILING_C_WITH_CLANG>
)

这指定了基于编译器 ID 和编译语言的不同编译定义的使用。当 Clang 是 CXX 编译器时,这个例子将有一个 COMPILING_CXX_WITH_CLANG 编译定义,当 Intel 是 CXX 编译器时,这个例子将有一个``COMPILING_CXX_WITH_INTEL``。同样,当 C 编译器是 Clang 时,它只会看到 COMPILING_C_WITH_CLANG 定义。

如果没有 COMPILE_LANG_AND_ID 生成器表达式,相同的逻辑将表示为:

target_compile_definitions(myapp
  PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:COMPILING_CXX_WITH_CLANG>
          $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Intel>>:COMPILING_CXX_WITH_INTEL>
          $<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:Clang>>:COMPILING_C_WITH_CLANG>
)

编译功能

$<COMPILE_FEATURES:features>

在 3.1 版本加入.

其中 features 是以逗号分隔的列表。如果所有“功能”都可用于“head”目标,则评估为“1”,否则评估为“0”。如果在评估目标的链接实现时使用此表达式,并且如果任何依赖项传递地增加了“头”目标所需的 C_STANDARDCXX_STANDARD,则会报告错误。有关编译功能的信息和支持的编译器列表,请参阅 cmake-compile-features(7) 手册。

链接器语言和 ID

在 3.18 版本加入.

评估链接选项时目标的链接语言。有关此生成器表达式的可移植性的注释,请参阅 相关的布尔表达式 <Boolean LINK_LANGUAGE Generator Expression>``$<LINK_LANGUAGE:languages>`

备注

链接库属性不支持此生成器表达式,以避免由于对这些属性进行双重评估而产生副作用。

$<LINK_LANGUAGE:languages>

在 3.18 版本加入.

当用于链接步骤的语言与“语言”中任何以逗号分隔的条目匹配时为“1”,否则为“0”。该表达式可用于指定目标中特定语言的链接库、链接选项、链接目录和链接依赖项。例如:

add_library(api_C ...)
add_library(api_CXX ...)
add_library(api INTERFACE)
target_link_options(api   INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c>
                                    $<$<LINK_LANGUAGE:CXX>:-opt_cxx>)
target_link_libraries(api INTERFACE $<$<LINK_LANGUAGE:C>:api_C>
                                    $<$<LINK_LANGUAGE:CXX>:api_CXX>)

add_executable(myapp1 main.c)
target_link_options(myapp1 PRIVATE api)

add_executable(myapp2 main.cpp)
target_link_options(myapp2 PRIVATE api)

这指定使用 api 目标来链接目标 myapp1myapp2。实际上,myapp1 将与目标``api_C`` 和选项``-opt_c`` 链接,因为它将使用``C`` 作为链接语言。 myapp2 将与``api_CXX`` 和选项``-opt_cxx`` 链接,因为``CXX`` 将是链接语言。

在 3.18 版本加入.

1 当用于链接步骤的语言与 language 匹配并且语言链接器的 CMake 编译器 ID 与 compiler_ids 中任何一个以逗号分隔的条目匹配时,否则为 0。此表达式是“$<LINK_LANGUAGE:language>”和“$<LANG_COMPILER_ID:compiler_ids>”组合的缩写形式。此表达式可用于指定目标中特定语言和链接器组合的链接库、链接选项、链接目录和链接依赖项。例如:

add_library(libC_Clang ...)
add_library(libCXX_Clang ...)
add_library(libC_Intel ...)
add_library(libCXX_Intel ...)

add_executable(myapp main.c)
if (CXX_CONFIG)
  target_sources(myapp PRIVATE file.cxx)
endif()
target_link_libraries(myapp
  PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang>
          $<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang>
          $<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel>
          $<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>)

这指定了基于编译器标识和链接语言使用不同的链接库。当 ClangAppleClangCXX 链接器时,此示例将目标 libCXX_Clang 作为链接依赖项,当 Intel 是 `` 时,libCXX_Intel CXX`` 链接器。同样,当 C 链接器是 ClangAppleClang 时,目标 libC_Clang 将被添加为链接依赖项,而 libC_IntelIntel 是 `` C``链接器。

请参阅 ``$<LINK_LANGUAGE:language> 相关的说明以了解有关此生成器表达式的使用的限制。

目标相关表达式

这些查询引用目标“tgt”。除非另有说明,这可以是任何运行时工件,即:

在下文中,短语“the tgt filename”表示 tgt 二进制文件的名称。这必须与短语“目标名称”区分开来,后者只是字符串“tgt”。

$<TARGET_EXISTS:tgt>

在 3.12 版本加入.

1 如果``tgt`` 作为 CMake 目标存在,否则为 0

$<TARGET_NAME_IF_EXISTS:tgt>

在 3.12 版本加入.

如果目标存在,则为目标名称“tgt”,否则为空字符串。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_NAME:...>

... 标记为目标的名称。如果将目标导出到多个相关导出集,则这是必需的。 ... 必须是目标的文字名称,它不能包含生成器表达式。

$<TARGET_PROPERTY:tgt,prop>

目标“tgt”上的属性“prop”的值。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

在 3.26 版本发生变更: 在评估 传递使用要求 期间遇到时,通常在 INTERFACE_* 目标属性中,tgt 名称的查找发生在指定要求的目标目录中,而不是目录中正在评估表达式的消费目标的名称。

$<TARGET_PROPERTY:prop>

正在评估表达式的目标上属性“prop”的值。请注意,对于 传递使用要求 中的生成器表达式,这是使用目标而不是指定要求的目标。

$<TARGET_OBJECTS:tgt>

在 3.1 版本加入.

构建“tgt”产生的对象列表。这通常用于 object library 目标。

$<TARGET_POLICY:policy>

如果创建“head”目标时“policy”为“NEW”,则为“1”,否则为“0”。如果未设置 policy,将发出该策略的警告消息。此生成器表达式仅适用于策略的子集。

$<TARGET_FILE:tgt>

tgt 二进制文件的完整路径。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项,除非该表达式正在 add_custom_command()add_custom_target() 中使用。

$<TARGET_FILE_BASE_NAME:tgt>

在 3.15 版本加入.

tgt 的基本名称,即没有前缀和后缀的 $<TARGET_FILE_NAME:tgt>。例如,如果 tgt 文件名为 libbase.so,则基本名称为 base

另请参阅:prop_tgt:OUTPUT_NAMEARCHIVE_OUTPUT_NAMELIBRARY_OUTPUT_NAME 和:prop_tgt:RUNTIME_OUTPUT_NAME 目标属性及其配置特定变体:prop_tgt:OUTPUT_NAME_<CONFIG>、:prop_tgt: ARCHIVE_OUTPUT_NAME_<CONFIG>LIBRARY_OUTPUT_NAME_<CONFIG>RUNTIME_OUTPUT_NAME_<CONFIG>

<CONFIG>_POSTFIXDEBUG_POSTFIX 目标属性也可以考虑。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_FILE_PREFIX:tgt>

在 3.15 版本加入.

tgt 文件名的前缀(例如 lib)。

另请参阅 PREFIX 目标属性。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_FILE_SUFFIX:tgt>

在 3.15 版本加入.

tgt 文件名的后缀(扩展名为 .so.exe)。

另请参阅 SUFFIX 目标属性。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_FILE_NAME:tgt>

tgt 文件名。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_FILE_DIR:tgt>

tgt 二进制文件的目录。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_LINKER_FILE:tgt>

链接到“tgt”目标时使用的文件。这通常是 tgt 代表的库(.a.lib.so),但对于 DLL 平台上的共享库,它将是 ` .lib` 导入与 DLL 关联的库。

$<TARGET_LINKER_FILE_BASE_NAME:tgt>

在 3.15 版本加入.

用于链接目标 tgt 的文件的基本名称,即 $<TARGET_LINKER_FILE_NAME:tgt> 没有前缀和后缀。例如,如果目标文件名为 libbase.a,则基本名称为 base

另请参阅:prop_tgt:OUTPUT_NAMEARCHIVE_OUTPUT_NAME 和:prop_tgt:LIBRARY_OUTPUT_NAME 目标属性及其配置特定变体:prop_tgt:OUTPUT_NAME_<CONFIG>ARCHIVE_OUTPUT_NAME_<CONFIG>`和 :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>

<CONFIG>_POSTFIXDEBUG_POSTFIX 目标属性也可以考虑。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_LINKER_FILE_PREFIX:tgt>

在 3.15 版本加入.

用于链接目标“tgt”的文件前缀。

另请参阅 PREFIXIMPORT_PREFIX 目标属性。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_LINKER_FILE_SUFFIX:tgt>

在 3.15 版本加入.

用于链接的文件后缀,其中“tgt”是目标名称。

后缀对应于文件扩展名(例如“.so”或“.lib”)。

另请参阅 SUFFIXIMPORT_SUFFIX 目标属性。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_LINKER_FILE_NAME:tgt>

用于链接目标“tgt”的文件名。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_LINKER_FILE_DIR:tgt>

用于链接目标“tgt”的文件目录。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_SONAME_FILE:tgt>

带有 soname (.so.3) 的文件,其中 tgt 是目标的名称。

$<TARGET_SONAME_FILE_NAME:tgt>

带有 soname 的文件名(.so.3)。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_SONAME_FILE_DIR:tgt>

soname 的目录(.so.3)。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_PDB_FILE:tgt>

在 3.1 版本加入.

链接器生成的程序数据库文件 (.pdb) 的完整路径,其中“tgt”是目标名称。

另请参阅 PDB_NAMEPDB_OUTPUT_DIRECTORY 目标属性及其配置特定变体 PDB_NAME_<CONFIG>PDB_OUTPUT_DIRECTORY_<CONFIG>

$<TARGET_PDB_FILE_BASE_NAME:tgt>

在 3.15 版本加入.

链接器生成的程序数据库文件 (.pdb) 的基本名称,其中“tgt”是目标名称。

基本名称对应于目标 PDB 文件名(参见 $<TARGET_PDB_FILE_NAME:tgt>),不带前缀和后缀。例如,如果目标文件名为 base.pdb,则基本名称为 base

另请参阅:prop_tgt:PDB_NAME 目标属性及其配置特定变体:prop_tgt:PDB_NAME_<CONFIG>

<CONFIG>_POSTFIXDEBUG_POSTFIX 目标属性也可以考虑。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项。

$<TARGET_PDB_FILE_NAME:tgt>

在 3.1 版本加入.

链接器生成的程序数据库文件 (.pdb) 的名称。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_PDB_FILE_DIR:tgt>

在 3.1 版本加入.

链接器生成的程序数据库文件 (.pdb) 的目录。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_BUNDLE_DIR:tgt>

在 3.9 版本加入.

包目录的完整路径(/path/to/my.app/path/to/my.framework/path/to/my.bundle),其中 ` tgt` 是目标的名称。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_BUNDLE_DIR_NAME:tgt>

在 3.24 版本加入.

包目录的名称(my.appmy.framework 或``my.bundle``),其中``tgt`` 是目标的名称。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_BUNDLE_CONTENT_DIR:tgt>

在 3.9 版本加入.

包内容目录的完整路径,其中“tgt”是目标名称。对于 macOS SDK,它导致“/path/to/my.app/Contents”、“/path/to/my.framework”或“/path/to/my.bundle/Contents” .对于所有其他 SDK(例如 iOS),它导致“/path/to/my.app”、“/path/to/my.framework”或“/path/to/my.bundle”由于扁平束结构。

请注意,tgt 不会添加为该表达式所评估的目标的依赖项(请参阅策略:policy:CMP0112)。

$<TARGET_RUNTIME_DLLS:tgt>

在 3.21 版本加入.

目标在运行时依赖的 DLL 列表。这由目标的传递依赖项中所有“共享”目标的位置决定。在除可执行文件、“SHARED”库和“MODULE”库之外的目标上使用此生成器表达式是错误的。 在非 DLL 平台上,此表达式的计算结果始终为空字符串

此生成器表达式可用于将目标依赖的所有 DLL 复制到 POST_BUILD 自定义命令中的输出目录中,使用 cmake -E copy -t 命令.例如:

find_package(foo CONFIG REQUIRED) # package generated by install(EXPORT)

add_executable(exe main.c)
target_link_libraries(exe PRIVATE foo::foo foo::bar)
add_custom_command(TARGET exe POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:exe> $<TARGET_RUNTIME_DLLS:exe>
  COMMAND_EXPAND_LISTS
)

备注

进口目标 只有在知道其 .dll 文件的位置时才受支持。导入的 SHARED 库必须将 IMPORTED_LOCATION 设置为其 .dll 文件。有关详细信息,请参阅 add_library imported libraries 部分。许多 查找模块 生成具有 UNKNOWN 类型的导入目标,因此将被忽略。

在支持运行时路径 (RPATH) 的平台上,请参考 INSTALL_RPATH 目标属性。在 Apple 平台上,请参考 INSTALL_NAME_DIR 目标属性。

导出和安装表达式

$<INSTALL_INTERFACE:...>

使用 install(EXPORT) 导出属性时 ... 的内容,否则为空。

$<BUILD_INTERFACE:...>

使用 export() 导出属性时,或者当目标被同一构建系统中的另一个目标使用时,...` 的内容。否则扩展为空字符串。

$<BUILD_LOCAL_INTERFACE:...>

在 3.26 版本加入.

当目标被同一构建系统中的另一个目标使用时,... 的内容。否则扩展为空字符串。

$<INSTALL_PREFIX>

当通过 install(EXPORT) 导出目标时,或在 INSTALL_NAME_DIR 属性或 install(RUNTIME_DEPENDENCY_SET) ``INSTALL_NAME_DIR`() 参数中评估时安装前缀的内容`,否则为空。

多级表达式评估

$<GENEX_EVAL:expr>

在 3.12 版本加入.

expr 的内容在当前上下文中评估为生成器表达式。这使得能够使用生成器表达式,其评估结果本身就是生成器表达式。

$<TARGET_GENEX_EVAL:tgt,expr>

在 3.12 版本加入.

expr 的内容在 tgt 目标的上下文中评估为生成器表达式。这允许使用本身包含生成器表达式的自定义目标属性。

当您想要管理支持生成器表达式的自定义属性时,具有计算生成器表达式的能力非常有用。例如:

add_library(foo ...)

set_property(TARGET foo PROPERTY
  CUSTOM_KEYS $<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>
)

add_custom_target(printFooKeys
  COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_PROPERTY:foo,CUSTOM_KEYS>
)

printFooKeys 自定义命令的这种天真实现是错误的,因为``CUSTOM_KEYS`` 目标属性未被评估并且内容按原样传递(即 $<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>) .

要获得预期的结果(即如果配置为“调试”,则为“FOO_EXTRA_THINGS”),需要评估“$<TARGET_PROPERTY:foo,CUSTOM_KEYS>”的输出:

add_custom_target(printFooKeys
  COMMAND ${CMAKE_COMMAND} -E
    echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>>
)

转义字符

这些表达式求值为特定的字符串文字。在需要防止它们具有特殊含义的地方使用它们代替实际的字符串文字。

$<ANGLE-R>

文字``>``。例如用于比较包含 > 的字符串。

$<COMMA>

一个字面的``,。例如用于比较包含 ``, 的字符串。

$<SEMICOLON>

一个字面的``;``。用于防止对带有 ; 的参数进行列表扩展。

弃用的表达式

$<CONFIGURATION>

配置名称。自 CMake 3.0 起已弃用。使用 CONFIG 代替。