CMake 与空字符串与 STREQUAL 进行比较失败

问题描述 投票:0回答:2

我一直认为,如果你想比较两个字符串(但不是变量),你需要做的就是像这样引用它:

if("${A}" STREQUAL "some string")

但现在我发现这段代码有时会打印

oops
:

cmake_minimum_required(VERSION 2.8)

if("d" STREQUAL "")
  message("oops...")
endif()

也许这是一个错误(因为它使用 Xcode 打印,但不使用 make)?

或者有一些特殊的变量?

  • cmake:2.8.12、2.8.11.2
  • x 代码:4.6.2、5.0.1

更新

有一个 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
2个回答
65
投票

您遇到了 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 不再对带引号的参数进行双重扩展。请参阅新政策


11
投票

从 CMake 3.1 开始,if()

中有新规则变量扩展。如果您满足以下任一条件,它们就会启用:

即使在这种情况下,

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()
© www.soinside.com 2019 - 2024. All rights reserved.