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
For more information, see this article by Craig Scott (one of the maintainers of CMake), which discusses quoting considerations with lists and command arguments, generator expressions, and the if()
command.