cmFindPackageCommand.cxx 68.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 "cmPolicies.h"
25 26 27 28 29
#include "cmSearchPath.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmVersion.h"
#include "cmake.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->PolicyScope = true;
99 100 101
  this->VersionMajor = 0;
  this->VersionMinor = 0;
  this->VersionPatch = 0;
102
  this->VersionTweak = 0;
103
  this->VersionCount = 0;
104 105 106 107
  this->VersionExact = false;
  this->VersionFoundMajor = 0;
  this->VersionFoundMinor = 0;
  this->VersionFoundPatch = 0;
108
  this->VersionFoundTweak = 0;
109
  this->VersionFoundCount = 0;
110
  this->RequiredCMakeVersion = 0;
111 112
  this->SortOrder = None;
  this->SortDirection = Asc;
113 114 115 116 117 118 119 120 121
  this->AppendSearchPathGroups();
}

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

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

  // Create the new path objects
132 133 134 135 136 137
  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)));
138 139
}

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

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

156 157 158
  // Check for debug mode.
  this->DebugMode = this->Makefile->IsOn("CMAKE_FIND_DEBUG_MODE");

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

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

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

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

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

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

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
  // 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;
  }

213 214 215 216 217 218
  // Find the current root path mode.
  this->SelectDefaultRootPathMode();

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

219
  // Record options.
220
  this->Name = args[0];
221 222
  std::string components;
  const char* components_sep = "";
223 224
  std::set<std::string> requiredComponents;
  std::set<std::string> optionalComponents;
225

226
  // Always search directly in a generated path.
227
  this->SearchPathSuffixes.emplace_back();
228

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

314
      std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
315
      this->AddFindDefinition(req_var, isRequired);
316

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

351 352 353 354
  std::vector<std::string> doubledComponents;
  std::set_intersection(requiredComponents.begin(), requiredComponents.end(),
                        optionalComponents.begin(), optionalComponents.end(),
                        std::back_inserter(doubledComponents));
355
  if (!doubledComponents.empty()) {
356
    std::ostringstream e;
357
    e << "called with components that are both required and optional:\n";
358
    e << cmWrap("  ", doubledComponents, "", "\n") << "\n";
359
    this->SetError(e.str());
360
    return false;
361
  }
362

363 364 365
  // Maybe choose one mode exclusively.
  this->UseFindModules = configArgs.empty();
  this->UseConfigFiles = moduleArgs.empty();
366
  if (!this->UseFindModules && !this->UseConfigFiles) {
367
    std::ostringstream e;
368
    e << "given options exclusive to Module mode:\n";
369 370
    for (unsigned int si : moduleArgs) {
      e << "  " << args[si] << "\n";
371
    }
372
    e << "and options exclusive to Config mode:\n";
373 374
    for (unsigned int si : configArgs) {
      e << "  " << args[si] << "\n";
375
    }
376
    e << "The options are incompatible.";
377
    this->SetError(e.str());
378
    return false;
379
  }
380

381
  // Ignore EXACT with no version.
382
  if (this->Version.empty() && this->VersionExact) {
383 384 385
    this->VersionExact = false;
    this->Makefile->IssueMessage(
      cmake::AUTHOR_WARNING, "Ignoring EXACT since no version is requested.");
386
  }
387

388
  if (this->Version.empty() || components.empty()) {
389 390 391 392
    // Check whether we are recursing inside "Find<name>.cmake" within
    // another find_package(<name>) call.
    std::string mod = this->Name;
    mod += "_FIND_MODULE";
393 394
    if (this->Makefile->IsOn(mod)) {
      if (this->Version.empty()) {
395 396 397 398
        // Get version information from the outer call if necessary.
        // Requested version string.
        std::string ver = this->Name;
        ver += "_FIND_VERSION";
399
        this->Version = this->Makefile->GetSafeDefinition(ver);
400

401 402 403
        // Whether an exact version is required.
        std::string exact = this->Name;
        exact += "_FIND_VERSION_EXACT";
404
        this->VersionExact = this->Makefile->IsOn(exact);
405 406
      }
      if (components.empty()) {
407
        std::string components_var = this->Name + "_FIND_COMPONENTS";
408
        components = this->Makefile->GetSafeDefinition(components_var);
409 410
      }
    }
411
  }
