resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmake.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 "cmake.h"
4
5 #include <algorithm>
6 #include <array>
7 #include <cstdio>
8 #include <cstdlib>
9 #include <cstring>
10 #include <initializer_list>
11 #include <iostream>
12 #include <sstream>
13 #include <stdexcept>
14 #include <utility>
15
16 #include <cm/memory>
17 #include <cm/optional>
18 #include <cm/string_view>
19 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
20 #  include <cm/iterator>
21 #endif
22
23 #include <cmext/algorithm>
24 #include <cmext/string_view>
25
26 #if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32)
27 #  include <unistd.h>
28 #endif
29
30 #include "cmsys/FStream.hxx"
31 #include "cmsys/Glob.hxx"
32 #include "cmsys/RegularExpression.hxx"
33
34 #include "cm_sys_stat.h"
35
36 #include "cmBuildOptions.h"
37 #include "cmCMakePath.h"
38 #include "cmCMakePresetsGraph.h"
39 #include "cmCommandLineArgument.h"
40 #include "cmCommands.h"
41 #include "cmDocumentation.h"
42 #include "cmDocumentationEntry.h"
43 #include "cmDocumentationFormatter.h"
44 #include "cmDuration.h"
45 #include "cmExternalMakefileProjectGenerator.h"
46 #include "cmFileTimeCache.h"
47 #include "cmGeneratorTarget.h"
48 #include "cmGlobalGenerator.h"
49 #include "cmGlobalGeneratorFactory.h"
50 #include "cmLinkLineComputer.h"
51 #include "cmLocalGenerator.h"
52 #include "cmMakefile.h"
53 #if !defined(CMAKE_BOOTSTRAP)
54 #  include "cmMakefileProfilingData.h"
55 #endif
56 #include "cmMessenger.h"
57 #include "cmState.h"
58 #include "cmStateDirectory.h"
59 #include "cmStringAlgorithms.h"
60 #include "cmSystemTools.h"
61 #include "cmTarget.h"
62 #include "cmTargetLinkLibraryType.h"
63 #include "cmUVProcessChain.h"
64 #include "cmUtils.hxx"
65 #include "cmVersionConfig.h"
66 #include "cmWorkingDirectory.h"
67
68 #if !defined(CMAKE_BOOTSTRAP)
69 #  include <unordered_map>
70
71 #  include <cm3p/curl/curl.h>
72 #  include <cm3p/json/writer.h>
73
74 #  include "cmFileAPI.h"
75 #  include "cmGraphVizWriter.h"
76 #  include "cmVariableWatch.h"
77 #endif
78
79 #if defined(__MINGW32__) && defined(CMAKE_BOOTSTRAP)
80 #  define CMAKE_BOOT_MINGW
81 #endif
82
83 // include the generator
84 #if defined(_WIN32) && !defined(__CYGWIN__)
85 #  if !defined(CMAKE_BOOT_MINGW)
86 #    include <cmext/memory>
87
88 #    include "cmGlobalBorlandMakefileGenerator.h"
89 #    include "cmGlobalJOMMakefileGenerator.h"
90 #    include "cmGlobalNMakeMakefileGenerator.h"
91 #    include "cmGlobalVisualStudio11Generator.h"
92 #    include "cmGlobalVisualStudio12Generator.h"
93 #    include "cmGlobalVisualStudio14Generator.h"
94 #    include "cmGlobalVisualStudio9Generator.h"
95 #    include "cmGlobalVisualStudioVersionedGenerator.h"
96 #    include "cmVSSetupHelper.h"
97
98 #    define CMAKE_HAVE_VS_GENERATORS
99 #  endif
100 #  include "cmGlobalMSYSMakefileGenerator.h"
101 #  include "cmGlobalMinGWMakefileGenerator.h"
102 #else
103 #endif
104 #if defined(CMAKE_USE_WMAKE)
105 #  include "cmGlobalWatcomWMakeGenerator.h"
106 #endif
107 #if !defined(CMAKE_BOOTSTRAP)
108 #  include "cmGlobalNinjaGenerator.h"
109 #  include "cmGlobalUnixMakefileGenerator3.h"
110 #elif defined(CMAKE_BOOTSTRAP_MAKEFILES)
111 #  include "cmGlobalUnixMakefileGenerator3.h"
112 #elif defined(CMAKE_BOOTSTRAP_NINJA)
113 #  include "cmGlobalNinjaGenerator.h"
114 #endif
115
116 #if !defined(CMAKE_BOOTSTRAP)
117 #  include "cmExtraCodeBlocksGenerator.h"
118 #  include "cmExtraCodeLiteGenerator.h"
119 #  include "cmExtraEclipseCDT4Generator.h"
120 #  include "cmExtraKateGenerator.h"
121 #  include "cmExtraSublimeTextGenerator.h"
122 #endif
123
124 // NOTE: the __linux__ macro is predefined on Android host too, but
125 // main CMakeLists.txt filters out this generator by host name.
126 #if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
127 #  include "cmGlobalGhsMultiGenerator.h"
128 #endif
129
130 #if defined(__APPLE__)
131 #  if !defined(CMAKE_BOOTSTRAP)
132 #    include "cmGlobalXCodeGenerator.h"
133
134 #    define CMAKE_USE_XCODE 1
135 #  endif
136 #  include <sys/resource.h>
137 #  include <sys/time.h>
138 #endif
139
140 namespace {
141
142 #if !defined(CMAKE_BOOTSTRAP)
143 using JsonValueMapType = std::unordered_map<std::string, Json::Value>;
144 #endif
145
146 auto IgnoreAndTrueLambda = [](std::string const&, cmake*) -> bool {
147   return true;
148 };
149
150 using CommandArgument =
151   cmCommandLineArgument<bool(std::string const& value, cmake* state)>;
152
153 } // namespace
154
155 static bool cmakeCheckStampFile(const std::string& stampName);
156 static bool cmakeCheckStampList(const std::string& stampList);
157
158 #ifndef CMAKE_BOOTSTRAP
159 static void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/,
160                                    void* ctx, const char* /*unused*/,
161                                    const cmMakefile* /*unused*/)
162 {
163   cmake* cm = reinterpret_cast<cmake*>(ctx);
164   cm->MarkCliAsUsed(variable);
165 }
166 #endif
167
168 cmake::cmake(Role role, cmState::Mode mode, cmState::ProjectKind projectKind)
169   : CMakeWorkingDirectory(cmSystemTools::GetCurrentWorkingDirectory())
170   , FileTimeCache(cm::make_unique<cmFileTimeCache>())
171 #ifndef CMAKE_BOOTSTRAP
172   , VariableWatch(cm::make_unique<cmVariableWatch>())
173 #endif
174   , State(cm::make_unique<cmState>(mode, projectKind))
175   , Messenger(cm::make_unique<cmMessenger>())
176 {
177   this->TraceFile.close();
178   this->CurrentSnapshot = this->State->CreateBaseSnapshot();
179
180 #ifdef __APPLE__
181   struct rlimit rlp;
182   if (!getrlimit(RLIMIT_STACK, &rlp)) {
183     if (rlp.rlim_cur != rlp.rlim_max) {
184       rlp.rlim_cur = rlp.rlim_max;
185       setrlimit(RLIMIT_STACK, &rlp);
186     }
187   }
188 #endif
189
190   this->AddDefaultGenerators();
191   this->AddDefaultExtraGenerators();
192   if (role == RoleScript || role == RoleProject) {
193     this->AddScriptingCommands();
194   }
195   if (role == RoleProject) {
196     this->AddProjectCommands();
197   }
198
199   if (mode == cmState::Project) {
200     this->LoadEnvironmentPresets();
201   }
202
203   // Make sure we can capture the build tool output.
204   cmSystemTools::EnableVSConsoleOutput();
205
206   // Set up a list of source and header extensions.
207   // These are used to find files when the extension is not given.
208   {
209     auto setupExts = [](FileExtensions& exts,
210                         std::initializer_list<cm::string_view> extList) {
211       // Fill ordered vector
212       exts.ordered.reserve(extList.size());
213       for (cm::string_view ext : extList) {
214         exts.ordered.emplace_back(ext);
215       }
216       // Fill unordered set
217       exts.unordered.insert(exts.ordered.begin(), exts.ordered.end());
218     };
219
220     // The "c" extension MUST precede the "C" extension.
221     setupExts(this->CLikeSourceFileExtensions,
222               { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "mpp", "m", "M",
223                 "mm", "ixx", "cppm" });
224     setupExts(this->HeaderFileExtensions,
225               { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
226     setupExts(this->CudaFileExtensions, { "cu" });
227     setupExts(this->FortranFileExtensions,
228               { "f", "F", "for", "f77", "f90", "f95", "f03" });
229     setupExts(this->HipFileExtensions, { "hip" });
230     setupExts(this->ISPCFileExtensions, { "ispc" });
231   }
232 }
233
234 cmake::~cmake() = default;
235
236 #if !defined(CMAKE_BOOTSTRAP)
237 Json::Value cmake::ReportVersionJson() const
238 {
239   Json::Value version = Json::objectValue;
240   version["string"] = CMake_VERSION;
241   version["major"] = CMake_VERSION_MAJOR;
242   version["minor"] = CMake_VERSION_MINOR;
243   version["suffix"] = CMake_VERSION_SUFFIX;
244   version["isDirty"] = (CMake_VERSION_IS_DIRTY == 1);
245   version["patch"] = CMake_VERSION_PATCH;
246   return version;
247 }
248
249 Json::Value cmake::ReportCapabilitiesJson() const
250 {
251   Json::Value obj = Json::objectValue;
252
253   // Version information:
254   obj["version"] = this->ReportVersionJson();
255
256   // Generators:
257   std::vector<cmake::GeneratorInfo> generatorInfoList;
258   this->GetRegisteredGenerators(generatorInfoList);
259
260   auto* curlVersion = curl_version_info(CURLVERSION_FIRST);
261
262   JsonValueMapType generatorMap;
263   for (cmake::GeneratorInfo const& gi : generatorInfoList) {
264     if (gi.isAlias) { // skip aliases, they are there for compatibility reasons
265                       // only
266       continue;
267     }
268
269     if (gi.extraName.empty()) {
270       Json::Value gen = Json::objectValue;
271       gen["name"] = gi.name;
272       gen["toolsetSupport"] = gi.supportsToolset;
273       gen["platformSupport"] = gi.supportsPlatform;
274       if (!gi.supportedPlatforms.empty()) {
275         Json::Value supportedPlatforms = Json::arrayValue;
276         for (std::string const& platform : gi.supportedPlatforms) {
277           supportedPlatforms.append(platform);
278         }
279         gen["supportedPlatforms"] = std::move(supportedPlatforms);
280       }
281       gen["extraGenerators"] = Json::arrayValue;
282       generatorMap[gi.name] = gen;
283     } else {
284       Json::Value& gen = generatorMap[gi.baseName];
285       gen["extraGenerators"].append(gi.extraName);
286     }
287   }
288
289   Json::Value generators = Json::arrayValue;
290   for (auto const& i : generatorMap) {
291     generators.append(i.second);
292   }
293   obj["generators"] = generators;
294   obj["fileApi"] = cmFileAPI::ReportCapabilities();
295   obj["serverMode"] = false;
296   obj["tls"] = static_cast<bool>(curlVersion->features & CURL_VERSION_SSL);
297
298   return obj;
299 }
300 #endif
301
302 std::string cmake::ReportCapabilities() const
303 {
304   std::string result;
305 #if !defined(CMAKE_BOOTSTRAP)
306   Json::FastWriter writer;
307   result = writer.write(this->ReportCapabilitiesJson());
308 #else
309   result = "Not supported";
310 #endif
311   return result;
312 }
313
314 void cmake::CleanupCommandsAndMacros()
315 {
316   this->CurrentSnapshot = this->State->Reset();
317   this->State->RemoveUserDefinedCommands();
318   this->CurrentSnapshot.SetDefaultDefinitions();
319   // FIXME: InstalledFiles probably belongs in the global generator.
320   this->InstalledFiles.clear();
321 }
322
323 #ifndef CMAKE_BOOTSTRAP
324 void cmake::SetWarningFromPreset(const std::string& name,
325                                  const cm::optional<bool>& warning,
326                                  const cm::optional<bool>& error)
327 {
328   if (warning) {
329     if (*warning) {
330       this->DiagLevels[name] = std::max(this->DiagLevels[name], DIAG_WARN);
331     } else {
332       this->DiagLevels[name] = DIAG_IGNORE;
333     }
334   }
335   if (error) {
336     if (*error) {
337       this->DiagLevels[name] = DIAG_ERROR;
338     } else {
339       this->DiagLevels[name] = std::min(this->DiagLevels[name], DIAG_WARN);
340     }
341   }
342 }
343
344 void cmake::ProcessPresetVariables()
345 {
346   for (auto const& var : this->UnprocessedPresetVariables) {
347     if (!var.second) {
348       continue;
349     }
350     cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
351     if (!var.second->Type.empty()) {
352       type = cmState::StringToCacheEntryType(var.second->Type);
353     }
354     this->ProcessCacheArg(var.first, var.second->Value, type);
355   }
356 }
357
358 void cmake::PrintPresetVariables()
359 {
360   bool first = true;
361   for (auto const& var : this->UnprocessedPresetVariables) {
362     if (!var.second) {
363       continue;
364     }
365     cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
366     if (!var.second->Type.empty()) {
367       type = cmState::StringToCacheEntryType(var.second->Type);
368     }
369     if (first) {
370       std::cout << "Preset CMake variables:\n\n";
371       first = false;
372     }
373     std::cout << "  " << var.first;
374     if (type != cmStateEnums::UNINITIALIZED) {
375       std::cout << ':' << cmState::CacheEntryTypeToString(type);
376     }
377     std::cout << "=\"" << var.second->Value << "\"\n";
378   }
379   if (!first) {
380     std::cout << '\n';
381   }
382   this->UnprocessedPresetVariables.clear();
383 }
384
385 void cmake::ProcessPresetEnvironment()
386 {
387   for (auto const& var : this->UnprocessedPresetEnvironment) {
388     if (var.second) {
389       cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
390     }
391   }
392 }
393
394 void cmake::PrintPresetEnvironment()
395 {
396   bool first = true;
397   for (auto const& var : this->UnprocessedPresetEnvironment) {
398     if (!var.second) {
399       continue;
400     }
401     if (first) {
402       std::cout << "Preset environment variables:\n\n";
403       first = false;
404     }
405     std::cout << "  " << var.first << "=\"" << *var.second << "\"\n";
406   }
407   if (!first) {
408     std::cout << '\n';
409   }
410   this->UnprocessedPresetEnvironment.clear();
411 }
412 #endif
413
414 // Parse the args
415 bool cmake::SetCacheArgs(const std::vector<std::string>& args)
416 {
417   auto DefineLambda = [](std::string const& entry, cmake* state) -> bool {
418     std::string var;
419     std::string value;
420     cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
421     if (cmState::ParseCacheEntry(entry, var, value, type)) {
422 #ifndef CMAKE_BOOTSTRAP
423       state->UnprocessedPresetVariables.erase(var);
424 #endif
425       state->ProcessCacheArg(var, value, type);
426     } else {
427       cmSystemTools::Error(cmStrCat("Parse error in command line argument: ",
428                                     entry, "\n Should be: VAR:type=value\n"));
429       return false;
430     }
431     return true;
432   };
433
434   auto WarningLambda = [](cm::string_view entry, cmake* state) -> bool {
435     bool foundNo = false;
436     bool foundError = false;
437
438     if (cmHasLiteralPrefix(entry, "no-")) {
439       foundNo = true;
440       entry.remove_prefix(3);
441     }
442
443     if (cmHasLiteralPrefix(entry, "error=")) {
444       foundError = true;
445       entry.remove_prefix(6);
446     }
447
448     if (entry.empty()) {
449       cmSystemTools::Error("No warning name provided.");
450       return false;
451     }
452
453     std::string const name = std::string(entry);
454     if (!foundNo && !foundError) {
455       // -W<name>
456       state->DiagLevels[name] = std::max(state->DiagLevels[name], DIAG_WARN);
457     } else if (foundNo && !foundError) {
458       // -Wno<name>
459       state->DiagLevels[name] = DIAG_IGNORE;
460     } else if (!foundNo && foundError) {
461       // -Werror=<name>
462       state->DiagLevels[name] = DIAG_ERROR;
463     } else {
464       // -Wno-error=<name>
465       // This can downgrade an error to a warning, but should not enable
466       // or disable a warning in the first place.
467       auto dli = state->DiagLevels.find(name);
468       if (dli != state->DiagLevels.end()) {
469         dli->second = std::min(dli->second, DIAG_WARN);
470       }
471     }
472     return true;
473   };
474
475   auto UnSetLambda = [](std::string const& entryPattern,
476                         cmake* state) -> bool {
477     cmsys::RegularExpression regex(
478       cmsys::Glob::PatternToRegex(entryPattern, true, true));
479     // go through all cache entries and collect the vars which will be
480     // removed
481     std::vector<std::string> entriesToDelete;
482     std::vector<std::string> cacheKeys = state->State->GetCacheEntryKeys();
483     for (std::string const& ck : cacheKeys) {
484       cmStateEnums::CacheEntryType t = state->State->GetCacheEntryType(ck);
485       if (t != cmStateEnums::STATIC) {
486         if (regex.find(ck)) {
487           entriesToDelete.push_back(ck);
488         }
489       }
490     }
491
492     // now remove them from the cache
493     for (std::string const& currentEntry : entriesToDelete) {
494 #ifndef CMAKE_BOOTSTRAP
495       state->UnprocessedPresetVariables.erase(currentEntry);
496 #endif
497       state->State->RemoveCacheEntry(currentEntry);
498     }
499     return true;
500   };
501
502   auto ScriptLambda = [&](std::string const& path, cmake* state) -> bool {
503     // Register fake project commands that hint misuse in script mode.
504     GetProjectCommandsInScriptMode(state->GetState());
505     // Documented behavior of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be
506     // set to $PWD for -P mode.
507     state->SetWorkingMode(SCRIPT_MODE);
508     state->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
509     state->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
510     state->ReadListFile(args, path);
511     return true;
512   };
513
514   auto PrefixLambda = [&](std::string const& path, cmake* state) -> bool {
515     const std::string var = "CMAKE_INSTALL_PREFIX";
516     cmStateEnums::CacheEntryType type = cmStateEnums::PATH;
517     cmCMakePath absolutePath(path);
518     if (absolutePath.IsAbsolute()) {
519 #ifndef CMAKE_BOOTSTRAP
520       state->UnprocessedPresetVariables.erase(var);
521 #endif
522       state->ProcessCacheArg(var, path, type);
523       return true;
524     }
525     cmSystemTools::Error("Absolute paths are required for --install-prefix");
526     return false;
527   };
528
529   auto ToolchainLambda = [&](std::string const& path, cmake* state) -> bool {
530     const std::string var = "CMAKE_TOOLCHAIN_FILE";
531     cmStateEnums::CacheEntryType type = cmStateEnums::FILEPATH;
532 #ifndef CMAKE_BOOTSTRAP
533     state->UnprocessedPresetVariables.erase(var);
534 #endif
535     state->ProcessCacheArg(var, path, type);
536     return true;
537   };
538
539   std::vector<CommandArgument> arguments = {
540     CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
541                      CommandArgument::Values::One,
542                      CommandArgument::RequiresSeparator::No, DefineLambda },
543     CommandArgument{ "-W", "-W must be followed with [no-]<name>.",
544                      CommandArgument::Values::One,
545                      CommandArgument::RequiresSeparator::No, WarningLambda },
546     CommandArgument{ "-U", "-U must be followed with VAR.",
547                      CommandArgument::Values::One,
548                      CommandArgument::RequiresSeparator::No, UnSetLambda },
549     CommandArgument{
550       "-C", "-C must be followed by a file name.",
551       CommandArgument::Values::One, CommandArgument::RequiresSeparator::No,
552       [&](std::string const& value, cmake* state) -> bool {
553         if (value.empty()) {
554           cmSystemTools::Error("No file name specified for -C");
555           return false;
556         }
557         cmSystemTools::Stdout("loading initial cache file " + value + "\n");
558         // Resolve script path specified on command line
559         // relative to $PWD.
560         auto path = cmSystemTools::CollapseFullPath(value);
561         state->ReadListFile(args, path);
562         return true;
563       } },
564
565     CommandArgument{ "-P", "-P must be followed by a file name.",
566                      CommandArgument::Values::One,
567                      CommandArgument::RequiresSeparator::No, ScriptLambda },
568     CommandArgument{ "--toolchain", "No file specified for --toolchain",
569                      CommandArgument::Values::One, ToolchainLambda },
570     CommandArgument{ "--install-prefix",
571                      "No install directory specified for --install-prefix",
572                      CommandArgument::Values::One, PrefixLambda },
573     CommandArgument{ "--find-package", CommandArgument::Values::Zero,
574                      IgnoreAndTrueLambda },
575   };
576   for (decltype(args.size()) i = 1; i < args.size(); ++i) {
577     std::string const& arg = args[i];
578
579     if (arg == "--" && this->GetWorkingMode() == SCRIPT_MODE) {
580       // Stop processing CMake args and avoid possible errors
581       // when arbitrary args are given to CMake script.
582       break;
583     }
584     for (auto const& m : arguments) {
585       if (m.matches(arg)) {
586         const bool parsedCorrectly = m.parse(arg, i, args, this);
587         if (!parsedCorrectly) {
588           return false;
589         }
590       }
591     }
592   }
593
594   if (this->GetWorkingMode() == FIND_PACKAGE_MODE) {
595     return this->FindPackage(args);
596   }
597
598   return true;
599 }
600
601 void cmake::ProcessCacheArg(const std::string& var, const std::string& value,
602                             cmStateEnums::CacheEntryType type)
603 {
604   // The value is transformed if it is a filepath for example, so
605   // we can't compare whether the value is already in the cache until
606   // after we call AddCacheEntry.
607   bool haveValue = false;
608   std::string cachedValue;
609   if (this->WarnUnusedCli) {
610     if (cmValue v = this->State->GetInitializedCacheValue(var)) {
611       haveValue = true;
612       cachedValue = *v;
613     }
614   }
615
616   this->AddCacheEntry(
617     var, value, "No help, variable specified on the command line.", type);
618
619   if (this->WarnUnusedCli) {
620     if (!haveValue ||
621         cachedValue != *this->State->GetInitializedCacheValue(var)) {
622       this->WatchUnusedCli(var);
623     }
624   }
625 }
626
627 void cmake::ReadListFile(const std::vector<std::string>& args,
628                          const std::string& path)
629 {
630   // if a generator was not yet created, temporarily create one
631   cmGlobalGenerator* gg = this->GetGlobalGenerator();
632
633   // if a generator was not specified use a generic one
634   std::unique_ptr<cmGlobalGenerator> gen;
635   if (!gg) {
636     gen = cm::make_unique<cmGlobalGenerator>(this);
637     gg = gen.get();
638   }
639
640   // read in the list file to fill the cache
641   if (!path.empty()) {
642     this->CurrentSnapshot = this->State->Reset();
643     cmStateSnapshot snapshot = this->GetCurrentSnapshot();
644     snapshot.GetDirectory().SetCurrentBinary(this->GetHomeOutputDirectory());
645     snapshot.GetDirectory().SetCurrentSource(this->GetHomeDirectory());
646     snapshot.SetDefaultDefinitions();
647     cmMakefile mf(gg, snapshot);
648     if (this->GetWorkingMode() != NORMAL_MODE) {
649       std::string file(cmSystemTools::CollapseFullPath(path));
650       cmSystemTools::ConvertToUnixSlashes(file);
651       mf.SetScriptModeFile(file);
652
653       mf.SetArgcArgv(args);
654     }
655     if (!mf.ReadListFile(path)) {
656       cmSystemTools::Error("Error processing file: " + path);
657     }
658   }
659 }
660
661 bool cmake::FindPackage(const std::vector<std::string>& args)
662 {
663   this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
664   this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
665
666   this->SetGlobalGenerator(cm::make_unique<cmGlobalGenerator>(this));
667
668   cmStateSnapshot snapshot = this->GetCurrentSnapshot();
669   snapshot.GetDirectory().SetCurrentBinary(
670     cmSystemTools::GetCurrentWorkingDirectory());
671   snapshot.GetDirectory().SetCurrentSource(
672     cmSystemTools::GetCurrentWorkingDirectory());
673   // read in the list file to fill the cache
674   snapshot.SetDefaultDefinitions();
675   auto mfu = cm::make_unique<cmMakefile>(this->GetGlobalGenerator(), snapshot);
676   cmMakefile* mf = mfu.get();
677   this->GlobalGenerator->AddMakefile(std::move(mfu));
678
679   mf->SetArgcArgv(args);
680
681   std::string systemFile = mf->GetModulesFile("CMakeFindPackageMode.cmake");
682   mf->ReadListFile(systemFile);
683
684   std::string language = mf->GetSafeDefinition("LANGUAGE");
685   std::string mode = mf->GetSafeDefinition("MODE");
686   std::string packageName = mf->GetSafeDefinition("NAME");
687   bool packageFound = mf->IsOn("PACKAGE_FOUND");
688   bool quiet = mf->IsOn("PACKAGE_QUIET");
689
690   if (!packageFound) {
691     if (!quiet) {
692       printf("%s not found.\n", packageName.c_str());
693     }
694   } else if (mode == "EXIST"_s) {
695     if (!quiet) {
696       printf("%s found.\n", packageName.c_str());
697     }
698   } else if (mode == "COMPILE"_s) {
699     std::string includes = mf->GetSafeDefinition("PACKAGE_INCLUDE_DIRS");
700     std::vector<std::string> includeDirs = cmExpandedList(includes);
701
702     this->GlobalGenerator->CreateGenerationObjects();
703     const auto& lg = this->GlobalGenerator->LocalGenerators[0];
704     std::string includeFlags =
705       lg->GetIncludeFlags(includeDirs, nullptr, language, std::string());
706
707     std::string definitions = mf->GetSafeDefinition("PACKAGE_DEFINITIONS");
708     printf("%s %s\n", includeFlags.c_str(), definitions.c_str());
709   } else if (mode == "LINK"_s) {
710     const char* targetName = "dummy";
711     std::vector<std::string> srcs;
712     cmTarget* tgt = mf->AddExecutable(targetName, srcs, true);
713     tgt->SetProperty("LINKER_LANGUAGE", language);
714
715     std::string libs = mf->GetSafeDefinition("PACKAGE_LIBRARIES");
716     std::vector<std::string> libList = cmExpandedList(libs);
717     for (std::string const& lib : libList) {
718       tgt->AddLinkLibrary(*mf, lib, GENERAL_LibraryType);
719     }
720
721     std::string buildType = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
722     buildType = cmSystemTools::UpperCase(buildType);
723
724     std::string linkLibs;
725     std::string frameworkPath;
726     std::string linkPath;
727     std::string flags;
728     std::string linkFlags;
729     this->GlobalGenerator->CreateGenerationObjects();
730     cmGeneratorTarget* gtgt =
731       this->GlobalGenerator->FindGeneratorTarget(tgt->GetName());
732     cmLocalGenerator* lg = gtgt->GetLocalGenerator();
733     cmLinkLineComputer linkLineComputer(lg,
734                                         lg->GetStateSnapshot().GetDirectory());
735     lg->GetTargetFlags(&linkLineComputer, buildType, linkLibs, flags,
736                        linkFlags, frameworkPath, linkPath, gtgt);
737     linkLibs = frameworkPath + linkPath + linkLibs;
738
739     printf("%s\n", linkLibs.c_str());
740
741     /*    if ( use_win32 )
742           {
743           tgt->SetProperty("WIN32_EXECUTABLE", "ON");
744           }
745         if ( use_macbundle)
746           {
747           tgt->SetProperty("MACOSX_BUNDLE", "ON");
748           }*/
749   }
750
751   return packageFound;
752 }
753
754 void cmake::LoadEnvironmentPresets()
755 {
756   std::string envGenVar;
757   bool hasEnvironmentGenerator = false;
758   if (cmSystemTools::GetEnv("CMAKE_GENERATOR", envGenVar)) {
759     hasEnvironmentGenerator = true;
760     this->EnvironmentGenerator = envGenVar;
761   }
762
763   auto readGeneratorVar = [&](std::string const& name, std::string& key) {
764     std::string varValue;
765     if (cmSystemTools::GetEnv(name, varValue)) {
766       if (hasEnvironmentGenerator) {
767         key = varValue;
768       } else if (!this->GetIsInTryCompile()) {
769         std::string message =
770           cmStrCat("Warning: Environment variable ", name,
771                    " will be ignored, because CMAKE_GENERATOR is not set.");
772         cmSystemTools::Message(message, "Warning");
773       }
774     }
775   };
776
777   readGeneratorVar("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance);
778   readGeneratorVar("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform);
779   readGeneratorVar("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset);
780 }
781
782 namespace {
783 enum class ListPresets
784 {
785   None,
786   Configure,
787   Build,
788   Test,
789   Package,
790   Workflow,
791   All,
792 };
793 }
794
795 // Parse the args
796 void cmake::SetArgs(const std::vector<std::string>& args)
797 {
798   bool haveToolset = false;
799   bool havePlatform = false;
800   bool haveBArg = false;
801   std::string possibleUnknownArg;
802   std::string extraProvidedPath;
803 #if !defined(CMAKE_BOOTSTRAP)
804   std::string profilingFormat;
805   std::string profilingOutput;
806   std::string presetName;
807
808   ListPresets listPresets = ListPresets::None;
809 #endif
810
811   auto EmptyStringArgLambda = [](std::string const&, cmake* state) -> bool {
812     state->IssueMessage(
813       MessageType::WARNING,
814       "Ignoring empty string (\"\") provided on the command line.");
815     return true;
816   };
817
818   auto SourceArgLambda = [](std::string const& value, cmake* state) -> bool {
819     if (value.empty()) {
820       cmSystemTools::Error("No source directory specified for -S");
821       return false;
822     }
823     std::string path = cmSystemTools::CollapseFullPath(value);
824     cmSystemTools::ConvertToUnixSlashes(path);
825
826     state->SetHomeDirectoryViaCommandLine(path);
827     return true;
828   };
829
830   auto BuildArgLambda = [&](std::string const& value, cmake* state) -> bool {
831     if (value.empty()) {
832       cmSystemTools::Error("No build directory specified for -B");
833       return false;
834     }
835     std::string path = cmSystemTools::CollapseFullPath(value);
836     cmSystemTools::ConvertToUnixSlashes(path);
837     state->SetHomeOutputDirectory(path);
838     haveBArg = true;
839     return true;
840   };
841
842   auto PlatformLambda = [&](std::string const& value, cmake* state) -> bool {
843     if (havePlatform) {
844       cmSystemTools::Error("Multiple -A options not allowed");
845       return false;
846     }
847     state->SetGeneratorPlatform(value);
848     havePlatform = true;
849     return true;
850   };
851
852   auto ToolsetLamda = [&](std::string const& value, cmake* state) -> bool {
853     if (haveToolset) {
854       cmSystemTools::Error("Multiple -T options not allowed");
855       return false;
856     }
857     state->SetGeneratorToolset(value);
858     haveToolset = true;
859     return true;
860   };
861
862   std::vector<CommandArgument> arguments = {
863     CommandArgument{ "", CommandArgument::Values::Zero, EmptyStringArgLambda },
864     CommandArgument{ "-S", "No source directory specified for -S",
865                      CommandArgument::Values::One,
866                      CommandArgument::RequiresSeparator::No, SourceArgLambda },
867     CommandArgument{ "-H", "No source directory specified for -H",
868                      CommandArgument::Values::One,
869                      CommandArgument::RequiresSeparator::No, SourceArgLambda },
870     CommandArgument{ "-O", CommandArgument::Values::Zero,
871                      IgnoreAndTrueLambda },
872     CommandArgument{ "-B", "No build directory specified for -B",
873                      CommandArgument::Values::One,
874                      CommandArgument::RequiresSeparator::No, BuildArgLambda },
875     CommandArgument{ "--fresh", CommandArgument::Values::Zero,
876                      [](std::string const&, cmake* cm) -> bool {
877                        cm->FreshCache = true;
878                        return true;
879                      } },
880     CommandArgument{ "-P", "-P must be followed by a file name.",
881                      CommandArgument::Values::One,
882                      CommandArgument::RequiresSeparator::No,
883                      IgnoreAndTrueLambda },
884     CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
885                      CommandArgument::Values::One,
886                      CommandArgument::RequiresSeparator::No,
887                      IgnoreAndTrueLambda },
888     CommandArgument{ "-C", "-C must be followed by a file name.",
889                      CommandArgument::Values::One,
890                      CommandArgument::RequiresSeparator::No,
891                      IgnoreAndTrueLambda },
892     CommandArgument{
893       "-U", "-U must be followed with VAR.", CommandArgument::Values::One,
894       CommandArgument::RequiresSeparator::No, IgnoreAndTrueLambda },
895     CommandArgument{ "-W", "-W must be followed with [no-]<name>.",
896                      CommandArgument::Values::One,
897                      CommandArgument::RequiresSeparator::No,
898                      IgnoreAndTrueLambda },
899     CommandArgument{ "-A", "No platform specified for -A",
900                      CommandArgument::Values::One,
901                      CommandArgument::RequiresSeparator::No, PlatformLambda },
902     CommandArgument{ "-T", "No toolset specified for -T",
903                      CommandArgument::Values::One,
904                      CommandArgument::RequiresSeparator::No, ToolsetLamda },
905     CommandArgument{ "--toolchain", "No file specified for --toolchain",
906                      CommandArgument::Values::One, IgnoreAndTrueLambda },
907     CommandArgument{ "--install-prefix",
908                      "No install directory specified for --install-prefix",
909                      CommandArgument::Values::One, IgnoreAndTrueLambda },
910
911     CommandArgument{ "--check-build-system", CommandArgument::Values::Two,
912                      [](std::string const& value, cmake* state) -> bool {
913                        std::vector<std::string> values = cmExpandedList(value);
914                        state->CheckBuildSystemArgument = values[0];
915                        state->ClearBuildSystem = (atoi(values[1].c_str()) > 0);
916                        return true;
917                      } },
918     CommandArgument{ "--check-stamp-file", CommandArgument::Values::One,
919                      [](std::string const& value, cmake* state) -> bool {
920                        state->CheckStampFile = value;
921                        return true;
922                      } },
923     CommandArgument{ "--check-stamp-list", CommandArgument::Values::One,
924                      [](std::string const& value, cmake* state) -> bool {
925                        state->CheckStampList = value;
926                        return true;
927                      } },
928     CommandArgument{ "--regenerate-during-build",
929                      CommandArgument::Values::Zero,
930                      [](std::string const&, cmake* state) -> bool {
931                        state->RegenerateDuringBuild = true;
932                        return true;
933                      } },
934
935     CommandArgument{ "--find-package", CommandArgument::Values::Zero,
936                      IgnoreAndTrueLambda },
937
938     CommandArgument{ "--graphviz", "No file specified for --graphviz",
939                      CommandArgument::Values::One,
940                      [](std::string const& value, cmake* state) -> bool {
941                        std::string path =
942                          cmSystemTools::CollapseFullPath(value);
943                        cmSystemTools::ConvertToUnixSlashes(path);
944                        state->GraphVizFile = path;
945                        return true;
946                      } },
947
948     CommandArgument{ "--debug-trycompile", CommandArgument::Values::Zero,
949                      [](std::string const&, cmake* state) -> bool {
950                        std::cout << "debug trycompile on\n";
951                        state->DebugTryCompileOn();
952                        return true;
953                      } },
954     CommandArgument{ "--debug-output", CommandArgument::Values::Zero,
955                      [](std::string const&, cmake* state) -> bool {
956                        std::cout << "Running with debug output on.\n";
957                        state->SetDebugOutputOn(true);
958                        return true;
959                      } },
960
961     CommandArgument{ "--log-level", "Invalid level specified for --log-level",
962                      CommandArgument::Values::One,
963                      [](std::string const& value, cmake* state) -> bool {
964                        const auto logLevel = StringToLogLevel(value);
965                        if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
966                          cmSystemTools::Error(
967                            "Invalid level specified for --log-level");
968                          return false;
969                        }
970                        state->SetLogLevel(logLevel);
971                        state->LogLevelWasSetViaCLI = true;
972                        return true;
973                      } },
974     // This is supported for backward compatibility. This option only
975     // appeared in the 3.15.x release series and was renamed to
976     // --log-level in 3.16.0
977     CommandArgument{ "--loglevel", "Invalid level specified for --loglevel",
978                      CommandArgument::Values::One,
979                      [](std::string const& value, cmake* state) -> bool {
980                        const auto logLevel = StringToLogLevel(value);
981                        if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
982                          cmSystemTools::Error(
983                            "Invalid level specified for --loglevel");
984                          return false;
985                        }
986                        state->SetLogLevel(logLevel);
987                        state->LogLevelWasSetViaCLI = true;
988                        return true;
989                      } },
990
991     CommandArgument{ "--log-context", CommandArgument::Values::Zero,
992                      [](std::string const&, cmake* state) -> bool {
993                        state->SetShowLogContext(true);
994                        return true;
995                      } },
996     CommandArgument{
997       "--debug-find", CommandArgument::Values::Zero,
998       [](std::string const&, cmake* state) -> bool {
999         std::cout << "Running with debug output on for the `find` commands.\n";
1000         state->SetDebugFindOutput(true);
1001         return true;
1002       } },
1003     CommandArgument{
1004       "--debug-find-pkg", "Provide a package argument for --debug-find-pkg",
1005       CommandArgument::Values::One, CommandArgument::RequiresSeparator::Yes,
1006       [](std::string const& value, cmake* state) -> bool {
1007         std::vector<std::string> find_pkgs(cmTokenize(value, ","));
1008         std::cout << "Running with debug output on for the 'find' commands "
1009                      "for package(s)";
1010         for (auto const& v : find_pkgs) {
1011           std::cout << " " << v;
1012           state->SetDebugFindOutputPkgs(v);
1013         }
1014         std::cout << ".\n";
1015         return true;
1016       } },
1017     CommandArgument{
1018       "--debug-find-var", CommandArgument::Values::One,
1019       CommandArgument::RequiresSeparator::Yes,
1020       [](std::string const& value, cmake* state) -> bool {
1021         std::vector<std::string> find_vars(cmTokenize(value, ","));
1022         std::cout << "Running with debug output on for the variable(s)";
1023         for (auto const& v : find_vars) {
1024           std::cout << " " << v;
1025           state->SetDebugFindOutputVars(v);
1026         }
1027         std::cout << ".\n";
1028         return true;
1029       } },
1030     CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
1031                      [](std::string const&, cmake* state) -> bool {
1032                        std::cout << "Running with expanded trace output on.\n";
1033                        state->SetTrace(true);
1034                        state->SetTraceExpand(true);
1035                        return true;
1036                      } },
1037     CommandArgument{ "--trace-format", CommandArgument::Values::One,
1038                      [](std::string const& value, cmake* state) -> bool {
1039                        state->SetTrace(true);
1040                        const auto traceFormat = StringToTraceFormat(value);
1041                        if (traceFormat == TraceFormat::TRACE_UNDEFINED) {
1042                          cmSystemTools::Error(
1043                            "Invalid format specified for --trace-format. "
1044                            "Valid formats are human, json-v1.");
1045                          return false;
1046                        }
1047                        state->SetTraceFormat(traceFormat);
1048                        return true;
1049                      } },
1050     CommandArgument{ "--trace-source", CommandArgument::Values::One,
1051                      [](std::string const& value, cmake* state) -> bool {
1052                        std::string file(value);
1053                        cmSystemTools::ConvertToUnixSlashes(file);
1054                        state->AddTraceSource(file);
1055                        state->SetTrace(true);
1056                        return true;
1057                      } },
1058     CommandArgument{ "--trace-redirect", CommandArgument::Values::One,
1059                      [](std::string const& value, cmake* state) -> bool {
1060                        std::string file(value);
1061                        cmSystemTools::ConvertToUnixSlashes(file);
1062                        state->SetTraceFile(file);
1063                        state->SetTrace(true);
1064                        return true;
1065                      } },
1066     CommandArgument{ "--trace", CommandArgument::Values::Zero,
1067                      [](std::string const&, cmake* state) -> bool {
1068                        std::cout << "Running with trace output on.\n";
1069                        state->SetTrace(true);
1070                        state->SetTraceExpand(false);
1071                        return true;
1072                      } },
1073     CommandArgument{ "--warn-uninitialized", CommandArgument::Values::Zero,
1074                      [](std::string const&, cmake* state) -> bool {
1075                        std::cout << "Warn about uninitialized values.\n";
1076                        state->SetWarnUninitialized(true);
1077                        return true;
1078                      } },
1079     CommandArgument{ "--warn-unused-vars", CommandArgument::Values::Zero,
1080                      IgnoreAndTrueLambda }, // Option was removed.
1081     CommandArgument{ "--no-warn-unused-cli", CommandArgument::Values::Zero,
1082                      [](std::string const&, cmake* state) -> bool {
1083                        std::cout
1084                          << "Not searching for unused variables given on the "
1085                          << "command line.\n";
1086                        state->SetWarnUnusedCli(false);
1087                        return true;
1088                      } },
1089     CommandArgument{
1090       "--check-system-vars", CommandArgument::Values::Zero,
1091       [](std::string const&, cmake* state) -> bool {
1092         std::cout << "Also check system files when warning about unused and "
1093                   << "uninitialized variables.\n";
1094         state->SetCheckSystemVars(true);
1095         return true;
1096       } },
1097     CommandArgument{
1098       "--compile-no-warning-as-error", CommandArgument::Values::Zero,
1099       [](std::string const&, cmake* state) -> bool {
1100         std::cout << "Ignoring COMPILE_WARNING_AS_ERROR target property and "
1101                   << "CMAKE_COMPILE_WARNING_AS_ERROR variable.\n";
1102         state->SetIgnoreWarningAsError(true);
1103         return true;
1104       } }
1105   };
1106
1107 #if defined(CMAKE_HAVE_VS_GENERATORS)
1108   arguments.emplace_back("--vs-solution-file", CommandArgument::Values::One,
1109                          [](std::string const& value, cmake* state) -> bool {
1110                            state->VSSolutionFile = value;
1111                            return true;
1112                          });
1113 #endif
1114
1115 #if !defined(CMAKE_BOOTSTRAP)
1116   arguments.emplace_back("--profiling-format",
1117                          "No format specified for --profiling-format",
1118                          CommandArgument::Values::One,
1119                          [&](std::string const& value, cmake*) -> bool {
1120                            profilingFormat = value;
1121                            return true;
1122                          });
1123   arguments.emplace_back(
1124     "--profiling-output", "No path specified for --profiling-output",
1125     CommandArgument::Values::One,
1126     [&](std::string const& value, cmake*) -> bool {
1127       profilingOutput = cmSystemTools::CollapseFullPath(value);
1128       cmSystemTools::ConvertToUnixSlashes(profilingOutput);
1129       return true;
1130     });
1131   arguments.emplace_back("--preset", "No preset specified for --preset",
1132                          CommandArgument::Values::One,
1133                          [&](std::string const& value, cmake*) -> bool {
1134                            presetName = value;
1135                            return true;
1136                          });
1137   arguments.emplace_back(
1138     "--list-presets", CommandArgument::Values::ZeroOrOne,
1139     [&](std::string const& value, cmake*) -> bool {
1140       if (value.empty() || value == "configure") {
1141         listPresets = ListPresets::Configure;
1142       } else if (value == "build") {
1143         listPresets = ListPresets::Build;
1144       } else if (value == "test") {
1145         listPresets = ListPresets::Test;
1146       } else if (value == "package") {
1147         listPresets = ListPresets::Package;
1148       } else if (value == "workflow") {
1149         listPresets = ListPresets::Workflow;
1150       } else if (value == "all") {
1151         listPresets = ListPresets::All;
1152       } else {
1153         cmSystemTools::Error(
1154           "Invalid value specified for --list-presets.\n"
1155           "Valid values are configure, build, test, package, or all. "
1156           "When no value is passed the default is configure.");
1157         return false;
1158       }
1159
1160       return true;
1161     });
1162
1163 #endif
1164
1165   bool badGeneratorName = false;
1166   CommandArgument generatorCommand(
1167     "-G", "No generator specified for -G", CommandArgument::Values::One,
1168     CommandArgument::RequiresSeparator::No,
1169     [&](std::string const& value, cmake* state) -> bool {
1170       bool valid = state->CreateAndSetGlobalGenerator(value, true);
1171       badGeneratorName = !valid;
1172       return valid;
1173     });
1174
1175   for (decltype(args.size()) i = 1; i < args.size(); ++i) {
1176     // iterate each argument
1177     std::string const& arg = args[i];
1178
1179     if (this->GetWorkingMode() == SCRIPT_MODE && arg == "--") {
1180       // Stop processing CMake args and avoid possible errors
1181       // when arbitrary args are given to CMake script.
1182       break;
1183     }
1184
1185     // Generator flag has special handling for when to print help
1186     // so it becomes the exception
1187     if (generatorCommand.matches(arg)) {
1188       bool parsed = generatorCommand.parse(arg, i, args, this);
1189       if (!parsed && !badGeneratorName) {
1190         this->PrintGeneratorList();
1191         return;
1192       }
1193       continue;
1194     }
1195
1196     bool matched = false;
1197     bool parsedCorrectly = true; // needs to be true so we can ignore
1198                                  // arguments so as -E
1199     for (auto const& m : arguments) {
1200       if (m.matches(arg)) {
1201         matched = true;
1202         parsedCorrectly = m.parse(arg, i, args, this);
1203         break;
1204       }
1205     }
1206
1207     // We have an issue where arguments to a "-P" script mode
1208     // can be provided before the "-P" argument. This means
1209     // that we need to lazily check this argument after checking
1210     // all args.
1211     // Additionally it can't be the source/binary tree location
1212     if (!parsedCorrectly) {
1213       cmSystemTools::Error("Run 'cmake --help' for all supported options.");
1214       exit(1);
1215     } else if (!matched && cmHasLiteralPrefix(arg, "-")) {
1216       possibleUnknownArg = arg;
1217     } else if (!matched) {
1218       bool parsedDirectory = this->SetDirectoriesFromFile(arg);
1219       if (!parsedDirectory) {
1220         extraProvidedPath = arg;
1221       }
1222     }
1223   }
1224
1225   if (!extraProvidedPath.empty() && this->GetWorkingMode() == NORMAL_MODE) {
1226     this->IssueMessage(MessageType::WARNING,
1227                        cmStrCat("Ignoring extra path from command line:\n \"",
1228                                 extraProvidedPath, "\""));
1229   }
1230   if (!possibleUnknownArg.empty() && this->GetWorkingMode() != SCRIPT_MODE) {
1231     cmSystemTools::Error(cmStrCat("Unknown argument ", possibleUnknownArg));
1232     cmSystemTools::Error("Run 'cmake --help' for all supported options.");
1233     exit(1);
1234   }
1235
1236   // Empty instance, platform and toolset if only a generator is specified
1237   if (this->GlobalGenerator) {
1238     this->GeneratorInstance = "";
1239     if (!this->GeneratorPlatformSet) {
1240       this->GeneratorPlatform = "";
1241     }
1242     if (!this->GeneratorToolsetSet) {
1243       this->GeneratorToolset = "";
1244     }
1245   }
1246
1247 #if !defined(CMAKE_BOOTSTRAP)
1248   if (!profilingOutput.empty() || !profilingFormat.empty()) {
1249     if (profilingOutput.empty()) {
1250       cmSystemTools::Error(
1251         "--profiling-format specified but no --profiling-output!");
1252       return;
1253     }
1254     if (profilingFormat == "google-trace"_s) {
1255       try {
1256         this->ProfilingOutput =
1257           cm::make_unique<cmMakefileProfilingData>(profilingOutput);
1258       } catch (std::runtime_error& e) {
1259         cmSystemTools::Error(
1260           cmStrCat("Could not start profiling: ", e.what()));
1261         return;
1262       }
1263     } else {
1264       cmSystemTools::Error("Invalid format specified for --profiling-format");
1265       return;
1266     }
1267   }
1268 #endif
1269
1270   const bool haveSourceDir = !this->GetHomeDirectory().empty();
1271   const bool haveBinaryDir = !this->GetHomeOutputDirectory().empty();
1272   const bool havePreset =
1273 #ifdef CMAKE_BOOTSTRAP
1274     false;
1275 #else
1276     !presetName.empty();
1277 #endif
1278
1279   if (this->CurrentWorkingMode == cmake::NORMAL_MODE && !haveSourceDir &&
1280       !haveBinaryDir && !havePreset) {
1281     this->IssueMessage(
1282       MessageType::WARNING,
1283       "No source or binary directory provided. Both will be assumed to be "
1284       "the same as the current working directory, but note that this "
1285       "warning will become a fatal error in future CMake releases.");
1286   }
1287
1288   if (!haveSourceDir) {
1289     this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
1290   }
1291   if (!haveBinaryDir) {
1292     this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
1293   }
1294
1295 #if !defined(CMAKE_BOOTSTRAP)
1296   if (listPresets != ListPresets::None || !presetName.empty()) {
1297     cmCMakePresetsGraph presetsGraph;
1298     auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory());
1299     if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
1300       std::string errorMsg =
1301         cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
1302                  ": ", cmCMakePresetsGraph::ResultToString(result));
1303       if (!presetsGraph.errors.empty()) {
1304         errorMsg = cmStrCat(errorMsg, "\nErrors:\n", presetsGraph.errors);
1305       }
1306       cmSystemTools::Error(errorMsg);
1307       return;
1308     }
1309
1310     if (listPresets != ListPresets::None) {
1311       if (listPresets == ListPresets::Configure) {
1312         this->PrintPresetList(presetsGraph);
1313       } else if (listPresets == ListPresets::Build) {
1314         presetsGraph.PrintBuildPresetList();
1315       } else if (listPresets == ListPresets::Test) {
1316         presetsGraph.PrintTestPresetList();
1317       } else if (listPresets == ListPresets::Package) {
1318         presetsGraph.PrintPackagePresetList();
1319       } else if (listPresets == ListPresets::Workflow) {
1320         presetsGraph.PrintWorkflowPresetList();
1321       } else if (listPresets == ListPresets::All) {
1322         presetsGraph.PrintAllPresets();
1323       }
1324
1325       this->SetWorkingMode(WorkingMode::HELP_MODE);
1326       return;
1327     }
1328
1329     auto preset = presetsGraph.ConfigurePresets.find(presetName);
1330     if (preset == presetsGraph.ConfigurePresets.end()) {
1331       cmSystemTools::Error(cmStrCat("No such preset in ",
1332                                     this->GetHomeDirectory(), ": \"",
1333                                     presetName, '"'));
1334       this->PrintPresetList(presetsGraph);
1335       return;
1336     }
1337     if (preset->second.Unexpanded.Hidden) {
1338       cmSystemTools::Error(cmStrCat("Cannot use hidden preset in ",
1339                                     this->GetHomeDirectory(), ": \"",
1340                                     presetName, '"'));
1341       this->PrintPresetList(presetsGraph);
1342       return;
1343     }
1344     auto const& expandedPreset = preset->second.Expanded;
1345     if (!expandedPreset) {
1346       cmSystemTools::Error(cmStrCat("Could not evaluate preset \"",
1347                                     preset->second.Unexpanded.Name,
1348                                     "\": Invalid macro expansion"));
1349       return;
1350     }
1351     if (!expandedPreset->ConditionResult) {
1352       cmSystemTools::Error(cmStrCat("Could not use disabled preset \"",
1353                                     preset->second.Unexpanded.Name, "\""));
1354       return;
1355     }
1356
1357     if (!this->State->IsCacheLoaded() && !haveBArg &&
1358         !expandedPreset->BinaryDir.empty()) {
1359       this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
1360     }
1361     if (!this->GlobalGenerator && !expandedPreset->Generator.empty()) {
1362       if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator,
1363                                              false)) {
1364         return;
1365       }
1366     }
1367     this->UnprocessedPresetVariables = expandedPreset->CacheVariables;
1368     this->UnprocessedPresetEnvironment = expandedPreset->Environment;
1369
1370     if (!expandedPreset->InstallDir.empty() &&
1371         !this->State->GetInitializedCacheValue("CMAKE_INSTALL_PREFIX")) {
1372       this->UnprocessedPresetVariables["CMAKE_INSTALL_PREFIX"] = {
1373         "PATH", expandedPreset->InstallDir
1374       };
1375     }
1376     if (!expandedPreset->ToolchainFile.empty() &&
1377         !this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) {
1378       this->UnprocessedPresetVariables["CMAKE_TOOLCHAIN_FILE"] = {
1379         "FILEPATH", expandedPreset->ToolchainFile
1380       };
1381     }
1382
1383     if (!expandedPreset->ArchitectureStrategy ||
1384         expandedPreset->ArchitectureStrategy ==
1385           cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
1386       if (!this->GeneratorPlatformSet) {
1387         this->SetGeneratorPlatform(expandedPreset->Architecture);
1388       }
1389     }
1390     if (!expandedPreset->ToolsetStrategy ||
1391         expandedPreset->ToolsetStrategy ==
1392           cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
1393       if (!this->GeneratorToolsetSet) {
1394         this->SetGeneratorToolset(expandedPreset->Toolset);
1395       }
1396     }
1397
1398     this->SetWarningFromPreset("dev", expandedPreset->WarnDev,
1399                                expandedPreset->ErrorDev);
1400     this->SetWarningFromPreset("deprecated", expandedPreset->WarnDeprecated,
1401                                expandedPreset->ErrorDeprecated);
1402     if (expandedPreset->WarnUninitialized == true) {
1403       this->SetWarnUninitialized(true);
1404     }
1405     if (expandedPreset->WarnUnusedCli == false) {
1406       this->SetWarnUnusedCli(false);
1407     }
1408     if (expandedPreset->WarnSystemVars == true) {
1409       this->SetCheckSystemVars(true);
1410     }
1411     if (expandedPreset->DebugOutput == true) {
1412       this->SetDebugOutputOn(true);
1413     }
1414     if (expandedPreset->DebugTryCompile == true) {
1415       this->DebugTryCompileOn();
1416     }
1417     if (expandedPreset->DebugFind == true) {
1418       this->SetDebugFindOutput(true);
1419     }
1420   }
1421 #endif
1422 }
1423
1424 namespace {
1425 using LevelsPair = std::pair<cm::string_view, Message::LogLevel>;
1426 using LevelsPairArray = std::array<LevelsPair, 7>;
1427 const LevelsPairArray& getStringToLogLevelPairs()
1428 {
1429   static const LevelsPairArray levels = {
1430     { { "error", Message::LogLevel::LOG_ERROR },
1431       { "warning", Message::LogLevel::LOG_WARNING },
1432       { "notice", Message::LogLevel::LOG_NOTICE },
1433       { "status", Message::LogLevel::LOG_STATUS },
1434       { "verbose", Message::LogLevel::LOG_VERBOSE },
1435       { "debug", Message::LogLevel::LOG_DEBUG },
1436       { "trace", Message::LogLevel::LOG_TRACE } }
1437   };
1438   return levels;
1439 }
1440 } // namespace
1441
1442 Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr)
1443 {
1444   const LevelsPairArray& levels = getStringToLogLevelPairs();
1445
1446   const auto levelStrLowCase =
1447     cmSystemTools::LowerCase(std::string{ levelStr });
1448
1449   // NOLINTNEXTLINE(readability-qualified-auto)
1450   const auto it = std::find_if(levels.cbegin(), levels.cend(),
1451                                [&levelStrLowCase](const LevelsPair& p) {
1452                                  return p.first == levelStrLowCase;
1453                                });
1454   return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED;
1455 }
1456
1457 std::string cmake::LogLevelToString(Message::LogLevel level)
1458 {
1459   const LevelsPairArray& levels = getStringToLogLevelPairs();
1460
1461   // NOLINTNEXTLINE(readability-qualified-auto)
1462   const auto it =
1463     std::find_if(levels.cbegin(), levels.cend(),
1464                  [&level](const LevelsPair& p) { return p.second == level; });
1465   const cm::string_view levelStrLowerCase =
1466     (it != levels.cend()) ? it->first : "undefined";
1467   std::string levelStrUpperCase =
1468     cmSystemTools::UpperCase(std::string{ levelStrLowerCase });
1469   return levelStrUpperCase;
1470 }
1471
1472 cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr)
1473 {
1474   using TracePair = std::pair<std::string, TraceFormat>;
1475   static const std::vector<TracePair> levels = {
1476     { "human", TraceFormat::TRACE_HUMAN },
1477     { "json-v1", TraceFormat::TRACE_JSON_V1 },
1478   };
1479
1480   const auto traceStrLowCase = cmSystemTools::LowerCase(traceStr);
1481
1482   const auto it = std::find_if(levels.cbegin(), levels.cend(),
1483                                [&traceStrLowCase](const TracePair& p) {
1484                                  return p.first == traceStrLowCase;
1485                                });
1486   return (it != levels.cend()) ? it->second : TraceFormat::TRACE_UNDEFINED;
1487 }
1488
1489 void cmake::SetTraceFile(const std::string& file)
1490 {
1491   this->TraceFile.close();
1492   this->TraceFile.open(file.c_str());
1493   if (!this->TraceFile) {
1494     std::stringstream ss;
1495     ss << "Error opening trace file " << file << ": "
1496        << cmSystemTools::GetLastSystemError();
1497     cmSystemTools::Error(ss.str());
1498     return;
1499   }
1500   std::cout << "Trace will be written to " << file << "\n";
1501 }
1502
1503 void cmake::PrintTraceFormatVersion()
1504 {
1505   if (!this->GetTrace()) {
1506     return;
1507   }
1508
1509   std::string msg;
1510
1511   switch (this->GetTraceFormat()) {
1512     case TraceFormat::TRACE_JSON_V1: {
1513 #ifndef CMAKE_BOOTSTRAP
1514       Json::Value val;
1515       Json::Value version;
1516       Json::StreamWriterBuilder builder;
1517       builder["indentation"] = "";
1518       version["major"] = 1;
1519       version["minor"] = 2;
1520       val["version"] = version;
1521       msg = Json::writeString(builder, val);
1522 #endif
1523       break;
1524     }
1525     case TraceFormat::TRACE_HUMAN:
1526       msg = "";
1527       break;
1528     case TraceFormat::TRACE_UNDEFINED:
1529       msg = "INTERNAL ERROR: Trace format is TRACE_UNDEFINED";
1530       break;
1531   }
1532
1533   if (msg.empty()) {
1534     return;
1535   }
1536
1537   auto& f = this->GetTraceFile();
1538   if (f) {
1539     f << msg << '\n';
1540   } else {
1541     cmSystemTools::Message(msg);
1542   }
1543 }
1544
1545 bool cmake::SetDirectoriesFromFile(const std::string& arg)
1546 {
1547   // Check if the argument refers to a CMakeCache.txt or
1548   // CMakeLists.txt file.
1549   std::string listPath;
1550   std::string cachePath;
1551   bool is_source_dir = false;
1552   bool is_empty_directory = false;
1553   if (cmSystemTools::FileIsDirectory(arg)) {
1554     std::string path = cmSystemTools::CollapseFullPath(arg);
1555     cmSystemTools::ConvertToUnixSlashes(path);
1556     std::string cacheFile = cmStrCat(path, "/CMakeCache.txt");
1557     std::string listFile = cmStrCat(path, "/CMakeLists.txt");
1558
1559     is_empty_directory = true;
1560     if (cmSystemTools::FileExists(cacheFile)) {
1561       cachePath = path;
1562       is_empty_directory = false;
1563     }
1564     if (cmSystemTools::FileExists(listFile)) {
1565       listPath = path;
1566       is_empty_directory = false;
1567       is_source_dir = true;
1568     }
1569   } else if (cmSystemTools::FileExists(arg)) {
1570     std::string fullPath = cmSystemTools::CollapseFullPath(arg);
1571     std::string name = cmSystemTools::GetFilenameName(fullPath);
1572     name = cmSystemTools::LowerCase(name);
1573     if (name == "cmakecache.txt"_s) {
1574       cachePath = cmSystemTools::GetFilenamePath(fullPath);
1575     } else if (name == "cmakelists.txt"_s) {
1576       listPath = cmSystemTools::GetFilenamePath(fullPath);
1577     }
1578   } else {
1579     // Specified file or directory does not exist.  Try to set things
1580     // up to produce a meaningful error message.
1581     std::string fullPath = cmSystemTools::CollapseFullPath(arg);
1582     std::string name = cmSystemTools::GetFilenameName(fullPath);
1583     name = cmSystemTools::LowerCase(name);
1584     if (name == "cmakecache.txt"_s || name == "cmakelists.txt"_s) {
1585       listPath = cmSystemTools::GetFilenamePath(fullPath);
1586     } else {
1587       listPath = fullPath;
1588     }
1589   }
1590
1591   // If there is a CMakeCache.txt file, use its settings.
1592   if (!cachePath.empty()) {
1593     if (this->LoadCache(cachePath)) {
1594       cmValue existingValue =
1595         this->State->GetCacheEntryValue("CMAKE_HOME_DIRECTORY");
1596       if (existingValue) {
1597         this->SetHomeOutputDirectory(cachePath);
1598         this->SetHomeDirectory(*existingValue);
1599         return true;
1600       }
1601     }
1602   }
1603
1604   bool no_source_tree = this->GetHomeDirectory().empty();
1605   bool no_build_tree = this->GetHomeOutputDirectory().empty();
1606
1607   // When invoked with a path that points to an existing CMakeCache
1608   // This function is called multiple times with the same path
1609   const bool passed_same_path = (listPath == this->GetHomeDirectory()) ||
1610     (listPath == this->GetHomeOutputDirectory());
1611   bool used_provided_path =
1612     (passed_same_path || is_source_dir || no_build_tree);
1613
1614   // If there is a CMakeLists.txt file, use it as the source tree.
1615   if (!listPath.empty()) {
1616     // When invoked with a path that points to an existing CMakeCache
1617     // This function is called multiple times with the same path
1618     if (is_source_dir) {
1619       this->SetHomeDirectoryViaCommandLine(listPath);
1620       if (no_build_tree) {
1621         std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
1622         this->SetHomeOutputDirectory(cwd);
1623       }
1624     } else if (no_source_tree && no_build_tree) {
1625       this->SetHomeDirectory(listPath);
1626
1627       std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
1628       this->SetHomeOutputDirectory(cwd);
1629     } else if (no_build_tree) {
1630       this->SetHomeOutputDirectory(listPath);
1631     }
1632   } else {
1633     if (no_source_tree) {
1634       // We didn't find a CMakeLists.txt and it wasn't specified
1635       // with -S. Assume it is the path to the source tree
1636       std::string full = cmSystemTools::CollapseFullPath(arg);
1637       this->SetHomeDirectory(full);
1638     }
1639     if (no_build_tree && !no_source_tree && is_empty_directory) {
1640       // passed `-S <path> <build_dir> when build_dir is an empty directory
1641       std::string full = cmSystemTools::CollapseFullPath(arg);
1642       this->SetHomeOutputDirectory(full);
1643     } else if (no_build_tree) {
1644       // We didn't find a CMakeCache.txt and it wasn't specified
1645       // with -B. Assume the current working directory as the build tree.
1646       std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
1647       this->SetHomeOutputDirectory(cwd);
1648       used_provided_path = false;
1649     }
1650   }
1651
1652   return used_provided_path;
1653 }
1654
1655 // at the end of this CMAKE_ROOT and CMAKE_COMMAND should be added to the
1656 // cache
1657 int cmake::AddCMakePaths()
1658 {
1659   // Save the value in the cache
1660   this->AddCacheEntry("CMAKE_COMMAND", cmSystemTools::GetCMakeCommand(),
1661                       "Path to CMake executable.", cmStateEnums::INTERNAL);
1662 #ifndef CMAKE_BOOTSTRAP
1663   this->AddCacheEntry("CMAKE_CTEST_COMMAND", cmSystemTools::GetCTestCommand(),
1664                       "Path to ctest program executable.",
1665                       cmStateEnums::INTERNAL);
1666   this->AddCacheEntry("CMAKE_CPACK_COMMAND", cmSystemTools::GetCPackCommand(),
1667                       "Path to cpack program executable.",
1668                       cmStateEnums::INTERNAL);
1669 #endif
1670   if (!cmSystemTools::FileExists(
1671         (cmSystemTools::GetCMakeRoot() + "/Modules/CMake.cmake"))) {
1672     // couldn't find modules
1673     cmSystemTools::Error(
1674       "Could not find CMAKE_ROOT !!!\n"
1675       "CMake has most likely not been installed correctly.\n"
1676       "Modules directory not found in\n" +
1677       cmSystemTools::GetCMakeRoot());
1678     return 0;
1679   }
1680   this->AddCacheEntry("CMAKE_ROOT", cmSystemTools::GetCMakeRoot(),
1681                       "Path to CMake installation.", cmStateEnums::INTERNAL);
1682
1683   return 1;
1684 }
1685
1686 void cmake::AddDefaultExtraGenerators()
1687 {
1688 #if !defined(CMAKE_BOOTSTRAP)
1689   this->ExtraGenerators.push_back(cmExtraCodeBlocksGenerator::GetFactory());
1690   this->ExtraGenerators.push_back(cmExtraCodeLiteGenerator::GetFactory());
1691   this->ExtraGenerators.push_back(cmExtraEclipseCDT4Generator::GetFactory());
1692   this->ExtraGenerators.push_back(cmExtraKateGenerator::GetFactory());
1693   this->ExtraGenerators.push_back(cmExtraSublimeTextGenerator::GetFactory());
1694 #endif
1695 }
1696
1697 void cmake::GetRegisteredGenerators(std::vector<GeneratorInfo>& generators,
1698                                     bool includeNamesWithPlatform) const
1699 {
1700   for (const auto& gen : this->Generators) {
1701     std::vector<std::string> names = gen->GetGeneratorNames();
1702
1703     if (includeNamesWithPlatform) {
1704       cm::append(names, gen->GetGeneratorNamesWithPlatform());
1705     }
1706
1707     for (std::string const& name : names) {
1708       GeneratorInfo info;
1709       info.supportsToolset = gen->SupportsToolset();
1710       info.supportsPlatform = gen->SupportsPlatform();
1711       info.supportedPlatforms = gen->GetKnownPlatforms();
1712       info.defaultPlatform = gen->GetDefaultPlatformName();
1713       info.name = name;
1714       info.baseName = name;
1715       info.isAlias = false;
1716       generators.push_back(std::move(info));
1717     }
1718   }
1719
1720   for (cmExternalMakefileProjectGeneratorFactory* eg : this->ExtraGenerators) {
1721     const std::vector<std::string> genList =
1722       eg->GetSupportedGlobalGenerators();
1723     for (std::string const& gen : genList) {
1724       GeneratorInfo info;
1725       info.name = cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
1726         gen, eg->GetName());
1727       info.baseName = gen;
1728       info.extraName = eg->GetName();
1729       info.supportsPlatform = false;
1730       info.supportsToolset = false;
1731       info.isAlias = false;
1732       generators.push_back(std::move(info));
1733     }
1734     for (std::string const& a : eg->Aliases) {
1735       GeneratorInfo info;
1736       info.name = a;
1737       if (!genList.empty()) {
1738         info.baseName = genList.at(0);
1739       }
1740       info.extraName = eg->GetName();
1741       info.supportsPlatform = false;
1742       info.supportsToolset = false;
1743       info.isAlias = true;
1744       generators.push_back(std::move(info));
1745     }
1746   }
1747 }
1748
1749 static std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>,
1750                  std::string>
1751 createExtraGenerator(
1752   const std::vector<cmExternalMakefileProjectGeneratorFactory*>& in,
1753   const std::string& name)
1754 {
1755   for (cmExternalMakefileProjectGeneratorFactory* i : in) {
1756     const std::vector<std::string> generators =
1757       i->GetSupportedGlobalGenerators();
1758     if (i->GetName() == name) { // Match aliases
1759       return { i->CreateExternalMakefileProjectGenerator(), generators.at(0) };
1760     }
1761     for (std::string const& g : generators) {
1762       const std::string fullName =
1763         cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
1764           g, i->GetName());
1765       if (fullName == name) {
1766         return { i->CreateExternalMakefileProjectGenerator(), g };
1767       }
1768     }
1769   }
1770   return { nullptr, name };
1771 }
1772
1773 std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator(
1774   const std::string& gname, bool allowArch)
1775 {
1776   std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>, std::string>
1777     extra = createExtraGenerator(this->ExtraGenerators, gname);
1778   std::unique_ptr<cmExternalMakefileProjectGenerator>& extraGenerator =
1779     extra.first;
1780   const std::string& name = extra.second;
1781
1782   std::unique_ptr<cmGlobalGenerator> generator;
1783   for (const auto& g : this->Generators) {
1784     generator = g->CreateGlobalGenerator(name, allowArch, this);
1785     if (generator) {
1786       break;
1787     }
1788   }
1789
1790   if (generator) {
1791     generator->SetExternalMakefileProjectGenerator(std::move(extraGenerator));
1792   }
1793
1794   return generator;
1795 }
1796
1797 bool cmake::CreateAndSetGlobalGenerator(const std::string& name,
1798                                         bool allowArch)
1799 {
1800   auto gen = this->CreateGlobalGenerator(name, allowArch);
1801   if (!gen) {
1802     std::string kdevError;
1803     std::string vsError;
1804     if (name.find("KDevelop3", 0) != std::string::npos) {
1805       kdevError = "\nThe KDevelop3 generator is not supported anymore.";
1806     }
1807     if (!allowArch && cmHasLiteralPrefix(name, "Visual Studio ") &&
1808         name.length() >= cmStrLen("Visual Studio xx xxxx ")) {
1809       vsError = "\nUsing platforms in Visual Studio generator names is not "
1810                 "supported in CMakePresets.json.";
1811     }
1812
1813     cmSystemTools::Error(
1814       cmStrCat("Could not create named generator ", name, kdevError, vsError));
1815     this->PrintGeneratorList();
1816     return false;
1817   }
1818
1819   this->SetGlobalGenerator(std::move(gen));
1820   return true;
1821 }
1822
1823 #ifndef CMAKE_BOOTSTRAP
1824 void cmake::PrintPresetList(const cmCMakePresetsGraph& graph) const
1825 {
1826   std::vector<GeneratorInfo> generators;
1827   this->GetRegisteredGenerators(generators, false);
1828   auto filter =
1829     [&generators](const cmCMakePresetsGraph::ConfigurePreset& preset) -> bool {
1830     if (preset.Generator.empty()) {
1831       return true;
1832     }
1833     auto condition = [&preset](const GeneratorInfo& info) -> bool {
1834       return info.name == preset.Generator;
1835     };
1836     auto it = std::find_if(generators.begin(), generators.end(), condition);
1837     return it != generators.end();
1838   };
1839
1840   graph.PrintConfigurePresetList(filter);
1841 }
1842 #endif
1843
1844 void cmake::SetHomeDirectoryViaCommandLine(std::string const& path)
1845 {
1846   if (path.empty()) {
1847     return;
1848   }
1849
1850   auto prev_path = this->GetHomeDirectory();
1851   if (prev_path != path && !prev_path.empty() &&
1852       this->GetWorkingMode() == NORMAL_MODE) {
1853     this->IssueMessage(MessageType::WARNING,
1854                        cmStrCat("Ignoring extra path from command line:\n \"",
1855                                 prev_path, "\""));
1856   }
1857   this->SetHomeDirectory(path);
1858 }
1859
1860 void cmake::SetHomeDirectory(const std::string& dir)
1861 {
1862   this->State->SetSourceDirectory(dir);
1863   if (this->CurrentSnapshot.IsValid()) {
1864     this->CurrentSnapshot.SetDefinition("CMAKE_SOURCE_DIR", dir);
1865   }
1866
1867   if (this->State->GetProjectKind() == cmState::ProjectKind::Normal) {
1868     this->Messenger->SetTopSource(this->GetHomeDirectory());
1869   } else {
1870     this->Messenger->SetTopSource(cm::nullopt);
1871   }
1872 }
1873
1874 std::string const& cmake::GetHomeDirectory() const
1875 {
1876   return this->State->GetSourceDirectory();
1877 }
1878
1879 void cmake::SetHomeOutputDirectory(const std::string& dir)
1880 {
1881   this->State->SetBinaryDirectory(dir);
1882   if (this->CurrentSnapshot.IsValid()) {
1883     this->CurrentSnapshot.SetDefinition("CMAKE_BINARY_DIR", dir);
1884   }
1885 }
1886
1887 std::string const& cmake::GetHomeOutputDirectory() const
1888 {
1889   return this->State->GetBinaryDirectory();
1890 }
1891
1892 std::string cmake::FindCacheFile(const std::string& binaryDir)
1893 {
1894   std::string cachePath = binaryDir;
1895   cmSystemTools::ConvertToUnixSlashes(cachePath);
1896   std::string cacheFile = cmStrCat(cachePath, "/CMakeCache.txt");
1897   if (!cmSystemTools::FileExists(cacheFile)) {
1898     // search in parent directories for cache
1899     std::string cmakeFiles = cmStrCat(cachePath, "/CMakeFiles");
1900     if (cmSystemTools::FileExists(cmakeFiles)) {
1901       std::string cachePathFound =
1902         cmSystemTools::FileExistsInParentDirectories("CMakeCache.txt",
1903                                                      cachePath, "/");
1904       if (!cachePathFound.empty()) {
1905         cachePath = cmSystemTools::GetFilenamePath(cachePathFound);
1906       }
1907     }
1908   }
1909   return cachePath;
1910 }
1911
1912 void cmake::SetGlobalGenerator(std::unique_ptr<cmGlobalGenerator> gg)
1913 {
1914   if (!gg) {
1915     cmSystemTools::Error("Error SetGlobalGenerator called with null");
1916     return;
1917   }
1918   if (this->GlobalGenerator) {
1919     // restore the original environment variables CXX and CC
1920     std::string env = "CC=";
1921     if (!this->CCEnvironment.empty()) {
1922       env += this->CCEnvironment;
1923       cmSystemTools::PutEnv(env);
1924     } else {
1925       cmSystemTools::UnPutEnv(env);
1926     }
1927     env = "CXX=";
1928     if (!this->CXXEnvironment.empty()) {
1929       env += this->CXXEnvironment;
1930       cmSystemTools::PutEnv(env);
1931     } else {
1932       cmSystemTools::UnPutEnv(env);
1933     }
1934   }
1935
1936   // set the new
1937   this->GlobalGenerator = std::move(gg);
1938
1939   // set the global flag for unix style paths on cmSystemTools as soon as
1940   // the generator is set.  This allows gmake to be used on windows.
1941   cmSystemTools::SetForceUnixPaths(this->GlobalGenerator->GetForceUnixPaths());
1942
1943   // Save the environment variables CXX and CC
1944   if (!cmSystemTools::GetEnv("CXX", this->CXXEnvironment)) {
1945     this->CXXEnvironment.clear();
1946   }
1947   if (!cmSystemTools::GetEnv("CC", this->CCEnvironment)) {
1948     this->CCEnvironment.clear();
1949   }
1950 }
1951
1952 int cmake::DoPreConfigureChecks()
1953 {
1954   // Make sure the Source directory contains a CMakeLists.txt file.
1955   std::string srcList = cmStrCat(this->GetHomeDirectory(), "/CMakeLists.txt");
1956   if (!cmSystemTools::FileExists(srcList)) {
1957     std::ostringstream err;
1958     if (cmSystemTools::FileIsDirectory(this->GetHomeDirectory())) {
1959       err << "The source directory \"" << this->GetHomeDirectory()
1960           << "\" does not appear to contain CMakeLists.txt.\n";
1961     } else if (cmSystemTools::FileExists(this->GetHomeDirectory())) {
1962       err << "The source directory \"" << this->GetHomeDirectory()
1963           << "\" is a file, not a directory.\n";
1964     } else {
1965       err << "The source directory \"" << this->GetHomeDirectory()
1966           << "\" does not exist.\n";
1967     }
1968     err << "Specify --help for usage, or press the help button on the CMake "
1969            "GUI.";
1970     cmSystemTools::Error(err.str());
1971     return -2;
1972   }
1973
1974   // do a sanity check on some values
1975   if (this->State->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY")) {
1976     std::string cacheStart =
1977       cmStrCat(*this->State->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"),
1978                "/CMakeLists.txt");
1979     std::string currentStart =
1980       cmStrCat(this->GetHomeDirectory(), "/CMakeLists.txt");
1981     if (!cmSystemTools::SameFile(cacheStart, currentStart)) {
1982       std::string message =
1983         cmStrCat("The source \"", currentStart,
1984                  "\" does not match the source \"", cacheStart,
1985                  "\" used to generate cache.  Re-run cmake with a different "
1986                  "source directory.");
1987       cmSystemTools::Error(message);
1988       return -2;
1989     }
1990   } else {
1991     return 0;
1992   }
1993   return 1;
1994 }
1995 struct SaveCacheEntry
1996 {
1997   std::string key;
1998   std::string value;
1999   std::string help;
2000   cmStateEnums::CacheEntryType type;
2001 };
2002
2003 int cmake::HandleDeleteCacheVariables(const std::string& var)
2004 {
2005   std::vector<std::string> argsSplit = cmExpandedList(var, true);
2006   // erase the property to avoid infinite recursion
2007   this->State->SetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_", "");
2008   if (this->GetIsInTryCompile()) {
2009     return 0;
2010   }
2011   std::vector<SaveCacheEntry> saved;
2012   std::ostringstream warning;
2013   /* clang-format off */
2014   warning
2015     << "You have changed variables that require your cache to be deleted.\n"
2016     << "Configure will be re-run and you may have to reset some variables.\n"
2017     << "The following variables have changed:\n";
2018   /* clang-format on */
2019   for (auto i = argsSplit.begin(); i != argsSplit.end(); ++i) {
2020     SaveCacheEntry save;
2021     save.key = *i;
2022     warning << *i << "= ";
2023     i++;
2024     if (i != argsSplit.end()) {
2025       save.value = *i;
2026       warning << *i << "\n";
2027     } else {
2028       warning << "\n";
2029       i -= 1;
2030     }
2031     cmValue existingValue = this->State->GetCacheEntryValue(save.key);
2032     if (existingValue) {
2033       save.type = this->State->GetCacheEntryType(save.key);
2034       if (cmValue help =
2035             this->State->GetCacheEntryProperty(save.key, "HELPSTRING")) {
2036         save.help = *help;
2037       }
2038     } else {
2039       save.type = cmStateEnums::CacheEntryType::UNINITIALIZED;
2040     }
2041     saved.push_back(std::move(save));
2042   }
2043
2044   // remove the cache
2045   this->DeleteCache(this->GetHomeOutputDirectory());
2046   // load the empty cache
2047   this->LoadCache();
2048   // restore the changed compilers
2049   for (SaveCacheEntry const& i : saved) {
2050     this->AddCacheEntry(i.key, i.value, i.help.c_str(), i.type);
2051   }
2052   cmSystemTools::Message(warning.str());
2053   // avoid reconfigure if there were errors
2054   if (!cmSystemTools::GetErrorOccurredFlag()) {
2055     // re-run configure
2056     return this->Configure();
2057   }
2058   return 0;
2059 }
2060
2061 int cmake::Configure()
2062 {
2063   DiagLevel diagLevel;
2064
2065   if (this->DiagLevels.count("deprecated") == 1) {
2066
2067     diagLevel = this->DiagLevels["deprecated"];
2068     if (diagLevel == DIAG_IGNORE) {
2069       this->SetSuppressDeprecatedWarnings(true);
2070       this->SetDeprecatedWarningsAsErrors(false);
2071     } else if (diagLevel == DIAG_WARN) {
2072       this->SetSuppressDeprecatedWarnings(false);
2073       this->SetDeprecatedWarningsAsErrors(false);
2074     } else if (diagLevel == DIAG_ERROR) {
2075       this->SetSuppressDeprecatedWarnings(false);
2076       this->SetDeprecatedWarningsAsErrors(true);
2077     }
2078   }
2079
2080   if (this->DiagLevels.count("dev") == 1) {
2081     bool setDeprecatedVariables = false;
2082
2083     cmValue cachedWarnDeprecated =
2084       this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
2085     cmValue cachedErrorDeprecated =
2086       this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
2087
2088     // don't overwrite deprecated warning setting from a previous invocation
2089     if (!cachedWarnDeprecated && !cachedErrorDeprecated) {
2090       setDeprecatedVariables = true;
2091     }
2092
2093     diagLevel = this->DiagLevels["dev"];
2094     if (diagLevel == DIAG_IGNORE) {
2095       this->SetSuppressDevWarnings(true);
2096       this->SetDevWarningsAsErrors(false);
2097
2098       if (setDeprecatedVariables) {
2099         this->SetSuppressDeprecatedWarnings(true);
2100         this->SetDeprecatedWarningsAsErrors(false);
2101       }
2102     } else if (diagLevel == DIAG_WARN) {
2103       this->SetSuppressDevWarnings(false);
2104       this->SetDevWarningsAsErrors(false);
2105
2106       if (setDeprecatedVariables) {
2107         this->SetSuppressDeprecatedWarnings(false);
2108         this->SetDeprecatedWarningsAsErrors(false);
2109       }
2110     } else if (diagLevel == DIAG_ERROR) {
2111       this->SetSuppressDevWarnings(false);
2112       this->SetDevWarningsAsErrors(true);
2113
2114       if (setDeprecatedVariables) {
2115         this->SetSuppressDeprecatedWarnings(false);
2116         this->SetDeprecatedWarningsAsErrors(true);
2117       }
2118     }
2119   }
2120
2121   // Cache variables may have already been set by a previous invocation,
2122   // so we cannot rely on command line options alone. Always ensure our
2123   // messenger is in sync with the cache.
2124   cmValue value = this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
2125   this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(*value));
2126
2127   value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
2128   this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value));
2129
2130   value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_WARNINGS");
2131   this->Messenger->SetSuppressDevWarnings(cmIsOn(value));
2132
2133   value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_ERRORS");
2134   this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(*value));
2135
2136   int ret = this->ActualConfigure();
2137   cmValue delCacheVars =
2138     this->State->GetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_");
2139   if (delCacheVars && !delCacheVars->empty()) {
2140     return this->HandleDeleteCacheVariables(*delCacheVars);
2141   }
2142   return ret;
2143 }
2144
2145 int cmake::ActualConfigure()
2146 {
2147   // Construct right now our path conversion table before it's too late:
2148   this->UpdateConversionPathTable();
2149   this->CleanupCommandsAndMacros();
2150
2151   cmSystemTools::RemoveADirectory(this->GetHomeOutputDirectory() +
2152                                   "/CMakeFiles/CMakeScratch");
2153
2154   int res = this->DoPreConfigureChecks();
2155   if (res < 0) {
2156     return -2;
2157   }
2158   if (!res) {
2159     this->AddCacheEntry(
2160       "CMAKE_HOME_DIRECTORY", this->GetHomeDirectory(),
2161       "Source directory with the top level CMakeLists.txt file for this "
2162       "project",
2163       cmStateEnums::INTERNAL);
2164   }
2165
2166   // We want to create the package redirects directory as early as possible,
2167   // but not before pre-configure checks have passed. This ensures we get
2168   // errors about inappropriate source/binary directories first.
2169   const auto redirectsDir =
2170     cmStrCat(this->GetHomeOutputDirectory(), "/CMakeFiles/pkgRedirects");
2171   cmSystemTools::RemoveADirectory(redirectsDir);
2172   if (!cmSystemTools::MakeDirectory(redirectsDir)) {
2173     cmSystemTools::Error(
2174       "Unable to (re)create the private pkgRedirects directory:\n" +
2175       redirectsDir);
2176     return -1;
2177   }
2178   this->AddCacheEntry("CMAKE_FIND_PACKAGE_REDIRECTS_DIR", redirectsDir,
2179                       "Value Computed by CMake.", cmStateEnums::STATIC);
2180
2181   // no generator specified on the command line
2182   if (!this->GlobalGenerator) {
2183     cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
2184     cmValue extraGenName =
2185       this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
2186     if (genName) {
2187       std::string fullName =
2188         cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
2189           *genName, extraGenName ? *extraGenName : "");
2190       this->GlobalGenerator = this->CreateGlobalGenerator(fullName);
2191     }
2192     if (this->GlobalGenerator) {
2193       // set the global flag for unix style paths on cmSystemTools as
2194       // soon as the generator is set.  This allows gmake to be used
2195       // on windows.
2196       cmSystemTools::SetForceUnixPaths(
2197         this->GlobalGenerator->GetForceUnixPaths());
2198     } else {
2199       this->CreateDefaultGlobalGenerator();
2200     }
2201     if (!this->GlobalGenerator) {
2202       cmSystemTools::Error("Could not create generator");
2203       return -1;
2204     }
2205   }
2206
2207   cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
2208   if (genName) {
2209     if (!this->GlobalGenerator->MatchesGeneratorName(*genName)) {
2210       std::string message =
2211         cmStrCat("Error: generator : ", this->GlobalGenerator->GetName(),
2212                  "\nDoes not match the generator used previously: ", *genName,
2213                  "\nEither remove the CMakeCache.txt file and CMakeFiles "
2214                  "directory or choose a different binary directory.");
2215       cmSystemTools::Error(message);
2216       return -2;
2217     }
2218   }
2219   if (!this->State->GetInitializedCacheValue("CMAKE_GENERATOR")) {
2220     this->AddCacheEntry("CMAKE_GENERATOR", this->GlobalGenerator->GetName(),
2221                         "Name of generator.", cmStateEnums::INTERNAL);
2222     this->AddCacheEntry(
2223       "CMAKE_EXTRA_GENERATOR", this->GlobalGenerator->GetExtraGeneratorName(),
2224       "Name of external makefile project generator.", cmStateEnums::INTERNAL);
2225
2226     if (!this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) {
2227       std::string envToolchain;
2228       if (cmSystemTools::GetEnv("CMAKE_TOOLCHAIN_FILE", envToolchain) &&
2229           !envToolchain.empty()) {
2230         this->AddCacheEntry("CMAKE_TOOLCHAIN_FILE", envToolchain,
2231                             "The CMake toolchain file",
2232                             cmStateEnums::FILEPATH);
2233       }
2234     }
2235   }
2236
2237   if (cmValue instance =
2238         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_INSTANCE")) {
2239     if (this->GeneratorInstanceSet && this->GeneratorInstance != *instance) {
2240       std::string message =
2241         cmStrCat("Error: generator instance: ", this->GeneratorInstance,
2242                  "\nDoes not match the instance used previously: ", *instance,
2243                  "\nEither remove the CMakeCache.txt file and CMakeFiles "
2244                  "directory or choose a different binary directory.");
2245       cmSystemTools::Error(message);
2246       return -2;
2247     }
2248   } else {
2249     this->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance,
2250                         "Generator instance identifier.",
2251                         cmStateEnums::INTERNAL);
2252   }
2253
2254   if (cmValue platformName =
2255         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_PLATFORM")) {
2256     if (this->GeneratorPlatformSet &&
2257         this->GeneratorPlatform != *platformName) {
2258       std::string message = cmStrCat(
2259         "Error: generator platform: ", this->GeneratorPlatform,
2260         "\nDoes not match the platform used previously: ", *platformName,
2261         "\nEither remove the CMakeCache.txt file and CMakeFiles "
2262         "directory or choose a different binary directory.");
2263       cmSystemTools::Error(message);
2264       return -2;
2265     }
2266   } else {
2267     this->AddCacheEntry("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform,
2268                         "Name of generator platform.", cmStateEnums::INTERNAL);
2269   }
2270
2271   if (cmValue tsName =
2272         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_TOOLSET")) {
2273     if (this->GeneratorToolsetSet && this->GeneratorToolset != *tsName) {
2274       std::string message =
2275         cmStrCat("Error: generator toolset: ", this->GeneratorToolset,
2276                  "\nDoes not match the toolset used previously: ", *tsName,
2277                  "\nEither remove the CMakeCache.txt file and CMakeFiles "
2278                  "directory or choose a different binary directory.");
2279       cmSystemTools::Error(message);
2280       return -2;
2281     }
2282   } else {
2283     this->AddCacheEntry("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset,
2284                         "Name of generator toolset.", cmStateEnums::INTERNAL);
2285   }
2286
2287   // reset any system configuration information, except for when we are
2288   // InTryCompile. With TryCompile the system info is taken from the parent's
2289   // info to save time
2290   if (!this->GetIsInTryCompile()) {
2291     this->GlobalGenerator->ClearEnabledLanguages();
2292
2293     this->TruncateOutputLog("CMakeOutput.log");
2294     this->TruncateOutputLog("CMakeError.log");
2295   }
2296
2297 #if !defined(CMAKE_BOOTSTRAP)
2298   this->FileAPI = cm::make_unique<cmFileAPI>(this);
2299   this->FileAPI->ReadQueries();
2300 #endif
2301
2302   // actually do the configure
2303   this->GlobalGenerator->Configure();
2304   // Before saving the cache
2305   // if the project did not define one of the entries below, add them now
2306   // so users can edit the values in the cache:
2307
2308   // We used to always present LIBRARY_OUTPUT_PATH and
2309   // EXECUTABLE_OUTPUT_PATH.  They are now documented as old-style and
2310   // should no longer be used.  Therefore we present them only if the
2311   // project requires compatibility with CMake 2.4.  We detect this
2312   // here by looking for the old CMAKE_BACKWARDS_COMPATIBILITY
2313   // variable created when CMP0001 is not set to NEW.
2314   if (this->State->GetInitializedCacheValue("CMAKE_BACKWARDS_COMPATIBILITY")) {
2315     if (!this->State->GetInitializedCacheValue("LIBRARY_OUTPUT_PATH")) {
2316       this->AddCacheEntry(
2317         "LIBRARY_OUTPUT_PATH", "",
2318         "Single output directory for building all libraries.",
2319         cmStateEnums::PATH);
2320     }
2321     if (!this->State->GetInitializedCacheValue("EXECUTABLE_OUTPUT_PATH")) {
2322       this->AddCacheEntry(
2323         "EXECUTABLE_OUTPUT_PATH", "",
2324         "Single output directory for building all executables.",
2325         cmStateEnums::PATH);
2326     }
2327   }
2328
2329   const auto& mf = this->GlobalGenerator->GetMakefiles()[0];
2330   if (mf->IsOn("CTEST_USE_LAUNCHERS") &&
2331       !this->State->GetGlobalProperty("RULE_LAUNCH_COMPILE")) {
2332     cmSystemTools::Error(
2333       "CTEST_USE_LAUNCHERS is enabled, but the "
2334       "RULE_LAUNCH_COMPILE global property is not defined.\n"
2335       "Did you forget to include(CTest) in the toplevel "
2336       "CMakeLists.txt ?");
2337   }
2338
2339   this->State->SaveVerificationScript(this->GetHomeOutputDirectory(),
2340                                       this->Messenger.get());
2341   this->SaveCache(this->GetHomeOutputDirectory());
2342   if (cmSystemTools::GetErrorOccurredFlag()) {
2343     return -1;
2344   }
2345   return 0;
2346 }
2347
2348 std::unique_ptr<cmGlobalGenerator> cmake::EvaluateDefaultGlobalGenerator()
2349 {
2350   if (!this->EnvironmentGenerator.empty()) {
2351     auto gen = this->CreateGlobalGenerator(this->EnvironmentGenerator);
2352     if (!gen) {
2353       cmSystemTools::Error("CMAKE_GENERATOR was set but the specified "
2354                            "generator doesn't exist. Using CMake default.");
2355     } else {
2356       return gen;
2357     }
2358   }
2359 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
2360   std::string found;
2361   // Try to find the newest VS installed on the computer and
2362   // use that as a default if -G is not specified
2363   const std::string vsregBase = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\";
2364   static const char* const vsVariants[] = {
2365     /* clang-format needs this comment to break after the opening brace */
2366     "VisualStudio\\", "VCExpress\\", "WDExpress\\"
2367   };
2368   struct VSVersionedGenerator
2369   {
2370     const char* MSVersion;
2371     const char* GeneratorName;
2372   };
2373   static VSVersionedGenerator const vsGenerators[] = {
2374     { "14.0", "Visual Studio 14 2015" }, //
2375     { "12.0", "Visual Studio 12 2013" }, //
2376     { "11.0", "Visual Studio 11 2012" }, //
2377     { "9.0", "Visual Studio 9 2008" }
2378   };
2379   static const char* const vsEntries[] = {
2380     "\\Setup\\VC;ProductDir", //
2381     ";InstallDir"             //
2382   };
2383   if (cmVSSetupAPIHelper(17).IsVSInstalled()) {
2384     found = "Visual Studio 17 2022";
2385   } else if (cmVSSetupAPIHelper(16).IsVSInstalled()) {
2386     found = "Visual Studio 16 2019";
2387   } else if (cmVSSetupAPIHelper(15).IsVSInstalled()) {
2388     found = "Visual Studio 15 2017";
2389   } else {
2390     for (VSVersionedGenerator const* g = cm::cbegin(vsGenerators);
2391          found.empty() && g != cm::cend(vsGenerators); ++g) {
2392       for (const char* const* v = cm::cbegin(vsVariants);
2393            found.empty() && v != cm::cend(vsVariants); ++v) {
2394         for (const char* const* e = cm::cbegin(vsEntries);
2395              found.empty() && e != cm::cend(vsEntries); ++e) {
2396           std::string const reg = vsregBase + *v + g->MSVersion + *e;
2397           std::string dir;
2398           if (cmSystemTools::ReadRegistryValue(reg, dir,
2399                                                cmSystemTools::KeyWOW64_32) &&
2400               cmSystemTools::PathExists(dir)) {
2401             found = g->GeneratorName;
2402           }
2403         }
2404       }
2405     }
2406   }
2407   auto gen = this->CreateGlobalGenerator(found);
2408   if (!gen) {
2409     gen = cm::make_unique<cmGlobalNMakeMakefileGenerator>(this);
2410   }
2411   return std::unique_ptr<cmGlobalGenerator>(std::move(gen));
2412 #elif defined(CMAKE_BOOTSTRAP_NINJA)
2413   return std::unique_ptr<cmGlobalGenerator>(
2414     cm::make_unique<cmGlobalNinjaGenerator>(this));
2415 #else
2416   return std::unique_ptr<cmGlobalGenerator>(
2417     cm::make_unique<cmGlobalUnixMakefileGenerator3>(this));
2418 #endif
2419 }
2420
2421 void cmake::CreateDefaultGlobalGenerator()
2422 {
2423   auto gen = this->EvaluateDefaultGlobalGenerator();
2424 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
2425   // This print could be unified for all platforms
2426   std::cout << "-- Building for: " << gen->GetName() << "\n";
2427 #endif
2428   this->SetGlobalGenerator(std::move(gen));
2429 }
2430
2431 void cmake::PreLoadCMakeFiles()
2432 {
2433   std::vector<std::string> args;
2434   std::string pre_load = this->GetHomeDirectory();
2435   if (!pre_load.empty()) {
2436     pre_load += "/PreLoad.cmake";
2437     if (cmSystemTools::FileExists(pre_load)) {
2438       this->ReadListFile(args, pre_load);
2439     }
2440   }
2441   pre_load = this->GetHomeOutputDirectory();
2442   if (!pre_load.empty()) {
2443     pre_load += "/PreLoad.cmake";
2444     if (cmSystemTools::FileExists(pre_load)) {
2445       this->ReadListFile(args, pre_load);
2446     }
2447   }
2448 }
2449
2450 // handle a command line invocation
2451 int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
2452 {
2453   // Process the arguments
2454   this->SetArgs(args);
2455   if (cmSystemTools::GetErrorOccurredFlag()) {
2456     return -1;
2457   }
2458   if (this->GetWorkingMode() == HELP_MODE) {
2459     return 0;
2460   }
2461
2462   // Log the trace format version to the desired output
2463   if (this->GetTrace()) {
2464     this->PrintTraceFormatVersion();
2465   }
2466
2467   // If we are given a stamp list file check if it is really out of date.
2468   if (!this->CheckStampList.empty() &&
2469       cmakeCheckStampList(this->CheckStampList)) {
2470     return 0;
2471   }
2472
2473   // If we are given a stamp file check if it is really out of date.
2474   if (!this->CheckStampFile.empty() &&
2475       cmakeCheckStampFile(this->CheckStampFile)) {
2476     return 0;
2477   }
2478
2479   if (this->GetWorkingMode() == NORMAL_MODE) {
2480     if (this->FreshCache) {
2481       this->DeleteCache(this->GetHomeOutputDirectory());
2482     }
2483     // load the cache
2484     if (this->LoadCache() < 0) {
2485       cmSystemTools::Error("Error executing cmake::LoadCache(). Aborting.\n");
2486       return -1;
2487     }
2488   } else {
2489     if (this->FreshCache) {
2490       cmSystemTools::Error("--fresh allowed only when configuring a project");
2491       return -1;
2492     }
2493     this->AddCMakePaths();
2494   }
2495
2496 #ifndef CMAKE_BOOTSTRAP
2497   this->ProcessPresetVariables();
2498   this->ProcessPresetEnvironment();
2499 #endif
2500   // Add any cache args
2501   if (!this->SetCacheArgs(args)) {
2502     cmSystemTools::Error("Run 'cmake --help' for all supported options.");
2503     return -1;
2504   }
2505 #ifndef CMAKE_BOOTSTRAP
2506   this->PrintPresetVariables();
2507   this->PrintPresetEnvironment();
2508 #endif
2509
2510   // In script mode we terminate after running the script.
2511   if (this->GetWorkingMode() != NORMAL_MODE) {
2512     if (cmSystemTools::GetErrorOccurredFlag()) {
2513       return -1;
2514     }
2515     return 0;
2516   }
2517
2518   // If MAKEFLAGS are given in the environment, remove the environment
2519   // variable.  This will prevent try-compile from succeeding when it
2520   // should fail (if "-i" is an option).  We cannot simply test
2521   // whether "-i" is given and remove it because some make programs
2522   // encode the MAKEFLAGS variable in a strange way.
2523   if (cmSystemTools::HasEnv("MAKEFLAGS")) {
2524     cmSystemTools::PutEnv("MAKEFLAGS=");
2525   }
2526
2527   this->PreLoadCMakeFiles();
2528
2529   if (noconfigure) {
2530     return 0;
2531   }
2532
2533   // now run the global generate
2534   // Check the state of the build system to see if we need to regenerate.
2535   if (!this->CheckBuildSystem()) {
2536     return 0;
2537   }
2538
2539   int ret = this->Configure();
2540   if (ret) {
2541 #if defined(CMAKE_HAVE_VS_GENERATORS)
2542     if (!this->VSSolutionFile.empty() && this->GlobalGenerator) {
2543       // CMake is running to regenerate a Visual Studio build tree
2544       // during a build from the VS IDE.  The build files cannot be
2545       // regenerated, so we should stop the build.
2546       cmSystemTools::Message("CMake Configure step failed.  "
2547                              "Build files cannot be regenerated correctly.  "
2548                              "Attempting to stop IDE build.");
2549       cmGlobalVisualStudioGenerator& gg =
2550         cm::static_reference_cast<cmGlobalVisualStudioGenerator>(
2551           this->GlobalGenerator);
2552       gg.CallVisualStudioMacro(cmGlobalVisualStudioGenerator::MacroStop,
2553                                this->VSSolutionFile);
2554     }
2555 #endif
2556     return ret;
2557   }
2558   ret = this->Generate();
2559   if (ret) {
2560     cmSystemTools::Message("CMake Generate step failed.  "
2561                            "Build files cannot be regenerated correctly.");
2562     return ret;
2563   }
2564   std::string message = cmStrCat("Build files have been written to: ",
2565                                  this->GetHomeOutputDirectory());
2566   this->UpdateProgress(message, -1);
2567   return ret;
2568 }
2569
2570 int cmake::Generate()
2571 {
2572   if (!this->GlobalGenerator) {
2573     return -1;
2574   }
2575   if (!this->GlobalGenerator->Compute()) {
2576     return -1;
2577   }
2578   this->GlobalGenerator->Generate();
2579   if (!this->GraphVizFile.empty()) {
2580     std::cout << "Generate graphviz: " << this->GraphVizFile << std::endl;
2581     this->GenerateGraphViz(this->GraphVizFile);
2582   }
2583   if (this->WarnUnusedCli) {
2584     this->RunCheckForUnusedVariables();
2585   }
2586   if (cmSystemTools::GetErrorOccurredFlag()) {
2587     return -1;
2588   }
2589   // Save the cache again after a successful Generate so that any internal
2590   // variables created during Generate are saved. (Specifically target GUIDs
2591   // for the Visual Studio and Xcode generators.)
2592   this->SaveCache(this->GetHomeOutputDirectory());
2593
2594 #if !defined(CMAKE_BOOTSTRAP)
2595   this->FileAPI->WriteReplies();
2596 #endif
2597
2598   return 0;
2599 }
2600
2601 void cmake::AddCacheEntry(const std::string& key, cmValue value,
2602                           const char* helpString, int type)
2603 {
2604   this->State->AddCacheEntry(key, value, helpString,
2605                              static_cast<cmStateEnums::CacheEntryType>(type));
2606   this->UnwatchUnusedCli(key);
2607
2608   if (key == "CMAKE_WARN_DEPRECATED"_s) {
2609     this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(value));
2610   } else if (key == "CMAKE_ERROR_DEPRECATED"_s) {
2611     this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value));
2612   } else if (key == "CMAKE_SUPPRESS_DEVELOPER_WARNINGS"_s) {
2613     this->Messenger->SetSuppressDevWarnings(cmIsOn(value));
2614   } else if (key == "CMAKE_SUPPRESS_DEVELOPER_ERRORS"_s) {
2615     this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(value));
2616   }
2617 }
2618
2619 bool cmake::DoWriteGlobVerifyTarget() const
2620 {
2621   return this->State->DoWriteGlobVerifyTarget();
2622 }
2623
2624 std::string const& cmake::GetGlobVerifyScript() const
2625 {
2626   return this->State->GetGlobVerifyScript();
2627 }
2628
2629 std::string const& cmake::GetGlobVerifyStamp() const
2630 {
2631   return this->State->GetGlobVerifyStamp();
2632 }
2633
2634 void cmake::AddGlobCacheEntry(bool recurse, bool listDirectories,
2635                               bool followSymlinks, const std::string& relative,
2636                               const std::string& expression,
2637                               const std::vector<std::string>& files,
2638                               const std::string& variable,
2639                               cmListFileBacktrace const& backtrace)
2640 {
2641   this->State->AddGlobCacheEntry(recurse, listDirectories, followSymlinks,
2642                                  relative, expression, files, variable,
2643                                  backtrace, this->Messenger.get());
2644 }
2645
2646 std::vector<std::string> cmake::GetAllExtensions() const
2647 {
2648   std::vector<std::string> allExt = this->CLikeSourceFileExtensions.ordered;
2649   allExt.insert(allExt.end(), this->HeaderFileExtensions.ordered.begin(),
2650                 this->HeaderFileExtensions.ordered.end());
2651   // cuda extensions are also in SourceFileExtensions so we ignore it here
2652   allExt.insert(allExt.end(), this->FortranFileExtensions.ordered.begin(),
2653                 this->FortranFileExtensions.ordered.end());
2654   allExt.insert(allExt.end(), this->HipFileExtensions.ordered.begin(),
2655                 this->HipFileExtensions.ordered.end());
2656   allExt.insert(allExt.end(), this->ISPCFileExtensions.ordered.begin(),
2657                 this->ISPCFileExtensions.ordered.end());
2658   return allExt;
2659 }
2660
2661 std::string cmake::StripExtension(const std::string& file) const
2662 {
2663   auto dotpos = file.rfind('.');
2664   if (dotpos != std::string::npos) {
2665 #if defined(_WIN32) || defined(__APPLE__)
2666     auto ext = cmSystemTools::LowerCase(file.substr(dotpos + 1));
2667 #else
2668     auto ext = cm::string_view(file).substr(dotpos + 1);
2669 #endif
2670     if (this->IsAKnownExtension(ext)) {
2671       return file.substr(0, dotpos);
2672     }
2673   }
2674   return file;
2675 }
2676
2677 cmValue cmake::GetCacheDefinition(const std::string& name) const
2678 {
2679   return this->State->GetInitializedCacheValue(name);
2680 }
2681
2682 void cmake::AddScriptingCommands() const
2683 {
2684   GetScriptingCommands(this->GetState());
2685 }
2686
2687 void cmake::AddProjectCommands() const
2688 {
2689   GetProjectCommands(this->GetState());
2690 }
2691
2692 void cmake::AddDefaultGenerators()
2693 {
2694 #if defined(_WIN32) && !defined(__CYGWIN__)
2695 #  if !defined(CMAKE_BOOT_MINGW)
2696   this->Generators.push_back(
2697     cmGlobalVisualStudioVersionedGenerator::NewFactory17());
2698   this->Generators.push_back(
2699     cmGlobalVisualStudioVersionedGenerator::NewFactory16());
2700   this->Generators.push_back(
2701     cmGlobalVisualStudioVersionedGenerator::NewFactory15());
2702   this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory());
2703   this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory());
2704   this->Generators.push_back(cmGlobalVisualStudio11Generator::NewFactory());
2705   this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory());
2706   this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory());
2707   this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory());
2708   this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory());
2709 #  endif
2710   this->Generators.push_back(cmGlobalMSYSMakefileGenerator::NewFactory());
2711   this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory());
2712 #endif
2713 #if !defined(CMAKE_BOOTSTRAP)
2714 #  if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
2715   this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory());
2716 #  endif
2717   this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
2718   this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory());
2719   this->Generators.push_back(cmGlobalNinjaMultiGenerator::NewFactory());
2720 #elif defined(CMAKE_BOOTSTRAP_NINJA)
2721   this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory());
2722 #elif defined(CMAKE_BOOTSTRAP_MAKEFILES)
2723   this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
2724 #endif
2725 #if defined(CMAKE_USE_WMAKE)
2726   this->Generators.push_back(cmGlobalWatcomWMakeGenerator::NewFactory());
2727 #endif
2728 #ifdef CMAKE_USE_XCODE
2729   this->Generators.push_back(cmGlobalXCodeGenerator::NewFactory());
2730 #endif
2731 }
2732
2733 bool cmake::ParseCacheEntry(const std::string& entry, std::string& var,
2734                             std::string& value,
2735                             cmStateEnums::CacheEntryType& type)
2736 {
2737   return cmState::ParseCacheEntry(entry, var, value, type);
2738 }
2739
2740 int cmake::LoadCache()
2741 {
2742   // could we not read the cache
2743   if (!this->LoadCache(this->GetHomeOutputDirectory())) {
2744     // if it does exist, but isn't readable then warn the user
2745     std::string cacheFile =
2746       cmStrCat(this->GetHomeOutputDirectory(), "/CMakeCache.txt");
2747     if (cmSystemTools::FileExists(cacheFile)) {
2748       cmSystemTools::Error(
2749         "There is a CMakeCache.txt file for the current binary tree but "
2750         "cmake does not have permission to read it. Please check the "
2751         "permissions of the directory you are trying to run CMake on.");
2752       return -1;
2753     }
2754   }
2755
2756   // setup CMAKE_ROOT and CMAKE_COMMAND
2757   if (!this->AddCMakePaths()) {
2758     return -3;
2759   }
2760   return 0;
2761 }
2762
2763 bool cmake::LoadCache(const std::string& path)
2764 {
2765   std::set<std::string> emptySet;
2766   return this->LoadCache(path, true, emptySet, emptySet);
2767 }
2768
2769 bool cmake::LoadCache(const std::string& path, bool internal,
2770                       std::set<std::string>& excludes,
2771                       std::set<std::string>& includes)
2772 {
2773   bool result = this->State->LoadCache(path, internal, excludes, includes);
2774   static const auto entries = { "CMAKE_CACHE_MAJOR_VERSION",
2775                                 "CMAKE_CACHE_MINOR_VERSION" };
2776   for (auto const& entry : entries) {
2777     this->UnwatchUnusedCli(entry);
2778   }
2779   return result;
2780 }
2781
2782 bool cmake::SaveCache(const std::string& path)
2783 {
2784   bool result = this->State->SaveCache(path, this->GetMessenger());
2785   static const auto entries = { "CMAKE_CACHE_MAJOR_VERSION",
2786                                 "CMAKE_CACHE_MINOR_VERSION",
2787                                 "CMAKE_CACHE_PATCH_VERSION",
2788                                 "CMAKE_CACHEFILE_DIR" };
2789   for (auto const& entry : entries) {
2790     this->UnwatchUnusedCli(entry);
2791   }
2792   return result;
2793 }
2794
2795 bool cmake::DeleteCache(const std::string& path)
2796 {
2797   return this->State->DeleteCache(path);
2798 }
2799
2800 void cmake::SetProgressCallback(ProgressCallbackType f)
2801 {
2802   this->ProgressCallback = std::move(f);
2803 }
2804
2805 void cmake::UpdateProgress(const std::string& msg, float prog)
2806 {
2807   if (this->ProgressCallback && !this->GetIsInTryCompile()) {
2808     this->ProgressCallback(msg, prog);
2809   }
2810 }
2811
2812 bool cmake::GetIsInTryCompile() const
2813 {
2814   return this->State->GetProjectKind() == cmState::ProjectKind::TryCompile;
2815 }
2816
2817 void cmake::AppendGlobalGeneratorsDocumentation(
2818   std::vector<cmDocumentationEntry>& v)
2819 {
2820   const auto defaultGenerator = this->EvaluateDefaultGlobalGenerator();
2821   const std::string defaultName = defaultGenerator->GetName();
2822   bool foundDefaultOne = false;
2823
2824   for (const auto& g : this->Generators) {
2825     cmDocumentationEntry e;
2826     g->GetDocumentation(e);
2827     if (!foundDefaultOne && cmHasPrefix(e.Name, defaultName)) {
2828       e.CustomNamePrefix = '*';
2829       foundDefaultOne = true;
2830     }
2831     v.push_back(std::move(e));
2832   }
2833 }
2834
2835 void cmake::AppendExtraGeneratorsDocumentation(
2836   std::vector<cmDocumentationEntry>& v)
2837 {
2838   for (cmExternalMakefileProjectGeneratorFactory* eg : this->ExtraGenerators) {
2839     const std::string doc = eg->GetDocumentation();
2840     const std::string name = eg->GetName();
2841
2842     // Aliases:
2843     for (std::string const& a : eg->Aliases) {
2844       cmDocumentationEntry e;
2845       e.Name = a;
2846       e.Brief = doc;
2847       v.push_back(std::move(e));
2848     }
2849
2850     // Full names:
2851     const std::vector<std::string> generators =
2852       eg->GetSupportedGlobalGenerators();
2853     for (std::string const& g : generators) {
2854       cmDocumentationEntry e;
2855       e.Name =
2856         cmExternalMakefileProjectGenerator::CreateFullGeneratorName(g, name);
2857       e.Brief = doc;
2858       v.push_back(std::move(e));
2859     }
2860   }
2861 }
2862
2863 std::vector<cmDocumentationEntry> cmake::GetGeneratorsDocumentation()
2864 {
2865   std::vector<cmDocumentationEntry> v;
2866   this->AppendGlobalGeneratorsDocumentation(v);
2867   this->AppendExtraGeneratorsDocumentation(v);
2868   return v;
2869 }
2870
2871 void cmake::PrintGeneratorList()
2872 {
2873 #ifndef CMAKE_BOOTSTRAP
2874   cmDocumentation doc;
2875   auto generators = this->GetGeneratorsDocumentation();
2876   doc.AppendSection("Generators", generators);
2877   std::cerr << "\n";
2878   doc.PrintDocumentation(cmDocumentation::ListGenerators, std::cerr);
2879 #endif
2880 }
2881
2882 void cmake::UpdateConversionPathTable()
2883 {
2884   // Update the path conversion table with any specified file:
2885   cmValue tablepath =
2886     this->State->GetInitializedCacheValue("CMAKE_PATH_TRANSLATION_FILE");
2887
2888   if (tablepath) {
2889     cmsys::ifstream table(tablepath->c_str());
2890     if (!table) {
2891       cmSystemTools::Error("CMAKE_PATH_TRANSLATION_FILE set to " + *tablepath +
2892                            ". CMake can not open file.");
2893       cmSystemTools::ReportLastSystemError("CMake can not open file.");
2894     } else {
2895       std::string a;
2896       std::string b;
2897       while (!table.eof()) {
2898         // two entries per line
2899         table >> a;
2900         table >> b;
2901         cmSystemTools::AddTranslationPath(a, b);
2902       }
2903     }
2904   }
2905 }
2906
2907 int cmake::CheckBuildSystem()
2908 {
2909   // We do not need to rerun CMake.  Check dependency integrity.
2910   const bool verbose = isCMakeVerbose();
2911
2912   // This method will check the integrity of the build system if the
2913   // option was given on the command line.  It reads the given file to
2914   // determine whether CMake should rerun.
2915
2916   // If no file is provided for the check, we have to rerun.
2917   if (this->CheckBuildSystemArgument.empty()) {
2918     if (verbose) {
2919       cmSystemTools::Stdout("Re-run cmake no build system arguments\n");
2920     }
2921     return 1;
2922   }
2923
2924   // If the file provided does not exist, we have to rerun.
2925   if (!cmSystemTools::FileExists(this->CheckBuildSystemArgument)) {
2926     if (verbose) {
2927       std::ostringstream msg;
2928       msg << "Re-run cmake missing file: " << this->CheckBuildSystemArgument
2929           << "\n";
2930       cmSystemTools::Stdout(msg.str());
2931     }
2932     return 1;
2933   }
2934
2935   // Read the rerun check file and use it to decide whether to do the
2936   // global generate.
2937   // Actually, all we need is the `set` command.
2938   cmake cm(RoleScript, cmState::Unknown);
2939   cm.SetHomeDirectory("");
2940   cm.SetHomeOutputDirectory("");
2941   cm.GetCurrentSnapshot().SetDefaultDefinitions();
2942   cmGlobalGenerator gg(&cm);
2943   cmMakefile mf(&gg, cm.GetCurrentSnapshot());
2944   if (!mf.ReadListFile(this->CheckBuildSystemArgument) ||
2945       cmSystemTools::GetErrorOccurredFlag()) {
2946     if (verbose) {
2947       std::ostringstream msg;
2948       msg << "Re-run cmake error reading : " << this->CheckBuildSystemArgument
2949           << "\n";
2950       cmSystemTools::Stdout(msg.str());
2951     }
2952     // There was an error reading the file.  Just rerun.
2953     return 1;
2954   }
2955
2956   if (this->ClearBuildSystem) {
2957     // Get the generator used for this build system.
2958     std::string genName = mf.GetSafeDefinition("CMAKE_DEPENDS_GENERATOR");
2959     if (!cmNonempty(genName)) {
2960       genName = "Unix Makefiles";
2961     }
2962
2963     // Create the generator and use it to clear the dependencies.
2964     std::unique_ptr<cmGlobalGenerator> ggd =
2965       this->CreateGlobalGenerator(genName);
2966     if (ggd) {
2967       cm.GetCurrentSnapshot().SetDefaultDefinitions();
2968       cmMakefile mfd(ggd.get(), cm.GetCurrentSnapshot());
2969       auto lgd = ggd->CreateLocalGenerator(&mfd);
2970       lgd->ClearDependencies(&mfd, verbose);
2971     }
2972   }
2973
2974   // If any byproduct of makefile generation is missing we must re-run.
2975   std::vector<std::string> products;
2976   mf.GetDefExpandList("CMAKE_MAKEFILE_PRODUCTS", products);
2977   for (std::string const& p : products) {
2978     if (!(cmSystemTools::FileExists(p) || cmSystemTools::FileIsSymlink(p))) {
2979       if (verbose) {
2980         std::ostringstream msg;
2981         msg << "Re-run cmake, missing byproduct: " << p << "\n";
2982         cmSystemTools::Stdout(msg.str());
2983       }
2984       return 1;
2985     }
2986   }
2987
2988   // Get the set of dependencies and outputs.
2989   std::vector<std::string> depends;
2990   std::vector<std::string> outputs;
2991   if (mf.GetDefExpandList("CMAKE_MAKEFILE_DEPENDS", depends)) {
2992     mf.GetDefExpandList("CMAKE_MAKEFILE_OUTPUTS", outputs);
2993   }
2994   if (depends.empty() || outputs.empty()) {
2995     // Not enough information was provided to do the test.  Just rerun.
2996     if (verbose) {
2997       cmSystemTools::Stdout("Re-run cmake no CMAKE_MAKEFILE_DEPENDS "
2998                             "or CMAKE_MAKEFILE_OUTPUTS :\n");
2999     }
3000     return 1;
3001   }
3002
3003   // Find the newest dependency.
3004   auto dep = depends.begin();
3005   std::string dep_newest = *dep++;
3006   for (; dep != depends.end(); ++dep) {
3007     int result = 0;
3008     if (this->FileTimeCache->Compare(dep_newest, *dep, &result)) {
3009       if (result < 0) {
3010         dep_newest = *dep;
3011       }
3012     } else {
3013       if (verbose) {
3014         cmSystemTools::Stdout(
3015           "Re-run cmake: build system dependency is missing\n");
3016       }
3017       return 1;
3018     }
3019   }
3020
3021   // Find the oldest output.
3022   auto out = outputs.begin();
3023   std::string out_oldest = *out++;
3024   for (; out != outputs.end(); ++out) {
3025     int result = 0;
3026     if (this->FileTimeCache->Compare(out_oldest, *out, &result)) {
3027       if (result > 0) {
3028         out_oldest = *out;
3029       }
3030     } else {
3031       if (verbose) {
3032         cmSystemTools::Stdout(
3033           "Re-run cmake: build system output is missing\n");
3034       }
3035       return 1;
3036     }
3037   }
3038
3039   // If any output is older than any dependency then rerun.
3040   {
3041     int result = 0;
3042     if (!this->FileTimeCache->Compare(out_oldest, dep_newest, &result) ||
3043         result < 0) {
3044       if (verbose) {
3045         std::ostringstream msg;
3046         msg << "Re-run cmake file: " << out_oldest
3047             << " older than: " << dep_newest << "\n";
3048         cmSystemTools::Stdout(msg.str());
3049       }
3050       return 1;
3051     }
3052   }
3053
3054   // No need to rerun.
3055   return 0;
3056 }
3057
3058 void cmake::TruncateOutputLog(const char* fname)
3059 {
3060   std::string fullPath = cmStrCat(this->GetHomeOutputDirectory(), '/', fname);
3061   struct stat st;
3062   if (::stat(fullPath.c_str(), &st)) {
3063     return;
3064   }
3065   if (!this->State->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR")) {
3066     cmSystemTools::RemoveFile(fullPath);
3067     return;
3068   }
3069   off_t fsize = st.st_size;
3070   const off_t maxFileSize = 50 * 1024;
3071   if (fsize < maxFileSize) {
3072     // TODO: truncate file
3073     return;
3074   }
3075 }
3076
3077 void cmake::MarkCliAsUsed(const std::string& variable)
3078 {
3079   this->UsedCliVariables[variable] = true;
3080 }
3081
3082 void cmake::GenerateGraphViz(const std::string& fileName) const
3083 {
3084 #ifndef CMAKE_BOOTSTRAP
3085   cmGraphVizWriter gvWriter(fileName, this->GetGlobalGenerator());
3086
3087   std::string settingsFile =
3088     cmStrCat(this->GetHomeOutputDirectory(), "/CMakeGraphVizOptions.cmake");
3089   std::string fallbackSettingsFile =
3090     cmStrCat(this->GetHomeDirectory(), "/CMakeGraphVizOptions.cmake");
3091
3092   gvWriter.ReadSettings(settingsFile, fallbackSettingsFile);
3093
3094   gvWriter.Write();
3095
3096 #endif
3097 }
3098
3099 void cmake::SetProperty(const std::string& prop, const char* value)
3100 {
3101   this->State->SetGlobalProperty(prop, value);
3102 }
3103 void cmake::SetProperty(const std::string& prop, cmValue value)
3104 {
3105   this->State->SetGlobalProperty(prop, value);
3106 }
3107
3108 void cmake::AppendProperty(const std::string& prop, const std::string& value,
3109                            bool asString)
3110 {
3111   this->State->AppendGlobalProperty(prop, value, asString);
3112 }
3113
3114 cmValue cmake::GetProperty(const std::string& prop)
3115 {
3116   return this->State->GetGlobalProperty(prop);
3117 }
3118
3119 bool cmake::GetPropertyAsBool(const std::string& prop)
3120 {
3121   return this->State->GetGlobalPropertyAsBool(prop);
3122 }
3123
3124 cmInstalledFile* cmake::GetOrCreateInstalledFile(cmMakefile* mf,
3125                                                  const std::string& name)
3126 {
3127   auto i = this->InstalledFiles.find(name);
3128
3129   if (i != this->InstalledFiles.end()) {
3130     cmInstalledFile& file = i->second;
3131     return &file;
3132   }
3133   cmInstalledFile& file = this->InstalledFiles[name];
3134   file.SetName(mf, name);
3135   return &file;
3136 }
3137
3138 cmInstalledFile const* cmake::GetInstalledFile(const std::string& name) const
3139 {
3140   auto i = this->InstalledFiles.find(name);
3141
3142   if (i != this->InstalledFiles.end()) {
3143     cmInstalledFile const& file = i->second;
3144     return &file;
3145   }
3146   return nullptr;
3147 }
3148
3149 int cmake::GetSystemInformation(std::vector<std::string>& args)
3150 {
3151   // so create the directory
3152   std::string resultFile;
3153   std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
3154   std::string destPath = cwd + "/__cmake_systeminformation";
3155   cmSystemTools::RemoveADirectory(destPath);
3156   if (!cmSystemTools::MakeDirectory(destPath)) {
3157     std::cerr << "Error: --system-information must be run from a "
3158                  "writable directory!\n";
3159     return 1;
3160   }
3161
3162   // process the arguments
3163   bool writeToStdout = true;
3164   for (unsigned int i = 1; i < args.size(); ++i) {
3165     std::string const& arg = args[i];
3166     if (cmHasLiteralPrefix(arg, "-G")) {
3167       std::string value = arg.substr(2);
3168       if (value.empty()) {
3169         ++i;
3170         if (i >= args.size()) {
3171           cmSystemTools::Error("No generator specified for -G");
3172           this->PrintGeneratorList();
3173           return -1;
3174         }
3175         value = args[i];
3176       }
3177       auto gen = this->CreateGlobalGenerator(value);
3178       if (!gen) {
3179         cmSystemTools::Error("Could not create named generator " + value);
3180         this->PrintGeneratorList();
3181       } else {
3182         this->SetGlobalGenerator(std::move(gen));
3183       }
3184     }
3185     // no option assume it is the output file
3186     else {
3187       if (!cmSystemTools::FileIsFullPath(arg)) {
3188         resultFile = cmStrCat(cwd, '/');
3189       }
3190       resultFile += arg;
3191       writeToStdout = false;
3192     }
3193   }
3194
3195   // we have to find the module directory, so we can copy the files
3196   this->AddCMakePaths();
3197   std::string modulesPath =
3198     cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules");
3199   std::string inFile = cmStrCat(modulesPath, "/SystemInformation.cmake");
3200   std::string outFile = cmStrCat(destPath, "/CMakeLists.txt");
3201
3202   // Copy file
3203   if (!cmsys::SystemTools::CopyFileAlways(inFile, outFile)) {
3204     std::cerr << "Error copying file \"" << inFile << "\" to \"" << outFile
3205               << "\".\n";
3206     return 1;
3207   }
3208
3209   // do we write to a file or to stdout?
3210   if (resultFile.empty()) {
3211     resultFile = cmStrCat(cwd, "/__cmake_systeminformation/results.txt");
3212   }
3213
3214   {
3215     // now run cmake on the CMakeLists file
3216     cmWorkingDirectory workdir(destPath);
3217     if (workdir.Failed()) {
3218       // We created the directory and we were able to copy the CMakeLists.txt
3219       // file to it, so we wouldn't expect to get here unless the default
3220       // permissions are questionable or some other process has deleted the
3221       // directory
3222       std::cerr << "Failed to change to directory " << destPath << " : "
3223                 << std::strerror(workdir.GetLastResult()) << std::endl;
3224       return 1;
3225     }
3226     std::vector<std::string> args2;
3227     args2.push_back(args[0]);
3228     args2.push_back(destPath);
3229     args2.push_back("-DRESULT_FILE=" + resultFile);
3230     int res = this->Run(args2, false);
3231
3232     if (res != 0) {
3233       std::cerr << "Error: --system-information failed on internal CMake!\n";
3234       return res;
3235     }
3236   }
3237
3238   // echo results to stdout if needed
3239   if (writeToStdout) {
3240     FILE* fin = cmsys::SystemTools::Fopen(resultFile, "r");
3241     if (fin) {
3242       const int bufferSize = 4096;
3243       char buffer[bufferSize];
3244       size_t n;
3245       while ((n = fread(buffer, 1, bufferSize, fin)) > 0) {
3246         for (char* c = buffer; c < buffer + n; ++c) {
3247           putc(*c, stdout);
3248         }
3249         fflush(stdout);
3250       }
3251       fclose(fin);
3252     }
3253   }
3254
3255   // clean up the directory
3256   cmSystemTools::RemoveADirectory(destPath);
3257   return 0;
3258 }
3259
3260 static bool cmakeCheckStampFile(const std::string& stampName)
3261 {
3262   // The stamp file does not exist.  Use the stamp dependencies to
3263   // determine whether it is really out of date.  This works in
3264   // conjunction with cmLocalVisualStudio7Generator to avoid
3265   // repeatedly re-running CMake when the user rebuilds the entire
3266   // solution.
3267   std::string stampDepends = cmStrCat(stampName, ".depend");
3268 #if defined(_WIN32) || defined(__CYGWIN__)
3269   cmsys::ifstream fin(stampDepends.c_str(), std::ios::in | std::ios::binary);
3270 #else
3271   cmsys::ifstream fin(stampDepends.c_str());
3272 #endif
3273   if (!fin) {
3274     // The stamp dependencies file cannot be read.  Just assume the
3275     // build system is really out of date.
3276     std::cout << "CMake is re-running because " << stampName
3277               << " dependency file is missing.\n";
3278     return false;
3279   }
3280
3281   // Compare the stamp dependencies against the dependency file itself.
3282   {
3283     cmFileTimeCache ftc;
3284     std::string dep;
3285     while (cmSystemTools::GetLineFromStream(fin, dep)) {
3286       int result;
3287       if (!dep.empty() && dep[0] != '#' &&
3288           (!ftc.Compare(stampDepends, dep, &result) || result < 0)) {
3289         // The stamp depends file is older than this dependency.  The
3290         // build system is really out of date.
3291         std::cout << "CMake is re-running because " << stampName
3292                   << " is out-of-date.\n";
3293         std::cout << "  the file '" << dep << "'\n";
3294         std::cout << "  is newer than '" << stampDepends << "'\n";
3295         std::cout << "  result='" << result << "'\n";
3296         return false;
3297       }
3298     }
3299   }
3300
3301   // The build system is up to date.  The stamp file has been removed
3302   // by the VS IDE due to a "rebuild" request.  Restore it atomically.
3303   std::ostringstream stampTempStream;
3304   stampTempStream << stampName << ".tmp" << cmSystemTools::RandomSeed();
3305   std::string stampTemp = stampTempStream.str();
3306   {
3307     // TODO: Teach cmGeneratedFileStream to use a random temp file (with
3308     // multiple tries in unlikely case of conflict) and use that here.
3309     cmsys::ofstream stamp(stampTemp.c_str());
3310     stamp << "# CMake generation timestamp file for this directory.\n";
3311   }
3312   std::string err;
3313   if (cmSystemTools::RenameFile(stampTemp, stampName,
3314                                 cmSystemTools::Replace::Yes, &err) ==
3315       cmSystemTools::RenameResult::Success) {
3316     // CMake does not need to re-run because the stamp file is up-to-date.
3317     return true;
3318   }
3319   cmSystemTools::RemoveFile(stampTemp);
3320   cmSystemTools::Error(
3321     cmStrCat("Cannot restore timestamp \"", stampName, "\": ", err));
3322   return false;
3323 }
3324
3325 static bool cmakeCheckStampList(const std::string& stampList)
3326 {
3327   // If the stamp list does not exist CMake must rerun to generate it.
3328   if (!cmSystemTools::FileExists(stampList)) {
3329     std::cout << "CMake is re-running because generate.stamp.list "
3330               << "is missing.\n";
3331     return false;
3332   }
3333   cmsys::ifstream fin(stampList.c_str());
3334   if (!fin) {
3335     std::cout << "CMake is re-running because generate.stamp.list "
3336               << "could not be read.\n";
3337     return false;
3338   }
3339
3340   // Check each stamp.
3341   std::string stampName;
3342   while (cmSystemTools::GetLineFromStream(fin, stampName)) {
3343     if (!cmakeCheckStampFile(stampName)) {
3344       return false;
3345     }
3346   }
3347   return true;
3348 }
3349
3350 void cmake::IssueMessage(MessageType t, std::string const& text,
3351                          cmListFileBacktrace const& backtrace) const
3352 {
3353   this->Messenger->IssueMessage(t, text, backtrace);
3354 }
3355
3356 std::vector<std::string> cmake::GetDebugConfigs()
3357 {
3358   std::vector<std::string> configs;
3359   if (cmValue config_list =
3360         this->State->GetGlobalProperty("DEBUG_CONFIGURATIONS")) {
3361     // Expand the specified list and convert to upper-case.
3362     cmExpandList(*config_list, configs);
3363     std::transform(configs.begin(), configs.end(), configs.begin(),
3364                    cmSystemTools::UpperCase);
3365   }
3366   // If no configurations were specified, use a default list.
3367   if (configs.empty()) {
3368     configs.emplace_back("DEBUG");
3369   }
3370   return configs;
3371 }
3372
3373 int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
3374                  std::string config, std::vector<std::string> nativeOptions,
3375                  cmBuildOptions& buildOptions, bool verbose,
3376                  const std::string& presetName, bool listPresets)
3377 {
3378   this->SetHomeDirectory("");
3379   this->SetHomeOutputDirectory("");
3380
3381 #if !defined(CMAKE_BOOTSTRAP)
3382   if (!presetName.empty() || listPresets) {
3383     this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
3384     this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
3385
3386     cmCMakePresetsGraph settingsFile;
3387     auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
3388     if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
3389       cmSystemTools::Error(
3390         cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
3391                  ": ", cmCMakePresetsGraph::ResultToString(result)));
3392       return 1;
3393     }
3394
3395     if (listPresets) {
3396       settingsFile.PrintBuildPresetList();
3397       return 0;
3398     }
3399
3400     auto presetPair = settingsFile.BuildPresets.find(presetName);
3401     if (presetPair == settingsFile.BuildPresets.end()) {
3402       cmSystemTools::Error(cmStrCat("No such build preset in ",
3403                                     this->GetHomeDirectory(), ": \"",
3404                                     presetName, '"'));
3405       settingsFile.PrintBuildPresetList();
3406       return 1;
3407     }
3408
3409     if (presetPair->second.Unexpanded.Hidden) {
3410       cmSystemTools::Error(cmStrCat("Cannot use hidden build preset in ",
3411                                     this->GetHomeDirectory(), ": \"",
3412                                     presetName, '"'));
3413       settingsFile.PrintBuildPresetList();
3414       return 1;
3415     }
3416
3417     auto const& expandedPreset = presetPair->second.Expanded;
3418     if (!expandedPreset) {
3419       cmSystemTools::Error(cmStrCat("Could not evaluate build preset \"",
3420                                     presetName,
3421                                     "\": Invalid macro expansion"));
3422       settingsFile.PrintBuildPresetList();
3423       return 1;
3424     }
3425
3426     if (!expandedPreset->ConditionResult) {
3427       cmSystemTools::Error(cmStrCat("Cannot use disabled build preset in ",
3428                                     this->GetHomeDirectory(), ": \"",
3429                                     presetName, '"'));
3430       settingsFile.PrintBuildPresetList();
3431       return 1;
3432     }
3433
3434     auto configurePresetPair =
3435       settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
3436     if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
3437       cmSystemTools::Error(cmStrCat("No such configure preset in ",
3438                                     this->GetHomeDirectory(), ": \"",
3439                                     expandedPreset->ConfigurePreset, '"'));
3440       this->PrintPresetList(settingsFile);
3441       return 1;
3442     }
3443
3444     if (configurePresetPair->second.Unexpanded.Hidden) {
3445       cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ",
3446                                     this->GetHomeDirectory(), ": \"",
3447                                     expandedPreset->ConfigurePreset, '"'));
3448       this->PrintPresetList(settingsFile);
3449       return 1;
3450     }
3451
3452     auto const& expandedConfigurePreset = configurePresetPair->second.Expanded;
3453     if (!expandedConfigurePreset) {
3454       cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"",
3455                                     expandedPreset->ConfigurePreset,
3456                                     "\": Invalid macro expansion"));
3457       return 1;
3458     }
3459
3460     if (!expandedConfigurePreset->BinaryDir.empty()) {
3461       dir = expandedConfigurePreset->BinaryDir;
3462     }
3463
3464     this->UnprocessedPresetEnvironment = expandedPreset->Environment;
3465     this->ProcessPresetEnvironment();
3466
3467     if ((jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL ||
3468          jobs == cmake::NO_BUILD_PARALLEL_LEVEL) &&
3469         expandedPreset->Jobs) {
3470       jobs = *expandedPreset->Jobs;
3471     }
3472
3473     if (targets.empty()) {
3474       targets.insert(targets.begin(), expandedPreset->Targets.begin(),
3475                      expandedPreset->Targets.end());
3476     }
3477
3478     if (config.empty()) {
3479       config = expandedPreset->Configuration;
3480     }
3481
3482     if (!buildOptions.Clean && expandedPreset->CleanFirst) {
3483       buildOptions.Clean = *expandedPreset->CleanFirst;
3484     }
3485
3486     if (buildOptions.ResolveMode == PackageResolveMode::Default &&
3487         expandedPreset->ResolvePackageReferences) {
3488       buildOptions.ResolveMode = *expandedPreset->ResolvePackageReferences;
3489     }
3490
3491     if (!verbose && expandedPreset->Verbose) {
3492       verbose = *expandedPreset->Verbose;
3493     }
3494
3495     if (nativeOptions.empty()) {
3496       nativeOptions.insert(nativeOptions.begin(),
3497                            expandedPreset->NativeToolOptions.begin(),
3498                            expandedPreset->NativeToolOptions.end());
3499     }
3500   }
3501 #endif
3502
3503   if (!cmSystemTools::FileIsDirectory(dir)) {
3504     std::cerr << "Error: " << dir << " is not a directory\n";
3505     return 1;
3506   }
3507
3508   std::string cachePath = FindCacheFile(dir);
3509   if (!this->LoadCache(cachePath)) {
3510     std::cerr << "Error: could not load cache\n";
3511     return 1;
3512   }
3513   cmValue cachedGenerator = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
3514   if (!cachedGenerator) {
3515     std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
3516     return 1;
3517   }
3518   auto gen = this->CreateGlobalGenerator(*cachedGenerator);
3519   if (!gen) {
3520     std::cerr << "Error: could not create CMAKE_GENERATOR \""
3521               << *cachedGenerator << "\"\n";
3522     return 1;
3523   }
3524   this->SetGlobalGenerator(std::move(gen));
3525   cmValue cachedGeneratorInstance =
3526     this->State->GetCacheEntryValue("CMAKE_GENERATOR_INSTANCE");
3527   if (cachedGeneratorInstance) {
3528     cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
3529     if (!this->GlobalGenerator->SetGeneratorInstance(*cachedGeneratorInstance,
3530                                                      &mf)) {
3531       return 1;
3532     }
3533   }
3534   cmValue cachedGeneratorPlatform =
3535     this->State->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
3536   if (cachedGeneratorPlatform) {
3537     cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
3538     if (!this->GlobalGenerator->SetGeneratorPlatform(*cachedGeneratorPlatform,
3539                                                      &mf)) {
3540       return 1;
3541     }
3542   }
3543   cmValue cachedGeneratorToolset =
3544     this->State->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET");
3545   if (cachedGeneratorToolset) {
3546     cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
3547     if (!this->GlobalGenerator->SetGeneratorToolset(*cachedGeneratorToolset,
3548                                                     true, &mf)) {
3549       return 1;
3550     }
3551   }
3552   std::string output;
3553   std::string projName;
3554   cmValue cachedProjectName =
3555     this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
3556   if (!cachedProjectName) {
3557     std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
3558     return 1;
3559   }
3560   projName = *cachedProjectName;
3561
3562   if (cmIsOn(this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE"))) {
3563     verbose = true;
3564   }
3565
3566 #ifdef CMAKE_HAVE_VS_GENERATORS
3567   // For VS generators, explicitly check if regeneration is necessary before
3568   // actually starting the build. If not done separately from the build
3569   // itself, there is the risk of building an out-of-date solution file due
3570   // to limitations of the underlying build system.
3571   std::string const stampList = cachePath + "/" + "CMakeFiles/" +
3572     cmGlobalVisualStudio9Generator::GetGenerateStampList();
3573
3574   // Note that the stampList file only exists for VS generators.
3575   if (cmSystemTools::FileExists(stampList)) {
3576
3577     // Check if running for Visual Studio 9 - we need to explicitly run
3578     // the glob verification script before starting the build
3579     this->AddScriptingCommands();
3580     if (this->GlobalGenerator->MatchesGeneratorName("Visual Studio 9 2008")) {
3581       std::string const globVerifyScript =
3582         cachePath + "/" + "CMakeFiles/" + "VerifyGlobs.cmake";
3583       if (cmSystemTools::FileExists(globVerifyScript)) {
3584         std::vector<std::string> args;
3585         this->ReadListFile(args, globVerifyScript);
3586       }
3587     }
3588
3589     if (!cmakeCheckStampList(stampList)) {
3590       // Correctly initialize the home (=source) and home output (=binary)
3591       // directories, which is required for running the generation step.
3592       std::string homeOrig = this->GetHomeDirectory();
3593       std::string homeOutputOrig = this->GetHomeOutputDirectory();
3594       this->SetDirectoriesFromFile(cachePath);
3595
3596       this->AddProjectCommands();
3597
3598       int ret = this->Configure();
3599       if (ret) {
3600         cmSystemTools::Message("CMake Configure step failed.  "
3601                                "Build files cannot be regenerated correctly.");
3602         return ret;
3603       }
3604       ret = this->Generate();
3605       if (ret) {
3606         cmSystemTools::Message("CMake Generate step failed.  "
3607                                "Build files cannot be regenerated correctly.");
3608         return ret;
3609       }
3610       std::string message = cmStrCat("Build files have been written to: ",
3611                                      this->GetHomeOutputDirectory());
3612       this->UpdateProgress(message, -1);
3613
3614       // Restore the previously set directories to their original value.
3615       this->SetHomeDirectory(homeOrig);
3616       this->SetHomeOutputDirectory(homeOutputOrig);
3617     }
3618   }
3619 #endif
3620
3621   if (!this->GlobalGenerator->ReadCacheEntriesForBuild(*this->State)) {
3622     return 1;
3623   }
3624
3625   this->GlobalGenerator->PrintBuildCommandAdvice(std::cerr, jobs);
3626   return this->GlobalGenerator->Build(
3627     jobs, "", dir, projName, targets, output, "", config, buildOptions,
3628     verbose, cmDuration::zero(), cmSystemTools::OUTPUT_PASSTHROUGH,
3629     nativeOptions);
3630 }
3631
3632 bool cmake::Open(const std::string& dir, bool dryRun)
3633 {
3634   this->SetHomeDirectory("");
3635   this->SetHomeOutputDirectory("");
3636   if (!cmSystemTools::FileIsDirectory(dir)) {
3637     std::cerr << "Error: " << dir << " is not a directory\n";
3638     return false;
3639   }
3640
3641   std::string cachePath = FindCacheFile(dir);
3642   if (!this->LoadCache(cachePath)) {
3643     std::cerr << "Error: could not load cache\n";
3644     return false;
3645   }
3646   cmValue genName = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
3647   if (!genName) {
3648     std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
3649     return false;
3650   }
3651   cmValue extraGenName =
3652     this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
3653   std::string fullName =
3654     cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
3655       *genName, extraGenName ? *extraGenName : "");
3656
3657   std::unique_ptr<cmGlobalGenerator> gen =
3658     this->CreateGlobalGenerator(fullName);
3659   if (!gen) {
3660     std::cerr << "Error: could not create CMAKE_GENERATOR \"" << fullName
3661               << "\"\n";
3662     return false;
3663   }
3664
3665   cmValue cachedProjectName =
3666     this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
3667   if (!cachedProjectName) {
3668     std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
3669     return false;
3670   }
3671
3672   return gen->Open(dir, *cachedProjectName, dryRun);
3673 }
3674
3675 #if !defined(CMAKE_BOOTSTRAP)
3676 template <typename T>
3677 const T* cmake::FindPresetForWorkflow(
3678   cm::static_string_view type,
3679   const std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
3680   const cmCMakePresetsGraph::WorkflowPreset::WorkflowStep& step)
3681 {
3682   auto it = presets.find(step.PresetName);
3683   if (it == presets.end()) {
3684     cmSystemTools::Error(cmStrCat("No such ", type, " preset in ",
3685                                   this->GetHomeDirectory(), ": \"",
3686                                   step.PresetName, '"'));
3687     return nullptr;
3688   }
3689
3690   if (it->second.Unexpanded.Hidden) {
3691     cmSystemTools::Error(cmStrCat("Cannot use hidden ", type, " preset in ",
3692                                   this->GetHomeDirectory(), ": \"",
3693                                   step.PresetName, '"'));
3694     return nullptr;
3695   }
3696
3697   if (!it->second.Expanded) {
3698     cmSystemTools::Error(cmStrCat("Could not evaluate ", type, " preset \"",
3699                                   step.PresetName,
3700                                   "\": Invalid macro expansion"));
3701     return nullptr;
3702   }
3703
3704   if (!it->second.Expanded->ConditionResult) {
3705     cmSystemTools::Error(cmStrCat("Cannot use disabled ", type, " preset in ",
3706                                   this->GetHomeDirectory(), ": \"",
3707                                   step.PresetName, '"'));
3708     return nullptr;
3709   }
3710
3711   return &*it->second.Expanded;
3712 }
3713
3714 std::function<int()> cmake::BuildWorkflowStep(
3715   const std::vector<std::string>& args)
3716 {
3717   cmUVProcessChainBuilder builder;
3718   builder
3719     .AddCommand(args)
3720 #  ifdef _WIN32
3721     .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, _fileno(stdout))
3722     .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, _fileno(stderr));
3723 #  else
3724     .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, STDOUT_FILENO)
3725     .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, STDERR_FILENO);
3726 #  endif
3727   return [builder]() -> int {
3728     auto chain = builder.Start();
3729     chain.Wait();
3730     return static_cast<int>(chain.GetStatus().front()->ExitStatus);
3731   };
3732 }
3733 #endif
3734
3735 int cmake::Workflow(const std::string& presetName,
3736                     WorkflowListPresets listPresets, WorkflowFresh fresh)
3737 {
3738 #ifndef CMAKE_BOOTSTRAP
3739   this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
3740   this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
3741
3742   cmCMakePresetsGraph settingsFile;
3743   auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
3744   if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
3745     cmSystemTools::Error(
3746       cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ": ",
3747                cmCMakePresetsGraph::ResultToString(result)));
3748     return 1;
3749   }
3750
3751   if (listPresets == WorkflowListPresets::Yes) {
3752     settingsFile.PrintWorkflowPresetList();
3753     return 0;
3754   }
3755
3756   auto presetPair = settingsFile.WorkflowPresets.find(presetName);
3757   if (presetPair == settingsFile.WorkflowPresets.end()) {
3758     cmSystemTools::Error(cmStrCat("No such workflow preset in ",
3759                                   this->GetHomeDirectory(), ": \"", presetName,
3760                                   '"'));
3761     settingsFile.PrintWorkflowPresetList();
3762     return 1;
3763   }
3764
3765   if (presetPair->second.Unexpanded.Hidden) {
3766     cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ",
3767                                   this->GetHomeDirectory(), ": \"", presetName,
3768                                   '"'));
3769     settingsFile.PrintWorkflowPresetList();
3770     return 1;
3771   }
3772
3773   auto const& expandedPreset = presetPair->second.Expanded;
3774   if (!expandedPreset) {
3775     cmSystemTools::Error(cmStrCat("Could not evaluate workflow preset \"",
3776                                   presetName, "\": Invalid macro expansion"));
3777     settingsFile.PrintWorkflowPresetList();
3778     return 1;
3779   }
3780
3781   if (!expandedPreset->ConditionResult) {
3782     cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ",
3783                                   this->GetHomeDirectory(), ": \"", presetName,
3784                                   '"'));
3785     settingsFile.PrintWorkflowPresetList();
3786     return 1;
3787   }
3788
3789   struct CalculatedStep
3790   {
3791     int StepNumber;
3792     cm::static_string_view Type;
3793     std::string Name;
3794     std::function<int()> Action;
3795
3796     CalculatedStep(int stepNumber, cm::static_string_view type,
3797                    std::string name, std::function<int()> action)
3798       : StepNumber(stepNumber)
3799       , Type(type)
3800       , Name(std::move(name))
3801       , Action(std::move(action))
3802     {
3803     }
3804   };
3805
3806   std::vector<CalculatedStep> steps;
3807   steps.reserve(expandedPreset->Steps.size());
3808   int stepNumber = 1;
3809   for (auto const& step : expandedPreset->Steps) {
3810     switch (step.PresetType) {
3811       case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::
3812         Configure: {
3813         auto const* configurePreset = this->FindPresetForWorkflow(
3814           "configure"_s, settingsFile.ConfigurePresets, step);
3815         if (!configurePreset) {
3816           return 1;
3817         }
3818         std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(),
3819                                        "--preset", step.PresetName };
3820         if (fresh == WorkflowFresh::Yes) {
3821           args.emplace_back("--fresh");
3822         }
3823         steps.emplace_back(stepNumber, "configure"_s, step.PresetName,
3824                            this->BuildWorkflowStep(args));
3825       } break;
3826       case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: {
3827         auto const* buildPreset = this->FindPresetForWorkflow(
3828           "build"_s, settingsFile.BuildPresets, step);
3829         if (!buildPreset) {
3830           return 1;
3831         }
3832         steps.emplace_back(
3833           stepNumber, "build"_s, step.PresetName,
3834           this->BuildWorkflowStep({ cmSystemTools::GetCMakeCommand(),
3835                                     "--build", "--preset", step.PresetName }));
3836       } break;
3837       case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Test: {
3838         auto const* testPreset = this->FindPresetForWorkflow(
3839           "test"_s, settingsFile.TestPresets, step);
3840         if (!testPreset) {
3841           return 1;
3842         }
3843         steps.emplace_back(
3844           stepNumber, "test"_s, step.PresetName,
3845           this->BuildWorkflowStep({ cmSystemTools::GetCTestCommand(),
3846                                     "--preset", step.PresetName }));
3847       } break;
3848       case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Package: {
3849         auto const* packagePreset = this->FindPresetForWorkflow(
3850           "package"_s, settingsFile.PackagePresets, step);
3851         if (!packagePreset) {
3852           return 1;
3853         }
3854         steps.emplace_back(
3855           stepNumber, "package"_s, step.PresetName,
3856           this->BuildWorkflowStep({ cmSystemTools::GetCPackCommand(),
3857                                     "--preset", step.PresetName }));
3858       } break;
3859     }
3860     stepNumber++;
3861   }
3862
3863   int stepResult;
3864   bool first = true;
3865   for (auto const& step : steps) {
3866     if (!first) {
3867       std::cout << "\n";
3868     }
3869     std::cout << "Executing workflow step " << step.StepNumber << " of "
3870               << steps.size() << ": " << step.Type << " preset \"" << step.Name
3871               << "\"\n\n"
3872               << std::flush;
3873     if ((stepResult = step.Action()) != 0) {
3874       return stepResult;
3875     }
3876     first = false;
3877   }
3878 #endif
3879
3880   return 0;
3881 }
3882
3883 void cmake::WatchUnusedCli(const std::string& var)
3884 {
3885 #ifndef CMAKE_BOOTSTRAP
3886   this->VariableWatch->AddWatch(var, cmWarnUnusedCliWarning, this);
3887   if (!cm::contains(this->UsedCliVariables, var)) {
3888     this->UsedCliVariables[var] = false;
3889   }
3890 #endif
3891 }
3892
3893 void cmake::UnwatchUnusedCli(const std::string& var)
3894 {
3895 #ifndef CMAKE_BOOTSTRAP
3896   this->VariableWatch->RemoveWatch(var, cmWarnUnusedCliWarning);
3897   this->UsedCliVariables.erase(var);
3898 #endif
3899 }
3900
3901 void cmake::RunCheckForUnusedVariables()
3902 {
3903 #ifndef CMAKE_BOOTSTRAP
3904   bool haveUnused = false;
3905   std::ostringstream msg;
3906   msg << "Manually-specified variables were not used by the project:";
3907   for (auto const& it : this->UsedCliVariables) {
3908     if (!it.second) {
3909       haveUnused = true;
3910       msg << "\n  " << it.first;
3911     }
3912   }
3913   if (haveUnused) {
3914     this->IssueMessage(MessageType::WARNING, msg.str());
3915   }
3916 #endif
3917 }
3918
3919 bool cmake::GetSuppressDevWarnings() const
3920 {
3921   return this->Messenger->GetSuppressDevWarnings();
3922 }
3923
3924 void cmake::SetSuppressDevWarnings(bool b)
3925 {
3926   std::string value;
3927
3928   // equivalent to -Wno-dev
3929   if (b) {
3930     value = "TRUE";
3931   }
3932   // equivalent to -Wdev
3933   else {
3934     value = "FALSE";
3935   }
3936
3937   this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", value,
3938                       "Suppress Warnings that are meant for"
3939                       " the author of the CMakeLists.txt files.",
3940                       cmStateEnums::INTERNAL);
3941 }
3942
3943 bool cmake::GetSuppressDeprecatedWarnings() const
3944 {
3945   return this->Messenger->GetSuppressDeprecatedWarnings();
3946 }
3947
3948 void cmake::SetSuppressDeprecatedWarnings(bool b)
3949 {
3950   std::string value;
3951
3952   // equivalent to -Wno-deprecated
3953   if (b) {
3954     value = "FALSE";
3955   }
3956   // equivalent to -Wdeprecated
3957   else {
3958     value = "TRUE";
3959   }
3960
3961   this->AddCacheEntry("CMAKE_WARN_DEPRECATED", value,
3962                       "Whether to issue warnings for deprecated "
3963                       "functionality.",
3964                       cmStateEnums::INTERNAL);
3965 }
3966
3967 bool cmake::GetDevWarningsAsErrors() const
3968 {
3969   return this->Messenger->GetDevWarningsAsErrors();
3970 }
3971
3972 void cmake::SetDevWarningsAsErrors(bool b)
3973 {
3974   std::string value;
3975
3976   // equivalent to -Werror=dev
3977   if (b) {
3978     value = "FALSE";
3979   }
3980   // equivalent to -Wno-error=dev
3981   else {
3982     value = "TRUE";
3983   }
3984
3985   this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_ERRORS", value,
3986                       "Suppress errors that are meant for"
3987                       " the author of the CMakeLists.txt files.",
3988                       cmStateEnums::INTERNAL);
3989 }
3990
3991 bool cmake::GetDeprecatedWarningsAsErrors() const
3992 {
3993   return this->Messenger->GetDeprecatedWarningsAsErrors();
3994 }
3995
3996 void cmake::SetDeprecatedWarningsAsErrors(bool b)
3997 {
3998   std::string value;
3999
4000   // equivalent to -Werror=deprecated
4001   if (b) {
4002     value = "TRUE";
4003   }
4004   // equivalent to -Wno-error=deprecated
4005   else {
4006     value = "FALSE";
4007   }
4008
4009   this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", value,
4010                       "Whether to issue deprecation errors for macros"
4011                       " and functions.",
4012                       cmStateEnums::INTERNAL);
4013 }
4014
4015 void cmake::SetDebugFindOutputPkgs(std::string const& args)
4016 {
4017   this->DebugFindPkgs.emplace(args);
4018 }
4019
4020 void cmake::SetDebugFindOutputVars(std::string const& args)
4021 {
4022   this->DebugFindVars.emplace(args);
4023 }
4024
4025 bool cmake::GetDebugFindOutput(std::string const& var) const
4026 {
4027   return this->DebugFindVars.count(var);
4028 }
4029
4030 bool cmake::GetDebugFindPkgOutput(std::string const& pkg) const
4031 {
4032   return this->DebugFindPkgs.count(pkg);
4033 }
4034
4035 #if !defined(CMAKE_BOOTSTRAP)
4036 cmMakefileProfilingData& cmake::GetProfilingOutput()
4037 {
4038   return *(this->ProfilingOutput);
4039 }
4040
4041 bool cmake::IsProfilingEnabled() const
4042 {
4043   return static_cast<bool>(this->ProfilingOutput);
4044 }
4045 #endif