【问题标题】:What's the CMake syntax to set and use variables?设置和使用变量的 CMake 语法是什么?
【发布时间】:2015-09-11 07:46:46
【问题描述】:

我在下次使用 CMake 时提醒自己。它永远不会坚持下去,而且 Google 的结果也不是很好。

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

【问题讨论】:

标签: cmake


【解决方案1】:

在编写 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 将寻找匹配的缓存条目。
  • 缓存值存储在二进制输出目录的CMakeCache.txt 文件中。
  • 缓存中的值可以在生成之前在CMake's GUI应用程序中进行修改。因此,与普通变量相比,它们具有typedocstring。我通常不使用 GUI,所以我使用set(... CACHE INTERNAL "") 来设置我的全局值和持久值。

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

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

  • 您可以使用命令行在缓存中设置条目,语法为cmake -D var:type=valuecmake -D var=valuecmake -C CMakeInitialCache.cmake
  • 您可以使用unset(... CACHE)unset 缓存中的条目。

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

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

为避免误入歧途,您应该了解以下有关变量的知识:

  • 如果两个变量同名,则局部变量会隐藏缓存变量
  • find_... 命令 - 如果成功 - 将其结果写入缓存变量,“这样就不会再次搜索”
  • Lists in CMake are just strings with semicolons delimiters 因此引号很重要
    • 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 环境可能与您的命令行不同,因此建议将您的环境变量转移到缓存中。

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

  • 只需使用message() 命令即可使用旧的printf 调试样式。 CMake 本身还附带了一些现成可用的模块:CMakePrintHelpers.cmakeCMakePrintSystemInformation.cmake
  • 查看二进制输出目录中的CMakeCache.txt 文件。如果您的 make 环境的实际生成失败,甚至会生成此文件。
  • 使用variable_watch() 查看您的变量在何处被读取/写入/删除。
  • 查看目录属性CACHE_VARIABLESVARIABLES
  • 拨打cmake --trace ...查看CMake的完整解析过程。这是最后的储备,因为它会产生大量产出。

特殊语法

  • 环境变量
    • 可以读取$ENV{...}和写入set(ENV{...} ...)环境变量
  • 生成器表达式
    • 生成器表达式 $<...> 仅在 CMake 的生成器编写 make 环境时进行评估(它与由解析器“就地”替换的普通变量进行比较)
    • 非常方便,例如在编译器/链接器命令行和多配置环境中
  • 参考资料
    • 使用${${...}},您可以在变量中指定变量名称并引用其内容。
    • 通常在将变量名称作为函数/宏参数时使用。
  • 常量值(参见if() 命令)
    • 使用if(MyVariable),您可以直接检查变量的真/假(此处不需要封闭的${...}
    • 如果常量为 1ONYESTRUEY 或非零数字,则为真。
    • 如果常量为0OFFNOFALSENIGNORENOTFOUND、空字符串或以后缀-NOTFOUND结尾,则为假。
    • 这种语法通常用于if(MSVC) 之类的东西,但对于不知道这种语法快捷方式的人来说,它可能会造成混淆。
  • 递归替换
    • 您可以使用变量构造变量名称。在 CMake 替换了变量后,它会再次检查结果是否是变量本身。这是 CMake 本身使用的非常强大的功能,例如作为模板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)
    • 好消息是,随着 policy CMP0054 的引入,这已在 CMake 3.1 中得到修复。我建议始终将 cmake_policy(SET CMP0054 NEW) 设置为“仅在未引用时将 if() 参数解释为变量或关键字。”
  • option() 命令
    • 主要只是缓存的字符串,只能是ONOFF,它们允许一些特殊处理,例如dependencies
    • 但是注意,不要将optionset 命令混淆。赋予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
  • @jww 警告意味着MyString 确实包含一个变量名,然后将再次取消引用该变量名。我相信没有人真正想要OLD 的行为。因此,从我的角度来看,它完全安全且向后兼容,只需将策略 CMP0054 设置为 NEW(参见讨论 here)。
  • @jww 避免这些问题/警告的最安全方法就是执行if (MyString ...)(如果您的代码发出警告)。
  • 谢谢。我们删除了所有出现的${MyString} 并将其替换为MyString(或者我相信我们将它们全部删除)。仍然没有喜悦:Build 372。废话甚至不是来自我们的代码。它似乎来自 CMake。第 283 行是if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
【解决方案2】:

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

一项变量

设置变量:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-23
      • 2018-06-05
      • 1970-01-01
      • 2017-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多