cmFindPackageCommand.cxx 69.7 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmFindPackageCommand.h"
4

5 6 7 8 9 10
#include "cmSystemTools.h"
#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cmsys/String.h"
11 12
#include <algorithm>
#include <assert.h>
13
#include <deque>
14 15
#include <functional>
#include <iterator>
16
#include <memory> // IWYU pragma: keep
17 18 19 20
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <utility>
21

22 23
#include "cmAlgorithms.h"
#include "cmMakefile.h"
24
#include "cmMessageType.h"
25
#include "cmPolicies.h"
26 27 28 29
#include "cmSearchPath.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmVersion.h"
30

31
#if defined(__HAIKU__)
32 33
#  include <FindDirectory.h>
#  include <StorageDefs.h>
34 35
#endif

36 37 38
class cmExecutionStatus;
class cmFileList;

39 40 41 42
cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::UserRegistry(
  "PACKAGE_REGISTRY");
cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::Builds(
  "BUILDS");
43 44 45
cmFindPackageCommand::PathLabel
  cmFindPackageCommand::PathLabel::SystemRegistry("SYSTEM_PACKAGE_REGISTRY");

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
struct StrverscmpGreater
{
  bool operator()(const std::string& lhs, const std::string& rhs) const
  {
    return cmSystemTools::strverscmp(lhs, rhs) > 0;
  }
};

struct StrverscmpLesser
{
  bool operator()(const std::string& lhs, const std::string& rhs) const
  {
    return cmSystemTools::strverscmp(lhs, rhs) < 0;
  }
};

void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin,
                                std::vector<std::string>::iterator end,
                                SortOrderType order, SortDirectionType dir)
{
  if (order == Name_order) {
    if (dir == Dec) {
      std::sort(begin, end, std::greater<std::string>());
    } else {
      std::sort(begin, end);
    }
  } else if (order == Natural)
  // natural order uses letters and numbers (contiguous numbers digit are
  // compared such that e.g. 000  00 < 01 < 010 < 09 < 0 < 1 < 9 < 10
  {
    if (dir == Dec) {
      std::sort(begin, end, StrverscmpGreater());
    } else {
      std::sort(begin, end, StrverscmpLesser());
    }
  }
  // else do not sort
}

85 86 87 88 89
cmFindPackageCommand::cmFindPackageCommand()
{
  this->CMakePathName = "PACKAGE";
  this->Quiet = false;
  this->Required = false;
90
  this->NoUserRegistry = false;
91
  this->NoSystemRegistry = false;
92
  this->UseConfigFiles = true;
93
  this->UseFindModules = true;
94
  this->DebugMode = false;
95
  this->UseLib32Paths = false;
96
  this->UseLib64Paths = false;
Steven Newbury's avatar
Steven Newbury committed
97
  this->UseLibx32Paths = false;
98
  this->UseRealPath = false;
99
  this->PolicyScope = true;
100 101 102
  this->VersionMajor = 0;
  this->VersionMinor = 0;
  this->VersionPatch = 0;
103
  this->VersionTweak = 0;
104
  this->VersionCount = 0;
105 106 107 108
  this->VersionExact = false;
  this->VersionFoundMajor = 0;
  this->VersionFoundMinor = 0;
  this->VersionFoundPatch = 0;
109
  this->VersionFoundTweak = 0;
110
  this->VersionFoundCount = 0;
111
  this->RequiredCMakeVersion = 0;
112 113
  this->SortOrder = None;
  this->SortDirection = Asc;
114
  this->AppendSearchPathGroups();
115 116

  this->DeprecatedFindModules["Qt"] = cmPolicies::CMP0084;
117 118 119 120 121 122 123 124
}

void cmFindPackageCommand::AppendSearchPathGroups()
{
  std::vector<cmFindCommon::PathLabel>* labels;

  // Update the All group with new paths
  labels = &this->PathGroupLabelMap[PathGroup::All];
125 126 127 128 129 130
  labels->insert(
    std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem),
    PathLabel::UserRegistry);
  labels->insert(
    std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem),
    PathLabel::Builds);
131 132 133 134
  labels->insert(std::find(labels->begin(), labels->end(), PathLabel::Guess),
                 PathLabel::SystemRegistry);

  // Create the new path objects
135 136 137 138 139 140
  this->LabeledPaths.insert(
    std::make_pair(PathLabel::UserRegistry, cmSearchPath(this)));
  this->LabeledPaths.insert(
    std::make_pair(PathLabel::Builds, cmSearchPath(this)));
  this->LabeledPaths.insert(
    std::make_pair(PathLabel::SystemRegistry, cmSearchPath(this)));
141 142
}

143 144
bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args,
                                       cmExecutionStatus&)
