浏览代码

Merge topic 'pkg-config-import-populate'

28a92bde80 PkgC: Implement cmake_pkg_config IMPORT / POPULATE
7f172faca5 Help: Add install() section heading for common options

Acked-by: Kitware Robot <[email protected]>
Merge-request: !10393
Brad King 8 月之前
父节点
当前提交
b7abc78e6c
共有 55 个文件被更改,包括 815 次插入185 次删除
  1. 102 9
      Help/command/cmake_pkg_config.rst
  2. 2 1
      Help/command/install.rst
  3. 6 0
      Help/release/dev/cmake-pkg-config-import.rst
  4. 412 107
      Source/cmCMakePkgConfigCommand.cxx
  5. 5 5
      Source/cmMakefile.cxx
  6. 44 22
      Source/cmPkgConfigResolver.cxx
  7. 4 1
      Source/cmPkgConfigResolver.h
  8. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractEnv-stderr.txt
  9. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractEnv.cmake
  10. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractFields-stderr.txt
  11. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractFields.cmake
  12. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractMangle-stderr.txt
  13. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractMangle.cmake
  14. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractQuiet.cmake
  15. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractRequired-result.txt
  16. 4 0
      Tests/RunCMake/cmake_pkg_config/ExtractRequired-stderr.txt
  17. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractRequired.cmake
  18. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractReroot-stderr.txt
  19. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractReroot.cmake
  20. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractStrictness-BEST_EFFORT-stderr.txt
  21. 4 4
      Tests/RunCMake/cmake_pkg_config/ExtractStrictness-PERMISSIVE-stderr.txt
  22. 5 5
      Tests/RunCMake/cmake_pkg_config/ExtractStrictness-STRICT-stderr.txt
  23. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractStrictness.cmake
  24. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractUninstalled-stderr.txt
  25. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractUninstalled.cmake
  26. 17 17
      Tests/RunCMake/cmake_pkg_config/ExtractVersion-stderr.txt
  27. 0 0
      Tests/RunCMake/cmake_pkg_config/ExtractVersion.cmake
  28. 14 0
      Tests/RunCMake/cmake_pkg_config/ImportRequires-check.cmake
  29. 18 0
      Tests/RunCMake/cmake_pkg_config/ImportRequires.cmake
  30. 15 0
      Tests/RunCMake/cmake_pkg_config/ImportSimple-check.cmake
  31. 15 0
      Tests/RunCMake/cmake_pkg_config/ImportSimple.cmake
  32. 1 0
      Tests/RunCMake/cmake_pkg_config/ImportTransitiveFail-result.txt
  33. 11 0
      Tests/RunCMake/cmake_pkg_config/ImportTransitiveFail-stderr.txt
  34. 5 0
      Tests/RunCMake/cmake_pkg_config/ImportTransitiveFail.cmake
  35. 3 0
      Tests/RunCMake/cmake_pkg_config/ImportTransitiveVersion.cmake
  36. 5 0
      Tests/RunCMake/cmake_pkg_config/ImportTransitiveVersionFail-stderr.txt
  37. 4 0
      Tests/RunCMake/cmake_pkg_config/ImportTransitiveVersionFail.cmake
  38. 5 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/alpha.pc
  39. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/bravo.pc
  40. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/charlie.pc
  41. 5 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/delta.pc
  42. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/echo.pc
  43. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/golf.pc
  44. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/hotel.pc
  45. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/india.pc
  46. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/juliet.pc
  47. 6 0
      Tests/RunCMake/cmake_pkg_config/PackageRoot/import-simple.pc
  48. 8 0
      Tests/RunCMake/cmake_pkg_config/PopulateFoundVar-stderr.txt
  49. 7 0
      Tests/RunCMake/cmake_pkg_config/PopulateFoundVar.cmake
  50. 10 0
      Tests/RunCMake/cmake_pkg_config/PopulateMissing-check.cmake
  51. 19 0
      Tests/RunCMake/cmake_pkg_config/PopulateMissing.cmake
  52. 17 10
      Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake
  53. 0 0
      Tests/RunCMake/cmake_pkg_config/TestDirectories/Include/dummy-header.h
  54. 0 0
      Tests/RunCMake/cmake_pkg_config/TestDirectories/Library/libdummy
  55. 0 4
      Tests/RunCMake/cmake_pkg_config/TestRequired-stderr.txt

+ 102 - 9
Help/command/cmake_pkg_config.rst

@@ -12,9 +12,11 @@ Process pkg-config format package files.
 Synopsis
 Synopsis
 ^^^^^^^^
 ^^^^^^^^
 
 
-.. code-block:: cmake
+.. parsed-literal::
 
 
-  cmake_pkg_config(EXTRACT <package> [<version>] [...])
+  cmake_pkg_config(`EXTRACT`_ <package> [<version>] [...])
+  cmake_pkg_config(`POPULATE`_ <package> [<version>] [...])
+  cmake_pkg_config(`IMPORT`_ <package> [<version>] [...])
 
 
 Introduction
 Introduction
 ^^^^^^^^^^^^
 ^^^^^^^^^^^^
@@ -27,7 +29,31 @@ search patterns. The optional ``<version>`` string has the same format and
 semantics as a pkg-config style version specifier, with the exception that if
 semantics as a pkg-config style version specifier, with the exception that if
 no comparison operator is specified ``=`` is assumed.
 no comparison operator is specified ``=`` is assumed.
 
 
-.. _`common options`:
+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.
+
+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
+via the ``POPULATE`` command, but modification should generally be performed
+inside the corresponding package file, not downstream in CMake.
+
+Pkg-config targets are reused across commands. Once a given package name has
+been resolved via ``POPULATE`` or ``IMPORT`` (but not ``EXTRACT``), all future
+requests for the corresponding package name by those commands will resolve to
+the previously generated pkg-config target.
+
+``EXTRACT`` always performs the complete package name lookup in order to allow
+searches for multiple installations of the same package in custom dependency
+management schemes.
+
+Common Options
+^^^^^^^^^^^^^^
 
 
 There are multiple signatures for this command, and some of the options are
 There are multiple signatures for this command, and some of the options are
 common between them. They are:
 common between them. They are:
@@ -143,7 +169,7 @@ common between them. They are:
      library directory paths and ``pc_sysrootdir`` will be set to ``/``
      library directory paths and ``pc_sysrootdir`` will be set to ``/``
 
 
 ``TOP_BUILD_DIR <path>``
 ``TOP_BUILD_DIR <path>``
-  Overrides the top build directory path used to derived the ``pc_top_builddir``
+  Overrides the top build directory path used to derive the ``pc_top_builddir``
   package variable.
   package variable.
 
 
   When this option is not provided, the default top build directory path is
   When this option is not provided, the default top build directory path is
@@ -154,29 +180,41 @@ common between them. They are:
   #. If no top build directory path is available, the ``pc_top_builddir``
   #. If no top build directory path is available, the ``pc_top_builddir``
      package variable is not set
      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
+  CMake-native target name (not a pkg-config target).
+
+  When a given package name appears in the ``Requires`` list of a package, it
+  will be fulfilled with the associated CMake target. This behavior applies to
+  all dependencies in the pkg-config graph that have not been previously
+  populated.
+
 Signatures
 Signatures
 ^^^^^^^^^^
 ^^^^^^^^^^
 
 
 .. signature::
 .. signature::
   cmake_pkg_config(EXTRACT <package> [<version>] [...])
   cmake_pkg_config(EXTRACT <package> [<version>] [...])
 
 
+  .. versionadded:: 3.31
+
   Extract the contents of the package into variables.
   Extract the contents of the package into variables.
 
 
   .. code-block:: cmake
   .. code-block:: cmake
 
 
     cmake_pkg_config(EXTRACT <package> [<version>]
     cmake_pkg_config(EXTRACT <package> [<version>]
                     [REQUIRED] [EXACT] [QUIET]
                     [REQUIRED] [EXACT] [QUIET]
+                    [SYSTEM_INCLUDE_DIRS <path>...]
+                    [SYSTEM_LIBRARY_DIRS <path>...]
+                    [ALLOW_SYSTEM_INCLUDES <bool>]
+                    [ALLOW_SYSTEM_LIBS <bool>]
                     [STRICTNESS <mode>]
                     [STRICTNESS <mode>]
                     [ENV_MODE <mode>]
                     [ENV_MODE <mode>]
                     [PC_LIBDIR <path>...]
                     [PC_LIBDIR <path>...]
                     [PC_PATH <path>...]
                     [PC_PATH <path>...]
                     [DISABLE_UNINSTALLED <bool>]
                     [DISABLE_UNINSTALLED <bool>]
                     [PC_SYSROOT_DIR <path>]
                     [PC_SYSROOT_DIR <path>]
-                    [TOP_BUILD_DIR <path>]
-                    [SYSTEM_INCLUDE_DIRS <path>...]
-                    [SYSTEM_LIBRARY_DIRS <path>...]
-                    [ALLOW_SYSTEM_INCLUDES <bool>]
-                    [ALLOW_SYSTEM_LIBS <bool>])
+                    [TOP_BUILD_DIR <path>])
 
 
 The following variables will be populated from the contents of package file:
 The following variables will be populated from the contents of package file:
 
 
@@ -261,3 +299,58 @@ The following variables will be populated from the contents of package file:
   #. ``CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS``
   #. ``CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS``
   #. If the ``PKG_CONFIG_ALLOW_SYSTEM_LIBS`` environment variable is defined
   #. If the ``PKG_CONFIG_ALLOW_SYSTEM_LIBS`` environment variable is defined
      the flags are preserved, otherwise they are filtered during flag mangling.
      the flags are preserved, otherwise they are filtered during flag mangling.
+
+.. signature::
+  cmake_pkg_config(POPULATE <package> [<version>] [...])
+
+  .. versionadded:: 4.1
+
+  Populate a package in the pkg-config target namespace
+
+  .. code-block:: cmake
+
+    cmake_pkg_config(POPULATE <package> [<version>]
+                    [REQUIRED] [EXACT] [QUIET]
+                    [BIND_PC_REQUIRES <<name>=<target>>...]
+                    [STRICTNESS <mode>]
+                    [ENV_MODE <mode>]
+                    [PC_LIBDIR <path>...]
+                    [PC_PATH <path>...]
+                    [DISABLE_UNINSTALLED <bool>]
+                    [PC_SYSROOT_DIR <path>]
+                    [TOP_BUILD_DIR <path>])
+
+``POPULATE`` enables manual control of resolution of a given package's
+``Requires`` list without importing onto a native CMake target. Once populated,
+a package and its dependencies will be used for resolution of all future
+``POPULATE`` and ``IMPORT`` commands.
+
+A ``PKGCONFIG_<package>_FOUND`` variable will be set to indicate whether the
+package was found.
+
+.. signature::
+  cmake_pkg_config(IMPORT <package> [<version>] [...])
+
+  .. versionadded:: 4.1
+
+  Import a pkg-config target as a CMake :prop_tgt:`IMPORTED` target
+
+  .. code-block:: cmake
+
+    cmake_pkg_config(IMPORT <package> [<version>]
+                    [REQUIRED] [EXACT] [QUIET]
+                    [BIND_PC_REQUIRES <<name>=<target>>...]
+                    [STRICTNESS <mode>]
+                    [ENV_MODE <mode>]
+                    [PC_LIBDIR <path>...]
+                    [PC_PATH <path>...]
+                    [DISABLE_UNINSTALLED <bool>]
+                    [PC_SYSROOT_DIR <path>]
+                    [TOP_BUILD_DIR <path>])
+
+Creates a native CMake ``IMPORTED`` target that can be linked to via
+:command:`target_link_libraries`. This new target is named
+``PkgConfig::<package>``.
+
+A ``PKGCONFIG_<package>_FOUND`` variable will be set to indicate whether the
+package was found.

+ 2 - 1
Help/command/install.rst

@@ -46,7 +46,8 @@ are executed in order during installation.
   and the order that install rules added in different subdirectories will run is
   and the order that install rules added in different subdirectories will run is
   not guaranteed.
   not guaranteed.
 
 
-.. _`common options`:
+Common Options
+""""""""""""""
 
 
 There are multiple signatures for this command.  Some of them define
 There are multiple signatures for this command.  Some of them define
 installation options for files and targets.  Options common to
 installation options for files and targets.  Options common to

+ 6 - 0
Help/release/dev/cmake-pkg-config-import.rst

@@ -0,0 +1,6 @@
+cmake-pkg-config-import
+-----------------------
+
+* The :command:`cmake_pkg_config` command now supports the ``IMPORT`` and
+  ``POPULATE`` subcommands for interfacing CMake targets with pkg-config based
+  dependencies.

+ 412 - 107
Source/cmCMakePkgConfigCommand.cxx

@@ -6,6 +6,7 @@
 #include <cstdio>
 #include <cstdio>
 #include <memory>
 #include <memory>
 #include <string>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
@@ -28,12 +29,14 @@
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSubcommandTable.h"
 #include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
