From c5097b5e8fc870dff7c663dd48059e2af3c90712 Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Thu, 12 Sep 2024 17:21:18 +0800 Subject: [PATCH 01/11] test: test installation of conan packages --- tests/myproj/CMakeLists.txt | 19 ++++++------------- tests/myproj/conanfile.txt | 3 +++ tests/myproj/src/main/main.cpp | 27 +++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index bd537086..189ca0bd 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -96,21 +96,14 @@ project_options( add_executable(main ./src/main/main.cpp) target_link_libraries(main PRIVATE myproj_project_options myproj_project_warnings) -target_find_dependencies( - main +target_find_dependencies(main PUBLIC - PACKAGE - fmt - CONFIG - PACKAGE - Eigen3 - CONFIG - PACKAGE - docopt - CONFIG + PACKAGE fmt CONFIG + PACKAGE Eigen3 CONFIG + PACKAGE docopt CONFIG ) -target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen) +target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen docopt) add_subdirectory(libs) @@ -180,7 +173,7 @@ package_project( myproj_project_options myproj_project_warnings PUBLIC_INCLUDES - ${lib2_INCLUDE_DIR22} + ${lib2_INCLUDE_DIR2} ) package_project(NAME myproj_main TARGETS main) diff --git a/tests/myproj/conanfile.txt b/tests/myproj/conanfile.txt index 1a3bf031..4305f5bc 100644 --- a/tests/myproj/conanfile.txt +++ b/tests/myproj/conanfile.txt @@ -3,5 +3,8 @@ [requires] docopt.cpp/0.6.3 +[options] +docopt.cpp/*:shared=True + [generators] CMakeDeps diff --git a/tests/myproj/src/main/main.cpp b/tests/myproj/src/main/main.cpp index 65c8ad70..866ed13c 100644 --- a/tests/myproj/src/main/main.cpp +++ b/tests/myproj/src/main/main.cpp @@ -1,10 +1,13 @@ // test external pac +#include #include #include +#include #include // test std libraries #include +#include #include #include @@ -15,15 +18,31 @@ #include #include -int main() { - fmt::print("Hello from fmt{}", "!"); +static std::string const usage{ +R"(main. + + Usage: + main + main (-h | --help) + main --version + + Options: + -h --help Show this screen. + --version Show version. +)"}; + +int main(int argc, char const* argv[]) { + std::map args{docopt::docopt(usage, {argv + 1, argv + argc}, /*help=*/true, "main 1.0")}; + for (auto const& arg : args) { + fmt::println("{}: {}", arg.first, fmt::streamed(arg.second)); + } Eigen::VectorXd eigen_vec = Eigen::Vector3d(1, 2, 3); - fmt::print("[{}]", fmt::join(eigen_vec, ", ")); + fmt::println("[{}]", fmt::join(eigen_vec, ", ")); #if !defined(__MINGW32__) && !defined(__MSYS__)// TODO fails Eigen::VectorXd eigen_vec2 = Eigen::VectorXd::LinSpaced(10, 0, 1); - fmt::print("[{}]", fmt::join(eigen_vec2, ", ")); + fmt::println("[{}]", fmt::join(eigen_vec2, ", ")); #endif // trigger address sanitizer From 7a98de4697e5e69cd5366b739adeab0eec4d1ed7 Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Thu, 12 Sep 2024 16:48:05 +0800 Subject: [PATCH 02/11] feat: automatically install runtimes for conan --- src/PackageProject.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index 71553534..bed5f7b6 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -255,6 +255,9 @@ function(package_project) "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}$<$:/debug>/lib" ) endif() + if(CONAN_RUNTIME_LIB_DIRS) + list(APPEND runtime_dirs ${CONAN_RUNTIME_LIB_DIRS}) + endif() set(_PackageProject_RUNTIME_DEPENDENCY_SET_ARGS) if(runtime_dirs) set(_PackageProject_RUNTIME_DEPENDENCY_SET_ARGS From 5c08852450530e9ff5534c558269377e6d4d49a3 Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 16:08:19 -0700 Subject: [PATCH 03/11] fix: stage Conan runtimes for Windows test --- tests/myproj/CMakeLists.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 189ca0bd..0d6dd0d3 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -103,6 +103,21 @@ target_find_dependencies(main PACKAGE docopt CONFIG ) +if(WIN32 AND MSVC) + target_compile_definitions(main PRIVATE DOCOPT_DLL) +endif() + +if(WIN32 AND CONAN_RUNTIME_LIB_DIRS) + foreach(runtime_dir IN LISTS CONAN_RUNTIME_LIB_DIRS) + add_custom_command( + TARGET main + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${runtime_dir}" "$" + VERBATIM + ) + endforeach() +endif() + target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen docopt) add_subdirectory(libs) From eb36f8de8ceb96dfc5b11c81c7113254a654e87c Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 16:21:39 -0700 Subject: [PATCH 04/11] fix: define docopt DLL import on Windows --- tests/myproj/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 0d6dd0d3..069b4e77 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -103,7 +103,7 @@ target_find_dependencies(main PACKAGE docopt CONFIG ) -if(WIN32 AND MSVC) +if(WIN32) target_compile_definitions(main PRIVATE DOCOPT_DLL) endif() From 6cfae1e1801b5f4f7ab9abefe38a0371078c1380 Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 16:46:29 -0700 Subject: [PATCH 05/11] fix: make Conan docopt test portable on Windows --- tests/myproj/CMakeLists.txt | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 069b4e77..20900ba6 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -24,7 +24,17 @@ set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg_triplets") run_vcpkg(VCPKG_URL "https://github.com/microsoft/vcpkg.git" VCPKG_REV "0cb95c860ea83aafc1b24350510b30dec535989a" ENABLE_VCPKG_UPDATE ) -run_conan() +if(WIN32) + run_conan( + INSTALL_ARGS + --build=missing + -c tools.system.package_manager:mode=install + -c tools.system.package_manager:sudo=True + -c "tools.cmake.cmaketoolchain:extra_variables={'MSVC':'ON'}" + ) +else() + run_conan() +endif() project(myproj VERSION 0.2.0 LANGUAGES CXX C) @@ -107,15 +117,15 @@ if(WIN32) target_compile_definitions(main PRIVATE DOCOPT_DLL) endif() -if(WIN32 AND CONAN_RUNTIME_LIB_DIRS) - foreach(runtime_dir IN LISTS CONAN_RUNTIME_LIB_DIRS) - add_custom_command( - TARGET main - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory "${runtime_dir}" "$" - VERBATIM - ) - endforeach() +if(WIN32) + add_custom_command( + TARGET main + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$" + VERBATIM + ) endif() target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen docopt) From 44e681140dfce2e93c1d07825ba89d0c4534438e Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 17:00:11 -0700 Subject: [PATCH 06/11] fix: use Conan docopt consumer target --- tests/myproj/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 20900ba6..61993255 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -24,6 +24,7 @@ set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg_triplets") run_vcpkg(VCPKG_URL "https://github.com/microsoft/vcpkg.git" VCPKG_REV "0cb95c860ea83aafc1b24350510b30dec535989a" ENABLE_VCPKG_UPDATE ) +# cspell:ignore cmaketoolchain if(WIN32) run_conan( INSTALL_ARGS @@ -122,7 +123,7 @@ if(WIN32) TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" + "$" "$" VERBATIM ) From 1c763f65f5ab13315928083bf6394031b7f2975d Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 17:08:42 -0700 Subject: [PATCH 07/11] test: allow more time for vcpkg configure --- tests/vcpkg-parallel/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vcpkg-parallel/CMakeLists.txt b/tests/vcpkg-parallel/CMakeLists.txt index eb255fbe..7a179bae 100644 --- a/tests/vcpkg-parallel/CMakeLists.txt +++ b/tests/vcpkg-parallel/CMakeLists.txt @@ -76,5 +76,5 @@ add_test( "-DVCPKG_REV=${FAKE_VCPKG_REVISION}" "-DEXERCISE_REENTRANT_LOCK=ON" ) set_tests_properties( - vcpkg_parallel_reentrant_configure PROPERTIES FIXTURES_REQUIRED vcpkg_parallel_shared TIMEOUT 5 + vcpkg_parallel_reentrant_configure PROPERTIES FIXTURES_REQUIRED vcpkg_parallel_shared TIMEOUT 30 ) From 42f0800249b7df32974bcd0ea34e03ee474aaae7 Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 17:28:46 -0700 Subject: [PATCH 08/11] feat: stage Conan runtimes automatically --- src/PackageProject.cmake | 64 ++++++++++++++++++++++++++++++++++++ src/copy_conan_runtime.cmake | 14 ++++++++ tests/myproj/CMakeLists.txt | 11 ------- 3 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 src/copy_conan_runtime.cmake diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index bed5f7b6..fe0ec332 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -2,6 +2,8 @@ include_guard() include("${CMAKE_CURRENT_LIST_DIR}/Utilities.cmake") +set(_PROJECT_OPTIONS_PACKAGE_PROJECT_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}") + # Uses ycm (permissive BSD-3-Clause license) and ForwardArguments (permissive MIT license) function(get_property_of_targets) @@ -22,6 +24,67 @@ function(get_property_of_targets) set(${args_OUTPUT} ${_Value} PARENT_SCOPE) endfunction() +#[[ + +``install_conan_runtime`` +========================= + +Copy Conan runtime DLLs next to binary targets after Conan has installed them. + +The Conan CMake toolchain exposes ``CONAN_RUNTIME_LIB_DIRS`` for this purpose. This helper is +normally called by ``package_project`` and is safe to call more than once for the same target. + +]] +function(install_conan_runtime) + if(NOT WIN32 OR NOT CONAN_RUNTIME_LIB_DIRS) + return() + endif() + + foreach(_target_name IN LISTS ARGN) + if(NOT TARGET "${_target_name}") + message(WARNING "install_conan_runtime: ${_target_name} is not a target.") + continue() + endif() + + get_target_property(_target_imported "${_target_name}" IMPORTED) + if(_target_imported) + message(WARNING "install_conan_runtime: ${_target_name} is imported; not copying next to it.") + continue() + endif() + + get_target_property(_target_type "${_target_name}" TYPE) + if(NOT _target_type MATCHES "^(EXECUTABLE|SHARED_LIBRARY|MODULE_LIBRARY)$") + continue() + endif() + + get_target_property(_runtime_already_installed "${_target_name}" + _PROJECT_OPTIONS_CONAN_RUNTIME_INSTALLED + ) + if(_runtime_already_installed) + continue() + endif() + + get_property(_runtime_target_index GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX) + if(NOT _runtime_target_index) + set(_runtime_target_index 0) + endif() + math(EXPR _runtime_target_index "${_runtime_target_index} + 1") + set_property(GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX "${_runtime_target_index}") + set(_runtime_target "_project_options_conan_runtime_${_runtime_target_index}") + + add_custom_target( + "${_runtime_target}" + COMMAND "${CMAKE_COMMAND}" + "-DPROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS=${CONAN_RUNTIME_LIB_DIRS}" + "-DPROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION=$" + -P "${_PROJECT_OPTIONS_PACKAGE_PROJECT_MODULE_DIR}/copy_conan_runtime.cmake" + VERBATIM + ) + add_dependencies("${_target_name}" "${_runtime_target}") + set_property(TARGET "${_target_name}" PROPERTY _PROJECT_OPTIONS_CONAN_RUNTIME_INSTALLED TRUE) + endforeach() +endfunction() + #[[.rst: ``package_project`` @@ -248,6 +311,7 @@ function(package_project) # Installation of package (compatible with vcpkg, etc) set(_targets_list ${_PackageProject_TARGETS}) unset(_PackageProject_TARGETS) # to avoid ycm conflict + install_conan_runtime(${_targets_list}) set(runtime_dirs) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.21.0" AND VCPKG_INSTALLED_DIR AND VCPKG_TARGET_TRIPLET) list(APPEND runtime_dirs diff --git a/src/copy_conan_runtime.cmake b/src/copy_conan_runtime.cmake new file mode 100644 index 00000000..a003f5d8 --- /dev/null +++ b/src/copy_conan_runtime.cmake @@ -0,0 +1,14 @@ +if(NOT DEFINED PROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS) + message(FATAL_ERROR "PROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS must be defined") +endif() +if(NOT DEFINED PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION) + message(FATAL_ERROR "PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION must be defined") +endif() + +file(MAKE_DIRECTORY "${PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION}") +foreach(_runtime_dir IN LISTS PROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS) + file(GLOB _runtime_libraries LIST_DIRECTORIES false "${_runtime_dir}/*.dll") + foreach(_runtime_library IN LISTS _runtime_libraries) + file(COPY "${_runtime_library}" DESTINATION "${PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION}") + endforeach() +endforeach() diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 61993255..e53c5a5a 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -118,17 +118,6 @@ if(WIN32) target_compile_definitions(main PRIVATE DOCOPT_DLL) endif() -if(WIN32) - add_custom_command( - TARGET main - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" - "$" - VERBATIM - ) -endif() - target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen docopt) add_subdirectory(libs) From 5841518f73b5499476172af9858120dedad86420 Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 18:22:06 -0700 Subject: [PATCH 09/11] fix: stage Conan imported runtimes automatically --- src/PackageProject.cmake | 155 ++++++++++++++++++++++++++++++----- src/copy_conan_runtime.cmake | 14 ---- 2 files changed, 135 insertions(+), 34 deletions(-) delete mode 100644 src/copy_conan_runtime.cmake diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index fe0ec332..99a0964a 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -26,17 +26,112 @@ endfunction() #[[ +``_project_options_unwrap_conan_link_item`` +============================================ + +Unwrap the generator expressions commonly used in target link interfaces. More complex +expressions are conservatively ignored by the runtime dependency walk below. + +]] +function(_project_options_unwrap_conan_link_item LINK_ITEM OUTPUT_VARIABLE) + set(_link_item "${LINK_ITEM}") + while(_link_item MATCHES "^\\$<(LINK_ONLY|BUILD_INTERFACE|INSTALL_INTERFACE|TARGET_NAME_IF_EXISTS):(.*)>") + set(_link_item "${CMAKE_MATCH_2}") + endwhile() + set(${OUTPUT_VARIABLE} "${_link_item}" PARENT_SCOPE) +endfunction() + +#[[ + +``_project_options_collect_conan_runtime_libraries`` +===================================================== + +Collect runtime locations from imported targets in a target's link graph. CMakeDeps exposes +Conan shared libraries this way, while ``CONAN_RUNTIME_LIB_DIRS`` covers toolchains that expose +the Conan runtime directories directly. + +]] +function(_project_options_collect_conan_runtime_libraries TARGET_NAME OUTPUT_VARIABLE) + set(${OUTPUT_VARIABLE} "" PARENT_SCOPE) + set(_visited ${ARGN}) + + if(NOT TARGET "${TARGET_NAME}") + return() + endif() + + get_target_property(_aliased_target "${TARGET_NAME}" ALIASED_TARGET) + if(_aliased_target AND NOT _aliased_target MATCHES "-NOTFOUND$") + set(_target_name "${_aliased_target}") + else() + set(_target_name "${TARGET_NAME}") + endif() + + if("${_target_name}" IN_LIST _visited) + return() + endif() + list(APPEND _visited "${_target_name}") + + set(_runtime_libraries) + get_target_property(_target_imported "${_target_name}" IMPORTED) + if(_target_imported) + get_target_property(_imported_configurations "${_target_name}" IMPORTED_CONFIGURATIONS) + if(_imported_configurations MATCHES "-NOTFOUND$") + set(_imported_configurations) + endif() + list(APPEND _imported_configurations DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) + list(REMOVE_DUPLICATES _imported_configurations) + + foreach(_configuration IN LISTS _imported_configurations) + get_target_property(_runtime_library "${_target_name}" "IMPORTED_LOCATION_${_configuration}") + if(_runtime_library AND NOT _runtime_library MATCHES "-NOTFOUND$" + AND _runtime_library MATCHES "\\.[dD][lL][lL]$") + list(APPEND _runtime_libraries "${_runtime_library}") + endif() + endforeach() + + get_target_property(_runtime_library "${_target_name}" IMPORTED_LOCATION) + if(_runtime_library AND NOT _runtime_library MATCHES "-NOTFOUND$" + AND _runtime_library MATCHES "\\.[dD][lL][lL]$") + list(APPEND _runtime_libraries "${_runtime_library}") + endif() + endif() + + foreach(_property LINK_LIBRARIES INTERFACE_LINK_LIBRARIES) + get_target_property(_links "${_target_name}" "${_property}") + if("${_links}" STREQUAL "" OR "${_links}" MATCHES "-NOTFOUND$") + continue() + endif() + + foreach(_link IN LISTS _links) + if("${_link}" STREQUAL "::@" OR "${_link}" MATCHES "^::@\\(.*\\)$") + continue() + endif() + + _project_options_unwrap_conan_link_item("${_link}" _link_target) + if(TARGET "${_link_target}") + _project_options_collect_conan_runtime_libraries( + "${_link_target}" _linked_runtime_libraries ${_visited} + ) + list(APPEND _runtime_libraries ${_linked_runtime_libraries}) + endif() + endforeach() + endforeach() + + list(REMOVE_DUPLICATES _runtime_libraries) + set(${OUTPUT_VARIABLE} "${_runtime_libraries}" PARENT_SCOPE) +endfunction() + +#[[ + ``install_conan_runtime`` ========================= -Copy Conan runtime DLLs next to binary targets after Conan has installed them. - -The Conan CMake toolchain exposes ``CONAN_RUNTIME_LIB_DIRS`` for this purpose. This helper is +Copy Conan runtime DLLs next to binary targets after Conan has installed them. This helper is normally called by ``package_project`` and is safe to call more than once for the same target. ]] function(install_conan_runtime) - if(NOT WIN32 OR NOT CONAN_RUNTIME_LIB_DIRS) + if(NOT WIN32) return() endif() @@ -64,23 +159,43 @@ function(install_conan_runtime) continue() endif() - get_property(_runtime_target_index GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX) - if(NOT _runtime_target_index) - set(_runtime_target_index 0) + _project_options_collect_conan_runtime_libraries("${_target_name}" _runtime_libraries) + foreach(_runtime_dir IN LISTS CONAN_RUNTIME_LIB_DIRS) + file(GLOB _runtime_directory_libraries LIST_DIRECTORIES false "${_runtime_dir}/*.dll") + list(APPEND _runtime_libraries ${_runtime_directory_libraries}) + endforeach() + list(REMOVE_DUPLICATES _runtime_libraries) + if(NOT _runtime_libraries) + continue() + endif() + + set(_runtime_copy_commands + COMMAND "${CMAKE_COMMAND}" -E make_directory "$") + foreach(_runtime_library IN LISTS _runtime_libraries) + list(APPEND _runtime_copy_commands + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${_runtime_library}" + "$") + endforeach() + + get_target_property(_target_source_dir "${_target_name}" SOURCE_DIR) + if("${_target_source_dir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + add_custom_command( + TARGET "${_target_name}" + POST_BUILD + ${_runtime_copy_commands} + VERBATIM + ) + else() + get_property(_runtime_target_index GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX) + if(NOT _runtime_target_index) + set(_runtime_target_index 0) + endif() + math(EXPR _runtime_target_index "${_runtime_target_index} + 1") + set_property(GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX "${_runtime_target_index}") + set(_runtime_target "_project_options_conan_runtime_${_runtime_target_index}") + add_custom_target("${_runtime_target}" ${_runtime_copy_commands} VERBATIM) + add_dependencies("${_target_name}" "${_runtime_target}") endif() - math(EXPR _runtime_target_index "${_runtime_target_index} + 1") - set_property(GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX "${_runtime_target_index}") - set(_runtime_target "_project_options_conan_runtime_${_runtime_target_index}") - - add_custom_target( - "${_runtime_target}" - COMMAND "${CMAKE_COMMAND}" - "-DPROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS=${CONAN_RUNTIME_LIB_DIRS}" - "-DPROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION=$" - -P "${_PROJECT_OPTIONS_PACKAGE_PROJECT_MODULE_DIR}/copy_conan_runtime.cmake" - VERBATIM - ) - add_dependencies("${_target_name}" "${_runtime_target}") set_property(TARGET "${_target_name}" PROPERTY _PROJECT_OPTIONS_CONAN_RUNTIME_INSTALLED TRUE) endforeach() endfunction() diff --git a/src/copy_conan_runtime.cmake b/src/copy_conan_runtime.cmake deleted file mode 100644 index a003f5d8..00000000 --- a/src/copy_conan_runtime.cmake +++ /dev/null @@ -1,14 +0,0 @@ -if(NOT DEFINED PROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS) - message(FATAL_ERROR "PROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS must be defined") -endif() -if(NOT DEFINED PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION) - message(FATAL_ERROR "PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION must be defined") -endif() - -file(MAKE_DIRECTORY "${PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION}") -foreach(_runtime_dir IN LISTS PROJECT_OPTIONS_CONAN_RUNTIME_LIB_DIRS) - file(GLOB _runtime_libraries LIST_DIRECTORIES false "${_runtime_dir}/*.dll") - foreach(_runtime_library IN LISTS _runtime_libraries) - file(COPY "${_runtime_library}" DESTINATION "${PROJECT_OPTIONS_CONAN_RUNTIME_DESTINATION}") - endforeach() -endforeach() From 39a677ba84fe4e4ed2935ec31199459202ebfa29 Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 18:37:29 -0700 Subject: [PATCH 10/11] fix: use CMake runtime DLL discovery --- src/PackageProject.cmake | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index 99a0964a..01d4c4bf 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -159,18 +159,26 @@ function(install_conan_runtime) continue() endif() - _project_options_collect_conan_runtime_libraries("${_target_name}" _runtime_libraries) + set(_runtime_libraries) foreach(_runtime_dir IN LISTS CONAN_RUNTIME_LIB_DIRS) file(GLOB _runtime_directory_libraries LIST_DIRECTORIES false "${_runtime_dir}/*.dll") list(APPEND _runtime_libraries ${_runtime_directory_libraries}) endforeach() list(REMOVE_DUPLICATES _runtime_libraries) - if(NOT _runtime_libraries) - continue() + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.21.0") + set(_runtime_copy_commands + COMMAND "${CMAKE_COMMAND}" -E make_directory "$" + COMMAND "${CMAKE_COMMAND}" -E copy -t "$" + "$") + set(_runtime_copy_options COMMAND_EXPAND_LISTS) + else() + if(NOT _runtime_libraries) + continue() + endif() + set(_runtime_copy_commands + COMMAND "${CMAKE_COMMAND}" -E make_directory "$") + set(_runtime_copy_options) endif() - - set(_runtime_copy_commands - COMMAND "${CMAKE_COMMAND}" -E make_directory "$") foreach(_runtime_library IN LISTS _runtime_libraries) list(APPEND _runtime_copy_commands COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${_runtime_library}" @@ -183,6 +191,7 @@ function(install_conan_runtime) TARGET "${_target_name}" POST_BUILD ${_runtime_copy_commands} + ${_runtime_copy_options} VERBATIM ) else() @@ -193,7 +202,7 @@ function(install_conan_runtime) math(EXPR _runtime_target_index "${_runtime_target_index} + 1") set_property(GLOBAL PROPERTY PROJECT_OPTIONS_CONAN_RUNTIME_TARGET_INDEX "${_runtime_target_index}") set(_runtime_target "_project_options_conan_runtime_${_runtime_target_index}") - add_custom_target("${_runtime_target}" ${_runtime_copy_commands} VERBATIM) + add_custom_target("${_runtime_target}" ${_runtime_copy_commands} ${_runtime_copy_options} VERBATIM) add_dependencies("${_target_name}" "${_runtime_target}") endif() set_property(TARGET "${_target_name}" PROPERTY _PROJECT_OPTIONS_CONAN_RUNTIME_INSTALLED TRUE) From abdff1f3fc010ec5e7ccc6cbfd60f9cc6fd2f0f6 Mon Sep 17 00:00:00 2001 From: Amin Ya Date: Sat, 5 Sep 2026 18:38:11 -0700 Subject: [PATCH 11/11] refactor: use CMake runtime DLL generator --- src/PackageProject.cmake | 97 ---------------------------------------- 1 file changed, 97 deletions(-) diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index 01d4c4bf..01a81304 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -26,103 +26,6 @@ endfunction() #[[ -``_project_options_unwrap_conan_link_item`` -============================================ - -Unwrap the generator expressions commonly used in target link interfaces. More complex -expressions are conservatively ignored by the runtime dependency walk below. - -]] -function(_project_options_unwrap_conan_link_item LINK_ITEM OUTPUT_VARIABLE) - set(_link_item "${LINK_ITEM}") - while(_link_item MATCHES "^\\$<(LINK_ONLY|BUILD_INTERFACE|INSTALL_INTERFACE|TARGET_NAME_IF_EXISTS):(.*)>") - set(_link_item "${CMAKE_MATCH_2}") - endwhile() - set(${OUTPUT_VARIABLE} "${_link_item}" PARENT_SCOPE) -endfunction() - -#[[ - -``_project_options_collect_conan_runtime_libraries`` -===================================================== - -Collect runtime locations from imported targets in a target's link graph. CMakeDeps exposes -Conan shared libraries this way, while ``CONAN_RUNTIME_LIB_DIRS`` covers toolchains that expose -the Conan runtime directories directly. - -]] -function(_project_options_collect_conan_runtime_libraries TARGET_NAME OUTPUT_VARIABLE) - set(${OUTPUT_VARIABLE} "" PARENT_SCOPE) - set(_visited ${ARGN}) - - if(NOT TARGET "${TARGET_NAME}") - return() - endif() - - get_target_property(_aliased_target "${TARGET_NAME}" ALIASED_TARGET) - if(_aliased_target AND NOT _aliased_target MATCHES "-NOTFOUND$") - set(_target_name "${_aliased_target}") - else() - set(_target_name "${TARGET_NAME}") - endif() - - if("${_target_name}" IN_LIST _visited) - return() - endif() - list(APPEND _visited "${_target_name}") - - set(_runtime_libraries) - get_target_property(_target_imported "${_target_name}" IMPORTED) - if(_target_imported) - get_target_property(_imported_configurations "${_target_name}" IMPORTED_CONFIGURATIONS) - if(_imported_configurations MATCHES "-NOTFOUND$") - set(_imported_configurations) - endif() - list(APPEND _imported_configurations DEBUG RELEASE RELWITHDEBINFO MINSIZEREL) - list(REMOVE_DUPLICATES _imported_configurations) - - foreach(_configuration IN LISTS _imported_configurations) - get_target_property(_runtime_library "${_target_name}" "IMPORTED_LOCATION_${_configuration}") - if(_runtime_library AND NOT _runtime_library MATCHES "-NOTFOUND$" - AND _runtime_library MATCHES "\\.[dD][lL][lL]$") - list(APPEND _runtime_libraries "${_runtime_library}") - endif() - endforeach() - - get_target_property(_runtime_library "${_target_name}" IMPORTED_LOCATION) - if(_runtime_library AND NOT _runtime_library MATCHES "-NOTFOUND$" - AND _runtime_library MATCHES "\\.[dD][lL][lL]$") - list(APPEND _runtime_libraries "${_runtime_library}") - endif() - endif() - - foreach(_property LINK_LIBRARIES INTERFACE_LINK_LIBRARIES) - get_target_property(_links "${_target_name}" "${_property}") - if("${_links}" STREQUAL "" OR "${_links}" MATCHES "-NOTFOUND$") - continue() - endif() - - foreach(_link IN LISTS _links) - if("${_link}" STREQUAL "::@" OR "${_link}" MATCHES "^::@\\(.*\\)$") - continue() - endif() - - _project_options_unwrap_conan_link_item("${_link}" _link_target) - if(TARGET "${_link_target}") - _project_options_collect_conan_runtime_libraries( - "${_link_target}" _linked_runtime_libraries ${_visited} - ) - list(APPEND _runtime_libraries ${_linked_runtime_libraries}) - endif() - endforeach() - endforeach() - - list(REMOVE_DUPLICATES _runtime_libraries) - set(${OUTPUT_VARIABLE} "${_runtime_libraries}" PARENT_SCOPE) -endfunction() - -#[[ - ``install_conan_runtime`` =========================