75

With the following cmake scrpt:

get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
    message(STATUS "${_variableName}=${${_variableName}}")
endforeach()

We can print all the variables in the CMake project. Then my question is: is there a function that can print all the properties of a target?

feelfree
  • 11,175
  • 20
  • 96
  • 167
  • This doesn't "print all the variables in the CMake project." It prints the one `_variable` passed to it. You can trivially do something similar for properties using `get_property`. Is that what you're asking about? – Angew is no longer proud of SO Aug 24 '15 at 13:53
  • @Angew Thanks, and I have rewritten the question. – feelfree Aug 24 '15 at 14:00
  • @m.s. Thanks, and it is exactly what I am looking for, and I will have an investigation. – feelfree Aug 25 '15 at 07:13
  • @m.s. I met a problem when running the script: http://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i – feelfree Aug 25 '15 at 07:14

4 Answers4

84

I have had limited success with the following work-around to the apparent lack of ability to dynamically query for the properties of a target.

I invoke the cmake command to list all properties, and then try each one on the target.

# Get all propreties that cmake supports
if(NOT CMAKE_PROPERTY_LIST)
    execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
    
    # Convert command output into a CMake list
    string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
    string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
    list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)
endif()
    
function(print_properties)
    message("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction()
    
function(print_target_properties target)
    if(NOT TARGET ${target})
      message(STATUS "There is no target named '${target}'")
      return()
    endif()

    foreach(property ${CMAKE_PROPERTY_LIST})
        string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" property ${property})

        # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
        if(property STREQUAL "LOCATION" OR property MATCHES "^LOCATION_" OR property MATCHES "_LOCATION$")
            continue()
        endif()

        get_property(was_set TARGET ${target} PROPERTY ${property} SET)
        if(was_set)
            get_target_property(value ${target} ${property})
            message("${target} ${property} = ${value}")
        endif()
    endforeach()
endfunction()
Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
AlwaysTraining
  • 2,039
  • 18
  • 12
  • yes, this is useful. Had to add a " when CMAKE_PROPERTY_LIST was used, and CMAKE_BUILD_TYPE has to be configured manually. – Lothar Jun 04 '16 at 11:18
  • The LOCATION property may not be read from target "PandemicCore". Use the target name directly with add_custom_command, or use the generator expression $, as appropriate. https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i – Gelldur Jun 09 '17 at 06:29
  • Fixed with: `# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i IF(prop STREQUAL "LOCATION" OR prop MATCHES "^LOCATION_" OR prop MATCHES "_LOCATION$") CONTINUE() ENDIF()` – Gelldur Jun 09 '17 at 06:43
  • Thought this answer was a great approach, but noticed it raised a lot of errors for INTERFACE_LIBRARY targets. I tweaked it to be able to handle this, as well as a few other small improvements (ie, removed duplicates, and pulled Dawid Drozd's fix outside of the main loop); wanted to submit as an edit, but it was rejected ("edit was intended to address the author of the post and makes no sense as an edit", which doesn't make sense to me... maybe there were just too many changes??), so I have an alternative "answer" below: https://stackoverflow.com/a/51987470/920545 – Paul Molodowitch Aug 24 '18 at 14:12
  • 1
    `LOCATION` property may be read without errors now (tested in cmake 3.14). – val - disappointed in SE Mar 09 '19 at 10:14
  • 1
    Does not work on INTERFACE library targets: `CMake Error at ring/common/CMakeLists.txt:49 (get_property): INTERFACE_LIBRARY targets may only have whitelisted properties. The property "PARENT_DIRECTORY" is not allowed. Call Stack (most recent call first): ring/common/CMakeLists.txt:57 (print_target_properties)` – kyb May 29 '19 at 14:40
  • To fix this use `get_target_properties()` instead of `get_property()`. With `get_target_properties()` you don't need `if(prop STREQUAL "LOCATION"...` – kyb Jun 07 '19 at 11:18
  • 4
    I also suggest to `list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)` – kyb Jun 07 '19 at 11:18
  • I appreciate all of the new answers, when I posted originally I just got enough info for debugging and moved on and didn't solve all the problems that you guys are mentioning, that is why my answer said "limited success with the following..." doing things besides printing the errors on special case properties should probably go into new answers as you guys are doing. – AlwaysTraining Jul 22 '19 at 13:10
20

This isn't really intended to be a competing answer, just an expansion on the answer by AlwaysTraining (and edited by Dawid Drozd)!

If the target is an INTERFACE_LIBRARY, this will print out a whole bunch of errors about whitelisted properties. Unfortunately, there doesn't seem to be a good way to dynamically query what the whitelisted properties are - by checking the source for cmTargetPropertyComputer::WhiteListedInterfaceProperty I was able to come up with a regexp, but it could change from version to version (I was looking at the source for cmake-3.11). Anyway, here's my revised version, with support for INTERFACE_LIBRARYtargets - I also removes duplicate, and made the filtering of LOCATION properties happen once, outside of the loop.

(I've made a suggested edit to the original question, but this is here in case it isn't accepted...)

Thanks AlwaysTraining and Dawid Drozd!

# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)

# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
# Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$")
# For some reason, "TYPE" shows up twice - others might too?
list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)

