瀏覽代碼

find_package: Start implementing CPS search

Teach find_package to search CPS search paths, and to look for CPS file
names. Modify the set of file names to also include the file type (CPS
or CMake-script). Modify the search function to allow specifying which
file type(s) to consider.

During full path search, each possible path is searched for only one of
the two possible file types. However, subsequent runs, or when
considering a user-specified path (<name>_DIR), CMake will look for both
file types.

Note that this only adds the new path search logic as described above;
CMake does not yet know how to read CPS files, and there is a high
likelihood that Bad Things will happen if it tries. However, this seemed
like a good place to checkpoint.
Matthew Woehlke 11 月之前
父節點
當前提交
b89e43b2bc
共有 3 個文件被更改,包括 280 次插入69 次删除
  1. 38 12
      Help/command/find_package.rst
  2. 207 53
      Source/cmFindPackageCommand.cxx
  3. 35 4
      Source/cmFindPackageCommand.h

+ 38 - 12
Help/command/find_package.rst

@@ -313,11 +313,19 @@ Each entry is meant for installation trees following Windows (``W``), UNIX
 ==================================================================== ==========
  Entry                                                               Convention
 ==================================================================== ==========
+ ``<prefix>/<name>/cps/`` [#p2]_                                        W
+ ``<prefix>/<name>/*/cps/`` [#p2]_                                      W
+ ``<prefix>/cps/<name>/`` [#p2]_                                        W
+ ``<prefix>/cps/<name>/*/`` [#p2]_                                      W
+ ``<prefix>/cps/`` [#p2]_                                               W
  ``<prefix>/``                                                          W
  ``<prefix>/(cmake|CMake)/``                                            W
  ``<prefix>/<name>*/``                                                  W
  ``<prefix>/<name>*/(cmake|CMake)/``                                    W
- ``<prefix>/<name>*/(cmake|CMake)/<name>*/`` [#]_                       W
+ ``<prefix>/<name>*/(cmake|CMake)/<name>*/`` [#p1]_                     W
+ ``<prefix>/(lib/<arch>|lib*|share)/cps/<name>/`` [#p2]_                U
+ ``<prefix>/(lib/<arch>|lib*|share)/cps/<name>/*/`` [#p2]_              U
+ ``<prefix>/(lib/<arch>|lib*|share)/cps/`` [#p2]_                       U
  ``<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/``                    U
  ``<prefix>/(lib/<arch>|lib*|share)/<name>*/``                          U
  ``<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/``            U
@@ -326,22 +334,34 @@ Each entry is meant for installation trees following Windows (``W``), UNIX
  ``<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/``    W/U
 ==================================================================== ==========
 
-.. [#] .. versionadded:: 3.25
+.. [#p1] .. versionadded:: 3.25
+
+.. [#p2] .. versionadded:: 3.32
 
 On systems supporting macOS :prop_tgt:`FRAMEWORK` and :prop_tgt:`BUNDLE`, the
 following directories are searched for Frameworks or Application Bundles
 containing a configuration file:
 
-=========================================================== ==========
- Entry                                                      Convention
-=========================================================== ==========
- ``<prefix>/<name>.framework/Resources/``                      A
- ``<prefix>/<name>.framework/Resources/CMake/``                A
- ``<prefix>/<name>.framework/Versions/*/Resources/``           A
- ``<prefix>/<name>.framework/Versions/*/Resources/CMake/``     A
- ``<prefix>/<name>.app/Contents/Resources/``                   A
- ``<prefix>/<name>.app/Contents/Resources/CMake/``             A
-=========================================================== ==========
+=============================================================== ==========
+ Entry                                                          Convention
+=============================================================== ==========
+ ``<prefix>/<name>.framework/Versions/*/Resources/CPS/`` [#p3]_    A
+ ``<prefix>/<name>.framework/Resources/CPS/`` [#p3]_               A
+ ``<prefix>/<name>.framework/Resources/``                          A
+ ``<prefix>/<name>.framework/Resources/CMake/``                    A
+ ``<prefix>/<name>.framework/Versions/*/Resources/``               A
+ ``<prefix>/<name>.framework/Versions/*/Resources/CMake/``         A
+ ``<prefix>/<name>.app/Contents/Resources/CPS/`` [#p3]_            A
+ ``<prefix>/<name>.app/Contents/Resources/``                       A
+ ``<prefix>/<name>.app/Contents/Resources/CMake/``                 A
+=============================================================== ==========
+
+.. [#p3] .. versionadded:: 3.32
+
+When searching the above paths, ``find_package`` will only look for ``.cps``
+files in search paths which contain ``/cps/``, and will only look for
+``.cmake`` files otherwise.  (This only applies to the paths as specified and
+does not consider the contents of ``<prefix>`` or ``<name>``.)
 
 In all cases the ``<name>`` is treated as case-insensitive and corresponds
 to any of the names specified (``<PackageName>`` or names given by ``NAMES``).
@@ -395,6 +415,12 @@ intended for installations on Apple platforms.  The
 :variable:`CMAKE_FIND_FRAMEWORK` and :variable:`CMAKE_FIND_APPBUNDLE`
 variables determine the order of preference.
 
+.. warning::
+
+  Setting :variable:`CMAKE_FIND_FRAMEWORK` or :variable:`CMAKE_FIND_APPBUNDLE`
+  to values other than ``FIRST`` (the default) will cause CMake searching for
+  CPS files in an order that is different from the order specified by CPS.
+
 The set of installation prefixes is constructed using the following
 steps.  If ``NO_DEFAULT_PATH`` is specified all ``NO_*`` options are
 enabled.

+ 207 - 53
Source/cmFindPackageCommand.cxx

@@ -57,6 +57,8 @@ class cmExecutionStatus;
 
 namespace {
 
+using pdt = cmFindPackageCommand::PackageDescriptionType;
+
 template <template <typename> class Op>
 struct StrverscmpOp
 {
@@ -379,14 +381,16 @@ void ResetGenerator(Generator&& generator, Generators&&... generators)
 
 template <typename CallbackFn>
 bool TryGeneratedPaths(CallbackFn&& filesCollector,
+                       cmFindPackageCommand::PackageDescriptionType type,
                        const std::string& fullPath)
 {
   assert(!fullPath.empty() && fullPath.back() != '/');
-  return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/');
+  return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/', type);
 }
 
 template <typename CallbackFn, typename Generator, typename... Rest>
 bool TryGeneratedPaths(CallbackFn&& filesCollector,
+                       cmFindPackageCommand::PackageDescriptionType type,
                        const std::string& startPath, Generator&& gen,
                        Rest&&... tail)
 {
@@ -394,8 +398,8 @@ bool TryGeneratedPaths(CallbackFn&& filesCollector,
   for (auto path = gen.GetNextCandidate(startPath); !path.empty();
        path = gen.GetNextCandidate(startPath)) {
     ResetGenerator(std::forward<Rest&&>(tail)...);
-    if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path,
-                          std::forward<Rest&&>(tail)...)) {
+    if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), type,
+                          path, std::forward<Rest&&>(tail)...)) {
       return true;
     }
   }
@@ -791,7 +795,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
           "a path and with a \".cmake\" extension."));
         return false;
       }
-      this->Configs.push_back(args[i]);
+      this->Configs.emplace_back(args[i], pdt::CMake);
     } else if (!haveVersion && versionRegex.find(args[i])) {
       haveVersion = true;
       this->VersionComplete = args[i];
@@ -824,6 +828,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   // Check and eliminate search modes not allowed by the args provided
   this->UseFindModules = configArgs.empty();
   this->UseConfigFiles = moduleArgs.empty();
+  if (this->UseConfigFiles && true /* FIXME check experimental flag */) {
+    this->UseCpsFiles = this->Configs.empty();
+  } else {
+    this->UseCpsFiles = false;
+  }
   if (!this->UseFindModules && !this->UseConfigFiles) {
     std::ostringstream e;
     e << "given options exclusive to Module mode:\n";
@@ -1193,11 +1202,22 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode()
   // Add the default configs.
   if (this->Configs.empty()) {
     for (std::string const& n : this->Names) {
-      std::string config = cmStrCat(n, "Config.cmake");
-      this->Configs.push_back(config);
+      std::string config;
+      if (this->UseCpsFiles) {
+        config = cmStrCat(n, ".cps");
+        this->Configs.emplace_back(std::move(config), pdt::Cps);
+
+        config = cmStrCat(cmSystemTools::LowerCase(n), ".cps");
+        if (config != this->Configs.front().Name) {
+          this->Configs.emplace_back(std::move(config), pdt::Cps);
+        }
+      }
+
+      config = cmStrCat(n, "Config.cmake");
+      this->Configs.emplace_back(std::move(config), pdt::CMake);
 
       config = cmStrCat(cmSystemTools::LowerCase(n), "-config.cmake");
-      this->Configs.push_back(std::move(config));
+      this->Configs.emplace_back(std::move(config), pdt::CMake);
     }
   }
 
@@ -1436,7 +1456,7 @@ bool cmFindPackageCommand::HandlePackageMode(
       }
       // The file location was cached.  Look for the correct file.
       std::string file;
-      if (this->FindConfigFile(dir, file)) {
+      if (this->FindConfigFile(dir, pdt::Any, file)) {
         this->FileFound = std::move(file);
         fileFound = true;
       }
@@ -1569,13 +1589,16 @@ bool cmFindPackageCommand::HandlePackageMode(
 
           if (this->Configs.size() == 1) {
             e << "Could not find a package configuration file named \""
-              << this->Configs[0] << "\" provided by package \"" << this->Name
-              << "\"" << requestedVersionString << ".\n";
+              << this->Configs[0].Name << "\" provided by package \""
+              << this->Name << "\"" << requestedVersionString << ".\n";
           } else {
+            auto configs = cmMakeRange(this->Configs);
+            auto configNames =
+              configs.transform([](ConfigName const& cn) { return cn.Name; });
             e << "Could not find a package configuration file provided by \""
               << this->Name << "\"" << requestedVersionString
               << " with any of the following names:\n"
-              << cmWrap("  ", this->Configs, "", "\n") << '\n';
+              << cmWrap("  "_s, configNames, ""_s, "\n"_s) << '\n';
           }
 
           e << "Add the installation prefix of \"" << this->Name
@@ -1678,6 +1701,10 @@ bool cmFindPackageCommand::FindConfig()
                                  this->Name, "'s Config module:\n");
   }
 
+  if (!found && this->UseCpsFiles) {
+    found = this->FindEnvironmentConfig();
+  }
+
   // Search for frameworks.
   if (!found && (this->SearchFrameworkFirst || this->SearchFrameworkOnly)) {
     found = this->FindFrameworkConfig();
@@ -1766,6 +1793,16 @@ bool cmFindPackageCommand::FindAppBundleConfig()
                      });
 }
 
+bool cmFindPackageCommand::FindEnvironmentConfig()
+{
+  std::vector<std::string> const& prefixes =
+    cmSystemTools::GetEnvPathNormalized("CPS_PATH");
+  return std::any_of(prefixes.begin(), prefixes.end(),
+                     [this](std::string const& p) -> bool {
+                       return this->SearchEnvironmentPrefix(p);
+                     });
+}
+
 bool cmFindPackageCommand::ReadListFile(const std::string& f,
                                         const PolicyScopeRule psr)
 {
@@ -2381,7 +2418,8 @@ void cmFindPackageCommand::FillPrefixesUserHints()
   }
 }
 
-bool cmFindPackageCommand::SearchDirectory(std::string const& dir)
+bool cmFindPackageCommand::SearchDirectory(std::string const& dir,
+                                           PackageDescriptionType type)
 {
   assert(!dir.empty() && dir.back() == '/');
 
@@ -2392,14 +2430,15 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir)
       d += s;
       d += '/';
     }
-    if (this->CheckDirectory(d)) {
+    if (this->CheckDirectory(d, type)) {
       return true;
     }
   }
   return false;
 }
 
-bool cmFindPackageCommand::CheckDirectory(std::string const& dir)
+bool cmFindPackageCommand::CheckDirectory(std::string const& dir,
+                                          PackageDescriptionType type)
 {
   assert(!dir.empty() && dir.back() == '/');
 
@@ -2410,7 +2449,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir)
 
   // Look for the file in this directory.
   std::string file;
-  if (this->FindConfigFile(d, file)) {
+  if (this->FindConfigFile(d, type, file)) {
     this->FileFound = std::move(file);
     return true;
   }
@@ -2418,10 +2457,14 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir)
 }
 
 bool cmFindPackageCommand::FindConfigFile(std::string const& dir,
+                                          PackageDescriptionType type,
                                           std::string& file)
 {
-  for (std::string const& c : this->Configs) {
-    file = cmStrCat(dir, '/', c);
+  for (auto const& config : this->Configs) {
+    if (type != pdt::Any && config.Type != type) {
+      continue;
+    }
+    file = cmStrCat(dir, '/', config.Name);
     if (this->DebugMode) {
       this->DebugBuffer = cmStrCat(this->DebugBuffer, "  ", file, '\n');
     }
@@ -2615,36 +2658,71 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
     return false;
   }
 
-  // PREFIX/ (useful on windows or in build trees)
-  if (this->SearchDirectory(prefix_in)) {
-    return true;
-  }
-
   // Strip the trailing slash because the path generator is about to
   // add one.
   std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
 
-  auto searchFn = [this](const std::string& fullPath) -> bool {
-    return this->SearchDirectory(fullPath);
+  auto searchFn = [this](const std::string& fullPath,
+                         PackageDescriptionType type) -> bool {
+    return this->SearchDirectory(fullPath, type);
   };
 
+  auto iCpsGen = cmCaseInsensitiveDirectoryListGenerator{ "cps"_s };
   auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s };
-  auto firstPkgDirGen =
+  auto anyDirGen =
+    cmAnyDirectoryListGenerator{ this->SortOrder, this->SortDirection };
+  auto cpsPkgDirGen =
+    cmProjectDirectoryListGenerator{ &this->Names, this->SortOrder,
+                                     this->SortDirection, true };
+  auto cmakePkgDirGen =
     cmProjectDirectoryListGenerator{ &this->Names, this->SortOrder,
                                      this->SortDirection, false };
 
+  // PREFIX/(Foo|foo|FOO)/(cps|CPS)/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, cpsPkgDirGen, iCpsGen)) {
+    return true;
+  }
+
+  // PREFIX/(Foo|foo|FOO)/*/(cps|CPS)/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, cpsPkgDirGen, iCpsGen,
+                        anyDirGen)) {
+    return true;
+  }
+
+  // PREFIX/(cps|CPS)/(Foo|foo|FOO)/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, iCpsGen, cpsPkgDirGen)) {
+    return true;
+  }
+
+  // PREFIX/(cps|CPS)/(Foo|foo|FOO)/*/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, iCpsGen, cpsPkgDirGen,
+                        anyDirGen)) {
+    return true;
+  }
+
+  // PREFIX/(cps|CPS)/ (useful on windows or in build trees)
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, iCpsGen)) {
+    return true;
+  }
+
+  // PREFIX/ (useful on windows or in build trees)
+  if (this->SearchDirectory(prefix_in, pdt::CMake)) {
+    return true;
+  }
+
   // PREFIX/(cmake|CMake)/ (useful on windows or in build trees)
