1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

install(PACKAGE_INFO): Add version and location to package dependencies

Refactor `cmFindPackageStack` to track additional metadata
about <package> found. This includes two new fields,
`Version` and `Location` which correspond to package version and path.
The remaining package information will be implemented in a later commit
This commit is contained in:
Taylor Sasser
2025-07-31 12:05:32 -04:00
parent 43a86ba605
commit ae373e93fb
23 changed files with 277 additions and 24 deletions

View File

@@ -6,9 +6,10 @@ EXPORT_FIND_PACKAGE_NAME
Experimental. Gated by ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES``. Experimental. Gated by ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES``.
Control the package name associated with a dependency target when exporting a Control the package name associated with a dependency target when exporting a
:command:`find_dependency` call in :command:`install(EXPORT)` or :command:`find_dependency` call in :command:`install(PACKAGE_INFO)`,
:command:`export(PACKAGE_INFO)`, :command:`install(EXPORT)` or
:command:`export(EXPORT)`. This can be used to assign a package name to a :command:`export(EXPORT)`. This can be used to assign a package name to a
package that is built by CMake and exported, or to override the package in the package that is built by CMake and exported, or a package that was provided by
:command:`find_package` call that created the target. :module:`FetchContent`.
This property is initialized by :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME`. This property is initialized by :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME`.

View File

@@ -5,9 +5,11 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <set> #include <set>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <cm/optional>
#include <cm/string_view> #include <cm/string_view>
#include <cmext/algorithm> #include <cmext/algorithm>
#include <cmext/string_view> #include <cmext/string_view>
@@ -27,7 +29,6 @@
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmTarget.h" #include "cmTarget.h"
#include "cmValue.h"
static std::string const kCPS_VERSION_STR = "0.13.0"; static std::string const kCPS_VERSION_STR = "0.13.0";
@@ -146,15 +147,25 @@ void cmExportPackageInfoGenerator::GeneratePackageRequires(
auto data = Json::Value{ Json::objectValue }; auto data = Json::Value{ Json::objectValue };
// Add required components. // Add required components.
if (!requirement.second.empty()) { if (!requirement.second.Components.empty()) {
auto components = Json::Value{ Json::arrayValue }; auto components = Json::Value{ Json::arrayValue };
for (std::string const& component : requirement.second) { for (std::string const& component : requirement.second.Components) {
components.append(component); components.append(component);
} }
data["components"] = components; data["components"] = components;
} }
// TODO: version, hint // Add additional dependency information.
if (requirement.second.Directory) {
auto hints = Json::Value{ Json::arrayValue };
hints.append(*requirement.second.Directory);
data["hints"] = hints;
}
if (requirement.second.Version) {
data["version"] = *requirement.second.Version;
}
requirements[requirement.first] = data; requirements[requirement.first] = data;
} }
} }
@@ -283,16 +294,24 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
if (linkedTarget->IsImported()) { if (linkedTarget->IsImported()) {
// Target is imported from a found package. // Target is imported from a found package.
auto pkgName = [linkedTarget]() -> std::string { using Package = cm::optional<std::pair<std::string, cmPackageInformation>>;
auto const& pkgStack = linkedTarget->Target->GetFindPackageStack(); auto pkgInfo = [](cmTarget* t) -> Package {
cmFindPackageStack pkgStack = t->GetFindPackageStack();
if (!pkgStack.Empty()) { if (!pkgStack.Empty()) {
return pkgStack.Top().Name; return std::make_pair(pkgStack.Top().Name, pkgStack.Top().PackageInfo);
} }
return linkedTarget->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME"); cmPackageInformation package;
}(); std::string const pkgName =
t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME");
if (pkgName.empty()) {
return cm::nullopt;
}
if (pkgName.empty()) { return std::make_pair(pkgName, package);
}(linkedTarget->Target);
if (!pkgInfo) {
target->Makefile->IssueMessage( target->Makefile->IssueMessage(
MessageType::FATAL_ERROR, MessageType::FATAL_ERROR,
cmStrCat("Target \"", target->GetName(), cmStrCat("Target \"", target->GetName(),
@@ -301,6 +320,8 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
return false; return false;
} }
std::string const& pkgName = pkgInfo->first;
auto const& prefix = cmStrCat(pkgName, "::"); auto const& prefix = cmStrCat(pkgName, "::");
if (!cmHasPrefix(linkedName, prefix)) { if (!cmHasPrefix(linkedName, prefix)) {
target->Makefile->IssueMessage( target->Makefile->IssueMessage(
@@ -314,8 +335,9 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
std::string component = linkedName.substr(prefix.length()); std::string component = linkedName.substr(prefix.length());
this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component)); this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
// TODO: Record package version, hint. cmPackageInformation& req =
this->Requirements[pkgName].emplace(std::move(component)); this->Requirements.insert(std::move(*pkgInfo)).first->second;
req.Components.emplace(std::move(component));
return true; return true;
} }
@@ -339,7 +361,7 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
this->LinkTargets.emplace(linkedName, cmStrCat(':', component)); this->LinkTargets.emplace(linkedName, cmStrCat(':', component));
} else { } else {
this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component)); this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
this->Requirements[pkgName].emplace(std::move(component)); this->Requirements[pkgName].Components.emplace(std::move(component));
} }
return true; return true;
} }

