ChatGPT解决这个技术问题 Extra ChatGPT

设置和使用变量的 CMake 语法是什么?

下次我使用 CMake 时,我问这个是为了提醒自己。它永远不会坚持下去,而且谷歌的结果也不是很好。

在 CMake 中设置和使用变量的语法是什么?


H
Hugo Burd

在编写 CMake 脚本时,您需要了解很多关于语法以及如何在 CMake 中使用变量的知识。

语法

使用 set() 的字符串:

设置(MyString“一些文本”)

set(MyStringWithVar "其他一些文本:${MyString}")

set(MyStringWithQuot "一些引用:\"${MyStringWithVar}\"")

或使用 string()

字符串(附加 MyStringWithContent “${MyString}”)

使用 set() 的列表:

设置(我的列表“a”“b”“c”)

设置(我的列表 ${我的列表}“d”)

或者使用 list() 更好:

列表(追加我的列表“a”“b”“c”)

列表(追加我的列表“d”)

文件名列表:

set(MySourcesList "File.name" "File with Space.name")

list(APPEND MySourcesList "File.name" "File with Space.name")

add_excutable(MyExeTarget ${MySourcesList})

文档

CMake/语言语法

CMake:变量列表字符串

CMake:有用的变量

CMake set() 命令

CMake string()命令

CMake list() 命令

Cmake:生成器表达式

范围或“我的变量有什么值?”

首先是“正常变量”以及您需要了解的有关其范围的事情:

普通变量对设置它们的 CMakeLists.txt 以及从那里调用的所有内容都是可见的(add_subdirectory()、include()、macro() 和 function())。

add_subdirectory() 和 function() 命令很特别,因为它们打开了自己的作用域。含义变量 set(...) 仅在那里可见,并且它们复制了它们被调用的范围级别的所有普通变量(称为父范围)。因此,如果您在子目录或函数中,您可以使用 set(... PARENT_SCOPE) 修改父作用域中已经存在的变量。您可以通过将变量名称作为函数参数传递来在函数中使用它,例如。一个例子是 function(xyz _resultVar) is setting set(${_resultVar} 1 PARENT_SCOPE)

含义变量 set(...) 仅在那里可见,并且它们复制了它们被调用的范围级别的所有普通变量(称为父范围)。

因此,如果您在子目录或函数中,您可以使用 set(... PARENT_SCOPE) 修改父范围中已经存在的变量

您可以通过将变量名称作为函数参数传递来利用它,例如在函数中。一个例子是 function(xyz _resultVar) is setting set(${_resultVar} 1 PARENT_SCOPE)

另一方面,您在 include() 或 macro() 脚本中设置的所有内容都将直接在调用它们的范围内修改变量。

其次是“全局变量缓存”。关于缓存你需要知道的事情:

如果在当前范围内没有定义具有给定名称的普通变量,CMake 将寻找匹配的缓存条目。

缓存值存储在二进制输出目录中的 CMakeCache.txt 文件中。

缓存中的值可以在生成之前在 CMake 的 GUI 应用程序中进行修改。因此,与普通变量相比,它们具有类型和文档字符串。我通常不使用 GUI,所以我使用 set(... CACHE INTERNAL "") 来设置我的全局和持久值。请注意,内部缓存变量类型确实意味着 FORCE

在 CMake 脚本中,如果您使用 set(... CACHE ... FORCE) 语法,您只能更改现有的缓存条目。这种行为例如由 CMake 本身使用,因为它通常不会强制缓存条目本身,因此您可以使用另一个值预定义它。

您可以使用命令行在缓存中设置条目,语法为 cmake -D var:type=value,只需 cmake -D var=value 或使用 cmake -C CMakeInitialCache.cmake。

您可以使用 unset(... CACHE) 取消设置缓存中的条目。

缓存是全局的,您几乎可以在 CMake 脚本中的任何位置设置它们。但我建议您三思而后行,在哪里使用缓存变量(它们是全局的并且是持久的)。我通常更喜欢 set_property(GLOBAL PROPERTY ...)set_property(GLOBAL APPEND PROPERTY ...) 语法来定义我自己的非持久性全局变量。

变量陷阱和“如何调试变量更改?”

为避免陷阱,您应该了解以下有关变量的知识:

如果两者具有相同的名称,则局部变量会隐藏缓存的变量

