I'm using CMake for generating cross-platform build configuration. And for project to work I need to copy additional precompiled libraries to binaries directory. So, I use such script to build this project:
cmake_minimum_required(VERSION 3.17)
project(foo)
add_executable(bar foobar.cpp)
# Doing some logic to find library files...
list(APPEND DBG_LIBRARIES_LIST "lib1" "lib2")
list(APPEND REL_LIBRARIES_LIST "lib1" "lib2")
# Joining all libraries with " " character to be parsed as different program arguments
list(JOIN DBG_LIBRARIES_LIST " " DEBUG_LIBRARIES)
list(JOIN REL_LIBRARIES_LIST " " RELEASE_LIBRARIES)
add_custom_command(TARGET bar POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<IF:$<CONFIG:Debug>,${DEBUG_LIBRARIES},${RELEASE_LIBRARIES}>
$<TARGET_FILE_DIR:estareng>
)
After I generate such project on Windows using VS16 my post-build events contain such line:
cmake -E copy_if_different "lib1 lib2" some\proj\dir
If I try to join list using $<JOIN:$<IF:$<CONFIG:Debug>,${DEBUG_LIBRARIES_LIST},${RELEASE_LIBRARIES_LIST}>," ">
I just get raw generator expression in post-build events.
NOTE: On GNU/Linux it works fine both using makefiles or Ninja
CodePudding user response:
This ought to work:
cmake_minimum_required(VERSION 3.18)
project(test)
add_executable(bar foobar.cpp)
list(APPEND DBG_LIBRARIES "lib1" "lib2")
list(APPEND REL_LIBRARIES "lib1" "lib2")
add_custom_command(
TARGET bar POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"$<IF:$<CONFIG:Debug>,${DBG_LIBRARIES},${REL_LIBRARIES}>"
"$<TARGET_FILE_DIR:bar>"
COMMAND_EXPAND_LISTS
VERBATIM
)
With VERBATIM
set, each argument to COMMAND
is considered a single command line argument, even if there are spaces or semicolons inside. Without this option, the interpretation of arguments to COMMAND
is platform-specific. As you have noticed, this sometimes means word splitting on spaces while other times it does not.
Lesson 1: You should always use the VERBATIM
option with add_custom_command
.
Yet, do not forget that CMake's own argument splitting runs before this happens. So without double quotes around $<IF:...>
, the semicolons in DBG_LIBRARIES
and REL_LIBRARIES
would split this into many arguments to add_custom_command
, none of which is a complete generator expression. This is why you see "raw" generator expressions on your command lines.
Lesson 2: Always wrap single arguments with variable expansions in double quotes.
Finally, we pass COMMAND_EXPAND_LISTS
so that any argument which contains a ;
after generator expression processing gets split on the ;
into multiple arguments.