145
{
146
  if (args.empty()) {
147 148
    this->SetError("called with incorrect number of arguments");
    return false;
149
  }
150

151
  // Lookup required version of CMake.
152 153 154
  if (const char* rv =
        this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
    unsigned int v[3] = { 0, 0, 0 };
155
    sscanf(rv, "%u.%u.%u", &v[0], &v[1], &v[2]);
156 157
    this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]);
  }
158

159 160 161
  // Check for debug mode.
  this->DebugMode = this->Makefile->IsOn("CMAKE_FIND_DEBUG_MODE");

162
  // Lookup target architecture, if any.
163 164
  if (const char* arch =
        this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
165
    this->LibraryArchitecture = arch;
166
  }
167

168 169 170 171 172 173 174
  // Lookup whether lib32 paths should be used.
  if (this->Makefile->PlatformIs32Bit() &&
      this->Makefile->GetState()->GetGlobalPropertyAsBool(
        "FIND_LIBRARY_USE_LIB32_PATHS")) {
    this->UseLib32Paths = true;
  }

175
  // Lookup whether lib64 paths should be used.
176 177 178
  if (this->Makefile->PlatformIs64Bit() &&
      this->Makefile->GetState()->GetGlobalPropertyAsBool(
        "FIND_LIBRARY_USE_LIB64_PATHS")) {
179
    this->UseLib64Paths = true;
180
  }
181

Steven Newbury's avatar
Steven Newbury committed
182 183 184 185 186 187 188
  // Lookup whether libx32 paths should be used.
  if (this->Makefile->PlatformIsx32() &&
      this->Makefile->GetState()->GetGlobalPropertyAsBool(
        "FIND_LIBRARY_USE_LIBX32_PATHS")) {
    this->UseLibx32Paths = true;
  }

189
  // Check if User Package Registry should be disabled
190
  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
191
    this->NoUserRegistry = true;
192
  }
193 194

  // Check if System Package Registry should be disabled
195
  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY")) {
196
    this->NoSystemRegistry = true;
197
  }
198

199 200 201 202 203
  // Check whether we should resolve symlinks when finding packages
  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS")) {
    this->UseRealPath = true;
  }

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
  // Check if Sorting should be enabled
  if (const char* so =
        this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) {

    if (strcmp(so, "NAME") == 0) {
      this->SortOrder = Name_order;
    } else if (strcmp(so, "NATURAL") == 0) {
      this->SortOrder = Natural;
    } else {
      this->SortOrder = None;
    }
  }
  if (const char* sd =
        this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) {
    this->SortDirection = strcmp(sd, "ASC") == 0 ? Asc : Dec;
  }

221 222 223 224 225 226
  // Find the current root path mode.
  this->SelectDefaultRootPathMode();

  // Find the current bundle/framework search policy.
  this->SelectDefaultMacMode();

227
  // Record options.
228
  this->Name = args[0];
229 230
  std::string components;
  const char* components_sep = "";
231 232
  std::set<std::string> requiredComponents;
  std::set<std::string> optionalComponents;
233

234
  // Always search directly in a generated path.
235
  this->SearchPathSuffixes.emplace_back();
236

237
  // Parse the arguments.
238 239 240 241 242 243 244 245 246 247 248
  enum Doing
  {
    DoingNone,
    DoingComponents,
    DoingOptionalComponents,
    DoingNames,
    DoingPaths,
    DoingPathSuffixes,
    DoingConfigs,
    DoingHints
  };
249
  Doing doing = DoingNone;
250 251
  cmsys::RegularExpression version("^[0-9.]+$");
  bool haveVersion = false;
252 253
  std::set<unsigned int> configArgs;
  std::set<unsigned int> moduleArgs;
254 255
  for (unsigned int i = 1; i < args.size(); ++i) {
    if (args[i] == "QUIET") {
256 257
      this->Quiet = true;
      doing = DoingNone;
258
    } else if (args[i] == "EXACT") {
259 260
      this->VersionExact = true;
      doing = DoingNone;
261
    } else if (args[i] == "MODULE") {
262
      moduleArgs.insert(i);
263
      doing = DoingNone;
264
    } else if (args[i] == "CONFIG") {
265
      configArgs.insert(i);
266
      doing = DoingNone;
267
    } else if (args[i] == "NO_MODULE") {
268
      configArgs.insert(i);
269
      doing = DoingNone;
270
    } else if (args[i] == "REQUIRED") {
271 272
      this->Required = true;
      doing = DoingComponents;
273
    } else if (args[i] == "COMPONENTS") {
274
      doing = DoingComponents;
275
    } else if (args[i] == "OPTIONAL_COMPONENTS") {
276
      doing = DoingOptionalComponents;
277
    } else if (args[i] == "NAMES") {
278
      configArgs.insert(i);
279
      doing = DoingNames;
280
    } else if (args[i] == "PATHS") {
281
      configArgs.insert(i);
282
      doing = DoingPaths;
283
    } else if (args[i] == "HINTS") {
284
      configArgs.insert(i);
285
      doing = DoingHints;
286
    } else if (args[i] == "PATH_SUFFIXES") {
287
      configArgs.insert(i);
288
      doing = DoingPathSuffixes;
289
    } else if (args[i] == "CONFIGS") {
290
      configArgs.insert(i);
291
      doing = DoingConfigs;
292
    } else if (args[i] == "NO_POLICY_SCOPE") {
293 294
      this->PolicyScope = false;
      doing = DoingNone;
295
    } else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") {
296
      this->NoUserRegistry = true;
297
      configArgs.insert(i);
298
      doing = DoingNone;
299
    } else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") {
