在编写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")0
-
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图形用户界面在生成它们之前应用程序。因此,与普通变量相比,它们具有一个
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)
.
缓存是全局的,您可以在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()
- 属性设置了许多CMake使用的变量。
和project()
因此,在使用这些命令之前设置一些变量可能变得非常重要。enable_language()
- 环境变量可能与CMake生成make环境和使用make文件的位置不同。
- 环境变量中的更改不会重新触发生成过程.
- 特别是生成的IDE环境可能与命令行不同,因此建议将环境变量转换为缓存的内容。
有时,只有调试变量才有帮助。以下几点可能对你有帮助:
- 简单地使用旧的
使用printf
命令。还有一些可以使用CMake本身附带的模块:message()
- 调查
文件在二进制输出目录中。如果make环境的实际生成失败,甚至会生成此文件。CMakeCache.txt
- 使用 查看变量的读取/写入/删除位置。
- 查看目录属性 和
- cmake追踪
查看CMake的完整解析过程。这是最后一个储备,因为它产生了大量的产出。cmake --trace ...
特殊语法
- 环境变量
- 你能读懂
写$ENV{...}
环境变量set(ENV{...} ...)
- 你能读懂
- 生成器表达式
- 生成器表达式
只有在CMake的生成器编写make环境时(它与解析器替换为“就地”的普通变量进行比较时,才会对其进行评估。)$<...>
- 非常方便,例如在编译器/链接器命令行和多配置环境中。
- 生成器表达式
- 参考文献
- 带着
您可以在变量中指定变量名并引用其内容。${${...}}
- 常用于将变量名命名为函数/宏参数时使用。
- 带着
- 常量值(见 命令)
- 带着
您可以直接检查变量是否为true/false(这里不需要将其括起来)。if(MyVariable)
${...}
) - 如果常数是
或者一个非零的数字。1
,ON
,YES
,TRUE
,Y
- 如果常量为
,空字符串,或以后缀结尾。0
,OFF
,NO
,FALSE
,N
,IGNORE
,NOTFOUND
-NOTFOUND
. - 此语法通常用于以下内容:
,但对于不知道此语法快捷方式的人来说,这可能会让人感到困惑。if(MSVC)
- 带着
- 递归替换
-
为true,因为它的计算值为if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
if("1" STREQUAL "1")
-
为false,因为它的计算值为if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if("MSVC" STREQUAL "1")
- 所以这里最好的解决方案是直接检查
if(MSVC)
- 可以使用变量构造变量名。在CMake替换了变量之后,它将再次检查结果本身是否是变量。这是CMake本身使用的非常强大的特性,例如作为模板
set(CMAKE_${lang}_COMPILER ...)
- 但
注意
这会让你头疼
命令。下面是一个例子if()
是CMAKE_CXX_COMPILER_ID
和"MSVC"
是MSVC
"1"
: - 好消息是,在CMake 3.1中,随着 ..我建议你总是
“只解释”cmake_policy(SET CMP0054 NEW)
在未引用时,参数作为变量或关键字。“if()
-
- 这个 命令
- 主要是缓存字符串,这些字符串只能
或ON
他们允许一些特殊的处理,如。OFF
- 但
注意
,不要弄错
带着option
命令。给予的价值set
实际上仅仅是“初始值”(在第一个配置步骤中只传输到缓存一次),然后由用户通过option
- 主要是缓存字符串,这些字符串只能