cmFindPackageCommand.cxx 70.9 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
#include "cmRange.h"
27
28
29
30
#include "cmSearchPath.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmVersion.h"
31

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

37
38
39
class cmExecutionStatus;
class cmFileList;

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

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
85
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
}

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

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

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

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

  // Create the new path objects
136
137
138
139
140
141
  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)));
Brad King's avatar
Brad King committed
142
143
}

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

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

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

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

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

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

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

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

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

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

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  // 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;
  }

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

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

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

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

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

323
      std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
Stephen Kelly's avatar
Stephen Kelly committed
324
      this->AddFindDefinition(req_var, isRequired);
325

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

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

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

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

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

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

423
  if (!this->Version.empty()) {
424
425
426
427
428
    // 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;
429
    unsigned int parsed_tweak;
430
431
432
433
434
    this->VersionCount =
      sscanf(this->Version.c_str(), "%u.%u.%u.%u", &parsed_major,
             &parsed_minor, &parsed_patch, &parsed_tweak);
    switch (this->VersionCount) {
      case 4:
435
436
        this->VersionTweak = parsed_tweak;
        CM_FALLTHROUGH;
437
      case 3:
438
439
        this->VersionPatch = parsed_patch;
        CM_FALLTHROUGH;
440
      case 2:
441
442
        this->VersionMinor = parsed_minor;
        CM_FALLTHROUGH;
443
      case 1:
444
445
        this->VersionMajor = parsed_major;
        CM_FALLTHROUGH;
446
447
      default:
        break;
448
    }
449
  }
450

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

463
464
    return true;
  }
465

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

    // 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(
484
          MessageType::FATAL_ERROR,
485
486
487
488
489
490
491
492
493
494
495
496
497
          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;
    }
  }

498
  this->SetModuleVariables(components);
499

500
  // See if there is a Find<PackageName>.cmake module.
501
  bool loadedPackage = false;
502
503
504
  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG")) {
    if (this->UseConfigFiles && this->FindPackageUsingConfigMode()) {
      loadedPackage = true;
505
506
507
508
509
    } else {
      if (this->FindPackageUsingModuleMode()) {
        loadedPackage = true;
      } else {
        // The package was not loaded. Report errors.
510
511
512
        if (HandlePackageMode(HandlePackageModeType::Module)) {
          loadedPackage = true;
        }
513
      }
514
    }
515
516
  } else {
    if (this->UseFindModules && this->FindPackageUsingModuleMode()) {
517
      loadedPackage = true;
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    } else {
      // Handle CMAKE_FIND_PACKAGE_WARN_NO_MODULE (warn when CONFIG mode is
      // implicitly assumed)
      if (this->UseFindModules && this->UseConfigFiles &&
          this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
        std::ostringstream aw;
        if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
          aw << "find_package called without either MODULE or CONFIG option "
                "and "
                "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 "
             << cmSystemTools::LowerCase(this->Name)
             << "-config.cmake).  "
                "Otherwise make Find"
             << this->Name
             << ".cmake available in "
                "CMAKE_MODULE_PATH.";
        }
        aw << "\n"
              "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this "
              "warning.)";
        this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str());
      }

      if (this->FindPackageUsingConfigMode()) {
        loadedPackage = true;
      }
564
    }
565
  }
566

567
568
569
570
571
572
573
574
575
  this->AppendSuccessInformation();
  return loadedPackage;
}

bool cmFindPackageCommand::FindPackageUsingModuleMode()
{
  bool foundModule = false;
  if (!this->FindModule(foundModule)) {
    return false;
576
  }
577
578
  return foundModule;
}
579

580
581
bool cmFindPackageCommand::FindPackageUsingConfigMode()
{
582
  this->Variable = this->Name;
583
  this->Variable += "_DIR";
584

585
  // Add the default name.
586
  if (this->Names.empty()) {
587
    this->Names.push_back(this->Name);
588
  }
589
590

  // Add the default configs.
591
  if (this->Configs.empty()) {
592
593
    for (std::string const& n : this->Names) {
      std::string config = n;
594
595
596
      config += "Config.cmake";
      this->Configs.push_back(config);

597
      config = cmSystemTools::LowerCase(n);
598
      config += "-config.cmake";
599
      this->Configs.push_back(std::move(config));
600
    }
601
  }
602

603
604
605
606
607
608
609
610
611
  // 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());

612
  // Find and load the package.
613
  return this->HandlePackageMode(HandlePackageModeType::Config);
614
615
}

616
void cmFindPackageCommand::SetModuleVariables(const std::string& components)
617
{
618
619
  this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name.c_str());