+#include "cmTarget.h"
 #include "cmValue.h"
 #include "cmValue.h"
 #include <cmllpkgc/llpkgc.h>
 #include <cmllpkgc/llpkgc.h>
 
 
 // IWYU wants this
 // IWYU wants this
 namespace {
 namespace {
 struct ExtractArguments;
 struct ExtractArguments;
+struct PopulateArguments;
 }
 }
 
 
 namespace {
 namespace {
@@ -441,89 +444,59 @@ void CollectEnv(cmMakefile& mf, cmPkgConfigEnv& env,
   *env.SysLibs += GetPkgConfSysLibs(mf);
   *env.SysLibs += GetPkgConfSysLibs(mf);
 }
 }
 
 
-cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
-                                             cmExecutionStatus& status)
+struct ImportEnv
 {
 {
+  bool required;
+  bool quiet;
+  bool exact;
+  bool err;
+  CommonArguments::StrictnessType strictness;
+  cmExecutionStatus& status;
+};
 
 
-  auto& mf = status.GetMakefile();
-
-  if (!args.CheckArgs(status)) {
-    return {};
-  }
-
-  auto warn_or_error = [&](std::string const& err) {
-    if (args.Required) {
-      status.SetError(err);
-      cmSystemTools::SetFatalErrorOccurred();
-    } else if (!args.Quiet) {
-      mf.IssueMessage(MessageType::WARNING, err);
-    }
-  };
-
-  cm::filesystem::path path{ *args.Package };
-
-  cmPkgConfigEnv env;
-
-  if (args.PcLibdir) {
-    env.LibDirs = std::move(*args.PcLibdir);
-  }
-
-  if (args.PcPath) {
-    env.Path = std::move(*args.PcPath);
-  }
-
-  if (args.DisableUninstalled) {
-    env.DisableUninstalled = args.DisableUninstalled;
-  }
-
-  if (args.SysrootDir) {
-    env.SysrootDir = std::move(*args.SysrootDir);
-  }
-
-  if (args.TopBuildDir) {
-    env.TopBuildDir = std::move(*args.TopBuildDir);
+void warn_or_error(std::string const& err, ImportEnv& imEnv)
+{
+  if (imEnv.required) {
+    imEnv.status.SetError(err);
+    cmSystemTools::SetFatalErrorOccurred();
+  } else if (!imEnv.quiet) {
+    imEnv.status.GetMakefile().IssueMessage(MessageType::WARNING, err);
   }
   }
+  imEnv.err = true;
+}
 
 
-  CollectEnv(mf, env, args.EnvMode);
+cm::optional<cmPkgConfigResult> ReadPackage(std::string const& package,
+                                            ImportEnv& imEnv,
+                                            cmPkgConfigEnv& pcEnv)
+{
+  cm::optional<cmPkgConfigResult> result;
+  cm::filesystem::path path{ package };
 
 
   if (path.extension() == ".pc") {
   if (path.extension() == ".pc") {
     if (!cmSystemTools::FileExists(path.string())) {
     if (!cmSystemTools::FileExists(path.string())) {
-      warn_or_error(cmStrCat("Could not find '", *args.Package, "'"));
-      return {};
+      return result;
     }
     }
   } else {
   } else {
 
 
-    std::vector<std::string> search;
-    if (env.Path) {
-      search = *env.Path;
-      if (env.LibDirs) {
-        search += *env.LibDirs;
-      }
-    } else if (env.LibDirs) {
-      search = *env.LibDirs;
-    }
-
-    if (env.DisableUninstalled && !*env.DisableUninstalled) {
+    if (pcEnv.DisableUninstalled && !*pcEnv.DisableUninstalled) {
       auto uninstalled = path;
       auto uninstalled = path;
       uninstalled.concat("-uninstalled.pc");
       uninstalled.concat("-uninstalled.pc");
       uninstalled =
       uninstalled =
-        cmSystemTools::FindFile(uninstalled.string(), search, true);
+        cmSystemTools::FindFile(uninstalled.string(), pcEnv.search, true);
       if (uninstalled.empty()) {
       if (uninstalled.empty()) {
-        path =
-          cmSystemTools::FindFile(path.concat(".pc").string(), search, true);
+        path = cmSystemTools::FindFile(path.concat(".pc").string(),
+                                       pcEnv.search, true);
         if (path.empty()) {
         if (path.empty()) {
-          warn_or_error(cmStrCat("Could not find '", *args.Package, "'"));
-          return {};
+          return result;
         }
         }
       } else {
       } else {
         path = uninstalled;
         path = uninstalled;
       }
       }
     } else {
     } else {
-      path =
-        cmSystemTools::FindFile(path.concat(".pc").string(), search, true);
+      path = cmSystemTools::FindFile(path.concat(".pc").string(), pcEnv.search,
+                                     true);
       if (path.empty()) {
       if (path.empty()) {
-        warn_or_error(cmStrCat("Could not find '", *args.Package, "'"));
-        return {};
+        return result;
       }
       }
     }
     }
   }
   }
@@ -534,8 +507,9 @@ cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
   cmsys::ifstream ifs(path.string().c_str(), std::ios::binary);
   cmsys::ifstream ifs(path.string().c_str(), std::ios::binary);
 
 
   if (!ifs) {
   if (!ifs) {
-    warn_or_error(cmStrCat("Could not open file '", path.string(), "'"));
-    return {};
+    warn_or_error(cmStrCat("Could not open file '", path.string(), "'"),
+                  imEnv);
+    return result;
   }
   }
 
 
   std::unique_ptr<char[]> buf(new char[len]);
   std::unique_ptr<char[]> buf(new char[len]);
@@ -543,8 +517,9 @@ cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
 
 
   // Shouldn't have hit eof on previous read, should hit eof now
   // Shouldn't have hit eof on previous read, should hit eof now
   if (ifs.fail() || ifs.eof() || ifs.get() != EOF) {
   if (ifs.fail() || ifs.eof() || ifs.get() != EOF) {
-    warn_or_error(cmStrCat("Error while reading file '", path.string(), "'"));
-    return {};
+    warn_or_error(cmStrCat("Error while reading file '", path.string(), "'"),
+                  imEnv);
+    return result;
   }
   }
 
 
   using StrictnessType = CommonArguments::StrictnessType;
   using StrictnessType = CommonArguments::StrictnessType;
@@ -552,53 +527,161 @@ cm::optional<cmPkgConfigResult> HandleCommon(CommonArguments& args,
   cmPkgConfigParser parser;
   cmPkgConfigParser parser;
   auto err = parser.Finish(buf.get(), len);
   auto err = parser.Finish(buf.get(), len);
 
 
-  if (args.Strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
+  if (imEnv.strictness != StrictnessType::STRICTNESS_BEST_EFFORT &&
       err != PCE_OK) {
       err != PCE_OK) {
-    warn_or_error(cmStrCat("Parsing failed for file '", path.string(), "'"));
-    return {};
+    warn_or_error(cmStrCat("Parsing failed for file '", path.string(), "'"),
+                  imEnv);
+    return result;
   }
   }
 
 
-  cm::optional<cmPkgConfigResult> result;
-  if (args.Strictness == StrictnessType::STRICTNESS_STRICT) {
-    result = cmPkgConfigResolver::ResolveStrict(parser.Data(), std::move(env));
-  } else if (args.Strictness == StrictnessType::STRICTNESS_PERMISSIVE) {
-    result =
-      cmPkgConfigResolver::ResolvePermissive(parser.Data(), std::move(env));
+  if (imEnv.strictness == StrictnessType::STRICTNESS_STRICT) {
+    result = cmPkgConfigResolver::ResolveStrict(parser.Data(), pcEnv);
+  } else if (imEnv.strictness == StrictnessType::STRICTNESS_PERMISSIVE) {
+    result = cmPkgConfigResolver::ResolvePermissive(parser.Data(), pcEnv);
   } else {
   } else {
-    result =
-      cmPkgConfigResolver::ResolveBestEffort(parser.Data(), std::move(env));
+    result = cmPkgConfigResolver::ResolveBestEffort(parser.Data(), pcEnv);
   }
   }
 
 
   if (!result) {
   if (!result) {
-    warn_or_error(
-      cmStrCat("Resolution failed for file '", path.string(), "'"));
-  } else if (args.Exact) {
+    warn_or_error(cmStrCat("Resolution failed for file '", path.string(), "'"),
+                  imEnv);
+  }
+
+  return result;
+}
+
+cm::optional<cmPkgConfigResult> ImportPackage(
+  std::string const& package, cm::optional<std::string> version,
+  ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
+{
+  auto result = ReadPackage(package, imEnv, pcEnv);
+
+  if (!result) {
+    if (!imEnv.err) {
+      warn_or_error(cmStrCat("Could not find pkg-config: '", package, "'"),
+                    imEnv);
+    }
+    return result;
+  }
+
+  if (imEnv.exact) {
     std::string ver;
     std::string ver;
 
 
-    if (args.Version) {
-      ver = cmPkgConfigResolver::ParseVersion(*args.Version).Version;
+    if (version) {
+      ver = cmPkgConfigResolver::ParseVersion(*version).Version;
     }
     }
 
 
     if (ver != result->Version()) {
     if (ver != result->Version()) {
       warn_or_error(
       warn_or_error(
-        cmStrCat("Package '", *args.Package, "' version '", result->Version(),
-                 "' does not meet exact version requirement '", ver, "'"));
+        cmStrCat("Package '", package, "' version '", result->Version(),
+                 "' does not meet exact version requirement '", ver, "'"),
+        imEnv);
       return {};
       return {};
     }
     }
 
 
-  } else if (args.Version) {
-    auto rv = cmPkgConfigResolver::ParseVersion(*args.Version);
+  } else if (version) {
+    auto rv = cmPkgConfigResolver::ParseVersion(*version);
     if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) {
     if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) {
       warn_or_error(
       warn_or_error(
-        cmStrCat("Package '", *args.Package, "' version '", result->Version(),
-                 "' does not meet version requirement '", *args.Version, "'"));
+        cmStrCat("Package '", package, "' version '", result->Version(),
+                 "' does not meet version requirement '", *version, "'"),
+        imEnv);
       return {};
       return {};
     }
     }
   }
   }
 
 
+  result->env = &pcEnv;
   return result;
   return result;
 }
 }
 
 
