Skip to content

Commit d85c6cd

Browse files
committed
Handle QUIET dependencies in dependency provider
Improves the cpp-library dependency provider to correctly filter out dependencies from find_package() calls with the QUIET flag if the package was not found, preventing phantom dependencies in generated Config files. Adds a verification function for QUIET dependencies, updates tests to cover these cases, and documents the behavior and best practices in the integration example.
1 parent 14e9674 commit d85c6cd

4 files changed

Lines changed: 141 additions & 9 deletions

File tree

cmake/cpp-library-dependency-provider.cmake

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,21 @@ if(CMAKE_VERSION VERSION_LESS "3.24")
3131
endif()
3232

3333
# Check if provider is already installed (avoid double-installation)
34+
# Skip this check in test mode to allow function definitions to be loaded
3435
get_property(_CPP_LIBRARY_PROVIDER_INSTALLED GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED)
35-
if(_CPP_LIBRARY_PROVIDER_INSTALLED)
36+
if(_CPP_LIBRARY_PROVIDER_INSTALLED AND NOT CPP_LIBRARY_TEST_MODE)
3637
return()
3738
endif()
3839

3940
# Define all functions BEFORE installing the provider
4041
# The dependency provider implementation
4142
# This function is called before every find_package() and FetchContent_MakeAvailable()
4243
# It tracks dependency information; CMake automatically falls back to default behavior after return
44+
#
45+
# QUIET Dependency Handling:
46+
# When find_package() is called with QUIET, we track it tentatively, then use cmake_language(DEFER)
47+
# to verify after find_package() completes whether the package was found. If not found, we remove
48+
# the tracking to prevent phantom dependencies in the generated Config.cmake.
4349
function(_cpp_library_dependency_provider method)
4450
if(method STREQUAL "FIND_PACKAGE")
4551
_cpp_library_track_find_package(${ARGN})
@@ -53,6 +59,7 @@ endfunction()
5359

5460
# Track a find_package() call
5561
# Records: package name, version, components, and full call syntax
62+
# For QUIET packages, defers verification until after find_package() completes
5663
function(_cpp_library_track_find_package package_name)
5764
# Parse find_package arguments
5865
set(options QUIET REQUIRED NO_MODULE CONFIG)
@@ -171,6 +178,46 @@ function(_cpp_library_track_find_package package_name)
171178
endif()
172179

173180
message(DEBUG "cpp-library: Tracked find_package(${package_name}) → find_dependency(${FIND_DEP_CALL})")
181+
182+
# For QUIET packages, defer a check to remove the tracking if the package wasn't found
183+
# This prevents phantom dependencies from QUIET find_package() calls that fail
184+
if(FP_QUIET)
185+
cmake_language(DEFER CALL _cpp_library_verify_quiet_dependency "${package_name}")
186+
endif()
187+
endfunction()
188+
189+
# Verify that a QUIET find_package() call actually found the package
190+
# Called via cmake_language(DEFER) after find_package() completes
191+
# Removes the tracking if the package wasn't found (prevents phantom dependencies)
192+
function(_cpp_library_verify_quiet_dependency package_name)
193+
# Check if the package was found using various possible _FOUND variable names
194+
# CMake allows packages to set <PackageName>_FOUND or <PACKAGENAME>_FOUND
195+
set(FOUND FALSE)
196+
197+
if(DEFINED ${package_name}_FOUND AND ${package_name}_FOUND)
198+
set(FOUND TRUE)
199+
else()
200+
# Try uppercase version
201+
string(TOUPPER "${package_name}" package_upper)
202+
if(DEFINED ${package_upper}_FOUND AND ${package_upper}_FOUND)
203+
set(FOUND TRUE)
204+
endif()
205+
endif()
206+
207+
# If not found, remove from tracking
208+
if(NOT FOUND)
209+
# Remove from the tracked dependency
210+
set_property(GLOBAL PROPERTY "_CPP_LIBRARY_TRACKED_DEP_${package_name}" "")
211+
212+
# Remove from the list of all tracked dependencies
213+
get_property(ALL_DEPS GLOBAL PROPERTY _CPP_LIBRARY_ALL_TRACKED_DEPS)
214+
list(REMOVE_ITEM ALL_DEPS "${package_name}")
215+
set_property(GLOBAL PROPERTY _CPP_LIBRARY_ALL_TRACKED_DEPS "${ALL_DEPS}")
216+
217+
message(DEBUG "cpp-library: Removed QUIET dependency ${package_name} (not found)")
218+
else()
219+
message(DEBUG "cpp-library: Verified QUIET dependency ${package_name} (found)")
220+
endif()
174221
endfunction()
175222

