3

Say there is a programme that requires input in form <prog> --param "one;two;three": a flag --param followed by a string (enclosed in quotes) of words separated by a semicolon. For example, the following Bash scrip can be used:

flag=${1}
items=(${2//;/ })

echo "flag: ${flag}"
echo "items: ${items[@]}"

Clearly, the output would be:

flag: --param
items: one two three

The delimiter ; is a must and therefore we need to pass a string enclosed in quotes, otherwise shell will treat things between semicolons as separate commands.

This question is somewhat similar to this one, but the answer proposed there doesn't work for the following.

Consider this CMakeLists.txt file:

cmake_minimum_required(VERSION 3.16.2)
project(clumsy)

add_custom_command(OUTPUT foo.txt
    COMMAND bash ${CMAKE_SOURCE_DIR}/script.sh --param "one;two;three" &>> foo.txt
    WORKING_DIRECTORY ${CMAKE_BUILD_DIR}
    VERBATIM
)
add_custom_target(foo DEPENDS foo.txt)

set(PARAMS "one;two;three")
add_custom_command(OUTPUT bar.txt
    COMMAND bash ${CMAKE_SOURCE_DIR}/script.sh --param "${PARAMS}" &>> bar.txt
    WORKING_DIRECTORY ${CMAKE_BUILD_DIR}
    VERBATIM
)
add_custom_target(bar DEPENDS bar.txt)

Running make foo after configuring works as expected:

[100%] Generating foo.txt
flag: --param
items: one two three  <----
[100%] Built target foo

However make bar results in wrong output because the variable PARAMS was expanded as list and the actual input was --param one two three instead of --param "one;two;three":

[100%] Generating bar.txt
flag: --param
items: one  <----
[100%] Built target bar

How to use variable PARAMS and still make the input for the external command to consist of two arguments?

UPDATE

I have made the code of CMakeLists.txt more complicated with use of output redirects involving & (like &>>) which conflicts with use of VERBATIM flag which is important to make variable quoting work.

scrutari
  • 1,378
  • 2
  • 17
  • 33
  • The only thing that actually makes `foo` works is `VERBATIM` option without which it also falls apart due to list expansion. – scrutari Feb 25 '20 at 22:27
  • So you want `--param "${PARAMS}"`? Och, I understand. You want to pass `;` literally. – KamilCuk Feb 25 '20 at 22:29
  • Yes, I want to pass `;` as is. In other words, cancelled the list expansion. – scrutari Feb 25 '20 at 22:43
  • So quote it. `"${PARAMS}"` – KamilCuk Feb 25 '20 at 22:46
  • No, I have mentioned this in the question. – scrutari Feb 26 '20 at 09:17
  • @scrutari I flagged this as a dup since at the time of my flag, the solution to your problem could be found in the answer you linked in your question (quote the variable). The flag is now invalid since you changed the question, and SO must have automatically made a comment on my behalf. – thomas_f Feb 26 '20 at 09:25

2 Answers2

0

After some bumping my head of why a variable usage is not identical to the literal usage (used to define the variable), I decided to post this answer to give some clarity.

As suggested by @KamilCuk, using quoted variable --params "${PARAMS}" works, but only if VERBATIM option is used as well. In most cases this is not a problem, but with lack of ability CMake support for redirecting output to a file, one cannot use &>> in VERBATIM mode (it gets surrounded by quotes).

So, it appears it is impossible to have redirects and non-expanding variables in add_custom_command, you may only have either of those.

scrutari
  • 1,378
  • 2
  • 17
  • 33
0

I may have misunderstood the question, but have you tried escaping? E.g., --param "one\;two\;three". In case of ${PARAMS} you will have to replace ; with \;.

Pugsley
  • 1,146
  • 14
  • 14