Ver Fonte

find_package: generate `find_package-v1` configure log events

Since `find_package` is far more complex than other `find_*` commands,
it gets its own event.

Closes: #24833
Ben Boeckel há 6 meses atrás
pai
commit
8df9819839

+ 240 - 0
Help/manual/cmake-configure-log.7.rst

@@ -441,3 +441,243 @@ The keys specific to ``find-v1`` mappings are:
 ``found``
   Either a string representing the found value or ``false`` if it was not
   found.
+
+.. _`find_package configure-log event`:
+
+Event Kind ``find_package``
+---------------------------
+
+.. versionadded:: 4.1
+
+The :command:`find_package` command logs ``find_package`` events.
+
+There is only one ``find_package`` event major version, version 1.
+
+.. _`find_package-v1 event`:
+
+``find_package-v1`` Event
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A ``find_package-v1`` event is a YAML mapping:
+
+.. code-block:: yaml
+
+  kind: "find_package-v1"
+  backtrace:
+    - "CMakeLists.txt:456 (find_program)"
+  name: "PackageName"
+  components:
+    -
+      name: "Component"
+      required: true
+      found: true
+  configs:
+    -
+      filename: PackageNameConfig.cmake
+      kind: "cmake"
+    -
+      filename: packagename-config.cmake
+      kind: "cmake"
+  version_request:
+    version: "1.0"
+    version_complete: "1.0...1.5"
+    min: "INCLUDE"
+    max: "INCLUDE"
+    exact: false
+  settings:
+    required: "optional"
+    quiet: false
+    global: false
+    policy_scope: true
+    bypass_provider: false
+    hints:
+      - "/hint/path"
+    names:
+      - "name1"
+      - "name2"
+    search_paths:
+      - "/search/path"
+    path_suffixes:
+      - ""
+      - "suffix"
+    registry_view: "HOST"
+    paths:
+      CMAKE_FIND_USE_CMAKE_PATH: true
+      CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH: true
+      CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: true
+      CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: true
+      CMAKE_FIND_USE_INSTALL_PREFIX: true
+      CMAKE_FIND_USE_PACKAGE_ROOT_PATH: true
+      CMAKE_FIND_USE_CMAKE_PACKAGE_REGISTRY: true
+      CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY: true
+      CMAKE_FIND_ROOT_PATH_MODE: "BOTH"
+    candidates:
+      -
+        path: "/path/to/config/PackageName/PackageNameConfig.cmake"
+        mode: "config"
+        reason: "insufficient_version"
+      -
+        path: "/path/to/config/PackageName/packagename-config.cmake"
+        mode: "config"
+        reason: "no_exist"
+    found:
+      path: "/path/to/config/PackageName-2.5/PackageNameConfig.cmake"
+      mode: "config"
+      version: "2.5"
+
+The keys specific to ``find_package-v1`` mappings are:
+
+``name``
+  The name of the requested package.
+
+``components``
+  If present, an array of objects containing the fields:
+
+  ``name``
+    The name of the component.
+
+  ``required``
+    A boolean indicating whether the component is required or optional.
+
+  ``found``
+    A boolean indicating whether the component was found or not.
+
+``configs``
+  If present, an array of objects indicating the configuration files to search
+  for.
+
+  ``filename``
+    The filename of the configuration file.
+
+  ``kind``
+    The kind of file. Either ``cmake`` or ``cps``.
+
+``version_request``
+  An object indicating the version constraints on the search.
+
+  ``version``
+    The minimum version required.
+
+  ``version_complete``
+    The user-provided version range.
+
+  ``min``
+    Whether to ``INCLUDE`` or ``EXCLUDE`` the lower bound on the version
+    range.
+
+  ``max``
+    Whether to ``INCLUDE`` or ``EXCLUDE`` the upper bound on the version
+    range.
+
+  ``exact``
+    A boolean indicating whether an ``EXACT`` version match was requested.
+
+``settings``
+  Search settings active for the search.
+
+  ``required``
+    The requirement request of the search. One of ``optional``,
+    ``optional_explicit``, ``required_explicit``,
+    ``required_from_package_variable``, or ``required_from_find_variable``.
+
+  ``quiet``
+    A boolean indicating whether the search is ``QUIET`` or not.
+
+  ``global``
+    A boolean indicating whether the ``GLOBAL`` keyword has been provided or
+    not.
+
+  ``policy_scope``
+    A boolean indicating whether the ``NO_POLICY_SCOPE`` keyword has been
+    provided or not.
+
+  ``bypass_provider``
+    A boolean indicating whether the ``BYPASS_PROVIDER`` keyword has been
+    provided or not.
+
+  ``hints``
+    An array of paths provided as ``HINTS``.
+
+  ``names``
+    An array of package names to use when searching, provided by ``NAMES``.
+
+  ``search_paths``
+    An array of paths to search, provided by ``PATHS``.
+
+  ``path_suffixes``
+    An array of suffixes to use when searching, provided by ``PATH_SUFFIXES``.
+
+  ``registry_view``
+    The ``REGISTRY_VIEW`` requested for the search.
+
+  ``paths``
+    Path settings active for the search.
+
+    ``CMAKE_FIND_USE_CMAKE_PATH``
+      A boolean indicating whether or not CMake-specific cache variables are
+      used when searching. See :variable:`CMAKE_FIND_USE_CMAKE_PATH`.
+
+    ``CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH``
+      A boolean indicating whether or not CMake-specific environment variables
+      are used when searching. See
+      :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`.
+
+    ``CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH``
+      A boolean indicating whether or not platform-specific environment
+      variables are used when searching. See
+      :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`.
+
+    ``CMAKE_FIND_USE_CMAKE_SYSTEM_PATH``
+      A boolean indicating whether or not platform-specific CMake variables are
+      used when searching. See :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`.
+
+    ``CMAKE_FIND_USE_INSTALL_PREFIX``
+      A boolean indicating whether or not the install prefix is used when
+      searching. See :variable:`CMAKE_FIND_USE_INSTALL_PREFIX`.
+
+    ``CMAKE_FIND_USE_CMAKE_PACKAGE_REGISTRY``
+      A boolean indicating whether or not to search the CMake package registry
+      for the package. See :variable:`CMAKE_FIND_USE_PACKAGE_REGISTRY`.
+
+    ``CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY``
+      A boolean indicating whether or not to search the system CMake package
+      registry for the package. See
+      :variable:`CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY`.
+
+    ``CMAKE_FIND_ROOT_PATH_MODE``
+      A string indicating the root path mode in effect as selected by the
+      ``CMAKE_FIND_ROOT_PATH_BOTH``, ``ONLY_CMAKE_FIND_ROOT_PATH``, and
+      ``NO_CMAKE_FIND_ROOT_PATH`` arguments.
+
+``candidates``
+  An array of rejected candidate paths. Each element contains the following
+  keys:
+
+  ``path``
+    The path to the considered file.
+
+  ``mode``
+    The mode which found the file. One of ``module``, ``cps``, ``cmake``, or
+    ``provider``.
+
+  ``reason``
+    The reason the path was rejected. One of ``insufficient_version``,
+    ``no_exist``, ``ignored``, ``no_config_file``, or ``not_found``.
+
+  ``message``
+    If present, a string describing why the package is considered as not
+    found.
+
+``found``
+  If the package has been found, information on the found file. If it is not
+  found, this is ``null``. Keys available:
+
+  ``path``
+    The path to the module or configuration that found the package.
+
+  ``mode``
+    The mode that considered the path. One of ``module``, ``cps``, ``cmake``,
+    or ``provider``.
+
+  ``version``
+    The reported version of the package.

+ 5 - 4
Source/cmFileAPIConfigureLog.cxx

@@ -52,10 +52,11 @@ Json::Value ConfigureLog::DumpEventKindNames()
   // major version of the configureLog object kind is needed.
   Json::Value eventKindNames = Json::arrayValue;
   if (this->Version == 1) {
-    eventKindNames.append("message-v1");     // WriteMessageEvent
-    eventKindNames.append("try_compile-v1"); // WriteTryCompileEvent
-    eventKindNames.append("try_run-v1");     // WriteTryRunEvent
-    eventKindNames.append("find-v1");        // WriteFindBaseEvent
+    eventKindNames.append("message-v1");      // WriteMessageEvent
+    eventKindNames.append("try_compile-v1");  // WriteTryCompileEvent
+    eventKindNames.append("try_run-v1");      // WriteTryRunEvent
+    eventKindNames.append("find-v1");         // WriteFindBaseEvent
+    eventKindNames.append("find_package-v1"); // WriteFindPackageEvent
   }
   return eventKindNames;
 }

+ 1 - 0
Source/cmFindCommon.h

@@ -40,6 +40,7 @@ protected:
   friend class cmSearchPath;
   friend class cmFindBaseDebugState;
   friend class cmFindCommonDebugState;
+  friend class cmFindPackageDebugState;
 
   /** Used to define groups of path labels */
   class PathGroup : public cmPathLabel

+ 271 - 19
Source/cmFindPackageCommand.cxx

@@ -24,6 +24,7 @@
 #include "cmsys/String.h"
 
 #include "cmAlgorithms.h"
+#include "cmConfigureLog.h"
 #include "cmDependencyProvider.h"
 #include "cmExecutionStatus.h"
 #include "cmExperimental.h"
@@ -60,8 +61,6 @@
 #  endif
 #endif
 
-class cmConfigureLog;
-
 namespace {
 
 using pdt = cmFindPackageCommand::PackageDescriptionType;
@@ -548,6 +547,13 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status)
   this->DeprecatedFindModules["Qt"] = cmPolicies::CMP0084;
 }
 
+cmFindPackageCommand::~cmFindPackageCommand()
+{
+  if (this->DebugState) {
+    this->DebugState->Write();
+  }
+}
+
 void cmFindPackageCommand::AppendSearchPathGroups()
 {
   // Update the All group with new paths. Note that package redirection must
@@ -608,8 +614,7 @@ void cmFindPackageCommand::InheritOptions(cmFindPackageCommand* other)
 
 bool cmFindPackageCommand::IsFound() const
 {
-  // TODO: track the actual found state.
-  return false;
+  return !this->FileFound.empty();
 }
 
 bool cmFindPackageCommand::IsDefined() const
@@ -717,7 +722,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   // Record options.
   this->Name = args[0];
   cm::string_view componentsSep = ""_s;
-  bool bypassProvider = false;
 
   // Always search directly in a generated path.
   this->SearchPathSuffixes.emplace_back();
@@ -751,7 +755,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
       this->Quiet = true;
       doing = DoingNone;
     } else if (args[i] == "BYPASS_PROVIDER") {
-      bypassProvider = true;
+      this->BypassProvider = true;
       doing = DoingNone;
     } else if (args[i] == "EXACT") {
       this->VersionExact = true;
@@ -1015,7 +1019,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
       this->VersionMaxPatch, this->VersionMaxTweak);
   }
 
-  return this->FindPackage(bypassProvider ? std::vector<std::string>{} : args);
+  return this->FindPackage(this->BypassProvider ? std::vector<std::string>{}
+                                                : args);
 }
 
 bool cmFindPackageCommand::FindPackage(
@@ -1517,6 +1522,23 @@ bool cmFindPackageCommand::FindModule(bool& found)
         this->DebugBuffer = cmStrCat(
           this->DebugBuffer, "The module is considered not found due to ",
           foundVar, " being FALSE.");
+
+        this->ConsideredPaths.emplace_back(mfile, FoundPackageMode::Module,
+                                           SearchResult::NotFound);
+        std::string const notFoundMessageVar =
+          cmStrCat(this->Name, "_NOT_FOUND_MESSAGE");
+        if (cmValue notFoundMessage =
+              this->Makefile->GetDefinition(notFoundMessageVar)) {
+
+          this->ConsideredPaths.back().Message = *notFoundMessage;
+        }
+      } else {
+        this->FileFound = mfile;
+        this->FileFoundMode = FoundPackageMode::Module;
+        std::string const versionVar = cmStrCat(this->Name, "_VERSION");
+        if (cmValue version = this->Makefile->GetDefinition(versionVar)) {
+          this->VersionFound = *version;
+        }
       }
     }
     return result;
@@ -1547,8 +1569,10 @@ bool cmFindPackageCommand::HandlePackageMode(
       }
       // The file location was cached.  Look for the correct file.
       std::string file;
-      if (this->FindConfigFile(dir, pdt::Any, file)) {
+      FoundPackageMode foundMode = FoundPackageMode::None;
+      if (this->FindConfigFile(dir, pdt::Any, file, foundMode)) {
         this->FileFound = std::move(file);
+        this->FileFoundMode = foundMode;
         fileFound = true;
       }
       def = this->Makefile->GetDefinition(this->Variable);
@@ -2765,13 +2789,17 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir,
 
   std::string const d = dir.substr(0, dir.size() - 1);
   if (cm::contains(this->IgnoredPaths, d)) {
+    this->ConsideredPaths.emplace_back(
+      dir, cmFindPackageCommand::FoundMode(type), SearchResult::Ignored);
     return false;
   }
 
   // Look for the file in this directory.
   std::string file;
-  if (this->FindConfigFile(d, type, file)) {
+  FoundPackageMode foundMode = FoundPackageMode::None;
+  if (this->FindConfigFile(d, type, file, foundMode)) {
     this->FileFound = std::move(file);
+    this->FileFoundMode = foundMode;
     return true;
   }
   return false;
@@ -2779,7 +2807,8 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir,
 
 bool cmFindPackageCommand::FindConfigFile(std::string const& dir,
                                           PackageDescriptionType type,
-                                          std::string& file)
+                                          std::string& file,
+                                          FoundPackageMode& foundMode)
 {
   for (auto const& config : this->Configs) {
     if (type != pdt::Any && config.Type != type) {
@@ -2789,14 +2818,24 @@ bool cmFindPackageCommand::FindConfigFile(std::string const& dir,
     if (this->DebugModeEnabled()) {
       this->DebugBuffer = cmStrCat(this->DebugBuffer, "  ", file, '\n');
     }
-    if (cmSystemTools::FileExists(file, true) && this->CheckVersion(file)) {
-      // Allow resolving symlinks when the config file is found through a link
-      if (this->UseRealPath) {
-        file = cmSystemTools::GetRealPath(file);
-      } else {
-        file = cmSystemTools::ToNormalizedPathOnDisk(file);
+    if (cmSystemTools::FileExists(file, true)) {
+      if (this->CheckVersion(file)) {
+        // Allow resolving symlinks when the config file is found through a
+        // link
+        if (this->UseRealPath) {
+          file = cmSystemTools::GetRealPath(file);
+        } else {
+          file = cmSystemTools::ToNormalizedPathOnDisk(file);
+        }
+        foundMode = cmFindPackageCommand::FoundMode(config.Type);
+        return true;
       }
-      return true;
+      this->ConsideredPaths.emplace_back(file,
+                                         cmFindPackageCommand::FoundMode(type),
+                                         SearchResult::InsufficientVersion);
+    } else {
+      this->ConsideredPaths.emplace_back(
+        file, cmFindPackageCommand::FoundMode(type), SearchResult::NoExist);
     }
   }
   return false;
@@ -3374,6 +3413,20 @@ bool cmFindPackageCommand::IsRequired() const
     this->Required == RequiredStatus::RequiredFromFindVar;
 }
 
+cmFindPackageCommand::FoundPackageMode cmFindPackageCommand::FoundMode(
+  PackageDescriptionType type)
+{
+  switch (type) {
+    case PackageDescriptionType::Any:
+      return FoundPackageMode::None;
+    case PackageDescriptionType::CMake:
+      return FoundPackageMode::Config;
+    case PackageDescriptionType::Cps:
+      return FoundPackageMode::Cps;
+  }
+  return FoundPackageMode::None;
+}
+
 // TODO: Debug cmsys::Glob double slash problem.
 
 bool cmFindPackage(std::vector<std::string> const& args,
@@ -3385,7 +3438,7 @@ bool cmFindPackage(std::vector<std::string> const& args,
 cmFindPackageDebugState::cmFindPackageDebugState(
   cmFindPackageCommand const* findPackage)
   : cmFindCommonDebugState("find_package", findPackage)
-// , FindPackageCommand(findPackage)
+  , FindPackageCommand(findPackage)
 {
 }
 
@@ -3416,6 +3469,205 @@ void cmFindPackageDebugState::WriteEvent(cmConfigureLog& log,
   (void)log;
   (void)mf;
 
-  // TODO
+  log.BeginEvent("find_package-v1", mf);
+
+  auto const* fpc = this->FindPackageCommand;
+
+  log.WriteValue("name"_s, fpc->Name);
+  if (!fpc->Components.empty()) {
+    log.BeginObject("components"_s);
+    log.BeginArray();
+    for (auto const& component : cmList{ fpc->Components }) {
+      log.NextArrayElement();
+      log.WriteValue("name"_s, component);
+      log.WriteValue("required"_s,
+                     fpc->RequiredComponents.find(component) !=
+                       fpc->RequiredComponents.end());
+      log.WriteValue("found"_s,
+                     mf.IsOn(cmStrCat(fpc->Name, '_', component, "_FOUND")));
+    }
+    log.EndArray();
+    log.EndObject();
+  }
+  if (!fpc->Configs.empty()) {
+    auto pdt_name =
+      [](cmFindPackageCommand::PackageDescriptionType type) -> std::string {
+      switch (type) {
+        case pdt::Any:
+          return "any";
+        case pdt::CMake:
+          return "cmake";
+        case pdt::Cps:
+          return "cps";
+      }
+      assert(false);
+      return "<UNKNOWN>";
+    };
+
+    log.BeginObject("configs"_s);
+    log.BeginArray();
+    for (auto const& config : fpc->Configs) {
+      log.NextArrayElement();
+      log.WriteValue("filename"_s, config.Name);
+      log.WriteValue("kind"_s, pdt_name(config.Type));
+    }
+    log.EndArray();
+    log.EndObject();
+  }
+  {
+    log.BeginObject("version_request"_s);
+    if (!fpc->Version.empty()) {
+      log.WriteValue("version"_s, fpc->Version);
+    }
+    if (!fpc->VersionComplete.empty()) {
+      log.WriteValue("version_complete"_s, fpc->VersionComplete);
+    }
+    if (!fpc->VersionRange.empty()) {
+      log.WriteValue("min"_s, std::string(fpc->VersionRangeMin));
+      log.WriteValue("max"_s, std::string(fpc->VersionRangeMax));
+    }
+    log.WriteValue("exact"_s, fpc->VersionExact);
+    log.EndObject();
+  }
+  {
+    auto required_str =
+      [](cmFindPackageCommand::RequiredStatus status) -> std::string {
+      switch (status) {
+        case cmFindPackageCommand::RequiredStatus::Optional:
+          return "optional";
+        case cmFindPackageCommand::RequiredStatus::OptionalExplicit:
+          return "optional_explicit";
+        case cmFindPackageCommand::RequiredStatus::RequiredExplicit:
+          return "required_explicit";
+        case cmFindPackageCommand::RequiredStatus::RequiredFromPackageVar:
+          return "required_from_package_variable";
+        case cmFindPackageCommand::RequiredStatus::RequiredFromFindVar:
+          return "required_from_find_variable";
+      }
+      assert(false);
+      return "<UNKNOWN>";
+    };
+    log.BeginObject("settings"_s);
+    log.WriteValue("required"_s, required_str(fpc->Required));
+    log.WriteValue("quiet"_s, fpc->Quiet);
+    log.WriteValue("global"_s, fpc->GlobalScope);
+    log.WriteValue("policy_scope"_s, fpc->PolicyScope);
+    log.WriteValue("bypass_provider"_s, fpc->BypassProvider);
+    if (!fpc->UserHintsArgs.empty()) {
+      log.WriteValue("hints"_s, fpc->UserHintsArgs);
+    }
+    if (!fpc->Names.empty()) {
+      log.WriteValue("names"_s, fpc->Names);
+    }
+    if (!fpc->UserGuessArgs.empty()) {
+      log.WriteValue("search_paths"_s, fpc->UserGuessArgs);
+    }
+    if (!fpc->SearchPathSuffixes.empty()) {
+      log.WriteValue("path_suffixes"_s, fpc->SearchPathSuffixes);
+    }
+    if (fpc->RegistryViewDefined) {
+      log.WriteValue(
+        "registry_view"_s,
+        std::string(cmWindowsRegistry::FromView(fpc->RegistryView)));
+    }
+    {
+      auto find_root_path_mode =
+        [](cmFindCommon::RootPathMode mode) -> std::string {
+        switch (mode) {
+          case cmFindCommon::RootPathModeNever:
+            return "NEVER";
+          case cmFindCommon::RootPathModeOnly:
+            return "ONLY";
+          case cmFindCommon::RootPathModeBoth:
+            return "BOTH";
+        }
+        assert(false);
+        return "<UNKNOWN>";
+      };
+      log.BeginObject("paths"_s);
+      log.WriteValue("CMAKE_FIND_USE_CMAKE_PATH"_s, !fpc->NoDefaultPath);
+      log.WriteValue("CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH"_s,
+                     !fpc->NoCMakeEnvironmentPath);
+      log.WriteValue("CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH"_s,
+                     !fpc->NoSystemEnvironmentPath);
+      log.WriteValue("CMAKE_FIND_USE_CMAKE_SYSTEM_PATH"_s,
+                     !fpc->NoCMakeSystemPath);
+      log.WriteValue("CMAKE_FIND_USE_INSTALL_PREFIX"_s,
+                     !fpc->NoCMakeInstallPath);
+      log.WriteValue("CMAKE_FIND_USE_PACKAGE_ROOT_PATH"_s,
+                     !fpc->NoPackageRootPath);
+      log.WriteValue("CMAKE_FIND_USE_CMAKE_PACKAGE_REGISTRY"_s,
+                     !fpc->NoUserRegistry);
+      log.WriteValue("CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY"_s,
+                     !fpc->NoSystemRegistry);
+      log.WriteValue("CMAKE_FIND_ROOT_PATH_MODE"_s,
+                     find_root_path_mode(fpc->FindRootPathMode));
+      log.EndObject();
+    }
+    log.EndObject();
+  }
+
+  auto found_mode =
+    [](cmFindPackageCommand::FoundPackageMode status) -> std::string {
+    switch (status) {
+      case cmFindPackageCommand::FoundPackageMode::None:
+        return "none?";
+      case cmFindPackageCommand::FoundPackageMode::Module:
+        return "module";
+      case cmFindPackageCommand::FoundPackageMode::Config:
+        return "config";
+      case cmFindPackageCommand::FoundPackageMode::Cps:
+        return "cps";
+      case cmFindPackageCommand::FoundPackageMode::Provider:
+        return "provider";
+    }
+    assert(false);
+    return "<UNKNOWN>";
+  };
+  if (!fpc->ConsideredPaths.empty()) {
+    auto search_result =
+      [](cmFindPackageCommand::SearchResult type) -> std::string {
+      switch (type) {
+        case cmFindPackageCommand::SearchResult::InsufficientVersion:
+          return "insufficient_version";
+        case cmFindPackageCommand::SearchResult::NoExist:
+          return "no_exist";
+        case cmFindPackageCommand::SearchResult::Ignored:
+          return "ignored";
+        case cmFindPackageCommand::SearchResult::NoConfigFile:
+          return "no_config_file";
+        case cmFindPackageCommand::SearchResult::NotFound:
+          return "not_found";
+      }
+      assert(false);
+      return "<UNKNOWN>";
+    };
+
+    log.BeginObject("candidates"_s);
+    log.BeginArray();
+    for (auto const& considered : fpc->ConsideredPaths) {
+      log.NextArrayElement();
+      log.WriteValue("path"_s, considered.Path);
+      log.WriteValue("mode"_s, found_mode(considered.Mode));
+      log.WriteValue("reason"_s, search_result(considered.Reason));
+      if (!considered.Message.empty()) {
+        log.WriteValue("message"_s, considered.Message);
+      }
+    }
+    log.EndArray();
+    log.EndObject();
+  }
+  // TODO: Add provider information (see #26925)
+  if (fpc->IsFound()) {
+    log.BeginObject("found"_s);
+    log.WriteValue("path"_s, fpc->FileFound);
+    log.WriteValue("mode"_s, found_mode(fpc->FileFoundMode));
+    log.WriteValue("version"_s, fpc->VersionFound);
+    log.EndObject();
+  } else {
+    log.WriteValue("found"_s, nullptr);
+  }
+
+  log.EndEvent();
 }
 #endif

+ 40 - 2
Source/cmFindPackageCommand.h

@@ -75,6 +75,7 @@ public:
                    SortDirectionType dir);
 
   cmFindPackageCommand(cmExecutionStatus& status);
+  ~cmFindPackageCommand() override;
 
   bool InitialPass(std::vector<std::string> const& args);
 
@@ -179,6 +180,14 @@ private:
   void PopFindPackageRootPathStack();
   class PushPopRootPathStack;
 
+  enum class FoundPackageMode
+  {
+    None,
+    Module,
+    Config,
+    Cps,
+    Provider,
+  };
   void ComputePrefixes();
   void FillPrefixesPackageRedirect();
   void FillPrefixesPackageRoot();
@@ -200,7 +209,7 @@ private:
   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);
+                      std::string& file, FoundPackageMode& foundMode);
   bool CheckVersion(std::string const& config_file);
   bool CheckVersionFile(std::string const& version_file,
                         std::string& result_version);
@@ -243,6 +252,7 @@ private:
   unsigned int VersionMaxCount = 0;
   bool VersionExact = false;
   std::string FileFound;
+  FoundPackageMode FileFoundMode = FoundPackageMode::None;
   std::string VersionFound;
   unsigned int VersionFoundMajor = 0;
   unsigned int VersionFoundMinor = 0;
@@ -250,6 +260,7 @@ private:
   unsigned int VersionFoundTweak = 0;
   unsigned int VersionFoundCount = 0;
   KWIML_INT_uint64_t RequiredCMakeVersion = 0;
+  bool BypassProvider = false;
   bool Quiet = false;
   RequiredStatus Required = RequiredStatus::Optional;
   bool UseCpsFiles = false;
@@ -273,6 +284,32 @@ private:
   std::set<std::string> OptionalComponents;
   std::set<std::string> RequiredTargets;
   std::string DebugBuffer;
+  enum class SearchResult
+  {
+    InsufficientVersion,
+    NoExist,
+    Ignored,
+    NoConfigFile,
+    NotFound,
+  };
+  struct ConsideredPath
+  {
+    ConsideredPath(std::string path, FoundPackageMode mode,
+                   SearchResult reason)
+      : Path(std::move(path))
+      , Mode(mode)
+      , Reason(reason)
+    {
+    }
+
+    std::string Path;
+    FoundPackageMode Mode;
+    SearchResult Reason;
+    std::string Message;
+  };
+  std::vector<ConsideredPath> ConsideredPaths;
+
+  static FoundPackageMode FoundMode(PackageDescriptionType type);
 
   struct ConfigName
   {
@@ -327,6 +364,7 @@ private:
   AppendixMap CpsAppendices;
 
   friend struct std::hash<ConfigFileInfo>;
+  friend class cmFindPackageDebugState;
 };
 
 namespace std {
@@ -363,5 +401,5 @@ private:
   void WriteEvent(cmConfigureLog& log, cmMakefile const& mf) const override;
 #endif
 
-  // cmFindPackageCommand const* const FindPackageCommand;
+  cmFindPackageCommand const* const FindPackageCommand;
 };

+ 1 - 1
Tests/RunCMake/FileAPI/FailConfigure-check.py

@@ -65,7 +65,7 @@ def check_object_configureLog(o):
     assert os.path.exists(path)
     eventKindNames = o["eventKindNames"]
     assert is_list(eventKindNames)
-    assert sorted(eventKindNames) == ["find-v1", "message-v1", "try_compile-v1", "try_run-v1"]
+    assert sorted(eventKindNames) == ["find-v1", "find_package-v1", "message-v1", "try_compile-v1", "try_run-v1"]
 
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]

+ 1 - 1
Tests/RunCMake/FileAPI/configureLog-v1-FailConfigure-check.py

@@ -45,7 +45,7 @@ def check_object_configureLog(o):
     assert os.path.exists(path)
     eventKindNames = o["eventKindNames"]
     assert is_list(eventKindNames)
-    assert sorted(eventKindNames) == ["find-v1", "message-v1", "try_compile-v1", "try_run-v1"]
+    assert sorted(eventKindNames) == ["find-v1", "find_package-v1", "message-v1", "try_compile-v1", "try_run-v1"]
 
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]

+ 1 - 1
Tests/RunCMake/FileAPI/configureLog-v1-check.py

@@ -14,7 +14,7 @@ def check_object_configureLog(o):
     assert os.path.exists(path)
     eventKindNames = o["eventKindNames"]
     assert is_list(eventKindNames)
-    assert sorted(eventKindNames) == ["find-v1", "message-v1", "try_compile-v1", "try_run-v1"]
+    assert sorted(eventKindNames) == ["find-v1", "find_package-v1", "message-v1", "try_compile-v1", "try_run-v1"]
 
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]