Skip to content

get_property: add mode to get list of set target properties

So, I've put together a prototype of a get_target_property_list() command (which does exactly what it says on the tin1) — my work so far can be seen in the target-property-list-cmd branch of my fork. The implementation is all there, and there's a start at documentation for the command, but there are no unit tests yet. (So, that's the minimum I'd have to add, before even considering an MR.)

Guidance sought

I figure, before putting any more work into this, it's worth seeking some input/guidance to ensure I don't end up wasting either my time or yours. Specifically:

  1. Would the devs would be amenable to possibly merging this feature (or some version of it) into the core repo? Is it worth iterating more on it, in an effort to bring it to a state where that discussion can be opened?
  2. Are there any significant changes that would be required to how the feature is either implemented or presented? (Obviously, details can be ironed out in an MR review, I'm talking fundamental, architectural stuff.)
  3. Would any of get_{cmake,directory,source_file,test}_property_list() commands, and/or a general get_property_list() command, be either required, or desired, either before or after merging a get_target_property_list() command?

Use case/background

Aside from being something I've just thought would be handy at times in general, I do have one particular use case in mind. (Whether this serves as an argument for or against get_target_propery_list() is up to you, it's certainly not the only possible solution to the problem.)

In the context of the Windows DLL target problem, I've long been looking for way to "repair" UNKNOWN IMPORTED targets that have their import library location stored in IMPORTED_LOCATION (as many Find modules create, on Windows), and turn them into proper SHARED IMPORTED targets with both an IMPORTED_LOCATION and IMPORTED_IMPLIB.

It's possible to use dlltool -E to look up the DLL name for a given import library, and that DLL can then be searched for on the system using find_program(). But the problem is, even if you do locate the DLL for a target that only has its import library defined, an UNKNOWN IMPORTED target on Windows can't have its IMPORTED_IMPLIB and IMPORTED_LOCATION set — IMPORTED_IMPLIB will be rejected for non-SHARED targets. And you can't change the TYPE of a target after creation.

So the only way to "turn" an UNKNOWN IMPORTED library target into a SHARED IMPORTED target with both IMPORTED_LOCATION and IMPORTED_IMPLIB defined is to create a new target with the correct parameters. And, re-creating a target with a different type only works if you can migrate all of the properties defined on the previous target over to the new one. Unless there's some way to list all of the properties, there's no way to know what custom properties you might have to transfer.

So, that's the primary utility of this command, at least in my mind, and the reason I started with get_target_property_list() specifically.

As I said, this could certainly be addressed in other ways, like:

  1. Make a target's TYPE modifiable after creation.
  2. Make UNKNOWN IMPORTED targets accept IMPORTED_IMPLIB properties the same way SHARED IMPORTED targets do.
  3. Fix all existing Find modules, everywhere, to create proper SHARED IMPORTED targets on Windows. (Realistically, that's... if not impossible, then impractical. And at a minimum, it would necessitate providing some sort of standard functionality to help locate DLLs in addition to import libraries, so that they don't all have to implement it themselves.)

But two things led me down this path:

  1. This command seems like the least invasive of the possible solutions. It's relatively self-contained C++ code with minimal impact on other functionality; a feature is added, but no features are changed. Having it means that everything else necessary to re-create DLL targets can be implemented in CMake scripting, and shouldn't require additional changes to the C++ code. Either of the other two solutions would require modifications to the logic for target management and parameter validation that's woven into much of CMake's core, which could have unexpected impact on existing functionality.
  2. I suspect get_target_property_list() would end up being generally useful to other users, and help solve problems that have nothing to do with Windows DLL target-fixing specifically.

Demonstration

The code is already far enough along that I can run a locally-built cmake on this CMakeLists.txt:

cmake_minimum_required(VERSION 3.25)
project(footest VERSION 0.1 LANGUAGES CXX)

macro(print_props tgt_name)
  if(TARGET ${tgt_name})
    get_target_property_list(_props ${tgt_name})
    message(STATUS "${tgt_name} properties:")
    list(APPEND CMAKE_MESSAGE_INDENT "  ")
    foreach(_n IN LISTS _props)
      get_target_property(_p ${tgt_name} ${_n})
      message(STATUS "${_n}: ${_p}")
    endforeach()
    list(POP_BACK CMAKE_MESSAGE_INDENT)
  endif()
endmacro()

find_package(Qt6 COMPONENTS Core)
print_props(Qt6::qtpaths)
print_props(Qt6::Core)

add_library(foo INTERFACE IMPORTED)
set_property(TARGET foo APPEND PROPERTY
	INTERFACE_INCLUDE_DIRECTORIES "/usr/local/include")
set_property(TARGET foo PROPERTY
	CUSTOM_PROP "is custom")

set_property(TARGET foo PROPERTY
	INTERFACE_LINK_LIBRARIES Qt6::Core)
target_link_libraries(foo INTERFACE "-lm")
target_include_directories(foo INTERFACE "/usr/include/qt5")
target_compile_definitions(foo INTERFACE "-Ddebug")

print_props(foo)

add_executable(dummy dummy.cxx)
target_link_libraries(dummy PUBLIC foo)

