When using a CMake list to specify multiple arguments to a function, empty arguments are not passed as arguments to the list. In some cases an empty string is needed as an argument. Is there a way to achieve this?
If I run this
set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDOUT)
set(LIST_VAR
"ONE"
"TWO"
""
"FOUR"
)
execute_process(
COMMAND
${CMAKE_COMMAND} -E echo
${LIST_VAR}
)
execute_process(
COMMAND
${CMAKE_COMMAND} -E echo
"ONE"
"TWO"
""
"FOUR"
)
with
cmake -P test.cmake
I get:
'cmake' '-E' 'echo' 'ONE' 'TWO' 'FOUR'
ONE TWO FOUR
'cmake' '-E' 'echo' 'ONE' 'TWO' '' 'FOUR'
ONE TWO FOUR
In the first variant the third, empty argument is swallowed, which is really annoying if there are cases, where an empty argument may be possible/needed and arguments are prepared as CMake lists.
In my application I need to call a script and not cmake -E echo
, which expects empty arguments in certain situations.
Also, the empty entries are - of course - not put in literally as in this simplified example. Instead of ""
I have something like "${MIGHT_BE_EMPTY}"
.
Is there a way to safely transport empty strings as list entries to function arguments?
If not, is there a good work-around for this problem?
E.g. transform unset variables to something like a space (" "
) which might be equivalent to an empty argument for the called script?
The problem is that there are no lists in CMake, only strings. And there is a convention that some CMake commands understand: if there are unquoted ;
(semicolon) in the variable then it is a list, where each element of the list separated from another by ;
. And there is a rule which allows to create lists out of multiple strings separated by a whitespace.
So this command:
set(LIST_VAR
"ONE"
"TWO"
""
"FOUR"
)
Creates a variable with the following content: ONE;TWO;;FOUR
which is a list in the CMake book. Now there is another rule (reverse) which CMake uses when it expands unquoted variables:
Each non-empty element is given to the command invocation as an argument.
So you can't use empty elements and have CMake lists propagate them across the script. You can use something non-empty like an empty element, though. For example, you could use the space character to mean the element is empty if it wouldn't mess with your other data:
set(EMPTY " ")
set(LIST_VAR
"ONE"
"TWO"
"${EMPTY}"
"FOUR"
)
And if you can't modify the list with explicitly providing an ${EMPTY}
element you can add it to the existing list like this: string(REPLACE ";;" ";${EMPTY};" LIST_VAR "${LIST_VAR}")
.
Finally, if you need to modify not a list but some particular, potentially empty variable then you can use the following:
if(MIGHT_BE_EMPTY STREQUAL "")
set(MIGHT_BE_EMPTY "${EMPTY}")
endif()
I use the EMPTY
variable because it is convenient, you can drop it and replace with whatever symbol you like—it won't make a difference.