+struct pkgStackEntry
+{
+  cmPkgConfigVersionReq ver;
+  std::string parent;
+};
+
+cm::optional<cmPkgConfigResult> ImportPackage(
+  std::string const& package, std::vector<pkgStackEntry> const& reqs,
+  ImportEnv& imEnv, cmPkgConfigEnv& pcEnv)
+{
+  auto result = ReadPackage(package, imEnv, pcEnv);
+
+  if (!result) {
+    if (!imEnv.err) {
+      std::string req_str = cmStrCat("'", reqs.begin()->parent, "'");
+      for (auto it = reqs.begin() + 1; it != reqs.end(); ++it) {
+        req_str = cmStrCat(req_str, ", '", it->parent, "'");
+      }
+      warn_or_error(cmStrCat("Could not find pkg-config: '", package,
+                             "' required by: ", req_str),
+                    imEnv);
+    }
+    return result;
+  }
+
+  auto ver = result->Version();
+  for (auto const& req : reqs) {
+
+    if (!cmPkgConfigResolver::CheckVersion(req.ver, ver)) {
+      warn_or_error(cmStrCat("Package '", package, "' version '", ver,
+                             "' does not meet version requirement '",
+                             req.ver.string(), "' ", "of '", req.parent, "'"),
+                    imEnv);
+      return {};
+    }
+  }
+
+  result->env = &pcEnv;
+  return result;
+}
+
+cm::optional<std::pair<cmPkgConfigEnv, ImportEnv>> HandleCommon(
+  CommonArguments& args, cmExecutionStatus& status)
+{
+
+  auto& mf = status.GetMakefile();
+
+  if (!args.CheckArgs(status)) {
+    return {};
+  }
+
+  cmPkgConfigEnv pcEnv;
+
+  if (args.PcLibdir) {
+    pcEnv.LibDirs = std::move(*args.PcLibdir);
+  }
+
+  if (args.PcPath) {
+    pcEnv.Path = std::move(*args.PcPath);
+  }
+
+  pcEnv.DisableUninstalled = args.DisableUninstalled;
+
+  if (args.SysrootDir) {
+    pcEnv.SysrootDir = std::move(*args.SysrootDir);
+  }
+
+  if (args.TopBuildDir) {
+    pcEnv.TopBuildDir = std::move(*args.TopBuildDir);
+  }
+
+  CollectEnv(mf, pcEnv, args.EnvMode);
+
+  if (pcEnv.Path) {
+    pcEnv.search = *pcEnv.Path;
+    if (pcEnv.LibDirs) {
+      pcEnv.search += *pcEnv.LibDirs;
+    }
+  } else if (pcEnv.LibDirs) {
+    pcEnv.search = *pcEnv.LibDirs;
+  }
+
+  return std::pair<cmPkgConfigEnv, ImportEnv>{
+    pcEnv,
+    { args.Required, args.Quiet, args.Exact, false, args.Strictness, status }
+  };
+}
+
 struct ExtractArguments : CommonArguments
 struct ExtractArguments : CommonArguments
 {
 {
   cm::optional<bool> AllowSystemIncludes;
   cm::optional<bool> AllowSystemIncludes;
@@ -623,35 +706,41 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
 
 
   std::vector<std::string> unparsed;
   std::vector<std::string> unparsed;
   auto parsedArgs = ExtractParser.Parse(args, &unparsed);
   auto parsedArgs = ExtractParser.Parse(args, &unparsed);
-  auto maybeResolved = HandleCommon(parsedArgs, status);
+  auto maybeEnv = HandleCommon(parsedArgs, status);
 
 
-  if (!maybeResolved) {
+  if (!maybeEnv) {
     return !parsedArgs.Required;
     return !parsedArgs.Required;
   }
   }
+  auto& pcEnv = maybeEnv->first;
+  auto& imEnv = maybeEnv->second;
 
 
-  auto& resolved = *maybeResolved;
-  auto version = resolved.Version();
+  auto maybePackage =
+    ImportPackage(*parsedArgs.Package, parsedArgs.Version, imEnv, pcEnv);
+  if (!maybePackage) {
+    return !parsedArgs.Required;
+  }
+  auto& package = *maybePackage;
 
 
   if (parsedArgs.AllowSystemIncludes) {
   if (parsedArgs.AllowSystemIncludes) {
-    resolved.env.AllowSysCflags = *parsedArgs.AllowSystemIncludes;
+    pcEnv.AllowSysCflags = *parsedArgs.AllowSystemIncludes;
   }
   }
 
 
   if (parsedArgs.AllowSystemLibs) {
   if (parsedArgs.AllowSystemLibs) {
-    resolved.env.AllowSysLibs = *parsedArgs.AllowSystemLibs;
+    pcEnv.AllowSysLibs = *parsedArgs.AllowSystemLibs;
   }
   }
 
 
   if (parsedArgs.SystemIncludeDirs) {
   if (parsedArgs.SystemIncludeDirs) {
-    resolved.env.SysCflags = *parsedArgs.SystemIncludeDirs;
+    pcEnv.SysCflags = *parsedArgs.SystemIncludeDirs;
   }
   }
 
 
   if (parsedArgs.SystemLibraryDirs) {
   if (parsedArgs.SystemLibraryDirs) {
-    resolved.env.SysLibs = *parsedArgs.SystemLibraryDirs;
+    pcEnv.SysLibs = *parsedArgs.SystemLibraryDirs;
   }
   }
 
 
   auto& mf = status.GetMakefile();
   auto& mf = status.GetMakefile();
-  mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", resolved.Name());
-  mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", resolved.Description());
-  mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", version);
+  mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", package.Name());
+  mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", package.Description());
+  mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", package.Version());
 
 
   auto make_list = [&](char const* def,
   auto make_list = [&](char const* def,
                        std::vector<cmPkgConfigDependency> const& deps) {
                        std::vector<cmPkgConfigDependency> const& deps) {
@@ -665,26 +754,26 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
     mf.AddDefinition(def, cmList::to_string(vec));
     mf.AddDefinition(def, cmList::to_string(vec));
   };
   };
 
 
-  make_list("CMAKE_PKG_CONFIG_CONFLICTS", resolved.Conflicts());
-  make_list("CMAKE_PKG_CONFIG_PROVIDES", resolved.Provides());
-  make_list("CMAKE_PKG_CONFIG_REQUIRES", resolved.Requires());
-  make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", resolved.Requires(true));
+  make_list("CMAKE_PKG_CONFIG_CONFLICTS", package.Conflicts());
+  make_list("CMAKE_PKG_CONFIG_PROVIDES", package.Provides());
+  make_list("CMAKE_PKG_CONFIG_REQUIRES", package.Requires());
+  make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", package.Requires(true));
 
 
-  auto cflags = resolved.Cflags();
+  auto cflags = package.Cflags();
   mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS", cflags.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS", cflags.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES",
   mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES",
                    cmList::to_string(cflags.Includes));
                    cmList::to_string(cflags.Includes));
   mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS",
   mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS",
                    cmList::to_string(cflags.CompileOptions));
                    cmList::to_string(cflags.CompileOptions));
 
 
-  cflags = resolved.Cflags(true);
+  cflags = package.Cflags(true);
   mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS_PRIVATE", cflags.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS_PRIVATE", cflags.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES_PRIVATE",
   mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES_PRIVATE",
                    cmList::to_string(cflags.Includes));
                    cmList::to_string(cflags.Includes));
   mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE",
   mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE",
                    cmList::to_string(cflags.CompileOptions));
                    cmList::to_string(cflags.CompileOptions));
 
 
-  auto libs = resolved.Libs();
+  auto libs = package.Libs();
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS",
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS",
                    cmList::to_string(libs.LibDirs));
                    cmList::to_string(libs.LibDirs));
@@ -693,7 +782,7 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
   mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS",
   mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS",
                    cmList::to_string(libs.LinkOptions));
                    cmList::to_string(libs.LinkOptions));
 
 
-  libs = resolved.Libs(true);
+  libs = package.Libs(true);
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS_PRIVATE", libs.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS_PRIVATE", libs.Flagline);
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE",
   mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE",
                    cmList::to_string(libs.LibDirs));
                    cmList::to_string(libs.LibDirs));
@@ -704,6 +793,220 @@ bool HandleExtractCommand(std::vector<std::string> const& args,
 
 
   return true;
   return true;
 }
 }