View File

@@ -6,13 +6,13 @@
#include <iosfwd> #include <iosfwd>
#include <map> #include <map>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cm/string_view> #include <cm/string_view>
#include "cmExportFileGenerator.h" #include "cmExportFileGenerator.h"
#include "cmFindPackageStack.h"
#include "cmStateTypes.h" #include "cmStateTypes.h"
namespace Json { namespace Json {
@@ -112,9 +112,10 @@ private:
std::string const PackageWebsite; std::string const PackageWebsite;
std::string const PackageLicense; std::string const PackageLicense;
std::string const DefaultLicense; std::string const DefaultLicense;
std::vector<std::string> DefaultTargets; std::vector<std::string> DefaultTargets;
std::vector<std::string> DefaultConfigurations; std::vector<std::string> DefaultConfigurations;
std::map<std::string, std::string> LinkTargets; std::map<std::string, std::string> LinkTargets;
std::map<std::string, std::set<std::string>> Requirements; std::map<std::string, cmPackageInformation> Requirements;
}; };

View File

@@ -1224,6 +1224,8 @@ bool cmFindPackageCommand::FindPackage(
SetRestoreFindDefinitions setRestoreFindDefinitions(*this); SetRestoreFindDefinitions setRestoreFindDefinitions(*this);
cmFindPackageStackRAII findPackageStackRAII(this->Makefile, this->Name); cmFindPackageStackRAII findPackageStackRAII(this->Makefile, this->Name);
findPackageStackRAII.BindTop(this->CurrentPackageInfo);
// See if we have been told to delegate to FetchContent or some other // See if we have been told to delegate to FetchContent or some other
// redirected config package first. We have to check all names that // redirected config package first. We have to check all names that
// find_package() may look for, but only need to invoke the override for the // find_package() may look for, but only need to invoke the override for the
@@ -1270,6 +1272,8 @@ bool cmFindPackageCommand::FindPackage(
this->Names.clear(); this->Names.clear();
this->Names.emplace_back(overrideName); // Force finding this one this->Names.emplace_back(overrideName); // Force finding this one
this->Variable = cmStrCat(this->Name, "_DIR"); this->Variable = cmStrCat(this->Name, "_DIR");
this->CurrentPackageInfo->Directory = redirectsDir;
this->CurrentPackageInfo->Version = this->VersionFound;
this->SetConfigDirCacheVariable(redirectsDir); this->SetConfigDirCacheVariable(redirectsDir);
break; break;
} }
@@ -1342,7 +1346,6 @@ bool cmFindPackageCommand::FindPackage(
} }
this->AppendSuccessInformation(); this->AppendSuccessInformation();
return loadedPackage; return loadedPackage;
} }
@@ -1971,6 +1974,8 @@ bool cmFindPackageCommand::FindConfig()
std::string init; std::string init;
if (found) { if (found) {
init = cmSystemTools::GetFilenamePath(this->FileFound); init = cmSystemTools::GetFilenamePath(this->FileFound);
this->CurrentPackageInfo->Directory = init;
this->CurrentPackageInfo->Version = this->VersionFound;
} else { } else {
init = this->Variable + "-NOTFOUND"; init = this->Variable + "-NOTFOUND";
} }