620
621
  // Store the list of components.
  std::string components_var = this->Name + "_FIND_COMPONENTS";
Stephen Kelly's avatar
Stephen Kelly committed
622
  this->AddFindDefinition(components_var, components.c_str());
623

624
  if (this->Quiet) {
625
626
627
628
    // Tell the module that is about to be read that it should find
    // quietly.
    std::string quietly = this->Name;
    quietly += "_FIND_QUIETLY";
Stephen Kelly's avatar
Stephen Kelly committed
629
    this->AddFindDefinition(quietly, "1");
630
  }
631

632
  if (this->Required) {
633
634
635
636
    // 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";
Stephen Kelly's avatar
Stephen Kelly committed
637
    this->AddFindDefinition(req, "1");
638
  }
639

640
  if (!this->Version.empty()) {
641
642
643
644
    // 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";
Stephen Kelly's avatar
Stephen Kelly committed
645
    this->AddFindDefinition(ver, this->Version.c_str());
646
    char buf[64];
647
    sprintf(buf, "%u", this->VersionMajor);
648
    this->AddFindDefinition(ver + "_MAJOR", buf);
649
    sprintf(buf, "%u", this->VersionMinor);
650
    this->AddFindDefinition(ver + "_MINOR", buf);
651
    sprintf(buf, "%u", this->VersionPatch);
652
    this->AddFindDefinition(ver + "_PATCH", buf);
653
    sprintf(buf, "%u", this->VersionTweak);
654
    this->AddFindDefinition(ver + "_TWEAK", buf);
655
    sprintf(buf, "%u", this->VersionCount);
656
    this->AddFindDefinition(ver + "_COUNT", buf);
657

658
659
660
    // Tell the module whether an exact version has been requested.
    std::string exact = this->Name;
    exact += "_FIND_VERSION_EXACT";
661
662
    this->AddFindDefinition(exact, this->VersionExact ? "1" : "0");
  }
663
664
}

665
666
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
                                             const char* val)
667
{
668
  if (const char* old = this->Makefile->GetDefinition(var)) {
669
670
    this->OriginalDefs[var].exists = true;
    this->OriginalDefs[var].value = old;
671
  } else {
672
    this->OriginalDefs[var].exists = false;
673
  }
674
675
676
677
678
  this->Makefile->AddDefinition(var, val);
}

void cmFindPackageCommand::RestoreFindDefinitions()
{
679
680
  for (auto const& i : this->OriginalDefs) {
    OriginalDef const& od = i.second;
681
    if (od.exists) {
682
      this->Makefile->AddDefinition(i.first, od.value.c_str());
683
    } else {
684
      this->Makefile->RemoveDefinition(i.first);
685
    }
686
  }
687
}
688
689
690
691
692
693

bool cmFindPackageCommand::FindModule(bool& found)
{
  std::string module = "Find";
  module += this->Name;
  module += ".cmake";
694
  bool system = false;
695
  std::string mfile = this->Makefile->GetModulesFile(module, system);
696
  if (!mfile.empty()) {
697
698
699
700
701
702
703
704
705
    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";
706
            this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
707
708
709
710
711
712
713
714
715
716
717
718
            CM_FALLTHROUGH;
          }
          case cmPolicies::OLD:
            break;
          case cmPolicies::REQUIRED_IF_USED:
          case cmPolicies::REQUIRED_ALWAYS:
          case cmPolicies::NEW:
            return true;
        }
      }
    }

719
720
    // Load the module we found, and set "<name>_FIND_MODULE" to true
    // while inside it.
721
    found = true;
722
723
    std::string var = this->Name;
    var += "_FIND_MODULE";
Stephen Kelly's avatar
Stephen Kelly committed
724
    this->Makefile->AddDefinition(var, "1");
725
    bool result = this->ReadListFile(mfile, DoPolicyScope);
Stephen Kelly's avatar
Stephen Kelly committed
726
    this->Makefile->RemoveDefinition(var);
727
    return result;
728
  }
729
730
731
  return true;
}

732
733
bool cmFindPackageCommand::HandlePackageMode(
  HandlePackageModeType handlePackageModeType)