412

413
  if (!this->Version.empty()) {
414 415 416 417 418
    // 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;
419
    unsigned int parsed_tweak;
420 421 422 423 424
    this->VersionCount =
      sscanf(this->Version.c_str(), "%u.%u.%u.%u", &parsed_major,
             &parsed_minor, &parsed_patch, &parsed_tweak);
    switch (this->VersionCount) {
      case 4:
425 426
        this->VersionTweak = parsed_tweak;
        CM_FALLTHROUGH;
427
      case 3:
428 429
        this->VersionPatch = parsed_patch;
        CM_FALLTHROUGH;
430
      case 2:
431 432
        this->VersionMinor = parsed_minor;
        CM_FALLTHROUGH;
433
      case 1:
434 435
        this->VersionMajor = parsed_major;
        CM_FALLTHROUGH;
436 437
      default:
        break;
438
    }
439
  }
440

441 442
  std::string disableFindPackageVar = "CMAKE_DISABLE_FIND_PACKAGE_";
  disableFindPackageVar += this->Name;
443 444
  if (this->Makefile->IsOn(disableFindPackageVar)) {
    if (this->Required) {
445
      std::ostringstream e;
446 447 448
      e << "for module " << this->Name << " called with REQUIRED, but "
        << disableFindPackageVar
        << " is enabled. A REQUIRED package cannot be disabled.";
449
      this->SetError(e.str());
450 451 452
      return false;
    }

453 454
    return true;
  }
455

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
  {
    // Allocate a PACKAGE_ROOT_PATH for the current find_package call.
    this->Makefile->FindPackageRootPathStack.emplace_back();
    std::vector<std::string>& rootPaths =
      *this->Makefile->FindPackageRootPathStack.rbegin();

    // 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(
          cmake::FATAL_ERROR,
          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;
    }
  }

488
  this->SetModuleVariables(components);
489

490
  // See if there is a Find<package>.cmake module.
491
  if (this->UseFindModules) {
492
    bool foundModule = false;
493
    if (!this->FindModule(foundModule)) {
494
      this->AppendSuccessInformation();
495
      return false;
496 497
    }
    if (foundModule) {
498
      this->AppendSuccessInformation();
499
      return true;
500
    }
501
  }
502

503 504
  if (this->UseFindModules && this->UseConfigFiles &&
      this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
505
    std::ostringstream aw;
506
    if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
507
      aw << "find_package called without either MODULE or CONFIG option and "
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
            "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 "
528 529 530 531 532 533
        << cmSystemTools::LowerCase(this->Name)
        << "-config.cmake).  "
           "Otherwise make Find"
        << this->Name
        << ".cmake available in "
           "CMAKE_MODULE_PATH.";
534
    }
535
    aw << "\n"
536
          "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this warning.)";
537
    this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, aw.str());
538
  }
539

540
  // No find module.  Assume the project has a CMake config file.  Use
541
  // a <package>_DIR cache variable to locate it.
542
  this->Variable = this->Name;
543
  this->Variable += "_DIR";
544

545
  // Add the default name.
546
  if (this->Names.empty()) {
547
    this->Names.push_back(this->Name);
548
  }
549 550

  // Add the default configs.
551
  if (this->Configs.empty()) {
552 553
    for (std::string const& n : this->Names) {
      std::string config = n;
554 555 556
      config += "Config.cmake";
      this->Configs.push_back(config);

557
      config = cmSystemTools::LowerCase(n);
558
      config += "-config.cmake";
559
      this->Configs.push_back(std::move(config));
560
    }
561
  }
562

563 564 565 566 567 568 569 570 571
  // 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());

572 573 574 575 576 577
  // Find and load the package.
  bool result = this->HandlePackageMode();
  this->AppendSuccessInformation();
  return result;
}

578
void cmFindPackageCommand::SetModuleVariables(const std::string& components)
579
{
580 581
  this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name.c_str());

582 583
  // Store the list of components.
  std::string components_var = this->Name + "_FIND_COMPONENTS";
584
  this->AddFindDefinition(components_var, components.c_str());
585

586
  if (this->Quiet) {
587 588 589 590
    // Tell the module that is about to be read that it should find
    // quietly.
    std::string quietly = this->Name;
    quietly += "_FIND_QUIETLY";
591
    this->AddFindDefinition(quietly, "1");
592
  }