300
      this->NoSystemRegistry = true;
301
      configArgs.insert(i);
302
      doing = DoingNone;
303
    } else if (args[i] == "NO_CMAKE_BUILDS_PATH") {
304
      // Ignore legacy option.
305
      configArgs.insert(i);
306
      doing = DoingNone;
307
    } else if (this->CheckCommonArgument(args[i])) {
308
      configArgs.insert(i);
309
      doing = DoingNone;
310 311
    } else if ((doing == DoingComponents) ||
               (doing == DoingOptionalComponents)) {
312
      // Set a variable telling the find script whether this component
313
      // is required.
314
      const char* isRequired = "1";
315
      if (doing == DoingOptionalComponents) {
316 317
        isRequired = "0";
        optionalComponents.insert(args[i]);
318
      } else {
319
        requiredComponents.insert(args[i]);
320
      }
321

322
      std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
323
      this->AddFindDefinition(req_var, isRequired);
324

325 326 327 328
      // Append to the list of required components.
      components += components_sep;
      components += args[i];
      components_sep = ";";
329
    } else if (doing == DoingNames) {
330
      this->Names.push_back(args[i]);
331
    } else if (doing == DoingPaths) {
332
      this->UserGuessArgs.push_back(args[i]);
333
    } else if (doing == DoingHints) {
334
      this->UserHintsArgs.push_back(args[i]);
335
    } else if (doing == DoingPathSuffixes) {
336
      this->AddPathSuffix(args[i]);
337
    } else if (doing == DoingConfigs) {
338
      if (args[i].find_first_of(":/\\") != std::string::npos ||
339
          cmSystemTools::GetFilenameLastExtension(args[i]) != ".cmake") {
340
        std::ostringstream e;
341 342
        e << "given CONFIGS option followed by invalid file name \"" << args[i]
          << "\".  The names given must be file names without "
343
          << "a path and with a \".cmake\" extension.";
344
        this->SetError(e.str());
345
        return false;
346
      }
347
      this->Configs.push_back(args[i]);
348
    } else if (!haveVersion && version.find(args[i])) {
349
      haveVersion = true;
350
      this->Version = args[i];
351
    } else {
352
      std::ostringstream e;
353
      e << "called with invalid argument \"" << args[i] << "\"";
354
      this->SetError(e.str());
355
      return false;
356
    }
357
  }
358

359 360 361 362
  std::vector<std::string> doubledComponents;
  std::set_intersection(requiredComponents.begin(), requiredComponents.end(),
                        optionalComponents.begin(), optionalComponents.end(),
                        std::back_inserter(doubledComponents));
363
  if (!doubledComponents.empty()) {
364
    std::ostringstream e;
365
    e << "called with components that are both required and optional:\n";
366
    e << cmWrap("  ", doubledComponents, "", "\n") << "\n";
367
    this->SetError(e.str());
368
    return false;
369
  }
370

371 372 373
  // Maybe choose one mode exclusively.
  this->UseFindModules = configArgs.empty();
  this->UseConfigFiles = moduleArgs.empty();
374
  if (!this->UseFindModules && !this->UseConfigFiles) {
375
    std::ostringstream e;
376
    e << "given options exclusive to Module mode:\n";
377 378
    for (unsigned int si : moduleArgs) {
      e << "  " << args[si] << "\n";
379
    }
380
    e << "and options exclusive to Config mode:\n";
381 382
    for (unsigned int si : configArgs) {
      e << "  " << args[si] << "\n";
383
    }
384
    e << "The options are incompatible.";
385
    this->SetError(e.str());
386
    return false;
387
  }
388

389
  // Ignore EXACT with no version.
390
  if (this->Version.empty() && this->VersionExact) {
391 392
    this->VersionExact = false;
    this->Makefile->IssueMessage(
393 394
      MessageType::AUTHOR_WARNING,
      "Ignoring EXACT since no version is requested.");
395
  }
396

397
  if (this->Version.empty() || components.empty()) {
398 399 400 401
    // Check whether we are recursing inside "Find<name>.cmake" within
    // another find_package(<name>) call.
    std::string mod = this->Name;
    mod += "_FIND_MODULE";
402 403
    if (this->Makefile->IsOn(mod)) {
      if (this->Version.empty()) {
404 405 406 407
        // Get version information from the outer call if necessary.
        // Requested version string.
        std::string ver = this->Name;
        ver += "_FIND_VERSION";
408
        this->Version = this->Makefile->GetSafeDefinition(ver);
409

410 411 412
        // Whether an exact version is required.
        std::string exact = this->Name;
        exact += "_FIND_VERSION_EXACT";
413
        this->VersionExact = this->Makefile->IsOn(exact);
414 415
      }
      if (components.empty()) {
416
        std::string components_var = this->Name + "_FIND_COMPONENTS";
417
        components = this->Makefile->GetSafeDefinition(components_var);
418 419
      }
    }
420
  }
421

422
  if (!this->Version.empty()) {
423 424 425 426 427
    // Try to parse the version number and store the results that were
    // successfully parsed.
    unsigned int parsed_major;
    unsigned int parsed_minor;
    unsigned int parsed_patch;
428
    unsigned int parsed_tweak;
429 430 431 432 433
    this->VersionCount =
      sscanf(this->Version.c_str(), "%u.%u.%u.%u", &parsed_major,
             &parsed_minor, &parsed_patch, &parsed_tweak);
    switch (this->VersionCount) {
      case 4:
434 435
        this->VersionTweak = parsed_tweak;
        CM_FALLTHROUGH;
436
      case 3:
437 438
        this->VersionPatch = parsed_patch;
        CM_FALLTHROUGH;
439
      case 2:
440 441
        this->VersionMinor = parsed_minor;
        CM_FALLTHROUGH;
442
      case 1:
443 444
        this->VersionMajor = parsed_major;
        CM_FALLTHROUGH;
445 446
      default:
        break;
447
    }
448
  }
449

450 451
  std::string disableFindPackageVar = "CMAKE_DISABLE_FIND_PACKAGE_";
  disableFindPackageVar += this->Name;
452 453
  if (this->Makefile->IsOn(disableFindPackageVar)) {
    if (this->Required) {
454
      std::ostringstream e;
455 456 457
      e << "for module " << this->Name << " called with REQUIRED, but "
        << disableFindPackageVar
        << " is enabled. A REQUIRED package cannot be disabled.";
458
      this->SetError(e.str());
459 460 461
      return false;
    }

462 463
    return true;
  }
464

465 466 467 468
  {
    // Allocate a PACKAGE_ROOT_PATH for the current find_package call.
    this->Makefile->FindPackageRootPathStack.emplace_back();
    std::vector<std::string>& rootPaths =
469
      this->Makefile->FindPackageRootPathStack.back();
470 471 472 473 474 475 476 477 478 479 480 481 482

    // Add root paths from <PackageName>_ROOT CMake and environment variables,
    // subject to CMP0074.
    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0074)) {
      case cmPolicies::WARN:
        this->Makefile->MaybeWarnCMP0074(this->Name);
        CM_FALLTHROUGH;
      case cmPolicies::OLD:
        // OLD behavior is to ignore the <pkg>_ROOT variables.
        break;
      case cmPolicies::REQUIRED_IF_USED:
      case cmPolicies::REQUIRED_ALWAYS:
        this->Makefile->IssueMessage(
483
          MessageType::FATAL_ERROR,
484 485 486 487 488 489 490 491 492 493 494 495 496
          cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0074));
        break;
      case cmPolicies::NEW: {
        // NEW behavior is to honor the <pkg>_ROOT variables.
        std::string const rootVar = this->Name + "_ROOT";
        if (const char* pkgRoot = this->Makefile->GetDefinition(rootVar)) {
          cmSystemTools::ExpandListArgument(pkgRoot, rootPaths, false);
        }
        cmSystemTools::GetPath(rootPaths, rootVar.c_str());
      } break;
    }
  }

497
  this->SetModuleVariables(components);
498

499
  // See if there is a Find<PackageName>.cmake module.
500
  if (this->UseFindModules) {
501
    bool foundModule = false;
502
    if (!this->FindModule(foundModule)) {
503
      this->AppendSuccessInformation();
504
      return false;
505 506
    }
    if (foundModule) {
507
      this->AppendSuccessInformation();
508
      return true;
509
    }
510
  }
511

512 513
  if (this->UseFindModules && this->UseConfigFiles &&
      this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
514
    std::ostringstream aw;
515
    if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
516
      aw << "find_package called without either MODULE or CONFIG option and "
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
            "no Find"
         << this->Name
         << ".cmake module is in CMAKE_MODULE_PATH.  "
            "Add MODULE to exclusively request Module mode and fail if "
            "Find"
         << this->Name
         << ".cmake is missing.  "
            "Add CONFIG to exclusively request Config mode and search for a "
            "package configuration file provided by "
         << this->Name << " (" << this->Name << "Config.cmake or "
         << cmSystemTools::LowerCase(this->Name) << "-config.cmake).  ";
    } else {
      aw
        << "find_package called without NO_MODULE option and no "
           "Find"
        << this->Name
        << ".cmake module is in CMAKE_MODULE_PATH.  "
           "Add NO_MODULE to exclusively request Config mode and search for a "
           "package configuration file provided by "
        << this->Name << " (" << this->Name << "Config.cmake or "
537 538 539 540 541 542
        << cmSystemTools::LowerCase(this->Name)
        << "-config.cmake).  "
           "Otherwise make Find"
        << this->Name
        << ".cmake available in "
           "CMAKE_MODULE_PATH.";
543
    }
