【发布时间】:2015-09-11 07:46:46
【问题描述】:
我在下次使用 CMake 时提醒自己。它永远不会坚持下去,而且 Google 的结果也不是很好。
在 CMake 中设置和使用变量的语法是什么?
【问题讨论】:
标签: cmake
我在下次使用 CMake 时提醒自己。它永远不会坚持下去,而且 Google 的结果也不是很好。
在 CMake 中设置和使用变量的语法是什么?
【问题讨论】:
标签: cmake
在编写 CMake 脚本时,您需要了解很多关于语法以及如何在 CMake 中使用变量的知识。
使用set()的字符串:
set(MyString "Some Text")set(MyStringWithVar "Some other Text: ${MyString}")set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")或者string():
string(APPEND MyStringWithContent " ${MyString}")使用set()的列表:
set(MyList "a" "b" "c")set(MyList ${MyList} "d")或者list()更好:
list(APPEND MyList "a" "b" "c")list(APPEND MyList "d")文件名列表:
set(MySourcesList "File.name" "File with Space.name")list(APPEND MySourcesList "File.name" "File with Space.name")add_excutable(MyExeTarget ${MySourcesList})set() Commandstring()Commandlist() Command首先是“正常变量”以及您需要了解的有关其范围的事情:
CMakeLists.txt 可见,并且从那里调用的所有内容(add_subdirectory()、include()、macro() 和function())。add_subdirectory() 和function() 命令很特别,因为它们打开了自己的作用域。
set(...) 仅在此处可见,它们会复制所有正常变量的副本,它们是从它们调用的范围级别(称为父范围)。set(... PARENT_SCOPE) 修改父作用域中已经存在的变量
function(xyz _resultVar) 正在设置 set(${_resultVar} 1 PARENT_SCOPE)
include() 或macro() 脚本中设置的所有内容都将直接在调用它们的范围内修改变量。 其次是“全局变量缓存”。关于缓存你需要知道的事情:
CMakeCache.txt 文件中。缓存中的值可以在生成之前在CMake's GUI应用程序中进行修改。因此,与普通变量相比,它们具有type 和docstring。我通常不使用 GUI,所以我使用set(... CACHE INTERNAL "") 来设置我的全局值和持久值。
请注意INTERNAL缓存变量类型确实暗示FORCE
在 CMake 脚本中,如果您使用 set(... CACHE ... FORCE) 语法,您只能更改现有的缓存条目。这种行为是利用例如由 CMake 自己决定,因为它通常不会强制缓存条目本身,因此您可以使用其他值预先定义它。
cmake -D var:type=value、cmake -D var=value 或cmake -C CMakeInitialCache.cmake。unset(... CACHE)unset 缓存中的条目。缓存是全局的,您几乎可以在 CMake 脚本中的任何位置设置它们。但我建议您三思而后行,在哪里使用缓存变量(它们是全局的并且是持久的)。我通常更喜欢set_property(GLOBAL PROPERTY ...) 和set_property(GLOBAL APPEND PROPERTY ...) 语法来定义我自己的非持久性全局变量。
为避免误入歧途,您应该了解以下有关变量的知识:
find_... 命令 - 如果成功 - 将其结果写入缓存变量,“这样就不会再次搜索”set(MyVar a b c) 是 "a;b;c" 和 set(MyVar "a b c") 是 "a b c"
list() 命令来处理列表functions() 而不是macros(),因为您不希望局部变量出现在父作用域中。 project() 和enable_language() 调用设置的。因此,在使用这些命令之前设置一些变量可能很重要。有时只有调试变量会有所帮助。以下内容可能对您有所帮助:
message() 命令即可使用旧的printf 调试样式。 CMake 本身还附带了一些现成可用的模块:CMakePrintHelpers.cmake、CMakePrintSystemInformation.cmake
CMakeCache.txt 文件。如果您的 make 环境的实际生成失败,甚至会生成此文件。cmake --trace ...查看CMake的完整解析过程。这是最后的储备,因为它会产生大量产出。$ENV{...}和写入set(ENV{...} ...)环境变量$<...> 仅在 CMake 的生成器编写 make 环境时进行评估(它与由解析器“就地”替换的普通变量进行比较)${${...}},您可以在变量中指定变量名称并引用其内容。if() 命令)
if(MyVariable),您可以直接检查变量的真/假(此处不需要封闭的${...})1、ON、YES、TRUE、Y 或非零数字,则为真。 0、OFF、NO、FALSE、N、IGNORE、NOTFOUND、空字符串或以后缀-NOTFOUND结尾,则为假。
if(MSVC) 之类的东西,但对于不知道这种语法快捷方式的人来说,它可能会造成混淆。set(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_policy(SET CMP0054 NEW) 设置为“仅在未引用时将 if() 参数解释为变量或关键字。”option() 命令
ON 或OFF,它们允许一些特殊处理,例如dependencies
option 与set 命令混淆。赋予option 的值实际上只是“初始值”(在第一个配置步骤中传输一次到缓存中),然后由用户通过CMake's GUI 进行更改。【讨论】:
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。
MyString 确实包含一个变量名,然后将再次取消引用该变量名。我相信没有人真正想要OLD 的行为。因此,从我的角度来看,它完全安全且向后兼容,只需将策略 CMP0054 设置为 NEW(参见讨论 here)。
if (MyString ...)(如果您的代码发出警告)。
${MyString} 并将其替换为MyString(或者我相信我们将它们全部删除)。仍然没有喜悦:Build 372。废话甚至不是来自我们的代码。它似乎来自 CMake。第 283 行是if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")。
这里有几个基本示例可以快速入门。
设置变量:
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}")
【讨论】:
$ENV{FOO} 用于使用,其中FOO 是从环境变量中获取的。否则用作${FOO},其中FOO 是其他一些变量。对于设置,SET(FOO "foo") 将在 CMake 中使用。
【讨论】: