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


168

我问这是我下次使用CMake时的提醒。它永远不会停留,而且Google的结果也不是很好。

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

Answers:


281

在编写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})

文档资料

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

首先是“普通变量”以及您需要了解的有关其范围的内容:

  • 普通变量是可见的CMakeLists.txt,他们在设置和一切所谓的从那里(add_subdirectory()include()macro()function())。
  • add_subdirectory()function()命令是特殊的,因为他们的开拓自己的范围。
    • 含义变量set(...)仅在此处可见,并且它们会复制从中调用的范围级别的所有普通变量(称为父范围)。
    • 因此,如果您位于子目录或函数中,则可以使用以下命令修改父作用域中已经存在的变量: set(... PARENT_SCOPE)
    • 您可以通过将变量名称作为函数参数传递来在函数中使用此功能。一个例子function(xyz _resultVar)是设置set(${_resultVar} 1 PARENT_SCOPE)
  • 另一方面,您在脚本中设置的所有内容include()macro()脚本都将直接在调用它们的范围内修改变量。

其次是“全局变量缓存”。您需要了解的有关缓存的信息:

  • 如果在当前范围内未定义具有给定名称的普通变量,则CMake将查找匹配的Cache条目。
  • 缓存值存储在CMakeCache.txt二进制输出目录中的文件中。
  • 缓存中的值可以在生成之前在CMake的GUI应用程序中进行修改。因此,与普通变量相比,它们具有a type和a docstring。我通常不使用GUI,因此可以set(... CACHE INTERNAL "")用来设置全局值和持久性值。

    请注意,INTERNAL缓存变量类型确实暗示FORCE

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

  • 您可以使用命令行通过cmake -D var:type=valuejust cmake -D var=value或语法设置Cache中的条目cmake -C CMakeInitialCache.cmake
  • 您可以使用取消设置缓存中的条目unset(... CACHE)

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

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

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

  • 如果局部变量具有相同的名称,则它们会隐藏高速缓存的变量
  • find_...命令-如果成功-做写他们的结果缓存变量“这样没有呼叫将再次搜索”
  • CMake中的列表只是带有分号分隔符的字符串,因此引号很重要
    • set(MyVar a b c)"a;b;c"set(MyVar "a b c")"a b c"
    • 建议您在要给列表作为列表时始终使用引号,只有一个例外
    • 通常更喜欢使用list()命令来处理列表
  • 上述整个问题。特别是,建议使用functions()而不是,macros()因为您不希望局部变量出现在父作用域中。
  • CMake使用的许多变量是通过project()enable_language()调用设置的。因此,在使用这些命令之前设置一些变量可能很重要。
  • 环境变量可能与CMake生成make环境的位置以及使用make文件的时间不同。
    • 环境变量的更改不会重新触发生成过程。
    • 特别是生成的IDE环境可能与您的命令行有所不同,因此建议将环境变量传输到已缓存的内容中。

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

  • printf通过使用message()命令,只需使用旧的调试样式即可。CMake本身也提供了一些可立即使用的模块:CMakePrintHelpers.cmakeCMakePrintSystemInformation.cmake
  • CMakeCache.txt在二进制输出目录中查找文件。如果实际生成的make环境失败,则甚至会生成此文件。
  • 使用variable_watch()来查看/写入/删除变量的位置。
  • 查看目录属性CACHE_VARIABLESVARIABLES
  • 致电cmake --trace ...查看CMake的完整解析过程。那是最后的保留,因为它会产生很多输出。

特殊语法

  • 环境变量
    • 您可以$ENV{...}读写set(ENV{...} ...)环境变量
  • 生成器表达式
    • $<...>仅当CMake的生成器写入make环境时才评估生成器表达式(与解析器“就地”替换的普通变量的比较)
    • 非常方便,例如在编译器/链接器命令行和多配置环境中
  • 参考资料
    • 使用,${${...}}您可以在变量中指定变量名称并引用其内容。
    • 通常在给变量名称作为函数/宏参数时使用。
  • 常量值(请参阅if()命令)
    • 使用,if(MyVariable)您可以直接检查一个变量是否为true / false(此处不需要包含${...}
    • 真,如果常数是1ONYESTRUEY,或一个非零数。
    • 假如果常数0OFFNOFALSENIGNORENOTFOUND,空字符串,或后缀结尾-NOTFOUND
    • 此类语法通常用于if(MSVC),但对于不了解此语法快捷方式的人可能会造成混淆。
  • 递归替换
    • 您可以使用变量构造变量名称。在CMake替换了变量之后,它将再次检查结果是否为变量本身。这是CMake本身使用的非常强大的功能,例如作为模板的一种set(CMAKE_${lang}_COMPILER ...)
    • 但是请注意,这会使您头疼if()。这是CMAKE_CXX_COMPILER_IDis "MSVC"MSVCis 的示例"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()在不加引号的情况下将参数解释为变量或关键字”。
  • option()命令
    • 主要只是缓存的字符串,只能是ON或,OFF并且它们允许某些特殊处理,例如依赖项
    • 但是要注意,不要optionset命令与错误。赋予的值option实际上只是“初始值”(在第一个配置步骤中一次传输到高速缓存),此后将由用户通过CMake的GUI进行更改。

参考资料


使用时,if ("${MyString}" ...)我会看到警告:Policy CMP0054 is not set: Only interpret if() arguments as variables or keywords when unquoted。参见,例如,Build 367。有任何想法吗?
jww

取消引号${MyString}会导致“如果给定参数...”错误,例如CMake在if附近出现错误:“如果给定参数”后跟括号,“ NOT”,“ EQUALS”等
jww

@jww警告表示MyString确实包含一个变量名,该变量名将再次被取消引用。我相信没有人真的想要这种OLD行为。因此,从我的角度来看,它完全安全且向后兼容,只需设置策略CMP0054即可NEW(请参见此处的讨论)。
Florian

@jww避免这些问题/警告的最安全的方法就是执行if (MyString ...)(如果您的代码给出警告)。
Florian

谢谢。我们删除了所有出现的,${MyString}然后将其替换为MyString(或者我相信我们已全部删除)。仍然没有喜悦:建立372。废话甚至不是来自我们的代码。它似乎来自CMake。283行是if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
jww

18

这里有一些基本示例,可以帮助您快速入门。

一项变量

设置变量:

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关于变量的文档


1

$ENV{FOO}对于使用,其中FOO正在从环境变量回升。否则用作as ${FOO},其中FOO还有一些其他变量。对于设置,SET(FOO "foo")将在CMake中使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.