593

594
  if (this->Required) {
595 596 597 598
    // 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";
599
    this->AddFindDefinition(req, "1");
600
  }
601

602
  if (!this->Version.empty()) {
603 604 605 606
    // 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";
607
    this->AddFindDefinition(ver, this->Version.c_str());
608
    char buf[64];
609
    sprintf(buf, "%u", this->VersionMajor);
610
    this->AddFindDefinition(ver + "_MAJOR", buf);
611
    sprintf(buf, "%u", this->VersionMinor);
612
    this->AddFindDefinition(ver + "_MINOR", buf);
613
    sprintf(buf, "%u", this->VersionPatch);
614
    this->AddFindDefinition(ver + "_PATCH", buf);
615
    sprintf(buf, "%u", this->VersionTweak);
616
    this->AddFindDefinition(ver + "_TWEAK", buf);
617
    sprintf(buf, "%u", this->VersionCount);
618
    this->AddFindDefinition(ver + "_COUNT", buf);
619

620 621 622
    // Tell the module whether an exact version has been requested.
    std::string exact = this->Name;
    exact += "_FIND_VERSION_EXACT";
623 624
    this->AddFindDefinition(exact, this->VersionExact ? "1" : "0");
  }
625 626
}

627 628
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
                                             const char* val)
629
{
630
  if (const char* old = this->Makefile->GetDefinition(var)) {
631 632
    this->OriginalDefs[var].exists = true;
    this->OriginalDefs[var].value = old;
633
  } else {
634
    this->OriginalDefs[var].exists = false;
635
  }
636 637 638 639 640
  this->Makefile->AddDefinition(var, val);
}

void cmFindPackageCommand::RestoreFindDefinitions()
{
641 642
  for (auto const& i : this->OriginalDefs) {
    OriginalDef const& od = i.second;
643
    if (od.exists) {
644
      this->Makefile->AddDefinition(i.first, od.value.c_str());
645
    } else {
646
      this->Makefile->RemoveDefinition(i.first);
647
    }
648
  }
649
}
650 651 652 653 654 655 656

bool cmFindPackageCommand::FindModule(bool& found)
{
  std::string module = "Find";
  module += this->Name;
  module += ".cmake";
  std::string mfile = this->Makefile->GetModulesFile(module.c_str());
657
  if (!mfile.empty()) {
658 659
    // Load the module we found, and set "<name>_FIND_MODULE" to true
    // while inside it.
660
    found = true;
661 662
    std::string var = this->Name;
    var += "_FIND_MODULE";
663
    this->Makefile->AddDefinition(var, "1");
664
    bool result = this->ReadListFile(mfile.c_str(), DoPolicyScope);
665
    this->Makefile->RemoveDefinition(var);
666
    return result;
667
  }
668 669 670 671 672
  return true;
}