+
+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)
+{
+  auto* tgt = mf.AddForeignTarget("pkgcfg", name);
+
+  tgt->AppendProperty("VERSION", pkg.Version());
+
+  auto libs = pkg.Libs();
+  for (auto const& flag : libs.LibNames) {
+    tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", flag.substr(2));
+  }
+  for (auto const& flag : libs.LibDirs) {
+    tgt->AppendProperty("INTERFACE_LINK_DIRECTORIES", flag.substr(2));
+  }
+  tgt->AppendProperty("INTERFACE_LINK_OPTIONS",
+                      cmList::to_string(libs.LinkOptions));
+
+  auto cflags = pkg.Cflags();
+  for (auto const& flag : cflags.Includes) {
+    tgt->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", flag.substr(2));
+  }
+  tgt->AppendProperty("INTERFACE_COMPILE_OPTIONS",
+                      cmList::to_string(cflags.CompileOptions));
+
+  for (auto& dep : pkg.Requires()) {
+    auto it = providers.find(dep.Name);
+    if (it != providers.end()) {
+      tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", it->second);
+      continue;
+    }
+
+    tgt->AppendProperty("INTERFACE_LINK_LIBRARIES",
+                        cmStrCat("@foreign_pkgcfg::", dep.Name));
+  }
+  return tgt;
+}
+
+bool CheckPackageDependencies(
+  std::string const& name, cmPkgConfigResult& pkg, pkgStack& inStack,
+  std::unordered_map<std::string, cmPkgConfigResult>& outStack,
+  pkgProviders& providers, ImportEnv& imEnv)
+{
+  for (auto& dep : pkg.Requires()) {
+    auto prov_it = providers.find(dep.Name);
+    if (prov_it != providers.end()) {
+      continue;
+    }
+
+    auto* tgt = imEnv.status.GetMakefile().FindTargetToUse(
+      cmStrCat("@foreign_pkgcfg::", dep.Name),
+      cmStateEnums::TargetDomain::FOREIGN);
+    if (tgt) {
+      auto ver = tgt->GetProperty("VERSION");
+      if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, *ver)) {
+        warn_or_error(cmStrCat("Package '", dep.Name, "' version '", *ver,
+                               "' does not meet version requirement '",
+                               dep.VerReq.string(), "' ", "of '", name, "'"),
+                      imEnv);
+        return false;
+      }
+      continue;
+    }
+
+    auto it = outStack.find(dep.Name);
+    if (it != outStack.end()) {
+      auto ver = it->second.Version();
+      if (!cmPkgConfigResolver::CheckVersion(dep.VerReq, ver)) {
+        warn_or_error(cmStrCat("Package '", dep.Name, "' version '", ver,
+                               "' does not meet version requirement '",
+                               dep.VerReq.string(), "' ", "of '", name, "'"),
+                      imEnv);
+        return false;
+      }
+      continue;
+    }
+
+    inStack[dep.Name].emplace_back(
+      pkgStackEntry{ std::move(dep.VerReq), name });
+  }
+
+  return true;
+}
+
+struct PopulateArguments : CommonArguments
+{
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> providers;
+};
+
+auto const PopulateParser =
+  BIND_COMMON(PopulateArguments)
+    .Bind("BIND_PC_REQUIRES"_s, &PopulateArguments::providers);
+
+std::pair<bool, bool> PopulatePCTarget(PopulateArguments& args,
+                                       cmExecutionStatus& status)
+{
+
+  auto& mf = status.GetMakefile();
+  auto maybeEnv = HandleCommon(args, status);
+
+  if (!maybeEnv) {
+    return { !args.Required, false };
+  }
+  auto& pcEnv = maybeEnv->first;
+  auto& imEnv = maybeEnv->second;
+
+  pkgProviders 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),
+                          provider_str.substr(assignment + 1));
+      } else {
+        imEnv.status.SetError(cmStrCat(
+          "No '=' found in BIND_PC_REQUIRES argument '", provider_str, "'"));
+        cmSystemTools::SetFatalErrorOccurred();
+        return { false, false };
+      }
+    }
+  }
+
+  pkgStack inStack;
+  std::unordered_map<std::string, cmPkgConfigResult> outStack;
+
+  auto maybePackage = ImportPackage(*args.Package, args.Version, imEnv, pcEnv);
+  if (!maybePackage) {
+    return { !args.Required, false };
+  }
+  imEnv.exact = false;
+
+  if (!CheckPackageDependencies(*args.Package, *maybePackage, inStack,
+                                outStack, providers, imEnv)) {
+    return { !args.Required, false };
+  }
+  outStack[*args.Package] = std::move(*maybePackage);
+
+  while (!inStack.empty()) {
+    auto name = inStack.begin()->first;
+    auto reqs = inStack.begin()->second;
+    maybePackage = ImportPackage(name, reqs, imEnv, pcEnv);
+    if (!maybePackage) {
+      return { !args.Required, false };
+    }
+    if (!CheckPackageDependencies(name, *maybePackage, inStack, outStack,
+                                  providers, imEnv)) {
+      return { !args.Required, false };
+    }
+    inStack.erase(name);
+    outStack[std::move(name)] = std::move(*maybePackage);
+  }
+
+  for (auto& entry : outStack) {
+    CreateCMakeTarget(entry.first, entry.second, providers, mf);
+  }
+
+  return { true, true };
+}
+
+bool HandlePopulateCommand(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 found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
+
+  auto& mf = status.GetMakefile();
+
+  if (mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) {
+    mf.AddDefinition(found_var, "TRUE");
+    return true;
+  }
+
+  auto result = PopulatePCTarget(parsedArgs, status);
+  mf.AddDefinition(found_var, result.second ? "TRUE" : "FALSE");
+  return result.first;
+}
+
+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 found_var = cmStrCat("PKGCONFIG_", *parsedArgs.Package, "_FOUND");
+
+  auto& mf = status.GetMakefile();
+
+  if (mf.FindTargetToUse(local_name)) {
+    mf.AddDefinition(found_var, "TRUE");
+    return true;
+  }
+
+  if (!mf.FindTargetToUse(foreign_name, cmStateEnums::TargetDomain::FOREIGN)) {
+    auto result = PopulatePCTarget(parsedArgs, status);
+    if (!result.second) {
+      mf.AddDefinition(found_var, "FALSE");
+      return result.first;
+    }
+  }
+
+  mf.AddDefinition(found_var, "TRUE");
+  auto* tgt = mf.AddImportedTarget(
+    local_name, cmStateEnums::TargetType::INTERFACE_LIBRARY, false);
+  tgt->AppendProperty("INTERFACE_LINK_LIBRARIES", foreign_name);
+  return true;
+}
+
 } // namespace
 } // namespace
 
 
 bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
 bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
