Request for enhancement: Better handling of IMPORTED_LOCATION_<CONFIG> in generator expressions
I have this codeblock
#
# Make the prebuilt also available in the artifacts directory for things like unit tests.
# If and only if the prebuilt is actually depended on by something in the build.
# We don't want random build artifacts showing up if they're not used.
#
# https://gitlab.kitware.com/cmake/cmake/-/issues/23504
# https://gitlab.kitware.com/cmake/cmake/-/issues/23478
# https://gitlab.kitware.com/cmake/cmake/-/issues/23498
#
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTI_CONFIG)
add_custom_target(populate_${TARGETNAME}
DEPENDS $<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION>>
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION>>
"${CMAKE_BINARY_DIR}/artifacts/$<CONFIG>/")
else()
add_custom_target(populate_${TARGETNAME}
DEPENDS $<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION>>
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION>>
"${CMAKE_BINARY_DIR}/artifacts/")
endif()
add_dependencies(${TARGETNAME} populate_${TARGETNAME})
Which lives inside my wrapper around add_library()
This wrapper is what calls add_library()
and returns prior to IMPORTED_LOCATION or IMPORTED_LOCATION_ being set.
This worked fine when all of my imported libraries were the same for all build configurations.
But I recently added a library which has a Release build, and a Debug build.
And now I'm finding myself needing to change my code block like this:
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTI_CONFIG)
add_custom_target(populate_${TARGETNAME}
DEPENDS $<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>>>
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>>>
"${CMAKE_BINARY_DIR}/artifacts/$<CONFIG>/")
else()
add_custom_target(populate_${TARGETNAME}
DEPENDS $<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:${CMAKE_BUILD_TYPE}>>>
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:${CMAKE_BUILD_TYPE}>>>
"${CMAKE_BINARY_DIR}/artifacts/")
endif()
add_dependencies(${TARGETNAME}populate_${TARGETNAME})
But this doesn't work for libraries that have the same artifact for all build configs. IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>
results in empty string for those libraries, and then I get a build break because copy_if_different
is only being given one argument.
So I can't just keep the current block, and add a new one for $<UPPER_CASE:$<CONFIG>>
, as that still leaves me with the copy_if_different
problem.
But not all of my imported targets have different configurations, and I really do not want to go through everything and fix the hundred or so places where it's used. That's a major pain.
I then noticed https://cmake.org/cmake/help/latest/prop_tgt/LOCATION.html#prop_tgt:LOCATION
Which I thought was the answer to my problem. LOCATION
APPEARED to automatically implement the logic for
if IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>> is defined
return IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>
else
return IMPORTED_LOCATION
But it does not do that. Instead it just complains that I don't have IMPORTED_LOCATION
set on my target, and fails the configuration stage.
So as far as I can tell, my only recourse is to do something like this
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTI_CONFIG)
unset(ULTRA_RIDICULUS_GENEX)
string(APPEND ULTRA_RIDICULUS_GENEX "$<IF:$<STREQUAL:,")
string(APPEND ULTRA_RIDICULUS_GENEX "$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>>>")
string(APPEND ULTRA_RIDICULUS_GENEX ">,") # Close the STRINGEQUAL
string(APPEND ULTRA_RIDICULUS_GENEX "$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION>>")
string(APPEND ULTRA_RIDICULUS_GENEX ",")
string(APPEND ULTRA_RIDICULUS_GENEX "$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>>>")
string(APPEND ULTRA_RIDICULUS_GENEX ">") # Close the IF
add_custom_target(populate_${TARGETNAME}
DEPENDS "${ULTRA_RIDICULUS_GENEX}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${ULTRA_RIDICULUS_GENEX}"
"${CMAKE_BINARY_DIR}/artifacts/$<CONFIG>/")
else()
unset(ULTRA_RIDICULUS_GENEX)
string(APPEND ULTRA_RIDICULUS_GENEX "$<IF:$<STREQUAL:,")
string(APPEND ULTRA_RIDICULUS_GENEX "$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:${CMAKE_BUILD_TYPE}>>>")
string(APPEND ULTRA_RIDICULUS_GENEX ">,") # Close the STRINGEQUAL
string(APPEND ULTRA_RIDICULUS_GENEX "$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION>>")
string(APPEND ULTRA_RIDICULUS_GENEX ",")
string(APPEND ULTRA_RIDICULUS_GENEX "$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:${TARGETNAME},IMPORTED_LOCATION_$<UPPER_CASE:${CMAKE_BUILD_TYPE}>>>")
string(APPEND ULTRA_RIDICULUS_GENEX ">") # Close the IF
add_custom_target(populate_${TARGETNAME}
DEPENDS "${ULTRA_RIDICULUS_GENEX}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${ULTRA_RIDICULUS_GENEX}"
"${CMAKE_BINARY_DIR}/artifacts/")
endif()
add_dependencies(${TARGETNAME} populate_${TARGETNAME})
This is as close to "literally cannot be maintained" as I think I've ever seen cmake code get. I've spent almost the entire day working through various permutations of this to support both libraries with no config specific artifact, and libraries with config specific artifacts.
So I'd like to request one or more of:
- Make querying
IMPORTED_LOCATION_<CONFIG>
case insensitive, or make things complain if name of anIMPORTED_LOCATION_<>
is not upper case, or something (#23831) - Support some generic syntax for "Get me the config dependent property if set, or fall back to the non-config-dependent one" for target properties with
_<CONFIG>
variants. - Some mechanism for a
add_custom_target
call to self-reference it's dependency list, so that I don't have to repeat my generator expression - Some generator expression mechanism for "if multi-config generator,
$<CONFIG>
, else${CMAKE_BUILD_TYPE}
" - Fix the documentation for https://cmake.org/cmake/help/latest/prop_tgt/LOCATION.html#prop_tgt:LOCATION to stop saying it checks
IMPORTED_LOCATION_<CONFIG>
when it doesn't. - Add a generator expression for
$<TARGET_PROPERTY_DEFINED>
so that I don't have to useSTREQUAL
- Fix
$<TARGET_PROPERTY>
so that I don't get this error when called like$<TARGET_GENEX_EVAL:${TARGETNAME},$<TARGET_PROPERTY:IMPORTED_LOCATION>>
, since this defeats one of the purposes of$<TARGET_GENEX_EVAL>
Error evaluating generator expression:
$<TARGET_PROPERTY:IMPORTED_LOCATION_$<UPPER_CASE:$<CONFIG>>>
$<TARGET_PROPERTY:prop> may only be used with binary targets. It may not
be used with add_custom_command or add_custom_target. Specify the target
to read a property from using the $<TARGET_PROPERTY:tgt,prop> signature
instead.