bool cmFindPackageCommand::HandlePackageMode()
{
673
  this->ConsideredConfigs.clear();
674

675 676 677 678 679
  // Support old capitalization behavior.
  std::string upperDir = cmSystemTools::UpperCase(this->Name);
  std::string upperFound = cmSystemTools::UpperCase(this->Name);
  upperDir += "_DIR";
  upperFound += "_FOUND";
680

681
  // Try to find the config file.
682
  const char* def = this->Makefile->GetDefinition(this->Variable);
683

684
  // Try to load the config file if the directory is known
685
  bool fileFound = false;
686 687
  if (this->UseConfigFiles) {
    if (!cmSystemTools::IsOff(def)) {
688 689 690 691 692
      // Get the directory from the variable value.
      std::string dir = def;
      cmSystemTools::ConvertToUnixSlashes(dir);

      // Treat relative paths with respect to the current source dir.
693
      if (!cmSystemTools::FileIsFullPath(dir)) {
694
        dir = "/" + dir;
695
        dir = this->Makefile->GetCurrentSourceDirectory() + dir;
696
      }
697 698
      // The file location was cached.  Look for the correct file.
      std::string file;
699
      if (this->FindConfigFile(dir, file)) {
700 701
        this->FileFound = file;
        fileFound = true;
Alexander Neundorf's avatar
Alexander Neundorf committed
702
      }
703 704
      def = this->Makefile->GetDefinition(this->Variable);
    }
705 706

    // Search for the config file if it is not already found.
707
    if (cmSystemTools::IsOff(def) || !fileFound) {
708
      fileFound = this->FindConfig();
709
    }
710

711
    // Sanity check.
712
    if (fileFound && this->FileFound.empty()) {
713 714 715
      this->Makefile->IssueMessage(
        cmake::INTERNAL_ERROR, "fileFound is true but FileFound is empty!");
      fileFound = false;
716
    }
717
  }
718

719 720
  std::string foundVar = this->Name;
  foundVar += "_FOUND";
721 722 723
  std::string notFoundMessageVar = this->Name;
  notFoundMessageVar += "_NOT_FOUND_MESSAGE";
  std::string notFoundMessage;
724

725 726 727
  // If the directory for the config file was found, try to read the file.
  bool result = true;
  bool found = false;
728 729
  bool configFileSetFOUNDFalse = false;

730
  if (fileFound) {
731 732
    if (this->Makefile->IsDefinitionSet(foundVar) &&
        !this->Makefile->IsOn(foundVar)) {
733 734 735 736
      // 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:
737
      this->Makefile->RemoveDefinition(foundVar);
738
    }
739
    this->Makefile->RemoveDefinition(notFoundMessageVar);
740

741 742 743 744 745
    // Set the version variables before loading the config file.
    // It may override them.
    this->StoreVersionFound();

    // Parse the configuration file.
746
    if (this->ReadListFile(this->FileFound.c_str(), DoPolicyScope)) {
747 748
      // The package has been found.
      found = true;
749 750

      // Check whether the Config file has set Foo_FOUND to FALSE:
751 752
      if (this->Makefile->IsDefinitionSet(foundVar) &&
          !this->Makefile->IsOn(foundVar)) {
753 754 755
        // we get here if the Config file has set Foo_FOUND actively to FALSE
        found = false;
        configFileSetFOUNDFalse = true;
756 757
        notFoundMessage =
          this->Makefile->GetSafeDefinition(notFoundMessageVar);
758
      }
759
    } else {
760
      // The configuration file is invalid.
761
      result = false;
762
    }
763
  }
764

765 766
  // package not found
  if (result && !found) {
767 768
    // warn if package required or neither quiet nor in config mode
    if (this->Required ||
769 770 771
        !(this->Quiet ||
          (this->UseConfigFiles && !this->UseFindModules &&
           this->ConsideredConfigs.empty()))) {
772 773 774 775 776 777 778 779 780 781 782 783 784
      // 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";
        }
785
      }
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
      // 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";
        for (std::vector<ConfigFileInfo>::const_iterator i =
               this->ConsideredConfigs.begin();
             i != duplicate_end; ++i) {
          e << "  " << i->filename << ", version: " << i->version << "\n";
801
        }
802 803 804 805 806 807
      } else {
        std::string requestedVersionString;
        if (!this->Version.empty()) {
          requestedVersionString = " (requested version ";
          requestedVersionString += this->Version;
          requestedVersionString += ")";
808
        }
809

810 811 812 813 814 815
        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 \""
816 817 818
              << this->Name
              << "\", "
                 "but CMake did not find one.\n";
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
          }

          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 \""
839 840 841
            << this->Name
            << "\" provides a separate development "
               "package or SDK, be sure it has been installed.";
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
        } 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";
862
        }
863
      }
864

865 866 867 868
      this->Makefile->IssueMessage(
        this->Required ? cmake::FATAL_ERROR : cmake::WARNING, e.str());
      if (this->Required) {
        cmSystemTools::SetFatalErrorOccured();
869
      }
870

871 872 873
      if (!aw.str().empty()) {
        this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, aw.str());
      }
874
    }
875 876 877 878 879 880
    // 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)";
      this->Makefile->DisplayStatus(aw.str().c_str(), -1);
881
    }
882
  }
883

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

887 888 889
  // Set a variable naming the configuration file that was found.
  std::string fileVar = this->Name;
  fileVar += "_CONFIG";
890
  if (found) {
891
    this->Makefile->AddDefinition(fileVar, this->FileFound.c_str());
892
  } else {
893
    this->Makefile->RemoveDefinition(fileVar);
894
  }
895