@@ -716,6 +1019,8 @@ bool cmCMakePkgConfigCommand(std::vector<std::string> const& args,
 
 
   static cmSubcommandTable const subcommand{
   static cmSubcommandTable const subcommand{
     { "EXTRACT"_s, HandleExtractCommand },
     { "EXTRACT"_s, HandleExtractCommand },
+    { "POPULATE"_s, HandlePopulateCommand },
+    { "IMPORT"_s, HandleImportCommand },
   };
   };
 
 
   return subcommand(args[0], args, status);
   return subcommand(args[0], args, status);

+ 5 - 5
Source/cmMakefile.cxx

@@ -3805,14 +3805,14 @@ cmTarget* cmMakefile::AddImportedTarget(std::string const& name,
 cmTarget* cmMakefile::AddForeignTarget(std::string const& origin,
 cmTarget* cmMakefile::AddForeignTarget(std::string const& origin,
                                        std::string const& name)
                                        std::string const& name)
 {
 {
+  auto foreign_name = cmStrCat("@foreign_", origin, "::", name);
   std::unique_ptr<cmTarget> target(new cmTarget(
   std::unique_ptr<cmTarget> target(new cmTarget(
-    cmStrCat("@foreign_", origin, "::", name),
-    cmStateEnums::TargetType::INTERFACE_LIBRARY, cmTarget::Visibility::Foreign,
-    this, cmTarget::PerConfig::Yes));
+    foreign_name, cmStateEnums::TargetType::INTERFACE_LIBRARY,
+    cmTarget::Visibility::Foreign, this, cmTarget::PerConfig::Yes));
 
 
-  this->ImportedTargets[name] = target.get();
+  this->ImportedTargets[foreign_name] = target.get();
   this->GetGlobalGenerator()->IndexTarget(target.get());
   this->GetGlobalGenerator()->IndexTarget(target.get());
-  this->GetStateSnapshot().GetDirectory().AddImportedTargetName(name);
+  this->GetStateSnapshot().GetDirectory().AddImportedTargetName(foreign_name);
 
 
   this->ImportedTargetsOwned.push_back(std::move(target));
   this->ImportedTargetsOwned.push_back(std::move(target));
   return this->ImportedTargetsOwned.back().get();
   return this->ImportedTargetsOwned.back().get();

+ 44 - 22
Source/cmPkgConfigResolver.cxx

@@ -16,6 +16,7 @@
 #include <cm/string_view>
 #include <cm/string_view>
 
 
 #include "cmPkgConfigParser.h"
 #include "cmPkgConfigParser.h"
+#include "cmStringAlgorithms.h"
 
 
 namespace {
 namespace {
 
 
@@ -57,6 +58,27 @@ std::string AppendAndTrim(std::string& str, cm::string_view sv)
 
 
 } // namespace
 } // namespace
 
 
+std::string cmPkgConfigVersionReq::string() const
+{
+  switch (Operation) {
+    case ANY:
+      return "";
+    case LT:
+      return cmStrCat("<", Version);
+    case LT_EQ:
+      return cmStrCat("<=", Version);
+    case EQ:
+      return cmStrCat("=", Version);
+    case NEQ:
+      return cmStrCat("!=", Version);
+    case GT_EQ:
+      return cmStrCat(">=", Version);
+    case GT:
+      return cmStrCat(">", Version);
+  }
+  return "";
+}
+
 std::string cmPkgConfigResult::StrOrDefault(std::string const& key,
 std::string cmPkgConfigResult::StrOrDefault(std::string const& key,
                                             cm::string_view def)
                                             cm::string_view def)
 {
 {
@@ -127,24 +149,24 @@ cmPkgConfigCflagsResult cmPkgConfigResult::Cflags(bool priv)
 
 
   auto tokens = cmPkgConfigResolver::TokenizeFlags(cflags);
   auto tokens = cmPkgConfigResolver::TokenizeFlags(cflags);
 
 
-  if (env.AllowSysCflags) {
-    if (env.SysrootDir) {
-      return cmPkgConfigResolver::MangleCflags(tokens, *env.SysrootDir);
+  if (env->AllowSysCflags) {
+    if (env->SysrootDir) {
+      return cmPkgConfigResolver::MangleCflags(tokens, *env->SysrootDir);
     }
     }
     return cmPkgConfigResolver::MangleCflags(tokens);
     return cmPkgConfigResolver::MangleCflags(tokens);
   }
   }
 
 
-  if (env.SysCflags) {
-    if (env.SysrootDir) {
-      return cmPkgConfigResolver::MangleCflags(tokens, *env.SysrootDir,
-                                               *env.SysCflags);
+  if (env->SysCflags) {
+    if (env->SysrootDir) {
+      return cmPkgConfigResolver::MangleCflags(tokens, *env->SysrootDir,
+                                               *env->SysCflags);
     }
     }
-    return cmPkgConfigResolver::MangleCflags(tokens, *env.SysCflags);
+    return cmPkgConfigResolver::MangleCflags(tokens, *env->SysCflags);
   }
   }
 
 
-  if (env.SysrootDir) {
+  if (env->SysrootDir) {
     return cmPkgConfigResolver::MangleCflags(
     return cmPkgConfigResolver::MangleCflags(
-      tokens, *env.SysrootDir, std::vector<std::string>{ "/usr/include" });
+      tokens, *env->SysrootDir, std::vector<std::string>{ "/usr/include" });
   }
   }
 
 
   return cmPkgConfigResolver::MangleCflags(
   return cmPkgConfigResolver::MangleCflags(
@@ -160,24 +182,24 @@ cmPkgConfigLibsResult cmPkgConfigResult::Libs(bool priv)
 
 
   auto tokens = cmPkgConfigResolver::TokenizeFlags(it->second);
   auto tokens = cmPkgConfigResolver::TokenizeFlags(it->second);
 
 
-  if (env.AllowSysLibs) {
-    if (env.SysrootDir) {
-      return cmPkgConfigResolver::MangleLibs(tokens, *env.SysrootDir);
+  if (env->AllowSysLibs) {
+    if (env->SysrootDir) {
+      return cmPkgConfigResolver::MangleLibs(tokens, *env->SysrootDir);
     }
     }
     return cmPkgConfigResolver::MangleLibs(tokens);
     return cmPkgConfigResolver::MangleLibs(tokens);
   }
   }
 
 
-  if (env.SysLibs) {
-    if (env.SysrootDir) {
-      return cmPkgConfigResolver::MangleLibs(tokens, *env.SysrootDir,
-                                             *env.SysLibs);
+  if (env->SysLibs) {
+    if (env->SysrootDir) {
+      return cmPkgConfigResolver::MangleLibs(tokens, *env->SysrootDir,
+                                             *env->SysLibs);
     }
     }
-    return cmPkgConfigResolver::MangleLibs(tokens, *env.SysLibs);
+    return cmPkgConfigResolver::MangleLibs(tokens, *env->SysLibs);
   }
   }
 
 
-  if (env.SysrootDir) {
+  if (env->SysrootDir) {
     return cmPkgConfigResolver::MangleLibs(
     return cmPkgConfigResolver::MangleLibs(
-      tokens, *env.SysrootDir, std::vector<std::string>{ "/usr/lib" });
+      tokens, *env->SysrootDir, std::vector<std::string>{ "/usr/lib" });
   }
   }
 
 
   return cmPkgConfigResolver::MangleLibs(
   return cmPkgConfigResolver::MangleLibs(
@@ -210,7 +232,7 @@ cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolveStrict(
     config.Variables["pc_top_builddir"] = *env.TopBuildDir;
     config.Variables["pc_top_builddir"] = *env.TopBuildDir;
   }
   }
 
 
-  config.env = std::move(env);
+  config.env = &env;
 
 
   for (auto const& entry : entries) {
   for (auto const& entry : entries) {
     std::string key(entry.Key);
     std::string key(entry.Key);
@@ -288,7 +310,7 @@ cmPkgConfigResult cmPkgConfigResolver::ResolveBestEffort(
     result.Variables["pc_top_builddir"] = *env.TopBuildDir;
     result.Variables["pc_top_builddir"] = *env.TopBuildDir;
   }
   }
 
 
-  result.env = std::move(env);
+  result.env = &env;
 
 
   for (auto const& entry : entries) {
   for (auto const& entry : entries) {
     std::string key(entry.Key);
     std::string key(entry.Key);

+ 4 - 1
Source/cmPkgConfigResolver.h

@@ -43,6 +43,8 @@ struct cmPkgConfigVersionReq
     GT,
     GT,
   } Operation = ANY;
   } Operation = ANY;
   std::string Version;
   std::string Version;
+
+  std::string string() const;
 };
 };
 
 
 struct cmPkgConfigDependency
 struct cmPkgConfigDependency
@@ -60,6 +62,7 @@ struct cmPkgConfigEnv
 
 
   cm::optional<std::string> SysrootDir;
   cm::optional<std::string> SysrootDir;
   cm::optional<std::string> TopBuildDir;
   cm::optional<std::string> TopBuildDir;
+  std::vector<std::string> search;
 
 
   cm::optional<bool> DisableUninstalled;
   cm::optional<bool> DisableUninstalled;
 
 
@@ -84,7 +87,7 @@ public:
   cmPkgConfigCflagsResult Cflags(bool priv = false);
   cmPkgConfigCflagsResult Cflags(bool priv = false);
   cmPkgConfigLibsResult Libs(bool priv = false);
   cmPkgConfigLibsResult Libs(bool priv = false);
 
 
-  cmPkgConfigEnv env;
+  cmPkgConfigEnv* env;
 
 
 private:
 private:
   std::string StrOrDefault(std::string const& key, cm::string_view def = "");
   std::string StrOrDefault(std::string const& key, cm::string_view def = "");

+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestEnv-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractEnv-stderr.txt


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestEnv.cmake → Tests/RunCMake/cmake_pkg_config/ExtractEnv.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestExtract-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractFields-stderr.txt


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestExtract.cmake → Tests/RunCMake/cmake_pkg_config/ExtractFields.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestMangle-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractMangle-stderr.txt


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestMangle.cmake → Tests/RunCMake/cmake_pkg_config/ExtractMangle.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestQuiet.cmake → Tests/RunCMake/cmake_pkg_config/ExtractQuiet.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestRequired-result.txt → Tests/RunCMake/cmake_pkg_config/ExtractRequired-result.txt


+ 4 - 0
Tests/RunCMake/cmake_pkg_config/ExtractRequired-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at ExtractRequired.cmake:[0-9]+ \(cmake_pkg_config\):
+  cmake_pkg_config Could not find pkg-config: 'does-not-exist'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestRequired.cmake → Tests/RunCMake/cmake_pkg_config/ExtractRequired.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestReroot-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractReroot-stderr.txt


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestReroot.cmake → Tests/RunCMake/cmake_pkg_config/ExtractReroot.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestStrictness-BEST_EFFORT-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractStrictness-BEST_EFFORT-stderr.txt


+ 4 - 4
Tests/RunCMake/cmake_pkg_config/TestStrictness-PERMISSIVE-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractStrictness-PERMISSIVE-stderr.txt

@@ -1,25 +1,25 @@
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/no-name.pc'
 ]*(.)*/PackageRoot/no-name.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/no-description.pc'
 ]*(.)*/PackageRoot/no-description.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/no-version.pc'
 ]*(.)*/PackageRoot/no-version.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Parsing failed for file[^
   Parsing failed for file[^
 ]*(.)*/PackageRoot/invalid.pc'
 ]*(.)*/PackageRoot/invalid.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):

+ 5 - 5
Tests/RunCMake/cmake_pkg_config/TestStrictness-STRICT-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractStrictness-STRICT-stderr.txt

@@ -1,25 +1,25 @@
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/no-name.pc'
 ]*(.)*/PackageRoot/no-name.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/no-description.pc'
 ]*(.)*/PackageRoot/no-description.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/no-version.pc'
 ]*(.)*/PackageRoot/no-version.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Parsing failed for file[^
   Parsing failed for file[^
 ]*(.)*/PackageRoot/invalid.pc'
 ]*(.)*/PackageRoot/invalid.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
@@ -28,7 +28,7 @@ Call Stack \(most recent call first\):
 
 
 Cflags: lowercase
 Cflags: lowercase
 CFlags: uppercase
 CFlags: uppercase
-CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractStrictness.cmake:[0-9]+ \(cmake_pkg_config\):
   Resolution failed for file[^
   Resolution failed for file[^
 ]*(.)*/PackageRoot/cflags-bothcase-f.pc'
 ]*(.)*/PackageRoot/cflags-bothcase-f.pc'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):

+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestStrictness.cmake → Tests/RunCMake/cmake_pkg_config/ExtractStrictness.cmake


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestUninstalled-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractUninstalled-stderr.txt


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestUninstalled.cmake → Tests/RunCMake/cmake_pkg_config/ExtractUninstalled.cmake


+ 17 - 17
Tests/RunCMake/cmake_pkg_config/TestVersion-stderr.txt → Tests/RunCMake/cmake_pkg_config/ExtractVersion-stderr.txt

@@ -1,103 +1,103 @@
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'a' version 'aa' does not meet version requirement '<a'
   Package 'a' version 'aa' does not meet version requirement '<a'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'a' version 'aa' does not meet version requirement '>aaa'
   Package 'a' version 'aa' does not meet version requirement '>aaa'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'a' version 'aa' does not meet version requirement '>bb'
   Package 'a' version 'aa' does not meet version requirement '>bb'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'a' version 'aa' does not meet version requirement '>1'
   Package 'a' version 'aa' does not meet version requirement '>1'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'empty-key' version '' does not meet version requirement '!='
   Package 'empty-key' version '' does not meet version requirement '!='
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'empty-key' version '' does not meet version requirement '=0'
   Package 'empty-key' version '' does not meet version requirement '=0'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'one' version '11' does not meet version requirement '<1'
   Package 'one' version '11' does not meet version requirement '<1'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'one' version '11' does not meet version requirement '>111'
   Package 'one' version '11' does not meet version requirement '>111'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'one' version '11' does not meet version requirement '>22'
   Package 'one' version '11' does not meet version requirement '>22'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'one' version '11' does not meet version requirement '<a'
   Package 'one' version '11' does not meet version requirement '<a'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'onedot' version '1.1.1' does not meet version requirement '>1.2.1'
   Package 'onedot' version '1.1.1' does not meet version requirement '>1.2.1'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'onedot' version '1.1.1' does not meet version requirement '>
   Package 'onedot' version '1.1.1' does not meet version requirement '>
   1.2.1'
   1.2.1'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'onedot' version '1.1.1' does not meet exact version requirement
   Package 'onedot' version '1.1.1' does not meet exact version requirement
   '01.01.01'
   '01.01.01'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'pseudo-empty' version '~0' does not meet version requirement '=~'
   Package 'pseudo-empty' version '~0' does not meet version requirement '=~'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'pseudo-empty' version '~0' does not meet version requirement
   Package 'pseudo-empty' version '~0' does not meet version requirement
   '!=~0'
   '!=~0'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'tilde' version '~~1' does not meet version requirement '>~1'
   Package 'tilde' version '~~1' does not meet version requirement '>~1'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)
 
 
 
 
-CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\):
+CMake Warning at ExtractVersion.cmake:[0-9]+ \(cmake_pkg_config\):
   Package 'tilde' version '~~1' does not meet version requirement '<~~~1'
   Package 'tilde' version '~~1' does not meet version requirement '<~~~1'
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
   CMakeLists.txt:[0-9]+ \(include\)

+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestVersion.cmake → Tests/RunCMake/cmake_pkg_config/ExtractVersion.cmake


+ 14 - 0
Tests/RunCMake/cmake_pkg_config/ImportRequires-check.cmake

@@ -0,0 +1,14 @@
+set(expected
+  "alpha: Alpha
+bravo: Bravo;Alpha
+charlie: Charlie;Bravo;Alpha
+delta: Delta
+echo: Echo;Bravo;Alpha;Delta
+"
+)
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/import-requires.txt" actual)
+
+if(NOT(expected STREQUAL actual))
+  set(RunCMake_TEST_FAILED "cmake_pkg_config import-requires.txt does not match expected:\n${actual}")
+endif()

+ 18 - 0
Tests/RunCMake/cmake_pkg_config/ImportRequires.cmake

@@ -0,0 +1,18 @@
+set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
+
+cmake_pkg_config(IMPORT echo REQUIRED)
+cmake_pkg_config(IMPORT delta REQUIRED)
+cmake_pkg_config(IMPORT charlie REQUIRED)
+cmake_pkg_config(IMPORT bravo REQUIRED)
+cmake_pkg_config(IMPORT alpha REQUIRED)
+
+file(GENERATE
+  OUTPUT import-requires.txt
+  CONTENT
+  "alpha: $<TARGET_PROPERTY:PkgConfig::alpha,INTERFACE_COMPILE_OPTIONS>
+bravo: $<TARGET_PROPERTY:PkgConfig::bravo,INTERFACE_COMPILE_OPTIONS>
+charlie: $<TARGET_PROPERTY:PkgConfig::charlie,INTERFACE_COMPILE_OPTIONS>
+delta: $<TARGET_PROPERTY:PkgConfig::delta,INTERFACE_COMPILE_OPTIONS>
+echo: $<TARGET_PROPERTY:PkgConfig::echo,INTERFACE_COMPILE_OPTIONS>
+"
+)

+ 15 - 0
Tests/RunCMake/cmake_pkg_config/ImportSimple-check.cmake

@@ -0,0 +1,15 @@
+set(expected
+"Import Simple Found: TRUE
+Include Directories: ${RunCMake_SOURCE_DIR}/TestDirectories/Include
+Compile Options: TestCflag
+Link Directories: ${RunCMake_SOURCE_DIR}/TestDirectories/Library
+Link Libraries: @foreign_pkgcfg::import-simple
+Link Options: TestLinkOption
+"
+)
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/import-simple.txt" actual)
+
+if(NOT(expected STREQUAL actual))
+  set(RunCMake_TEST_FAILED "cmake_pkg_config import-simple.txt does not match expected:\n${actual}")
+endif()

+ 15 - 0
Tests/RunCMake/cmake_pkg_config/ImportSimple.cmake

