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

时间:2013-11-14 15:58:28

标签: cmake

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

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
  • xcode: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 test

的输出
CMake version: 3.0.2
Quoted test
Surprise!
Unquoted test
Surprise!

3.1.0 test

的输出
CMake version: 3.1.0
Quoted test
OK
Unquoted test
Surprise!

2 个答案:

答案 0 :(得分:32)

你遇到了一个相当恼人的“它不是一个bug,它是CMake的一个特征”行为。正如if command

的文档中所述
 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右侧的意外评估,请使用CMake regular expression代替MATCHES

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

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

答案 1 :(得分:3)

从CMake 3.1开始,有新的规则variable expansions in 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()