544
    aw << "\n"
545
          "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this warning.)";
546
    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str());
547
  }
548

549
  // No find module.  Assume the project has a CMake config file.  Use
550
  // a <PackageName>_DIR cache variable to locate it.
551
  this->Variable = this->Name;
552
  this->Variable += "_DIR";
553

554
  // Add the default name.
555
  if (this->Names.empty()) {
556
    this->Names.push_back(this->Name);
557
  }
558 559

  // Add the default configs.
560
  if (this->Configs.empty()) {
561 562
    for (std::string const& n : this->Names) {
      std::string config = n;
563 564 565
      config += "Config.cmake";
      this->Configs.push_back(config);

566
      config = cmSystemTools::LowerCase(n);
567
      config += "-config.cmake";
568
      this->Configs.push_back(std::move(config));
569
    }
570
  }
571

572 573 574 575 576 577 578 579 580
  // get igonored paths from vars and reroot them.
  std::vector<std::string> ignored;
  this->GetIgnoredPaths(ignored);
  this->RerootPaths(ignored);

  // Construct a set of ignored paths
  this->IgnoredPaths.clear();
  this->IgnoredPaths.insert(ignored.begin(), ignored.end());

581 582 583 584 585 586
  // Find and load the package.
  bool result = this->HandlePackageMode();
  this->AppendSuccessInformation();
  return result;
}

587
void cmFindPackageCommand::SetModuleVariables(const std::string& components)
588
{
589 590
  this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name.c_str());

591 592
  // Store the list of components.
  std::string components_var = this->Name + "_FIND_COMPONENTS";
593
  this->AddFindDefinition(components_var, components.c_str());
594

595
  if (this->Quiet) {
596 597 598 599
    // Tell the module that is about to be read that it should find
    // quietly.
    std::string quietly = this->Name;
    quietly += "_FIND_QUIETLY";
600
    this->AddFindDefinition(quietly, "1");
601
  }
602

603
  if (this->Required) {
604 605 606 607
    // Tell the module that is about to be read that it should report
    // a fatal error if the package is not found.
    std::string req = this->Name;
    req += "_FIND_REQUIRED";
608
    this->AddFindDefinition(req, "1");
609
  }
610

611
  if (!this->Version.empty()) {
612 613 614 615
    // Tell the module that is about to be read what version of the
    // package has been requested.
    std::string ver = this->Name;
    ver += "_FIND_VERSION";
616
    this->AddFindDefinition(ver, this->Version.c_str());
617
    char buf[64];
618
    sprintf(buf, "%u", this->VersionMajor);
619
    this->AddFindDefinition(ver + "_MAJOR", buf);
620
    sprintf(buf, "%u", this->VersionMinor);
621
    this->AddFindDefinition(ver + "_MINOR", buf);
622
    sprintf(buf, "%u", this->VersionPatch);
623
    this->AddFindDefinition(ver + "_PATCH", buf);
624
    sprintf(buf, "%u", this->VersionTweak);
625
    this->AddFindDefinition(ver + "_TWEAK", buf);
626
    sprintf(buf, "%u", this->VersionCount);
627
    this->AddFindDefinition(ver + "_COUNT", buf);
628

629 630 631
    // Tell the module whether an exact version has been requested.
    std::string exact = this->Name;
    exact += "_FIND_VERSION_EXACT";
632 633
    this->AddFindDefinition(exact, this->VersionExact ? "1" : "0");
  }
634 635
}

636 637
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
                                             const char* val)
638
{
639
  if (const char* old = this->Makefile->GetDefinition(var)) {
640 641
    this->OriginalDefs[var].exists = true;
    this->OriginalDefs[var].value = old;
642
  } else {
643
    this->OriginalDefs[var].exists = false;
644
  }
645 646 647 648 649
  this->Makefile->AddDefinition(var, val);
}

void cmFindPackageCommand::RestoreFindDefinitions()
{
650 651
  for (auto const& i : this->OriginalDefs) {
    OriginalDef const& od = i.second;
652
    if (od.exists) {
653
      this->Makefile->AddDefinition(i.first, od.value.c_str());
654
    } else {
655
      this->Makefile->RemoveDefinition(i.first);
656
    }
657
  }
658
}
659 660 661 662 663 664