-  if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, iCMakeGen)) {
     return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/
-  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, cmakePkgDirGen)) {
     return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/
-  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, cmakePkgDirGen,
+                        iCMakeGen)) {
     return true;
   }
 
@@ -2653,8 +2731,8 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
                                      this->SortDirection, false };
 
   // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/(Foo|foo|FOO).*/
-  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen,
-                        secondPkgDirGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, cmakePkgDirGen,
+                        iCMakeGen, secondPkgDirGen)) {
     return true;
   }
 
@@ -2677,39 +2755,60 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
   common.emplace_back("lib"_s);
   common.emplace_back("share"_s);
 
-  auto cmnGen = cmEnumPathSegmentsGenerator{ common };
+  auto commonGen = cmEnumPathSegmentsGenerator{ common };
   auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s };
+  auto cpsGen = cmAppendPathSegmentGenerator{ "cps"_s };
+
+  // PREFIX/(lib/ARCH|lib*|share)/cps/(Foo|foo|FOO)/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, commonGen, cpsGen,
+                        cpsPkgDirGen)) {
+    return true;
+  }
+
+  // PREFIX/(lib/ARCH|lib*|share)/cps/(Foo|foo|FOO)/*/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, commonGen, cpsGen,
+                        cpsPkgDirGen, anyDirGen)) {
+    return true;
+  }
+
+  // PREFIX/(lib/ARCH|lib*|share)/cps/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, commonGen, cpsGen)) {
+    return true;
+  }
 
   // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