find_... 命令 - 如果成功 - 将它们的结果写入缓存变量“这样就不会再次搜索”

CMake 中的列表只是带有分号分隔符的字符串,因此引号很重要 set(MyVar abc) is "a;b;c" and set(MyVar "ab c") is "ab c" 建议您始终当您想将列表作为列表时使用引号,但一个例外 通常更喜欢使用 list() 命令来处理列表

set(MyVar abc) 是 "a;b;c" 并且 set(MyVar "ab c") 是 "ab c"

建议您始终使用引号,但当您想将列表作为列表给出时除外

通常更喜欢使用 list() 命令来处理列表

上面描述的整个范围问题。特别推荐使用 functions() 而不是 macros() 因为你不希望你的局部变量出现在父作用域中。

CMake 使用的许多变量是通过 project() 和 enable_language() 调用设置的。因此,在使用这些命令之前设置一些变量可能很重要。

环境变量可能与 CMake 生成 make 环境的位置以及使用 make 文件的时间不同。环境变量的更改不会重新触发生成过程。尤其是生成的 IDE 环境可能与您的命令行不同,因此建议将您的环境变量传输到缓存中。

环境变量的更改不会重新触发生成过程。

尤其是生成的 IDE 环境可能与您的命令行不同,因此建议将您的环境变量传输到缓存中。

有时只有调试变量会有所帮助。以下内容可能对您有所帮助:

只需使用 message() 命令即可使用旧的 printf 调试样式。 CMake 本身还附带了一些现成的模块:CMakePrintHelpers.cmake、CMakePrintSystemInformation.cmake

查看二进制输出目录中的 CMakeCache.txt 文件。如果您的 make 环境的实际生成失败,甚至会生成此文件。

使用 variable_watch() 查看您的变量在哪里被读取/写入/删除。

查看目录属性 CACHE_VARIABLES 和 VARIABLES

调用 cmake --trace ... 查看 CMake 的完整解析过程。这是最后的储备,因为它产生了大量的输出。

特殊语法

环境变量您可以读取 $ENV{...} 并编写 set(ENV{...} ...) 环境变量

您可以读取 $ENV{...} 并写入 set(ENV{...} ...) 环境变量

生成器表达式 生成器表达式 $<...> 仅在 CMake 的生成器编写 make 环境时进行评估(它与由解析器“就地”替换的普通变量进行比较)非常方便,例如在编译器/链接器命令行和多- 配置环境

生成器表达式 $<...> 仅在 CMake 的生成器编写 make 环境时进行评估(它与由解析器“就地”替换的普通变量进行比较)

非常方便,例如在编译器/链接器命令行和多配置环境中

引用 使用 ${${...}} 您可以在变量中指定变量名称并引用其内容。通常在将变量名称作为函数/宏参数时使用。

使用 ${${...}} 您可以在变量中指定变量名称并引用其内容。

通常在将变量名称作为函数/宏参数时使用。

常量值(参见 if() 命令) 使用 if(MyVariable),您可以直接检查变量的真/假(此处不需要封闭的 ${...}) 如果常量为 1、ON、YES、TRUE,则为 True , Y 或非零数。如果常量为 0、OFF、NO、FALSE、N、IGNORE、NOTFOUND、空字符串或以后缀 -NOTFOUND 结尾,则为 False。这种语法通常用于 if(MSVC) 之类的东西,但对于不知道这种语法快捷方式的人来说,它可能会造成混淆。

使用 if(MyVariable) 您可以直接检查变量的真/假(此处不需要封闭的 ${...})

如果常数为 1、ON、YES、TRUE、Y 或非零数字,则为真。

如果常量为 0、OFF、NO、FALSE、N、IGNORE、NOTFOUND、空字符串或以后缀 -NOTFOUND 结尾,则为 False。

这种语法通常用于 if(MSVC) 之类的东西,但对于不知道这种语法快捷方式的人来说,它可能会造成混淆。

