mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-10-04 04:03:22 +03:00
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:
36 files changed
+595
-20
No files matched your search
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -46,4 +46,11 @@ enum class ImportedTargetScope
|
||||
Local,
|
||||
Global,
|
||||
};
|
||||
|
||||
enum class PackageDomain
|
||||
{
|
||||
Unknown,
|
||||
Default,
|
||||
PkgConfig,
|
||||
};
|
||||
};
|
||||
@@ -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:
|
||||
Reference in new issue
Block a user