Using get_target_property (or just get_property) with generator expressions

I’ve been trying to obtain either IMPORTED_LOCATION_DEBUG, IMPORTED_LOCATION_RELEASE, IMPORTED_LOCATION_RELWITHDEBINFO, or IMPORTED_LOCATION_MINSIZEREL for a target, depending upon the configuration that I’m currently building, and then strip off the filename to get the directory.

For a single-configuration generator, this works fine:

if (CMAKE_BUILD_TYPE MATCHES "Debug")
    get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_DEBUG)
elseif (CMAKE_BUILD_TYPE MATCHES "Release")
    get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_RELEASE)
elseif (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
    get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_RELWITHDEBINFO)
else()
    get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_MINSIZEREL)
endif()
get_filename_component(PY_LIB_PATH ${PY_LIB_ADDRESS} DIRECTORY)

…but it’s a bit ugly, and won’t work for a multi-configuration generator. I’d like, and expect, to be able to write:

get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_$<$<CONFIG:Debug>:DEBUG>$<$<CONFIG:RelWithDebInfo>:RELWITHDEBINFO>$<$<CONFIG:MinSizeRel>:MINSIZEREL>$<$<CONFIG:Release>:RELEASE>)
get_filename_component(PY_LIB_PATH ${PY_LIB_ADDRESS} DIRECTORY)

…but it fails because because PY_LIB_ADRESS comes back as NOTFOUND. I’ve tried the

get_property(PY_LIB_ADDRESS TARGET python ...)

… form as well, but it’s no better.

I know that get_filename_component works with generator expressions, but does get_target_property not?

If not, how else would I achieve the necessary outcome with a multi-config generator?

Generator expressions are evaluated during generation step so they cannot used directly in CMake commands except when they are evaluated as part of some commands like file(GENERATE...) for example.

However you can simplify your command using the following syntax:

if (CMAKE_BUILD_TYPE)
  get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_${CMAKE_BUILD_TYPE})
else()
  get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION)
endif()

And to finish, as far as I know, get_filename_component() does not support generator expressions.

Sadly that didn’t work for me:

if (CMAKE_BUILD_TYPE)
    get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_${CMAKE_BUILD_TYPE})
else()
    get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION)
endif()
message("PY_LIB_ADDRESS = ${PY_LIB_ADDRESS}")
get_filename_component(PY_LIB_PATH ${PY_LIB_ADDRESS} DIRECTORY)
set_target_properties(${targetName} PROPERTIES BUILD_RPATH ${PY_LIB_PATH})

…leads to the output

PY_LIB_ADDRESS = PY_LIB_ADDRESS-NOTFOUND
CMake Error at <file & line no.> (set_target_properties): 
    set_target_properties called with incorrect number of arguments.

…which is what I’d half-expected, because CMAKE_BUILD_TYPE is a mixed-case string whilst I thought the suffix to IMPORTED_LOCATION needed to be upper-case.

get_filename_component() definitely works with generator expressions. I’ve used this form elsewhere:

get_filename_component(PDB_PATH ${CMAKE_CURRENT_BINARY_DIR}/${targetName}.dir/$<$<CONFIG:Debug>:Debug>$<$<CONFIG:RelWithDebInfo>:RelWithDebInfo>$<$<CONFIG:MinSizeRel>:MinSizeRel>$<$<CONFIG:Release>:Release> ABSOLUTE)
set_target_properties(${targetName} PROPERTIES COMPILE_PDB_NAME             "vc_${CMAKE_VS_PLATFORM_TOOLSET}"
                                                       COMPILE_PDB_OUTPUT_DIRECTORY "${PDB_PATH}")

…and it works exactly as I’d expect it to when using the Visual Studio generator.

Your are right for the example, you have to switch to upper=case first:

if (CMAKE_BUILD_TYPE)
  string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPER_BUILD_TYPE)
  get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION_${UPPER_BUILD_TYPE})
else()
  get_target_property(PY_LIB_ADDRESS python IMPORTED_LOCATION)
endif()

But your interpretation of behavior of get_filename_component() is erroneous. If you look at PDB_PATH contents, you will see that generator expressions are not evaluated. The evaluation occurred during generation when property COMPILE_PDB_OUTPUT_DIRECTORY is evaluated.

Again, I repeat, generator expressions cannot be used during configuration step because they are evaluated during generation step.

Ah, I see. Yes, thank you, that works now.

And I guess it was just dumb luck that what I did before with get_filename_command() worked as intended.

Just one more question, if I may. When I’m setting up my imported Python library, I do so by writing:

set_target_properties(python PROPERTIES IMPORTED_LOCATION_DEBUG          ${PY2_DEBUG_PATH}/lib/libpython2.7.so
                                        IMPORTED_LOCATION_RELEASE        ${PY2_RELEASE_PATH}/lib/libpython2.7.so
                                        IMPORTED_LOCATION_RELWITHDEBINFO ${PY2_RELEASE_PATH}/lib/libpython2.7.so
                                        IMPORTED_LOCATION_MINSIZEREL     ${PY2_RELEASE_PATH}/lib/libpython2.7.so)

…so the unqualified ‘IMPORTED_LOCATION’ property is never defined explicitly. Will the format you suggest still work in that case, with a multi-config generator?

Yes. If specialized property is not defined, the standard one will be used.

Ah, excellent; thank you.