cmFindPackageCommand.cxx 69.3 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
16
17
18
19
#include <functional>
#include <iterator>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <utility>
20

21
22
23
24
25
26
27
28
#include "cmAlgorithms.h"
#include "cmMakefile.h"
#include "cmSearchPath.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmVersion.h"
#include "cm_auto_ptr.hxx"
#include "cmake.h"
29

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

35
36
37
class cmExecutionStatus;
class cmFileList;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

225
226
227
  // Always search directly in a generated path.
  this->SearchPathSuffixes.push_back("");

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

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

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

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

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

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

389
  if (this->Version.empty() || components.empty()) {
390
391
392
393
    // Check whether we are recursing inside "Find<name>.cmake" within
    // another find_package(<name>) call.
    std::string mod = this->Name;
    mod += "_FIND_MODULE";
394
395
    if (this->Makefile->IsOn(mod)) {
      if (this->Version.empty()) {
396
397
398
399
        // 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
400
        this->Version = this->Makefile->GetSafeDefinition(ver);
401

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

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

442
443
  std::string disableFindPackageVar = "CMAKE_DISABLE_FIND_PACKAGE_";
  disableFindPackageVar += this->Name;
444
445
  if (this->Makefile->IsOn(disableFindPackageVar)) {
    if (this->Required) {
446
      std::ostringstream e;
447
448
449
      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
450
      this->SetError(e.str());
451
452
453
      return false;
    }

454
455
    return true;
  }
456

457
  this->SetModuleVariables(components);
458

459
  // See if there is a Find<package>.cmake module.
460
  if (this->UseFindModules) {
461
    bool foundModule = false;
462
    if (!this->FindModule(foundModule)) {
463
      this->AppendSuccessInformation();
464
      return false;
465
466
    }
    if (foundModule) {
467
      this->AppendSuccessInformation();
468
      return true;
469
    }
470
  }
471

472
473
  if (this->UseFindModules && this->UseConfigFiles &&
      this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
474
    std::ostringstream aw;
475
    if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
476
      aw << "find_package called without either MODULE or CONFIG option and "
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
            "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.";
    }
502
    aw << "\n"
503
          "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this warning.)";
504
    this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, aw.str());
505
  }
506

507
  // No find module.  Assume the project has a CMake config file.  Use
508
  // a <package>_DIR cache variable to locate it.
509
  this->Variable = this->Name;
510
  this->Variable += "_DIR";
511

512
  // Add the default name.
513
  if (this->Names.empty()) {
514
    this->Names.push_back(this->Name);
515
  }
516
517

  // Add the default configs.
518
519
520
  if (this->Configs.empty()) {
    for (std::vector<std::string>::const_iterator ni = this->Names.begin();
         ni != this->Names.end(); ++ni) {
521
522
523
524
525
526
527
528
      std::string config = *ni;
      config += "Config.cmake";
      this->Configs.push_back(config);

      config = cmSystemTools::LowerCase(*ni);
      config += "-config.cmake";
      this->Configs.push_back(config);
    }
529
  }
530

531
532
533
534
535
536
537
538
539
  // 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());

540
541
542
543
544
545
  // Find and load the package.
  bool result = this->HandlePackageMode();
  this->AppendSuccessInformation();
  return result;
}

546
void cmFindPackageCommand::SetModuleVariables(const std::string& components)
547
{
548
549
  this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name.c_str());

550
551
  // Store the list of components.
  std::string components_var = this->Name + "_FIND_COMPONENTS";
Stephen Kelly's avatar
Stephen Kelly committed
552
  this->AddFindDefinition(components_var, components.c_str());
553

554
  if (this->Quiet) {
555
556
557
558
    // 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
559
    this->AddFindDefinition(quietly, "1");
560
  }
561

562
  if (this->Required) {
563
564
565
566
    // 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
567
    this->AddFindDefinition(req, "1");
568
  }
569

