resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmFindBase.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmFindBase.h"
4
5 #include <algorithm>
6 #include <cstddef>
7 #include <deque>
8 #include <functional>
9 #include <map>
10 #include <utility>
11
12 #include <cm/optional>
13 #include <cmext/algorithm>
14 #include <cmext/string_view>
15
16 #include "cmCMakePath.h"
17 #include "cmExecutionStatus.h"
18 #include "cmListFileCache.h"
19 #include "cmMakefile.h"
20 #include "cmMessageType.h"
21 #include "cmPolicies.h"
22 #include "cmRange.h"
23 #include "cmSearchPath.h"
24 #include "cmState.h"
25 #include "cmStateTypes.h"
26 #include "cmStringAlgorithms.h"
27 #include "cmSystemTools.h"
28 #include "cmValue.h"
29 #include "cmWindowsRegistry.h"
30 #include "cmake.h"
31
32 cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status)
33   : cmFindCommon(status)
34   , FindCommandName(std::move(findCommandName))
35 {
36 }
37
38 bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
39 {
40   if (argsIn.size() < 2) {
41     this->SetError("called with incorrect number of arguments");
42     return false;
43   }
44
45   // copy argsIn into args so it can be modified,
46   // in the process extract the DOC "documentation"
47   // and handle options NO_CACHE and ENV
48   size_t size = argsIn.size();
49   std::vector<std::string> args;
50   bool foundDoc = false;
51   for (unsigned int j = 0; j < size; ++j) {
52     if (foundDoc || argsIn[j] != "DOC") {
53       if (argsIn[j] == "NO_CACHE") {
54         this->StoreResultInCache = false;
55       } else if (argsIn[j] == "ENV") {
56         if (j + 1 < size) {
57           j++;
58           cmSystemTools::GetPath(args, argsIn[j].c_str());
59         }
60       } else {
61         args.push_back(argsIn[j]);
62       }
63     } else {
64       if (j + 1 < size) {
65         foundDoc = true;
66         this->VariableDocumentation = argsIn[j + 1];
67         j++;
68         if (j >= size) {
69           break;
70         }
71       }
72     }
73   }
74   if (args.size() < 2) {
75     this->SetError("called with incorrect number of arguments");
76     return false;
77   }
78   this->VariableName = args[0];
79   if (this->CheckForVariableDefined()) {
80     this->AlreadyDefined = true;
81     return true;
82   }
83
84   // Find what search path locations have been enabled/disable
85   this->SelectDefaultSearchModes();
86
87   // Find the current root path mode.
88   this->SelectDefaultRootPathMode();
89
90   // Find the current bundle/framework search policy.
91   this->SelectDefaultMacMode();
92
93   bool newStyle = false;
94   enum Doing
95   {
96     DoingNone,
97     DoingNames,
98     DoingPaths,
99     DoingPathSuffixes,
100     DoingHints
101   };
102   Doing doing = DoingNames; // assume it starts with a name
103   for (unsigned int j = 1; j < args.size(); ++j) {
104     if (args[j] == "NAMES") {
105       doing = DoingNames;
106       newStyle = true;
107     } else if (args[j] == "PATHS") {
108       doing = DoingPaths;
109       newStyle = true;
110     } else if (args[j] == "HINTS") {
111       doing = DoingHints;
112       newStyle = true;
113     } else if (args[j] == "PATH_SUFFIXES") {
114       doing = DoingPathSuffixes;
115       newStyle = true;
116     } else if (args[j] == "NAMES_PER_DIR") {
117       doing = DoingNone;
118       if (this->NamesPerDirAllowed) {
119         this->NamesPerDir = true;
120       } else {
121         this->SetError("does not support NAMES_PER_DIR");
122         return false;
123       }
124     } else if (args[j] == "NO_SYSTEM_PATH") {
125       doing = DoingNone;
126       this->NoDefaultPath = true;
127     } else if (args[j] == "REQUIRED") {
128       doing = DoingNone;
129       this->Required = true;
130       newStyle = true;
131     } else if (args[j] == "REGISTRY_VIEW") {
132       if (++j == args.size()) {
133         this->SetError("missing required argument for \"REGISTRY_VIEW\"");
134         return false;
135       }
136       auto view = cmWindowsRegistry::ToView(args[j]);
137       if (view) {
138         this->RegistryView = *view;
139       } else {
140         this->SetError(
141           cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j]));
142         return false;
143       }
144     } else if (args[j] == "VALIDATOR") {
145       if (++j == args.size()) {
146         this->SetError("missing required argument for \"VALIDATOR\"");
147         return false;
148       }
149       auto command = this->Makefile->GetState()->GetCommand(args[j]);
150       if (command == nullptr) {
151         this->SetError(cmStrCat(
152           "command specified for \"VALIDATOR\" is undefined: ", args[j], '.'));
153         return false;
154       }
155       // ensure a macro is not specified as validator
156       const auto& validatorName = args[j];
157       auto macros = cmExpandedList(this->Makefile->GetProperty("MACROS"));
158       if (std::find_if(macros.begin(), macros.end(),
159                        [&validatorName](const std::string& item) {
160                          return cmSystemTools::Strucmp(validatorName.c_str(),
161                                                        item.c_str()) == 0;
162                        }) != macros.end()) {
163         this->SetError(cmStrCat(
164           "command specified for \"VALIDATOR\" is not a function: ", args[j],
165           '.'));
166         return false;
167       }
168       this->ValidatorName = args[j];
169     } else if (this->CheckCommonArgument(args[j])) {
170       doing = DoingNone;
171     } else {
172       // Some common arguments were accidentally supported by CMake
173       // 2.4 and 2.6.0 in the short-hand form of the command, so we
174       // must support it even though it is not documented.
175       if (doing == DoingNames) {
176         this->Names.push_back(args[j]);
177       } else if (doing == DoingPaths) {
178         this->UserGuessArgs.push_back(args[j]);
179       } else if (doing == DoingHints) {
180         this->UserHintsArgs.push_back(args[j]);
181       } else if (doing == DoingPathSuffixes) {
182         this->AddPathSuffix(args[j]);
183       }
184     }
185   }
186
187   if (this->VariableDocumentation.empty()) {
188     this->VariableDocumentation = "Where can ";
189     if (this->Names.empty()) {
190       this->VariableDocumentation += "the (unknown) library be found";
191     } else if (this->Names.size() == 1) {
192       this->VariableDocumentation +=
193         "the " + this->Names.front() + " library be found";
194     } else {
195       this->VariableDocumentation += "one of the ";
196       this->VariableDocumentation +=
197         cmJoin(cmMakeRange(this->Names).retreat(1), ", ");
198       this->VariableDocumentation +=
199         " or " + this->Names.back() + " libraries be found";
200     }
201   }
202
203   // look for old style
204   // FIND_*(VAR name path1 path2 ...)
205   if (!newStyle && !this->Names.empty()) {
206     // All the short-hand arguments have been recorded as names.
207     std::vector<std::string> shortArgs = this->Names;
208     this->Names.clear(); // clear out any values in Names
209     this->Names.push_back(shortArgs[0]);
210     cm::append(this->UserGuessArgs, shortArgs.begin() + 1, shortArgs.end());
211   }
212   this->ExpandPaths();
213
214   this->ComputeFinalPaths(IgnorePaths::Yes);
215
216   return true;
217 }
218
219 bool cmFindBase::Validate(const std::string& path) const
220 {
221   if (this->ValidatorName.empty()) {
222     return true;
223   }
224
225   // The validator command will be executed in an isolated scope.
226   cmMakefile::ScopePushPop varScope(this->Makefile);
227   cmMakefile::PolicyPushPop polScope(this->Makefile);
228   static_cast<void>(varScope);
229   static_cast<void>(polScope);
230
231   auto resultName =
232     cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName),
233              "_VALIDATOR_STATUS"_s);
234
235   this->Makefile->AddDefinitionBool(resultName, true);
236
237   cmListFileFunction validator(
238     this->ValidatorName, 0, 0,
239     { cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0),
240       cmListFileArgument(path, cmListFileArgument::Quoted, 0) });
241   cmExecutionStatus status(*this->Makefile);
242
243   if (this->Makefile->ExecuteCommand(validator, status)) {
244     return this->Makefile->GetDefinition(resultName).IsOn();
245   }
246   return false;
247 }
248
249 void cmFindBase::ExpandPaths()
250 {
251   if (!this->NoDefaultPath) {
252     if (!this->NoPackageRootPath) {
253       this->FillPackageRootPath();
254     }
255     if (!this->NoCMakePath) {
256       this->FillCMakeVariablePath();
257     }
258     if (!this->NoCMakeEnvironmentPath) {
259       this->FillCMakeEnvironmentPath();
260     }
261   }
262   this->FillUserHintsPath();
263   if (!this->NoDefaultPath) {
264     if (!this->NoSystemEnvironmentPath) {
265       this->FillSystemEnvironmentPath();
266     }
267     if (!this->NoCMakeSystemPath) {
268       this->FillCMakeSystemVariablePath();
269     }
270   }
271   this->FillUserGuessPath();
272 }
273
274 void cmFindBase::FillCMakeEnvironmentPath()
275 {
276   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeEnvironment];
277
278   // Add CMAKE_*_PATH environment variables
279   std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
280   paths.AddEnvPrefixPath("CMAKE_PREFIX_PATH");
281   paths.AddEnvPath(var);
282
283   if (this->CMakePathName == "PROGRAM") {
284     paths.AddEnvPath("CMAKE_APPBUNDLE_PATH");
285   } else {
286     paths.AddEnvPath("CMAKE_FRAMEWORK_PATH");
287   }
288   paths.AddSuffixes(this->SearchPathSuffixes);
289 }
290
291 void cmFindBase::FillPackageRootPath()
292 {
293   cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot];
294
295   // Add the PACKAGE_ROOT_PATH from each enclosing find_package call.
296   for (std::vector<std::string> const& pkgPaths :
297        cmReverseRange(this->Makefile->FindPackageRootPathStack)) {
298     paths.AddPrefixPaths(pkgPaths);
299   }
300
301   paths.AddSuffixes(this->SearchPathSuffixes);
302 }
303
304 void cmFindBase::FillCMakeVariablePath()
305 {
306   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMake];
307
308   // Add CMake variables of the same name as the previous environment
309   // variables CMAKE_*_PATH to be used most of the time with -D
310   // command line options
311   std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
312   paths.AddCMakePrefixPath("CMAKE_PREFIX_PATH");
313   paths.AddCMakePath(var);
314
315   if (this->CMakePathName == "PROGRAM") {
316     paths.AddCMakePath("CMAKE_APPBUNDLE_PATH");
317   } else {
318     paths.AddCMakePath("CMAKE_FRAMEWORK_PATH");
319   }
320   paths.AddSuffixes(this->SearchPathSuffixes);
321 }
322
323 void cmFindBase::FillSystemEnvironmentPath()
324 {
325   cmSearchPath& paths = this->LabeledPaths[PathLabel::SystemEnvironment];
326
327   // Add LIB or INCLUDE
328   if (!this->EnvironmentPath.empty()) {
329     paths.AddEnvPath(this->EnvironmentPath);
330 #if defined(_WIN32) || defined(__CYGWIN__)
331     paths.AddEnvPrefixPath("PATH", true);
332 #endif
333   }
334   // Add PATH
335   paths.AddEnvPath("PATH");
336   paths.AddSuffixes(this->SearchPathSuffixes);
337 }
338
339 namespace {
340 struct entry_to_remove
341 {
342   entry_to_remove(std::string const& name, cmMakefile* makefile)
343     : value()
344   {
345     if (cmValue to_skip = makefile->GetDefinition(
346           cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_COUNT"))) {
347       cmStrToLong(to_skip, &count);
348     }
349     if (cmValue prefix_value = makefile->GetDefinition(
350           cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_VALUE"))) {
351       value = *prefix_value;
352     }
353   }
354   bool valid() const { return count > 0 && !value.empty(); }
355
356   void remove_self(std::vector<std::string>& entries) const
357   {
358     if (this->valid()) {
359       long to_skip = this->count;
360       long index_to_remove = 0;
361       for (const auto& path : entries) {
362         if (path == this->value && --to_skip == 0) {
363           break;
364         }
365         ++index_to_remove;
366       }
367       entries.erase(entries.begin() + index_to_remove);
368     }
369   }
370
371   long count = -1;
372   std::string value;
373 };
374 }
375
376 void cmFindBase::FillCMakeSystemVariablePath()
377 {
378   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeSystem];
379
380   const bool install_prefix_in_list =
381     !this->Makefile->IsOn("CMAKE_FIND_NO_INSTALL_PREFIX");
382   const bool remove_install_prefix = this->NoCMakeInstallPath;
383   const bool add_install_prefix = !this->NoCMakeInstallPath &&
384     this->Makefile->IsDefinitionSet("CMAKE_FIND_USE_INSTALL_PREFIX");
385
386   // We have 3 possible states for `CMAKE_SYSTEM_PREFIX_PATH` and
387   // `CMAKE_INSTALL_PREFIX`.
388   // Either we need to remove `CMAKE_INSTALL_PREFIX`, add
389   // `CMAKE_INSTALL_PREFIX`, or do nothing.
390   //
391   // When we need to remove `CMAKE_INSTALL_PREFIX` we remove the Nth occurrence
392   // of `CMAKE_INSTALL_PREFIX` from `CMAKE_SYSTEM_PREFIX_PATH`, where `N` is
393   // computed by `CMakeSystemSpecificInformation.cmake` while constructing
394   // `CMAKE_SYSTEM_PREFIX_PATH`. This ensures that if projects / toolchains
395   // have removed `CMAKE_INSTALL_PREFIX` from the list, we don't remove
396   // some other entry by mistake ( likewise for `CMAKE_STAGING_PREFIX` )
397   entry_to_remove install_entry("INSTALL", this->Makefile);
398   entry_to_remove staging_entry("STAGING", this->Makefile);
399
400   if (remove_install_prefix && install_prefix_in_list &&
401       (install_entry.valid() || staging_entry.valid())) {
402     cmValue prefix_paths =
403       this->Makefile->GetDefinition("CMAKE_SYSTEM_PREFIX_PATH");
404
405     // remove entries from CMAKE_SYSTEM_PREFIX_PATH
406     std::vector<std::string> expanded = cmExpandedList(*prefix_paths);
407     install_entry.remove_self(expanded);
408     staging_entry.remove_self(expanded);
409
410     paths.AddPrefixPaths(expanded,
411                          this->Makefile->GetCurrentSourceDirectory().c_str());
412   } else if (add_install_prefix && !install_prefix_in_list) {
413     paths.AddCMakePrefixPath("CMAKE_INSTALL_PREFIX");
414     paths.AddCMakePrefixPath("CMAKE_STAGING_PREFIX");
415     paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
416   } else {
417     // Otherwise the current setup of `CMAKE_SYSTEM_PREFIX_PATH` is correct
418     paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
419   }
420
421   std::string var = cmStrCat("CMAKE_SYSTEM_", this->CMakePathName, "_PATH");
422   paths.AddCMakePath(var);
423
424   if (this->CMakePathName == "PROGRAM") {
425     paths.AddCMakePath("CMAKE_SYSTEM_APPBUNDLE_PATH");
426   } else {
427     paths.AddCMakePath("CMAKE_SYSTEM_FRAMEWORK_PATH");
428   }
429   paths.AddSuffixes(this->SearchPathSuffixes);
430 }
431
432 void cmFindBase::FillUserHintsPath()
433 {
434   cmSearchPath& paths = this->LabeledPaths[PathLabel::Hints];
435
436   for (std::string const& p : this->UserHintsArgs) {
437     paths.AddUserPath(p);
438   }
439   paths.AddSuffixes(this->SearchPathSuffixes);
440 }
441
442 void cmFindBase::FillUserGuessPath()
443 {
444   cmSearchPath& paths = this->LabeledPaths[PathLabel::Guess];
445
446   for (std::string const& p : this->UserGuessArgs) {
447     paths.AddUserPath(p);
448   }
449   paths.AddSuffixes(this->SearchPathSuffixes);
450 }
451
452 bool cmFindBase::CheckForVariableDefined()
453 {
454   if (cmValue value = this->Makefile->GetDefinition(this->VariableName)) {
455     cmState* state = this->Makefile->GetState();
456     cmValue cacheEntry = state->GetCacheEntryValue(this->VariableName);
457     bool found = !cmIsNOTFOUND(*value);
458     bool cached = cacheEntry != nullptr;
459     auto cacheType = cached ? state->GetCacheEntryType(this->VariableName)
460                             : cmStateEnums::UNINITIALIZED;
461
462     if (cached && cacheType != cmStateEnums::UNINITIALIZED) {
463       this->VariableType = cacheType;
464       if (const auto& hs =
465             state->GetCacheEntryProperty(this->VariableName, "HELPSTRING")) {
466         this->VariableDocumentation = *hs;
467       }
468     }
469
470     if (found) {
471       // If the user specifies the entry on the command line without a
472       // type we should add the type and docstring but keep the
473       // original value.  Tell the subclass implementations to do
474       // this.
475       if (cached && cacheType == cmStateEnums::UNINITIALIZED) {
476         this->AlreadyInCacheWithoutMetaInfo = true;
477       }
478       return true;
479     }
480   }
481   return false;
482 }
483
484 void cmFindBase::NormalizeFindResult()
485 {
486   if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) ==
487       cmPolicies::NEW) {
488     // ensure the path returned by find_* command is absolute
489     const auto& existingValue =
490       this->Makefile->GetDefinition(this->VariableName);
491     std::string value;
492     if (!existingValue->empty()) {
493       value =
494         cmCMakePath(*existingValue, cmCMakePath::auto_format)
495           .Absolute(cmCMakePath(
496             this->Makefile->GetCMakeInstance()->GetCMakeWorkingDirectory()))
497           .Normal()
498           .GenericString();
499       // value = cmSystemTools::CollapseFullPath(*existingValue);
500       if (!cmSystemTools::FileExists(value, false)) {
501         value = *existingValue;
502       }
503     }
504
505     if (this->StoreResultInCache) {
506       // If the user specifies the entry on the command line without a
507       // type we should add the type and docstring but keep the original
508       // value.
509       if (value != *existingValue || this->AlreadyInCacheWithoutMetaInfo) {
510         this->Makefile->GetCMakeInstance()->AddCacheEntry(
511           this->VariableName, value, this->VariableDocumentation.c_str(),
512           this->VariableType);
513         if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
514             cmPolicies::NEW) {
515           if (this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
516             this->Makefile->AddDefinition(this->VariableName, value);
517           }
518         } else {
519           // if there was a definition then remove it
520           // This is required to ensure same behavior as
521           // cmMakefile::AddCacheDefinition.
522           this->Makefile->RemoveDefinition(this->VariableName);
523         }
524       }
525     } else {
526       // ensure a normal variable is defined.
527       this->Makefile->AddDefinition(this->VariableName, value);
528     }
529   } else {
530     // If the user specifies the entry on the command line without a
531     // type we should add the type and docstring but keep the original
532     // value.
533     if (this->StoreResultInCache) {
534       if (this->AlreadyInCacheWithoutMetaInfo) {
535         this->Makefile->AddCacheDefinition(this->VariableName, "",
536                                            this->VariableDocumentation.c_str(),
537                                            this->VariableType);
538         if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
539               cmPolicies::NEW &&
540             this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
541           this->Makefile->AddDefinition(
542             this->VariableName,
543             *this->Makefile->GetCMakeInstance()->GetCacheDefinition(
544               this->VariableName));
545         }
546       }
547     } else {
548       // ensure a normal variable is defined.
549       this->Makefile->AddDefinition(
550         this->VariableName,
551         this->Makefile->GetSafeDefinition(this->VariableName));
552     }
553   }
554 }
555
556 void cmFindBase::StoreFindResult(const std::string& value)
557 {
558   bool force =
559     this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) == cmPolicies::NEW;
560   bool updateNormalVariable =
561     this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) == cmPolicies::NEW;
562
563   if (!value.empty()) {
564     if (this->StoreResultInCache) {
565       this->Makefile->AddCacheDefinition(this->VariableName, value,
566                                          this->VariableDocumentation.c_str(),
567                                          this->VariableType, force);
568       if (updateNormalVariable &&
569           this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
570         this->Makefile->AddDefinition(this->VariableName, value);
571       }
572     } else {
573       this->Makefile->AddDefinition(this->VariableName, value);
574     }
575
576     return;
577   }
578
579   auto notFound = cmStrCat(this->VariableName, "-NOTFOUND");
580   if (this->StoreResultInCache) {
581     this->Makefile->AddCacheDefinition(this->VariableName, notFound,
582                                        this->VariableDocumentation.c_str(),
583                                        this->VariableType, force);
584     if (updateNormalVariable &&
585         this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
586       this->Makefile->AddDefinition(this->VariableName, notFound);
587     }
588   } else {
589     this->Makefile->AddDefinition(this->VariableName, notFound);
590   }
591
592   if (this->Required) {
593     this->Makefile->IssueMessage(
594       MessageType::FATAL_ERROR,
595       cmStrCat("Could not find ", this->VariableName, " using the following ",
596                (this->FindCommandName == "find_file" ||
597                     this->FindCommandName == "find_path"
598                   ? "files"
599                   : "names"),
600                ": ", cmJoin(this->Names, ", ")));
601     cmSystemTools::SetFatalErrorOccurred();
602   }
603 }
604
605 cmFindBaseDebugState::cmFindBaseDebugState(std::string commandName,
606                                            cmFindBase const* findBase)
607   : FindCommand(findBase)
608   , CommandName(std::move(commandName))
609 {
610 }
611
612 cmFindBaseDebugState::~cmFindBaseDebugState()
613 {
614   if (this->FindCommand->DebugMode) {
615     std::string buffer =
616       cmStrCat(this->CommandName, " called with the following settings:\n");
617     buffer += cmStrCat("  VAR: ", this->FindCommand->VariableName, "\n");
618     buffer += cmStrCat(
619       "  NAMES: ", cmWrap("\"", this->FindCommand->Names, "\"", "\n         "),
620       "\n");
621     buffer += cmStrCat(
622       "  Documentation: ", this->FindCommand->VariableDocumentation, "\n");
623     buffer += "  Framework\n";
624     buffer += cmStrCat("    Only Search Frameworks: ",
625                        this->FindCommand->SearchFrameworkOnly, "\n");
626
627     buffer += cmStrCat("    Search Frameworks Last: ",
628                        this->FindCommand->SearchFrameworkLast, "\n");
629     buffer += cmStrCat("    Search Frameworks First: ",
630                        this->FindCommand->SearchFrameworkFirst, "\n");
631     buffer += "  AppBundle\n";
632     buffer += cmStrCat("    Only Search AppBundle: ",
633                        this->FindCommand->SearchAppBundleOnly, "\n");
634     buffer += cmStrCat("    Search AppBundle Last: ",
635                        this->FindCommand->SearchAppBundleLast, "\n");
636     buffer += cmStrCat("    Search AppBundle First: ",
637                        this->FindCommand->SearchAppBundleFirst, "\n");
638
639     if (this->FindCommand->NoDefaultPath) {
640       buffer += "  NO_DEFAULT_PATH Enabled\n";
641     } else {
642       buffer += cmStrCat(
643         "  CMAKE_FIND_USE_CMAKE_PATH: ", !this->FindCommand->NoCMakePath, "\n",
644         "  CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH: ",
645         !this->FindCommand->NoCMakeEnvironmentPath, "\n",
646         "  CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: ",
647         !this->FindCommand->NoSystemEnvironmentPath, "\n",
648         "  CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: ",
649         !this->FindCommand->NoCMakeSystemPath, "\n",
650         "  CMAKE_FIND_USE_INSTALL_PREFIX: ",
651         !this->FindCommand->NoCMakeInstallPath, "\n");
652     }
653
654     buffer +=
655       cmStrCat(this->CommandName, " considered the following locations:\n");
656     for (auto const& state : this->FailedSearchLocations) {
657       std::string path = cmStrCat("  ", state.path);
658       if (!state.regexName.empty()) {
659         path = cmStrCat(path, "/", state.regexName);
660       }
661       buffer += cmStrCat(path, "\n");
662     }
663
664     if (!this->FoundSearchLocation.path.empty()) {
665       buffer += cmStrCat("The item was found at\n  ",
666                          this->FoundSearchLocation.path, "\n");
667     } else {
668       buffer += "The item was not found.\n";
669     }
670
671     this->FindCommand->DebugMessage(buffer);
672   }
673 }
674
675 void cmFindBaseDebugState::FoundAt(std::string const& path,
676                                    std::string regexName)
677 {
678   if (this->FindCommand->DebugMode) {
679     this->FoundSearchLocation = DebugLibState{ std::move(regexName), path };
680   }
681 }
682
683 void cmFindBaseDebugState::FailedAt(std::string const& path,
684                                     std::string regexName)
685 {
686   if (this->FindCommand->DebugMode) {
687     this->FailedSearchLocations.emplace_back(std::move(regexName), path);
688   }
689 }