# build whitelist by filtering down from CMAKE_PROPERTY_LIST in case cmake is
# a different version, and one of our hardcoded whitelisted properties
# doesn't exist!
unset(CMAKE_WHITELISTED_PROPERTY_LIST)
foreach(prop ${CMAKE_PROPERTY_LIST})
    if(prop MATCHES "^(INTERFACE|[_a-z]|IMPORTED_LIBNAME_|MAP_IMPORTED_CONFIG_)|^(COMPATIBLE_INTERFACE_(BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|EXPORT_NAME|IMPORTED(_GLOBAL|_CONFIGURATIONS|_LIBNAME)?|NAME|TYPE|NO_SYSTEM_FROM_IMPORTED)$")
        list(APPEND CMAKE_WHITELISTED_PROPERTY_LIST ${prop})
    endif()
endforeach(prop)

function(print_properties)
    message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction(print_properties)

function(print_whitelisted_properties)
    message ("CMAKE_WHITELISTED_PROPERTY_LIST = ${CMAKE_WHITELISTED_PROPERTY_LIST}")
endfunction(print_whitelisted_properties)

function(print_target_properties tgt)
    if(NOT TARGET ${tgt})
      message("There is no target named '${tgt}'")
      return()
    endif()

    get_target_property(target_type ${tgt} TYPE)
    if(target_type STREQUAL "INTERFACE_LIBRARY")
        set(PROP_LIST ${CMAKE_WHITELISTED_PROPERTY_LIST})
    else()
        set(PROP_LIST ${CMAKE_PROPERTY_LIST})
    endif()

    foreach (prop ${PROP_LIST})
        string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
        # message ("Checking ${prop}")
        get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
        if (propval)
            get_target_property(propval ${tgt} ${prop})
            message ("${tgt} ${prop} = ${propval}")
        endif()
    endforeach(prop)
endfunction(print_target_properties)
mr NAE
  • 3,144
  • 1
  • 15
  • 35
Paul Molodowitch
  • 1,366
  • 3
  • 12
  • 29
  • This expects OpenGL::GLU to be a valid target. Was this intended? – Torsten Robitzki Nov 22 '18 at 12:05
  • Both of these scripts failed to print `LOCATION`, the main property of a library target. And it was the one I was looking for :( – Dženan May 20 '19 at 15:33
  • I'm not sure what you mean by "expects OpenGL::GLU" to be a valid target. I don't see a reference to OpenGL (or GLU) anywhere. Did you mean that if you call `print_target_properties(OpenGL::GLU)` that `OpenGL::GLU` needs to be a target? – Paul Molodowitch Jun 04 '19 at 22:55
  • 1
    These scripts are for older versions of cmake (< 3.14), where it would error if you tried to read the LOCATION property. Apparently this works in new versions. You can simply remove the line filtering the location: `list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$")` – Paul Molodowitch Jun 04 '19 at 22:57
13

Improved @AlwaysTraining's and @PaulMolodowitch's answers.
Solves problem with INTERFACE_LIBRARY described in kyb's comment

Functionally my implementation does at most the same as @PaulMolodowitch's implementation. But it is more laconic.

## https://stackoverflow.com/questions/32183975/how-to-print-all-the-properties-of-a-target-in-cmake/56738858#56738858
## https://stackoverflow.com/a/56738858/3743145

## Get all properties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)
## Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")

list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)

