From 2145aa107da11b54c7cef95aa57cd5159adc20df Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Sun, 28 Apr 2024 21:20:48 +0800 Subject: [PATCH 1/5] Add complex mode for `target_find_dependencies` syntax For packages like Qt, users might custom find_package like `find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets)`. This patch supports such a customazation by adding a complex mode for `target_find_dependencies`. That is, ```cmake target_find_dependencies(target PUBLIC PACKAGE Qt6 COMPONENTS Widgets PACAKGE Microsoft.GSL QUIET PUBLIC fmt range-v3 ) ``` --- src/PackageProject.cmake | 138 ++++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 44 deletions(-) diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index 1fae5228..d52ed6c3 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -400,7 +400,7 @@ function(target_include_interface_directories target) endforeach() endfunction() -#[[.rst: +#[=[.rst: ``target_find_dependencies`` ===================================== @@ -408,15 +408,17 @@ endfunction() .. code:: cmake target_find_dependencies( - [INTERFACE dependency ...] - [PUBLIC dependency ...] - [PRIVATE dependency ...] - [INTERFACE_CONFIG dependency ...] - [PUBLIC_CONFIG dependency ...] - [PRIVATE_CONFIG dependency ...] + [ + + [dependency1...] + ]... + [ + + [PACKAGE [find_package() argument1...]]... + ]... ) -This macro calls ``find_package(${dependency} [CONFIG] REQUIRED)`` for +This macro calls ``find_package(${dependency} [CONFIG] REQUIRED [OTHER_ARGUMENTS])`` for all dependencies required and binds them to the target. Properties named @@ -436,60 +438,108 @@ to add more dependencies. target_find_dependencies(my_lib PUBLIC_CONFIG - fmt + fmt + PRIVATE_CONFIG - Microsoft.GSL + PACKAGE Microsoft.GSL + PACKAGE Qt6 COMPONENTS Widgets ) target_find_dependencies(my_lib - PRIVATE_CONFIG - range-v3 + PRIVATE + PACKAGE range-v3 CONFIG QUIET # you can also set CONFIG here ) target_link_system_libraries(my_lib PUBLIC fmt::fmt + PRIVATE Microsoft.GSL::GSL + Qt6::Widgets range-v3::range-v3 ) package_project(TARGETS my_lib) -]] -macro(target_find_dependencies target) - set(_options) - set(_oneValueArgs) - set(_MultiValueArgs - PRIVATE - PUBLIC - INTERFACE - PRIVATE_CONFIG - PUBLIC_CONFIG - INTERFACE_CONFIG - ) - cmake_parse_arguments(args "${_options}" "${_oneValueArgs}" "${_MultiValueArgs}" ${ARGN}) +]=] +function(target_find_dependencies target) + set(unparsed_args ${ARGN}) + + set(type "") + set(package_name "") + set(find_package_args "") + set(simple_mode FALSE) + + macro(_parse_target_find_dependencies) + set(package_name "") + set(find_package_args "") + + while(unparsed_args) + list(POP_FRONT unparsed_args _current) + + if(_current MATCHES "^(PRIVATE|PUBLIC|INTERFACE)(_CONFIG)?$") # Parse an option section + # Set the option section type + set(type "${_current}") + + # Check mode for this option section + if(unparsed_args) + list(GET unparsed_args 0 _next) + + if(_next STREQUAL "PACKAGE") + set(simple_mode FALSE) + else() + set(simple_mode TRUE) + endif() + endif() + elseif(simple_mode) # Parse a simple option item + set(package_name "${_current}") + break() + else() # Parse a complex option item + # _current == "PACKAGE", so the next is the package_name + list(POP_FRONT unparsed_args package_name) + + while(unparsed_args) + list(POP_FRONT unparsed_args _find_package_arg) + + # Done if _find_package_arg belongs to next option item + if((_find_package_arg MATCHES "^(PRIVATE|PUBLIC|INTERFACE)(_CONFIG)?$") + OR(_find_package_arg STREQUAL "PACKAGE")) + list(PREPEND unparsed_args "${_find_package_arg}") + break() + endif() + + list(APPEND find_package_args "${_find_package_arg}") + endwhile() + break() + endif() + endwhile() + endmacro() + + macro(_add_dependency) + if(package_name) + if("CONFIG" IN_LIST find_package_args) + list(REMOVE_ITEM find_package_args "CONFIG") + + if(type MATCHES "^(PRIVATE|PUBLIC|INTERFACE)$") + set(type "${type}_CONFIG") + endif() + endif() - macro(_property_for property) - # Call find_package to all newly added dependencies - foreach(_Dependency IN LISTS args_${property}) - if(${property} MATCHES ".*CONFIG") - find_package(${_Dependency} CONFIG REQUIRED) + if(${type} MATCHES ".*CONFIG") + find_package(${package_name} CONFIG REQUIRED ${find_package_args}) else() - include(CMakeFindDependencyMacro) - find_dependency(${_Dependency}) + find_package(${package_name} REQUIRED ${find_package_args}) endif() - endforeach() - # Append to target property - set_or_append_target_property( - ${target} "PROJECT_OPTIONS_${property}_DEPENDENCIES" "${args_${property}}" - ) + # Append to target property + set_or_append_target_property( + ${target} "PROJECT_OPTIONS_${type}_DEPENDENCIES" "${package_name}" + ) + endif() endmacro() - _property_for(PRIVATE) - _property_for(PUBLIC) - _property_for(INTERFACE) - _property_for(PRIVATE_CONFIG) - _property_for(PUBLIC_CONFIG) - _property_for(INTERFACE_CONFIG) -endmacro() + while(unparsed_args) + _parse_target_find_dependencies() + _add_dependency() + endwhile() +endfunction() \ No newline at end of file From 91482315de659f0d95dbe453281995c51f765ac5 Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Sun, 28 Apr 2024 21:29:13 +0800 Subject: [PATCH 2/5] Add tests for target_find_dependencies --- tests/minimal/CMakeLists.txt | 8 +++++--- tests/myproj/CMakeLists.txt | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/minimal/CMakeLists.txt b/tests/minimal/CMakeLists.txt index c36d2370..5a0734b3 100644 --- a/tests/minimal/CMakeLists.txt +++ b/tests/minimal/CMakeLists.txt @@ -93,11 +93,13 @@ project_options( ) target_compile_features(project_options INTERFACE cxx_std_20) -find_package(Microsoft.GSL CONFIG REQUIRED) -find_package(fmt CONFIG REQUIRED) - add_executable(example) target_sources(example PRIVATE main.cpp) target_include_directories(example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(example PRIVATE project_options project_warnings) +target_find_dependencies(example + PUBLIC_CONFIG + Microsoft.GSL + fmt +) target_link_system_libraries(example PRIVATE Microsoft.GSL::GSL fmt::fmt-header-only) diff --git a/tests/myproj/CMakeLists.txt b/tests/myproj/CMakeLists.txt index 5c4b88e2..666839ff 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -95,12 +95,12 @@ project_options( add_executable(main ./src/main/main.cpp) target_link_libraries(main PRIVATE myproj_project_options myproj_project_warnings) -## dependencies -set(DEPENDENCIES_CONFIGURED fmt Eigen3 docopt) - -foreach(DEPENDENCY ${DEPENDENCIES_CONFIGURED}) - find_package(${DEPENDENCY} CONFIG REQUIRED) -endforeach() +target_find_dependencies(main + PUBLIC + PACKAGE fmt CONFIG + PACKAGE Eigen3 CONFIG + PACAKGE docopt CONFIG +) target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen) From b2bb3a9587493c1f9e7be8ccd5d68fad5c8e0db4 Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Sun, 28 Apr 2024 21:34:21 +0800 Subject: [PATCH 3/5] Replace `set_or_append_target_property` The official `set_property` contains supports the same behaviour when turning on the `APPEND` option. --- src/PackageProject.cmake | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index d52ed6c3..c69b8cde 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -319,19 +319,6 @@ function(package_project) include("${_ycm_SOURCE_DIR}/modules/AddUninstallTarget.cmake") endfunction() -function(set_or_append_target_property target property new_values) - get_target_property(_AllValues ${target} ${property}) - - if(NOT _AllValues) # If the property hasn't set - set(_AllValues "${new_values}") - else() - list(APPEND _AllValues ${new_values}) - endif() - list(REMOVE_DUPLICATES _AllValues) - - set_target_properties(${target} PROPERTIES ${property} "${_AllValues}") -endfunction() - #[[.rst: ``target_include_interface_directories`` @@ -378,7 +365,7 @@ function(target_include_interface_directories target) endif() # Append include_dir to target property PROJECT_OPTIONS_INTERFACE_DIRECTORIES - set_or_append_target_property(${target} "PROJECT_OPTIONS_INTERFACE_DIRECTORIES" ${include_dir}) + set_property(TARGET ${target} APPEND PROPERTY "PROJECT_OPTIONS_INTERFACE_DIRECTORIES" ${include_dir}) # Include the interface directory get_target_property(_HasSourceFiles ${target} SOURCES) @@ -531,10 +518,7 @@ function(target_find_dependencies target) find_package(${package_name} REQUIRED ${find_package_args}) endif() - # Append to target property - set_or_append_target_property( - ${target} "PROJECT_OPTIONS_${type}_DEPENDENCIES" "${package_name}" - ) + set_property(TARGET ${target} APPEND PROPERTY "PROJECT_OPTIONS_${type}_DEPENDENCIES" "${package_name}") endif() endmacro() From be90a2f0d36582115c173c36c545dcc64263db9b Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Sun, 28 Apr 2024 21:55:25 +0800 Subject: [PATCH 4/5] typo: PACAKGE -> PACAKGE --- 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 666839ff..d578b17f 100644 --- a/tests/myproj/CMakeLists.txt +++ b/tests/myproj/CMakeLists.txt @@ -99,7 +99,7 @@ target_find_dependencies(main PUBLIC PACKAGE fmt CONFIG PACKAGE Eigen3 CONFIG - PACAKGE docopt CONFIG + PACKAGE docopt CONFIG ) target_link_system_libraries(main PRIVATE fmt::fmt Eigen3::Eigen) From 29770b7d571262874021429c274f501d7b572297 Mon Sep 17 00:00:00 2001 From: FeignClaims Date: Mon, 29 Apr 2024 21:57:28 +0800 Subject: [PATCH 5/5] Install args of find_package correctly --- src/PackageProject.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PackageProject.cmake b/src/PackageProject.cmake index c69b8cde..eab21f37 100644 --- a/src/PackageProject.cmake +++ b/src/PackageProject.cmake @@ -518,7 +518,8 @@ function(target_find_dependencies target) find_package(${package_name} REQUIRED ${find_package_args}) endif() - set_property(TARGET ${target} APPEND PROPERTY "PROJECT_OPTIONS_${type}_DEPENDENCIES" "${package_name}") + list(JOIN find_package_args " " installation_args) + set_property(TARGET ${target} APPEND PROPERTY "PROJECT_OPTIONS_${type}_DEPENDENCIES" "${package_name} ${installation_args}") endif() endmacro()