From f59bab006dfa50c685b39d46fd53e7220d9823e8 Mon Sep 17 00:00:00 2001 From: Vito Gamberini Date: Fri, 14 Mar 2025 14:58:27 -0400 Subject: [PATCH] PkgC: Add NAME and PREFIX Fixes: #26067 --- Help/command/cmake_pkg_config.rst | 28 ++++++-- Source/cmCMakePkgConfigCommand.cxx | 67 +++++++++++++------ .../cmake_pkg_config/ImportName.cmake | 13 ++++ .../cmake_pkg_config/ImportPrefix-check.cmake | 13 ++++ .../cmake_pkg_config/ImportPrefix.cmake | 33 +++++++++ .../PackageRoot/AltRequiresPackages/alpha.pc | 5 ++ .../cmake_pkg_config/RunCMakeTest.cmake | 4 +- 7 files changed, 135 insertions(+), 28 deletions(-) create mode 100644 Tests/RunCMake/cmake_pkg_config/ImportName.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/ImportPrefix-check.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/ImportPrefix.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/AltRequiresPackages/alpha.pc diff --git a/Help/command/cmake_pkg_config.rst b/Help/command/cmake_pkg_config.rst index a794e2345b..32ffed3f8f 100644 --- a/Help/command/cmake_pkg_config.rst +++ b/Help/command/cmake_pkg_config.rst @@ -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::`` and are exposed via the -:prop_tgt:`INTERFACE_LINK_LIBRARIES` target property of an ``IMPORT``-generated -target. +names take the form of ``@foreign_pkgcfg::[_]`` 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 ``=`` 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 `` + 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 <=>...`` + A list of ``=`` 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 [] [REQUIRED] [EXACT] [QUIET] + [PREFIX ] [BIND_PC_REQUIRES <=>...] [STRICTNESS ] [ENV_MODE ] @@ -339,6 +347,8 @@ package was found. cmake_pkg_config(IMPORT [] [REQUIRED] [EXACT] [QUIET] + [NAME ] + [PREFIX ] [BIND_PC_REQUIRES <=>...] [STRICTNESS ] [ENV_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::``. +``PkgConfig::`` by default. A ``PKGCONFIG__FOUND`` variable will be set to indicate whether the package was found. + +``NAME`` + Overrides the name of the created CMake target to ``PkgConfig::``. This + *does not* affect the ``PKGCONFIG__FOUND`` variable. diff --git a/Source/cmCMakePkgConfigCommand.cxx b/Source/cmCMakePkgConfigCommand.cxx index cd3589492f..24676d771a 100644 --- a/Source/cmCMakePkgConfigCommand.cxx +++ b/Source/cmCMakePkgConfigCommand.cxx @@ -37,6 +37,7 @@ namespace { struct ExtractArguments; struct PopulateArguments; +struct ImportArguments; } namespace { @@ -797,10 +798,11 @@ bool HandleExtractCommand(std::vector const& args, using pkgStack = std::unordered_map>; using pkgProviders = std::unordered_map; -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& 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>> providers; + cm::optional Prefix; + cm::optional>> 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 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 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 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 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 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 const& args, std::vector 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 const& args, return result.first; } +struct ImportArguments : PopulateArguments +{ + cm::optional Name; +}; + +auto const ImportParser = + BIND_POPULATE(ImportArguments).Bind("NAME"_s, &ImportArguments::Name); + bool HandleImportCommand(std::vector const& args, cmExecutionStatus& status) { std::vector 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(); diff --git a/Tests/RunCMake/cmake_pkg_config/ImportName.cmake b/Tests/RunCMake/cmake_pkg_config/ImportName.cmake new file mode 100644 index 0000000000..216a9b1b59 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/ImportName.cmake @@ -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:: target") +endif() + +if(NOT TARGET PkgConfig::moe) + message("cmake_pkg_config target rename failed to create PkgConfig:: target") +endif() diff --git a/Tests/RunCMake/cmake_pkg_config/ImportPrefix-check.cmake b/Tests/RunCMake/cmake_pkg_config/ImportPrefix-check.cmake new file mode 100644 index 0000000000..5545c62206 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/ImportPrefix-check.cmake @@ -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() diff --git a/Tests/RunCMake/cmake_pkg_config/ImportPrefix.cmake b/Tests/RunCMake/cmake_pkg_config/ImportPrefix.cmake new file mode 100644 index 0000000000..1bb7280425 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/ImportPrefix.cmake @@ -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: $ +curly: $ +moe: $ +shemp: $ +" +) diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/AltRequiresPackages/alpha.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/AltRequiresPackages/alpha.pc new file mode 100644 index 0000000000..08b6a72507 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/AltRequiresPackages/alpha.pc @@ -0,0 +1,5 @@ +Name: AltAlpha +Description: AltAlpha +Version: 1.0.0 + +Cflags: AltAlpha diff --git a/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake b/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake index 32e0866128..422a993fdd 100644 --- a/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake @@ -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)