@@ -0,0 +1,15 @@
+set(CMAKE_PKG_CONFIG_SYSROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+cmake_pkg_config(IMPORT import-simple REQUIRED)
+
+file(GENERATE
+  OUTPUT import-simple.txt
+  CONTENT
+"Import Simple Found: ${PKGCONFIG_import-simple_FOUND}
+Include Directories: $<TARGET_PROPERTY:PkgConfig::import-simple,INTERFACE_INCLUDE_DIRECTORIES>
+Compile Options: $<TARGET_PROPERTY:PkgConfig::import-simple,INTERFACE_COMPILE_OPTIONS>
+Link Directories: $<TARGET_PROPERTY:PkgConfig::import-simple,INTERFACE_LINK_DIRECTORIES>
+Link Libraries: $<TARGET_PROPERTY:PkgConfig::import-simple,INTERFACE_LINK_LIBRARIES>
+Link Options: $<TARGET_PROPERTY:PkgConfig::import-simple,INTERFACE_LINK_OPTIONS>
+"
+)

+ 1 - 0
Tests/RunCMake/cmake_pkg_config/ImportTransitiveFail-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/cmake_pkg_config/ImportTransitiveFail-stderr.txt

@@ -0,0 +1,11 @@
+CMake Warning at ImportTransitiveFail.cmake:[0-9]+ \(cmake_pkg_config\):
+  Could not find pkg-config: 'foxtrot' required by: 'golf'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+
+
+Import Golf Found: FALSE
+CMake Error at ImportTransitiveFail.cmake:[0-9]+ \(cmake_pkg_config\):
+  cmake_pkg_config Could not find pkg-config: 'foxtrot' required by: 'golf'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 5 - 0
Tests/RunCMake/cmake_pkg_config/ImportTransitiveFail.cmake

@@ -0,0 +1,5 @@
+set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
+
+cmake_pkg_config(IMPORT golf)
+message("Import Golf Found: ${PKGCONFIG_golf_FOUND}")
+cmake_pkg_config(IMPORT golf REQUIRED)

+ 3 - 0
Tests/RunCMake/cmake_pkg_config/ImportTransitiveVersion.cmake

@@ -0,0 +1,3 @@
+set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
+
+cmake_pkg_config(IMPORT india)

+ 5 - 0
Tests/RunCMake/cmake_pkg_config/ImportTransitiveVersionFail-stderr.txt

@@ -0,0 +1,5 @@
+CMake Warning at ImportTransitiveVersionFail.cmake:[0-9]+ \(cmake_pkg_config\):
+  Package 'alpha' version '1.0.0' does not meet version requirement '>=2.0.0'
+  of 'hotel'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 4 - 0
Tests/RunCMake/cmake_pkg_config/ImportTransitiveVersionFail.cmake

@@ -0,0 +1,4 @@
+set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
+
+cmake_pkg_config(POPULATE bravo)
+cmake_pkg_config(IMPORT hotel)

+ 5 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/alpha.pc

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

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/bravo.pc

@@ -0,0 +1,6 @@
+Name: Bravo
+Description: Bravo
+Version: 1.0.0
+
+Cflags: Bravo
+Requires: alpha

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/charlie.pc

@@ -0,0 +1,6 @@
+Name: Charlie
+Description: Charlie
+Version: 1.0.0
+
+Cflags: Charlie
+Requires: bravo

+ 5 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/delta.pc

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

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/echo.pc

@@ -0,0 +1,6 @@
+Name: Echo
+Description: Echo
+Version: 1.0.0
+
+Cflags: Echo
+Requires: bravo delta

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/golf.pc

@@ -0,0 +1,6 @@
+Name: Golf
+Description: Golf
+Version: 1.0.0
+
+Cflags: Golf
+Requires: foxtrot

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/hotel.pc

@@ -0,0 +1,6 @@
+Name: Hotel
+Description: Hotel
+Version: 1.0.0
+
+Cflags: Hotel
+Requires: alpha >= 2.0.0

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/india.pc

@@ -0,0 +1,6 @@
+Name: India
+Description: India
+Version: 1.0.0
+
+Cflags: India
+Requires: alpha = 1.0.0 bravo > 0.5.0

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/RequiresPackages/juliet.pc

@@ -0,0 +1,6 @@
+Name: Juliet
+Description: Juliet
+Version: 1.0.0
+
+Cflags: Juliet
+Requires: golf

+ 6 - 0
Tests/RunCMake/cmake_pkg_config/PackageRoot/import-simple.pc

@@ -0,0 +1,6 @@
+Name: Import Simple
+Description: A simple import with no dependencies
+Version: 1.0.0
+
+Cflags: -I/TestDirectories/Include TestCflag
+Libs: -L/TestDirectories/Library -ldummy TestLinkOption

+ 8 - 0
Tests/RunCMake/cmake_pkg_config/PopulateFoundVar-stderr.txt

@@ -0,0 +1,8 @@
+Found Alpha: TRUE
+CMake Warning at PopulateFoundVar.cmake:[0-9]+ \(cmake_pkg_config\):
+  Could not find pkg-config: 'foxtrot'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+
+
+Found Foxtrot: FALSE

+ 7 - 0
Tests/RunCMake/cmake_pkg_config/PopulateFoundVar.cmake

@@ -0,0 +1,7 @@
+set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
+
+cmake_pkg_config(POPULATE alpha)
+message("Found Alpha: ${PKGCONFIG_alpha_FOUND}")
+
+cmake_pkg_config(POPULATE foxtrot)
+message("Found Foxtrot: ${PKGCONFIG_foxtrot_FOUND}")

+ 10 - 0
Tests/RunCMake/cmake_pkg_config/PopulateMissing-check.cmake

@@ -0,0 +1,10 @@
+set(expected
+  "juliet: Juliet;Golf;Foxtrot
+"
+)
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/populate-missing.txt" actual)
+
+if(NOT(expected STREQUAL actual))
+  set(RunCMake_TEST_FAILED "cmake_pkg_config populate-missing.txt does not match expected:\n${actual}")
+endif()

+ 19 - 0
Tests/RunCMake/cmake_pkg_config/PopulateMissing.cmake

@@ -0,0 +1,19 @@
+set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/RequiresPackages)
+
+add_library(native-foxtrot INTERFACE)
+target_compile_options(native-foxtrot INTERFACE Foxtrot)
+
+cmake_pkg_config(
+  POPULATE golf
+  BIND_PC_REQUIRES
+    foxtrot=native-foxtrot
+)
+
+cmake_pkg_config(IMPORT juliet)
+
+file(GENERATE
+  OUTPUT populate-missing.txt
+  CONTENT
+  "juliet: $<TARGET_PROPERTY:PkgConfig::juliet,INTERFACE_COMPILE_OPTIONS>
+"
+)

+ 17 - 10
Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake

@@ -3,16 +3,23 @@ include(RunCMake)
 set(cmd ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR} -G ${RunCMake_GENERATOR})
 set(cmd ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR} -G ${RunCMake_GENERATOR})
 
 
 foreach(strictness IN ITEMS STRICT PERMISSIVE BEST_EFFORT)
 foreach(strictness IN ITEMS STRICT PERMISSIVE BEST_EFFORT)
-  run_cmake_command(TestStrictness-${strictness} ${cmd}
-    -DRunCMake_TEST=TestStrictness -DSTRICTNESS=${strictness}
+  run_cmake_command(ExtractStrictness-${strictness} ${cmd}
+    -DRunCMake_TEST=ExtractStrictness -DSTRICTNESS=${strictness}
   )
   )
 endforeach()
 endforeach()
 
 
-run_cmake(TestEnv)
-run_cmake(TestExtract)
-run_cmake(TestMangle)
-run_cmake(TestQuiet)
-run_cmake(TestRequired)
-run_cmake(TestReroot)
-run_cmake(TestUninstalled)
-run_cmake(TestVersion)
+run_cmake(ExtractEnv)
+run_cmake(ExtractFields)
+run_cmake(ExtractMangle)
+run_cmake(ExtractQuiet)
+run_cmake(ExtractRequired)
+run_cmake(ExtractReroot)
+run_cmake(ExtractUninstalled)
+run_cmake(ExtractVersion)
+run_cmake(ImportSimple)
+run_cmake(ImportRequires)
+run_cmake(ImportTransitiveFail)
+run_cmake(ImportTransitiveVersion)
+run_cmake(ImportTransitiveVersionFail)
+run_cmake(PopulateFoundVar)
+run_cmake(PopulateMissing)

+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestDirectories/Include/dummy-header.h


+ 0 - 0
Tests/RunCMake/cmake_pkg_config/TestDirectories/Library/libdummy


+ 0 - 4
Tests/RunCMake/cmake_pkg_config/TestRequired-stderr.txt

@@ -1,4 +0,0 @@
-CMake Error at TestRequired.cmake:[0-9]+ \(cmake_pkg_config\):
-  cmake_pkg_config Could not find 'does-not-exist'
-Call Stack \(most recent call first\):
-  CMakeLists.txt:[0-9]+ \(include\)