-  if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, commonGen, cmakeGen,
+                        cmakePkgDirGen)) {
     return true;
   }
 
   // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
-  if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, commonGen,
+                        cmakePkgDirGen)) {
     return true;
   }
 
   // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
-  if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, commonGen,
+                        cmakePkgDirGen, iCMakeGen)) {
     return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
-  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen,
-                        secondPkgDirGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, cmakePkgDirGen,
+                        commonGen, cmakeGen, secondPkgDirGen)) {
     return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
-  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen,
-                        secondPkgDirGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, cmakePkgDirGen,
+                        commonGen, secondPkgDirGen)) {
     return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
-  return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen,
-                           secondPkgDirGen, iCMakeGen);
+  return TryGeneratedPaths(searchFn, pdt::CMake, prefix, cmakePkgDirGen,
+                           commonGen, secondPkgDirGen, iCMakeGen);
 }
 
 bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
@@ -2720,11 +2819,13 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
   // add one.
   std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
 
-  auto searchFn = [this](const std::string& fullPath) -> bool {
-    return this->SearchDirectory(fullPath);
+  auto searchFn = [this](const std::string& fullPath,
+                         PackageDescriptionType type) -> bool {
+    return this->SearchDirectory(fullPath, type);
   };
 
   auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s };
+  auto iCpsGen = cmCaseInsensitiveDirectoryListGenerator{ "cps"_s };
   auto fwGen =
     cmMacProjectDirectoryListGenerator{ &this->Names, ".framework"_s };
   auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s };
@@ -2732,24 +2833,37 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
   auto anyGen =
     cmAnyDirectoryListGenerator{ this->SortOrder, this->SortDirection };
 
+  // <prefix>/Foo.framework/Versions/*/Resources/CPS/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, fwGen, vGen, anyGen, rGen,
+                        iCpsGen)) {
+    return true;
+  }
+
+  // <prefix>/Foo.framework/Resources/CPS/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, fwGen, rGen, iCpsGen)) {
+    return true;
+  }
+
   // <prefix>/Foo.framework/Resources/
-  if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, fwGen, rGen)) {
     return true;
   }
 
   // <prefix>/Foo.framework/Resources/CMake/
-  if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, fwGen, rGen,
+                        iCMakeGen)) {
     return true;
   }
 
   // <prefix>/Foo.framework/Versions/*/Resources/
-  if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, anyGen, rGen)) {
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, fwGen, vGen, anyGen,
+                        rGen)) {
     return true;
   }
 
   // <prefix>/Foo.framework/Versions/*/Resources/CMake/
-  return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, anyGen, rGen,
-                           iCMakeGen);
+  return TryGeneratedPaths(searchFn, pdt::CMake, prefix, fwGen, vGen, anyGen,
+                           rGen, iCMakeGen);
 }
 
 bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in)