176223
# Track a FetchContent_MakeAvailable() call
@@ -231,13 +278,16 @@ function(_cpp_library_get_all_tracked_deps OUTPUT_VAR)
231278
endfunction()
232279

233280
# Now install the dependency provider (after all functions are defined)
234-
set_property(GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED TRUE)
281+
# Only install if not already marked as installed (allows tests to skip installation)
282+
if(NOT _CPP_LIBRARY_PROVIDER_INSTALLED)
283+
set_property(GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED TRUE)
235284

236-
cmake_language(SET_DEPENDENCY_PROVIDER _cpp_library_dependency_provider
237-
SUPPORTED_METHODS
238-
FIND_PACKAGE
239-
FETCHCONTENT_MAKEAVAILABLE_SERIAL
240-
)
285+
cmake_language(SET_DEPENDENCY_PROVIDER _cpp_library_dependency_provider
286+
SUPPORTED_METHODS
287+
FIND_PACKAGE
288+
FETCHCONTENT_MAKEAVAILABLE_SERIAL
289+
)
241290

242-
message(STATUS "cpp-library: Dependency tracking enabled")
291+
message(STATUS "cpp-library: Dependency tracking enabled")
292+
endif()
243293

tests/install/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66

77
cmake_minimum_required(VERSION 3.20)
88

9-
# Include the module we're testing
9+
# Include the modules we're testing
1010
include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/cpp-library-install.cmake)
1111

12+
# Include dependency provider functions in test mode
13+
# This loads the function definitions without installing the provider
14+
set(CPP_LIBRARY_TEST_MODE TRUE)
15+
set_property(GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED TRUE)
16+
include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/cpp-library-dependency-provider.cmake)
17+
1218
# Test counter
1319
set(TEST_COUNT 0)
1420
set(TEST_PASSED 0)

tests/install/test_dependency_provider.cmake

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,48 @@ mock_target_links(test29_target "MyPkg::MyPkg")
131131
_cpp_library_generate_dependencies(RESULT test29_target "mylib")
132132
verify_output("${RESULT}" "find_dependency(MyPkg 1.0.0 CONFIG)" "Test 29")
133133

