Files
CMake/Source/cmFindBase.cxx
Ben Boeckel a3f273b657 cmFindBase: Create find-v1 configure log events
Record `find_` command events in the configure log, except
`find_package` as it is far more complicated (and will have its own
event kind).

Note that testing only generates the events of interest, there is no
verification. Also note that testing that the "found" to "notfound"
transition causes an event is not testable because a truthy value in the
variable skips any kind of verification or other logic beyond
normalization.

Co-Authored-by: Ryan Krattiger <ryan.krattiger@kitware.com>
See: #24833
2025-04-28 13:55:36 +02:00

804 lines
26 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmFindBase.h"
#include <algorithm>
#include <cstddef>
#include <deque>
#include <iterator>
#include <map>
#include <utility>
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmCMakePath.h"
#include "cmConfigureLog.h"
#include "cmExecutionStatus.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmSearchPath.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
#include "cmWindowsRegistry.h"
#include "cmake.h"
cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status)
: cmFindCommon(status)
, FindCommandName(std::move(findCommandName))
{
}
bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
{
if (argsIn.size() < 2) {
this->SetError("called with incorrect number of arguments");
return false;
}
// copy argsIn into args so it can be modified,
// in the process extract the DOC "documentation"
// and handle options NO_CACHE and ENV
size_t size = argsIn.size();
std::vector<std::string> args;
bool foundDoc = false;
for (unsigned int j = 0; j < size; ++j) {
if (foundDoc || argsIn[j] != "DOC") {
if (argsIn[j] == "NO_CACHE") {
this->StoreResultInCache = false;
} else if (argsIn[j] == "ENV") {
if (j + 1 < size) {
j++;
std::vector<std::string> p =
cmSystemTools::GetEnvPathNormalized(argsIn[j]);
std::move(p.begin(), p.end(), std::back_inserter(args));
}
} else {
args.push_back(argsIn[j]);
}
} else {
if (j + 1 < size) {
foundDoc = true;
this->VariableDocumentation = argsIn[j + 1];
j++;
if (j >= size) {
break;
}
}
}
}
if (args.size() < 2) {
this->SetError("called with incorrect number of arguments");
return false;
}
this->VariableName = args[0];
this->InitialState = this->GetInitialState();
if (this->IsFound()) {
return true;
}
// Find what search path locations have been enabled/disable
this->SelectDefaultSearchModes();
// Find the current root path mode.
this->SelectDefaultRootPathMode();
// Find the current bundle/framework search policy.
this->SelectDefaultMacMode();
bool newStyle = false;
bool haveRequiredOrOptional = false;
enum Doing
{
DoingNone,
DoingNames,
DoingPaths,
DoingPathSuffixes,
DoingHints
};
Doing doing = DoingNames; // assume it starts with a name
for (unsigned int j = 1; j < args.size(); ++j) {
if (args[j] == "NAMES") {
doing = DoingNames;
newStyle = true;
} else if (args[j] == "PATHS") {
doing = DoingPaths;
newStyle = true;
} else if (args[j] == "HINTS") {
doing = DoingHints;
newStyle = true;
} else if (args[j] == "PATH_SUFFIXES") {
doing = DoingPathSuffixes;
newStyle = true;
} else if (args[j] == "NAMES_PER_DIR") {
doing = DoingNone;
if (this->NamesPerDirAllowed) {
this->NamesPerDir = true;
} else {
this->SetError("does not support NAMES_PER_DIR");
return false;
}
} else if (args[j] == "NO_SYSTEM_PATH") {
doing = DoingNone;
this->NoDefaultPath = true;
} else if (args[j] == "REQUIRED") {
doing = DoingNone;
if (haveRequiredOrOptional && !this->Required) {
this->SetError("cannot be both REQUIRED and OPTIONAL");
return false;
}
this->Required = true;
newStyle = true;
haveRequiredOrOptional = true;
} else if (args[j] == "OPTIONAL") {
doing = DoingNone;
if (haveRequiredOrOptional && this->Required) {
this->SetError("cannot be both REQUIRED and OPTIONAL");
return false;
}
newStyle = true;
haveRequiredOrOptional = true;
} else if (args[j] == "REGISTRY_VIEW") {
if (++j == args.size()) {
this->SetError("missing required argument for REGISTRY_VIEW");
return false;
}
auto view = cmWindowsRegistry::ToView(args[j]);
if (view) {
this->RegistryView = *view;
} else {
this->SetError(
cmStrCat("given invalid value for REGISTRY_VIEW: ", args[j]));
return false;
}
} else if (args[j] == "VALIDATOR") {
if (++j == args.size()) {
this->SetError("missing required argument for VALIDATOR");
return false;
}
auto command = this->Makefile->GetState()->GetCommand(args[j]);
if (!command) {
this->SetError(cmStrCat(
"command specified for VALIDATOR is undefined: ", args[j], '.'));
return false;
}
// ensure a macro is not specified as validator
auto const& validatorName = args[j];
cmList macros{ this->Makefile->GetProperty("MACROS") };
if (std::find_if(macros.begin(), macros.end(),
[&validatorName](std::string const& item) {
return cmSystemTools::Strucmp(validatorName.c_str(),
item.c_str()) == 0;
}) != macros.end()) {
this->SetError(cmStrCat(
"command specified for VALIDATOR is not a function: ", args[j],
'.'));
return false;
}
this->ValidatorName = args[j];
} else if (this->CheckCommonArgument(args[j])) {
doing = DoingNone;
} else {
// Some common arguments were accidentally supported by CMake
// 2.4 and 2.6.0 in the short-hand form of the command, so we
// must support it even though it is not documented.
if (doing == DoingNames) {
this->Names.push_back(args[j]);
} else if (doing == DoingPaths) {
this->UserGuessArgs.push_back(args[j]);
} else if (doing == DoingHints) {
this->UserHintsArgs.push_back(args[j]);
} else if (doing == DoingPathSuffixes) {
this->AddPathSuffix(args[j]);
}
}
}
if (!haveRequiredOrOptional) {
this->Required = this->Makefile->IsOn("CMAKE_FIND_REQUIRED");
}
if (this->VariableDocumentation.empty()) {
this->VariableDocumentation = "Where can ";
if (this->Names.empty()) {
this->VariableDocumentation += "the (unknown) library be found";
} else if (this->Names.size() == 1) {
this->VariableDocumentation +=
"the " + this->Names.front() + " library be found";
} else {
this->VariableDocumentation += "one of the ";
this->VariableDocumentation +=
cmJoin(cmMakeRange(this->Names).retreat(1), ", ");
this->VariableDocumentation +=
" or " + this->Names.back() + " libraries be found";
}
}
// look for old style
// FIND_*(VAR name path1 path2 ...)
if (!newStyle && !this->Names.empty()) {
// All the short-hand arguments have been recorded as names.
std::vector<std::string> shortArgs = this->Names;
this->Names.clear(); // clear out any values in Names
this->Names.push_back(shortArgs[0]);
cm::append(this->UserGuessArgs, shortArgs.begin() + 1, shortArgs.end());
}
this->ExpandPaths();
this->ComputeFinalPaths(IgnorePaths::Yes);
return true;
}
bool cmFindBase::Validate(std::string const& path) const
{
if (this->ValidatorName.empty()) {
return true;
}
// The validator command will be executed in an isolated scope.
cmMakefile::ScopePushPop varScope(this->Makefile);
cmMakefile::PolicyPushPop polScope(this->Makefile);
static_cast<void>(varScope);
static_cast<void>(polScope);
auto resultName =
cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName),
"_VALIDATOR_STATUS"_s);
this->Makefile->AddDefinitionBool(resultName, true);
cmListFileFunction validator(
this->ValidatorName, 0, 0,
{ cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0),
cmListFileArgument(path, cmListFileArgument::Quoted, 0) });
cmExecutionStatus status(*this->Makefile);
if (this->Makefile->ExecuteCommand(validator, status)) {
return this->Makefile->GetDefinition(resultName).IsOn();
}
return false;
}
void cmFindBase::ExpandPaths()
{
if (!this->NoDefaultPath) {
if (!this->NoPackageRootPath) {
this->FillPackageRootPath();
}
if (!this->NoCMakePath) {
this->FillCMakeVariablePath();
}
if (!this->NoCMakeEnvironmentPath) {
this->FillCMakeEnvironmentPath();
}
}
this->FillUserHintsPath();
if (!this->NoDefaultPath) {
if (!this->NoSystemEnvironmentPath) {
this->FillSystemEnvironmentPath();
}
if (!this->NoCMakeSystemPath) {
this->FillCMakeSystemVariablePath();
}
}
this->FillUserGuessPath();
}
void cmFindBase::FillCMakeEnvironmentPath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeEnvironment];
// Add CMAKE_*_PATH environment variables
std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
paths.AddEnvPrefixPath("CMAKE_PREFIX_PATH");
paths.AddEnvPath(var);
if (this->CMakePathName == "PROGRAM") {
paths.AddEnvPath("CMAKE_APPBUNDLE_PATH");
} else {
paths.AddEnvPath("CMAKE_FRAMEWORK_PATH");
}
paths.AddSuffixes(this->SearchPathSuffixes);
}
void cmFindBase::FillPackageRootPath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot];
// Add the PACKAGE_ROOT_PATH from each enclosing find_package call.
for (std::vector<std::string> const& pkgPaths :
cmReverseRange(this->Makefile->FindPackageRootPathStack)) {
paths.AddPrefixPaths(pkgPaths);
}
paths.AddSuffixes(this->SearchPathSuffixes);
}
void cmFindBase::FillCMakeVariablePath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::CMake];
// Add CMake variables of the same name as the previous environment
// variables CMAKE_*_PATH to be used most of the time with -D
// command line options
std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
paths.AddCMakePrefixPath("CMAKE_PREFIX_PATH");
paths.AddCMakePath(var);
if (this->CMakePathName == "PROGRAM") {
paths.AddCMakePath("CMAKE_APPBUNDLE_PATH");
} else {
paths.AddCMakePath("CMAKE_FRAMEWORK_PATH");
}
paths.AddSuffixes(this->SearchPathSuffixes);
}
void cmFindBase::FillSystemEnvironmentPath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::SystemEnvironment];
// Add LIB or INCLUDE
if (!this->EnvironmentPath.empty()) {
paths.AddEnvPath(this->EnvironmentPath);
}
// Add PATH
paths.AddEnvPath("PATH");
paths.AddSuffixes(this->SearchPathSuffixes);
}
namespace {
struct entry_to_remove
{
entry_to_remove(std::string const& name, cmMakefile* makefile)
{
if (cmValue to_skip = makefile->GetDefinition(
cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_COUNT"))) {
cmStrToLong(*to_skip, &count);
}
if (cmValue prefix_value = makefile->GetDefinition(
cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_VALUE"))) {
value = *prefix_value;
}
}
bool valid() const { return count > 0 && !value.empty(); }
void remove_self(std::vector<std::string>& entries) const
{
if (this->valid()) {
long to_skip = this->count;
size_t index_to_remove = 0;
for (auto const& path : entries) {
if (path == this->value && --to_skip == 0) {
break;
}
++index_to_remove;
}
if (index_to_remove < entries.size() && to_skip == 0) {
entries.erase(entries.begin() + index_to_remove);
}
}
}
long count = -1;
std::string value;
};
}
void cmFindBase::FillCMakeSystemVariablePath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeSystem];
bool const install_prefix_in_list =
!this->Makefile->IsOn("CMAKE_FIND_NO_INSTALL_PREFIX");
bool const remove_install_prefix = this->NoCMakeInstallPath;
bool const add_install_prefix = !this->NoCMakeInstallPath &&
this->Makefile->IsDefinitionSet("CMAKE_FIND_USE_INSTALL_PREFIX");
// We have 3 possible states for `CMAKE_SYSTEM_PREFIX_PATH` and
// `CMAKE_INSTALL_PREFIX`.
// Either we need to remove `CMAKE_INSTALL_PREFIX`, add
// `CMAKE_INSTALL_PREFIX`, or do nothing.
//
// When we need to remove `CMAKE_INSTALL_PREFIX` we remove the Nth occurrence
// of `CMAKE_INSTALL_PREFIX` from `CMAKE_SYSTEM_PREFIX_PATH`, where `N` is
// computed by `CMakeSystemSpecificInformation.cmake` while constructing
// `CMAKE_SYSTEM_PREFIX_PATH`. This ensures that if projects / toolchains
// have removed `CMAKE_INSTALL_PREFIX` from the list, we don't remove
// some other entry by mistake ( likewise for `CMAKE_STAGING_PREFIX` )
entry_to_remove install_entry("INSTALL", this->Makefile);
entry_to_remove staging_entry("STAGING", this->Makefile);
if (remove_install_prefix && install_prefix_in_list &&
(install_entry.valid() || staging_entry.valid())) {
cmValue prefix_paths =
this->Makefile->GetDefinition("CMAKE_SYSTEM_PREFIX_PATH");
// remove entries from CMAKE_SYSTEM_PREFIX_PATH
cmList expanded{ *prefix_paths };
install_entry.remove_self(expanded);
staging_entry.remove_self(expanded);
for (std::string& p : expanded) {
p = cmSystemTools::CollapseFullPath(
p, this->Makefile->GetCurrentSourceDirectory());
}
paths.AddPrefixPaths(expanded);
} else if (add_install_prefix && !install_prefix_in_list) {
paths.AddCMakePrefixPath("CMAKE_INSTALL_PREFIX");
paths.AddCMakePrefixPath("CMAKE_STAGING_PREFIX");
paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
} else {
// Otherwise the current setup of `CMAKE_SYSTEM_PREFIX_PATH` is correct
paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
}
std::string var = cmStrCat("CMAKE_SYSTEM_", this->CMakePathName, "_PATH");
paths.AddCMakePath(var);
if (this->CMakePathName == "PROGRAM") {
paths.AddCMakePath("CMAKE_SYSTEM_APPBUNDLE_PATH");
} else {
paths.AddCMakePath("CMAKE_SYSTEM_FRAMEWORK_PATH");
}
paths.AddSuffixes(this->SearchPathSuffixes);
}
void cmFindBase::FillUserHintsPath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::Hints];
for (std::string const& p : this->UserHintsArgs) {
paths.AddUserPath(p);
}
paths.AddSuffixes(this->SearchPathSuffixes);
}
void cmFindBase::FillUserGuessPath()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::Guess];
for (std::string const& p : this->UserGuessArgs) {
paths.AddUserPath(p);
}
paths.AddSuffixes(this->SearchPathSuffixes);
}
cmFindBase::FindState cmFindBase::GetInitialState()
{
if (cmValue value = this->Makefile->GetDefinition(this->VariableName)) {
cmState* state = this->Makefile->GetState();
cmValue cacheEntry = state->GetCacheEntryValue(this->VariableName);
bool found = !cmIsNOTFOUND(*value);
bool cached = cacheEntry != nullptr;
auto cacheType = cached ? state->GetCacheEntryType(this->VariableName)
: cmStateEnums::UNINITIALIZED;
if (cached && cacheType != cmStateEnums::UNINITIALIZED) {
this->VariableType = cacheType;
if (auto const& hs =
state->GetCacheEntryProperty(this->VariableName, "HELPSTRING")) {
this->VariableDocumentation = *hs;
}
}
if (found) {
// If the user specifies the entry on the command line without a
// type we should add the type and docstring but keep the
// original value. Tell the subclass implementations to do
// this.
if (cached && cacheType == cmStateEnums::UNINITIALIZED) {
this->AlreadyInCacheWithoutMetaInfo = true;
}
return FindState::Found;
}
return FindState::NotFound;
}
return FindState::Undefined;
}
bool cmFindBase::IsFound() const
{
return this->InitialState == FindState::Found;
}
bool cmFindBase::IsDefined() const
{
return this->InitialState != FindState::Undefined;
}
void cmFindBase::NormalizeFindResult()
{
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) ==
cmPolicies::NEW) {
// ensure the path returned by find_* command is absolute
auto const& existingValue =
this->Makefile->GetDefinition(this->VariableName);
std::string value;
if (!existingValue->empty()) {
value =
cmCMakePath(*existingValue, cmCMakePath::auto_format)
.Absolute(cmCMakePath(
this->Makefile->GetCMakeInstance()->GetCMakeWorkingDirectory()))
.Normal()
.GenericString();
if (!cmSystemTools::FileExists(value, false)) {
value = *existingValue;
}
}
if (this->StoreResultInCache) {
// If the user specifies the entry on the command line without a
// type we should add the type and docstring but keep the original
// value.
if (value != *existingValue || this->AlreadyInCacheWithoutMetaInfo) {
this->Makefile->GetCMakeInstance()->AddCacheEntry(
this->VariableName, value, this->VariableDocumentation,
this->VariableType);
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
cmPolicies::NEW) {
if (this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
this->Makefile->AddDefinition(this->VariableName, value);
}
} else {
// if there was a definition then remove it
// This is required to ensure same behavior as
// cmMakefile::AddCacheDefinition.
this->Makefile->RemoveDefinition(this->VariableName);
}
}
} else {
// ensure a normal variable is defined.
this->Makefile->AddDefinition(this->VariableName, value);
}
} else {
// If the user specifies the entry on the command line without a
// type we should add the type and docstring but keep the original
// value.
if (this->StoreResultInCache) {
if (this->AlreadyInCacheWithoutMetaInfo) {
this->Makefile->AddCacheDefinition(this->VariableName, "",
this->VariableDocumentation,
this->VariableType);
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
cmPolicies::NEW &&
this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
this->Makefile->AddDefinition(
this->VariableName,
*this->Makefile->GetCMakeInstance()->GetCacheDefinition(
this->VariableName));
}
}
} else {
// ensure a normal variable is defined.
this->Makefile->AddDefinition(
this->VariableName,
this->Makefile->GetSafeDefinition(this->VariableName));
}
}
}
void cmFindBase::StoreFindResult(std::string const& value)
{
bool force =
this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) == cmPolicies::NEW;
bool updateNormalVariable =
this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) == cmPolicies::NEW;
if (!value.empty()) {
if (this->StoreResultInCache) {
this->Makefile->AddCacheDefinition(this->VariableName, value,
this->VariableDocumentation,
this->VariableType, force);
if (updateNormalVariable &&
this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
this->Makefile->AddDefinition(this->VariableName, value);
}
} else {
this->Makefile->AddDefinition(this->VariableName, value);
}
return;
}
auto notFound = cmStrCat(this->VariableName, "-NOTFOUND");
if (this->StoreResultInCache) {
this->Makefile->AddCacheDefinition(this->VariableName, notFound,
this->VariableDocumentation,
this->VariableType, force);
if (updateNormalVariable &&
this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
this->Makefile->AddDefinition(this->VariableName, notFound);
}
} else {
this->Makefile->AddDefinition(this->VariableName, notFound);
}
if (this->Required) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Could not find ", this->VariableName, " using the following ",
(this->FindCommandName == "find_file" ||
this->FindCommandName == "find_path"
? "files"
: "names"),
": ", cmJoin(this->Names, ", ")));
cmSystemTools::SetFatalErrorOccurred();
}
}
cmFindBaseDebugState::cmFindBaseDebugState(std::string commandName,
cmFindBase const* findBase)
: FindCommand(findBase)
, CommandName(std::move(commandName))
{
}
cmFindBaseDebugState::~cmFindBaseDebugState()
{
bool found = !this->FoundSearchLocation.path.empty();
#ifndef CMAKE_BOOTSTRAP
// Write find event to the configure log if the log exists
if (cmConfigureLog* log =
this->FindCommand->Makefile->GetCMakeInstance()->GetConfigureLog()) {
// Write event if any of:
// - debug mode is enabled
// - the variable was not defined (first run)
// - the variable found state does not match the new found state (state
// transition)
if (this->FindCommand->DebugMode || !this->FindCommand->IsDefined() ||
this->FindCommand->IsFound() != found) {
this->WriteFindEvent(*log, *this->FindCommand->Makefile);
}
}
#endif
if (!this->FindCommand->DebugMode) {
return;
}
// clang-format off
auto buffer =
cmStrCat(
this->CommandName, " called with the following settings:"
"\n VAR: ", this->FindCommand->VariableName,
"\n NAMES: ", cmWrap('"', this->FindCommand->Names, '"', "\n "),
"\n Documentation: ", this->FindCommand->VariableDocumentation,
"\n Framework"
"\n Only Search Frameworks: ", this->FindCommand->SearchFrameworkOnly,
"\n Search Frameworks Last: ", this->FindCommand->SearchFrameworkLast,
"\n Search Frameworks First: ", this->FindCommand->SearchFrameworkFirst,
"\n AppBundle"
"\n Only Search AppBundle: ", this->FindCommand->SearchAppBundleOnly,
"\n Search AppBundle Last: ", this->FindCommand->SearchAppBundleLast,
"\n Search AppBundle First: ", this->FindCommand->SearchAppBundleFirst,
"\n"
);
// clang-format on
if (this->FindCommand->NoDefaultPath) {
buffer += " NO_DEFAULT_PATH Enabled\n";
} else {
// clang-format off
buffer += cmStrCat(
" CMAKE_FIND_USE_CMAKE_PATH: ", !this->FindCommand->NoCMakePath,
"\n CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH: ", !this->FindCommand->NoCMakeEnvironmentPath,
"\n CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: ", !this->FindCommand->NoSystemEnvironmentPath,
"\n CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: ", !this->FindCommand->NoCMakeSystemPath,
"\n CMAKE_FIND_USE_INSTALL_PREFIX: ", !this->FindCommand->NoCMakeInstallPath,
"\n"
);
// clang-format on
}
buffer +=
cmStrCat(this->CommandName, " considered the following locations:\n");
for (auto const& state : this->FailedSearchLocations) {
std::string path = cmStrCat(" ", state.path);
if (!state.regexName.empty()) {
path = cmStrCat(path, '/', state.regexName);
}
buffer += cmStrCat(path, '\n');
}
if (found) {
buffer += cmStrCat("The item was found at\n ",
this->FoundSearchLocation.path, '\n');
} else {
buffer += "The item was not found.\n";
}
this->FindCommand->DebugMessage(buffer);
}
void cmFindBaseDebugState::FoundAt(std::string const& path,
std::string regexName)
{
if (this->TrackSearchProgress()) {
this->FoundSearchLocation = DebugLibState{ std::move(regexName), path };
}
}
void cmFindBaseDebugState::FailedAt(std::string const& path,
std::string regexName)
{
if (this->TrackSearchProgress()) {
this->FailedSearchLocations.emplace_back(std::move(regexName), path);
}
}
#ifndef CMAKE_BOOTSTRAP
void cmFindBaseDebugState::WriteFindEvent(cmConfigureLog& log,
cmMakefile const& mf) const
{
log.BeginEvent("find-v1", mf);
// Mode is the Command name without the "find_" prefix
log.WriteValue("mode"_s, this->CommandName.substr(5));
log.WriteValue("variable"_s, this->FindCommand->VariableName);
log.WriteValue("description"_s, this->FindCommand->VariableDocumentation);
// Yes, this needs to return a `std::string`. If it returns a `const char*`,
// the `WriteValue` method prefers the `bool` overload. There's no overload
// for a `cm::string_view` because the underlying JSON library doesn't
// support `string_view` arguments itself.
auto search_opt_to_str = [](bool first, bool last,
bool only) -> std::string {
return first ? "FIRST" : (last ? "LAST" : (only ? "ONLY" : "NEVER"));
};
log.BeginObject("settings"_s);
log.WriteValue("SearchFramework"_s,
search_opt_to_str(this->FindCommand->SearchFrameworkFirst,
this->FindCommand->SearchFrameworkLast,
this->FindCommand->SearchFrameworkOnly));
log.WriteValue("SearchAppBundle"_s,
search_opt_to_str(this->FindCommand->SearchAppBundleFirst,
this->FindCommand->SearchAppBundleLast,
this->FindCommand->SearchAppBundleOnly));
log.WriteValue("CMAKE_FIND_USE_CMAKE_PATH"_s,
!this->FindCommand->NoCMakePath);
log.WriteValue("CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH"_s,
!this->FindCommand->NoCMakeEnvironmentPath);
log.WriteValue("CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH"_s,
!this->FindCommand->NoSystemEnvironmentPath);
log.WriteValue("CMAKE_FIND_USE_CMAKE_SYSTEM_PATH"_s,
!this->FindCommand->NoCMakeSystemPath);
log.WriteValue("CMAKE_FIND_USE_INSTALL_PREFIX"_s,
!this->FindCommand->NoCMakeInstallPath);
log.EndObject();
log.WriteValue("names"_s, this->FindCommand->Names);
std::vector<std::string> directories;
directories.reserve(this->FailedSearchLocations.size());
for (auto const& location : this->FailedSearchLocations) {
directories.push_back(location.path);
}
log.WriteValue("candidate_directories"_s, this->FindCommand->SearchPaths);
log.WriteValue("searched_directories"_s, directories);
if (!this->FoundSearchLocation.path.empty()) {
log.WriteValue("found"_s, this->FoundSearchLocation.path);
} else {
log.WriteValue("found"_s, false);
}
log.EndEvent();
}
#endif
bool cmFindBaseDebugState::TrackSearchProgress() const
{
// Track search progress if debugging or logging the configure.
return this->FindCommand->DebugMode
#ifndef CMAKE_BOOTSTRAP
|| this->FindCommand->Makefile->GetCMakeInstance()->GetConfigureLog()
#endif
;
}