function(print_target_properties tgt)
    if(NOT TARGET ${tgt})
      message("There is no target named '${tgt}'")
      return()
    endif()

    foreach (prop ${CMAKE_PROPERTY_LIST})
        string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
        get_target_property(propval ${tgt} ${prop})
        if (propval)
            message ("${tgt} ${prop} = ${propval}")
        endif()
    endforeach(prop)
endfunction(print_target_properties)
kyb
  • 7,233
  • 5
  • 52
  • 105
  • As soon as I use `pkg_check_modules()` somewhere I get a billion errors like this one: ``` CMake Error at cwm4/cmake/print_target_properties.cmake:19 (get_target_property): INTERFACE_LIBRARY targets may only have whitelisted properties. The property "INSTALL_RPATH" is not allowed. ``` – Carlo Wood Jan 27 '20 at 17:46
  • This generally works, but for MSVC builds, usually, ${CMAKE_BUILD_TYPE} is not set to a single "DEBUG" or "RELEASE" value (since the solutions are usually generated for multiple build configurations at the same time). I just manually used "DEBUG" instead of "${CMAKE_BUILD_TYPE}". – Greg Kramida Jun 26 '20 at 15:14
  • 2
    @kyb Is there a reason to call `get_target_property()` a second time in the `if()`? – Tilman Vogel Feb 15 '21 at 17:49
2

This version uses ideas from David Drozd's and Paul Molodowitch's answers but includes processing for <CONFIG> and <LANG> text returned by cmake --help-property-list:

# Sets the AVAILABLE_CONFIGURATION_TYPES variable to the default available configurations
# (For some reason, CMAKE_CONFIGURATION_TYPES tends to be empty)
function(get_available_configuration_types)
    # Get all variables that cmake cache defines by default
    execute_process(COMMAND cmake -LAH -N OUTPUT_VARIABLE CMAKE_CACHE_VARIABLE_LIST)

    # Convert command output into a CMake list
    string(REGEX REPLACE ";" "[:semicolon:]" CMAKE_CACHE_VARIABLE_LIST "${CMAKE_CACHE_VARIABLE_LIST}")
    string(REGEX REPLACE "\n" ";" CMAKE_CACHE_VARIABLE_LIST "${CMAKE_CACHE_VARIABLE_LIST}")

    # filter down to the variables
    list(FILTER CMAKE_CACHE_VARIABLE_LIST EXCLUDE REGEX "^$|^//.*$|^\-\-$")

    # Get the configuration types
    set(AVAILABLE_CONFIGURATION_TYPES ${CMAKE_CACHE_VARIABLE_LIST})
    list(FILTER AVAILABLE_CONFIGURATION_TYPES INCLUDE REGEX "^CMAKE_CONFIGURATION_TYPES")
    list(GET AVAILABLE_CONFIGURATION_TYPES 0 AVAILABLE_CONFIGURATION_TYPES)
    string(REGEX REPLACE ".*=" "" AVAILABLE_CONFIGURATION_TYPES "${AVAILABLE_CONFIGURATION_TYPES}")
    string(REPLACE "[:semicolon:]" ";" AVAILABLE_CONFIGURATION_TYPES "${AVAILABLE_CONFIGURATION_TYPES}")
    string(TOUPPER "${AVAILABLE_CONFIGURATION_TYPES}" AVAILABLE_CONFIGURATION_TYPES)

    # Add the current build type if it isn't already there
    string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
    list(FILTER AVAILABLE_CONFIGURATION_TYPES EXCLUDE REGEX ${BUILD_TYPE})
    list(APPEND AVAILABLE_CONFIGURATION_TYPES ${BUILD_TYPE})
    list(SORT AVAILABLE_CONFIGURATION_TYPES)

    # make AVAILABLE_CONFIGURATION_TYPES available to parent
    set(AVAILABLE_CONFIGURATION_TYPES ${AVAILABLE_CONFIGURATION_TYPES} PARENT_SCOPE)
endfunction()

