瀏覽代碼

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
  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>/``                                                          W
  ``<prefix>/(cmake|CMake)/``                                            W
  ``<prefix>/(cmake|CMake)/``                                            W
  ``<prefix>/<name>*/``                                                  W
  ``<prefix>/<name>*/``                                                  W
  ``<prefix>/<name>*/(cmake|CMake)/``                                    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)/cmake/<name>*/``                    U
  ``<prefix>/(lib/<arch>|lib*|share)/<name>*/``                          U
  ``<prefix>/(lib/<arch>|lib*|share)/<name>*/``                          U
  ``<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/``            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
  ``<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
 On systems supporting macOS :prop_tgt:`FRAMEWORK` and :prop_tgt:`BUNDLE`, the
 following directories are searched for Frameworks or Application Bundles
 following directories are searched for Frameworks or Application Bundles
 containing a configuration file:
 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
 In all cases the ``<name>`` is treated as case-insensitive and corresponds
 to any of the names specified (``<PackageName>`` or names given by ``NAMES``).
 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`
 :variable:`CMAKE_FIND_FRAMEWORK` and :variable:`CMAKE_FIND_APPBUNDLE`
 variables determine the order of preference.
 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
 The set of installation prefixes is constructed using the following
 steps.  If ``NO_DEFAULT_PATH`` is specified all ``NO_*`` options are
 steps.  If ``NO_DEFAULT_PATH`` is specified all ``NO_*`` options are
 enabled.
 enabled.

+ 207 - 53
Source/cmFindPackageCommand.cxx

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

+ 35 - 4
Source/cmFindPackageCommand.h

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