print_props(dummy)

And I get this output:

-- The CXX compiler identification is GNU 12.2.1
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/lib64/ccache/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Performing Test HAVE_STDATOMIC
-- Performing Test HAVE_STDATOMIC - Success
-- Found WrapAtomic: TRUE  
-- Qt6::qtpaths properties:
--   BINARY_DIR: .../_test_proplist/_build
--   COMPATIBLE_INTERFACE_STRING: QT_MAJOR_VERSION
--   IMPORTED: TRUE
--   IMPORTED_CONFIGURATIONS: RELWITHDEBINFO;RELEASE;MINSIZEREL
--   IMPORTED_GLOBAL: TRUE
--   IMPORTED_LOCATION: /usr/lib64/qt6/bin/qtpaths
--   IMPORTED_LOCATION_MINSIZEREL: /usr/lib64/qt6/bin/qtpaths
--   IMPORTED_LOCATION_RELEASE: /usr/lib64/qt6/bin/qtpaths
--   IMPORTED_LOCATION_RELWITHDEBINFO: /usr/lib64/qt6/bin/qtpaths
--   INTERFACE_INCLUDE_DIRECTORIES: /usr/include/qt6
--   INTERFACE_QT_MAJOR_VERSION: 6
--   NAME: Qt6::qtpaths
--   SOURCE_DIR: .../_test_proplist
--   SYSTEM: ON
--   TYPE: EXECUTABLE
--   _qt_package_version: 6.4.1
-- Qt6::Core properties:
--   BINARY_DIR: .../_test_proplist/_build
--   COMPATIBLE_INTERFACE_STRING: QT_MAJOR_VERSION;QT_COORD_TYPE
--   IMPORTED: TRUE
--   IMPORTED_CONFIGURATIONS: RELWITHDEBINFO;RELEASE;MINSIZEREL
--   IMPORTED_GLOBAL: FALSE
--   IMPORTED_LOCATION: /usr/lib64/libQt6Core.so.6.4.1
--   IMPORTED_LOCATION_MINSIZEREL: /usr/lib64/libQt6Core.so.6.4.1
--   IMPORTED_LOCATION_RELEASE: /usr/lib64/libQt6Core.so.6.4.1
--   IMPORTED_LOCATION_RELWITHDEBINFO: /usr/lib64/libQt6Core.so.6.4.1
--   IMPORTED_SONAME: libQt6Core.so.6
--   IMPORTED_SONAME_MINSIZEREL: libQt6Core.so.6
--   IMPORTED_SONAME_RELEASE: libQt6Core.so.6
--   IMPORTED_SONAME_RELWITHDEBINFO: libQt6Core.so.6
--   INTERFACE_COMPILE_DEFINITIONS: QT_CORE_LIB;$<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG>
--   INTERFACE_COMPILE_FEATURES: cxx_decltype
--   INTERFACE_INCLUDE_DIRECTORIES: /usr/include/qt6/QtCore;/usr/include/qt6
--   INTERFACE_LINK_LIBRARIES: Qt6::Platform;WrapAtomic::WrapAtomic
--   INTERFACE_QT_COORD_TYPE: double
--   INTERFACE_QT_MAJOR_VERSION: 6
--   INTERFACE_SOURCES: $<$<BOOL:$<TARGET_PROPERTY:QT_CONSUMES_METATYPES>>:/usr/lib64/metatypes/qt6core_relwithdebinfo_metatypes.json>
--   NAME: Qt6::Core
--   POSITION_INDEPENDENT_CODE: True
--   QT_DISABLED_PRIVATE_FEATURES: futimes;poll_pollts;poll_poll;poll_select;qqnx_pps;slog2;syslog;cpp_winrt;lttng;etw;use_bfd_linker;use_gold_linker;use_lld_linker;use_mold_linker;android_style_assets;gc_binaries;developer_build;no_prefix;private_tests;debug;no_direct_extern_access;mips_dsp;mips_dspr2;neon;arm_crc32;arm_crypto;alloca_malloc_h;stack_protector_strong;stdlib_libcpp;relocatable
--   QT_DISABLED_PUBLIC_FEATURES: cpp_winrt;static;cross_compile;debug_and_release;separate_debug_info;appstore_compliant;simulator_and_device;rpath;force_asserts;framework;cxx20;cxx2a;cxx2b;reduce_relocations;zstd
--   QT_ENABLED_PRIVATE_FEATURES: clock_gettime;doubleconversion;system_doubleconversion;dladdr;futimens;getauxval;getentropy;glib;glibc;icu;inotify;journald;system_libb2;linkat;mimetype_database;system_pcre2;poll_ppoll;renameat2;statx;backtrace;sha3_fast;hijricalendar;datetimeparser;forkfd_pidfd;pcre2;reduce_exports;sse2;sse3;ssse3;sse4_1;sse4_2;avx;f16c;avx2;avx512f;avx512er;avx512cd;avx512pf;avx512dq;avx512bw;avx512vl;avx512ifma;avx512vbmi;avx512vbmi2;aesni;vaes;rdrnd;rdseed;shani;posix_fallocate;alloca_h;alloca;system_zlib;dbus;dbus_linked;gui;network;printsupport;sql;testlib;widgets;xml;libudev;dlopen;intelcet;force_debug_info;largefile;precompile_header;enable_new_dtags;sse2;sse3;ssse3;sse4_1;sse4_2;avx;f16c;avx2;avx512f;avx512er;avx512cd;avx512pf;avx512dq;avx512bw;avx512vl;avx512ifma;avx512vbmi;avx512vbmi2;aesni;vaes;rdrnd;rdseed;shani
--   QT_ENABLED_PUBLIC_FEATURES: clock_monotonic;doubleconversion;cxx11_future;cxx17_filesystem;eventfd;glib;inotify;std_atomic64;mimetype;regularexpression;sharedmemory;shortcut;systemsemaphore;xmlstream;xmlstreamreader;xmlstreamwriter;textdate;datestring;process;processenvironment;temporaryfile;library;settings;filesystemwatcher;filesystemiterator;itemmodel;proxymodel;sortfilterproxymodel;identityproxymodel;transposeproxymodel;concatenatetablesproxymodel;stringlistmodel;translation;easingcurve;animation;gestures;jalalicalendar;islamiccivilcalendar;timezone;commandlineparser;cborstreamreader;cborstreamwriter;threadsafe_cloexec;shared;pkg_config;cxx11;cxx14;cxx17;cxx1z;c99;c11;signaling_nan;thread;future;concurrent;dbus;shared;plugin_manifest;shared;cxx11;cxx14;cxx17;cxx1z;reduce_exports
--   QT_QMAKE_PRIVATE_CONFIG: pcre2;force_debug_info;largefile;precompile_header;enable_new_dtags;sse2;sse3;ssse3;sse4_1;sse4_2;avx;f16c;avx2;avx512f;avx512er;avx512cd;avx512pf;avx512dq;avx512bw;avx512vl;avx512ifma;avx512vbmi;avx512vbmi2;aesni;vaes;rdrnd;rdseed;shani
--   QT_QMAKE_PUBLIC_CONFIG: shared;plugin_manifest
--   QT_QMAKE_PUBLIC_QT_CONFIG: threadsafe_cloexec;shared;cxx11;cxx14;cxx17;cxx1z;reduce_exports
--   SOURCE_DIR: .../_test_proplist
--   SYSTEM: ON
--   TYPE: SHARED_LIBRARY
--   _qt_config_module_name: core
--   _qt_deploy_support_files: /usr/lib64/cmake/Qt6Core/Qt6CoreDeploySupport.cmake
--   _qt_is_public_module: TRUE
--   _qt_module_has_headers: ON
--   _qt_module_include_name: QtCore
--   _qt_module_interface_name: Core
--   _qt_package_name: Qt6Core
--   _qt_package_version: 6.4.1
--   _qt_private_module_target_name: CorePrivate
-- foo properties:
--   BINARY_DIR: .../_test_proplist/_build
--   CUSTOM_PROP: is custom
--   IMPORTED: TRUE
--   IMPORTED_GLOBAL: FALSE
--   INTERFACE_COMPILE_DEFINITIONS: debug
--   INTERFACE_INCLUDE_DIRECTORIES: /usr/local/include;/usr/include/qt5
--   INTERFACE_LINK_LIBRARIES: Qt6::Core;-lm
--   NAME: foo
--   SOURCE_DIR: .../_test_proplist
--   SYSTEM: ON
--   TYPE: INTERFACE_LIBRARY
-- dummy properties:
--   AUTOGEN_ORIGIN_DEPENDS: ON
--   AUTOMOC_COMPILER_PREDEFINES: ON
--   AUTOMOC_MACRO_NAMES: Q_OBJECT;Q_GADGET;Q_GADGET_EXPORT;Q_NAMESPACE;Q_NAMESPACE_EXPORT
--   AUTOMOC_PATH_PREFIX: OFF
--   BINARY_DIR: .../_test_proplist/_build
--   BUILD_WITH_INSTALL_RPATH: OFF
--   EXPORT_COMPILE_COMMANDS: 
--   IMPORTED: FALSE
--   IMPORTED_GLOBAL: FALSE
--   INSTALL_RPATH: 
--   INSTALL_RPATH_USE_LINK_PATH: OFF
--   INTERFACE_LINK_LIBRARIES: foo
--   ISPC_HEADER_SUFFIX: _ispc.h
--   LINK_LIBRARIES: foo
--   NAME: dummy
--   PCH_INSTANTIATE_TEMPLATES: ON
--   PCH_WARN_INVALID: ON
--   SKIP_BUILD_RPATH: OFF
--   SOURCE_DIR: .../_test_proplist
--   TYPE: EXECUTABLE
--   UNITY_BUILD_BATCH_SIZE: 8
--   UNITY_BUILD_MODE: BATCH
-- Configuring done
-- Generating done
-- Build files have been written to: .../_test_proplist/_build

cc: @brad.king @craig.scott

Notes

  1. https://en.wikipedia.org/wiki/Does_exactly_what_it_says_on_the_tin
Edited by Frank Dana
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information