Saturday, October 30, 2021

[SOLVED] When should I wrap variables with ${...} in CMake?

Issue

I wonder why often variables in CMake are wrapped with a dollar sign and curly brackets. For example, I saw this call in a CMake tutorial.

include_directories(${PROJECT_BINARY_DIR})

But from what I tried, this does the same thing.

include_directories(PROJECT_BINARY_DIR)

When is the wrapping with ${...} needed and what does it mean? Why are variables often wrapped with this even if it makes no difference?


Solution

Quoting the CMake documentation:

A variable reference has the form ${variable_name} and is evaluated inside a Quoted Argument or an Unquoted Argument. A variable reference is replaced by the value of the variable, or by the empty string if the variable is not set.

In other words, writing PROJECT_BINARY_DIR refers, literally, to the string "PROJECT_BINARY_DIR". Encapsulating it in ${...} gives you the contents of the variable with the name PROJECT_BINARY_DIR.

Consider:

set(FOO "Hello there!")
message(FOO)     # prints FOO
message(${FOO})  # prints Hello there!

As you have probably guessed already, include_directories(PROJECT_BINARY_DIR) simply attempts to add a subdirectory of the name PROJECT_BINARY_DIR to the include directories. On most build systems, if no such directory exists, it will simply ignore the command, which might have tricked you into the impression that it works as expected.

A popular source of confusion comes from the fact that if() does not require explicit dereferencing of variables:

set(FOO TRUE)
if(FOO)
    message("Foo was set!")
endif()

Again the documentation explains this behavior:

if(<constant>)

True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number. False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in the suffix -NOTFOUND. Named boolean constants are case-insensitive. If the argument is not one of these constants, it is treated as a variable.

if(<variable>)

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

In particular, one can come up with weird examples like:

unset(BLA)
set(FOO "BLA")
if(FOO)
    message("if(<variable>): True")
else()
    message("if(<variable>): False")
endif()
if(${FOO})
    message("if(<constant>): True")
else()
    message("if(<constant>): False")
endif()

Which will take the TRUE branch in the variable case, and the FALSE branch in the constant case. This is due to the fact that in the constant case, CMake will go look for a variable BLA to perform the check on (which is not defined, hence we end up in the FALSE branch).



Answered By - ComicSansMS