PkgC: Add NAME and PREFIX

Fixes: #26067
This commit is contained in:
Vito Gamberini 2025-03-14 14:58:27 -04:00
parent c7173f18f8
commit f59bab006d
No known key found for this signature in database
GPG Key ID: D332F9FE732494B0
7 changed files with 135 additions and 28 deletions

View File

@ -34,9 +34,9 @@ PkgConfig Targets
``cmake_pkg_config`` may recursively generate target-like names in the global
scope in order to resolve a package ``IMPORT`` or ``POPULATE`` command. These
names take the form of ``@foreign_pkgcfg::<package>`` and are exposed via the
:prop_tgt:`INTERFACE_LINK_LIBRARIES` target property of an ``IMPORT``-generated
target.
names take the form of ``@foreign_pkgcfg::[<prefix>_]<package>`` and are exposed
via the :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property of an
``IMPORT``-generated target.
It is not possible to modify or address these pkg-config native targets via
normal target-based commands. Limited control over their generation is possible
@ -180,9 +180,16 @@ common between them. They are:
#. If no top build directory path is available, the ``pc_top_builddir``
package variable is not set
``BIND_PC_REQUIRES``
A list of ``<Name>=<Target>`` pairs, the ``Name`` is a package name as it
appears in the ``Requires`` list of a pkg-config file and the ``Target`` is a
``PREFIX <name>``
Specifying a prefix creates an independent collection of pkg-config targets
separate from previously populated targets. This enables multiple version of
a given package to co-exist, for example packages from different sysroots.
The default prefix is an empty string.
``BIND_PC_REQUIRES <<name>=<target>>...``
A list of ``<name>=<target>`` pairs, the ``name`` is a package name as it
appears in the ``Requires`` list of a pkg-config file and the ``target`` is a
CMake-native target name (not a pkg-config target).
When a given package name appears in the ``Requires`` list of a package, it
@ -311,6 +318,7 @@ The following variables will be populated from the contents of package file:
cmake_pkg_config(POPULATE <package> [<version>]
[REQUIRED] [EXACT] [QUIET]
[PREFIX <prefix>]
[BIND_PC_REQUIRES <<name>=<target>>...]
[STRICTNESS <mode>]
[ENV_MODE <mode>]
@ -339,6 +347,8 @@ package was found.
cmake_pkg_config(IMPORT <package> [<version>]
[REQUIRED] [EXACT] [QUIET]
[NAME <name>]
[PREFIX <prefix>]
[BIND_PC_REQUIRES <<name>=<target>>...]
[STRICTNESS <mode>]
[ENV_MODE <mode>]
@ -350,7 +360,11 @@ package was found.
Creates a native CMake ``IMPORTED`` target that can be linked to via
:command:`target_link_libraries`. This new target is named
``PkgConfig::<package>``.
``PkgConfig::<package>`` by default.
A ``PKGCONFIG_<package>_FOUND`` variable will be set to indicate whether the
package was found.
``NAME``
Overrides the name of the created CMake target to ``PkgConfig::<name>``. This
*does not* affect the ``PKGCONFIG_<package>_FOUND`` variable.

View File

@ -37,6 +37,7 @@
namespace {
struct ExtractArguments;
struct PopulateArguments;
struct ImportArguments;
}
namespace {
@ -797,10 +798,11 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
using pkgStack = std::unordered_map<std::string, std::vector<pkgStackEntry>>;
using pkgProviders = std::unordered_map<std::string, std::string>;
cmTarget* CreateCMakeTarget(std::string const& name, cmPkgConfigResult& pkg,
pkgProviders& providers, cmMakefile& mf)
cmTarget* CreateCMakeTarget(std::string const& name, std::string const& prefix,
cmPkgConfigResult& pkg, pkgProviders& providers,
cmMakefile& mf)
{
auto* tgt = mf.AddForeignTarget("pkgcfg", name);
auto* tgt = mf.AddForeignTarget("pkgcfg", cmStrCat(prefix, name));
tgt->AppendProperty("VERSION", pkg.Version());
@ -829,13 +831,14 @@ cmTarget* CreateCMakeTarget(std::string const& name, cmPkgConfigResult& pkg,
}
tgt->AppendProperty("INTERFACE_LINK_LIBRARIES",
cmStrCat("@foreign_pkgcfg::", dep.Name));
cmStrCat("@foreign_pkgcfg::", prefix, dep.Name));
}
return tgt;
}
bool CheckPackageDependencies(
std::string const& name, cmPkgConfigResult& pkg, pkgStack& inStack,
std::string const& name, std::string const& prefix, cmPkgConfigResult& pkg,
pkgStack& inStack,
std::unordered_map<std::string, cmPkgConfigResult>& outStack,
pkgProviders& providers, ImportEnv& imEnv)
{
@ -846,7 +849,7 @@ bool CheckPackageDependencies(
}
auto* tgt = imEnv.status.GetMakefile().FindTargetToUse(
cmStrCat("@foreign_pkgcfg::", dep.Name),
cmStrCat("@foreign_pkgcfg::", prefix, dep.Name),
cmStateEnums::TargetDomain::FOREIGN);
if (tgt) {
auto ver = tgt->GetProperty("VERSION");
@ -882,17 +885,23 @@ bool CheckPackageDependencies(
struct PopulateArguments : CommonArguments
{
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> providers;
cm::optional<std::string> Prefix;
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Providers;
};
auto const PopulateParser =
BIND_COMMON(PopulateArguments)
.Bind("BIND_PC_REQUIRES"_s, &PopulateArguments::providers);
#define BIND_POPULATE(argtype) \
BIND_COMMON(argtype) \
.Bind("PREFIX"_s, &argtype::Prefix) \
.Bind("BIND_PC_REQUIRES"_s, &argtype::Providers)
auto const PopulateParser = BIND_POPULATE(PopulateArguments);
std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
cmExecutionStatus& status)
{
std::string prefix = args.Prefix ? cmStrCat(*args.Prefix, "_"_s) : "";
auto& mf = status.GetMakefile();
auto maybeEnv = HandleCommon(args, status);
@ -903,8 +912,8 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
auto& imEnv = maybeEnv->second;
pkgProviders providers;
if (args.providers) {
for (auto const& provider_str : *args.providers) {
if (args.Providers) {
for (auto const& provider_str : *args.Providers) {
auto assignment = provider_str.find('=');
if (assignment != std::string::npos) {
providers.emplace(provider_str.substr(0, assignment),
@ -927,7 +936,7 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
}
imEnv.exact = false;
if (!CheckPackageDependencies(*args.Package, *maybePackage, inStack,
if (!CheckPackageDependencies(*args.Package, prefix, *maybePackage, inStack,
outStack, providers, imEnv)) {
return { !args.Required, false };
}
@ -940,8 +949,8 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
if (!maybePackage) {
return { !args.Required, false };
}
if (!CheckPackageDependencies(name, *maybePackage, inStack, outStack,
providers, imEnv)) {
if (!CheckPackageDependencies(name, prefix, *maybePackage, inStack,
outStack, providers, imEnv)) {
return { !args.Required, false };
}
inStack.erase(name);
@ -949,7 +958,7 @@ std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
}
for (auto& entry : outStack) {
CreateCMakeTarget(entry.first, entry.second, providers, mf);
CreateCMakeTarget(entry.first, prefix, entry.second, providers, mf);
}
return { true, true };
@ -961,7 +970,11 @@ bool HandlePopulateCommand(std::vector<std::string> const& args,
std::vector<std::string> unparsed;
auto parsedArgs = PopulateParser.Parse(args, &unparsed);
auto foreign_name = cmStrCat("@foreign_pkgcfg::", *parsedArgs.Package);
std::string prefix =
parsedArgs.Prefix ? cmStrCat(*parsedArgs.Prefix, "_"_s) : "";
auto foreign_name =
cmStrCat("@foreign_pkgcfg::", prefix, *parsedArgs.Package);
auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
auto& mf = status.GetMakefile();
@ -976,13 +989,27 @@ bool HandlePopulateCommand(std::vector<std::string> const& args,
return result.first;
}
struct ImportArguments : PopulateArguments
{
cm::optional<std::string> Name;
};
auto const ImportParser =
BIND_POPULATE(ImportArguments).Bind("NAME"_s, &ImportArguments::Name);
bool HandleImportCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
std::vector<std::string> unparsed;
auto parsedArgs = PopulateParser.Parse(args, &unparsed);
auto foreign_name = cmStrCat("@foreign_pkgcfg::", *parsedArgs.Package);
auto local_name = cmStrCat("PkgConfig::", *parsedArgs.Package);
auto parsedArgs = ImportParser.Parse(args, &unparsed);
std::string prefix =
parsedArgs.Prefix ? cmStrCat(*parsedArgs.Prefix, "_"_s) : "";
auto foreign_name =
cmStrCat("@foreign_pkgcfg::", prefix, *parsedArgs.Package);
auto local_name =
cmStrCat("PkgConfig::", parsedArgs.Name.value_or(*parsedArgs.Package));
auto found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
auto& mf = status.GetMakefile();

View File

@ -0,0 +1,13 @@
set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
cmake_pkg_config(IMPORT alpha REQUIRED
NAME moe
)
if(TARGET PkgConfig::alpha)
message("cmake_pkg_config target rename created PkgConfig::<package> target")
endif()
if(NOT TARGET PkgConfig::moe)
message("cmake_pkg_config target rename failed to create PkgConfig::<name> target")
endif()

View File

@ -0,0 +1,13 @@
set(expected
"larry: Alpha
curly: Alpha
moe: AltAlpha
shemp: AltAlpha
"
)
file(READ "${RunCMake_TEST_BINARY_DIR}/import-prefix.txt" actual)
if(NOT(expected STREQUAL actual))
set(RunCMake_TEST_FAILED "cmake_pkg_config import-prefix.txt does not match expected:\n${actual}")
endif()

View File

@ -0,0 +1,33 @@
set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
cmake_pkg_config(IMPORT alpha REQUIRED
NAME larry
)
set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/AltRequiresPackages)
cmake_pkg_config(IMPORT alpha REQUIRED
NAME curly
)
cmake_pkg_config(IMPORT alpha REQUIRED
NAME moe
PREFIX moe
)
set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
cmake_pkg_config(IMPORT alpha REQUIRED
NAME shemp
PREFIX moe
)
file(GENERATE
OUTPUT import-prefix.txt
CONTENT
"larry: $<TARGET_PROPERTY:PkgConfig::larry,INTERFACE_COMPILE_OPTIONS>
curly: $<TARGET_PROPERTY:PkgConfig::curly,INTERFACE_COMPILE_OPTIONS>
moe: $<TARGET_PROPERTY:PkgConfig::moe,INTERFACE_COMPILE_OPTIONS>
shemp: $<TARGET_PROPERTY:PkgConfig::shemp,INTERFACE_COMPILE_OPTIONS>
"
)

View File

@ -0,0 +1,5 @@
Name: AltAlpha
Description: AltAlpha
Version: 1.0.0
Cflags: AltAlpha

View File

@ -16,8 +16,10 @@ run_cmake(ExtractRequired)
run_cmake(ExtractReroot)
run_cmake(ExtractUninstalled)
run_cmake(ExtractVersion)
run_cmake(ImportSimple)
run_cmake(ImportName)
run_cmake(ImportPrefix)
run_cmake(ImportRequires)
run_cmake(ImportSimple)
run_cmake(ImportTransitiveFail)
run_cmake(ImportTransitiveVersion)
run_cmake(ImportTransitiveVersionFail)