我一直认为,如果你想比较两个字符串(但不是变量),你需要做的就是像这样引用它:
if("${A}" STREQUAL "some string")
但现在我发现这段代码有时会打印
oops
:
cmake_minimum_required(VERSION 2.8)
if("d" STREQUAL "")
message("oops...")
endif()
也许这是一个错误(因为它使用 Xcode 打印,但不使用 make)?
或者有一些特殊的变量?
更新
有一个 string 命令没有描述的问题:
string(COMPARE EQUAL "${A}" "" result)
if(result)
message("...")
endif()
更新2
我所期望的行为自 CMake 3.1.0 以来已实现(请参阅 CMP0054)。
3.0.2 测试的输出:
CMake version: 3.0.2
Quoted test
Surprise!
Unquoted test
Surprise!
3.1.0 测试的输出:
CMake version: 3.1.0
Quoted test
OK
Unquoted test
Surprise!
您遇到了 CMake 相当烦人的“这不是错误,这是功能”行为。正如 if 命令的文档中所述:
The if command was written very early in CMake's history, predating the ${}
variable evaluation syntax, and for convenience evaluates variables named
by its arguments as shown in the above signatures.
好吧,便利却带来了不便。在您的示例中,字符串
"d"
被 d
命令视为名为 if
的变量。如果变量d
恰好被定义为空字符串,则消息语句将打印“oops...”,例如:
set (d "")
if("d" STREQUAL "")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
对于像这样的语句,这可能会给出令人惊讶的结果
if("${A}" STREQUAL "some string")
因为如果变量
A
碰巧定义为一个字符串,该字符串也是 CMake 变量的名称,则可能会出现第一个参数的意外双重扩展,例如:
set (A "d")
set (d "some string")
if("${A}" STREQUAL "some string")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
您可以在
${}
扩展之后向字符串添加后缀字符,这会阻止 if 语句进行自动评估:
set (A "d")
set (d "some string")
if("${A} " STREQUAL "some string ")
message("oops...")
else()
# this branch will be taken
message("fine")
endif()
不要使用
${}
扩展:
set (A "d")
set (d "some string")
if(A STREQUAL "some string")
message("oops...")
else()
# this branch will be taken
message("fine")
endif()
为了防止在
STREQUAL
右侧进行意外求值,请使用 MATCHES
和 CMake 正则表达式 代替:
if(A MATCHES "^value$")
...
endif()
附录:CMake 3.1 不再对带引号的参数进行双重扩展。请参阅新政策。
从 CMake 3.1 开始,if()
cmake_minimum_required(3.1)
(或更高),或者NEW
。即使在这种情况下,
if
的第一个参数仍然会被扩展为与该名称匹配的变量的值(如果存在):
set (d "")
if(d STREQUAL "")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
但是,如果第一个参数被引用,现在将被禁用:
set (d "")
if("d" STREQUAL "")
message("oops...")
else()
# due to quotes around "d" in if statement,
# this branch will be taken
message("fine")
endif()
如果您确实想要根据值测试变量的内容,您可以使用经典的不带引号的语法,或使用您建议的
"${d}"
语法。感谢新规则,这永远不会遇到 sakra 答案中提到的双展开问题:
set (A "d")
set (d "some string")
if("${A}" STREQUAL "d")
# this branch will be taken
message("fine")
elseif("${A}" STREQUAL "some string")
message("oops...")
else()
message("??")
endif()