bool cmFindPackageCommand::FindModule(bool& found)
{
  std::string module = "Find";
  module += this->Name;
  module += ".cmake";
665
  bool system = false;
666
  std::string mfile = this->Makefile->GetModulesFile(module, system);
667
  if (!mfile.empty()) {
668 669 670 671 672 673 674 675 676
    if (system) {
      auto it = this->DeprecatedFindModules.find(this->Name);
      if (it != this->DeprecatedFindModules.end()) {
        cmPolicies::PolicyStatus status =
          this->Makefile->GetPolicyStatus(it->second);
        switch (status) {
          case cmPolicies::WARN: {
            std::ostringstream e;
            e << cmPolicies::GetPolicyWarning(it->second) << "\n";
677
            this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
678 679 680 681 682 683 684 685 686 687 688 689
            CM_FALLTHROUGH;
          }
          case cmPolicies::OLD:
            break;
          case cmPolicies::REQUIRED_IF_USED:
          case cmPolicies::REQUIRED_ALWAYS:
          case cmPolicies::NEW:
            return true;
        }
      }
    }

690 691
    // Load the module we found, and set "<name>_FIND_MODULE" to true
    // while inside it.
692
    found = true;
693 694
    std::string var = this->Name;
    var += "_FIND_MODULE";
695
    this->Makefile->AddDefinition(var, "1");
696
    bool result = this->ReadListFile(mfile, DoPolicyScope);
697
    this->Makefile->RemoveDefinition(var);
698
    return result;
699
  }
700 701 702 703 704
  return true;
}

