与STREQUAL失败的CMake比较空字符串

26 cmake

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

if("${A}" STREQUAL "some string")
Run Code Online (Sandbox Code Playgroud)

但现在我发现这段代码有时打印出来oops:

cmake_minimum_required(VERSION 2.8)

if("d" STREQUAL "")
  message("oops...")
endif()
Run Code Online (Sandbox Code Playgroud)

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

  • cmake:2.8.12,2.8.11.2
  • xcode:4.6.2,5.0.1

更新

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

string(COMPARE EQUAL "${A}" "" result)
if(result)
  message("...")
endif()
Run Code Online (Sandbox Code Playgroud)

更新2

我期望从CMake 3.1.0开始实现的行为(参见CMP0054).

3.0.2 测试的输出:

CMake version: 3.0.2
Quoted test
Surprise!
Unquoted test
Surprise!
Run Code Online (Sandbox Code Playgroud)

3.1.0 测试的输出:

CMake version: 3.1.0
Quoted test
OK
Unquoted test
Surprise!
Run Code Online (Sandbox Code Playgroud)

sak*_*kra 34

你遇到了一个相当恼人的"它不是一个bug,它是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.
Run Code Online (Sandbox Code Playgroud)

嗯,便利性变得不方便.在您的示例字符串"d"被视为命名变量dif命令.如果变量d恰好被定义为空字符串,则消息语句将打印"oops ...",例如:

set (d "")
if("d" STREQUAL "")
  # this branch will be taken
  message("oops...")
else()
  message("fine")
endif()
Run Code Online (Sandbox Code Playgroud)

这可以给像这样的陈述带来惊人的结果

if("${A}" STREQUAL "some string")
Run Code Online (Sandbox Code Playgroud)

因为如果变量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()
Run Code Online (Sandbox Code Playgroud)

可能的解决方法:

您可以在${}扩展后向字符串添加后缀字符,这会阻止if语句执行自动评估:

set (A "d")
set (d "some string")
if("${A} " STREQUAL "some string ")
  message("oops...")
else()
  # this branch will be taken
  message("fine")
endif()
Run Code Online (Sandbox Code Playgroud)

不要使用${}扩展:

set (A "d")
set (d "some string")
if(A STREQUAL "some string")
  message("oops...")
else()
  # this branch will be taken
  message("fine")
endif()
Run Code Online (Sandbox Code Playgroud)

为了防止在右侧无意评价STREQUAL使用MATCHESCMake的正则表达式来代替:

if(A MATCHES "^value$")
  ...
endif()
Run Code Online (Sandbox Code Playgroud)

附录:CMake 3.1不再对引用的参数进行双重扩展.查看新政策.

  • @Fraser`因为你无法访问名称中包含空格的变量,为什么不呢?示例:`set("A""some string")set(VAR_NAME"A")消息($ {$ {VAR_NAME}})`输出:`some string`.我知道这个例子看起来是合成的,但如果你经常使用包装器,你可以在实际代码中遇到这种情况.这样的事情:`set(VAR_NAME"$ {FIRST} $ {SECOND}")`如果`SECOND`为空,你将得到`VAR_NAME`以空格结尾. (2认同)

Art*_*cca 7

随着CMake的3.1的,也有新的规则变量扩展if()。如果您:

即使在这种情况下,第一个参数仍然是正确的if,如果它存在,则使用与该名称匹配的变量的值进行扩展:

set (d "")
if(d STREQUAL "")
  # this branch will be taken
  message("oops...")
else()
  message("fine")
endif()
Run Code Online (Sandbox Code Playgroud)

但是,如果引用第一个参数,则现在禁用此功能:

set (d "")
if("d" STREQUAL "")
  message("oops...")
else()
  # due to quotes around "d" in if statement,
  # this branch will be taken
  message("fine")
endif()
Run Code Online (Sandbox Code Playgroud)

如果您确实想针对某个值测试变量的内容,您可以使用经典的不带引号的语法,或者使用"${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()
Run Code Online (Sandbox Code Playgroud)