896 897 898 899 900 901 902
  std::string consideredConfigsVar = this->Name;
  consideredConfigsVar += "_CONSIDERED_CONFIGS";
  std::string consideredVersionsVar = this->Name;
  consideredVersionsVar += "_CONSIDERED_VERSIONS";

  std::string consideredConfigFiles;
  std::string consideredVersions;
903

904
  const char* sep = "";
905
  for (ConfigFileInfo const& i : this->ConsideredConfigs) {
906 907
    consideredConfigFiles += sep;
    consideredVersions += sep;
908 909
    consideredConfigFiles += i.filename;
    consideredVersions += i.version;
910
    sep = ";";
911
  }
912

913
  this->Makefile->AddDefinition(consideredConfigsVar,
914 915
                                consideredConfigFiles.c_str());

916
  this->Makefile->AddDefinition(consideredVersionsVar,
917 918
                                consideredVersions.c_str());

919 920 921
  return result;
}

922
bool cmFindPackageCommand::FindConfig()
923
{
924 925
  // Compute the set of search prefixes.
  this->ComputePrefixes();
926

927 928
  // Look for the project's configuration file.
  bool found = false;
929

930
  // Search for frameworks.
931
  if (!found && (this->SearchFrameworkFirst || this->SearchFrameworkOnly)) {
932
    found = this->FindFrameworkConfig();
933
  }
934

935
  // Search for apps.
936
  if (!found && (this->SearchAppBundleFirst || this->SearchAppBundleOnly)) {
937
    found = this->FindAppBundleConfig();
938
  }
939

940
  // Search prefixes.
941
  if (!found && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) {
942
    found = this->FindPrefixedConfig();
943
  }
944

945
  // Search for frameworks.
946
  if (!found && this->SearchFrameworkLast) {
947
    found = this->FindFrameworkConfig();
948
  }
949

950
  // Search for apps.
951
  if (!found && this->SearchAppBundleLast) {
952
    found = this->FindAppBundleConfig();
953
  }
954

955
  // Store the entry in the cache so it can be set by the user.
956
  std::string init;
957
  if (found) {
958
    init = cmSystemTools::GetFilenamePath(this->FileFound);
959
  } else {
960
    init = this->Variable + "-NOTFOUND";
961
  }
962 963 964 965
  std::string help =
    "The directory containing a CMake configuration file for ";
  help += this->Name;
  help += ".";
966
  // We force the value since we do not get here if it was already set.
967
  this->Makefile->AddCacheDefinition(this->Variable, init.c_str(),
968
                                     help.c_str(), cmStateEnums::PATH, true);
969
  return found;
970 971
}

972
bool cmFindPackageCommand::FindPrefixedConfig()
973
{
974
  std::vector<std::string> const& prefixes = this->SearchPaths;
975 976
  for (std::string const& p : prefixes) {
    if (this->SearchPrefix(p)) {
977
      return true;
978
    }
979
  }
980 981
  return false;
}
982

983 984
bool cmFindPackageCommand::FindFrameworkConfig()
{
985
  std::vector<std::string> const& prefixes = this->SearchPaths;
986 987
  for (std::string const& p : prefixes) {
    if (this->SearchFrameworkPrefix(p)) {
988
      return true;
989
    }
990
  }
991 992
  return false;
}
993

994 995
bool cmFindPackageCommand::FindAppBundleConfig()
{
996
  std::vector<std::string> const& prefixes = this->SearchPaths;
997 998
  for (std::string const& p : prefixes) {
    if (this->SearchAppBundlePrefix(p)) {
999
      return true;
1000
    }
1001
  }
1002
  return false;
1003 1004
}

1005
bool cmFindPackageCommand::ReadListFile(const char* f, PolicyScopeRule psr)
1006
{
1007
  const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope;
1008
  if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
1009
    return true;
1010
  }
1011 1012 1013
  std::string e = "Error reading CMake code from \"";
  e += f;
  e += "\".";
1014
  this->SetError(e);
1015 1016
  return false;
}
Alexander Neundorf's avatar
Alexander Neundorf committed
1017

1018
void cmFindPackageCommand::AppendToFoundProperty(bool found)
Alexander Neundorf's avatar
Alexander Neundorf committed
1019
{
1020
  std::vector<std::string> foundContents;