什么是CMake语法来设置和使用variables?

我在下一次使用CMake时提醒自己。 它从来没有坚持,谷歌的结果不是很好。

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

在编写CMake脚本时,需要了解很多关于CMake语法和如何使用variables的知识。

语法

使用set()string:

  • 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})

文档

  • CMake /语言语法
  • CMake:variables列表string
  • CMake:有用的variables
  • CMake set()命令
  • CMake string()命令
  • CMake list()命令
  • Cmake:生成器expression式

范围或“我的variables有什么价值?”

首先是“正常variables”和你需要知道的范围:

  • 正常variables对于它们设置的CMakeLists.txt以及从那里调用的所有东西( add_subdirectory()include()macro()function() )是可见的。
  • add_subdirectory()function()命令是特殊的,因为它们打开了自己的范围。
    • 意义variablesset(...)仅在那里可见,并且它们作为从其调用范围级别(称为父范围)的所有正常variables的副本。
    • 所以,如果你在一个子目录或者一个函数中,你可以使用set(... PARENT_SCOPE)修改父范围中已经存在的variables,
    • 你可以使用这个例如在函数中传递variables名作为函数参数。 一个例子是function(xyz _resultVar)是设置set(${_resultVar} 1 PARENT_SCOPE)
  • 另一方面,你在include()或者macro()脚本中设置的所有东西都会直接在它们被调用的范围内修改variables。

其次是“全局variablescaching”。 你需要知道的有关caching的事情:

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

    请注意, INTERNALcachingvariablestypes意味着FORCE

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

  • 您可以使用命令行在cmake -D var:type=value设置caching中的条目,只需cmake -D var=value或使用cmake -C CMakeInitialCache.cmake
  • 您可以使用unset(... CACHE)caching中的条目。

caching是全球性的,你可以在你的CMake脚本的任何地方设置它们。 但是我build议你考虑一下使用Cachevariables的地方(它们是全局的,它们是持久的)。 我通常更喜欢set_property(GLOBAL PROPERTY ...)set_property(GLOBAL APPEND PROPERTY ...)语法来定义我自己的非set_property(GLOBAL APPEND PROPERTY ...)性全局variables。

可变的陷阱和“如何debuggingvariables的变化?

为了避免陷阱,您应该了解以下variables:

  • CMake中的列表只是带有分号分隔符的string,因此引号很重要
    • set(MyVar abc)"a;b;c"set(MyVar "abc")"abc"
    • build议您总是使用引号,但有一个例外,当您要列出一个列表
    • 一般更喜欢list()命令来处理列表
  • 上述的整个范围问题。 特别是build议使用functions()而不是macros()因为您不希望本地variables显示在父范围中。
  • CMake使用的很多variables都是通过project()enable_language()调用来设置的。 所以在使用这些命令之前设置一些variables会变得很重要。
  • 环境variables可能与CMake生成make环境以及make文件被使用的地方不同。
    • 环境variables的更改不会重新触发生成过程。
    • 尤其是生成的IDE环境可能与您的命令行不同,因此build议将您的环境variables转换为caching的内容。

有时只有debuggingvariables有帮助。 以下可能会帮助你:

  • 只需使用message()命令即可使用旧的printfdebugging样式。 还有一些随CMake自带的准备使用的模块: CMakePrintHelpers.cmake , CMakePrintSystemInformation.cmake
  • 查看二进制输出目录中的CMakeCache.txt文件。 如果make环境的实际生成失败,甚至会生成该文件。
  • 使用variable_watch()来查看variables的读/写/删除位置。
  • 查看目录属性CACHE_VARIABLES和VARIABLES
  • 调用cmake --trace ...来查看CMake完整的parsing过程。 这是最后一个储备,因为它产生了很多输出。

特殊语法

  • 环境variables
    • 您可以读取$ENV{...}并写入set(ENV{...} ...)环境variables
  • 生成器expression式
    • 生成器expression式$<...>仅在CMake的生成器写入make环境时进行计算(与正常variables相比,parsing器“in-place”replace了正常variables)
    • 在编译器/链接器命令行和多configuration环境中非常方便
  • 参考
    • 使用${${...}}您可以在variables中给variables名称并引用其内容。
    • 在给variables名称作为函数/macros参数时经常使用。
  • 常量值(请参阅if()命令)
    • if(MyVariable)你可以直接检查一个variables是否为true / false(这里不需要包含${...}
    • 如果常数为1ONYESTRUEY或非零数字,则为TRUE
    • 如果常量为0OFFNOFALSENIGNORENOTFOUND ,空string,或者以-NOTFOUND结尾, -NOTFOUND
    • 这个语法通常用于if (MSVC)类的东西,但是对于不知道这个语法快捷方式的人来说,这可能会让人困惑。
  • recursionreplace
    • 您可以使用variables构造variables名称。 CMake替代variables后,会再次检查结果本身是否为variables。 这是CMake本身使用的非常强大的function,例如,作为一种模板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")
      • 所以这里最好的解决scheme是 – 见上面 – 直接检查if (MSVC)
    • 好消息是,在CMake 3.1中引入了策略CMP0054 ,这个问题得到了解决。 我会build议总是将cmake_policy(SET CMP0054 NEW)为“仅if()参数作为variables或关键字解释引号时解释”。
  • option()命令
    • 主要只是caching的string,只能是ONOFF ,他们允许一些特殊的处理,如依赖
    • 但请注意 ,不要将该optionset命令相混淆。 赋予option的值实际上只是“初始值”(在第一个configuration步骤中一次传送到caching),之后将由用户通过CMake的GUI进行更改。

参考

  • CMake如何使用?
  • cmake,在全局variables的概念(和PARENT_SCOPE或add_subdirectory替代)
  • 在一个string列表中循环
  • 如何存储CMake构build设置
  • CMake与STREQUAL的空string比较失败
  • cmake:何时引用variables?

这里有几个基本的例子,开始快速和肮脏。

一个项目variables

设置variables:

 SET(INSTALL_ETC_DIR "etc") 

使用variables:

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

多项variables(即列表)

设置variables:

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

使用variables:

 add_executable(program "${PROGRAM_SRCS}") 

CMake文档的variables

$ENV{FOO}的用法,其中FOO正从环境variables中拾取。 否则用作${FOO} ,其中FOO是其他variables。 对于设置,将在cmake中使用SET(FOO "foo")