@@ -2760,24 +2874,64 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in)
   // add one.
   std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
 
-  auto searchFn = [this](const std::string& fullPath) -> bool {
-    return this->SearchDirectory(fullPath);
+  auto searchFn = [this](const std::string& fullPath,
+                         PackageDescriptionType type) -> bool {
+    return this->SearchDirectory(fullPath, type);
   };
 
   auto appGen = cmMacProjectDirectoryListGenerator{ &this->Names, ".app"_s };
   auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s };
 
-  // <prefix>/Foo.app/Contents/Resources
-  if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) {
+  // <prefix>/Foo.app/Contents/Resources/CPS/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, appGen, crGen,
+                        cmCaseInsensitiveDirectoryListGenerator{ "cps"_s })) {
     return true;
   }
 
-  // <prefix>/Foo.app/Contents/Resources/CMake
+  // <prefix>/Foo.app/Contents/Resources/
+  if (TryGeneratedPaths(searchFn, pdt::CMake, prefix, appGen, crGen)) {
+    return true;
+  }
+
+  // <prefix>/Foo.app/Contents/Resources/CMake/
   return TryGeneratedPaths(
-    searchFn, prefix, appGen, crGen,
+    searchFn, pdt::CMake, prefix, appGen, crGen,
     cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s });
 }
 
