cmFindPackageCommand.cxx 70.1 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
501
502
503
504
505
506
507
508
509
510
511
512
513
  const char* include =
    this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_INCLUDE");

  if (include) {
    const bool readit = this->Makefile->ReadDependentFile(include);
    if (!readit && !cmSystemTools::GetFatalErrorOccured()) {
      std::string m = "could not find file:\n"
                      "  ";
      m += include;
      this->SetError(m);
      return false;
    }
  }

514
  // See if there is a Find<PackageName>.cmake module.
515
  if (this->UseFindModules) {
516
    bool foundModule = false;
517
    if (!this->FindModule(foundModule)) {
518
      this->AppendSuccessInformation();
519
      return false;
520
521
    }
    if (foundModule) {
522
      this->AppendSuccessInformation();
523
      return true;
524
    }
525
  }
526

527
528
  if (this->UseFindModules && this->UseConfigFiles &&
      this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
529
    std::ostringstream aw;
530
    if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
531
      aw << "find_package called without either MODULE or CONFIG option and "
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
            "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 "
552
553
554
555
556
557
        << cmSystemTools::LowerCase(this->Name)
        << "-config.cmake).  "
           "Otherwise make Find"
        << this->Name
        << ".cmake available in "
           "CMAKE_MODULE_PATH.";
558
    }
559
    aw << "\n"
560
          "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this warning.)";
561
    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str());
562
  }
563

564
  // No find module.  Assume the project has a CMake config file.  Use
565
  // a <PackageName>_DIR cache variable to locate it.
566
  this->Variable = this->Name;
567
  this->Variable += "_DIR";
568

569
  // Add the default name.
570
  if (this->Names.empty()) {
571
    this->Names.push_back(this->Name);
572
  }
573
574

  // Add the default configs.
575
  if (this->Configs.empty()) {
576
577
    for (std::string const& n : this->Names) {
      std::string config = n;
578
579
580
      config += "Config.cmake";
      this->Configs.push_back(config);

581
      config = cmSystemTools::LowerCase(n);
582
      config += "-config.cmake";
583
      this->Configs.push_back(std::move(config));
584
    }
585
  }
586

587
588
589
590
591
592
593
594
595
  // 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());

596
597
598
599
600
601
  // Find and load the package.
  bool result = this->HandlePackageMode();
  this->AppendSuccessInformation();
  return result;
}

602
void cmFindPackageCommand::SetModuleVariables(const std::string& components)
603
{
604
605
  this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name.c_str());

606
607
  // Store the list of components.
  std::string components_var = this->Name + "_FIND_COMPONENTS";
Stephen Kelly's avatar
Stephen Kelly committed
608
  this->AddFindDefinition(components_var, components.c_str());
609

610
  if (this->Quiet) {
611
612
613
614
    // 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
615
    this->AddFindDefinition(quietly, "1");
616
  }
617

618
  if (this->Required) {
619
620
621
622
    // 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
623
    this->AddFindDefinition(req, "1");
624
  }
625

626
  if (!this->Version.empty()) {
627
628
629
630
    // 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
631
    this->AddFindDefinition(ver, this->Version.c_str());
632
    char buf[64];
633
    sprintf(buf, "%u", this->VersionMajor);
634
    this->AddFindDefinition(ver + "_MAJOR", buf);
635
    sprintf(buf, "%u", this->VersionMinor);
636
    this->AddFindDefinition(ver + "_MINOR", buf);
637
    sprintf(buf, "%u", this->VersionPatch);
638
    this->AddFindDefinition(ver + "_PATCH", buf);
639
    sprintf(buf, "%u", this->VersionTweak);
640
    this->AddFindDefinition(ver + "_TWEAK", buf);
641
    sprintf(buf, "%u", this->VersionCount);
642
    this->AddFindDefinition(ver + "_COUNT", buf);
643

644
645
646
    // Tell the module whether an exact version has been requested.
    std::string exact = this->Name;
    exact += "_FIND_VERSION_EXACT";
647
648
    this->AddFindDefinition(exact, this->VersionExact ? "1" : "0");
  }
649
650
}

651
652
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
                                             const char* val)
653
{
654
  if (const char* old = this->Makefile->GetDefinition(var)) {
655
656
    this->OriginalDefs[var].exists = true;
    this->OriginalDefs[var].value = old;
657
  } else {
658
    this->OriginalDefs[var].exists = false;
659
  }
660
661
662
663
664
  this->Makefile->AddDefinition(var, val);
}

void cmFindPackageCommand::RestoreFindDefinitions()
{
665
666
  for (auto const& i : this->OriginalDefs) {
    OriginalDef const& od = i.second;
667
    if (od.exists) {
668
      this->Makefile->AddDefinition(i.first, od.value.c_str());
669
    } else {
670
      this->Makefile->RemoveDefinition(i.first);
671
    }
672
  }
673
}
674
675
676
677
678
679

bool cmFindPackageCommand::FindModule(bool& found)
{
  std::string module = "Find";
  module += this->Name;
  module += ".cmake";
680
  bool system = false;
681
  std::string mfile = this->Makefile->GetModulesFile(module, system);
682
  if (!mfile.empty()) {
683
684
685
686
687
688
689
690
691
    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";
692
            this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
693
694
695
696
697
698
699
700
701
702
703
704
            CM_FALLTHROUGH;
          }
          case cmPolicies::OLD:
            break;
          case cmPolicies::REQUIRED_IF_USED:
          case cmPolicies::REQUIRED_ALWAYS:
          case cmPolicies::NEW:
            return true;
        }
      }
    }