134+
# Test 30: QUIET dependency that was not found should be removed
135+
run_test("QUIET dependency not found - should be removed")
136+
# Simulate provider tracking a QUIET find_package() that failed
137+
set_property(GLOBAL PROPERTY "_CPP_LIBRARY_TRACKED_DEP_Qt5" "Qt5 5.15 COMPONENTS Core")
138+
set_property(GLOBAL APPEND PROPERTY _CPP_LIBRARY_ALL_TRACKED_DEPS "Qt5")
139+
set_property(GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED TRUE)
140+
# Simulate that Qt5 was NOT found
141+
set(Qt5_FOUND FALSE)
142+
# Call the verification function that would normally be deferred
143+
_cpp_library_verify_quiet_dependency("Qt5")
144+
# Now try to generate dependencies - Qt5 should NOT appear
145+
mock_target_links(test30_target "Threads::Threads")
146+
_cpp_library_generate_dependencies(RESULT test30_target "mylib")
147+
verify_output("${RESULT}" "find_dependency(Threads)" "Test 30")
148+
149+
# Test 31: QUIET dependency that was found should be kept
150+
run_test("QUIET dependency found - should be kept")
151+
# Simulate provider tracking a QUIET find_package() that succeeded
152+
set_property(GLOBAL PROPERTY "_CPP_LIBRARY_TRACKED_DEP_OpenSSL" "OpenSSL 1.1.1")
153+
set_property(GLOBAL APPEND PROPERTY _CPP_LIBRARY_ALL_TRACKED_DEPS "OpenSSL")
154+
set_property(GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED TRUE)
155+
# Simulate that OpenSSL WAS found
156+
set(OpenSSL_FOUND TRUE)
157+
# Call the verification function
158+
_cpp_library_verify_quiet_dependency("OpenSSL")
159+
# Now generate dependencies - OpenSSL SHOULD appear
160+
mock_target_links(test31_target "OpenSSL::SSL")
161+
_cpp_library_generate_dependencies(RESULT test31_target "mylib")
162+
verify_output("${RESULT}" "find_dependency(OpenSSL 1.1.1)" "Test 31")
163+
164+
# Test 32: QUIET dependency with uppercase _FOUND variable
165+
run_test("QUIET dependency with uppercase _FOUND")
166+
# Simulate provider tracking a QUIET find_package()
167+
set_property(GLOBAL PROPERTY "_CPP_LIBRARY_TRACKED_DEP_ZLIB" "ZLIB")
168+
set_property(GLOBAL APPEND PROPERTY _CPP_LIBRARY_ALL_TRACKED_DEPS "ZLIB")
169+
set_property(GLOBAL PROPERTY _CPP_LIBRARY_PROVIDER_INSTALLED TRUE)
170+
# Some packages set UPPERCASE_FOUND instead of PackageName_FOUND
171+
set(ZLIB_FOUND TRUE)
172+
# Call the verification function
173+
_cpp_library_verify_quiet_dependency("ZLIB")
174+
# ZLIB should be kept
175+
mock_target_links(test32_target "ZLIB::ZLIB")
176+
_cpp_library_generate_dependencies(RESULT test32_target "mylib")
177+
verify_output("${RESULT}" "find_dependency(ZLIB)" "Test 32")
178+

tests/install/test_integration_example.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,37 @@ If you need to use CMake 3.20-3.23, use an older version of cpp-library that sup
7373

7474
## Handling Special Cases
7575

76+
### QUIET Dependencies (Conditional/Optional Packages)
77+
78+
When you use `find_package()` with the QUIET flag, cpp-library automatically filters out dependencies that weren't found. This prevents phantom dependencies in your Config file:
79+
80+
```cmake
81+
# This will only be included in the Config if Qt5 is actually found
82+
find_package(Qt5 QUIET COMPONENTS Core)
83+
84+
if(Qt5_FOUND)
85+
target_link_libraries(my-library INTERFACE Qt5::Core)
86+
endif()
87+
```
88+
89+
**Best Practice for Conditional Dependencies:**
90+
91+
```cmake
92+
# Option 1: Use if() to conditionally search (recommended)
93+
if(MYLIB_FEATURE_X)
94+
find_package(Qt5 COMPONENTS Core) # Only searched if feature enabled
95+
target_link_libraries(my-library INTERFACE Qt5::Core)
96+
endif()
97+
98+
# Option 2: Use QUIET and check _FOUND (automatic filtering)
99+
find_package(Qt5 QUIET COMPONENTS Core)
100+
if(Qt5_FOUND)
101+
target_link_libraries(my-library INTERFACE Qt5::Core)
102+
endif()
103+
```
104+
105+
Both approaches work correctly with cpp-library's dependency tracking.
106+
76107
### Non-namespaced Targets
77108

78109
Non-namespaced targets require explicit mapping (both with and without provider):

0 commit comments

Comments
 (0)