+bool cmFindPackageCommand::SearchEnvironmentPrefix(
+  std::string const& prefix_in)
+{
+  assert(!prefix_in.empty() && prefix_in.back() == '/');
+
+  // Skip this if the prefix does not exist.
+  if (!cmSystemTools::FileIsDirectory(prefix_in)) {
+    return false;
+  }
+
+  // Strip the trailing slash because the path generator is about to
+  // add one.
+  std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
+
+  auto searchFn = [this](const std::string& fullPath,
+                         PackageDescriptionType type) -> bool {
+    return this->SearchDirectory(fullPath, type);
+  };
+
+  auto pkgDirGen =
+    cmProjectDirectoryListGenerator{ &this->Names, this->SortOrder,
+                                     this->SortDirection, true };
+
+  // <environment-path>/(Foo|foo|FOO)/cps/
+  if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, pkgDirGen,
+                        cmAppendPathSegmentGenerator{ "cps"_s })) {
+    return true;
+  }
+
+  // <environment-path>/(Foo|foo|FOO)/
+  return TryGeneratedPaths(searchFn, pdt::Cps, prefix, pkgDirGen);
+}
+
 // TODO: Debug cmsys::Glob double slash problem.
 
 bool cmFindPackage(std::vector<std::string> const& args,

+ 35 - 4
Source/cmFindPackageCommand.h

@@ -9,6 +9,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <cm/string_view>
@@ -56,6 +57,13 @@ public:
     Dec
   };
 
+  enum class PackageDescriptionType
+  {
+    Any,
+    CMake,
+    Cps,
+  };
+
   /*! sorts a given list of string based on the input sort parameters */
   static void Sort(std::vector<std::string>::iterator begin,
                    std::vector<std::string>::iterator end, SortOrderType order,
@@ -120,6 +128,7 @@ private:
   bool FindPrefixedConfig();
   bool FindFrameworkConfig();
   bool FindAppBundleConfig();
+  bool FindEnvironmentConfig();
   enum PolicyScopeRule
   {
     NoPolicyScope,
@@ -151,15 +160,17 @@ private:
                               cmSearchPath& outPaths);
   bool CheckPackageRegistryEntry(const std::string& fname,
                                  cmSearchPath& outPaths);
-  bool SearchDirectory(std::string const& dir);
-  bool CheckDirectory(std::string const& dir);
-  bool FindConfigFile(std::string const& dir, std::string& file);
+  bool SearchDirectory(std::string const& dir, PackageDescriptionType type);
+  bool CheckDirectory(std::string const& dir, PackageDescriptionType type);
+  bool FindConfigFile(std::string const& dir, PackageDescriptionType type,
+                      std::string& file);
   bool CheckVersion(std::string const& config_file);
   bool CheckVersionFile(std::string const& version_file,
                         std::string& result_version);
   bool SearchPrefix(std::string const& prefix);
   bool SearchFrameworkPrefix(std::string const& prefix_in);
   bool SearchAppBundlePrefix(std::string const& prefix_in);
+  bool SearchEnvironmentPrefix(std::string const& prefix_in);
 
   struct OriginalDef
   {
@@ -202,6 +213,7 @@ private:
   KWIML_INT_uint64_t RequiredCMakeVersion = 0;
   bool Quiet = false;
   bool Required = false;
+  bool UseCpsFiles = false;
   bool UseConfigFiles = true;
   bool UseFindModules = true;
   bool NoUserRegistry = false;
@@ -215,7 +227,6 @@ private:
   bool RegistryViewDefined = false;
   std::string LibraryArchitecture;
   std::vector<std::string> Names;
-  std::vector<std::string> Configs;
   std::set<std::string> IgnoredPaths;
   std::set<std::string> IgnoredPrefixPaths;
   std::string Components;
@@ -223,6 +234,26 @@ private:
   std::set<std::string> OptionalComponents;
   std::string DebugBuffer;
 
+  struct ConfigName
+  {
+    ConfigName(std::string const& name, PackageDescriptionType type)
+      : Name{ name }
+      , Type{ type }
+    {
+    }
+    ConfigName(std::string&& name, PackageDescriptionType type)
+      : Name{ std::move(name) }
+      , Type{ type }
+    {
+    }
+    ConfigName(ConfigName const&) = default;
+    ConfigName(ConfigName&&) = default;
+
+    std::string Name;
+    PackageDescriptionType Type;
+  };
+  std::vector<ConfigName> Configs;
+
   class FlushDebugBufferOnExit;
 
   /*! the selected sortOrder (None by default)*/