CPS/PkgC: Add PkgC interop to CPS

Extend the automatic export system of install(PACKAGE_INFO) and
export(PACKAGE_INFO) to handle targets created with cmake_pkg_config,
and allow find_package to resolve CPS requirements in the pkg-config
domain.

A pkg-config package has no components of its own, so it is exposed to
CPS as a package named after itself with a single component of the same
name; such references take the form "<PkgName>:<PkgName>" and resolve
directly to the foreign target which provides the package.  The domains
in which a requirement may be found are recorded in a CMake extension
section rather than a non-standard `domains` attribute.
This commit is contained in:
Vito Gamberini committed 2026-09-24 14:47:59 -04:00
1 parent ec85176fbe
commit a4f08400f4
36 files changed
+595 -20

No files matched your search

+79
View File
@@ -861,6 +861,7 @@ cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix,
{
auto* tgt = mf.AddForeignTarget("pkgcfg", cmStrCat(prefix, name));
tgt->SetForeignPackageName(name);
tgt->AppendProperty("VERSION", pkg.Version());
auto libs = pkg.Libs();
@@ -1097,11 +1098,89 @@ bool HandleImportCommand(std::vector<std::string> const& args,
mf.AddImportedTarget(local_name, cm::TargetType::INTERFACE_LIBRARY,
cm::ImportedTargetScope::Local);
tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", foreign_name);
if (auto* ft = mf.FindTargetToUse(foreign_name, cm::TargetDomain::FOREIGN)) {
tgt->SetForeignTarget(ft);
}
return true;
}
bool PkgConfigImportPackageImpl(cmMakefile& mf, cmExecutionStatus& status,
std::string const& name)
{
std::string const prefix;
// A pkg-config package is referenced in CPS as "<name>:<name>" and linked
// directly as the foreign target which provides it, so there is no
// interface target to create here.
auto const foreignName = cmStrCat("@foreign_pkgcfg::", prefix, name);
if (mf.FindTargetToUse(foreignName, cm::TargetDomain::FOREIGN)) {
return true;
}
cmPkgConfigEnv pcEnv;
CollectEnv(mf, pcEnv, CommonArguments::EnvModeType::ENVMODE_PKGCONF);
if (pcEnv.Path) {
pcEnv.search = *pcEnv.Path;
if (pcEnv.LibDirs) {
pcEnv.search += *pcEnv.LibDirs;
}
} else if (pcEnv.LibDirs) {
pcEnv.search = *pcEnv.LibDirs;
}
ImportEnv imEnv{ false, true, false,
CommonArguments::StrictnessType::STRICTNESS_PERMISSIVE };
ImportState state{ status, false };
cm::optional<cmPkgConfigResolver> maybePackage =
ImportPackage(name, cm::optional<std::string>{}, imEnv, pcEnv, state);
if (!maybePackage) {
return false;
}
pkgProviders providers;
pkgStack inStack;
std::unordered_map<std::string, cmPkgConfigResolver> outStack;
ImportEnv depEnv = imEnv;
depEnv.exact = false;
if (!CheckPackageDependencies(name, prefix, *maybePackage, inStack, outStack,
providers, depEnv, state)) {
return false;
}
outStack.emplace(name, std::move(*maybePackage));
while (!inStack.empty()) {
auto depName = inStack.begin()->first;
auto reqs = inStack.begin()->second;
maybePackage = ImportPackage(depName, reqs, depEnv, pcEnv, state);
if (!maybePackage) {
return false;
}
if (!CheckPackageDependencies(depName, prefix, *maybePackage, inStack,
outStack, providers, depEnv, state)) {
return false;
}
inStack.erase(depName);
outStack.emplace(std::move(depName), std::move(*maybePackage));
}
for (auto& entry : outStack) {
CreateCMakeTarget(entry.first, prefix, entry.second, providers, mf);
}
return true;
}
} // namespace
bool cmImportPkgConfigPackage(cmMakefile& mf, cmExecutionStatus& status,
std::string const& name)
{
return PkgConfigImportPackageImpl(mf, status, name);
}
bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
+4
View File
@@ -8,6 +8,10 @@
#include <vector>
class cmExecutionStatus;
class cmMakefile;
bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
cmExecutionStatus& status);
bool cmImportPkgConfigPackage(cmMakefile& mf, cmExecutionStatus& status,
std::string const& name);
+63 -4
View File
@@ -228,6 +228,19 @@ void cmExportPackageInfoGenerator::GeneratePackageRequires(
data["version"] = *requirement.second.Version;
}
if (!requirement.second.Domains.empty()) {
auto requirementDomains = Json::Value{ Json::arrayValue };
for (cm::PackageDomain domain : requirement.second.Domains) {
if (domain == cm::PackageDomain::PkgConfig) {
requirementDomains.append("pkg-config");
}
}
if (!requirementDomains.empty()) {
data["extensions"]["cmake"]["domains@v1"] =
std::move(requirementDomains);
}
}
requirements[requirement.first] = data;
}
}
@@ -358,6 +371,25 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
return true;
}
auto getRequirement =
[&](std::string const& name,
cmPackageInformation&& requirement) -> cmPackageInformation* {
// emplace may move from requirement even when the key already exists.
auto const domains = requirement.Domains;
auto record = this->Requirements.emplace(name, std::move(requirement));
if (!record.second) {
if (record.first->second.Domains != domains) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Package \"", this->GetPackageName(),
"\" requires package \"", name,
"\" in multiple domain sets. This is not supported."));
return nullptr;
}
}
return &record.first->second;
};
if (linkedTarget->IsImported()) {
// Target is imported from a found package.
using Package = cm::optional<std::pair<std::string, cmPackageInformation>>;
@@ -379,6 +411,26 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
}(linkedTarget->Target);
if (!pkgInfo) {
if (cmTarget* foreignTarget = linkedTarget->Target->GetForeignTarget()) {
cmPackageInformation reqInit;
reqInit.Domains = { cm::PackageDomain::PkgConfig };
std::string foreignPkgName = foreignTarget->GetForeignPackageName();
cmPackageInformation* req =
getRequirement(foreignPkgName, std::move(reqInit));
if (!req) {
return false;
}
// A pkg-config package has no components of its own; expose it as a
// component named after the package itself.
this->LinkTargets.emplace(
linkedName, cmStrCat(foreignPkgName, ':', foreignPkgName));
req->Components.emplace(std::move(foreignPkgName));
return true;
}
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Target \"", target->GetName(),
@@ -402,10 +454,13 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
}
std::string component = linkedName.substr(prefix.length());
cmPackageInformation* req =
getRequirement(pkgName, std::move(pkgInfo->second));
if (!req) {
return false;
}
this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
cmPackageInformation& req =
this->Requirements.insert(std::move(*pkgInfo)).first->second;
req.Components.emplace(std::move(component));
req->Components.emplace(std::move(component));
return true;
}
@@ -428,8 +483,12 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
if (pkgName == this->GetPackageName()) {
this->LinkTargets.emplace(linkedName, cmStrCat(':', component));
} else {
cmPackageInformation* req = getRequirement(pkgName, {});
if (!req) {
return false;
}
this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
this->Requirements[pkgName].Components.emplace(std::move(component));
req->Components.emplace(std::move(component));
}
return true;
}
+12 -2
View File
@@ -24,6 +24,7 @@
#include "cmsys/String.h"
#include "cmAlgorithms.h"
#include "cmCMakePkgConfigCommand.h"
#include "cmConfigureLog.h"
#include "cmDependencyProvider.h"
#include "cmDiagnostics.h"
@@ -2193,8 +2194,17 @@ bool cmFindPackageCommand::FindPackageDependencies(
std::string const& filePath, cmPackageInfoReader const& reader,
RequiredStatus required)
{
// Get package requirements.
for (cmPackageRequirement const& dep : reader.GetRequirements()) {
for (auto const& dep : reader.GetRequirements()) {
// Try domain-based resolution first.
#ifndef CMAKE_BOOTSTRAP
if (cm::contains(dep.Domains, cm::PackageDomain::PkgConfig)) {
cmExecutionStatus status{ *this->Makefile };
if (cmImportPkgConfigPackage(*this->Makefile, status, dep.Name)) {
continue;
}
}
#endif
cmExecutionStatus status{ *this->Makefile };
cmMakefile::CallRAII scope{ this->Makefile, filePath, status };
+6
View File
@@ -7,11 +7,16 @@
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <cm/optional>
#include "cmConstStack.h"
namespace cm {
enum class PackageDomain;
}
/**
* This data represents the actual contents of find_package
* <PACKAGE>-Config.cmake or <PACKAGE>.cps file, and not what is passed
@@ -28,6 +33,7 @@ public:
cm::optional<std::string> Website;
cm::optional<std::string> PackageUrl;
std::set<std::string> Components;
std::vector<cm::PackageDomain> Domains;
};
/**
+7
View File
@@ -4084,6 +4084,13 @@ cmTarget* cmMakefile::AddForeignTarget(std::string const& origin,
return this->ImportedTargetsOwned.back().get();
}
cmTarget* cmMakefile::FindForeignTarget(std::string const& origin,
std::string const& name) const
{
return this->FindTargetToUse(cmStrCat("@foreign_", origin, "::", name),
cm::TargetDomain::FOREIGN);
}
cmTarget* cmMakefile::FindTargetToUse(std::string const& name,
cm::TargetDomainSet domains) const
{
+4
View File
@@ -297,6 +297,10 @@ public:
cmTarget* AddForeignTarget(std::string const& origin,
std::string const& name);
/** Find a foreign target previously created by AddForeignTarget. */
cmTarget* FindForeignTarget(std::string const& origin,
std::string const& name) const;
std::pair<cmTarget&, bool> CreateNewTarget(
std::string const& name, cm::TargetType type,
cmTarget::PerConfig perConfig = cmTarget::PerConfig::Yes,
+54 -13
View File
@@ -509,6 +509,7 @@ std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
if (parent) {
reader->ComponentTargets = parent->ComponentTargets;
reader->DefaultConfigurations = parent->DefaultConfigurations;
reader->RequirementDomains = parent->RequirementDomains;
} else {
for (std::string const& config :
ReadList(reader->Data, "configurations")) {
@@ -517,6 +518,12 @@ std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
}
}
// Read package-level requirements. (Configuration supplements are not
// allowed to declare package requirements.)
if (cmSystemTools::GetFilenameName(path).find('@') == std::string::npos) {
reader->ReadRequirements();
}
// Check for a default license.
Json::Value const& defaultLicense = reader->Data["default_license"];
if (!defaultLicense.isNull()) {
@@ -590,24 +597,56 @@ cmPackageInfoReader::ParseVersion(
return cm::nullopt;
}
std::vector<cmPackageRequirement> cmPackageInfoReader::GetRequirements() const
void cmPackageInfoReader::ReadRequirements()
{
std::vector<cmPackageRequirement> requirements;
auto const& requirementObjects = this->Data["requires"];
if (!requirementObjects.isObject()) {
return {};
return;
}
for (auto ri = requirementObjects.begin(), re = requirementObjects.end();
ri != re; ++ri) {
cmPackageRequirement r{ ri.name(), ToString((*ri)["version"]),
cmPackageRequirement r{ ri.name(),
ToString((*ri)["version"]),
ReadList(*ri, "components"),
ReadList(*ri, "hints") };
requirements.emplace_back(std::move(r));
ReadList(*ri, "hints"),
{} };
for (std::string const& domain :
ReadList(GetExtensions(*ri), "domains@v1")) {
r.Domains.push_back(domain == "pkg-config" ? cm::PackageDomain::PkgConfig
: cm::PackageDomain::Unknown);
}
if (!r.Domains.empty()) {
this->RequirementDomains[r.Name] = r.Domains;
} else {
// A local declaration without domains clears any inherited domains.
this->RequirementDomains.erase(r.Name);
}
this->Requirements.emplace_back(std::move(r));
}
}
std::string cmPackageInfoReader::ResolveTargetName(
cmMakefile* makefile, std::string const& name,
std::string const& context) const
{
// A pkg-config package is exposed as a single component of the same name.
// Only resolve it to a foreign target if this requirement opts in.
std::string::size_type const n = name.find(':');
if (n != std::string::npos && n > 0 &&
name.substr(0, n) == name.substr(n + 1)) {
std::string const package = name.substr(0, n);
auto const di = this->RequirementDomains.find(package);
if (di != this->RequirementDomains.end() &&
cm::contains(di->second, cm::PackageDomain::PkgConfig)) {
if (cmTarget* foreignTarget =
makefile->FindForeignTarget("pkgcfg", package)) {
return foreignTarget->GetName();
}
}
}
return requirements;
return NormalizeTargetName(name, context);
}
std::vector<std::string> cmPackageInfoReader::GetComponentNames() const
@@ -794,24 +833,26 @@ void cmPackageInfoReader::SetTargetProperties(
// Add transitive dependencies.
for (std::string const& dep : ReadList(data, "requires")) {
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
NormalizeTargetName(dep, package));
this->ResolveTargetName(makefile, dep, package));
}
for (std::string const& dep : ReadList(data, "compile_requires")) {
std::string const& lib =
cmStrCat("$<COMPILE_ONLY:"_s, NormalizeTargetName(dep, package), '>');
cmStrCat("$<COMPILE_ONLY:"_s,
this->ResolveTargetName(makefile, dep, package), '>');
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
}
for (std::string const& dep : ReadList(data, "link_requires")) {
std::string const& lib =
cmStrCat("$<LINK_ONLY:"_s, NormalizeTargetName(dep, package), '>');
std::string const& lib = cmStrCat(
"$<LINK_ONLY:"_s, this->ResolveTargetName(makefile, dep, package), '>');
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
}
for (std::string const& dep : ReadList(data, "dyld_requires")) {
AppendImportProperty(makefile, target, "LINK_DEPENDENT_LIBRARIES"_s,
configuration, NormalizeTargetName(dep, package));
configuration,
this->ResolveTargetName(makefile, dep, package));
}
for (std::string const& lib : ReadList(data, "link_libraries")) {
+13 -1
View File
@@ -20,6 +20,7 @@ class cmTarget;
namespace cm {
enum class ImportedTargetScope;
enum class PackageDomain;
enum class TargetType;
} // namespace cm
@@ -29,6 +30,7 @@ struct cmPackageRequirement
std::string Version;
std::vector<std::string> Components;
std::vector<std::string> Hints;
std::vector<cm::PackageDomain> Domains;
};
/** \class cmPackageInfoReader
@@ -76,7 +78,11 @@ public:
cm::optional<Pep440Version> ParseVersion(
cm::optional<std::string> const& version) const;
std::vector<cmPackageRequirement> GetRequirements() const;
/// Requirements declared by this file, excluding inherited requirements.
std::vector<cmPackageRequirement> const& GetRequirements() const
{
return this->Requirements;
}
std::vector<std::string> GetComponentNames() const;
/// Create targets for components specified in the CPS file.
@@ -118,10 +124,16 @@ private:
Json::Value const& object) const;
std::string ResolvePath(std::string path) const;
std::string ResolveTargetName(cmMakefile* makefile, std::string const& name,
std::string const& context) const;
void ReadRequirements();
std::string Path;
Json::Value Data;
std::string Prefix;
std::vector<cmPackageRequirement> Requirements;
std::map<std::string, std::vector<cm::PackageDomain>> RequirementDomains;
std::map<std::string, cmTarget*> ComponentTargets;
std::vector<std::string> DefaultConfigurations;
+22
View File
@@ -648,6 +648,8 @@ public:
bool IsExportPassthrough = false;
bool CxxModuleNeedsInterfaceObjects = false;
cmTarget::Visibility TargetVisibility;
std::string ForeignPackageName;
cmTarget* ForeignTarget = nullptr;
std::set<BT<std::pair<std::string, bool>>> Utilities;
std::set<std::string> CodegenDependencies;
std::vector<cmCustomCommand> PreBuildCommands;
@@ -3339,6 +3341,26 @@ std::string cmTarget::ImportedGetFullPath(
return result;
}
void cmTarget::SetForeignPackageName(std::string const& name)
{
this->impl->ForeignPackageName = name;
}
std::string const& cmTarget::GetForeignPackageName() const
{
return this->impl->ForeignPackageName;
}
void cmTarget::SetForeignTarget(cmTarget* target)
{
this->impl->ForeignTarget = target;
}
cmTarget* cmTarget::GetForeignTarget() const
{
return this->impl->ForeignTarget;
}
cmFileSet const* cmTarget::GetFileSet(std::string const& name) const
{
auto it = this->impl->FileSets.find(name);
+5
View File
@@ -289,6 +289,11 @@ public:
bool HasKnownObjectFileLocation(std::string* reason = nullptr) const;
void SetForeignPackageName(std::string const& name);
std::string const& GetForeignPackageName() const;
void SetForeignTarget(cmTarget* target);
cmTarget* GetForeignTarget() const;
//! Get a backtrace from the creation of the target.
cmListFileBacktrace const& GetBacktrace() const;
+7
View File
@@ -46,4 +46,11 @@ enum class ImportedTargetScope
Local,
Global,
};
enum class PackageDomain
{
Unknown,
Default,
PkgConfig,
};
};
+48
View File
@@ -457,3 +457,51 @@ else()
"include set 'bar' files"
"${CMAKE_SOURCE_DIR}/include/bar.h")
endif()
###############################################################################
# Test CPS + pkg-config interoperability.
set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/pc")
find_package(pc-link-test)
if(NOT pc-link-test_FOUND)
message(SEND_ERROR "pc-link-test not found !")
elseif(NOT TARGET pc-link-test::Parent)
message(SEND_ERROR "pc-link-test::Parent missing !")
elseif(NOT TARGET pc-link-test::InheritsRequirement)
message(SEND_ERROR "pc-link-test::InheritsRequirement missing !")
elseif(NOT TARGET pc-link-test::ShadowsRequirement)
message(SEND_ERROR "pc-link-test::ShadowsRequirement missing !")
elseif(NOT TARGET pc-link-test::UnknownDomain)
message(SEND_ERROR "pc-link-test::UnknownDomain missing !")
endif()
expect_property(pc-link-test::Parent INTERFACE_LINK_LIBRARIES
"link libraries (from pkg-config dependency)"
"@foreign_pkgcfg::cps-pkg-test"
"$<$<CONFIG:debug>:$<COMPILE_ONLY:@foreign_pkgcfg::cps-pkg-test>>"
"$<$<CONFIG:debug>:$<LINK_ONLY:@foreign_pkgcfg::cps-pkg-test>>")
expect_property(pc-link-test::Parent IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG
"dyld requirements (from pkg-config dependency)"
"@foreign_pkgcfg::cps-pkg-test")
# Appendices inherit base requirement domains and keep additional requirements
# local. Configuration supplements inherit both levels of requirement domains.
# Their package-level requires blocks are ignored, including attempted
# overrides of the inherited pkg-config domains.
expect_property(pc-link-test::InheritsRequirement INTERFACE_LINK_LIBRARIES
"link libraries (from inherited and local dependencies)"
"@foreign_pkgcfg::cps-pkg-test"
"@foreign_pkgcfg::cps-pkg-appendix"
"$<$<CONFIG:debug>:@foreign_pkgcfg::cps-pkg-test>"
"$<$<CONFIG:debug>:@foreign_pkgcfg::cps-pkg-appendix>")
expect_property(pc-link-test::ShadowsRequirement INTERFACE_LINK_LIBRARIES
"link libraries (from shadowing dependency)"
"cps-pkg-test::cps-pkg-test"
"$<COMPILE_ONLY:cps-pkg-test::cps-pkg-test>"
"$<LINK_ONLY:cps-pkg-test::cps-pkg-test>")
expect_property(pc-link-test::ShadowsRequirement IMPORTED_LINK_DEPENDENT_LIBRARIES
"dyld requirements (from shadowing dependency)"
"cps-pkg-test::cps-pkg-test")
expect_property(pc-link-test::UnknownDomain INTERFACE_LINK_LIBRARIES
"link libraries (from unknown domain dependency)"
"cps-pkg-test::cps-pkg-test")
@@ -0,0 +1,10 @@
{
"cps_version": "0.15.0",
"name": "cps-pkg-test",
"cps_path": "@prefix@/cps",
"components": {
"cps-pkg-test": {
"type": "interface"
}
}
}
@@ -0,0 +1,19 @@
{
"name": "pc-link-test",
"requires": {
"cps-pkg-appendix": {
"components": ["cps-pkg-appendix"],
"extensions": {
"cmake": {
"domains@v1": ["pkg-config"]
}
}
}
},
"components": {
"InheritsRequirement": {
"type": "interface",
"requires": ["cps-pkg-test:cps-pkg-test", "cps-pkg-appendix:cps-pkg-appendix"]
}
}
}
@@ -0,0 +1,14 @@
{
"name": "pc-link-test",
"configuration": "debug",
"requires": {
"cps-pkg-test": {},
"cps-pkg-appendix": {},
"cps-pkg-unavailable": {}
},
"components": {
"InheritsRequirement": {
"requires": ["cps-pkg-test:cps-pkg-test", "cps-pkg-appendix:cps-pkg-appendix"]
}
}
}
@@ -0,0 +1,17 @@
{
"name": "pc-link-test",
"requires": {
"cps-pkg-test": {
"components": ["cps-pkg-test"]
}
},
"components": {
"ShadowsRequirement": {
"type": "interface",
"requires": ["cps-pkg-test:cps-pkg-test"],
"compile_requires": ["cps-pkg-test:cps-pkg-test"],
"link_requires": ["cps-pkg-test:cps-pkg-test"],
"dyld_requires": ["cps-pkg-test:cps-pkg-test"]
}
}
}
@@ -0,0 +1,19 @@
{
"name": "pc-link-test",
"requires": {
"cps-pkg-test": {
"components": ["cps-pkg-test"],
"extensions": {
"cmake": {
"domains@v1": ["unrecognized-domain"]
}
}
}
},
"components": {
"UnknownDomain": {
"type": "interface",
"requires": ["cps-pkg-test:cps-pkg-test"]
}
}
}
@@ -0,0 +1,21 @@
{
"cps_version": "0.15.0",
"name": "pc-link-test",
"cps_path": "@prefix@/cps",
"components": {
"Parent": {
"type": "interface",
"requires": ["cps-pkg-test:cps-pkg-test"]
}
},
"requires": {
"cps-pkg-test": {
"components": ["cps-pkg-test"],
"extensions": {
"cmake": {
"domains@v1": ["unrecognized-domain", "pkg-config"]
}
}
}
}
}
@@ -0,0 +1,15 @@
{
"name": "pc-link-test",
"configuration": "debug",
"requires": {
"cps-pkg-test": {},
"cps-pkg-unavailable": {}
},
"components": {
"Parent": {
"compile_requires": ["cps-pkg-test:cps-pkg-test"],
"link_requires": ["cps-pkg-test:cps-pkg-test"],
"dyld_requires": ["cps-pkg-test:cps-pkg-test"]
}
}
}
@@ -0,0 +1,5 @@
Name: cps-pkg-appendix
Description: Dummy package for an appendix-local CPS requirement
Version: 1.0
Libs: -L/dummy
Cflags:
@@ -0,0 +1,5 @@
Name: cps-pkg-test
Description: Dummy package for CPS + pkg-config interoperability test
Version: 1.0
Libs: -L/dummy
Cflags:
@@ -0,0 +1,14 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/PkgConfig-build")
file(READ "${out_dir}/cps/foo/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_array("${content}" 1 "requires" "cps-pkg-test" "components")
expect_value("${content}" "cps-pkg-test" "requires" "cps-pkg-test" "components" 0)
expect_array("${content}" 1 "requires" "cps-pkg-test" "extensions" "cmake" "domains@v1")
expect_value("${content}" "pkg-config" "requires" "cps-pkg-test" "extensions" "cmake" "domains@v1" 0)
string(JSON component GET "${content}" "components" "foo")
expect_array("${component}" 1 "requires")
expect_value("${component}" "cps-pkg-test:cps-pkg-test" "requires" 0)
@@ -0,0 +1,13 @@
set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_LIST_DIR}/pc")
cmake_pkg_config(IMPORT cps-pkg-test REQUIRED)
add_library(foo INTERFACE)
target_link_libraries(foo INTERFACE PkgConfig::cps-pkg-test)
# Multiple components may require the same package in the same domain.
add_library(bar INTERFACE)
target_link_libraries(bar INTERFACE PkgConfig::cps-pkg-test)
install(TARGETS foo bar EXPORT foo DESTINATION .)
export(PACKAGE_INFO foo EXPORT foo)
@@ -0,0 +1 @@
1
@@ -0,0 +1,3 @@
CMake Error in CMakeLists.txt:
Package "consumer" requires package "cps-pkg-test" in multiple domain sets\.
This is not supported\.
@@ -0,0 +1,25 @@
set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_LIST_DIR}/pc")
cmake_pkg_config(IMPORT cps-pkg-test REQUIRED)
if(imported)
add_library(cps-pkg-test::bar INTERFACE IMPORTED)
set_property(TARGET cps-pkg-test::bar PROPERTY
EXPORT_FIND_PACKAGE_NAME cps-pkg-test)
else()
add_library(bar INTERFACE)
add_library(cps-pkg-test::bar ALIAS bar)
install(TARGETS bar EXPORT dependency)
export(PACKAGE_INFO cps-pkg-test EXPORT dependency)
endif()
add_library(consumer INTERFACE)
if(pkg_config_first)
target_link_libraries(consumer INTERFACE
PkgConfig::cps-pkg-test cps-pkg-test::bar)
else()
target_link_libraries(consumer INTERFACE
cps-pkg-test::bar PkgConfig::cps-pkg-test)
endif()
install(TARGETS consumer EXPORT consumer)
export(PACKAGE_INFO consumer EXPORT consumer)
@@ -17,6 +17,16 @@ run_cmake(DependsMultipleDifferentSets)
run_cmake(LinkInterfaceGeneratorExpression)
run_cmake(CompileOnlyRecursive)
run_cmake(LinkOnlyRecursive)
foreach(imported IN ITEMS ON OFF)
foreach(pkg_config_first IN ITEMS ON OFF)
set(RunCMake_TEST_VARIANT_DESCRIPTION
" (imported=${imported}, pkg_config_first=${pkg_config_first})")
run_cmake_with_options(PkgConfigCollision
-Dimported=${imported}
-Dpkg_config_first=${pkg_config_first})
endforeach()
endforeach()
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
# Test functionality
run_cmake(Appendix)
@@ -28,6 +38,7 @@ run_cmake(Minimal)
run_cmake(MinimalVersion)
run_cmake(LowerCaseFile)
run_cmake(Requirements)
run_cmake(PkgConfig)
run_cmake(LinkDependentLibraries)
run_cmake(PrivateLinkDependency)
run_cmake(ExportSymbolicComponent)
@@ -0,0 +1,5 @@
Name: cps-pkg-test
Description: Dummy package for CPS + pkg-config interoperability export test
Version: 1.0
Libs: -L/dummy
Cflags:
@@ -0,0 +1,16 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/PkgConfig-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_array("${content}" 1 "requires" "cps-pkg-test" "components")
expect_value("${content}" "cps-pkg-test" "requires" "cps-pkg-test" "components" 0)
expect_array("${content}" 1 "requires" "cps-pkg-test" "extensions" "cmake" "domains@v1")
expect_value("${content}" "pkg-config" "requires" "cps-pkg-test" "extensions" "cmake" "domains@v1" 0)
foreach(name IN ITEMS foo bar)
string(JSON component GET "${content}" "components" "${name}")
expect_array("${component}" 1 "requires")
expect_value("${component}" "cps-pkg-test:cps-pkg-test" "requires" 0)
endforeach()
@@ -0,0 +1,13 @@
set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_LIST_DIR}/pc")
cmake_pkg_config(IMPORT cps-pkg-test REQUIRED)
add_library(foo INTERFACE)
target_link_libraries(foo INTERFACE PkgConfig::cps-pkg-test)
# Multiple components may require the same package in the same domain.
add_library(bar INTERFACE)
target_link_libraries(bar INTERFACE PkgConfig::cps-pkg-test)
install(TARGETS foo bar EXPORT foo DESTINATION .)
install(PACKAGE_INFO foo EXPORT foo DESTINATION cps)
@@ -0,0 +1 @@
1
@@ -0,0 +1,3 @@
CMake Error in CMakeLists.txt:
Package "consumer" requires package "cps-pkg-test" in multiple domain sets\.
This is not supported\.
@@ -0,0 +1,25 @@
set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_LIST_DIR}/pc")
cmake_pkg_config(IMPORT cps-pkg-test REQUIRED)
if(imported)
add_library(cps-pkg-test::bar INTERFACE IMPORTED)
set_property(TARGET cps-pkg-test::bar PROPERTY
EXPORT_FIND_PACKAGE_NAME cps-pkg-test)
else()
add_library(bar INTERFACE)
add_library(cps-pkg-test::bar ALIAS bar)
install(TARGETS bar EXPORT dependency)
install(PACKAGE_INFO cps-pkg-test EXPORT dependency DESTINATION cps)
endif()
add_library(consumer INTERFACE)
if(pkg_config_first)
target_link_libraries(consumer INTERFACE
PkgConfig::cps-pkg-test cps-pkg-test::bar)
else()
target_link_libraries(consumer INTERFACE
cps-pkg-test::bar PkgConfig::cps-pkg-test)
endif()
install(TARGETS consumer EXPORT consumer)
install(PACKAGE_INFO consumer EXPORT consumer DESTINATION cps)
@@ -26,6 +26,16 @@ run_cmake(ReferencesWronglyImportedTarget)
run_cmake(ReferencesWronglyNamespacedTarget)
run_cmake(DependsMultipleDifferentNamespace)
run_cmake(DependsMultipleDifferentSets)
foreach(imported IN ITEMS ON OFF)
foreach(pkg_config_first IN ITEMS ON OFF)
set(RunCMake_TEST_VARIANT_DESCRIPTION
" (imported=${imported}, pkg_config_first=${pkg_config_first})")
run_cmake_with_options(PkgConfigCollision
-Dimported=${imported}
-Dpkg_config_first=${pkg_config_first})
endforeach()
endforeach()
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
# Test functionality
run_cmake(Appendix)
@@ -38,6 +48,7 @@ run_cmake(Minimal)
run_cmake(MinimalVersion)
run_cmake(LowerCaseFile)
run_cmake(Requirements)
run_cmake(PkgConfig)
run_cmake(PrivateLinkDependency)
run_cmake(TargetTypes)
run_cmake(DependsMultiple)
@@ -0,0 +1,5 @@
Name: cps-pkg-test
Description: Dummy package for CPS + pkg-config interoperability install test
Version: 1.0
Libs: -L/dummy
Cflags: