CMake compare to empty string with STREQUAL failed
Solution 1:
You ran into a rather annoying "it's not a bug, it's a feature" behavior of CMake. As explained in the documentation of the 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.
Well, the convenience turned out to be an inconvenience. In your example the the string "d"
is treated as a variable named d
by the if
command. If the variable d
happens to be defined to the empty string, the message statement will print "oops...", e.g.:
set (d "")
if("d" STREQUAL "")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
This can give surprising results for statements like
if("${A}" STREQUAL "some string")
because there can be an unintended double expansion of the first argument if the variable A
happens to be defined to a string which is also the name of a CMake variable, e.g.:
set (A "d")
set (d "some string")
if("${A}" STREQUAL "some string")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
Possible work-arounds:
You can add a suffix character to the string after the ${}
expansion which prevents the if statement from doing the automatic evaluation:
set (A "d")
set (d "some string")
if("${A} " STREQUAL "some string ")
message("oops...")
else()
# this branch will be taken
message("fine")
endif()
Do not use ${}
expansion:
set (A "d")
set (d "some string")
if(A STREQUAL "some string")
message("oops...")
else()
# this branch will be taken
message("fine")
endif()
To prevent unintended evaluation on the right side of STREQUAL
use MATCHES
with a CMake regular expression instead:
if(A MATCHES "^value$")
...
endif()
Addendum: CMake 3.1 no longer does double expansions for quoted arguments. See the new policy.
Solution 2:
As of CMake 3.1, there are new rules variable expansions in if()
. They are enabled if you either:
- set
cmake_minimum_required(3.1)
(or higher) at the top of you project file, or - use a lower minimum version number but manually set policy CMP0054 to
NEW
.
Even in that case, it remains true is that the first argument to if
is expanded with the value of a variable matching that name, if it exists:
set (d "")
if(d STREQUAL "")
# this branch will be taken
message("oops...")
else()
message("fine")
endif()
However, this is now disabled if the first argument is quoted:
set (d "")
if("d" STREQUAL "")
message("oops...")
else()
# due to quotes around "d" in if statement,
# this branch will be taken
message("fine")
endif()
If you do want to test a variable's contents against a value, you can either use the classic unquoted syntax, or use the "${d}"
syntax you suggested. Thanks to the new rules, this will never suffer the double-expansion problem mentioned in sakra's answer:
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()