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:
- 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?
- 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.)
- Would any of
get_{cmake,directory,source_file,test}_property_list()
commands, and/or a generalget_property_list()
command, be either required, or desired, either before or after merging aget_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:
- Make a target's
TYPE
modifiable after creation. - Make
UNKNOWN IMPORTED
targets acceptIMPORTED_IMPLIB
properties the same waySHARED IMPORTED
targets do. - 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:
- 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.
- 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