in Source/cmFindPackageCommand.cxx [602:980]
bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
{
if (args.empty()) {
this->SetError("called with incorrect number of arguments");
return false;
}
// Lookup required version of CMake.
if (cmValue const rv =
this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
unsigned int v[3] = { 0, 0, 0 };
std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]);
this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]);
}
// Lookup target architecture, if any.
if (cmValue const arch =
this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
this->LibraryArchitecture = *arch;
}
// Lookup whether lib32 paths should be used.
if (this->Makefile->PlatformIs32Bit() &&
this->Makefile->GetState()->GetGlobalPropertyAsBool(
"FIND_LIBRARY_USE_LIB32_PATHS")) {
this->UseLib32Paths = true;
}
// Lookup whether lib64 paths should be used.
if (this->Makefile->PlatformIs64Bit() &&
this->Makefile->GetState()->GetGlobalPropertyAsBool(
"FIND_LIBRARY_USE_LIB64_PATHS")) {
this->UseLib64Paths = true;
}
// Lookup whether libx32 paths should be used.
if (this->Makefile->PlatformIsx32() &&
this->Makefile->GetState()->GetGlobalPropertyAsBool(
"FIND_LIBRARY_USE_LIBX32_PATHS")) {
this->UseLibx32Paths = true;
}
// Check if User Package Registry should be disabled
// The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has
// priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
if (cmValue const def =
this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) {
this->NoUserRegistry = !def.IsOn();
} else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
this->NoUserRegistry = true;
}
// Check if System Package Registry should be disabled
// The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has
// priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
if (cmValue const def = this->Makefile->GetDefinition(
"CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) {
this->NoSystemRegistry = !def.IsOn();
} else if (this->Makefile->IsOn(
"CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY")) {
this->NoSystemRegistry = true;
}
// Check whether we should resolve symlinks when finding packages
if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS")) {
this->UseRealPath = true;
}
// Check if Sorting should be enabled
if (cmValue const so =
this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) {
if (*so == "NAME") {
this->SortOrder = Name_order;
} else if (*so == "NATURAL") {
this->SortOrder = Natural;
} else {
this->SortOrder = None;
}
}
if (cmValue const sd =
this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) {
this->SortDirection = (*sd == "ASC") ? Asc : Dec;
}
// 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();
// 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();
// Process debug mode
cmMakefile::DebugFindPkgRAII debugFindPkgRAII(this->Makefile, this->Name);
this->DebugMode = this->ComputeIfDebugModeWanted();
// Parse the arguments.
enum Doing
{
DoingNone,
DoingComponents,
DoingOptionalComponents,
DoingNames,
DoingPaths,
DoingPathSuffixes,
DoingConfigs,
DoingHints
};
Doing doing = DoingNone;
cmsys::RegularExpression versionRegex(
R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V");
bool haveVersion = false;
std::vector<std::size_t> configArgs;
std::vector<std::size_t> moduleArgs;
for (std::size_t i = 1u; i < args.size(); ++i) {
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.push_back(i);
doing = DoingNone;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (args[i] == "CONFIG") {
configArgs.push_back(i);
doing = DoingNone;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (args[i] == "NO_MODULE") {
configArgs.push_back(i);
doing = DoingNone;
} else if (args[i] == "REQUIRED") {
this->Required = true;
doing = DoingComponents;
} else if (args[i] == "COMPONENTS") {
doing = DoingComponents;
} else if (args[i] == "OPTIONAL_COMPONENTS") {
doing = DoingOptionalComponents;
} else if (args[i] == "NAMES") {
configArgs.push_back(i);
doing = DoingNames;
} else if (args[i] == "PATHS") {
configArgs.push_back(i);
doing = DoingPaths;
} else if (args[i] == "HINTS") {
configArgs.push_back(i);
doing = DoingHints;
} else if (args[i] == "PATH_SUFFIXES") {
configArgs.push_back(i);
doing = DoingPathSuffixes;
} else if (args[i] == "CONFIGS") {
configArgs.push_back(i);
doing = DoingConfigs;
} else if (args[i] == "NO_POLICY_SCOPE") {
this->PolicyScope = false;
doing = DoingNone;
} else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") {
this->NoUserRegistry = true;
configArgs.push_back(i);
doing = DoingNone;
} else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") {
this->NoSystemRegistry = true;
configArgs.push_back(i);
doing = DoingNone;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (args[i] == "NO_CMAKE_BUILDS_PATH") {
// Ignore legacy option.
configArgs.push_back(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.push_back(i);
doing = DoingNone;
} else if ((doing == DoingComponents) ||
(doing == DoingOptionalComponents)) {
// Set a variable telling the find script whether this component
// is required.
if (doing == DoingOptionalComponents) {
this->OptionalComponents.insert(args[i]);
} else {
this->RequiredComponents.insert(args[i]);
}
// Append to the list of required components.
this->Components += componentsSep;
this->Components += args[i];
componentsSep = ";"_s;
} else if (doing == DoingNames) {
this->Names.push_back(args[i]);
} else if (doing == DoingPaths) {
this->UserGuessArgs.push_back(args[i]);
} else if (doing == DoingHints) {
this->UserHintsArgs.push_back(args[i]);
} else if (doing == DoingPathSuffixes) {
this->AddPathSuffix(args[i]);
} else if (doing == DoingConfigs) {
if (args[i].find_first_of(":/\\") != std::string::npos ||
cmSystemTools::GetFilenameLastExtension(args[i]) != ".cmake") {
this->SetError(cmStrCat(
"given CONFIGS option followed by invalid file name \"", args[i],
"\". The names given must be file names without "
"a path and with a \".cmake\" extension."));
return false;
}
this->Configs.emplace_back(args[i], pdt::CMake);
} else if (!haveVersion && versionRegex.find(args[i])) {
haveVersion = true;
this->VersionComplete = args[i];
} else {
this->SetError(
cmStrCat("called with invalid argument \"", args[i], "\""));
return false;
}
}
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(
this->RequiredComponents.begin(), this->RequiredComponents.end(),
this->OptionalComponents.begin(), this->OptionalComponents.end(),
std::back_inserter(doubledComponents));
if (!doubledComponents.empty()) {
this->SetError(
cmStrCat("called with components that are both required and "
"optional:\n",
cmWrap(" ", doubledComponents, "", "\n"), '\n'));
return false;
}
// Check and eliminate search modes not allowed by the args provided
this->UseFindModules = configArgs.empty();
this->UseConfigFiles = moduleArgs.empty();
if (this->UseConfigFiles &&
cmExperimental::HasSupportEnabled(
*this->Makefile, cmExperimental::Feature::ImportPackageInfo)) {
this->UseCpsFiles = this->Configs.empty();
} else {
this->UseCpsFiles = false;
}
if (!this->UseFindModules && !this->UseConfigFiles) {
std::ostringstream e;
e << "given options exclusive to Module mode:\n";
for (auto si : moduleArgs) {
e << " " << args[si] << "\n";
}
e << "and options exclusive to Config mode:\n";
for (auto si : configArgs) {
e << " " << args[si] << "\n";
}
e << "The options are incompatible.";
this->SetError(e.str());
return false;
}
// Ignore EXACT with no version.
if (this->VersionComplete.empty() && this->VersionExact) {
this->VersionExact = false;
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
"Ignoring EXACT since no version is requested.");
}
if (this->VersionComplete.empty() || this->Components.empty()) {
// Check whether we are recursing inside "Find<name>.cmake" within
// another find_package(<name>) call.
std::string const mod = cmStrCat(this->Name, "_FIND_MODULE");
if (this->Makefile->IsOn(mod)) {
if (this->VersionComplete.empty()) {
// Get version information from the outer call if necessary.
// Requested version string.
std::string const ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE");
this->VersionComplete = this->Makefile->GetSafeDefinition(ver);
// Whether an exact version is required.
std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
this->VersionExact = this->Makefile->IsOn(exact);
}
if (this->Components.empty()) {
std::string const componentsVar = this->Name + "_FIND_COMPONENTS";
this->Components = this->Makefile->GetSafeDefinition(componentsVar);
for (auto const& component : cmList{ this->Components }) {
std::string const crVar =
cmStrCat(this->Name, "_FIND_REQUIRED_"_s, component);
if (this->Makefile->GetDefinition(crVar).IsOn()) {
this->RequiredComponents.insert(component);
} else {
this->OptionalComponents.insert(component);
}
}
}
}
}
// fill various parts of version specification
if (!this->VersionComplete.empty()) {
if (!versionRegex.find(this->VersionComplete)) {
this->SetError("called with invalid version specification.");
return false;
}
this->Version = versionRegex.match(1);
this->VersionMax = versionRegex.match(5);
if (versionRegex.match(4) == "<"_s) {
this->VersionRangeMax = VERSION_ENDPOINT_EXCLUDED;
}
if (!this->VersionMax.empty()) {
this->VersionRange = this->VersionComplete;
}
}
if (!this->VersionRange.empty()) {
// version range must not be empty
if ((this->VersionRangeMax == VERSION_ENDPOINT_INCLUDED &&
cmSystemTools::VersionCompareGreater(this->Version,
this->VersionMax)) ||
(this->VersionRangeMax == VERSION_ENDPOINT_EXCLUDED &&
cmSystemTools::VersionCompareGreaterEq(this->Version,
this->VersionMax))) {
this->SetError("specified version range is empty.");
return false;
}
}
if (this->VersionExact && !this->VersionRange.empty()) {
this->SetError("EXACT cannot be specified with a version range.");
return false;
}
if (!this->Version.empty()) {
this->VersionCount =
parseVersion(this->Version, this->VersionMajor, this->VersionMinor,
this->VersionPatch, this->VersionTweak);
}
if (!this->VersionMax.empty()) {
this->VersionMaxCount = parseVersion(
this->VersionMax, this->VersionMaxMajor, this->VersionMaxMinor,
this->VersionMaxPatch, this->VersionMaxTweak);
}
return this->FindPackage(bypassProvider ? std::vector<std::string>{} : args);
}