1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
10 #include <initializer_list>
17 #include <cm/optional>
18 #include <cm/string_view>
19 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
20 # include <cm/iterator>
23 #include <cmext/algorithm>
24 #include <cmext/string_view>
26 #if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32)
30 #include "cmsys/FStream.hxx"
31 #include "cmsys/Glob.hxx"
32 #include "cmsys/RegularExpression.hxx"
34 #include "cm_sys_stat.h"
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"
56 #include "cmMessenger.h"
58 #include "cmStateDirectory.h"
59 #include "cmStringAlgorithms.h"
60 #include "cmSystemTools.h"
62 #include "cmTargetLinkLibraryType.h"
63 #include "cmUVProcessChain.h"
64 #include "cmUtils.hxx"
65 #include "cmVersionConfig.h"
66 #include "cmWorkingDirectory.h"
68 #if !defined(CMAKE_BOOTSTRAP)
69 # include <unordered_map>
71 # include <cm3p/curl/curl.h>
72 # include <cm3p/json/writer.h>
74 # include "cmFileAPI.h"
75 # include "cmGraphVizWriter.h"
76 # include "cmVariableWatch.h"
79 #if defined(__MINGW32__) && defined(CMAKE_BOOTSTRAP)
80 # define CMAKE_BOOT_MINGW
83 // include the generator
84 #if defined(_WIN32) && !defined(__CYGWIN__)
85 # if !defined(CMAKE_BOOT_MINGW)
86 # include <cmext/memory>
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"
98 # define CMAKE_HAVE_VS_GENERATORS
100 # include "cmGlobalMSYSMakefileGenerator.h"
101 # include "cmGlobalMinGWMakefileGenerator.h"
104 #if defined(CMAKE_USE_WMAKE)
105 # include "cmGlobalWatcomWMakeGenerator.h"
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"
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"
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"
130 #if defined(__APPLE__)
131 # if !defined(CMAKE_BOOTSTRAP)
132 # include "cmGlobalXCodeGenerator.h"
134 # define CMAKE_USE_XCODE 1
136 # include <sys/resource.h>
137 # include <sys/time.h>
142 #if !defined(CMAKE_BOOTSTRAP)
143 using JsonValueMapType = std::unordered_map<std::string, Json::Value>;
146 auto IgnoreAndTrueLambda = [](std::string const&, cmake*) -> bool {
150 using CommandArgument =
151 cmCommandLineArgument<bool(std::string const& value, cmake* state)>;
155 static bool cmakeCheckStampFile(const std::string& stampName);
156 static bool cmakeCheckStampList(const std::string& stampList);
158 #ifndef CMAKE_BOOTSTRAP
159 static void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/,
160 void* ctx, const char* /*unused*/,
161 const cmMakefile* /*unused*/)
163 cmake* cm = reinterpret_cast<cmake*>(ctx);
164 cm->MarkCliAsUsed(variable);
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>())
174 , State(cm::make_unique<cmState>(mode, projectKind))
175 , Messenger(cm::make_unique<cmMessenger>())
177 this->TraceFile.close();
178 this->CurrentSnapshot = this->State->CreateBaseSnapshot();
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);
190 this->AddDefaultGenerators();
191 this->AddDefaultExtraGenerators();
192 if (role == RoleScript || role == RoleProject) {
193 this->AddScriptingCommands();
195 if (role == RoleProject) {
196 this->AddProjectCommands();
199 if (mode == cmState::Project) {
200 this->LoadEnvironmentPresets();
203 // Make sure we can capture the build tool output.
204 cmSystemTools::EnableVSConsoleOutput();
206 // Set up a list of source and header extensions.
207 // These are used to find files when the extension is not given.
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);
216 // Fill unordered set
217 exts.unordered.insert(exts.ordered.begin(), exts.ordered.end());
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" });
234 cmake::~cmake() = default;
236 #if !defined(CMAKE_BOOTSTRAP)
237 Json::Value cmake::ReportVersionJson() const
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;
249 Json::Value cmake::ReportCapabilitiesJson() const
251 Json::Value obj = Json::objectValue;
253 // Version information:
254 obj["version"] = this->ReportVersionJson();
257 std::vector<cmake::GeneratorInfo> generatorInfoList;
258 this->GetRegisteredGenerators(generatorInfoList);
260 auto* curlVersion = curl_version_info(CURLVERSION_FIRST);
262 JsonValueMapType generatorMap;
263 for (cmake::GeneratorInfo const& gi : generatorInfoList) {
264 if (gi.isAlias) { // skip aliases, they are there for compatibility reasons
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);
279 gen["supportedPlatforms"] = std::move(supportedPlatforms);
281 gen["extraGenerators"] = Json::arrayValue;
282 generatorMap[gi.name] = gen;
284 Json::Value& gen = generatorMap[gi.baseName];
285 gen["extraGenerators"].append(gi.extraName);
289 Json::Value generators = Json::arrayValue;
290 for (auto const& i : generatorMap) {
291 generators.append(i.second);
293 obj["generators"] = generators;
294 obj["fileApi"] = cmFileAPI::ReportCapabilities();
295 obj["serverMode"] = false;
296 obj["tls"] = static_cast<bool>(curlVersion->features & CURL_VERSION_SSL);
302 std::string cmake::ReportCapabilities() const
305 #if !defined(CMAKE_BOOTSTRAP)
306 Json::FastWriter writer;
307 result = writer.write(this->ReportCapabilitiesJson());
309 result = "Not supported";
314 void cmake::CleanupCommandsAndMacros()
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();
323 #ifndef CMAKE_BOOTSTRAP
324 void cmake::SetWarningFromPreset(const std::string& name,
325 const cm::optional<bool>& warning,
326 const cm::optional<bool>& error)
330 this->DiagLevels[name] = std::max(this->DiagLevels[name], DIAG_WARN);
332 this->DiagLevels[name] = DIAG_IGNORE;
337 this->DiagLevels[name] = DIAG_ERROR;
339 this->DiagLevels[name] = std::min(this->DiagLevels[name], DIAG_WARN);
344 void cmake::ProcessPresetVariables()
346 for (auto const& var : this->UnprocessedPresetVariables) {
350 cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
351 if (!var.second->Type.empty()) {
352 type = cmState::StringToCacheEntryType(var.second->Type);
354 this->ProcessCacheArg(var.first, var.second->Value, type);
358 void cmake::PrintPresetVariables()
361 for (auto const& var : this->UnprocessedPresetVariables) {
365 cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
366 if (!var.second->Type.empty()) {
367 type = cmState::StringToCacheEntryType(var.second->Type);
370 std::cout << "Preset CMake variables:\n\n";
373 std::cout << " " << var.first;
374 if (type != cmStateEnums::UNINITIALIZED) {
375 std::cout << ':' << cmState::CacheEntryTypeToString(type);
377 std::cout << "=\"" << var.second->Value << "\"\n";
382 this->UnprocessedPresetVariables.clear();
385 void cmake::ProcessPresetEnvironment()
387 for (auto const& var : this->UnprocessedPresetEnvironment) {
389 cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
394 void cmake::PrintPresetEnvironment()
397 for (auto const& var : this->UnprocessedPresetEnvironment) {
402 std::cout << "Preset environment variables:\n\n";
405 std::cout << " " << var.first << "=\"" << *var.second << "\"\n";
410 this->UnprocessedPresetEnvironment.clear();
415 bool cmake::SetCacheArgs(const std::vector<std::string>& args)
417 auto DefineLambda = [](std::string const& entry, cmake* state) -> bool {
420 cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
421 if (cmState::ParseCacheEntry(entry, var, value, type)) {
422 #ifndef CMAKE_BOOTSTRAP
423 state->UnprocessedPresetVariables.erase(var);
425 state->ProcessCacheArg(var, value, type);
427 cmSystemTools::Error(cmStrCat("Parse error in command line argument: ",
428 entry, "\n Should be: VAR:type=value\n"));
434 auto WarningLambda = [](cm::string_view entry, cmake* state) -> bool {
435 bool foundNo = false;
436 bool foundError = false;
438 if (cmHasLiteralPrefix(entry, "no-")) {
440 entry.remove_prefix(3);
443 if (cmHasLiteralPrefix(entry, "error=")) {
445 entry.remove_prefix(6);
449 cmSystemTools::Error("No warning name provided.");
453 std::string const name = std::string(entry);
454 if (!foundNo && !foundError) {
456 state->DiagLevels[name] = std::max(state->DiagLevels[name], DIAG_WARN);
457 } else if (foundNo && !foundError) {
459 state->DiagLevels[name] = DIAG_IGNORE;
460 } else if (!foundNo && foundError) {
462 state->DiagLevels[name] = DIAG_ERROR;
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);
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
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);
492 // now remove them from the cache
493 for (std::string const& currentEntry : entriesToDelete) {
494 #ifndef CMAKE_BOOTSTRAP
495 state->UnprocessedPresetVariables.erase(currentEntry);
497 state->State->RemoveCacheEntry(currentEntry);
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);
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);
522 state->ProcessCacheArg(var, path, type);
525 cmSystemTools::Error("Absolute paths are required for --install-prefix");
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);
535 state->ProcessCacheArg(var, path, type);
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 },
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 {
554 cmSystemTools::Error("No file name specified for -C");
557 cmSystemTools::Stdout("loading initial cache file " + value + "\n");
558 // Resolve script path specified on command line
560 auto path = cmSystemTools::CollapseFullPath(value);
561 state->ReadListFile(args, path);
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 },
576 for (decltype(args.size()) i = 1; i < args.size(); ++i) {
577 std::string const& arg = args[i];
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.
584 for (auto const& m : arguments) {
585 if (m.matches(arg)) {
586 const bool parsedCorrectly = m.parse(arg, i, args, this);
587 if (!parsedCorrectly) {
594 if (this->GetWorkingMode() == FIND_PACKAGE_MODE) {
595 return this->FindPackage(args);
601 void cmake::ProcessCacheArg(const std::string& var, const std::string& value,
602 cmStateEnums::CacheEntryType type)
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)) {
617 var, value, "No help, variable specified on the command line.", type);
619 if (this->WarnUnusedCli) {
621 cachedValue != *this->State->GetInitializedCacheValue(var)) {
622 this->WatchUnusedCli(var);
627 void cmake::ReadListFile(const std::vector<std::string>& args,
628 const std::string& path)
630 // if a generator was not yet created, temporarily create one
631 cmGlobalGenerator* gg = this->GetGlobalGenerator();
633 // if a generator was not specified use a generic one
634 std::unique_ptr<cmGlobalGenerator> gen;
636 gen = cm::make_unique<cmGlobalGenerator>(this);
640 // read in the list file to fill the cache
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);
653 mf.SetArgcArgv(args);
655 if (!mf.ReadListFile(path)) {
656 cmSystemTools::Error("Error processing file: " + path);
661 bool cmake::FindPackage(const std::vector<std::string>& args)
663 this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
664 this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
666 this->SetGlobalGenerator(cm::make_unique<cmGlobalGenerator>(this));
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));
679 mf->SetArgcArgv(args);
681 std::string systemFile = mf->GetModulesFile("CMakeFindPackageMode.cmake");
682 mf->ReadListFile(systemFile);
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");
692 printf("%s not found.\n", packageName.c_str());
694 } else if (mode == "EXIST"_s) {
696 printf("%s found.\n", packageName.c_str());
698 } else if (mode == "COMPILE"_s) {
699 std::string includes = mf->GetSafeDefinition("PACKAGE_INCLUDE_DIRS");
700 std::vector<std::string> includeDirs = cmExpandedList(includes);
702 this->GlobalGenerator->CreateGenerationObjects();
703 const auto& lg = this->GlobalGenerator->LocalGenerators[0];
704 std::string includeFlags =
705 lg->GetIncludeFlags(includeDirs, nullptr, language, std::string());
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);
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);
721 std::string buildType = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
722 buildType = cmSystemTools::UpperCase(buildType);
724 std::string linkLibs;
725 std::string frameworkPath;
726 std::string linkPath;
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;
739 printf("%s\n", linkLibs.c_str());
743 tgt->SetProperty("WIN32_EXECUTABLE", "ON");
747 tgt->SetProperty("MACOSX_BUNDLE", "ON");
754 void cmake::LoadEnvironmentPresets()
756 std::string envGenVar;
757 bool hasEnvironmentGenerator = false;
758 if (cmSystemTools::GetEnv("CMAKE_GENERATOR", envGenVar)) {
759 hasEnvironmentGenerator = true;
760 this->EnvironmentGenerator = envGenVar;
763 auto readGeneratorVar = [&](std::string const& name, std::string& key) {
764 std::string varValue;
765 if (cmSystemTools::GetEnv(name, varValue)) {
766 if (hasEnvironmentGenerator) {
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");
777 readGeneratorVar("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance);
778 readGeneratorVar("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform);
779 readGeneratorVar("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset);
783 enum class ListPresets
796 void cmake::SetArgs(const std::vector<std::string>& args)
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;
808 ListPresets listPresets = ListPresets::None;
811 auto EmptyStringArgLambda = [](std::string const&, cmake* state) -> bool {
813 MessageType::WARNING,
814 "Ignoring empty string (\"\") provided on the command line.");
818 auto SourceArgLambda = [](std::string const& value, cmake* state) -> bool {
820 cmSystemTools::Error("No source directory specified for -S");
823 std::string path = cmSystemTools::CollapseFullPath(value);
824 cmSystemTools::ConvertToUnixSlashes(path);
826 state->SetHomeDirectoryViaCommandLine(path);
830 auto BuildArgLambda = [&](std::string const& value, cmake* state) -> bool {
832 cmSystemTools::Error("No build directory specified for -B");
835 std::string path = cmSystemTools::CollapseFullPath(value);
836 cmSystemTools::ConvertToUnixSlashes(path);
837 state->SetHomeOutputDirectory(path);
842 auto PlatformLambda = [&](std::string const& value, cmake* state) -> bool {
844 cmSystemTools::Error("Multiple -A options not allowed");
847 state->SetGeneratorPlatform(value);
852 auto ToolsetLamda = [&](std::string const& value, cmake* state) -> bool {
854 cmSystemTools::Error("Multiple -T options not allowed");
857 state->SetGeneratorToolset(value);
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;
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 },
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 },
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);
918 CommandArgument{ "--check-stamp-file", CommandArgument::Values::One,
919 [](std::string const& value, cmake* state) -> bool {
920 state->CheckStampFile = value;
923 CommandArgument{ "--check-stamp-list", CommandArgument::Values::One,
924 [](std::string const& value, cmake* state) -> bool {
925 state->CheckStampList = value;
928 CommandArgument{ "--regenerate-during-build",
929 CommandArgument::Values::Zero,
930 [](std::string const&, cmake* state) -> bool {
931 state->RegenerateDuringBuild = true;
935 CommandArgument{ "--find-package", CommandArgument::Values::Zero,
936 IgnoreAndTrueLambda },
938 CommandArgument{ "--graphviz", "No file specified for --graphviz",
939 CommandArgument::Values::One,
940 [](std::string const& value, cmake* state) -> bool {
942 cmSystemTools::CollapseFullPath(value);
943 cmSystemTools::ConvertToUnixSlashes(path);
944 state->GraphVizFile = path;
948 CommandArgument{ "--debug-trycompile", CommandArgument::Values::Zero,
949 [](std::string const&, cmake* state) -> bool {
950 std::cout << "debug trycompile on\n";
951 state->DebugTryCompileOn();
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);
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");
970 state->SetLogLevel(logLevel);
971 state->LogLevelWasSetViaCLI = true;
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");
986 state->SetLogLevel(logLevel);
987 state->LogLevelWasSetViaCLI = true;
991 CommandArgument{ "--log-context", CommandArgument::Values::Zero,
992 [](std::string const&, cmake* state) -> bool {
993 state->SetShowLogContext(true);
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);
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 "
1010 for (auto const& v : find_pkgs) {
1011 std::cout << " " << v;
1012 state->SetDebugFindOutputPkgs(v);
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);
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);
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.");
1047 state->SetTraceFormat(traceFormat);
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);
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);
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);
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);
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 {
1084 << "Not searching for unused variables given on the "
1085 << "command line.\n";
1086 state->SetWarnUnusedCli(false);
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);
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);
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;
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;
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);
1131 arguments.emplace_back("--preset", "No preset specified for --preset",
1132 CommandArgument::Values::One,
1133 [&](std::string const& value, cmake*) -> bool {
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;
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.");
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;
1175 for (decltype(args.size()) i = 1; i < args.size(); ++i) {
1176 // iterate each argument
1177 std::string const& arg = args[i];
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.
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();
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)) {
1202 parsedCorrectly = m.parse(arg, i, args, this);
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
1211 // Additionally it can't be the source/binary tree location
1212 if (!parsedCorrectly) {
1213 cmSystemTools::Error("Run 'cmake --help' for all supported options.");
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;
1225 if (!extraProvidedPath.empty() && this->GetWorkingMode() == NORMAL_MODE) {
1226 this->IssueMessage(MessageType::WARNING,
1227 cmStrCat("Ignoring extra path from command line:\n \"",
1228 extraProvidedPath, "\""));
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.");
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 = "";
1242 if (!this->GeneratorToolsetSet) {
1243 this->GeneratorToolset = "";
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!");
1254 if (profilingFormat == "google-trace"_s) {
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()));
1264 cmSystemTools::Error("Invalid format specified for --profiling-format");
1270 const bool haveSourceDir = !this->GetHomeDirectory().empty();
1271 const bool haveBinaryDir = !this->GetHomeOutputDirectory().empty();
1272 const bool havePreset =
1273 #ifdef CMAKE_BOOTSTRAP
1276 !presetName.empty();
1279 if (this->CurrentWorkingMode == cmake::NORMAL_MODE && !haveSourceDir &&
1280 !haveBinaryDir && !havePreset) {
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.");
1288 if (!haveSourceDir) {
1289 this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
1291 if (!haveBinaryDir) {
1292 this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
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);
1306 cmSystemTools::Error(errorMsg);
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();
1325 this->SetWorkingMode(WorkingMode::HELP_MODE);
1329 auto preset = presetsGraph.ConfigurePresets.find(presetName);
1330 if (preset == presetsGraph.ConfigurePresets.end()) {
1331 cmSystemTools::Error(cmStrCat("No such preset in ",
1332 this->GetHomeDirectory(), ": \"",
1334 this->PrintPresetList(presetsGraph);
1337 if (preset->second.Unexpanded.Hidden) {
1338 cmSystemTools::Error(cmStrCat("Cannot use hidden preset in ",
1339 this->GetHomeDirectory(), ": \"",
1341 this->PrintPresetList(presetsGraph);
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"));
1351 if (!expandedPreset->ConditionResult) {
1352 cmSystemTools::Error(cmStrCat("Could not use disabled preset \"",
1353 preset->second.Unexpanded.Name, "\""));
1357 if (!this->State->IsCacheLoaded() && !haveBArg &&
1358 !expandedPreset->BinaryDir.empty()) {
1359 this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
1361 if (!this->GlobalGenerator && !expandedPreset->Generator.empty()) {
1362 if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator,
1367 this->UnprocessedPresetVariables = expandedPreset->CacheVariables;
1368 this->UnprocessedPresetEnvironment = expandedPreset->Environment;
1370 if (!expandedPreset->InstallDir.empty() &&
1371 !this->State->GetInitializedCacheValue("CMAKE_INSTALL_PREFIX")) {
1372 this->UnprocessedPresetVariables["CMAKE_INSTALL_PREFIX"] = {
1373 "PATH", expandedPreset->InstallDir
1376 if (!expandedPreset->ToolchainFile.empty() &&
1377 !this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) {
1378 this->UnprocessedPresetVariables["CMAKE_TOOLCHAIN_FILE"] = {
1379 "FILEPATH", expandedPreset->ToolchainFile
1383 if (!expandedPreset->ArchitectureStrategy ||
1384 expandedPreset->ArchitectureStrategy ==
1385 cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
1386 if (!this->GeneratorPlatformSet) {
1387 this->SetGeneratorPlatform(expandedPreset->Architecture);
1390 if (!expandedPreset->ToolsetStrategy ||
1391 expandedPreset->ToolsetStrategy ==
1392 cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
1393 if (!this->GeneratorToolsetSet) {
1394 this->SetGeneratorToolset(expandedPreset->Toolset);
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);
1405 if (expandedPreset->WarnUnusedCli == false) {
1406 this->SetWarnUnusedCli(false);
1408 if (expandedPreset->WarnSystemVars == true) {
1409 this->SetCheckSystemVars(true);
1411 if (expandedPreset->DebugOutput == true) {
1412 this->SetDebugOutputOn(true);
1414 if (expandedPreset->DebugTryCompile == true) {
1415 this->DebugTryCompileOn();
1417 if (expandedPreset->DebugFind == true) {
1418 this->SetDebugFindOutput(true);
1425 using LevelsPair = std::pair<cm::string_view, Message::LogLevel>;
1426 using LevelsPairArray = std::array<LevelsPair, 7>;
1427 const LevelsPairArray& getStringToLogLevelPairs()
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 } }
1442 Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr)
1444 const LevelsPairArray& levels = getStringToLogLevelPairs();
1446 const auto levelStrLowCase =
1447 cmSystemTools::LowerCase(std::string{ levelStr });
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;
1454 return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED;
1457 std::string cmake::LogLevelToString(Message::LogLevel level)
1459 const LevelsPairArray& levels = getStringToLogLevelPairs();
1461 // NOLINTNEXTLINE(readability-qualified-auto)
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;
1472 cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr)
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 },
1480 const auto traceStrLowCase = cmSystemTools::LowerCase(traceStr);
1482 const auto it = std::find_if(levels.cbegin(), levels.cend(),
1483 [&traceStrLowCase](const TracePair& p) {
1484 return p.first == traceStrLowCase;
1486 return (it != levels.cend()) ? it->second : TraceFormat::TRACE_UNDEFINED;
1489 void cmake::SetTraceFile(const std::string& file)
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());
1500 std::cout << "Trace will be written to " << file << "\n";
1503 void cmake::PrintTraceFormatVersion()
1505 if (!this->GetTrace()) {
1511 switch (this->GetTraceFormat()) {
1512 case TraceFormat::TRACE_JSON_V1: {
1513 #ifndef CMAKE_BOOTSTRAP
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);
1525 case TraceFormat::TRACE_HUMAN:
1528 case TraceFormat::TRACE_UNDEFINED:
1529 msg = "INTERNAL ERROR: Trace format is TRACE_UNDEFINED";
1537 auto& f = this->GetTraceFile();
1541 cmSystemTools::Message(msg);
1545 bool cmake::SetDirectoriesFromFile(const std::string& arg)
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");
1559 is_empty_directory = true;
1560 if (cmSystemTools::FileExists(cacheFile)) {
1562 is_empty_directory = false;
1564 if (cmSystemTools::FileExists(listFile)) {
1566 is_empty_directory = false;
1567 is_source_dir = true;
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);
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);
1587 listPath = fullPath;
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);
1604 bool no_source_tree = this->GetHomeDirectory().empty();
1605 bool no_build_tree = this->GetHomeOutputDirectory().empty();
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);
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);
1624 } else if (no_source_tree && no_build_tree) {
1625 this->SetHomeDirectory(listPath);
1627 std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
1628 this->SetHomeOutputDirectory(cwd);
1629 } else if (no_build_tree) {
1630 this->SetHomeOutputDirectory(listPath);
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);
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;
1652 return used_provided_path;
1655 // at the end of this CMAKE_ROOT and CMAKE_COMMAND should be added to the
1657 int cmake::AddCMakePaths()
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);
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());
1680 this->AddCacheEntry("CMAKE_ROOT", cmSystemTools::GetCMakeRoot(),
1681 "Path to CMake installation.", cmStateEnums::INTERNAL);
1686 void cmake::AddDefaultExtraGenerators()
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());
1697 void cmake::GetRegisteredGenerators(std::vector<GeneratorInfo>& generators,
1698 bool includeNamesWithPlatform) const
1700 for (const auto& gen : this->Generators) {
1701 std::vector<std::string> names = gen->GetGeneratorNames();
1703 if (includeNamesWithPlatform) {
1704 cm::append(names, gen->GetGeneratorNamesWithPlatform());
1707 for (std::string const& name : names) {
1709 info.supportsToolset = gen->SupportsToolset();
1710 info.supportsPlatform = gen->SupportsPlatform();
1711 info.supportedPlatforms = gen->GetKnownPlatforms();
1712 info.defaultPlatform = gen->GetDefaultPlatformName();
1714 info.baseName = name;
1715 info.isAlias = false;
1716 generators.push_back(std::move(info));
1720 for (cmExternalMakefileProjectGeneratorFactory* eg : this->ExtraGenerators) {
1721 const std::vector<std::string> genList =
1722 eg->GetSupportedGlobalGenerators();
1723 for (std::string const& gen : genList) {
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));
1734 for (std::string const& a : eg->Aliases) {
1737 if (!genList.empty()) {
1738 info.baseName = genList.at(0);
1740 info.extraName = eg->GetName();
1741 info.supportsPlatform = false;
1742 info.supportsToolset = false;
1743 info.isAlias = true;
1744 generators.push_back(std::move(info));
1749 static std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>,
1751 createExtraGenerator(
1752 const std::vector<cmExternalMakefileProjectGeneratorFactory*>& in,
1753 const std::string& name)
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) };
1761 for (std::string const& g : generators) {
1762 const std::string fullName =
1763 cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
1765 if (fullName == name) {
1766 return { i->CreateExternalMakefileProjectGenerator(), g };
1770 return { nullptr, name };
1773 std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator(
1774 const std::string& gname, bool allowArch)
1776 std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>, std::string>
1777 extra = createExtraGenerator(this->ExtraGenerators, gname);
1778 std::unique_ptr<cmExternalMakefileProjectGenerator>& extraGenerator =
1780 const std::string& name = extra.second;
1782 std::unique_ptr<cmGlobalGenerator> generator;
1783 for (const auto& g : this->Generators) {
1784 generator = g->CreateGlobalGenerator(name, allowArch, this);
1791 generator->SetExternalMakefileProjectGenerator(std::move(extraGenerator));
1797 bool cmake::CreateAndSetGlobalGenerator(const std::string& name,
1800 auto gen = this->CreateGlobalGenerator(name, allowArch);
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.";
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.";
1813 cmSystemTools::Error(
1814 cmStrCat("Could not create named generator ", name, kdevError, vsError));
1815 this->PrintGeneratorList();
1819 this->SetGlobalGenerator(std::move(gen));
1823 #ifndef CMAKE_BOOTSTRAP
1824 void cmake::PrintPresetList(const cmCMakePresetsGraph& graph) const
1826 std::vector<GeneratorInfo> generators;
1827 this->GetRegisteredGenerators(generators, false);
1829 [&generators](const cmCMakePresetsGraph::ConfigurePreset& preset) -> bool {
1830 if (preset.Generator.empty()) {
1833 auto condition = [&preset](const GeneratorInfo& info) -> bool {
1834 return info.name == preset.Generator;
1836 auto it = std::find_if(generators.begin(), generators.end(), condition);
1837 return it != generators.end();
1840 graph.PrintConfigurePresetList(filter);
1844 void cmake::SetHomeDirectoryViaCommandLine(std::string const& path)
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 \"",
1857 this->SetHomeDirectory(path);
1860 void cmake::SetHomeDirectory(const std::string& dir)
1862 this->State->SetSourceDirectory(dir);
1863 if (this->CurrentSnapshot.IsValid()) {
1864 this->CurrentSnapshot.SetDefinition("CMAKE_SOURCE_DIR", dir);
1867 if (this->State->GetProjectKind() == cmState::ProjectKind::Normal) {
1868 this->Messenger->SetTopSource(this->GetHomeDirectory());
1870 this->Messenger->SetTopSource(cm::nullopt);
1874 std::string const& cmake::GetHomeDirectory() const
1876 return this->State->GetSourceDirectory();
1879 void cmake::SetHomeOutputDirectory(const std::string& dir)
1881 this->State->SetBinaryDirectory(dir);
1882 if (this->CurrentSnapshot.IsValid()) {
1883 this->CurrentSnapshot.SetDefinition("CMAKE_BINARY_DIR", dir);
1887 std::string const& cmake::GetHomeOutputDirectory() const
1889 return this->State->GetBinaryDirectory();
1892 std::string cmake::FindCacheFile(const std::string& binaryDir)
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",
1904 if (!cachePathFound.empty()) {
1905 cachePath = cmSystemTools::GetFilenamePath(cachePathFound);
1912 void cmake::SetGlobalGenerator(std::unique_ptr<cmGlobalGenerator> gg)
1915 cmSystemTools::Error("Error SetGlobalGenerator called with null");
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);
1925 cmSystemTools::UnPutEnv(env);
1928 if (!this->CXXEnvironment.empty()) {
1929 env += this->CXXEnvironment;
1930 cmSystemTools::PutEnv(env);
1932 cmSystemTools::UnPutEnv(env);
1937 this->GlobalGenerator = std::move(gg);
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());
1943 // Save the environment variables CXX and CC
1944 if (!cmSystemTools::GetEnv("CXX", this->CXXEnvironment)) {
1945 this->CXXEnvironment.clear();
1947 if (!cmSystemTools::GetEnv("CC", this->CCEnvironment)) {
1948 this->CCEnvironment.clear();
1952 int cmake::DoPreConfigureChecks()
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";
1965 err << "The source directory \"" << this->GetHomeDirectory()
1966 << "\" does not exist.\n";
1968 err << "Specify --help for usage, or press the help button on the CMake "
1970 cmSystemTools::Error(err.str());
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"),
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);
1995 struct SaveCacheEntry
2000 cmStateEnums::CacheEntryType type;
2003 int cmake::HandleDeleteCacheVariables(const std::string& var)
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()) {
2011 std::vector<SaveCacheEntry> saved;
2012 std::ostringstream warning;
2013 /* clang-format off */
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;
2022 warning << *i << "= ";
2024 if (i != argsSplit.end()) {
2026 warning << *i << "\n";
2031 cmValue existingValue = this->State->GetCacheEntryValue(save.key);
2032 if (existingValue) {
2033 save.type = this->State->GetCacheEntryType(save.key);
2035 this->State->GetCacheEntryProperty(save.key, "HELPSTRING")) {
2039 save.type = cmStateEnums::CacheEntryType::UNINITIALIZED;
2041 saved.push_back(std::move(save));
2045 this->DeleteCache(this->GetHomeOutputDirectory());
2046 // load the empty cache
2048 // restore the changed compilers
2049 for (SaveCacheEntry const& i : saved) {
2050 this->AddCacheEntry(i.key, i.value, i.help.c_str(), i.type);
2052 cmSystemTools::Message(warning.str());
2053 // avoid reconfigure if there were errors
2054 if (!cmSystemTools::GetErrorOccurredFlag()) {
2056 return this->Configure();
2061 int cmake::Configure()
2063 DiagLevel diagLevel;
2065 if (this->DiagLevels.count("deprecated") == 1) {
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);
2080 if (this->DiagLevels.count("dev") == 1) {
2081 bool setDeprecatedVariables = false;
2083 cmValue cachedWarnDeprecated =
2084 this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
2085 cmValue cachedErrorDeprecated =
2086 this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
2088 // don't overwrite deprecated warning setting from a previous invocation
2089 if (!cachedWarnDeprecated && !cachedErrorDeprecated) {
2090 setDeprecatedVariables = true;
2093 diagLevel = this->DiagLevels["dev"];
2094 if (diagLevel == DIAG_IGNORE) {
2095 this->SetSuppressDevWarnings(true);
2096 this->SetDevWarningsAsErrors(false);
2098 if (setDeprecatedVariables) {
2099 this->SetSuppressDeprecatedWarnings(true);
2100 this->SetDeprecatedWarningsAsErrors(false);
2102 } else if (diagLevel == DIAG_WARN) {
2103 this->SetSuppressDevWarnings(false);
2104 this->SetDevWarningsAsErrors(false);
2106 if (setDeprecatedVariables) {
2107 this->SetSuppressDeprecatedWarnings(false);
2108 this->SetDeprecatedWarningsAsErrors(false);
2110 } else if (diagLevel == DIAG_ERROR) {
2111 this->SetSuppressDevWarnings(false);
2112 this->SetDevWarningsAsErrors(true);
2114 if (setDeprecatedVariables) {
2115 this->SetSuppressDeprecatedWarnings(false);
2116 this->SetDeprecatedWarningsAsErrors(true);
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));
2127 value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
2128 this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value));
2130 value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_WARNINGS");
2131 this->Messenger->SetSuppressDevWarnings(cmIsOn(value));
2133 value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_ERRORS");
2134 this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(*value));
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);
2145 int cmake::ActualConfigure()
2147 // Construct right now our path conversion table before it's too late:
2148 this->UpdateConversionPathTable();
2149 this->CleanupCommandsAndMacros();
2151 cmSystemTools::RemoveADirectory(this->GetHomeOutputDirectory() +
2152 "/CMakeFiles/CMakeScratch");
2154 int res = this->DoPreConfigureChecks();
2159 this->AddCacheEntry(
2160 "CMAKE_HOME_DIRECTORY", this->GetHomeDirectory(),
2161 "Source directory with the top level CMakeLists.txt file for this "
2163 cmStateEnums::INTERNAL);
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" +
2178 this->AddCacheEntry("CMAKE_FIND_PACKAGE_REDIRECTS_DIR", redirectsDir,
2179 "Value Computed by CMake.", cmStateEnums::STATIC);
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");
2187 std::string fullName =
2188 cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
2189 *genName, extraGenName ? *extraGenName : "");
2190 this->GlobalGenerator = this->CreateGlobalGenerator(fullName);
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
2196 cmSystemTools::SetForceUnixPaths(
2197 this->GlobalGenerator->GetForceUnixPaths());
2199 this->CreateDefaultGlobalGenerator();
2201 if (!this->GlobalGenerator) {
2202 cmSystemTools::Error("Could not create generator");
2207 cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
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);
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);
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);
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);
2249 this->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance,
2250 "Generator instance identifier.",
2251 cmStateEnums::INTERNAL);
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);
2267 this->AddCacheEntry("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform,
2268 "Name of generator platform.", cmStateEnums::INTERNAL);
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);
2283 this->AddCacheEntry("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset,
2284 "Name of generator toolset.", cmStateEnums::INTERNAL);
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();
2293 this->TruncateOutputLog("CMakeOutput.log");
2294 this->TruncateOutputLog("CMakeError.log");
2297 #if !defined(CMAKE_BOOTSTRAP)
2298 this->FileAPI = cm::make_unique<cmFileAPI>(this);
2299 this->FileAPI->ReadQueries();
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:
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);
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);
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 ?");
2339 this->State->SaveVerificationScript(this->GetHomeOutputDirectory(),
2340 this->Messenger.get());
2341 this->SaveCache(this->GetHomeOutputDirectory());
2342 if (cmSystemTools::GetErrorOccurredFlag()) {
2348 std::unique_ptr<cmGlobalGenerator> cmake::EvaluateDefaultGlobalGenerator()
2350 if (!this->EnvironmentGenerator.empty()) {
2351 auto gen = this->CreateGlobalGenerator(this->EnvironmentGenerator);
2353 cmSystemTools::Error("CMAKE_GENERATOR was set but the specified "
2354 "generator doesn't exist. Using CMake default.");
2359 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
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\\"
2368 struct VSVersionedGenerator
2370 const char* MSVersion;
2371 const char* GeneratorName;
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" }
2379 static const char* const vsEntries[] = {
2380 "\\Setup\\VC;ProductDir", //
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";
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;
2398 if (cmSystemTools::ReadRegistryValue(reg, dir,
2399 cmSystemTools::KeyWOW64_32) &&
2400 cmSystemTools::PathExists(dir)) {
2401 found = g->GeneratorName;
2407 auto gen = this->CreateGlobalGenerator(found);
2409 gen = cm::make_unique<cmGlobalNMakeMakefileGenerator>(this);
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));
2416 return std::unique_ptr<cmGlobalGenerator>(
2417 cm::make_unique<cmGlobalUnixMakefileGenerator3>(this));
2421 void cmake::CreateDefaultGlobalGenerator()
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";
2428 this->SetGlobalGenerator(std::move(gen));
2431 void cmake::PreLoadCMakeFiles()
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);
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);
2450 // handle a command line invocation
2451 int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
2453 // Process the arguments
2454 this->SetArgs(args);
2455 if (cmSystemTools::GetErrorOccurredFlag()) {
2458 if (this->GetWorkingMode() == HELP_MODE) {
2462 // Log the trace format version to the desired output
2463 if (this->GetTrace()) {
2464 this->PrintTraceFormatVersion();
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)) {
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)) {
2479 if (this->GetWorkingMode() == NORMAL_MODE) {
2480 if (this->FreshCache) {
2481 this->DeleteCache(this->GetHomeOutputDirectory());
2484 if (this->LoadCache() < 0) {
2485 cmSystemTools::Error("Error executing cmake::LoadCache(). Aborting.\n");
2489 if (this->FreshCache) {
2490 cmSystemTools::Error("--fresh allowed only when configuring a project");
2493 this->AddCMakePaths();
2496 #ifndef CMAKE_BOOTSTRAP
2497 this->ProcessPresetVariables();
2498 this->ProcessPresetEnvironment();
2500 // Add any cache args
2501 if (!this->SetCacheArgs(args)) {
2502 cmSystemTools::Error("Run 'cmake --help' for all supported options.");
2505 #ifndef CMAKE_BOOTSTRAP
2506 this->PrintPresetVariables();
2507 this->PrintPresetEnvironment();
2510 // In script mode we terminate after running the script.
2511 if (this->GetWorkingMode() != NORMAL_MODE) {
2512 if (cmSystemTools::GetErrorOccurredFlag()) {
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=");
2527 this->PreLoadCMakeFiles();
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()) {
2539 int ret = this->Configure();
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);
2558 ret = this->Generate();
2560 cmSystemTools::Message("CMake Generate step failed. "
2561 "Build files cannot be regenerated correctly.");
2564 std::string message = cmStrCat("Build files have been written to: ",
2565 this->GetHomeOutputDirectory());
2566 this->UpdateProgress(message, -1);
2570 int cmake::Generate()
2572 if (!this->GlobalGenerator) {
2575 if (!this->GlobalGenerator->Compute()) {
2578 this->GlobalGenerator->Generate();
2579 if (!this->GraphVizFile.empty()) {
2580 std::cout << "Generate graphviz: " << this->GraphVizFile << std::endl;
2581 this->GenerateGraphViz(this->GraphVizFile);
2583 if (this->WarnUnusedCli) {
2584 this->RunCheckForUnusedVariables();
2586 if (cmSystemTools::GetErrorOccurredFlag()) {
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());
2594 #if !defined(CMAKE_BOOTSTRAP)
2595 this->FileAPI->WriteReplies();
2601 void cmake::AddCacheEntry(const std::string& key, cmValue value,
2602 const char* helpString, int type)
2604 this->State->AddCacheEntry(key, value, helpString,
2605 static_cast<cmStateEnums::CacheEntryType>(type));
2606 this->UnwatchUnusedCli(key);
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));
2619 bool cmake::DoWriteGlobVerifyTarget() const
2621 return this->State->DoWriteGlobVerifyTarget();
2624 std::string const& cmake::GetGlobVerifyScript() const
2626 return this->State->GetGlobVerifyScript();
2629 std::string const& cmake::GetGlobVerifyStamp() const
2631 return this->State->GetGlobVerifyStamp();
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)
2641 this->State->AddGlobCacheEntry(recurse, listDirectories, followSymlinks,
2642 relative, expression, files, variable,
2643 backtrace, this->Messenger.get());
2646 std::vector<std::string> cmake::GetAllExtensions() const
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());
2661 std::string cmake::StripExtension(const std::string& file) const
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));
2668 auto ext = cm::string_view(file).substr(dotpos + 1);
2670 if (this->IsAKnownExtension(ext)) {
2671 return file.substr(0, dotpos);
2677 cmValue cmake::GetCacheDefinition(const std::string& name) const
2679 return this->State->GetInitializedCacheValue(name);
2682 void cmake::AddScriptingCommands() const
2684 GetScriptingCommands(this->GetState());
2687 void cmake::AddProjectCommands() const
2689 GetProjectCommands(this->GetState());
2692 void cmake::AddDefaultGenerators()
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());
2710 this->Generators.push_back(cmGlobalMSYSMakefileGenerator::NewFactory());
2711 this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory());
2713 #if !defined(CMAKE_BOOTSTRAP)
2714 # if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
2715 this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory());
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());
2725 #if defined(CMAKE_USE_WMAKE)
2726 this->Generators.push_back(cmGlobalWatcomWMakeGenerator::NewFactory());
2728 #ifdef CMAKE_USE_XCODE
2729 this->Generators.push_back(cmGlobalXCodeGenerator::NewFactory());
2733 bool cmake::ParseCacheEntry(const std::string& entry, std::string& var,
2735 cmStateEnums::CacheEntryType& type)
2737 return cmState::ParseCacheEntry(entry, var, value, type);
2740 int cmake::LoadCache()
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.");
2756 // setup CMAKE_ROOT and CMAKE_COMMAND
2757 if (!this->AddCMakePaths()) {
2763 bool cmake::LoadCache(const std::string& path)
2765 std::set<std::string> emptySet;
2766 return this->LoadCache(path, true, emptySet, emptySet);
2769 bool cmake::LoadCache(const std::string& path, bool internal,
2770 std::set<std::string>& excludes,
2771 std::set<std::string>& includes)
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);
2782 bool cmake::SaveCache(const std::string& path)
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);
2795 bool cmake::DeleteCache(const std::string& path)
2797 return this->State->DeleteCache(path);
2800 void cmake::SetProgressCallback(ProgressCallbackType f)
2802 this->ProgressCallback = std::move(f);
2805 void cmake::UpdateProgress(const std::string& msg, float prog)
2807 if (this->ProgressCallback && !this->GetIsInTryCompile()) {
2808 this->ProgressCallback(msg, prog);
2812 bool cmake::GetIsInTryCompile() const
2814 return this->State->GetProjectKind() == cmState::ProjectKind::TryCompile;
2817 void cmake::AppendGlobalGeneratorsDocumentation(
2818 std::vector<cmDocumentationEntry>& v)
2820 const auto defaultGenerator = this->EvaluateDefaultGlobalGenerator();
2821 const std::string defaultName = defaultGenerator->GetName();
2822 bool foundDefaultOne = false;
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;
2831 v.push_back(std::move(e));
2835 void cmake::AppendExtraGeneratorsDocumentation(
2836 std::vector<cmDocumentationEntry>& v)
2838 for (cmExternalMakefileProjectGeneratorFactory* eg : this->ExtraGenerators) {
2839 const std::string doc = eg->GetDocumentation();
2840 const std::string name = eg->GetName();
2843 for (std::string const& a : eg->Aliases) {
2844 cmDocumentationEntry e;
2847 v.push_back(std::move(e));
2851 const std::vector<std::string> generators =
2852 eg->GetSupportedGlobalGenerators();
2853 for (std::string const& g : generators) {
2854 cmDocumentationEntry e;
2856 cmExternalMakefileProjectGenerator::CreateFullGeneratorName(g, name);
2858 v.push_back(std::move(e));
2863 std::vector<cmDocumentationEntry> cmake::GetGeneratorsDocumentation()
2865 std::vector<cmDocumentationEntry> v;
2866 this->AppendGlobalGeneratorsDocumentation(v);
2867 this->AppendExtraGeneratorsDocumentation(v);
2871 void cmake::PrintGeneratorList()
2873 #ifndef CMAKE_BOOTSTRAP
2874 cmDocumentation doc;
2875 auto generators = this->GetGeneratorsDocumentation();
2876 doc.AppendSection("Generators", generators);
2878 doc.PrintDocumentation(cmDocumentation::ListGenerators, std::cerr);
2882 void cmake::UpdateConversionPathTable()
2884 // Update the path conversion table with any specified file:
2886 this->State->GetInitializedCacheValue("CMAKE_PATH_TRANSLATION_FILE");
2889 cmsys::ifstream table(tablepath->c_str());
2891 cmSystemTools::Error("CMAKE_PATH_TRANSLATION_FILE set to " + *tablepath +
2892 ". CMake can not open file.");
2893 cmSystemTools::ReportLastSystemError("CMake can not open file.");
2897 while (!table.eof()) {
2898 // two entries per line
2901 cmSystemTools::AddTranslationPath(a, b);
2907 int cmake::CheckBuildSystem()
2909 // We do not need to rerun CMake. Check dependency integrity.
2910 const bool verbose = isCMakeVerbose();
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.
2916 // If no file is provided for the check, we have to rerun.
2917 if (this->CheckBuildSystemArgument.empty()) {
2919 cmSystemTools::Stdout("Re-run cmake no build system arguments\n");
2924 // If the file provided does not exist, we have to rerun.
2925 if (!cmSystemTools::FileExists(this->CheckBuildSystemArgument)) {
2927 std::ostringstream msg;
2928 msg << "Re-run cmake missing file: " << this->CheckBuildSystemArgument
2930 cmSystemTools::Stdout(msg.str());
2935 // Read the rerun check file and use it to decide whether to do the
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()) {
2947 std::ostringstream msg;
2948 msg << "Re-run cmake error reading : " << this->CheckBuildSystemArgument
2950 cmSystemTools::Stdout(msg.str());
2952 // There was an error reading the file. Just rerun.
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";
2963 // Create the generator and use it to clear the dependencies.
2964 std::unique_ptr<cmGlobalGenerator> ggd =
2965 this->CreateGlobalGenerator(genName);
2967 cm.GetCurrentSnapshot().SetDefaultDefinitions();
2968 cmMakefile mfd(ggd.get(), cm.GetCurrentSnapshot());
2969 auto lgd = ggd->CreateLocalGenerator(&mfd);
2970 lgd->ClearDependencies(&mfd, verbose);
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))) {
2980 std::ostringstream msg;
2981 msg << "Re-run cmake, missing byproduct: " << p << "\n";
2982 cmSystemTools::Stdout(msg.str());
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);
2994 if (depends.empty() || outputs.empty()) {
2995 // Not enough information was provided to do the test. Just rerun.
2997 cmSystemTools::Stdout("Re-run cmake no CMAKE_MAKEFILE_DEPENDS "
2998 "or CMAKE_MAKEFILE_OUTPUTS :\n");
3003 // Find the newest dependency.
3004 auto dep = depends.begin();
3005 std::string dep_newest = *dep++;
3006 for (; dep != depends.end(); ++dep) {
3008 if (this->FileTimeCache->Compare(dep_newest, *dep, &result)) {
3014 cmSystemTools::Stdout(
3015 "Re-run cmake: build system dependency is missing\n");
3021 // Find the oldest output.
3022 auto out = outputs.begin();
3023 std::string out_oldest = *out++;
3024 for (; out != outputs.end(); ++out) {
3026 if (this->FileTimeCache->Compare(out_oldest, *out, &result)) {
3032 cmSystemTools::Stdout(
3033 "Re-run cmake: build system output is missing\n");
3039 // If any output is older than any dependency then rerun.
3042 if (!this->FileTimeCache->Compare(out_oldest, dep_newest, &result) ||
3045 std::ostringstream msg;
3046 msg << "Re-run cmake file: " << out_oldest
3047 << " older than: " << dep_newest << "\n";
3048 cmSystemTools::Stdout(msg.str());
3054 // No need to rerun.
3058 void cmake::TruncateOutputLog(const char* fname)
3060 std::string fullPath = cmStrCat(this->GetHomeOutputDirectory(), '/', fname);
3062 if (::stat(fullPath.c_str(), &st)) {
3065 if (!this->State->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR")) {
3066 cmSystemTools::RemoveFile(fullPath);
3069 off_t fsize = st.st_size;
3070 const off_t maxFileSize = 50 * 1024;
3071 if (fsize < maxFileSize) {
3072 // TODO: truncate file
3077 void cmake::MarkCliAsUsed(const std::string& variable)
3079 this->UsedCliVariables[variable] = true;
3082 void cmake::GenerateGraphViz(const std::string& fileName) const
3084 #ifndef CMAKE_BOOTSTRAP
3085 cmGraphVizWriter gvWriter(fileName, this->GetGlobalGenerator());
3087 std::string settingsFile =
3088 cmStrCat(this->GetHomeOutputDirectory(), "/CMakeGraphVizOptions.cmake");
3089 std::string fallbackSettingsFile =
3090 cmStrCat(this->GetHomeDirectory(), "/CMakeGraphVizOptions.cmake");
3092 gvWriter.ReadSettings(settingsFile, fallbackSettingsFile);
3099 void cmake::SetProperty(const std::string& prop, const char* value)
3101 this->State->SetGlobalProperty(prop, value);
3103 void cmake::SetProperty(const std::string& prop, cmValue value)
3105 this->State->SetGlobalProperty(prop, value);
3108 void cmake::AppendProperty(const std::string& prop, const std::string& value,
3111 this->State->AppendGlobalProperty(prop, value, asString);
3114 cmValue cmake::GetProperty(const std::string& prop)
3116 return this->State->GetGlobalProperty(prop);
3119 bool cmake::GetPropertyAsBool(const std::string& prop)
3121 return this->State->GetGlobalPropertyAsBool(prop);
3124 cmInstalledFile* cmake::GetOrCreateInstalledFile(cmMakefile* mf,
3125 const std::string& name)
3127 auto i = this->InstalledFiles.find(name);
3129 if (i != this->InstalledFiles.end()) {
3130 cmInstalledFile& file = i->second;
3133 cmInstalledFile& file = this->InstalledFiles[name];
3134 file.SetName(mf, name);
3138 cmInstalledFile const* cmake::GetInstalledFile(const std::string& name) const
3140 auto i = this->InstalledFiles.find(name);
3142 if (i != this->InstalledFiles.end()) {
3143 cmInstalledFile const& file = i->second;
3149 int cmake::GetSystemInformation(std::vector<std::string>& args)
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";
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()) {
3170 if (i >= args.size()) {
3171 cmSystemTools::Error("No generator specified for -G");
3172 this->PrintGeneratorList();
3177 auto gen = this->CreateGlobalGenerator(value);
3179 cmSystemTools::Error("Could not create named generator " + value);
3180 this->PrintGeneratorList();
3182 this->SetGlobalGenerator(std::move(gen));
3185 // no option assume it is the output file
3187 if (!cmSystemTools::FileIsFullPath(arg)) {
3188 resultFile = cmStrCat(cwd, '/');
3191 writeToStdout = false;
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");
3203 if (!cmsys::SystemTools::CopyFileAlways(inFile, outFile)) {
3204 std::cerr << "Error copying file \"" << inFile << "\" to \"" << outFile
3209 // do we write to a file or to stdout?
3210 if (resultFile.empty()) {
3211 resultFile = cmStrCat(cwd, "/__cmake_systeminformation/results.txt");
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
3222 std::cerr << "Failed to change to directory " << destPath << " : "
3223 << std::strerror(workdir.GetLastResult()) << std::endl;
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);
3233 std::cerr << "Error: --system-information failed on internal CMake!\n";
3238 // echo results to stdout if needed
3239 if (writeToStdout) {
3240 FILE* fin = cmsys::SystemTools::Fopen(resultFile, "r");
3242 const int bufferSize = 4096;
3243 char buffer[bufferSize];
3245 while ((n = fread(buffer, 1, bufferSize, fin)) > 0) {
3246 for (char* c = buffer; c < buffer + n; ++c) {
3255 // clean up the directory
3256 cmSystemTools::RemoveADirectory(destPath);
3260 static bool cmakeCheckStampFile(const std::string& stampName)
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
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);
3271 cmsys::ifstream fin(stampDepends.c_str());
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";
3281 // Compare the stamp dependencies against the dependency file itself.
3283 cmFileTimeCache ftc;
3285 while (cmSystemTools::GetLineFromStream(fin, dep)) {
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";
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();
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";
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.
3319 cmSystemTools::RemoveFile(stampTemp);
3320 cmSystemTools::Error(
3321 cmStrCat("Cannot restore timestamp \"", stampName, "\": ", err));
3325 static bool cmakeCheckStampList(const std::string& stampList)
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 "
3333 cmsys::ifstream fin(stampList.c_str());
3335 std::cout << "CMake is re-running because generate.stamp.list "
3336 << "could not be read.\n";
3340 // Check each stamp.
3341 std::string stampName;
3342 while (cmSystemTools::GetLineFromStream(fin, stampName)) {
3343 if (!cmakeCheckStampFile(stampName)) {
3350 void cmake::IssueMessage(MessageType t, std::string const& text,
3351 cmListFileBacktrace const& backtrace) const
3353 this->Messenger->IssueMessage(t, text, backtrace);
3356 std::vector<std::string> cmake::GetDebugConfigs()
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);
3366 // If no configurations were specified, use a default list.
3367 if (configs.empty()) {
3368 configs.emplace_back("DEBUG");
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)
3378 this->SetHomeDirectory("");
3379 this->SetHomeOutputDirectory("");
3381 #if !defined(CMAKE_BOOTSTRAP)
3382 if (!presetName.empty() || listPresets) {
3383 this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
3384 this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
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)));
3396 settingsFile.PrintBuildPresetList();
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(), ": \"",
3405 settingsFile.PrintBuildPresetList();
3409 if (presetPair->second.Unexpanded.Hidden) {
3410 cmSystemTools::Error(cmStrCat("Cannot use hidden build preset in ",
3411 this->GetHomeDirectory(), ": \"",
3413 settingsFile.PrintBuildPresetList();
3417 auto const& expandedPreset = presetPair->second.Expanded;
3418 if (!expandedPreset) {
3419 cmSystemTools::Error(cmStrCat("Could not evaluate build preset \"",
3421 "\": Invalid macro expansion"));
3422 settingsFile.PrintBuildPresetList();
3426 if (!expandedPreset->ConditionResult) {
3427 cmSystemTools::Error(cmStrCat("Cannot use disabled build preset in ",
3428 this->GetHomeDirectory(), ": \"",
3430 settingsFile.PrintBuildPresetList();
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);
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);
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"));
3460 if (!expandedConfigurePreset->BinaryDir.empty()) {
3461 dir = expandedConfigurePreset->BinaryDir;
3464 this->UnprocessedPresetEnvironment = expandedPreset->Environment;
3465 this->ProcessPresetEnvironment();
3467 if ((jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL ||
3468 jobs == cmake::NO_BUILD_PARALLEL_LEVEL) &&
3469 expandedPreset->Jobs) {
3470 jobs = *expandedPreset->Jobs;
3473 if (targets.empty()) {
3474 targets.insert(targets.begin(), expandedPreset->Targets.begin(),
3475 expandedPreset->Targets.end());
3478 if (config.empty()) {
3479 config = expandedPreset->Configuration;
3482 if (!buildOptions.Clean && expandedPreset->CleanFirst) {
3483 buildOptions.Clean = *expandedPreset->CleanFirst;
3486 if (buildOptions.ResolveMode == PackageResolveMode::Default &&
3487 expandedPreset->ResolvePackageReferences) {
3488 buildOptions.ResolveMode = *expandedPreset->ResolvePackageReferences;
3491 if (!verbose && expandedPreset->Verbose) {
3492 verbose = *expandedPreset->Verbose;
3495 if (nativeOptions.empty()) {
3496 nativeOptions.insert(nativeOptions.begin(),
3497 expandedPreset->NativeToolOptions.begin(),
3498 expandedPreset->NativeToolOptions.end());
3503 if (!cmSystemTools::FileIsDirectory(dir)) {
3504 std::cerr << "Error: " << dir << " is not a directory\n";
3508 std::string cachePath = FindCacheFile(dir);
3509 if (!this->LoadCache(cachePath)) {
3510 std::cerr << "Error: could not load cache\n";
3513 cmValue cachedGenerator = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
3514 if (!cachedGenerator) {
3515 std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
3518 auto gen = this->CreateGlobalGenerator(*cachedGenerator);
3520 std::cerr << "Error: could not create CMAKE_GENERATOR \""
3521 << *cachedGenerator << "\"\n";
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,
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,
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,
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";
3560 projName = *cachedProjectName;
3562 if (cmIsOn(this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE"))) {
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();
3574 // Note that the stampList file only exists for VS generators.
3575 if (cmSystemTools::FileExists(stampList)) {
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);
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);
3596 this->AddProjectCommands();
3598 int ret = this->Configure();
3600 cmSystemTools::Message("CMake Configure step failed. "
3601 "Build files cannot be regenerated correctly.");
3604 ret = this->Generate();
3606 cmSystemTools::Message("CMake Generate step failed. "
3607 "Build files cannot be regenerated correctly.");
3610 std::string message = cmStrCat("Build files have been written to: ",
3611 this->GetHomeOutputDirectory());
3612 this->UpdateProgress(message, -1);
3614 // Restore the previously set directories to their original value.
3615 this->SetHomeDirectory(homeOrig);
3616 this->SetHomeOutputDirectory(homeOutputOrig);
3621 if (!this->GlobalGenerator->ReadCacheEntriesForBuild(*this->State)) {
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,
3632 bool cmake::Open(const std::string& dir, bool dryRun)
3634 this->SetHomeDirectory("");
3635 this->SetHomeOutputDirectory("");
3636 if (!cmSystemTools::FileIsDirectory(dir)) {
3637 std::cerr << "Error: " << dir << " is not a directory\n";
3641 std::string cachePath = FindCacheFile(dir);
3642 if (!this->LoadCache(cachePath)) {
3643 std::cerr << "Error: could not load cache\n";
3646 cmValue genName = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
3648 std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
3651 cmValue extraGenName =
3652 this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
3653 std::string fullName =
3654 cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
3655 *genName, extraGenName ? *extraGenName : "");
3657 std::unique_ptr<cmGlobalGenerator> gen =
3658 this->CreateGlobalGenerator(fullName);
3660 std::cerr << "Error: could not create CMAKE_GENERATOR \"" << fullName
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";
3672 return gen->Open(dir, *cachedProjectName, dryRun);
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)
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, '"'));
3690 if (it->second.Unexpanded.Hidden) {
3691 cmSystemTools::Error(cmStrCat("Cannot use hidden ", type, " preset in ",
3692 this->GetHomeDirectory(), ": \"",
3693 step.PresetName, '"'));
3697 if (!it->second.Expanded) {
3698 cmSystemTools::Error(cmStrCat("Could not evaluate ", type, " preset \"",
3700 "\": Invalid macro expansion"));
3704 if (!it->second.Expanded->ConditionResult) {
3705 cmSystemTools::Error(cmStrCat("Cannot use disabled ", type, " preset in ",
3706 this->GetHomeDirectory(), ": \"",
3707 step.PresetName, '"'));
3711 return &*it->second.Expanded;
3714 std::function<int()> cmake::BuildWorkflowStep(
3715 const std::vector<std::string>& args)
3717 cmUVProcessChainBuilder builder;
3721 .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, _fileno(stdout))
3722 .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, _fileno(stderr));
3724 .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, STDOUT_FILENO)
3725 .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, STDERR_FILENO);
3727 return [builder]() -> int {
3728 auto chain = builder.Start();
3730 return static_cast<int>(chain.GetStatus().front()->ExitStatus);
3735 int cmake::Workflow(const std::string& presetName,
3736 WorkflowListPresets listPresets, WorkflowFresh fresh)
3738 #ifndef CMAKE_BOOTSTRAP
3739 this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
3740 this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
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)));
3751 if (listPresets == WorkflowListPresets::Yes) {
3752 settingsFile.PrintWorkflowPresetList();
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,
3761 settingsFile.PrintWorkflowPresetList();
3765 if (presetPair->second.Unexpanded.Hidden) {
3766 cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ",
3767 this->GetHomeDirectory(), ": \"", presetName,
3769 settingsFile.PrintWorkflowPresetList();
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();
3781 if (!expandedPreset->ConditionResult) {
3782 cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ",
3783 this->GetHomeDirectory(), ": \"", presetName,
3785 settingsFile.PrintWorkflowPresetList();
3789 struct CalculatedStep
3792 cm::static_string_view Type;
3794 std::function<int()> Action;
3796 CalculatedStep(int stepNumber, cm::static_string_view type,
3797 std::string name, std::function<int()> action)
3798 : StepNumber(stepNumber)
3800 , Name(std::move(name))
3801 , Action(std::move(action))
3806 std::vector<CalculatedStep> steps;
3807 steps.reserve(expandedPreset->Steps.size());
3809 for (auto const& step : expandedPreset->Steps) {
3810 switch (step.PresetType) {
3811 case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::
3813 auto const* configurePreset = this->FindPresetForWorkflow(
3814 "configure"_s, settingsFile.ConfigurePresets, step);
3815 if (!configurePreset) {
3818 std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(),
3819 "--preset", step.PresetName };
3820 if (fresh == WorkflowFresh::Yes) {
3821 args.emplace_back("--fresh");
3823 steps.emplace_back(stepNumber, "configure"_s, step.PresetName,
3824 this->BuildWorkflowStep(args));
3826 case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: {
3827 auto const* buildPreset = this->FindPresetForWorkflow(
3828 "build"_s, settingsFile.BuildPresets, step);
3833 stepNumber, "build"_s, step.PresetName,
3834 this->BuildWorkflowStep({ cmSystemTools::GetCMakeCommand(),
3835 "--build", "--preset", step.PresetName }));
3837 case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Test: {
3838 auto const* testPreset = this->FindPresetForWorkflow(
3839 "test"_s, settingsFile.TestPresets, step);
3844 stepNumber, "test"_s, step.PresetName,
3845 this->BuildWorkflowStep({ cmSystemTools::GetCTestCommand(),
3846 "--preset", step.PresetName }));
3848 case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Package: {
3849 auto const* packagePreset = this->FindPresetForWorkflow(
3850 "package"_s, settingsFile.PackagePresets, step);
3851 if (!packagePreset) {
3855 stepNumber, "package"_s, step.PresetName,
3856 this->BuildWorkflowStep({ cmSystemTools::GetCPackCommand(),
3857 "--preset", step.PresetName }));
3865 for (auto const& step : steps) {
3869 std::cout << "Executing workflow step " << step.StepNumber << " of "
3870 << steps.size() << ": " << step.Type << " preset \"" << step.Name
3873 if ((stepResult = step.Action()) != 0) {
3883 void cmake::WatchUnusedCli(const std::string& var)
3885 #ifndef CMAKE_BOOTSTRAP
3886 this->VariableWatch->AddWatch(var, cmWarnUnusedCliWarning, this);
3887 if (!cm::contains(this->UsedCliVariables, var)) {
3888 this->UsedCliVariables[var] = false;
3893 void cmake::UnwatchUnusedCli(const std::string& var)
3895 #ifndef CMAKE_BOOTSTRAP
3896 this->VariableWatch->RemoveWatch(var, cmWarnUnusedCliWarning);
3897 this->UsedCliVariables.erase(var);
3901 void cmake::RunCheckForUnusedVariables()
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) {
3910 msg << "\n " << it.first;
3914 this->IssueMessage(MessageType::WARNING, msg.str());
3919 bool cmake::GetSuppressDevWarnings() const
3921 return this->Messenger->GetSuppressDevWarnings();
3924 void cmake::SetSuppressDevWarnings(bool b)
3928 // equivalent to -Wno-dev
3932 // equivalent to -Wdev
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);
3943 bool cmake::GetSuppressDeprecatedWarnings() const
3945 return this->Messenger->GetSuppressDeprecatedWarnings();
3948 void cmake::SetSuppressDeprecatedWarnings(bool b)
3952 // equivalent to -Wno-deprecated
3956 // equivalent to -Wdeprecated
3961 this->AddCacheEntry("CMAKE_WARN_DEPRECATED", value,
3962 "Whether to issue warnings for deprecated "
3964 cmStateEnums::INTERNAL);
3967 bool cmake::GetDevWarningsAsErrors() const
3969 return this->Messenger->GetDevWarningsAsErrors();
3972 void cmake::SetDevWarningsAsErrors(bool b)
3976 // equivalent to -Werror=dev
3980 // equivalent to -Wno-error=dev
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);
3991 bool cmake::GetDeprecatedWarningsAsErrors() const
3993 return this->Messenger->GetDeprecatedWarningsAsErrors();
3996 void cmake::SetDeprecatedWarningsAsErrors(bool b)
4000 // equivalent to -Werror=deprecated
4004 // equivalent to -Wno-error=deprecated
4009 this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", value,
4010 "Whether to issue deprecation errors for macros"
4012 cmStateEnums::INTERNAL);
4015 void cmake::SetDebugFindOutputPkgs(std::string const& args)
4017 this->DebugFindPkgs.emplace(args);
4020 void cmake::SetDebugFindOutputVars(std::string const& args)
4022 this->DebugFindVars.emplace(args);
4025 bool cmake::GetDebugFindOutput(std::string const& var) const
4027 return this->DebugFindVars.count(var);
4030 bool cmake::GetDebugFindPkgOutput(std::string const& pkg) const
4032 return this->DebugFindPkgs.count(pkg);
4035 #if !defined(CMAKE_BOOTSTRAP)
4036 cmMakefileProfilingData& cmake::GetProfilingOutput()
4038 return *(this->ProfilingOutput);
4041 bool cmake::IsProfilingEnabled() const
4043 return static_cast<bool>(this->ProfilingOutput);