570
  if (!this->Version.empty()) {
571
572
573
574
    // 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
575
    this->AddFindDefinition(ver, this->Version.c_str());
576
    char buf[64];
577
    sprintf(buf, "%u", this->VersionMajor);
578
    this->AddFindDefinition(ver + "_MAJOR", buf);
579
    sprintf(buf, "%u", this->VersionMinor);
580
    this->AddFindDefinition(ver + "_MINOR", buf);
581
    sprintf(buf, "%u", this->VersionPatch);
582
    this->AddFindDefinition(ver + "_PATCH", buf);
583
    sprintf(buf, "%u", this->VersionTweak);
584
    this->AddFindDefinition(ver + "_TWEAK", buf);
585
    sprintf(buf, "%u", this->VersionCount);
586
    this->AddFindDefinition(ver + "_COUNT", buf);
587

588
589
590
    // Tell the module whether an exact version has been requested.
    std::string exact = this->Name;
    exact += "_FIND_VERSION_EXACT";
591
592
    this->AddFindDefinition(exact, this->VersionExact ? "1" : "0");
  }
593
594
595

  // Push on to the pacakge stack
  this->Makefile->FindPackageModuleStack.push_back(this->Name);
596
597
}

598
599
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
                                             const char* val)
600
{
601
  if (const char* old = this->Makefile->GetDefinition(var)) {
602
603
    this->OriginalDefs[var].exists = true;
    this->OriginalDefs[var].value = old;
604
  } else {
605
    this->OriginalDefs[var].exists = false;
606
  }
607
608
609
610
611
  this->Makefile->AddDefinition(var, val);
}

void cmFindPackageCommand::RestoreFindDefinitions()
{
612
613
614
  for (std::map<std::string, OriginalDef>::iterator i =
         this->OriginalDefs.begin();
       i != this->OriginalDefs.end(); ++i) {
615
    OriginalDef const& od = i->second;
616
    if (od.exists) {
Stephen Kelly's avatar
Stephen Kelly committed
617
      this->Makefile->AddDefinition(i->first, od.value.c_str());
618
    } else {
Stephen Kelly's avatar
Stephen Kelly committed
619
      this->Makefile->RemoveDefinition(i->first);
620
    }
621
  }
622
}
623
624
625
626
627
628
629

bool cmFindPackageCommand::FindModule(bool& found)
{
  std::string module = "Find";
  module += this->Name;
  module += ".cmake";
  std::string mfile = this->Makefile->GetModulesFile(module.c_str());
630
  if (!mfile.empty()) {
631
632
    // Load the module we found, and set "<name>_FIND_MODULE" to true
    // while inside it.
633
    found = true;
634
635
    std::string var = this->Name;
    var += "_FIND_MODULE";
Stephen Kelly's avatar
Stephen Kelly committed
636
    this->Makefile->AddDefinition(var, "1");
637
    bool result = this->ReadListFile(mfile.c_str(), DoPolicyScope);
Stephen Kelly's avatar
Stephen Kelly committed
638
    this->Makefile->RemoveDefinition(var);
639
    return result;
640
  }
641
642
643
644
645
  return true;
}

