Provide a way to handle target based generator expressions in file(GENERATE) and add_custom_command()
I'm trying to implement a 'headersclean' feature, which compiles C++ header files of a shared library with add_custom_command
and extra compile flags, to check that there are no warnings, etc.
The gist of it is the following:
set(target "MySharedLibrary")
set(target_includes_genex "$<TARGET_PROPERTY:${target},INCLUDE_DIRECTORIES>")
set(includes_exist_genex "$<BOOL:${target_includes_genex}>")
set(target_includes_joined_genex
"$<${includes_exist_genex}:-I$<JOIN:${target_includes_genex},;-I>>")
set(target_defines_genex "$<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>")
set(defines_exist_genex "$<BOOL:${target_defines_genex}>")
set(target_defines_joined_genex
"$<${defines_exist_genex}:-D$<JOIN:${target_defines_genex},;-D>>")
set(target_compile_options_genex "$<TARGET_PROPERTY:${target},COMPILE_OPTIONS>")
set(compile_options_exist_genex "$<BOOL:${target_compile_options_genex}>")
set(target_compile_options_joined_genex
"$<${compile_options_exist_genex}:$<JOIN:${target_compile_options_genex},;>>")
set(target_compile_flags_genex "$<TARGET_PROPERTY:${target},COMPILE_FLAGS>")
set(compile_flags_exist_genex "$<BOOL:${target_compile_flags_genex}>")
set(target_compile_flags_joined_genex
"$<${compile_flags_exist_genex}:$<JOIN:${target_compile_flags_genex},;>>")
add_custom_command(
OUTPUT "${artifact_path}"
COMMENT "headersclean: Checking header ${header_path}"
COMMAND
"${CMAKE_CXX_COMPILER}" -c ${CMAKE_CXX_FLAGS}
"${target_compile_options_joined_genex}"
"${target_compile_flags_joined_genex}"
"${target_defines_joined_genex}"
"${target_includes_joined_genex}"
${hcleanFLAGS} # extra -W flags
-xc++ "${header_path}"
-o${artifact_path}
VERBATIM
COMMAND_EXPAND_LISTS)
This works fine for me on Windows and macOS, but breaks down on Linux with the following error:
CMake Error at foo.cmake:71 (target_link_libraries):
Error evaluating generator expression:
$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>
$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets to
specify include directories, compile definitions, and compile options. It
may not be used with the add_custom_command, add_custom_target, or
file(GENERATE) commands.
That's because a library linking against Threads::Threads
brings in $<$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>:SHELL:-Xcompiler -pthread>
as part of the $<TARGET_PROPERTY:${target},COMPILE_OPTIONS>
generator expression.
This is similar to #21074 (closed)
The linked issue works around it by replacing <COMPILE_LANG_AND_ID:CUDA,NVIDIA>
with <COMPILE_LANGUAGE:CUDA>
.
While that helps for that specific case I have a few issues with this approach:
-
It handles only one specific case of a generator expression
-
It requires replacing the easy to use
$<TARGET_PROPERTY:${target},COMPILE_OPTIONS>
consuming generator expression, with 100+ lines of CMake code that recursively processes a target and its dependencies to try and collect all its usage requirements by querying forCOMPILE_OPTIONS
,COMPILE_DEFINITIONS
, etc properties. This is error prone, and might not handle all cases as a generator expression might.
Other problematic genexes are
-
$<TARGET_PROPERTY:foo>
which need to be manually replaced with$<TARGET_PROPERTY:target,foo>
. -
$<BUILD_INTERFACE>
/$<INSTALL_INTERFACE>
$<TARGET_POLICY>
$<TARGET_OBJECTS>
$<LINK_ONLY>
I think we need a better way of handling genexes like that in the described scenario.