递归替换 您可以使用变量构造变量名称。在 CMake 替换了变量后,它会再次检查结果是否是变量本身。这是 CMake 本身使用的非常强大的功能,例如作为模板集的一种(CMAKE_${lang}_COMPILER ...)但请注意,这可能会让您在 if() 命令中头疼。以下是 CMAKE_CXX_COMPILER_ID 为“MSVC”且 MSVC 为“1”的示例: if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 为真,因为它的计算结果为 if("1" STREQUAL "1") if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 为假,因为它的计算结果为 if("MSVC" STREQUAL "1") 所以这里最好的解决方案是 - 见上文 - 直接检查 if(MSVC) 好消息是这已修复CMake 3.1 引入了策略 CMP0054。我建议始终将 cmake_policy(SET CMP0054 NEW) 设置为“仅在未引用时将 if() 参数解释为变量或关键字”。

您可以使用变量构造变量名称。在 CMake 替换了变量后,它会再次检查结果是否是变量本身。这是 CMake 本身使用的非常强大的功能,例如作为模板集的一种(CMAKE_${lang}_COMPILER ...)

但请注意,这会使您在 if() 命令中感到头疼。以下是 CMAKE_CXX_COMPILER_ID 为“MSVC”且 MSVC 为“1”的示例: if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 为真,因为它的计算结果为 if("1" STREQUAL "1") if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 是假的,因为它的计算结果为 if("MSVC" STREQUAL "1") 所以这里最好的解决方案是 - 见上文 - 直接检查 if(MSVC)

if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 为真,因为它的计算结果为 if("1" STREQUAL "1")

if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 为假,因为它的计算结果为 if("MSVC" STREQUAL "1")

所以这里最好的解决方案是 - 见上文 - 直接检查 if(MSVC)

好消息是这个问题在 CMake 3.1 中通过引入策略 CMP0054 得到了修复。我建议始终将 cmake_policy(SET CMP0054 NEW) 设置为“仅在未引用时将 if() 参数解释为变量或关键字”。

option() 命令 主要只是缓存的字符串,它们只能是 ON 或 OFF,它们允许一些特殊的处理,例如依赖关系。但是请注意,不要将选项与 set 命令混淆。赋予 option 的值实际上只是“初始值”(在第一个配置步骤中传输一次到缓存),然后由用户通过 CMake 的 GUI 进行更改。

主要只是缓存的字符串,只能打开或关闭,它们允许一些特殊处理,例如依赖项

但请注意,不要将选项与 set 命令混淆。赋予 option 的值实际上只是“初始值”(在第一个配置步骤中传输一次到缓存),然后由用户通过 CMake 的 GUI 进行更改。

参考

如何使用 CMake?

cmake,迷失在全局变量的概念中(以及 PARENT_SCOPE 或 add_subdirectory 替代方案)

循环遍历字符串列表

如何存储 CMake 构建设置

CMake与STREQUAL比较空字符串失败

我什么时候应该引用变量?


当我使用 if ("${MyString}" ...) 时,我看到了警告:Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted。例如,参见 Build 367。有任何想法吗?
并且取消引用 ${MyString} 会导致 “if given arguments ...” 出现一堆错误,例如 CMake error near if: “if given arguments” followed by parantheses, “NOT”, “EQUALS” and similar
@jww 警告意味着 MyString 确实包含一个变量名,然后将再次取消引用该变量名。我相信没有人真正想要这种 OLD 行为。因此,从我的角度来看,它完全安全且向后兼容,只需将策略 CMP0054 设置为 NEW(参见讨论 here)。
@jww 避免这些问题/警告的最安全方法就是执行 if (MyString ...) (如果您的代码发出警告)。
谢谢。我们删除了所有出现的 ${MyString} 并将其替换为 MyString(或者我相信我们已将它们全部删除)。仍然没有喜悦:Build 372。废话甚至不是来自我们的代码。它似乎来自 CMake。第 283 行是 if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
C
CivFan

这里有几个基本示例,可以快速入门。

一项变量

设置变量:

SET(INSTALL_ETC_DIR "etc")

使用变量:

SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")

多项目变量(即列表)

设置变量:

SET(PROGRAM_SRCS
        program.c
        program_utils.c
        a_lib.c
        b_lib.c
        config.c
        )

使用变量:

add_executable(program "${PROGRAM_SRCS}")

CMake docs on variables


K
Kevin

$ENV{FOO} 用于使用,其中 FOO 是从环境变量中提取的。否则用作 ${FOO},其中 FOO 是一些其他变量。对于设置,SET(FOO "foo") 将在 CMake 中使用。