View File

@@ -37,6 +37,7 @@ class cmExecutionStatus;
class cmMakefile; class cmMakefile;
class cmPackageState; class cmPackageState;
class cmSearchPath; class cmSearchPath;
class cmPackageInformation;
/** \class cmFindPackageCommand /** \class cmFindPackageCommand
* \brief Load settings from an external project. * \brief Load settings from an external project.
@@ -286,6 +287,8 @@ private:
std::set<std::string> OptionalComponents; std::set<std::string> OptionalComponents;
std::set<std::string> RequiredTargets; std::set<std::string> RequiredTargets;
std::string DebugBuffer; std::string DebugBuffer;
cmPackageInformation* CurrentPackageInfo;
enum class SearchResult enum class SearchResult
{ {
InsufficientVersion, InsufficientVersion,

View File

@@ -5,19 +5,41 @@
#include "cmConfigure.h" // IWYU pragma: keep #include "cmConfigure.h" // IWYU pragma: keep
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include <cm/optional>
#include "cmStack.h" #include "cmStack.h"
class cmMakefile; class cmMakefile;
/**
* This data represents the actual contents of find_package
* <PACKAGE>-Config.cmake or <PACKAGE>.cps file, and not what is passed
* to the find_package command. They can be the same, but it is not guaranteed.
*/
class cmPackageInformation
{
public:
cm::optional<std::string> Directory;
cm::optional<std::string> Version;
cm::optional<std::string> Description;
cm::optional<std::string> License;
cm::optional<std::string> Website;
cm::optional<std::string> PackageUrl;
std::set<std::string> Components;
};
/** /**
* Represents one call to find_package. * Represents one call to find_package.
*/ */
class cmFindPackageCall class cmFindPackageCall
{ {
public: public:
std::string Name; std::string const Name;
cmPackageInformation PackageInfo;
unsigned int Index; unsigned int Index;
}; };
@@ -28,7 +50,7 @@ public:
class cmFindPackageStackRAII class cmFindPackageStackRAII
{ {
cmMakefile* Makefile; cmMakefile* Makefile;
cmFindPackageCall** Value = nullptr; cmPackageInformation** Value = nullptr;
public: public:
cmFindPackageStackRAII(cmMakefile* mf, std::string const& pkg); cmFindPackageStackRAII(cmMakefile* mf, std::string const& pkg);
@@ -40,7 +62,7 @@ public:
/** Get a mutable pointer to the top of the stack. /** Get a mutable pointer to the top of the stack.
The pointer is invalidated if BindTop is called again or when the The pointer is invalidated if BindTop is called again or when the
cmFindPackageStackRAII goes out of scope. */ cmFindPackageStackRAII goes out of scope. */
void BindTop(cmFindPackageCall*& value); void BindTop(cmPackageInformation*& value);
}; };
/** /**

View File

@@ -4238,18 +4238,19 @@ cmFindPackageStackRAII::cmFindPackageStackRAII(cmMakefile* mf,
this->Makefile->FindPackageStack = this->Makefile->FindPackageStack =
this->Makefile->FindPackageStack.Push(cmFindPackageCall{ this->Makefile->FindPackageStack.Push(cmFindPackageCall{
name, name,
cmPackageInformation(),
this->Makefile->FindPackageStackNextIndex, this->Makefile->FindPackageStackNextIndex,
}); });
this->Makefile->FindPackageStackNextIndex++; this->Makefile->FindPackageStackNextIndex++;
} }
void cmFindPackageStackRAII::BindTop(cmFindPackageCall*& value) void cmFindPackageStackRAII::BindTop(cmPackageInformation*& value)
{ {
if (this->Value) { if (this->Value) {
*this->Value = nullptr; *this->Value = nullptr;
} }
this->Value = &value; this->Value = &value;
value = &this->Makefile->FindPackageStack.cmStack::Top(); value = &this->Makefile->FindPackageStack.cmStack::Top().PackageInfo;
} }
cmFindPackageStackRAII::~cmFindPackageStackRAII() cmFindPackageStackRAII::~cmFindPackageStackRAII()

View File

@@ -0,0 +1,15 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/DependencyVersionCMake-build")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_array("${content}" 1 "requires" "bar" "components")
expect_value("${content}" "bar" "requires" "bar" "components" 0)
expect_value("${content}" "1.3.5" "requires" "bar" "version")
expect_array("${content}" 1 "requires" "bar" "hints")
expect_value("${content}" "${CMAKE_CURRENT_LIST_DIR}/config" "requires" "bar" "hints" 0)
string(JSON component GET "${content}" "components" "foo")
expect_array("${component}" 1 "requires")
expect_value("${component}" "bar:bar" "requires" 0)

View File

@@ -0,0 +1,11 @@
find_package(
bar 1.3.4 REQUIRED CONFIG
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}/config
)
add_library(foo INTERFACE)
target_link_libraries(foo INTERFACE bar::bar)
install(TARGETS foo EXPORT foo DESTINATION .)
export(EXPORT foo PACKAGE_INFO foo)

View File

@@ -0,0 +1,15 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/DependencyVersionCps-build/")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_array("${content}" 1 "requires" "baz" "components")
expect_value("${content}" "baz" "requires" "baz" "components" 0)
expect_value("${content}" "1.3.5" "requires" "baz" "version")
expect_array("${content}" 1 "requires" "baz" "hints")
expect_value("${content}" "${CMAKE_CURRENT_LIST_DIR}/cps" "requires" "baz" "hints" 0)
string(JSON component GET "${content}" "components" "foo")
expect_array("${component}" 1 "requires")
expect_value("${component}" "baz:baz" "requires" 0)

View File

@@ -0,0 +1,11 @@
find_package(
baz 1.3.4 REQUIRED
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
add_library(foo INTERFACE)
target_link_libraries(foo INTERFACE baz::baz)
install(TARGETS foo EXPORT foo DESTINATION .)
export(EXPORT foo PACKAGE_INFO foo)

View File

@@ -8,6 +8,7 @@ run_cmake(ExperimentalWarning)
set(RunCMake_TEST_OPTIONS set(RunCMake_TEST_OPTIONS
-Wno-dev -Wno-dev
"-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e" "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e"
"-DCMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES:STRING=e82e467b-f997-4464-8ace-b00808fff261"
) )
# Test incorrect usage # Test incorrect usage
@@ -43,3 +44,5 @@ run_cmake(LinkOnly)
run_cmake(Config) run_cmake(Config)
run_cmake(EmptyConfig) run_cmake(EmptyConfig)
run_cmake(FileSetHeaders) run_cmake(FileSetHeaders)
run_cmake(DependencyVersionCMake)
run_cmake(DependencyVersionCps)

View File

@@ -0,0 +1,29 @@
set(PACKAGE_VERSION "1.3.5")
if (PACKAGE_FIND_VERSION_RANGE)
# Check for a version range
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_RANGE_MIN)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
if (PACKAGE_FIND_VERSION_RANGE_MAX)
if (PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_RANGE_MAX)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
endif ()
elseif (PACKAGE_FIND_VERSION)
# Check for a specific version or minimum version
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if (PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif ()
endif ()
endif ()

View File

@@ -0,0 +1 @@
add_library(bar::bar INTERFACE IMPORTED)

View File

@@ -0,0 +1,14 @@
{
"components" :
{
"baz" :
{
"type" : "interface"
}
},
"cps_path" : "@prefix@/cps",
"cps_version" : "0.13.0",
"compat_version": "1.0.0",
"name" : "baz",
"version": "1.3.5"
}

View File

@@ -0,0 +1,15 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/DependencyVersionCMake-build/CMakeFiles/Export/5058f1af8388633f609cadb75a75dc9d")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_array("${content}" 1 "requires" "bar" "components")
expect_value("${content}" "bar" "requires" "bar" "components" 0)
expect_value("${content}" "1.3.5" "requires" "bar" "version")
expect_array("${content}" 1 "requires" "bar" "hints")
expect_value("${content}" "${CMAKE_CURRENT_LIST_DIR}/config" "requires" "bar" "hints" 0)
string(JSON component GET "${content}" "components" "foo")
expect_array("${component}" 1 "requires")
expect_value("${component}" "bar:bar" "requires" 0)

View File

@@ -0,0 +1,11 @@
find_package(
bar 1.3.4 REQUIRED CONFIG
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}/config
)
add_library(foo INTERFACE)
target_link_libraries(foo INTERFACE bar::bar)
install(TARGETS foo EXPORT foo)
install(PACKAGE_INFO foo EXPORT foo DESTINATION .)

View File

@@ -0,0 +1,15 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/DependencyVersionCps-build/CMakeFiles/Export/5058f1af8388633f609cadb75a75dc9d")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_array("${content}" 1 "requires" "baz" "components")
expect_value("${content}" "baz" "requires" "baz" "components" 0)
expect_value("${content}" "1.3.5" "requires" "baz" "version")
expect_array("${content}" 1 "requires" "baz" "hints")
expect_value("${content}" "${CMAKE_CURRENT_LIST_DIR}/cps" "requires" "baz" "hints" 0)
string(JSON component GET "${content}" "components" "foo")
expect_array("${component}" 1 "requires")
expect_value("${component}" "baz:baz" "requires" 0)

View File

@@ -0,0 +1,11 @@
find_package(
baz 1.3.4 REQUIRED
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
add_library(foo INTERFACE)
target_link_libraries(foo INTERFACE baz::baz)
install(TARGETS foo EXPORT foo)
install(PACKAGE_INFO foo EXPORT foo DESTINATION .)

View File

@@ -8,6 +8,7 @@ run_cmake(ExperimentalWarning)
set(RunCMake_TEST_OPTIONS set(RunCMake_TEST_OPTIONS
-Wno-dev -Wno-dev
"-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e" "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e"
"-DCMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES:STRING=e82e467b-f997-4464-8ace-b00808fff261"
) )
function(run_cmake_install test) function(run_cmake_install test)
@@ -51,4 +52,6 @@ run_cmake(DependsMultipleNotInstalled)
run_cmake(Config) run_cmake(Config)
run_cmake(EmptyConfig) run_cmake(EmptyConfig)
run_cmake(FileSetHeaders) run_cmake(FileSetHeaders)
run_cmake(DependencyVersionCMake)
run_cmake(DependencyVersionCps)
run_cmake_install(Destination) run_cmake_install(Destination)

View File

@@ -0,0 +1,29 @@
set(PACKAGE_VERSION "1.3.5")
if (PACKAGE_FIND_VERSION_RANGE)
# Check for a version range
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_RANGE_MIN)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
if (PACKAGE_FIND_VERSION_RANGE_MAX)
if (PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_RANGE_MAX)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
endif ()
elseif (PACKAGE_FIND_VERSION)
# Check for a specific version or minimum version
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if (PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif ()
endif ()
endif ()

View File

@@ -0,0 +1 @@
add_library(bar::bar INTERFACE IMPORTED)

View File

@@ -0,0 +1,14 @@
{
"components" :
{
"baz" :
{
"type" : "interface"
}
},
"cps_path" : "@prefix@/cps",
"cps_version" : "0.13.0",
"compat_version": "1.0.0",
"name" : "baz",
"version": "1.3.5"
}