705
706
    // Load the module we found, and set "<name>_FIND_MODULE" to true
    // while inside it.
707
    found = true;
708
709
    std::string var = this->Name;
    var += "_FIND_MODULE";
Stephen Kelly's avatar
Stephen Kelly committed
710
    this->Makefile->AddDefinition(var, "1");
711
    bool result = this->ReadListFile(mfile, DoPolicyScope);
Stephen Kelly's avatar
Stephen Kelly committed
712
    this->Makefile->RemoveDefinition(var);
713
    return result;
714
  }
715
716
717
718
719
  return true;
}

bool cmFindPackageCommand::HandlePackageMode()
{
720
  this->ConsideredConfigs.clear();
721

722
723
724
725
726
  // Support old capitalization behavior.
  std::string upperDir = cmSystemTools::UpperCase(this->Name);
  std::string upperFound = cmSystemTools::UpperCase(this->Name);
  upperDir += "_DIR";
  upperFound += "_FOUND";
727

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

731
  // Try to load the config file if the directory is known
732
  bool fileFound = false;
733
734
  if (this->UseConfigFiles) {
    if (!cmSystemTools::IsOff(def)) {
735
736
737
738
739
      // Get the directory from the variable value.
      std::string dir = def;
      cmSystemTools::ConvertToUnixSlashes(dir);

      // Treat relative paths with respect to the current source dir.
740
      if (!cmSystemTools::FileIsFullPath(dir)) {
741
        dir = "/" + dir;
742
        dir = this->Makefile->GetCurrentSourceDirectory() + dir;
743
      }
744
745
      // The file location was cached.  Look for the correct file.
      std::string file;
746
      if (this->FindConfigFile(dir, file)) {
747
748
        this->FileFound = file;
        fileFound = true;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
749
      }
750
751
      def = this->Makefile->GetDefinition(this->Variable);
    }
752
753

    // Search for the config file if it is not already found.
754
    if (cmSystemTools::IsOff(def) || !fileFound) {
755
      fileFound = this->FindConfig();
756
    }
757

758
    // Sanity check.
759
    if (fileFound && this->FileFound.empty()) {
760
      this->Makefile->IssueMessage(
761
762
        MessageType::INTERNAL_ERROR,
        "fileFound is true but FileFound is empty!");
763
      fileFound = false;
764
    }
765
  }
766

767
768
  std::string foundVar = this->Name;
  foundVar += "_FOUND";
769
770
771
  std::string notFoundMessageVar = this->Name;
  notFoundMessageVar += "_NOT_FOUND_MESSAGE";
  std::string notFoundMessage;
772

773
774
775
  // If the directory for the config file was found, try to read the file.
  bool result = true;
  bool found = false;
776
777
  bool configFileSetFOUNDFalse = false;

778
  if (fileFound) {
779
780
    if (this->Makefile->IsDefinitionSet(foundVar) &&
        !this->Makefile->IsOn(foundVar)) {
781
782
783
784
      // 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
785
      this->Makefile->RemoveDefinition(foundVar);
786
    }
Stephen Kelly's avatar
Stephen Kelly committed
787
    this->Makefile->RemoveDefinition(notFoundMessageVar);
788

789
790
791
792
793
    // Set the version variables before loading the config file.
    // It may override them.
    this->StoreVersionFound();

    // Parse the configuration file.
794
    if (this->ReadListFile(this->FileFound, DoPolicyScope)) {
795
796
      // The package has been found.
      found = true;
797
798

      // Check whether the Config file has set Foo_FOUND to FALSE:
799
800
      if (this->Makefile->IsDefinitionSet(foundVar) &&
          !this->Makefile->IsOn(foundVar)) {
801
802
803
        // we get here if the Config file has set Foo_FOUND actively to FALSE
        found = false;
        configFileSetFOUNDFalse = true;
804
805
        notFoundMessage =
          this->Makefile->GetSafeDefinition(notFoundMessageVar);
806
      }
807
    } else {
808
      // The configuration file is invalid.
809
      result = false;
810
    }
811
  }
812

813
814
  // package not found
  if (result && !found) {
815
816
    // warn if package required or neither quiet nor in config mode
    if (this->Required ||
817
818
819
        !(this->Quiet ||
          (this->UseConfigFiles && !this->UseFindModules &&
           this->ConsideredConfigs.empty()))) {
820
821
822
823
824
825
826
827
828
829
830
831
832
      // 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";
        }
833
      }
834
835
836
837
838
839
840
841
842
843
844
      // 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";
845
846
847
848

        for (ConfigFileInfo const& info :
             cmMakeRange(this->ConsideredConfigs.cbegin(), duplicate_end)) {
          e << "  " << info.filename << ", version: " << info.version << "\n";
849
        }
850
851
852
853
854
855
      } else {
        std::string requestedVersionString;
        if (!this->Version.empty()) {
          requestedVersionString = " (requested version ";
          requestedVersionString += this->Version;
          requestedVersionString += ")";
856
        }
857

858
859