尝试运行以下命令:
message("foo: ${foo}")
set(foo ${foo} abc)
message("foo: ${foo}")
set(foo ${foo} def)
message("foo: ${foo}")
set(foo ${foo} ghi)
message("foo: ${foo}")
输出将是:
foo:
foo: abc
foo: abc;def
foo: abc;def;ghi
如果您将其更改为在set
调用中引用变量引用,如下所示:
message("foo: ${foo}")
set(foo "${foo}" abc)
message("foo: ${foo}")
set(foo "${foo}" def)
message("foo: ${foo}")
set(foo "${foo}" ghi)
message("foo: ${foo}")
你会看到这个:
foo:
foo: ;abc
foo: ;abc;def
foo: ;abc;def;ghi
这是如何工作的/为什么在CMake的机制方面会发生这种情况?
1条答案
按热度按时间eni9jsuy1#
正如您所看到的,
set(varname ${varname} value)
使用分号作为分隔符(您可以使用分号)向变量追加值。(注意,还有另一种方法可以附加到列表变量:list(APPEND <list> [<element> ...])
)。要理解这是如何工作的/为什么会发生这种情况,您需要了解变量引用如何工作,列表如何工作,
set
命令如何工作,以及命令参数处理如何工作。CMake变量引用
变量引用的文档可以在这里找到。TL;DR是指如果你引用一个不存在的变量而没有传递
--warn-uninitialized
,CMake将把这个引用计算为一个空字符串。这就是为什么上面的第一个message
调用打印“foo:”,以及为什么第一次调用set(foo ${foo} abc)
(在第二个参数中引用foo
)不会出错(只要不使用--warn-uninitialized
)CMake列表
在CMake中,列表只是字符串,其中列表的每个条目由分号字符分隔。分号可以用反斜杠转义。你可以在这里找到完整的文档列表。
CMake中的
set
命令来自the
set
command的CMake文档:指定
<value>...
占位符的此命令签名需要零个或多个参数。多个参数将连接为一个分号分隔的列表,以形成要设置的实际变量值。set
的一个行为是,如果你传递一个看起来像列表的参数,它不会转义列表中的分号。CMake命令参数处理
命令参数的文档可以在这里找到。
引自引用参数部分:
引用的参数内容由左引号和右引号之间的所有文本组成。转义序列和变量引用都被计算。带引号的参数始终作为一个参数提供给命令调用。
来自无引号参数部分:
未加引号的参数内容由连续的允许字符或转义字符块中的所有文本组成。转义序列和变量引用都被计算。结果值的划分方式与List划分为元素的方式相同。每个非空元素都作为参数提供给命令调用。因此,未加引号的参数可以作为零个或多个参数提供给命令调用。
Tying it all Together
当你使用
set(foo ${foo} abc)
(不带引号的参数)时,${foo}
被计算为一个空字符串,因此它不会作为参数传递,但字符串参数abc
不为空,因此它作为第一个值参数传递-因此,foo
被设置为abc
。${foo}
被计算为一个带有一个元素的常规字符串/列表(abc
),该字符串/一个元素作为第一个值参数传递沿着def
作为第二个值参数传递。set
用分号连接两个值参数,结果字符串被分配给foo
。${foo}
被计算为一个包含abc
和def
的列表,这两个值分别作为第一个和第二个值参数传递。然后将ghi
作为第三个参数。set
用分号连接三个值参数,结果字符串被分配给foo
。当您使用
set(foo "${foo}" abc)
(引用参数)时,"${foo}"
被计算为一个空字符串,但请记住“* 一个带引号的参数总是作为一个参数被传递给命令调用。*”,因此空字符串作为第一个参数被传递给set
调用。字符串参数abc
不为空,因此将其作为第二个值参数传递。set
用分号连接两个值参数,结果字符串被分配给foo
。因此,您得到;abc
(第一个值为空字符串的列表)。"${foo}"
被求值为;abc
。这是一个列表,但由于它被指定为一个带引号的参数,因此列表中的每个条目都不是作为单独的参数传递的,;abc
作为第一个值参数传递沿着def
作为第二个值参数传递。两个值参数用分号连接,结果字符串被赋给foo
(;abc;foo
-一个包含三个元素的列表)。