bool cmFindPackageCommand::HandlePackageMode()
{
705
  this->ConsideredConfigs.clear();
706

707 708 709 710 711
  // Support old capitalization behavior.
  std::string upperDir = cmSystemTools::UpperCase(this->Name);
  std::string upperFound = cmSystemTools::UpperCase(this->Name);
  upperDir += "_DIR";
  upperFound += "_FOUND";
712

713
  // Try to find the config file.
714
  const char* def = this->Makefile->GetDefinition(this->Variable);
715

716
  // Try to load the config file if the directory is known
717
  bool fileFound = false;
718 719
  if (this->UseConfigFiles) {
    if (!cmSystemTools::IsOff(def)) {
720 721 722 723 724
      // Get the directory from the variable value.
      std::string dir = def;
      cmSystemTools::ConvertToUnixSlashes(dir);

      // Treat relative paths with respect to the current source dir.
725
      if (!cmSystemTools::FileIsFullPath(dir)) {
726
        dir = "/" + dir;
727
        dir = this->Makefile->GetCurrentSourceDirectory() + dir;
728
      }
729 730
      // The file location was cached.  Look for the correct file.
      std::string file;
731
      if (this->FindConfigFile(dir, file)) {
732 733
        this->FileFound = file;
        fileFound = true;
734
      }
735 736
      def = this->Makefile->GetDefinition(this->Variable);
    }
737 738

    // Search for the config file if it is not already found.
739
    if (cmSystemTools::IsOff(def) || !fileFound) {
740
      fileFound = this->FindConfig();
741
    }
742

743
    // Sanity check.
744
    if (fileFound && this->FileFound.empty()) {
745
      this->Makefile->IssueMessage(
746 747
        MessageType::INTERNAL_ERROR,
        "fileFound is true but FileFound is empty!");
748
      fileFound = false;
749
    }
750
  }
751

752 753
  std::string foundVar = this->Name;
  foundVar += "_FOUND";
754 755 756
  std::string notFoundMessageVar = this->Name;
  notFoundMessageVar += "_NOT_FOUND_MESSAGE";
  std::string notFoundMessage;
757

758 759 760
  // If the directory for the config file was found, try to read the file.
  bool result = true;
  bool found = false;
761 762
  bool configFileSetFOUNDFalse = false;

763
  if (fileFound) {
764 765
    if (this->Makefile->IsDefinitionSet(foundVar) &&
        !this->Makefile->IsOn(foundVar)) {
766 767 768 769
      // by removing Foo_FOUND here if it is FALSE, we don't really change
      // the situation for the Config file which is about to be included,
      // but we make it possible to detect later on whether the Config file
      // has set Foo_FOUND to FALSE itself:
770
      this->Makefile->RemoveDefinition(foundVar);
771
    }
772
    this->Makefile->RemoveDefinition(notFoundMessageVar);
773

774 775 776 777 778
    // Set the version variables before loading the config file.
    // It may override them.
    this->StoreVersionFound();

    // Parse the configuration file.
779
    if (this->ReadListFile(this->FileFound, DoPolicyScope)) {
780 781
      // The package has been found.
      found = true;
782 783

      // Check whether the Config file has set Foo_FOUND to FALSE:
784 785
      if (this->Makefile->IsDefinitionSet(foundVar) &&
          !this->Makefile->IsOn(foundVar)) {
786 787 788
        // we get here if the Config file has set Foo_FOUND actively to FALSE
        found = false;
        configFileSetFOUNDFalse = true;
789 790
        notFoundMessage =
          this->Makefile->GetSafeDefinition(notFoundMessageVar);
791
      }
792
    } else {
793
      // The configuration file is invalid.
794
      result = false;
795
    }
796
  }
797

798 799
  // package not found
  if (result && !found) {
800 801
    // warn if package required or neither quiet nor in config mode
    if (this->Required ||
802 803 804
        !(this->Quiet ||
          (this->UseConfigFiles && !this->UseFindModules &&
           this->ConsideredConfigs.empty()))) {
805 806 807 808 809 810 811 812 813 814 815 816 817
      // The variable is not set.
      std::ostringstream e;
      std::ostringstream aw;
      if (configFileSetFOUNDFalse) {
        /* clang-format off */
        e << "Found package configuration file:\n"
          "  " << this->FileFound << "\n"
          "but it set " << foundVar << " to FALSE so package \"" <<
          this->Name << "\" is considered to be NOT FOUND.";
        /* clang-format on */
        if (!notFoundMessage.empty()) {
          e << " Reason given by package: \n" << notFoundMessage << "\n";
        }
818
      }
819 820 821 822 823 824 825 826 827 828 829
      // If there are files in ConsideredConfigs, it means that FooConfig.cmake
      // have been found, but they didn't have appropriate versions.
      else if (!this->ConsideredConfigs.empty()) {
        std::vector<ConfigFileInfo>::const_iterator duplicate_end =
          cmRemoveDuplicates(this->ConsideredConfigs);
        e << "Could not find a configuration file for package \"" << this->Name
          << "\" that "
          << (this->VersionExact ? "exactly matches" : "is compatible with")
          << " requested version \"" << this->Version << "\".\n"
          << "The following configuration files were considered but not "
             "accepted:\n";
830 831 832 833

        for (ConfigFileInfo const& info :
             cmMakeRange(this->ConsideredConfigs.cbegin(), duplicate_end)) {
          e << "  " << info.filename << ", version: " << info.version << "\n";
834
        }
835 836 837 838 839 840
      } else {
        std::string requestedVersionString;
        if (!this->Version.empty()) {
          requestedVersionString = " (requested version ";
          requestedVersionString += this->Version;
          requestedVersionString += ")";
841
        }
842

843 844 845 846 847 848
        if (this->UseConfigFiles) {
          if (this->UseFindModules) {
            e << "By not providing \"Find" << this->Name
              << ".cmake\" in "
                 "CMAKE_MODULE_PATH this project has asked CMake to find a "
                 "package configuration file provided by \""
849 850 851
              << this->Name
              << "\", "
                 "but CMake did not find one.\n";
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
          }

          if (this->Configs.size() == 1) {
            e << "Could not find a package configuration file named \""
              << this->Configs[0] << "\" provided by package \"" << this->Name
              << "\"" << requestedVersionString << ".\n";
          } else {
            e << "Could not find a package configuration file provided by \""
              << this->Name << "\"" << requestedVersionString
              << " with any of the following names:\n"
              << cmWrap("  ", this->Configs, "", "\n") << "\n";
          }

          e << "Add the installation prefix of \"" << this->Name
            << "\" to "
               "CMAKE_PREFIX_PATH or set \""
            << this->Variable
            << "\" to a "
               "directory containing one of the above files. "
               "If \""
872 873 874
            << this->Name
            << "\" provides a separate development "
               "package or SDK, be sure it has been installed.";
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
        } else // if(!this->UseFindModules && !this->UseConfigFiles)
        {
          e << "No \"Find" << this->Name << ".cmake\" found in "
            << "CMAKE_MODULE_PATH.";

          aw
            << "Find" << this->Name
            << ".cmake must either be part of this "
               "project itself, in this case adjust CMAKE_MODULE_PATH so that "
               "it points to the correct location inside its source tree.\n"
               "Or it must be installed by a package which has already been "
               "found via find_package().  In this case make sure that "
               "package has indeed been found and adjust CMAKE_MODULE_PATH to "
               "contain the location where that package has installed "
               "Find"
            << this->Name
            << ".cmake.  This must be a location "
               "provided by that package.  This error in general means that "
               "the buildsystem of this project is relying on a Find-module "
               "without ensuring that it is actually available.\n";
895
        }
896
      }
897

898 899 900
      this->Makefile->IssueMessage(this->Required ? MessageType::FATAL_ERROR
                                                  : MessageType::WARNING,
                                   e.str());
901 902
      if (this->Required) {
        cmSystemTools::SetFatalErrorOccured();
903
      }
904

905
      if (!aw.str().empty()) {
906
        this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str());
907
      }
908
    }
909 910 911 912 913
    // output result if in config mode but not in quiet mode
    else if (!this->Quiet) {
      std::ostringstream aw;
      aw << "Could NOT find " << this->Name << " (missing: " << this->Name
         << "_DIR)";
914
      this->Makefile->DisplayStatus(aw.str(), -1);
915
    }
916
  }
