1

My hello.txt

cmake_policy(SET CMP0054 NEW)

set(VAR ON)

# VAR will be treated as a string
if("VAR")
  message(TRUE)
else()
  message(FALSE)
endif()

# output prints FALSE

From policy CMP0054:

To prevent ambiguity, potential variable or keyword names can be specified in a Quoted Argument or a Bracket Argument. A quoted or bracketed variable or keyword will be interpreted as a string and not dereferenced or interpreted. See policy CMP0054.

CMake documentation doesn't mention if(<string>):

if(<variable|string>)

True if given a variable that is defined to a value that is not a false constant. False otherwise. (Note macro arguments are not variables.)

Why does a non-empty string evaluate as FALSE?

Community
  • 1
  • 1
Izana
  • 1,253
  • 15
  • 18
  • it seems like, in the documentation, `False otherwise` gives the answer? The behavior of treating the non-empty string as false is weird. – Izana Dec 21 '19 at 08:07

1 Answers1

1

You are looking in the right place in the documentation:

if(<variable|string>)

True if given a variable that is defined to a value that is not a false constant. False otherwise. (Note macro arguments are not variables.)

However, the documentation is not complete, as the <string> case is not explicitly mentioned. The behavior for CMake if-statements simply containing a string is different than what is described here for variables. The documentation for strings should read:

True if given a string that does matches a true constant (1, ON, YES, TRUE, Y, or a non-zero number). False otherwise.

In other words, any string that doesn't match one of these CMake true constants will evaluate to False. As you already pointed out, the string "VAR":

if ("VAR")
  message(TRUE)
else()
  message(FALSE)
endif()

prints FALSE. However, a true constant as a string (let's say "y"):

if ("y")
  message(TRUE)
else()
  message(FALSE)
endif()

prints TRUE.

This logic is verifiable in the CMake source code in a function called GetBooleanValue():

bool cmConditionEvaluator::GetBooleanValue(
  cmExpandedCommandArgument& arg) const
{
  // Check basic constants.
  if (arg == "0") {
    return false;
  }
  if (arg == "1") {
    return true;
  }

  // Check named constants.
  if (cmIsOn(arg.GetValue())) {
    return true;
  }
  if (cmIsOff(arg.GetValue())) {
    return false;
  }

  // Check for numbers.
  if (!arg.empty()) {
    char* end;
    double d = strtod(arg.c_str(), &end);
    if (*end == '\0') {
      // The whole string is a number.  Use C conversion to bool.
      return static_cast<bool>(d);
    }
  }

  // Check definition.
  const char* def = this->GetDefinitionIfUnquoted(arg);
  return !cmIsOff(def);
}
Community
  • 1
  • 1
squareskittles
  • 11,997
  • 7
  • 31
  • 48
  • 1
    Thanks a lot! The source code is crystal clear! I also have a post on my blog https://guihao-liang.github.io/2020-01-04-cmake-101/, where I didn't read the source code but had a guess for the internal logic to evaluate conditions. I will update mine with your answer! – Izana Jan 05 '20 at 18:52