summaryrefslogtreecommitdiffstats
path: root/Source/cmFindPackageCommand.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmFindPackageCommand.cxx')
-rw-r--r--Source/cmFindPackageCommand.cxx270
1 files changed, 246 insertions, 24 deletions
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index f55d838..4ad9124 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -13,6 +13,7 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/string_view>
#include "cmsys/Directory.hxx"
@@ -22,6 +23,8 @@
#include "cmsys/String.h"
#include "cmAlgorithms.h"
+#include "cmDependencyProvider.h"
+#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
@@ -33,6 +36,7 @@
#include "cmSystemTools.h"
#include "cmValue.h"
#include "cmVersion.h"
+#include "cmWindowsRegistry.h"
#if defined(__HAIKU__)
# include <FindDirectory.h>
@@ -42,6 +46,8 @@
class cmExecutionStatus;
class cmFileList;
+cmFindPackageCommand::PathLabel
+ cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT");
cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::UserRegistry(
"PACKAGE_REGISTRY");
cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::Builds(
@@ -109,8 +115,10 @@ void cmFindPackageCommand::AppendSearchPathGroups()
{
std::vector<cmFindCommon::PathLabel>* labels;
- // Update the All group with new paths
+ // Update the All group with new paths. Note that package redirection must
+ // take precedence over everything else, so it has to be first in the array.
labels = &this->PathGroupLabelMap[PathGroup::All];
+ labels->insert(labels->begin(), PathLabel::PackageRedirect);
labels->insert(
std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem),
PathLabel::UserRegistry);
@@ -122,6 +130,8 @@ void cmFindPackageCommand::AppendSearchPathGroups()
// Create the new path objects
this->LabeledPaths.insert(
+ std::make_pair(PathLabel::PackageRedirect, cmSearchPath(this)));
+ this->LabeledPaths.insert(
std::make_pair(PathLabel::UserRegistry, cmSearchPath(this)));
this->LabeledPaths.insert(
std::make_pair(PathLabel::Builds, cmSearchPath(this)));
@@ -229,6 +239,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
const char* components_sep = "";
std::set<std::string> requiredComponents;
std::set<std::string> optionalComponents;
+ std::vector<std::pair<std::string, const char*>> componentVarDefs;
+ bool bypassProvider = false;
// Always search directly in a generated path.
this->SearchPathSuffixes.emplace_back();
@@ -259,9 +271,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
if (args[i] == "QUIET") {
this->Quiet = true;
doing = DoingNone;
+ } else if (args[i] == "BYPASS_PROVIDER") {
+ bypassProvider = true;
+ doing = DoingNone;
} else if (args[i] == "EXACT") {
this->VersionExact = true;
doing = DoingNone;
+ } else if (args[i] == "GLOBAL") {
+ this->GlobalScope = true;
+ doing = DoingNone;
} else if (args[i] == "MODULE") {
moduleArgs.insert(i);
doing = DoingNone;
@@ -314,6 +332,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
// Ignore legacy option.
configArgs.insert(i);
doing = DoingNone;
+ } else if (args[i] == "REGISTRY_VIEW") {
+ if (++i == args.size()) {
+ this->SetError("missing required argument for \"REGISTRY_VIEW\"");
+ return false;
+ }
+ auto view = cmWindowsRegistry::ToView(args[i]);
+ if (view) {
+ this->RegistryView = *view;
+ this->RegistryViewDefined = true;
+ } else {
+ this->SetError(
+ cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[i]));
+ return false;
+ }
} else if (this->CheckCommonArgument(args[i])) {
configArgs.insert(i);
doing = DoingNone;
@@ -330,7 +362,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
- this->AddFindDefinition(req_var, isRequired);
+ componentVarDefs.emplace_back(req_var, isRequired);
// Append to the list of required components.
components += components_sep;
@@ -364,6 +396,12 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
}
+ if (!this->GlobalScope) {
+ cmValue value(
+ this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_TARGETS_GLOBAL"));
+ this->GlobalScope = value.IsOn();
+ }
+
std::vector<std::string> doubledComponents;
std::set_intersection(requiredComponents.begin(), requiredComponents.end(),
optionalComponents.begin(), optionalComponents.end(),
@@ -376,7 +414,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
return false;
}
- // Maybe choose one mode exclusively.
+ // Check and eliminate search modes not allowed by the args provided
this->UseFindModules = configArgs.empty();
this->UseConfigFiles = moduleArgs.empty();
if (!this->UseFindModules && !this->UseConfigFiles) {
@@ -511,6 +549,48 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
return true;
}
+ // Now choose what method(s) we will use to satisfy the request. Note that
+ // we still want all the above checking of arguments, etc. regardless of the
+ // method used. This will ensure ill-formed arguments are caught earlier,
+ // before things like dependency providers need to deal with them.
+
+ // A dependency provider (if set) gets first look before other methods.
+ // We do this before modifying the package root path stack because a
+ // provider might use methods that ignore that.
+ cmState* state = this->Makefile->GetState();
+ cmState::Command providerCommand = state->GetDependencyProviderCommand(
+ cmDependencyProvider::Method::FindPackage);
+ if (bypassProvider) {
+ if (this->DebugMode && providerCommand) {
+ this->DebugMessage(
+ "BYPASS_PROVIDER given, skipping dependency provider");
+ }
+ } else if (providerCommand) {
+ if (this->DebugMode) {
+ this->DebugMessage(cmStrCat("Trying dependency provider command: ",
+ state->GetDependencyProvider()->GetCommand(),
+ "()"));
+ }
+ std::vector<cmListFileArgument> listFileArgs(args.size() + 1);
+ listFileArgs[0] =
+ cmListFileArgument("FIND_PACKAGE", cmListFileArgument::Unquoted, 0);
+ std::transform(args.begin(), args.end(), listFileArgs.begin() + 1,
+ [](const std::string& arg) {
+ return cmListFileArgument(arg,
+ cmListFileArgument::Bracket, 0);
+ });
+ if (!providerCommand(listFileArgs, this->Status)) {
+ return false;
+ }
+ if (this->Makefile->IsOn(cmStrCat(this->Name, "_FOUND"))) {
+ if (this->DebugMode) {
+ this->DebugMessage("Package was found by the dependency provider");
+ }
+ this->AppendSuccessInformation();
+ return true;
+ }
+ }
+
{
// Allocate a PACKAGE_ROOT_PATH for the current find_package call.
this->Makefile->FindPackageRootPathStack.emplace_back();
@@ -541,11 +621,64 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
}
- this->SetModuleVariables(components);
+ this->SetModuleVariables(components, componentVarDefs);
+
+ // See if we have been told to delegate to FetchContent or some other
+ // redirected config package first. We have to check all names that
+ // find_package() may look for, but only need to invoke the override for the
+ // first one that matches.
+ auto overrideNames = this->Names;
+ if (overrideNames.empty()) {
+ overrideNames.push_back(this->Name);
+ }
+ bool forceConfigMode = false;
+ const auto redirectsDir =
+ this->Makefile->GetSafeDefinition("CMAKE_FIND_PACKAGE_REDIRECTS_DIR");
+ for (const auto& overrideName : overrideNames) {
+ const auto nameLower = cmSystemTools::LowerCase(overrideName);
+ const auto delegatePropName =
+ cmStrCat("_FetchContent_", nameLower, "_override_find_package");
+ const cmValue delegateToFetchContentProp =
+ this->Makefile->GetState()->GetGlobalProperty(delegatePropName);
+ if (delegateToFetchContentProp.IsOn()) {
+ // When this property is set, the FetchContent module has already been
+ // included at least once, so we know the FetchContent_MakeAvailable()
+ // command will be defined. Any future find_package() calls after this
+ // one for this package will by-pass this once-only delegation.
+ // The following call will typically create a <name>-config.cmake file
+ // in the redirectsDir, which we still want to process like any other
+ // config file to ensure we follow normal find_package() processing.
+ cmListFileFunction func(
+ "FetchContent_MakeAvailable", 0, 0,
+ { cmListFileArgument(overrideName, cmListFileArgument::Unquoted, 0) });
+ if (!this->Makefile->ExecuteCommand(func, this->Status)) {
+ return false;
+ }
+ }
+
+ if (cmSystemTools::FileExists(
+ cmStrCat(redirectsDir, '/', nameLower, "-config.cmake")) ||
+ cmSystemTools::FileExists(
+ cmStrCat(redirectsDir, '/', overrideName, "Config.cmake"))) {
+ // Force the use of this redirected config package file, regardless of
+ // the type of find_package() call. Files in the redirectsDir must always
+ // take priority over everything else.
+ forceConfigMode = true;
+ this->UseConfigFiles = true;
+ this->UseFindModules = false;
+ this->Names.clear();
+ this->Names.emplace_back(overrideName); // Force finding this one
+ this->Variable = cmStrCat(this->Name, "_DIR");
+ this->SetConfigDirCacheVariable(redirectsDir);
+ break;
+ }
+ }
// See if there is a Find<PackageName>.cmake module.
bool loadedPackage = false;
- if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG")) {
+ if (forceConfigMode) {
+ loadedPackage = this->FindPackageUsingConfigMode();
+ } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG")) {
if (this->UseConfigFiles && this->FindPackageUsingConfigMode()) {
loadedPackage = true;
} else {
@@ -612,6 +745,12 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
this->AppendSuccessInformation();
+ // Restore original state of "_FIND_" variables set in SetModuleVariables()
+ this->RestoreFindDefinitions();
+
+ // Pop the package stack
+ this->Makefile->FindPackageRootPathStack.pop_back();
+
if (!this->DebugBuffer.empty()) {
this->DebugMessage(this->DebugBuffer);
}
@@ -683,23 +822,28 @@ void cmFindPackageCommand::SetVersionVariables(
char buf[64];
snprintf(buf, sizeof(buf), "%u", major);
addDefinition(prefix + "_MAJOR", buf);
- sprintf(buf, "%u", minor);
+ snprintf(buf, sizeof(buf), "%u", minor);
addDefinition(prefix + "_MINOR", buf);
- sprintf(buf, "%u", patch);
+ snprintf(buf, sizeof(buf), "%u", patch);
addDefinition(prefix + "_PATCH", buf);
- sprintf(buf, "%u", tweak);
+ snprintf(buf, sizeof(buf), "%u", tweak);
addDefinition(prefix + "_TWEAK", buf);
- sprintf(buf, "%u", count);
+ snprintf(buf, sizeof(buf), "%u", count);
addDefinition(prefix + "_COUNT", buf);
}
-void cmFindPackageCommand::SetModuleVariables(const std::string& components)
+void cmFindPackageCommand::SetModuleVariables(
+ const std::string& components,
+ const std::vector<std::pair<std::string, const char*>>& componentVarDefs)
{
this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name);
- // Store the list of components.
+ // Store the list of components and associated variable definitions
std::string components_var = this->Name + "_FIND_COMPONENTS";
this->AddFindDefinition(components_var, components);
+ for (const auto& varDef : componentVarDefs) {
+ this->AddFindDefinition(varDef.first, varDef.second);
+ }
if (this->Quiet) {
// Tell the module that is about to be read that it should find
@@ -758,6 +902,11 @@ void cmFindPackageCommand::SetModuleVariables(const std::string& components)
id = cmStrCat(this->Name, "_FIND_VERSION_RANGE_MAX");
this->AddFindDefinition(id, this->VersionRangeMax);
}
+
+ if (this->RegistryViewDefined) {
+ this->AddFindDefinition(cmStrCat(this->Name, "_FIND_REGISTRY_VIEW"),
+ cmWindowsRegistry::FromView(this->RegistryView));
+ }
}
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
@@ -1048,7 +1197,7 @@ bool cmFindPackageCommand::HandlePackageMode(
: MessageType::WARNING,
e.str());
if (this->Required) {
- cmSystemTools::SetFatalErrorOccured();
+ cmSystemTools::SetFatalErrorOccurred();
}
if (!aw.str().empty()) {
@@ -1155,19 +1304,24 @@ bool cmFindPackageCommand::FindConfig()
} else {
init = this->Variable + "-NOTFOUND";
}
+ // We force the value since we do not get here if it was already set.
+ this->SetConfigDirCacheVariable(init);
+
+ return found;
+}
+
+void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value)
+{
std::string help =
cmStrCat("The directory containing a CMake configuration file for ",
this->Name, '.');
- // We force the value since we do not get here if it was already set.
- this->Makefile->AddCacheDefinition(this->Variable, init, help.c_str(),
+ this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(),
cmStateEnums::PATH, true);
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
cmPolicies::NEW &&
this->Makefile->IsNormalDefinitionSet(this->Variable)) {
- this->Makefile->AddDefinition(this->Variable, init);
+ this->Makefile->AddDefinition(this->Variable, value);
}
-
- return found;
}
bool cmFindPackageCommand::FindPrefixedConfig()
@@ -1200,6 +1354,11 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f,
PolicyScopeRule psr)
{
const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope;
+
+ using ITScope = cmMakefile::ImportedTargetScope;
+ ITScope scope = this->GlobalScope ? ITScope::Global : ITScope::Local;
+ cmMakefile::SetGlobalTargetImportScope globScope(this->Makefile, scope);
+
if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
return true;
}
@@ -1288,12 +1447,6 @@ void cmFindPackageCommand::AppendSuccessInformation()
this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName,
"REQUIRED");
}
-
- // Restore original state of "_FIND_" variables we set.
- this->RestoreFindDefinitions();
-
- // Pop the package stack
- this->Makefile->FindPackageRootPathStack.pop_back();
}
inline std::size_t collectPathsForDebug(std::string& buffer,
@@ -1313,6 +1466,8 @@ inline std::size_t collectPathsForDebug(std::string& buffer,
void cmFindPackageCommand::ComputePrefixes()
{
+ this->FillPrefixesPackageRedirect();
+
if (!this->NoDefaultPath) {
if (!this->NoPackageRootPath) {
this->FillPrefixesPackageRoot();
@@ -1346,6 +1501,23 @@ void cmFindPackageCommand::ComputePrefixes()
this->ComputeFinalPaths(IgnorePaths::No);
}
+void cmFindPackageCommand::FillPrefixesPackageRedirect()
+{
+ cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRedirect];
+
+ const auto redirectDir =
+ this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_REDIRECTS_DIR");
+ if (redirectDir && !redirectDir->empty()) {
+ paths.AddPath(*redirectDir);
+ }
+ if (this->DebugMode) {
+ std::string debugBuffer =
+ "The internally managed CMAKE_FIND_PACKAGE_REDIRECTS_DIR.\n";
+ collectPathsForDebug(debugBuffer, paths);
+ this->DebugBuffer = cmStrCat(this->DebugBuffer, debugBuffer);
+ }
+}
+
void cmFindPackageCommand::FillPrefixesPackageRoot()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot];
@@ -1668,7 +1840,57 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable()
{
cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeSystem];
- paths.AddCMakePath("CMAKE_SYSTEM_PREFIX_PATH");
+ const bool install_prefix_in_list =
+ !this->Makefile->IsOn("CMAKE_FIND_NO_INSTALL_PREFIX");
+ const bool remove_install_prefix = this->NoCMakeInstallPath;
+ const bool 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
+ long install_prefix_count = -1;
+ std::string install_path_to_remove;
+ if (cmValue to_skip = this->Makefile->GetDefinition(
+ "_CMAKE_SYSTEM_PREFIX_PATH_INSTALL_PREFIX_COUNT")) {
+ cmStrToLong(to_skip, &install_prefix_count);
+ }
+ if (cmValue install_value = this->Makefile->GetDefinition(
+ "_CMAKE_SYSTEM_PREFIX_PATH_INSTALL_PREFIX_VALUE")) {
+ install_path_to_remove = *install_value;
+ }
+
+ if (remove_install_prefix && install_prefix_in_list &&
+ install_prefix_count > 0 && !install_path_to_remove.empty()) {
+
+ cmValue prefix_paths =
+ this->Makefile->GetDefinition("CMAKE_SYSTEM_PREFIX_PATH");
+ // remove entry from CMAKE_SYSTEM_PREFIX_PATH
+ std::vector<std::string> expanded = cmExpandedList(*prefix_paths);
+ long count = 0;
+ for (const auto& path : expanded) {
+ bool to_add =
+ !(path == install_path_to_remove && ++count == install_prefix_count);
+ if (to_add) {
+ paths.AddPath(path);
+ }
+ }
+ } else if (add_install_prefix && !install_prefix_in_list) {
+ paths.AddCMakePath("CMAKE_INSTALL_PREFIX");
+ paths.AddCMakePath("CMAKE_SYSTEM_PREFIX_PATH");
+ } else {
+ // Otherwise the current setup of `CMAKE_SYSTEM_PREFIX_PATH` is correct
+ paths.AddCMakePath("CMAKE_SYSTEM_PREFIX_PATH");
+ }
+
paths.AddCMakePath("CMAKE_SYSTEM_FRAMEWORK_PATH");
paths.AddCMakePath("CMAKE_SYSTEM_APPBUNDLE_PATH");