# Sets the CMAKE_PROPERTY_LIST and CMAKE_WHITELISTED_PROPERTY_LIST variables to
# the list of properties
function(get_cmake_property_list)
    # See https://stackoverflow.com/a/44477728/240845
    set(LANGS ASM-ATT ASM ASM_MASM ASM_NASM C CSHARP CUDA CXX FORTRAN HIP ISPC JAVA OBJC OBJCXX RC SWIFT)
    get_available_configuration_types()

    # Get all propreties that cmake supports
    execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)

    # Convert command output into a CMake list
    string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
    string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")

    # Populate "<CONFIG>" with AVAILBLE_CONFIG_TYPES
    set(CONFIG_LINES ${CMAKE_PROPERTY_LIST})
    list(FILTER CONFIG_LINES INCLUDE REGEX "<CONFIG>")
    list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "<CONFIG>")
    foreach(CONFIG_LINE IN LISTS CONFIG_LINES)
        foreach(CONFIG_VALUE IN LISTS AVAILABLE_CONFIGURATION_TYPES)
            string(REPLACE "<CONFIG>" "${CONFIG_VALUE}" FIXED "${CONFIG_LINE}")
            list(APPEND CMAKE_PROPERTY_LIST ${FIXED})
        endforeach()
    endforeach()

    # Populate "<LANG>" with LANGS
    set(LANG_LINES ${CMAKE_PROPERTY_LIST})
    list(FILTER LANG_LINES INCLUDE REGEX "<LANG>")
    list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "<LANG>")
    foreach(LANG_LINE IN LISTS LANG_LINES)
        foreach(LANG IN LISTS LANGS)
            string(REPLACE "<LANG>" "${LANG}" FIXED "${LANG_LINE}")
            list(APPEND CMAKE_PROPERTY_LIST ${FIXED})
        endforeach()
    endforeach()

    # no repeats
    list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST)

    # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
    list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$")

    list(SORT CMAKE_PROPERTY_LIST)

    # Whitelisted property list for use with interface libraries to reduce warnings
    set(CMAKE_WHITELISTED_PROPERTY_LIST ${CMAKE_PROPERTY_LIST})

    # regex from https://stackoverflow.com/a/51987470/240845
    list(FILTER CMAKE_WHITELISTED_PROPERTY_LIST INCLUDE REGEX "^(INTERFACE|[_a-z]|IMPORTED_LIBNAME_|MAP_IMPORTED_CONFIG_)|^(COMPATIBLE_INTERFACE_(BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|EXPORT_NAME|IMPORTED(_GLOBAL|_CONFIGURATIONS|_LIBNAME)?|NAME|TYPE|NO_SYSTEM_FROM_IMPORTED)$")

    # make the lists available
    set(CMAKE_PROPERTY_LIST ${CMAKE_PROPERTY_LIST} PARENT_SCOPE)
    set(CMAKE_WHITELISTED_PROPERTY_LIST ${CMAKE_WHITELISTED_PROPERTY_LIST} PARENT_SCOPE)
endfunction()

get_cmake_property_list()

function(print_target_properties tgt)
    if(NOT TARGET ${tgt})
      message("There is no target named '${tgt}'")
      return()
    endif()

    get_target_property(target_type ${tgt} TYPE)
    if(target_type STREQUAL "INTERFACE_LIBRARY")
        set(PROPERTIES ${CMAKE_WHITELISTED_PROPERTY_LIST})
    else()
        set(PROPERTIES ${CMAKE_PROPERTY_LIST})
    endif()

    foreach (prop ${PROPERTIES})
        #message ("Checking ${prop}")
        get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
        if (propval)
            get_target_property(propval ${tgt} ${prop})
            message ("${tgt} ${prop} = ${propval}")
        endif()
    endforeach(prop)
endfunction(print_target_properties)
Martin Valgur
  • 5,793
  • 1
  • 33
  • 45
mheyman
  • 4,211
  • 37
  • 34
  • Since there can be no CMAKE_CONFIGURATION_TYPES at all, cmake would error on `list(GET AVAILABLE_CONFIGURATION_TYPES 0 AVAILABLE_CONFIGURATION_TYPES)` – Roman Orekhov Jul 19 '23 at 23:22