44

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

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

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

cmake_minimum_required(VERSION 2.8)

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

可能是错误(因为它用Xcode打印,但不是用make打印)?还是有一些特殊的变量?

  • 制作:2.8.12、2.8.11.2
  • xcode:4.6.2、5.0.1

更新

有没有描述问题的命令字符串:

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!
4

2 回答 2

56

您遇到了 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"被视为dif命令命名的变量。如果变量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使用CMake 正则表达式MATCHES进行意外评估:

if(A MATCHES "^value$")
  ...
endif()

附录:CMake 3.1 不再对引用的参数进行双重扩展。请参阅新政策

于 2013-11-14T21:33:37.670 回答
9

从 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()
于 2020-08-21T17:24:58.600 回答