Issue
I'm trying to make a compiler-dependent configuration using CMake generator expressions. One part of this configuration is to set compiler flags with use of list of include directories, similar to what CMake has in their documentation. However for some reason the JOIN
expression does not work properly for me in this case. Here is the simplified example:
cmake_minimum_required(VERSION 3.6)
project(NinjaPlayground)
add_executable(NinjaPlayground main.cpp)
list(APPEND LIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/libraryOne/include")
list(APPEND LIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/librartTwo/include")
target_compile_options(NinjaPlayground PRIVATE
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-isystem $<JOIN:${LIB_INCLUDE_DIRS}, -isystem >>
$<$<CXX_COMPILER_ID:MSVC>:-experimental:external -external:w -external:I $<JOIN:${LIB_INCLUDE_DIRS}, -external:I >>)
If I build this in a UNIX system, the command this configuration produces looks like this:
/usr/bin/c++ -isystem /home/the-dreams-wind/Documents/Ninja/libraryOne/include /home/the-dreams-wind/Documents/Ninja/librartTwo/include -MD -MT CMakeFiles/NinjaPlayground.dir/main.cpp.o -MF CMakeFiles/NinjaPlayground.dir/main.cpp.o.d -o CMakeFiles/NinjaPlayground.dir/main.cpp.o -c ../main.cpp
As you can see the list is expanded, but the join expression ignored it. I also tried to quote the arguments:
target_compile_options(NinjaPlayground PRIVATE
"$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-isystem $<JOIN:${LIB_INCLUDE_DIRS}, -isystem >>"
"$<$<CXX_COMPILER_ID:MSVC>:-experimental:external -external:w -external:I $<JOIN:${LIB_INCLUDE_DIRS}, -external:I >>")
It joins the string correctly, but keeps the quotes in argument list:
/usr/bin/c++ "-isystem /home/the-dreams-wind/Documents/Ninja/libraryOne/include -isystem /home/the-dreams-wind/Documents/Ninja/librartTwo/include" -MD -MT CMakeFiles/NinjaPlayground.dir/main.cpp.o -MF CMakeFiles/NinjaPlayground.dir/main.cpp.o.d -o CMakeFiles/NinjaPlayground.dir/main.cpp.o -c ../main.cpp
The same issues happen with MSVC. Just to be clear, the result i'm expecting should be like this:
/usr/bin/c++ -isystem /home/the-dreams-wind/Documents/Ninja/libraryOne/include -isystem /home/the-dreams-wind/Documents/Ninja/librartTwo/include -MD -MT CMakeFiles/NinjaPlayground.dir/main.cpp.o -MF CMakeFiles/NinjaPlayground.dir/main.cpp.o.d -o CMakeFiles/NinjaPlayground.dir/main.cpp.o -c ../main.cpp
I understand that i can just loop over the list and add the options one by one, however i'd like to learn what I am missing here?
Solution
After playing around with it for some time it turned out that the problem comes down to both spaces and arguments deduplication. Partially it can be handled by removing space between option (e.g. -systemi
) and its argument (path). However some flags just cannot avoid duplication (like -Xclang
option). In order to suppress deduplication the prefix SHELL:
was introduced in CMake 3.12. It keeps spaces, parses arguments properly and can be used like this:
target_compile_options(NinjaPlayground PRIVATE
"$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:SHELL:-isystem$<JOIN:${LIB_INCLUDE_DIRS}, -isystem>>"
"$<$<CXX_COMPILER_ID:MSVC>:SHELL:-experimental:external -external:w -external:I$<JOIN:${LIB_INCLUDE_DIRS}, -external:I>>")
Answered By - The Dreams Wind