3

I want to execute a CMake command in a subdirectory with execute_process, and also pass some cache variables as -D options.

If the variable is of type string, it works. However, if the variable is a list, the typical method of passing a list in command line does not seem to work.

I tried all of the combinations listed in that answer. I even tried to join mylist with "\\;" or "\\\\;". However, the execute_process seems to always unpack the '-DVal2=a\\;b\\;c\\;' or '-DVal2=a;b;c' to -Dval2=a b c.

How can I prevent this? Only -DVal2=a\\;b\\;c works, but it's very annoying.

set(
    mylist
    a
    b
    c
)

set(
    cmake_args
    "-DVal1=abc"
    "'-DVal2=${mylist}'" #does not work, the execute_process will unpack it into seperated args
)

execute_process(
        COMMAND ${CMAKE_COMMAND} ${cmake_args} ${CMAKE_SOURCE_DIR}/subproject
        OUTPUT_FILE ${CMAKE_BINARY_DIR}/config.log
        ERROR_FILE ${CMAKE_BINARY_DIR}/config.log
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/subproject
        RESULT_VARIABLE config_result
)
compor
  • 2,239
  • 1
  • 19
  • 29
Wang
  • 7,250
  • 4
  • 35
  • 66

2 Answers2

3

Before passing the list, run this line on it:

string(REPLACE ";" "\\;" escaped_list "${my_list}")

and then pass escaped_list. On the other end, it will have the exact same value as my_list.

For example,

set(my_list "a\;b" "c" "d")
string(REPLACE ";" "\\;" escaped_list "${my_list}")
execute_process(COMMAND ${CMAKE_COMMAND} -Dmy_list=${escaped_list} -P test.cmake)

(Tested with cmake 3.17).

This also works when assigning first to cmake_args and passing that. For example,

test1.cmake

# Construction.
set(my_list "a\;b" "c" "d")
set(other_list "e" "f\;g" "h")

# For debugging purposes.
message("my_list = \"${my_list}\".")
foreach(arg ${my_list})
  message("-> ${arg}")
endforeach()
message("other_list = \"${other_list}\".")
foreach(arg ${other_list})
  message("-> ${arg}")
endforeach()

# Encoding.
string(REPLACE ";" "\\;" escaped_list "${my_list}")
message("escaped_list = \"${escaped_list}\".")

string(REPLACE ";" "\\;" other_escaped_list "${other_list}")
message("other_escaped_list = \"${other_escaped_list}\".")

set(cmake_args "-Dother_list=${other_escaped_list}" "-Dmy_list=${escaped_list}")

execute_process(
  COMMAND
  ${CMAKE_COMMAND} ${cmake_args} -P test2.cmake
)

test2.cmake

# For debugging purpose.
message("my_list = \"${my_list}\".")
foreach(arg ${my_list})
  message("-> ${arg}")
endforeach()

message("other_list = \"${other_list}\".")
foreach(arg ${other_list})
  message("-> ${arg}")
endforeach()

Output of running cmake -P test1.cmake :

my_list = "a\;b;c;d".
-> a;b
-> c
-> d
other_list = "e;f\;g;h".
-> e
-> f;g
-> h
escaped_list = "a\\;b\;c\;d".
other_escaped_list = "e\;f\\;g\;h".
my_list = "a\;b;c;d".
-> a;b
-> c
-> d
other_list = "e;f\;g;h".
-> e
-> f;g
-> h

Please observe closely where double quotes were and weren't used.

Carlo Wood
  • 5,648
  • 2
  • 35
  • 47
1

I think you need to escape the ; character which is the default separator for lists in CMake, but it's not clear how you do it so that it doesn't work for you.

So, try something like this

set(mylist_str "")

foreach(item ${mylist})
  string(APPEND mylist_str ${item} "\;")
endforeach()

# this is for debugging
message(STATUS "List as string: ${mylist_str}")

set(cmake_args 
    "-DVal1=abc"
    "-DVal2=${mylist_str}"
    "-DVal3=\"${mylist_str}\"" # this has quotes around it
)

# this is for debugging
foreach(item ${cmake_args})
  message(STATUS "A list item: ${item}")
endforeach()
compor
  • 2,239
  • 1
  • 19
  • 29