When should I quote CMake variables?
I am writing CMake macros for the first time, and I have a hard time understanding how variables work. Most specifically, ${a}
seems to have a different meaning than "${a}"
.
For example here: Passing a list to a CMake macro
I fail to understand when I am supposed to add quotes, and what are the bigger underlying principles.
Two principles of CMake you have to keep in mind:
- CMake is a script language and arguments are evaluated after the variables are expanded
- CMake differentiates between normal strings and list variables (strings with semicolon delimiters)
Examples
-
set(_my_text "A B C")
withmessage("${_my_text}")
would giveA B C
-
set(_my_list A B C)
withmessage("${_my_list}")
would giveA;B;C
-
set(_my_list "A" "B" "C")
withmessage("${_my_list}")
would giveA;B;C
-
set(_my_list "A" "B" "C")
withmessage(${_my_list})
would giveABC
Some Rules of Thumb
There are some rules of thumb you should consider:
-
a) When your variable contains text - especially one that could contain semicolons - you should add quotes.
Reasoning: A semicolon is a delimiter for list elements in CMake. So put quotes around a text that is supposed to be one (it works everywhere and for me personally looks better with CMake syntax highlighting)
EDIT: Thanks for the hint from @schieferstapel
b) To be more precise: A variable content with spaces that already had quotes does keep those quotes (imagine as it getting part of the variable's content). This works everywhere also unquoted (normal or user-defined function parameters) with the prominent exception of
if()
calls, where CMake re-interprets the content of unquoted variables after variable expansion (see also rule of thumb #3 and policy CMP0054: Only interpretif()
arguments as variables or keywords when unquoted)Examples:
-
set(_my_text "A B C")
withmessage(${_my_text})
would also giveA B C
-
set(_my_text "A;B;C")
withif (${_my_text} STREQUAL "A;B;C")
would giveif given arguments: "A" "B" "C" "STREQUAL" "A;B;C" Unknown arguments specified
-
-
If your variable contains a list you normally don't add quotes.
Reasoning: If you give something like a file list to an CMake command it normally expect a list of strings and not one string containing a list. The difference you can see e.g. in the
foreach()
command acceptingITEMS
orLISTS
. -
if()
statements are a special case where you normally don't even put the braces.Reasoning: A string could - after expansion - evaluate again to a variable name. To prevent this it's recommended to just name the variable whose content you want to compare (e.g.
if (_my_text STREQUAL "A B C")
).
COMMAND
Examples
-
set(_my_text "A B C")
withCOMMAND "${CMAKE_COMMAND}" -E echo "${_my_text}"
would- call
cmake.exe -E echo "A B C"
on VS/Windows - call
cmake -E echo A\ B\ C
on GCC/Ubuntu - give
A B C
- call
-
set(_my_text "A B C")
withCOMMAND "${CMAKE_COMMAND}" -E echo "${_my_text}" VERBATIM
would- call
cmake.exe -E echo "A B C"
on VS/Windows - call
cmake -E echo "A B C"
on GCC/Ubuntu - give
A B C
- call
-
set(_my_list A B C)
withCOMMAND "${CMAKE_COMMAND}" -E echo "${_my_list}"
would- call
cmake.exe -E echo A;B;C
- give
A
,B: command not found
,C: command not found
- call
-
set(_my_list A B C)
withCOMMAND "${CMAKE_COMMAND}" -E echo "${_my_list}" VERBATIM
would- call
cmake.exe -E echo "A;B;C"
- give
A;B;C
- call
-
set(_my_list "A" "B" "C")
withCOMMAND "${CMAKE_COMMAND}" -E echo "${_my_list}" VERBATIM
would- call
cmake.exe -E echo "A;B;C"
- give
A;B;C
- call
-
set(_my_list "A" "B" "C")
withCOMMAND "${CMAKE_COMMAND}" -E echo ${_my_list} VERBATIM
would- call
cmake.exe -E echo A B C
- give
A B C
- call
-
set(_my_list "A + B" "=" "C")
withCOMMAND "${CMAKE_COMMAND}" -E echo ${_my_list} VERBATIM
would- call
cmake.exe -E echo "A + B" = C
- give
A + B = C
- call
Some Rules of Thumb with add_custom_target()
/add_custom_command()
/execute_process()
There are some rules of thumb you should consider when you use variables in COMMAND
calls:
-
a) Use quotes for the arguments that contain file paths (like the first argument containing the executable itself).
Reasoning: It could contain spaces and could be reinterpreted as separate arguments to the
COMMAND
callb) See above, works also if the variable
set()
did include quotes. -
Use quotes only if you want to concatenate something into a single parameter to be passed to executable that is called.
Reasoning: A variable could contain a list of parameters which - when using quotes - won't be correctly extracted (semicolons instead of spaces)
-
Always add the
VERBATIM
option withadd_custom_target()
/add_custom_command()
Reasoning: Otherwise the cross-platform behavior is undefined and you could get surprises with your quoted strings.
References
- CMake: difference between ${} and "${}"
- What's the CMake syntax to set and use variables?
- Looping over a string list
- CMake compare to empty string with STREQUAL failed