find_package: Report why a candidate was rejected

Improve how find_package reports the list of candidate package
configuration files that were considered but rejected to include a
reason for rejection. For CPS in particular, this allows the user to
tell if a CPS file was rejected due to a version mismatch, missing
required components, or because the file could not be read.

While we do not try to report more detail for why cmPackageInfoReader
rejected a file, the possible reasons are usually easy enough to
distinguish:

- The file is so malformed that we cannot read a JSON object.
- The schema version is not a version that CMake understands.
- The root object does not contain a string named "name".
- The root object does not contain an object named "components".
- Prefix resolution failed.

Three of these can only result from a file that fails schema validation.
This commit is contained in:
Matthew Woehlke
2025-11-04 11:59:15 -05:00
committed by Brad King
parent 6ece8dee79
commit 6c2fc502b6
30 changed files with 224 additions and 24 deletions

View File

@@ -1789,7 +1789,8 @@ bool cmFindPackageCommand::HandlePackageMode(
for (ConfigFileInfo const& info :
cmMakeRange(this->ConsideredConfigs.cbegin(), duplicate_end)) {
e << " " << info.filename << ", version: " << info.version << '\n';
e << " " << info.filename << ", version: " << info.version
<< "\n " << info.message << '\n';
}
} else {
std::string requestedVersionString;
@@ -2927,9 +2928,10 @@ bool cmFindPackageCommand::FindConfigFile(std::string const& dir,
foundMode = cmFindPackageCommand::FoundMode(config.Type);
return true;
}
this->ConsideredPaths.emplace_back(file,
cmFindPackageCommand::FoundMode(type),
SearchResult::InsufficientVersion);
this->ConsideredPaths.emplace_back(
file, cmFindPackageCommand::FoundMode(type),
this->ConsideredConfigs.back().result,
this->ConsideredConfigs.back().message);
} else {
this->ConsideredPaths.emplace_back(
file, cmFindPackageCommand::FoundMode(type), SearchResult::NoExist);
@@ -2943,6 +2945,8 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file)
bool result = false; // by default, assume the version is not ok.
bool haveResult = false;
std::string version = "unknown";
std::string message;
SearchResult reason = SearchResult::InsufficientVersion;
// Get the file extension.
std::string::size_type pos = config_file.rfind('.');
@@ -2997,6 +3001,14 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file)
this->VersionMax, version);
}
}
if (!result) {
message =
cmStrCat("Version \""_s, version,
"\" (compatibility version \""_s, *compatVersion,
"\") is not compatible "
"with the version requested."_s);
}
} else {
// If no, compat_version is assumed to be exactly the actual
// version, so the result is whether the requested version is
@@ -3007,6 +3019,12 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file)
}
}
}
if (!result && message.empty()) {
message =
cmStrCat("Version \""_s, version,
"\" is not compatible with the version requested."_s);
}
}
if (result) {
@@ -3037,7 +3055,13 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file)
allComponents.end(),
std::back_inserter(missingComponents));
if (!missingComponents.empty()) {
bool const single = (missingComponents.size() == 1);
result = false;
message =
cmStrCat((single ? "Required component was not found: "_s
: "Required components were not found: "_s),
cmJoin(missingComponents, ", "_s), '.');
reason = SearchResult::InsufficientComponents;
}
if (result && hasVersion) {
@@ -3073,6 +3097,14 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file)
this->CpsAppendices = std::move(appendices);
this->RequiredComponents = std::move(requiredComponents);
}
} else if (reader) {
message =
cmStrCat("The file describes the package \""_s, reader->GetName(),
"\", which is not the requested package."_s);
reason = SearchResult::Ignored;
} else {
message = "The package description file could not be read.";
reason = SearchResult::Error;
}
} else {
// Get the filename without the .cmake extension.
@@ -3092,15 +3124,26 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file)
haveResult = true;
}
if (haveResult && !result) {
message =
"The version found is not compatible with the version requested.";
}
// If no version was requested a versionless package is acceptable.
if (!haveResult && this->Version.empty()) {
result = true;
}
}
if (result) {
reason = SearchResult::Acceptable;
}
ConfigFileInfo configFileInfo;
configFileInfo.filename = config_file;
configFileInfo.version = version;
configFileInfo.message = message;
configFileInfo.result = reason;
this->ConsideredConfigs.push_back(std::move(configFileInfo));
return result;
@@ -3738,8 +3781,14 @@ void cmFindPackageDebugState::WriteEvent(cmConfigureLog& log,
auto search_result =
[](cmFindPackageCommand::SearchResult type) -> std::string {
switch (type) {
case cmFindPackageCommand::SearchResult::Acceptable:
return "acceptable";
case cmFindPackageCommand::SearchResult::InsufficientVersion:
return "insufficient_version";
case cmFindPackageCommand::SearchResult::InsufficientComponents:
return "insufficient_components";
case cmFindPackageCommand::SearchResult::Error:
return "error";
case cmFindPackageCommand::SearchResult::NoExist:
return "no_exist";
case cmFindPackageCommand::SearchResult::Ignored:

View File

@@ -291,7 +291,10 @@ private:
enum class SearchResult
{
Acceptable,
InsufficientVersion,
InsufficientComponents,
Error,
NoExist,
Ignored,
NoConfigFile,
@@ -300,10 +303,11 @@ private:
struct ConsideredPath
{
ConsideredPath(std::string path, FoundPackageMode mode,
SearchResult reason)
SearchResult result, std::string message = {})
: Path(std::move(path))
, Mode(mode)
, Reason(reason)
, Reason(result)
, Message(std::move(message))
{
}
@@ -347,6 +351,8 @@ private:
{
std::string filename;
std::string version;
std::string message;
SearchResult result;
bool operator<(ConfigFileInfo const& rhs) const
{

View File

@@ -5,6 +5,7 @@ CMake Error at .*/Modules/CMakeFindDependencyMacro\.cmake:[0-9]+ \(find_package\
The following configuration files were considered but not accepted:
.*/Tests/RunCMake/find_dependency/share/cmake/Pack1/Pack1Config\.cmake, version: 1\.3
The version found is not compatible with the version requested\.
Call Stack \(most recent call first\):
[^

View File

@@ -5,6 +5,7 @@ CMake Error at .*/Modules/CMakeFindDependencyMacro\.cmake:[0-9]+ \(find_package\
The following configuration files were considered but not accepted:
.*/Tests/RunCMake/find_dependency/share/cmake/Pack1/Pack1Config\.cmake, version: 1\.3
The version found is not compatible with the version requested\.
Call Stack \(most recent call first\):
[^

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,12 @@
CMake Error at BadPrefix\.cmake:[0-9]+ \(find_package\):
Could not find a configuration file for package "BadPrefix" that is
compatible with requested version ""\.
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/[Bb]ad[Pp]refix\.cps, version: unknown
The package description file could not be read\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 4.0)
include(Setup.cmake)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
###############################################################################
# Test reporting when trying to read a .cps whose absolute prefix cannot be
# determined.
find_package(BadPrefix REQUIRED)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,12 @@
CMake Error at InvalidCps1\.cmake:[0-9]+ \(find_package\):
Could not find a configuration file for package "Empty" that is compatible
with requested version ""\.
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/[Ee]mpty\.cps, version: unknown
The package description file could not be read\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 4.0)
include(Setup.cmake)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
###############################################################################
# Test reporting when trying to read a .cps that is not valid JSON.
find_package(Empty REQUIRED)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,12 @@
CMake Error at InvalidCps2\.cmake:[0-9]+ \(find_package\):
Could not find a configuration file for package "NotAnObject" that is
compatible with requested version ""\.
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/[Nn]ot[Aa]n[Oo]bject\.cps, version: unknown
The package description file could not be read\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 4.0)
include(Setup.cmake)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
###############################################################################
# Test reporting when trying to read a .cps that is not a JSON object.
find_package(NotAnObject REQUIRED)

View File

@@ -5,7 +5,8 @@ CMake Error at MissingComponent\.cmake:[0-9]+ \(find_package\):
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0)+
]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0
Required component was not found: DoesNotExist\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -5,7 +5,8 @@ CMake Error in cps/[Tt]ransitive[Mm]issing[Cc][Pp][Ss]\.cps:
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0)+
]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0
Required component was not found: DoesNotExist\.)+
Call Stack \(most recent call first\):
MissingTransitiveComponentCPS\.cmake:[0-9]+ \(find_package\)

View File

@@ -5,13 +5,17 @@ CMake Error at MissingVersion1\.cmake:[0-9]+ \(find_package\):
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven
Version "1\.5\.0\+niven" is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams
Version "1\.4\.2\+adams" \(compatibility version "1\.3\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke
Version "1\.2\.3\+clarke" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov)+
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov
Version "1\.1\.0\+asimov" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -5,13 +5,17 @@ CMake Error at MissingVersion2\.cmake:[0-9]+ \(find_package\):
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven
Version "1\.5\.0\+niven" is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams
Version "1\.4\.2\+adams" \(compatibility version "1\.3\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke
Version "1\.2\.3\+clarke" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov)+
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov
Version "1\.1\.0\+asimov" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -29,6 +29,12 @@ function(run_cmake_build test)
endfunction()
# General failure tests
run_cmake(InvalidCps1)
run_cmake(InvalidCps2)
run_cmake(WrongName)
run_cmake(BadPrefix)
# Version-matching tests
run_cmake(ExactVersion)
run_cmake(CompatVersion)

View File

@@ -5,13 +5,17 @@ CMake Error at VersionLimit3\.cmake:[0-9]+ \(find_package\):
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven
Version "1\.5\.0\+niven" is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams
Version "1\.4\.2\+adams" \(compatibility version "1\.3\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke
Version "1\.2\.3\+clarke" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov)+
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov
Version "1\.1\.0\+asimov" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -5,13 +5,17 @@ CMake Error at VersionLimit4\.cmake:[0-9]+ \(find_package\):
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/[Ss]ample\.cps, version: 1\.5\.0\+niven
Version "1\.5\.0\+niven" is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/[Ss]ample\.cps, version: 1\.4\.2\+adams
Version "1\.4\.2\+adams" \(compatibility version "1\.3\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke)+(
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/[Ss]ample\.cps, version: 1\.2\.3\+clarke
Version "1\.2\.3\+clarke" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+(
[^
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov)+
]*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/[Ss]ample\.cps, version: 1\.1\.0\+asimov
Version "1\.1\.0\+asimov" \(compatibility version "1\.0\.0"\) is not compatible with the version requested\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,12 @@
CMake Error at WrongName\.cmake:[0-9]+ \(find_package\):
Could not find a configuration file for package "WrongName" that is
compatible with requested version ""\.
The following configuration files were considered but not accepted:
(
[^
]*/Tests/RunCMake/find_package-CPS/cps/[Ww]rong[Nn]ame\.cps, version: unknown
The file describes the package "Mismatch", which is not the requested package\.)+
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 4.0)
include(Setup.cmake)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
###############################################################################
# Test reporting when trying to read a .cps that is not for the package
# requested. This is somewhat unlikely to occur in practice, since the most
# likely scenario would be searching for e.g. "a-b" and finding an appendix to
# a package named "a". However, we're likely to fail prefix resolution in that
# case, unless the appendix superfluously provides a "prefix" or "cps_path",
# and reject the file before getting as far as the name check.
find_package(WrongName REQUIRED)

View File

@@ -0,0 +1,6 @@
{
"cps_version": "0.13",
"name": "BadPrefix",
"cps_path": "@prefix@/share/cps",
"components": {}
}

View File

@@ -0,0 +1,4 @@
[
"Answer",
42
]

View File

@@ -0,0 +1,6 @@
{
"cps_version": "0.13",
"name": "Mismatch",
"cps_path": "@prefix@/cps",
"components": {}
}

View File

@@ -273,6 +273,7 @@ events:(
path: "[^"]*/Tests/RunCMake/find_package/ConfigureLog/lib/cmake/VersionCheck-1.5/VersionCheckConfig.cmake"
mode: "config"
reason: "insufficient_version"
message: "The version found is not compatible with the version requested."
-
path: "[^"]*/Tests/RunCMake/find_package/ConfigureLog/lib/cmake/VersionCheck-1.5/versioncheck-config.cmake"
mode: "config"

View File

@@ -5,7 +5,9 @@
The following configuration files were considered but not accepted:
.*/Tests/RunCMake/find_package/VersionedA-[12]/VersionedAConfig\.cmake, version: [12]
The version found is not compatible with the version requested\.
.*/Tests/RunCMake/find_package/VersionedA-[12]/VersionedAConfig\.cmake, version: [12]
The version found is not compatible with the version requested\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -5,7 +5,9 @@
The following configuration files were considered but not accepted:
.*/Tests/RunCMake/find_package/VersionedA-[12]/VersionedAConfig\.cmake, version: [12]
The version found is not compatible with the version requested\.
.*/Tests/RunCMake/find_package/VersionedA-[12]/VersionedAConfig\.cmake, version: [12]
The version found is not compatible with the version requested\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$