bool cmFindPackageCommand::HandlePackageMode()
{
646
  this->ConsideredConfigs.clear();
647

648
649
650
651
652
  // Support old capitalization behavior.
  std::string upperDir = cmSystemTools::UpperCase(this->Name);
  std::string upperFound = cmSystemTools::UpperCase(this->Name);
  upperDir += "_DIR";
  upperFound += "_FOUND";
653

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

657
  // Try to load the config file if the directory is known
658
  bool fileFound = false;
659
660
  if (this->UseConfigFiles) {
    if (!cmSystemTools::IsOff(def)) {
661
662
663
664
665
      // Get the directory from the variable value.
      std::string dir = def;
      cmSystemTools::ConvertToUnixSlashes(dir);

      // Treat relative paths with respect to the current source dir.
666
      if (!cmSystemTools::FileIsFullPath(dir.c_str())) {
667
        dir = "/" + dir;
668
        dir = this->Makefile->GetCurrentSourceDirectory() + dir;
669
      }
670
671
      // The file location was cached.  Look for the correct file.
      std::string file;
672
      if (this->FindConfigFile(dir, file)) {
673
674
        this->FileFound = file;
        fileFound = true;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
675
      }
676
677
      def = this->Makefile->GetDefinition(this->Variable);
    }
678
679

    // Search for the config file if it is not already found.
680
    if (cmSystemTools::IsOff(def) || !fileFound) {
681
      fileFound = this->FindConfig();
682
    }
683

684
    // Sanity check.
685
    if (fileFound && this->FileFound.empty()) {
686
687
688
      this->Makefile->IssueMessage(
        cmake::INTERNAL_ERROR, "fileFound is true but FileFound is empty!");
      fileFound = false;
689
    }
690
  }
691

692
693
  std::string foundVar = this->Name;
  foundVar += "_FOUND";
694
695
696
  std::string notFoundMessageVar = this->Name;
  notFoundMessageVar += "_NOT_FOUND_MESSAGE";
  std::string notFoundMessage;
697

698
699
700
  // If the directory for the config file was found, try to read the file.
  bool result = true;
  bool found = false;
701
702
  bool configFileSetFOUNDFalse = false;

703
  if (fileFound) {
704
705
    if (this->Makefile->IsDefinitionSet(foundVar) &&
        !this->Makefile->IsOn(foundVar)) {
706
707
708
709
      // 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
710
      this->Makefile->RemoveDefinition(foundVar);
711
    }
Stephen Kelly's avatar
Stephen Kelly committed
712
    this->Makefile->RemoveDefinition(notFoundMessageVar);
713

714
715
716
717
718
    // Set the version variables before loading the config file.
    // It may override them.
    this->StoreVersionFound();

    // Parse the configuration file.
719
    if (this->ReadListFile(this->FileFound.c_str(), DoPolicyScope)) {
720
721
      // The package has been found.
      found = true;
722
723

      // Check whether the Config file has set Foo_FOUND to FALSE:
724
725
      if (this->Makefile->IsDefinitionSet(foundVar) &&
          !this->Makefile->IsOn(foundVar)) {
726
727
728
        // we get here if the Config file has set Foo_FOUND actively to FALSE
        found = false;
        configFileSetFOUNDFalse = true;
729
730
        notFoundMessage =
          this->Makefile->GetSafeDefinition(notFoundMessageVar);
731
      }
732
    } else {
733
      // The configuration file is invalid.
734
      result = false;
735
    }
736
  }
737

738
739
  // package not found
  if (result && !found) {
740
741
    // warn if package required or neither quiet nor in config mode
    if (this->Required ||
742
743
        !(this->Quiet || (this->UseConfigFiles && !this->UseFindModules &&
                          this->ConsideredConfigs.empty()))) {
744
745
746
747
748
749
750
751
752
753
754
755
756
      // 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";
        }
757
      }
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
      // 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";
773
        }
774
775
776
777
778
779
      } else {
        std::string requestedVersionString;
        if (!this->Version.empty()) {
          requestedVersionString = " (requested version ";
          requestedVersionString += this->Version;
          requestedVersionString += ")";
780
        }
781

782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
        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 \""
              << this->Name << "\", "
                               "but CMake did not find one.\n";
          }

          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 \""
            << this->Name << "\" provides a separate development "
                             "package or SDK, be sure it has been installed.";
        } 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";
832
        }
833
      }
834

835
836
837
838
      this->Makefile->IssueMessage(
        this->Required ? cmake::FATAL_ERROR : cmake::WARNING, e.str());
      if (this->Required) {
        cmSystemTools::SetFatalErrorOccured();
839
      }
840

841
842
843
      if (!aw.str().empty()) {
        this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, aw.str());
      }
844
    }
845
846
847
848
849
850
    // 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);
851
    }
852
  }
853

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

857
858
859
  // Set a variable naming the configuration file that was found.
  std::string fileVar = this->Name;
  fileVar += "_CONFIG";
860
  if (found) {
Stephen Kelly's avatar
Stephen Kelly committed
861
    this->Makefile->AddDefinition(fileVar, this->FileFound.c_str());
862
  } else {
Stephen Kelly's avatar
Stephen Kelly committed
863
    this->Makefile->RemoveDefinition(fileVar);
864
  }
865

866
867
868
869
870
871
872
  std::string consideredConfigsVar = this->Name;
  consideredConfigsVar += "_CONSIDERED_CONFIGS";
  std::string consideredVersionsVar = this->Name;
  consideredVersionsVar += "_CONSIDERED_VERSIONS";

  std::string consideredConfigFiles;
  std::string consideredVersions;
873

874
  const char* sep = "";