734
{
735
  this->ConsideredConfigs.clear();
736

737
738
739
740
741
  // Support old capitalization behavior.
  std::string upperDir = cmSystemTools::UpperCase(this->Name);
  std::string upperFound = cmSystemTools::UpperCase(this->Name);
  upperDir += "_DIR";
  upperFound += "_FOUND";
742

743
  // Try to find the config file.
Stephen Kelly's avatar
Stephen Kelly committed
744
  const char* def = this->Makefile->GetDefinition(this->Variable);
745

746
  // Try to load the config file if the directory is known
747
  bool fileFound = false;
748
749
  if (this->UseConfigFiles) {
    if (!cmSystemTools::IsOff(def)) {
750
751
752
753
754
      // Get the directory from the variable value.
      std::string dir = def;
      cmSystemTools::ConvertToUnixSlashes(dir);

      // Treat relative paths with respect to the current source dir.
755
      if (!cmSystemTools::FileIsFullPath(dir)) {
756
        dir = "/" + dir;
757
        dir = this->Makefile->GetCurrentSourceDirectory() + dir;
758
      }
759
760
      // The file location was cached.  Look for the correct file.
      std::string file;
761
      if (this->FindConfigFile(dir, file)) {
762
763
        this->FileFound = file;
        fileFound = true;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
764
      }
765
766
      def = this->Makefile->GetDefinition(this->Variable);
    }
767
768

    // Search for the config file if it is not already found.
769
    if (cmSystemTools::IsOff(def) || !fileFound) {
770
      fileFound = this->FindConfig();
771
    }
772

773
    // Sanity check.
774
    if (fileFound && this->FileFound.empty()) {
775
      this->Makefile->IssueMessage(
776
777
        MessageType::INTERNAL_ERROR,
        "fileFound is true but FileFound is empty!");
778
      fileFound = false;
779
    }
780
  }
781

782
783
  std::string foundVar = this->Name;
  foundVar += "_FOUND";
784
785
786
  std::string notFoundMessageVar = this->Name;
  notFoundMessageVar += "_NOT_FOUND_MESSAGE";
  std::string notFoundMessage;
787

788
789
790
  // If the directory for the config file was found, try to read the file.
  bool result = true;
  bool found = false;
791
792
  bool configFileSetFOUNDFalse = false;

793
  if (fileFound) {
794
795
    if (this->Makefile->IsDefinitionSet(foundVar) &&
        !this->Makefile->IsOn(foundVar)) {
796
797
798
799
      // 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:
Stephen Kelly's avatar
Stephen Kelly committed
800
      this->Makefile->RemoveDefinition(foundVar);
801
    }
Stephen Kelly's avatar
Stephen Kelly committed
802
    this->Makefile->RemoveDefinition(notFoundMessageVar);
803

804
805
806
807
808
    // Set the version variables before loading the config file.
    // It may override them.
    this->StoreVersionFound();

    // Parse the configuration file.
809
    if (this->ReadListFile(this->FileFound, DoPolicyScope)) {
810
811
      // The package has been found.
      found = true;
812
813

      // Check whether the Config file has set Foo_FOUND to FALSE:
814
815
      if (this->Makefile->IsDefinitionSet(foundVar) &&
          !this->Makefile->IsOn(foundVar)) {
816
817
818
        // we get here if the Config file has set Foo_FOUND actively to FALSE
        found = false;
        configFileSetFOUNDFalse = true;
819
820
        notFoundMessage =
          this->Makefile->GetSafeDefinition(notFoundMessageVar);
821
      }
822
    } else {
823
      // The configuration file is invalid.
824
      result = false;
825
    }
826
  }
827

828
829
830
  if (this->UseFindModules && !found &&
      handlePackageModeType == HandlePackageModeType::Config &&
      this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG")) {
831
832
833
834
    // Config mode failed. Allow Module case.
    result = false;
  }

835
836
  // package not found
  if (result && !found) {
837
838
    // warn if package required or neither quiet nor in config mode
    if (this->Required ||
839
840
841
        !(this->Quiet ||
          (this->UseConfigFiles && !this->UseFindModules &&
           this->ConsideredConfigs.empty()))) {
842
843
844
845
846
847
848
849
850
851
852
853
854
      // 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";
        }
855
      }
856
857
858
859
860
861
862
863
864
865
866
      // 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";
867
868
869
870

        for (ConfigFileInfo const& info :
             cmMakeRange(this->ConsideredConfigs.cbegin(), duplicate_end)) {
          e << "  " << info.filename << ", version: " << info.version << "\n";
871
        }
872
873
874
875
876
877
      } else {
        std::string requestedVersionString;
        if (!this->Version.empty()) {
          requestedVersionString = " (requested version ";
          requestedVersionString += this->Version;
          requestedVersionString += ")";
878
        }
879

880
881
882
883
884
885
        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 \""
886
887
888
              << this->Name
              << "\", "
                 "but CMake did not find one.\n";
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
          }

          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 \""
909
910
911
            << this->Name
            << "\" provides a separate development "
               "package or SDK, be sure it has been installed.";
912
913
914
915
916
917
918
919
920