Skip to content

Commit fc7d4e4

Browse files
committed
Automate find_dependency generation for installed packages
Added logic to introspect INTERFACE_LINK_LIBRARIES and generate appropriate find_dependency() calls in installed CMake package config files. Updated documentation to explain dependency handling, added a helper function in cpp-library-install.cmake, and modified the config template to include generated dependencies. This ensures downstream users automatically find and link required dependencies.
1 parent 49eaf58 commit fc7d4e4

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,46 @@ cmake --install build/default --prefix /opt/mylib
123123

124124
For information about using installed packages with `find_package()`, see the [CPM.cmake documentation](https://github.com/cpm-cmake/CPM.cmake) about [controlling how dependencies are found](https://github.com/cpm-cmake/CPM.cmake#cpm_use_local_packages).
125125

126+
#### Dependency Handling in Installed Packages
127+
128+
cpp-library automatically generates correct `find_dependency()` calls in the installed CMake package configuration files by introspecting your target's `INTERFACE_LINK_LIBRARIES`. This ensures downstream users can find and link all required dependencies.
129+
130+
**How it works:**
131+
132+
When you link dependencies to your target using `target_link_libraries()`, cpp-library analyzes these links during installation and generates appropriate `find_dependency()` calls. For example:
133+
134+
```cmake
135+
# In your library's CMakeLists.txt
136+
add_library(my-lib INTERFACE)
137+
138+
# Link dependencies - these will be automatically handled during installation
139+
target_link_libraries(my-lib INTERFACE
140+
stlab::copy-on-write # Internal CPM dependency
141+
stlab::enum-ops # Internal CPM dependency
142+
Threads::Threads # System dependency
143+
)
144+
```
145+
146+
When installed, the generated `my-libConfig.cmake` will include:
147+
148+
```cmake
149+
include(CMakeFindDependencyMacro)
150+
151+
# Find dependencies required by this package
152+
find_dependency(copy-on-write)
153+
find_dependency(enum-ops)
154+
find_dependency(Threads)
155+
156+
include("${CMAKE_CURRENT_LIST_DIR}/my-libTargets.cmake")
157+
```
158+
159+
**Supported dependency patterns:**
160+
161+
- **CPM/Internal dependencies** (`namespace::target`): Automatically mapped to their package names (e.g., `stlab::copy-on-write``find_dependency(copy-on-write)`)
162+
- **Threading** (`Threads::Threads`): Generates `find_dependency(Threads)`
163+
- **Qt libraries** (`Qt5::Core`, `Qt6::Widgets`): Generates `find_dependency(Qt5 COMPONENTS Core)` with proper components
164+
- **Generic packages** (`PackageName::Target`): Generates `find_dependency(PackageName)`
165+
126166
### Updating cpp-library
127167

128168
To update to the latest version of cpp-library in your project:

cmake/cpp-library-install.cmake

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,58 @@
1212
include(GNUInstallDirs)
1313
include(CMakePackageConfigHelpers)
1414

15+
# Generates find_dependency() calls for target's INTERFACE link libraries
16+
# - Precondition: TARGET_NAME specifies existing target with INTERFACE_LINK_LIBRARIES
17+
# - Postcondition: OUTPUT_VAR contains newline-separated find_dependency() calls for public dependencies
18+
# - Handles common patterns: namespace::target from CPM, Qt5/Qt6::Component, Threads::Threads, etc.
19+
function(_cpp_library_generate_dependencies OUTPUT_VAR TARGET_NAME NAMESPACE)
20+
get_target_property(LINK_LIBS ${TARGET_NAME} INTERFACE_LINK_LIBRARIES)
21+
22+
if(NOT LINK_LIBS)
23+
set(${OUTPUT_VAR} "" PARENT_SCOPE)
24+
return()
25+
endif()
26+
27+
set(DEPENDENCY_LIST "")
28+
29+
foreach(LIB IN LISTS LINK_LIBS)
30+
# Skip generator expressions (typically BUILD_INTERFACE dependencies)
31+
if(LIB MATCHES "^\\$<")
32+
continue()
33+
endif()
34+
35+
# Parse namespaced target: PackageName::Component
36+
if(LIB MATCHES "^([^:]+)::(.+)$")
37+
set(PKG_NAME "${CMAKE_MATCH_1}")
38+
set(COMPONENT "${CMAKE_MATCH_2}")
39+
40+
# Determine find_dependency() call based on package pattern
41+
if(PKG_NAME STREQUAL NAMESPACE)
42+
# Internal dependency: use component as package name (e.g., stlab::copy-on-write → copy-on-write)
43+
list(APPEND DEPENDENCY_LIST "find_dependency(${COMPONENT})")
44+
elseif(PKG_NAME STREQUAL "Threads")
45+
list(APPEND DEPENDENCY_LIST "find_dependency(Threads)")
46+
elseif(PKG_NAME MATCHES "^Qt[56]$")
47+
# Qt with component (e.g., Qt5::Core → find_dependency(Qt5 COMPONENTS Core))
48+
list(APPEND DEPENDENCY_LIST "find_dependency(${PKG_NAME} COMPONENTS ${COMPONENT})")
49+
else()
50+
# Generic package (e.g., libdispatch::libdispatch → libdispatch)
51+
list(APPEND DEPENDENCY_LIST "find_dependency(${PKG_NAME})")
52+
endif()
53+
endif()
54+
endforeach()
55+
56+
# Remove duplicates and convert to newline-separated string
57+
if(DEPENDENCY_LIST)
58+
list(REMOVE_DUPLICATES DEPENDENCY_LIST)
59+
list(JOIN DEPENDENCY_LIST "\n" DEPENDENCY_LINES)
60+
else()
61+
set(DEPENDENCY_LINES "")
62+
endif()
63+
64+
set(${OUTPUT_VAR} "${DEPENDENCY_LINES}" PARENT_SCOPE)
65+
endfunction()
66+
1567
# Configures CMake install rules for library target and package config files.
1668
# - Precondition: NAME, PACKAGE_NAME, VERSION, and NAMESPACE specified; target NAME exists
1769
# - Postcondition: install rules created for target, config files, and export with NAMESPACE:: prefix
@@ -65,6 +117,9 @@ function(_cpp_library_setup_install)
65117
)
66118
endif()
67119

120+
# Generate find_dependency() calls for package dependencies
121+
_cpp_library_generate_dependencies(PACKAGE_DEPENDENCIES ${ARG_NAME} ${ARG_NAMESPACE})
122+
68123
# Generate package version file
69124
# Uses SameMajorVersion compatibility (e.g., 2.1.0 is compatible with 2.0.0)
70125
write_basic_package_version_file(
@@ -74,6 +129,7 @@ function(_cpp_library_setup_install)
74129
)
75130

76131
# Generate package config file from template
132+
# PACKAGE_DEPENDENCIES will be substituted via @PACKAGE_DEPENDENCIES@
77133
configure_file(
78134
"${CPP_LIBRARY_ROOT}/templates/Config.cmake.in"
79135
"${CMAKE_CURRENT_BINARY_DIR}/${ARG_PACKAGE_NAME}Config.cmake"

templates/Config.cmake.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33

44
include(CMakeFindDependencyMacro)
55

6-
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
6+
# Find dependencies required by this package
7+
@PACKAGE_DEPENDENCIES@
8+
9+
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")

0 commit comments

Comments
 (0)