917

918
  // Set a variable marking whether the package was found.
919
  this->Makefile->AddDefinition(foundVar, found ? "1" : "0");
920

921 922 923
  // Set a variable naming the configuration file that was found.
  std::string fileVar = this->Name;
  fileVar += "_CONFIG";
924
  if (found) {
925
    this->Makefile->AddDefinition(fileVar, this->FileFound.c_str());
926
  } else {
927
    this->Makefile->RemoveDefinition(fileVar);
928
  }
929

930 931 932 933 934 935 936
  std::string consideredConfigsVar = this->Name;
  consideredConfigsVar += "_CONSIDERED_CONFIGS";
  std::string consideredVersionsVar = this->Name;
  consideredVersionsVar += "_CONSIDERED_VERSIONS";

  std::string consideredConfigFiles;
  std::string consideredVersions;
937

938
  const char* sep = "";
939
  for (ConfigFileInfo const& i : this->ConsideredConfigs) {
940 941
    consideredConfigFiles += sep;
    consideredVersions += sep;
942 943
    consideredConfigFiles += i.filename;
    consideredVersions += i.version;
944
    sep = ";";
945
  }
946

947
  this->Makefile->AddDefinition(consideredConfigsVar,
948 949
                                consideredConfigFiles.c_str());

950
  this->Makefile->AddDefinition(consideredVersionsVar,
951 952
                                consideredVersions.c_str());

953 954 955
  return result;
}

956
bool cmFindPackageCommand::FindConfig()
957
{
958 959
  // Compute the set of search prefixes.
  this->ComputePrefixes();
960

961 962
  // Look for the project's configuration file.
  bool found = false;
963

964
  // Search for frameworks.
965
  if (!found && (this->SearchFrameworkFirst || this->SearchFrameworkOnly)) {
966
    found = this->FindFrameworkConfig();
967
  }
968

969
  // Search for apps.
970
  if (!found && (this->SearchAppBundleFirst || this->SearchAppBundleOnly)) {
971
    found = this->FindAppBundleConfig();
972
  }
973

974
  // Search prefixes.
975
  if (!found && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) {
976
    found = this->FindPrefixedConfig();
977
  }
978

979
  // Search for frameworks.
980
  if (!found && this->SearchFrameworkLast) {
981
    found = this->FindFrameworkConfig();
982
  }
983

984
  // Search for apps.
985
  if (!found && this->SearchAppBundleLast) {
986
    found = this->FindAppBundleConfig();
987
  }
988

989
  // Store the entry in the cache so it can be set by the user.
990
  std::string init;
991
  if (found) {
992
    init = cmSystemTools::GetFilenamePath(this->FileFound);
993
  } else {
994
    init = this->Variable + "-NOTFOUND";
995
  }
996 997 998 999
  std::string help =
    "The directory containing a CMake configuration file for ";
  help += this->Name;
  help += ".";
1000
  // We force the value since we do not get here if it was already set.
1001
  this->Makefile->AddCacheDefinition(this->Variable, init.c_str(),
1002
                                     help.c_str(), cmStateEnums::PATH, true);
1003
  return found;
1004 1005
}

1006
bool cmFindPackageCommand::FindPrefixedConfig()
1007
{
1008
  std::vector<std::string> const& prefixes = this->SearchPaths;
1009 1010
  for (std::string const& p : prefixes) {
    if (this->SearchPrefix(p)) {
1011
      return true;
1012
    }
1013
  }
1014 1015
  return false;
}
1016

1017 1018
bool cmFindPackageCommand::FindFrameworkConfig()
{
1019
  std::vector<std::string> const& prefixes = this->SearchPaths;
1020 1021
  for (std::string const& p : prefixes) {
    if (this->SearchFrameworkPrefix(p)) {
1022
      return true;
1023
    }
1024
  }
1025 1026
  return false;
}
1027

1028 1029
bool cmFindPackageCommand::FindAppBundleConfig()
{
1030
  std::vector<std::string> const& prefixes = this->SearchPaths;
1031 1032
  for (std::string const& p : prefixes) {
    if (this->SearchAppBundlePrefix(p)) {
1033
      return true;
1034
    }
1035
  }
1036
  return false;
1037 1038
}

1039 1040
bool cmFindPackageCommand::ReadListFile(const std::string& f,
                                        PolicyScopeRule psr)
1041
{
1042
  const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope;
1043
  if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
1044
    return true;
1045
  }
1046 1047 1048
  std::string e = "Error reading CMake code from \"";
  e += f;
  e += "\".";
1049
  this->SetError(e);