CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmGlobalGenerator.cxx
blobec4481784e23bab55f80ace33b69a0ec523ca80b
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmGlobalGenerator.h"
5 #include <algorithm>
6 #include <cassert>
7 #include <cstdio>
8 #include <cstdlib>
9 #include <cstring>
10 #include <functional>
11 #include <initializer_list>
12 #include <iterator>
13 #include <sstream>
14 #include <type_traits>
15 #include <utility>
17 #include <cm/memory>
18 #include <cm/optional>
19 #include <cmext/algorithm>
20 #include <cmext/string_view>
22 #include "cmsys/Directory.hxx"
23 #include "cmsys/FStream.hxx"
24 #include "cmsys/RegularExpression.hxx"
26 #include "cm_codecvt_Encoding.hxx"
28 #include "cmAlgorithms.h"
29 #include "cmCMakePath.h"
30 #include "cmCPackPropertiesGenerator.h"
31 #include "cmComputeTargetDepends.h"
32 #include "cmCryptoHash.h"
33 #include "cmCustomCommand.h"
34 #include "cmCustomCommandLines.h"
35 #include "cmCustomCommandTypes.h"
36 #include "cmDuration.h"
37 #include "cmExperimental.h"
38 #include "cmExportBuildFileGenerator.h"
39 #include "cmExternalMakefileProjectGenerator.h"
40 #include "cmGeneratedFileStream.h"
41 #include "cmGeneratorExpression.h"
42 #include "cmGeneratorTarget.h"
43 #include "cmInstallGenerator.h"
44 #include "cmInstallRuntimeDependencySet.h"
45 #include "cmLinkLineComputer.h"
46 #include "cmList.h"
47 #include "cmLocalGenerator.h"
48 #include "cmMSVC60LinkLineComputer.h"
49 #include "cmMakefile.h"
50 #include "cmMessageType.h"
51 #include "cmOutputConverter.h"
52 #include "cmPolicies.h"
53 #include "cmRange.h"
54 #include "cmSourceFile.h"
55 #include "cmState.h"
56 #include "cmStateDirectory.h"
57 #include "cmStateTypes.h"
58 #include "cmStringAlgorithms.h"
59 #include "cmSyntheticTargetCache.h"
60 #include "cmSystemTools.h"
61 #include "cmValue.h"
62 #include "cmVersion.h"
63 #include "cmWorkingDirectory.h"
64 #include "cmake.h"
66 #if !defined(CMAKE_BOOTSTRAP)
67 # include <cm3p/json/value.h>
68 # include <cm3p/json/writer.h>
70 # include "cmQtAutoGenGlobalInitializer.h"
71 #endif
73 class cmListFileBacktrace;
75 const std::string kCMAKE_PLATFORM_INFO_INITIALIZED =
76 "CMAKE_PLATFORM_INFO_INITIALIZED";
78 class cmInstalledFile;
80 namespace detail {
81 std::string GeneratedMakeCommand::QuotedPrintable() const
83 std::string output;
84 const char* sep = "";
85 int flags = 0;
86 #if !defined(_WIN32)
87 flags |= cmOutputConverter::Shell_Flag_IsUnix;
88 #endif
89 for (auto const& arg : this->PrimaryCommand) {
90 output += cmStrCat(sep, cmOutputConverter::EscapeForShell(arg, flags));
91 sep = " ";
93 return output;
97 bool cmTarget::StrictTargetComparison::operator()(cmTarget const* t1,
98 cmTarget const* t2) const
100 int nameResult = strcmp(t1->GetName().c_str(), t2->GetName().c_str());
101 if (nameResult == 0) {
102 return strcmp(t1->GetMakefile()->GetCurrentBinaryDirectory().c_str(),
103 t2->GetMakefile()->GetCurrentBinaryDirectory().c_str()) < 0;
105 return nameResult < 0;
108 cmGlobalGenerator::cmGlobalGenerator(cmake* cm)
109 : CMakeInstance(cm)
111 // By default the .SYMBOLIC dependency is not needed on symbolic rules.
112 this->NeedSymbolicMark = false;
114 // by default use the native paths
115 this->ForceUnixPaths = false;
117 // By default do not try to support color.
118 this->ToolSupportsColor = false;
120 // By default do not use link scripts.
121 this->UseLinkScript = false;
123 // Whether an install target is needed.
124 this->InstallTargetEnabled = false;
126 // how long to let try compiles run
127 this->TryCompileTimeout = cmDuration::zero();
129 this->CurrentConfigureMakefile = nullptr;
130 this->TryCompileOuterMakefile = nullptr;
132 this->ConfigureDoneCMP0026AndCMP0024 = false;
133 this->FirstTimeProgress = 0.0f;
135 cm->GetState()->SetIsGeneratorMultiConfig(false);
136 cm->GetState()->SetMinGWMake(false);
137 cm->GetState()->SetMSYSShell(false);
138 cm->GetState()->SetNMake(false);
139 cm->GetState()->SetWatcomWMake(false);
140 cm->GetState()->SetWindowsShell(false);
141 cm->GetState()->SetWindowsVSIDE(false);
143 #if !defined(CMAKE_BOOTSTRAP)
144 Json::StreamWriterBuilder wbuilder;
145 wbuilder["indentation"] = "\t";
146 this->JsonWriter =
147 std::unique_ptr<Json::StreamWriter>(wbuilder.newStreamWriter());
148 #endif
151 cmGlobalGenerator::~cmGlobalGenerator()
153 this->ClearGeneratorMembers();
155 codecvt_Encoding cmGlobalGenerator::GetMakefileEncoding() const
157 return codecvt_Encoding::None;
160 #if !defined(CMAKE_BOOTSTRAP)
161 Json::Value cmGlobalGenerator::GetJson() const
163 Json::Value generator = Json::objectValue;
164 generator["name"] = this->GetName();
165 generator["multiConfig"] = this->IsMultiConfig();
166 return generator;
168 #endif
170 bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i,
171 cmMakefile* mf)
173 if (i.empty()) {
174 return true;
177 std::ostringstream e;
178 /* clang-format off */
179 e <<
180 "Generator\n"
181 " " << this->GetName() << "\n"
182 "does not support instance specification, but instance\n"
183 " " << i << "\n"
184 "was specified.";
185 /* clang-format on */
186 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
187 return false;
190 bool cmGlobalGenerator::SetGeneratorPlatform(std::string const& p,
191 cmMakefile* mf)
193 if (p.empty()) {
194 return true;
197 std::ostringstream e;
198 /* clang-format off */
199 e <<
200 "Generator\n"
201 " " << this->GetName() << "\n"
202 "does not support platform specification, but platform\n"
203 " " << p << "\n"
204 "was specified.";
205 /* clang-format on */
206 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
207 return false;
210 bool cmGlobalGenerator::SetGeneratorToolset(std::string const& ts, bool,
211 cmMakefile* mf)
213 if (ts.empty()) {
214 return true;
216 std::ostringstream e;
217 /* clang-format off */
218 e <<
219 "Generator\n"
220 " " << this->GetName() << "\n"
221 "does not support toolset specification, but toolset\n"
222 " " << ts << "\n"
223 "was specified.";
224 /* clang-format on */
225 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
226 return false;
229 std::string cmGlobalGenerator::SelectMakeProgram(
230 const std::string& inMakeProgram, const std::string& makeDefault) const
232 std::string makeProgram = inMakeProgram;
233 if (cmIsOff(makeProgram)) {
234 cmValue makeProgramCSTR =
235 this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
236 if (makeProgramCSTR.IsOff()) {
237 makeProgram = makeDefault;
238 } else {
239 makeProgram = *makeProgramCSTR;
241 if (cmIsOff(makeProgram) && !makeProgram.empty()) {
242 makeProgram = "CMAKE_MAKE_PROGRAM-NOTFOUND";
245 return makeProgram;
248 void cmGlobalGenerator::ResolveLanguageCompiler(const std::string& lang,
249 cmMakefile* mf,
250 bool optional) const
252 std::string langComp = cmStrCat("CMAKE_", lang, "_COMPILER");
254 if (!mf->GetDefinition(langComp)) {
255 if (!optional) {
256 cmSystemTools::Error(
257 cmStrCat(langComp, " not set, after EnableLanguage"));
259 return;
261 std::string const& name = mf->GetRequiredDefinition(langComp);
262 std::string path;
263 if (!cmSystemTools::FileIsFullPath(name)) {
264 path = cmSystemTools::FindProgram(name);
265 } else {
266 path = name;
268 if (!optional && (path.empty() || !cmSystemTools::FileExists(path))) {
269 return;
271 cmValue cname =
272 this->GetCMakeInstance()->GetState()->GetInitializedCacheValue(langComp);
274 // Split compiler from arguments
275 cmList cnameArgList;
276 if (cname && !cname->empty()) {
277 cnameArgList.assign(*cname);
278 cname = cmValue(cnameArgList.front());
281 std::string changeVars;
282 if (cname && !optional) {
283 cmCMakePath cachedPath;
284 if (!cmSystemTools::FileIsFullPath(*cname)) {
285 cachedPath = cmSystemTools::FindProgram(*cname);
286 } else {
287 cachedPath = *cname;
289 cmCMakePath foundPath = path;
290 if (foundPath.Normal() != cachedPath.Normal()) {
291 cmValue cvars = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
292 "__CMAKE_DELETE_CACHE_CHANGE_VARS_");
293 if (cvars) {
294 changeVars += *cvars;
295 changeVars += ";";
297 changeVars += langComp;
298 changeVars += ";";
299 changeVars += *cname;
300 this->GetCMakeInstance()->GetState()->SetGlobalProperty(
301 "__CMAKE_DELETE_CACHE_CHANGE_VARS_", changeVars);
306 void cmGlobalGenerator::AddBuildExportSet(cmExportBuildFileGenerator* gen)
308 this->BuildExportSets[gen->GetMainExportFileName()] = gen;
311 void cmGlobalGenerator::AddBuildExportExportSet(
312 cmExportBuildFileGenerator* gen)
314 this->BuildExportExportSets[gen->GetMainExportFileName()] = gen;
315 this->AddBuildExportSet(gen);
318 bool cmGlobalGenerator::GenerateImportFile(const std::string& file)
320 auto const it = this->BuildExportSets.find(file);
321 if (it != this->BuildExportSets.end()) {
322 bool result = it->second->GenerateImportFile();
324 if (!this->ConfigureDoneCMP0026AndCMP0024) {
325 for (const auto& m : this->Makefiles) {
326 m->RemoveExportBuildFileGeneratorCMP0024(it->second);
330 this->BuildExportSets.erase(it);
331 return result;
333 return false;
336 void cmGlobalGenerator::ForceLinkerLanguages()
340 bool cmGlobalGenerator::CheckTargetsForMissingSources() const
342 bool failed = false;
343 for (const auto& localGen : this->LocalGenerators) {
344 for (const auto& target : localGen->GetGeneratorTargets()) {
345 if (!target->CanCompileSources() ||
346 target->GetProperty("ghs_integrity_app").IsOn()) {
347 continue;
350 if (target->GetAllConfigSources().empty()) {
351 std::ostringstream e;
352 e << "No SOURCES given to target: " << target->GetName();
353 this->GetCMakeInstance()->IssueMessage(
354 MessageType::FATAL_ERROR, e.str(), target->GetBacktrace());
355 failed = true;
359 return failed;
362 void cmGlobalGenerator::CheckTargetLinkLibraries() const
364 for (const auto& generator : this->LocalGenerators) {
365 for (const auto& gt : generator->GetGeneratorTargets()) {
366 gt->CheckLinkLibraries();
368 for (const auto& gt : generator->GetOwnedImportedGeneratorTargets()) {
369 gt->CheckLinkLibraries();
374 bool cmGlobalGenerator::CheckTargetsForType() const
376 if (!this->GetLanguageEnabled("Swift")) {
377 return false;
379 bool failed = false;
380 for (const auto& generator : this->LocalGenerators) {
381 for (const auto& target : generator->GetGeneratorTargets()) {
382 std::string systemName =
383 target->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME");
384 if (systemName.find("Windows") == std::string::npos) {
385 continue;
388 if (target->GetType() == cmStateEnums::EXECUTABLE) {
389 std::vector<std::string> const& configs =
390 target->Makefile->GetGeneratorConfigs(
391 cmMakefile::IncludeEmptyConfig);
392 for (std::string const& config : configs) {
393 if (target->IsWin32Executable(config) &&
394 target->GetLinkerLanguage(config) == "Swift") {
395 this->GetCMakeInstance()->IssueMessage(
396 MessageType::FATAL_ERROR,
397 "WIN32_EXECUTABLE property is not supported on Swift "
398 "executables",
399 target->GetBacktrace());
400 failed = true;
406 return failed;
409 bool cmGlobalGenerator::CheckTargetsForPchCompilePdb() const
411 if (!this->GetLanguageEnabled("C") && !this->GetLanguageEnabled("CXX")) {
412 return false;
414 bool failed = false;
415 for (const auto& generator : this->LocalGenerators) {
416 for (const auto& target : generator->GetGeneratorTargets()) {
417 if (!target->CanCompileSources() ||
418 target->GetProperty("ghs_integrity_app").IsOn()) {
419 continue;
422 std::string const& reuseFrom =
423 target->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM");
424 std::string const& compilePdb =
425 target->GetSafeProperty("COMPILE_PDB_NAME");
427 if (!reuseFrom.empty() && reuseFrom != compilePdb) {
428 const std::string e = cmStrCat(
429 "PRECOMPILE_HEADERS_REUSE_FROM property is set on target (\"",
430 target->GetName(),
431 "\"). Reusable precompile headers requires the COMPILE_PDB_NAME"
432 " property to have the value \"",
433 reuseFrom, "\"\n");
434 this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
435 target->GetBacktrace());
436 failed = true;
440 return failed;
443 bool cmGlobalGenerator::IsExportedTargetsFile(
444 const std::string& filename) const
446 auto const it = this->BuildExportSets.find(filename);
447 if (it == this->BuildExportSets.end()) {
448 return false;
450 return !cm::contains(this->BuildExportExportSets, filename);
453 // Find the make program for the generator, required for try compiles
454 bool cmGlobalGenerator::FindMakeProgram(cmMakefile* mf)
456 if (this->FindMakeProgramFile.empty()) {
457 cmSystemTools::Error(
458 "Generator implementation error, "
459 "all generators must specify this->FindMakeProgramFile");
460 return false;
462 if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) {
463 std::string setMakeProgram = mf->GetModulesFile(this->FindMakeProgramFile);
464 if (!setMakeProgram.empty()) {
465 mf->ReadListFile(setMakeProgram);
468 if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) {
469 std::ostringstream err;
470 err << "CMake was unable to find a build program corresponding to \""
471 << this->GetName() << "\". CMAKE_MAKE_PROGRAM is not set. You "
472 << "probably need to select a different build tool.";
473 cmSystemTools::Error(err.str());
474 cmSystemTools::SetFatalErrorOccurred();
475 return false;
477 std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
478 // if there are spaces in the make program use short path
479 // but do not short path the actual program name, as
480 // this can cause trouble with VSExpress
481 if (makeProgram.find(' ') != std::string::npos) {
482 std::string dir;
483 std::string file;
484 cmSystemTools::SplitProgramPath(makeProgram, dir, file);
485 std::string saveFile = file;
486 cmSystemTools::GetShortPath(makeProgram, makeProgram);
487 cmSystemTools::SplitProgramPath(makeProgram, dir, file);
488 makeProgram = cmStrCat(dir, '/', saveFile);
489 mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", makeProgram, "make program",
490 cmStateEnums::FILEPATH);
492 return true;
495 bool cmGlobalGenerator::CheckLanguages(
496 std::vector<std::string> const& /* languages */, cmMakefile* /* mf */) const
498 return true;
501 // enable the given language
503 // The following files are loaded in this order:
505 // First figure out what OS we are running on:
507 // CMakeSystem.cmake - configured file created by CMakeDetermineSystem.cmake
508 // CMakeDetermineSystem.cmake - figure out os info and create
509 // CMakeSystem.cmake IF CMAKE_SYSTEM
510 // not set
511 // CMakeSystem.cmake - configured file created by
512 // CMakeDetermineSystem.cmake IF CMAKE_SYSTEM_LOADED
514 // CMakeSystemSpecificInitialize.cmake
515 // - includes Platform/${CMAKE_SYSTEM_NAME}-Initialize.cmake
517 // Next try and enable all languages found in the languages vector
519 // FOREACH LANG in languages
520 // CMake(LANG)Compiler.cmake - configured file create by
521 // CMakeDetermine(LANG)Compiler.cmake
522 // CMakeDetermine(LANG)Compiler.cmake - Finds compiler for LANG and
523 // creates CMake(LANG)Compiler.cmake
524 // CMake(LANG)Compiler.cmake - configured file created by
525 // CMakeDetermine(LANG)Compiler.cmake
527 // CMakeSystemSpecificInformation.cmake
528 // - includes Platform/${CMAKE_SYSTEM_NAME}.cmake
529 // may use compiler stuff
531 // FOREACH LANG in languages
532 // CMake(LANG)Information.cmake
533 // - loads Platform/${CMAKE_SYSTEM_NAME}-${COMPILER}.cmake
534 // CMakeTest(LANG)Compiler.cmake
535 // - Make sure the compiler works with a try compile if
536 // CMakeDetermine(LANG) was loaded
538 // CMake(LANG)LinkerInformation.cmake
539 // - loads Platform/Linker/${CMAKE_SYSTEM_NAME}-${LINKER}.cmake
541 // Now load a few files that can override values set in any of the above
542 // (PROJECTNAME)Compatibility.cmake
543 // - load any backwards compatibility stuff for current project
544 // ${CMAKE_USER_MAKE_RULES_OVERRIDE}
545 // - allow users a chance to override system variables
549 void cmGlobalGenerator::EnableLanguage(
550 std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
552 if (!this->IsMultiConfig() &&
553 !this->GetCMakeInstance()->GetIsInTryCompile()) {
554 std::string envBuildType;
555 if (!mf->GetDefinition("CMAKE_BUILD_TYPE") &&
556 cmSystemTools::GetEnv("CMAKE_BUILD_TYPE", envBuildType)) {
557 mf->AddCacheDefinition(
558 "CMAKE_BUILD_TYPE", envBuildType,
559 "Choose the type of build. Options include: empty, "
560 "Debug, Release, RelWithDebInfo, MinSizeRel.",
561 cmStateEnums::STRING);
565 if (languages.empty()) {
566 cmSystemTools::Error("EnableLanguage must have a lang specified!");
567 cmSystemTools::SetFatalErrorOccurred();
568 return;
571 std::set<std::string> cur_languages(languages.begin(), languages.end());
572 for (std::string const& li : cur_languages) {
573 if (!this->LanguagesInProgress.insert(li).second) {
574 std::ostringstream e;
575 e << "Language '" << li
576 << "' is currently being enabled. "
577 "Recursive call not allowed.";
578 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
579 cmSystemTools::SetFatalErrorOccurred();
580 return;
584 if (this->TryCompileOuterMakefile) {
585 // In a try-compile we can only enable languages provided by caller.
586 for (std::string const& lang : languages) {
587 if (lang == "NONE") {
588 this->SetLanguageEnabled("NONE", mf);
589 } else {
590 if (!cm::contains(this->LanguagesReady, lang)) {
591 std::ostringstream e;
592 e << "The test project needs language " << lang
593 << " which is not enabled.";
594 this->TryCompileOuterMakefile->IssueMessage(MessageType::FATAL_ERROR,
595 e.str());
596 cmSystemTools::SetFatalErrorOccurred();
597 return;
603 bool fatalError = false;
605 mf->AddDefinitionBool("RUN_CONFIGURE", true);
606 std::string rootBin =
607 cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
609 // If the configuration files path has been set,
610 // then we are in a try compile and need to copy the enable language
611 // files from the parent cmake bin dir, into the try compile bin dir
612 if (!this->ConfiguredFilesPath.empty()) {
613 rootBin = this->ConfiguredFilesPath;
615 rootBin += '/';
616 rootBin += cmVersion::GetCMakeVersion();
618 // set the dir for parent files so they can be used by modules
619 mf->AddDefinition("CMAKE_PLATFORM_INFO_DIR", rootBin);
621 if (!this->CMakeInstance->GetIsInTryCompile()) {
622 // Keep a mark in the cache to indicate that we've initialized the
623 // platform information directory. If the platform information
624 // directory exists but the mark is missing then CMakeCache.txt
625 // has been removed or replaced without also removing the CMakeFiles/
626 // directory. In this case remove the platform information directory
627 // so that it will be re-initialized and the relevant information
628 // restored in the cache.
629 if (cmSystemTools::FileIsDirectory(rootBin) &&
630 !mf->IsOn(kCMAKE_PLATFORM_INFO_INITIALIZED)) {
631 cmSystemTools::RemoveADirectory(rootBin);
633 this->GetCMakeInstance()->AddCacheEntry(
634 kCMAKE_PLATFORM_INFO_INITIALIZED, "1",
635 "Platform information initialized", cmStateEnums::INTERNAL);
638 // try and load the CMakeSystem.cmake if it is there
639 std::string fpath = rootBin;
640 bool const readCMakeSystem = !mf->GetDefinition("CMAKE_SYSTEM_LOADED");
641 if (readCMakeSystem) {
642 fpath += "/CMakeSystem.cmake";
643 if (cmSystemTools::FileExists(fpath)) {
644 mf->ReadListFile(fpath);
648 // Load the CMakeDetermineSystem.cmake file and find out
649 // what platform we are running on
650 if (!mf->GetDefinition("CMAKE_SYSTEM")) {
651 #if defined(_WIN32) && !defined(__CYGWIN__)
652 cmSystemTools::WindowsVersion windowsVersion =
653 cmSystemTools::GetWindowsVersion();
654 auto windowsVersionString = cmStrCat(windowsVersion.dwMajorVersion, '.',
655 windowsVersion.dwMinorVersion, '.',
656 windowsVersion.dwBuildNumber);
657 mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString);
658 #endif
659 // Read the DetermineSystem file
660 std::string systemFile = mf->GetModulesFile("CMakeDetermineSystem.cmake");
661 mf->ReadListFile(systemFile);
662 // load the CMakeSystem.cmake from the binary directory
663 // this file is configured by the CMakeDetermineSystem.cmake file
664 fpath = cmStrCat(rootBin, "/CMakeSystem.cmake");
665 mf->ReadListFile(fpath);
668 if (readCMakeSystem) {
669 // Tell the generator about the instance, if any.
670 std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
671 if (!this->SetGeneratorInstance(instance, mf)) {
672 cmSystemTools::SetFatalErrorOccurred();
673 return;
676 // Tell the generator about the target system.
677 std::string system = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
678 if (!this->SetSystemName(system, mf)) {
679 cmSystemTools::SetFatalErrorOccurred();
680 return;
683 // Tell the generator about the platform, if any.
684 std::string platform = mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM");
685 if (!this->SetGeneratorPlatform(platform, mf)) {
686 cmSystemTools::SetFatalErrorOccurred();
687 return;
690 // Tell the generator about the toolset, if any.
691 std::string toolset = mf->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET");
692 if (!this->SetGeneratorToolset(toolset, false, mf)) {
693 cmSystemTools::SetFatalErrorOccurred();
694 return;
697 // Find the native build tool for this generator.
698 if (!this->FindMakeProgram(mf)) {
699 return;
702 // One-time includes of user-provided project setup files
703 mf->GetState()->SetInTopLevelIncludes(true);
704 std::string includes =
705 mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES");
706 cmList includesList{ includes };
707 for (std::string setupFile : includesList) {
708 // Any relative path without a .cmake extension is checked for valid
709 // cmake modules. This logic should be consistent with CMake's include()
710 // command. Otherwise default to checking relative path w.r.t. source
711 // directory
712 if (!cmSystemTools::FileIsFullPath(setupFile) &&
713 !cmHasLiteralSuffix(setupFile, ".cmake")) {
714 std::string mfile = mf->GetModulesFile(cmStrCat(setupFile, ".cmake"));
715 if (mfile.empty()) {
716 cmSystemTools::Error(cmStrCat(
717 "CMAKE_PROJECT_TOP_LEVEL_INCLUDES module:\n ", setupFile));
718 mf->GetState()->SetInTopLevelIncludes(false);
719 return;
721 setupFile = mfile;
723 std::string absSetupFile = cmSystemTools::CollapseFullPath(
724 setupFile, mf->GetCurrentSourceDirectory());
725 if (!cmSystemTools::FileExists(absSetupFile)) {
726 cmSystemTools::Error(
727 cmStrCat("CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: ",
728 setupFile));
729 mf->GetState()->SetInTopLevelIncludes(false);
730 return;
732 if (cmSystemTools::FileIsDirectory(absSetupFile)) {
733 cmSystemTools::Error(
734 cmStrCat("CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: ",
735 setupFile));
736 mf->GetState()->SetInTopLevelIncludes(false);
737 return;
739 if (!mf->ReadListFile(absSetupFile)) {
740 cmSystemTools::Error(
741 cmStrCat("Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: ",
742 setupFile));
743 mf->GetState()->SetInTopLevelIncludes(false);
744 return;
748 mf->GetState()->SetInTopLevelIncludes(false);
750 // Check that the languages are supported by the generator and its
751 // native build tool found above.
752 if (!this->CheckLanguages(languages, mf)) {
753 return;
756 // **** Load the system specific initialization if not yet loaded
757 if (!mf->GetDefinition("CMAKE_SYSTEM_SPECIFIC_INITIALIZE_LOADED")) {
758 fpath = mf->GetModulesFile("CMakeSystemSpecificInitialize.cmake");
759 if (!mf->ReadListFile(fpath)) {
760 cmSystemTools::Error("Could not find cmake module file: "
761 "CMakeSystemSpecificInitialize.cmake");
765 std::map<std::string, bool> needTestLanguage;
766 std::map<std::string, bool> needSetLanguageEnabledMaps;
767 // foreach language
768 // load the CMakeDetermine(LANG)Compiler.cmake file to find
769 // the compiler
771 for (std::string const& lang : languages) {
772 needSetLanguageEnabledMaps[lang] = false;
773 if (lang == "NONE") {
774 this->SetLanguageEnabled("NONE", mf);
775 continue;
777 std::string loadedLang = cmStrCat("CMAKE_", lang, "_COMPILER_LOADED");
778 if (!mf->GetDefinition(loadedLang)) {
779 fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
781 // If the existing build tree was already configured with this
782 // version of CMake then try to load the configured file first
783 // to avoid duplicate compiler tests.
784 if (cmSystemTools::FileExists(fpath)) {
785 if (!mf->ReadListFile(fpath)) {
786 cmSystemTools::Error(
787 cmStrCat("Could not find cmake module file: ", fpath));
789 // if this file was found then the language was already determined
790 // to be working
791 needTestLanguage[lang] = false;
792 this->SetLanguageEnabledFlag(lang, mf);
793 needSetLanguageEnabledMaps[lang] = true;
794 // this can only be called after loading CMake(LANG)Compiler.cmake
798 if (!this->GetLanguageEnabled(lang)) {
799 if (this->CMakeInstance->GetIsInTryCompile()) {
800 cmSystemTools::Error("This should not have happened. "
801 "If you see this message, you are probably "
802 "using a broken CMakeLists.txt file or a "
803 "problematic release of CMake");
805 // if the CMake(LANG)Compiler.cmake file was not found then
806 // load CMakeDetermine(LANG)Compiler.cmake
807 std::string determineCompiler =
808 cmStrCat("CMakeDetermine", lang, "Compiler.cmake");
809 std::string determineFile = mf->GetModulesFile(determineCompiler);
810 if (!mf->ReadListFile(determineFile)) {
811 cmSystemTools::Error(
812 cmStrCat("Could not find cmake module file: ", determineCompiler));
814 if (cmSystemTools::GetFatalErrorOccurred()) {
815 return;
817 needTestLanguage[lang] = true;
818 // Some generators like visual studio should not use the env variables
819 // So the global generator can specify that in this variable
820 if ((mf->GetPolicyStatus(cmPolicies::CMP0132) == cmPolicies::OLD ||
821 mf->GetPolicyStatus(cmPolicies::CMP0132) == cmPolicies::WARN) &&
822 !mf->GetDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV")) {
823 // put ${CMake_(LANG)_COMPILER_ENV_VAR}=${CMAKE_(LANG)_COMPILER
824 // into the environment, in case user scripts want to run
825 // configure, or sub cmakes
826 std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
827 std::string compilerEnv =
828 cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
829 const std::string& envVar = mf->GetRequiredDefinition(compilerEnv);
830 const std::string& envVarValue =
831 mf->GetRequiredDefinition(compilerName);
832 std::string env = cmStrCat(envVar, '=', envVarValue);
833 cmSystemTools::PutEnv(env);
836 // if determineLanguage was called then load the file it
837 // configures CMake(LANG)Compiler.cmake
838 fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
839 if (!mf->ReadListFile(fpath)) {
840 cmSystemTools::Error(
841 cmStrCat("Could not find cmake module file: ", fpath));
843 this->SetLanguageEnabledFlag(lang, mf);
844 needSetLanguageEnabledMaps[lang] = true;
845 // this can only be called after loading CMake(LANG)Compiler.cmake
846 // the language must be enabled for try compile to work, but we do
847 // not know if it is a working compiler yet so set the test language
848 // flag
849 needTestLanguage[lang] = true;
850 } // end if(!this->GetLanguageEnabled(lang) )
851 } // end loop over languages
853 // **** Load the system specific information if not yet loaded
854 if (!mf->GetDefinition("CMAKE_SYSTEM_SPECIFIC_INFORMATION_LOADED")) {
855 fpath = mf->GetModulesFile("CMakeSystemSpecificInformation.cmake");
856 if (!mf->ReadListFile(fpath)) {
857 cmSystemTools::Error("Could not find cmake module file: "
858 "CMakeSystemSpecificInformation.cmake");
861 // loop over languages again loading CMake(LANG)Information.cmake
863 for (std::string const& lang : languages) {
864 if (lang == "NONE") {
865 this->SetLanguageEnabled("NONE", mf);
866 continue;
869 // Check that the compiler was found.
870 std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
871 std::string compilerEnv = cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
872 std::ostringstream noCompiler;
873 cmValue compilerFile = mf->GetDefinition(compilerName);
874 if (!cmNonempty(compilerFile) || cmIsNOTFOUND(*compilerFile)) {
875 /* clang-format off */
876 noCompiler <<
877 "No " << compilerName << " could be found.\n"
879 /* clang-format on */
880 } else if ((lang != "RC") && (lang != "ASM_MARMASM") &&
881 (lang != "ASM_MASM")) {
882 if (!cmSystemTools::FileIsFullPath(*compilerFile)) {
883 /* clang-format off */
884 noCompiler <<
885 "The " << compilerName << ":\n"
886 " " << *compilerFile << "\n"
887 "is not a full path and was not found in the PATH."
888 #ifdef _WIN32
889 " Perhaps the extension is missing?"
890 #endif
891 "\n"
893 /* clang-format on */
894 } else if (!cmSystemTools::FileExists(*compilerFile)) {
895 /* clang-format off */
896 noCompiler <<
897 "The " << compilerName << ":\n"
898 " " << *compilerFile << "\n"
899 "is not a full path to an existing compiler tool.\n"
901 /* clang-format on */
904 if (!noCompiler.str().empty()) {
905 // Skip testing this language since the compiler is not found.
906 needTestLanguage[lang] = false;
907 if (!optional) {
908 // The compiler was not found and it is not optional. Remove
909 // CMake(LANG)Compiler.cmake so we try again next time CMake runs.
910 std::string compilerLangFile =
911 cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
912 cmSystemTools::RemoveFile(compilerLangFile);
913 if (!this->CMakeInstance->GetIsInTryCompile()) {
914 this->PrintCompilerAdvice(noCompiler, lang,
915 mf->GetDefinition(compilerEnv));
916 mf->IssueMessage(MessageType::FATAL_ERROR, noCompiler.str());
917 fatalError = true;
922 std::string langLoadedVar =
923 cmStrCat("CMAKE_", lang, "_INFORMATION_LOADED");
924 if (!mf->GetDefinition(langLoadedVar)) {
925 fpath = cmStrCat("CMake", lang, "Information.cmake");
926 std::string informationFile = mf->GetModulesFile(fpath);
927 if (informationFile.empty()) {
928 cmSystemTools::Error(
929 cmStrCat("Could not find cmake module file: ", fpath));
930 } else if (!mf->ReadListFile(informationFile)) {
931 cmSystemTools::Error(
932 cmStrCat("Could not process cmake module file: ", informationFile));
935 if (needSetLanguageEnabledMaps[lang]) {
936 this->SetLanguageEnabledMaps(lang, mf);
938 this->LanguagesReady.insert(lang);
940 // Test the compiler for the language just setup
941 // (but only if a compiler has been actually found)
942 // At this point we should have enough info for a try compile
943 // which is used in the backward stuff
944 // If the language is untested then test it now with a try compile.
945 if (needTestLanguage[lang]) {
946 if (!this->CMakeInstance->GetIsInTryCompile()) {
947 std::string testLang = cmStrCat("CMakeTest", lang, "Compiler.cmake");
948 std::string ifpath = mf->GetModulesFile(testLang);
949 if (!mf->ReadListFile(ifpath)) {
950 cmSystemTools::Error(
951 cmStrCat("Could not find cmake module file: ", testLang));
953 std::string compilerWorks =
954 cmStrCat("CMAKE_", lang, "_COMPILER_WORKS");
955 // if the compiler did not work, then remove the
956 // CMake(LANG)Compiler.cmake file so that it will get tested the
957 // next time cmake is run
958 if (!mf->IsOn(compilerWorks)) {
959 std::string compilerLangFile =
960 cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
961 cmSystemTools::RemoveFile(compilerLangFile);
963 } // end if in try compile
964 } // end need test language
966 // load linker configuration
967 std::string langLinkerLoadedVar =
968 cmStrCat("CMAKE_", lang, "_LINKER_INFORMATION_LOADED");
969 if (!mf->GetDefinition(langLinkerLoadedVar)) {
970 fpath = cmStrCat("Internal/CMake", lang, "LinkerInformation.cmake");
971 std::string informationFile = mf->GetModulesFile(fpath);
972 if (informationFile.empty()) {
973 cmSystemTools::Error(
974 cmStrCat("Could not find cmake module file: ", fpath));
975 } else if (!mf->ReadListFile(informationFile)) {
976 cmSystemTools::Error(
977 cmStrCat("Could not process cmake module file: ", informationFile));
981 // Store the shared library flags so that we can satisfy CMP0018
982 std::string sharedLibFlagsVar =
983 cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS");
984 this->LanguageToOriginalSharedLibFlags[lang] =
985 mf->GetSafeDefinition(sharedLibFlagsVar);
987 // Translate compiler ids for compatibility.
988 this->CheckCompilerIdCompatibility(mf, lang);
989 } // end for each language
991 // Now load files that can override any settings on the platform or for
992 // the project First load the project compatibility file if it is in
993 // cmake
994 std::string projectCompatibility =
995 cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/",
996 mf->GetSafeDefinition("PROJECT_NAME"), "Compatibility.cmake");
997 if (cmSystemTools::FileExists(projectCompatibility)) {
998 mf->ReadListFile(projectCompatibility);
1000 // Inform any extra generator of the new language.
1001 if (this->ExtraGenerator) {
1002 this->ExtraGenerator->EnableLanguage(languages, mf, false);
1005 if (fatalError) {
1006 cmSystemTools::SetFatalErrorOccurred();
1009 for (std::string const& lang : cur_languages) {
1010 this->LanguagesInProgress.erase(lang);
1014 void cmGlobalGenerator::PrintCompilerAdvice(std::ostream& os,
1015 std::string const& lang,
1016 cmValue envVar) const
1018 // Subclasses override this method if they do not support this advice.
1019 os << "Tell CMake where to find the compiler by setting ";
1020 if (envVar) {
1021 os << "either the environment variable \"" << *envVar << "\" or ";
1023 os << "the CMake cache entry CMAKE_" << lang
1024 << "_COMPILER "
1025 "to the full path to the compiler, or to the compiler name "
1026 "if it is in the PATH.";
1029 void cmGlobalGenerator::CheckCompilerIdCompatibility(
1030 cmMakefile* mf, std::string const& lang) const
1032 std::string compilerIdVar = cmStrCat("CMAKE_", lang, "_COMPILER_ID");
1033 std::string const compilerId = mf->GetSafeDefinition(compilerIdVar);
1035 if (compilerId == "AppleClang") {
1036 switch (mf->GetPolicyStatus(cmPolicies::CMP0025)) {
1037 case cmPolicies::WARN:
1038 if (!this->CMakeInstance->GetIsInTryCompile() &&
1039 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0025")) {
1040 std::ostringstream w;
1041 /* clang-format off */
1042 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0025) << "\n"
1043 "Converting " << lang <<
1044 R"( compiler id "AppleClang" to "Clang" for compatibility.)"
1046 /* clang-format on */
1047 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
1049 CM_FALLTHROUGH;
1050 case cmPolicies::OLD:
1051 // OLD behavior is to convert AppleClang to Clang.
1052 mf->AddDefinition(compilerIdVar, "Clang");
1053 break;
1054 case cmPolicies::REQUIRED_IF_USED:
1055 case cmPolicies::REQUIRED_ALWAYS:
1056 mf->IssueMessage(
1057 MessageType::FATAL_ERROR,
1058 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0025));
1059 break;
1060 case cmPolicies::NEW:
1061 // NEW behavior is to keep AppleClang.
1062 break;
1066 if (compilerId == "QCC") {
1067 switch (mf->GetPolicyStatus(cmPolicies::CMP0047)) {
1068 case cmPolicies::WARN:
1069 if (!this->CMakeInstance->GetIsInTryCompile() &&
1070 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0047")) {
1071 std::ostringstream w;
1072 /* clang-format off */
1073 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0047) << "\n"
1074 "Converting " << lang <<
1075 R"( compiler id "QCC" to "GNU" for compatibility.)"
1077 /* clang-format on */
1078 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
1080 CM_FALLTHROUGH;
1081 case cmPolicies::OLD:
1082 // OLD behavior is to convert QCC to GNU.
1083 mf->AddDefinition(compilerIdVar, "GNU");
1084 if (lang == "C") {
1085 mf->AddDefinition("CMAKE_COMPILER_IS_GNUCC", "1");
1086 } else if (lang == "CXX") {
1087 mf->AddDefinition("CMAKE_COMPILER_IS_GNUCXX", "1");
1089 break;
1090 case cmPolicies::REQUIRED_IF_USED:
1091 case cmPolicies::REQUIRED_ALWAYS:
1092 mf->IssueMessage(
1093 MessageType::FATAL_ERROR,
1094 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0047));
1095 CM_FALLTHROUGH;
1096 case cmPolicies::NEW:
1097 // NEW behavior is to keep QCC.
1098 break;
1102 if (compilerId == "XLClang") {
1103 switch (mf->GetPolicyStatus(cmPolicies::CMP0089)) {
1104 case cmPolicies::WARN:
1105 if (!this->CMakeInstance->GetIsInTryCompile() &&
1106 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0089")) {
1107 std::ostringstream w;
1108 /* clang-format off */
1109 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0089) << "\n"
1110 "Converting " << lang <<
1111 R"( compiler id "XLClang" to "XL" for compatibility.)"
1113 /* clang-format on */
1114 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
1116 CM_FALLTHROUGH;
1117 case cmPolicies::OLD:
1118 // OLD behavior is to convert XLClang to XL.
1119 mf->AddDefinition(compilerIdVar, "XL");
1120 break;
1121 case cmPolicies::REQUIRED_IF_USED:
1122 case cmPolicies::REQUIRED_ALWAYS:
1123 mf->IssueMessage(
1124 MessageType::FATAL_ERROR,
1125 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0089));
1126 break;
1127 case cmPolicies::NEW:
1128 // NEW behavior is to keep AppleClang.
1129 break;
1133 if (compilerId == "LCC") {
1134 switch (mf->GetPolicyStatus(cmPolicies::CMP0129)) {
1135 case cmPolicies::WARN:
1136 if (!this->CMakeInstance->GetIsInTryCompile() &&
1137 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0129")) {
1138 std::ostringstream w;
1139 /* clang-format off */
1140 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0129) << "\n"
1141 "Converting " << lang <<
1142 R"( compiler id "LCC" to "GNU" for compatibility.)"
1144 /* clang-format on */
1145 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
1147 CM_FALLTHROUGH;
1148 case cmPolicies::OLD:
1149 // OLD behavior is to convert LCC to GNU.
1150 mf->AddDefinition(compilerIdVar, "GNU");
1151 if (lang == "C") {
1152 mf->AddDefinition("CMAKE_COMPILER_IS_GNUCC", "1");
1153 } else if (lang == "CXX") {
1154 mf->AddDefinition("CMAKE_COMPILER_IS_GNUCXX", "1");
1155 } else if (lang == "Fortran") {
1156 mf->AddDefinition("CMAKE_COMPILER_IS_GNUG77", "1");
1159 // Fix compiler versions.
1160 std::string version = cmStrCat("CMAKE_", lang, "_COMPILER_VERSION");
1161 std::string emulated = cmStrCat("CMAKE_", lang, "_SIMULATE_VERSION");
1162 std::string emulatedId = cmStrCat("CMAKE_", lang, "_SIMULATE_ID");
1163 std::string const& actual = mf->GetRequiredDefinition(emulated);
1164 mf->AddDefinition(version, actual);
1165 mf->RemoveDefinition(emulatedId);
1166 mf->RemoveDefinition(emulated);
1168 break;
1169 case cmPolicies::REQUIRED_IF_USED:
1170 case cmPolicies::REQUIRED_ALWAYS:
1171 mf->IssueMessage(
1172 MessageType::FATAL_ERROR,
1173 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0129));
1174 CM_FALLTHROUGH;
1175 case cmPolicies::NEW:
1176 // NEW behavior is to keep LCC.
1177 break;
1182 std::string cmGlobalGenerator::GetLanguageOutputExtension(
1183 cmSourceFile const& source) const
1185 const std::string& lang = source.GetLanguage();
1186 if (!lang.empty()) {
1187 return this->GetLanguageOutputExtension(lang);
1189 // if no language is found then check to see if it is already an
1190 // output extension for some language. In that case it should be ignored
1191 // and in this map, so it will not be compiled but will just be used.
1192 std::string const& ext = source.GetExtension();
1193 if (!ext.empty()) {
1194 if (this->OutputExtensions.count(ext)) {
1195 return ext;
1198 return "";
1201 std::string cmGlobalGenerator::GetLanguageOutputExtension(
1202 std::string const& lang) const
1204 auto const it = this->LanguageToOutputExtension.find(lang);
1205 if (it != this->LanguageToOutputExtension.end()) {
1206 return it->second;
1208 return "";
1211 std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const
1213 // if there is an extension and it starts with . then move past the
1214 // . because the extensions are not stored with a . in the map
1215 if (!ext) {
1216 return "";
1218 if (*ext == '.') {
1219 ++ext;
1221 auto const it = this->ExtensionToLanguage.find(ext);
1222 if (it != this->ExtensionToLanguage.end()) {
1223 return it->second;
1225 return "";
1228 /* SetLanguageEnabled() is now split in two parts:
1229 at first the enabled-flag is set. This can then be used in EnabledLanguage()
1230 for checking whether the language is already enabled. After setting this
1231 flag still the values from the cmake variables have to be copied into the
1232 internal maps, this is done in SetLanguageEnabledMaps() which is called
1233 after the system- and compiler specific files have been loaded.
1235 This split was done originally so that compiler-specific configuration
1236 files could change the object file extension
1237 (CMAKE_<LANG>_OUTPUT_EXTENSION) before the CMake variables were copied
1238 to the C++ maps.
1240 void cmGlobalGenerator::SetLanguageEnabled(const std::string& l,
1241 cmMakefile* mf)
1243 this->SetLanguageEnabledFlag(l, mf);
1244 this->SetLanguageEnabledMaps(l, mf);
1247 void cmGlobalGenerator::SetLanguageEnabledFlag(const std::string& l,
1248 cmMakefile* mf)
1250 this->CMakeInstance->GetState()->SetLanguageEnabled(l);
1252 // Fill the language-to-extension map with the current variable
1253 // settings to make sure it is available for the try_compile()
1254 // command source file signature. In SetLanguageEnabledMaps this
1255 // will be done again to account for any compiler- or
1256 // platform-specific entries.
1257 this->FillExtensionToLanguageMap(l, mf);
1260 void cmGlobalGenerator::SetLanguageEnabledMaps(const std::string& l,
1261 cmMakefile* mf)
1263 // use LanguageToLinkerPreference to detect whether this functions has
1264 // run before
1265 if (cm::contains(this->LanguageToLinkerPreference, l)) {
1266 return;
1269 std::string linkerPrefVar = cmStrCat("CMAKE_", l, "_LINKER_PREFERENCE");
1270 cmValue linkerPref = mf->GetDefinition(linkerPrefVar);
1271 int preference = 0;
1272 if (cmNonempty(linkerPref)) {
1273 if (sscanf(linkerPref->c_str(), "%d", &preference) != 1) {
1274 // backward compatibility: before 2.6 LINKER_PREFERENCE
1275 // was either "None" or "Preferred", and only the first character was
1276 // tested. So if there is a custom language out there and it is
1277 // "Preferred", set its preference high
1278 if ((*linkerPref)[0] == 'P') {
1279 preference = 100;
1280 } else {
1281 preference = 0;
1286 if (preference < 0) {
1287 std::string msg =
1288 cmStrCat(linkerPrefVar, " is negative, adjusting it to 0");
1289 cmSystemTools::Message(msg, "Warning");
1290 preference = 0;
1293 this->LanguageToLinkerPreference[l] = preference;
1295 std::string outputExtensionVar = cmStrCat("CMAKE_", l, "_OUTPUT_EXTENSION");
1296 if (cmValue p = mf->GetDefinition(outputExtensionVar)) {
1297 std::string outputExtension = *p;
1298 this->LanguageToOutputExtension[l] = outputExtension;
1299 this->OutputExtensions[outputExtension] = outputExtension;
1300 if (cmHasPrefix(outputExtension, ".")) {
1301 outputExtension = outputExtension.substr(1);
1302 this->OutputExtensions[outputExtension] = outputExtension;
1306 // The map was originally filled by SetLanguageEnabledFlag, but
1307 // since then the compiler- and platform-specific files have been
1308 // loaded which might have added more entries.
1309 this->FillExtensionToLanguageMap(l, mf);
1311 std::string ignoreExtensionsVar =
1312 cmStrCat("CMAKE_", l, "_IGNORE_EXTENSIONS");
1313 std::string ignoreExts = mf->GetSafeDefinition(ignoreExtensionsVar);
1314 cmList extensionList{ ignoreExts };
1315 for (std::string const& i : extensionList) {
1316 this->IgnoreExtensions[i] = true;
1320 void cmGlobalGenerator::FillExtensionToLanguageMap(const std::string& l,
1321 cmMakefile* mf)
1323 std::string extensionsVar = cmStrCat("CMAKE_", l, "_SOURCE_FILE_EXTENSIONS");
1324 const std::string& exts = mf->GetSafeDefinition(extensionsVar);
1325 cmList extensionList{ exts };
1326 for (std::string const& i : extensionList) {
1327 this->ExtensionToLanguage[i] = l;
1331 cmValue cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
1333 assert(!this->Makefiles.empty());
1334 return this->Makefiles[0]->GetDefinition(name);
1337 bool cmGlobalGenerator::GlobalSettingIsOn(std::string const& name) const
1339 assert(!this->Makefiles.empty());
1340 return this->Makefiles[0]->IsOn(name);
1343 std::string cmGlobalGenerator::GetSafeGlobalSetting(
1344 std::string const& name) const
1346 assert(!this->Makefiles.empty());
1347 return this->Makefiles[0]->GetDefinition(name);
1350 bool cmGlobalGenerator::IgnoreFile(const char* ext) const
1352 if (!this->GetLanguageFromExtension(ext).empty()) {
1353 return false;
1355 return (this->IgnoreExtensions.count(ext) > 0);
1358 bool cmGlobalGenerator::GetLanguageEnabled(const std::string& l) const
1360 return this->CMakeInstance->GetState()->GetLanguageEnabled(l);
1363 void cmGlobalGenerator::ClearEnabledLanguages()
1365 this->CMakeInstance->GetState()->ClearEnabledLanguages();
1368 void cmGlobalGenerator::CreateLocalGenerators()
1370 this->LocalGeneratorSearchIndex.clear();
1371 this->LocalGenerators.clear();
1372 this->LocalGenerators.reserve(this->Makefiles.size());
1373 for (const auto& m : this->Makefiles) {
1374 auto lg = this->CreateLocalGenerator(m.get());
1375 this->IndexLocalGenerator(lg.get());
1376 this->LocalGenerators.push_back(std::move(lg));
1380 void cmGlobalGenerator::Configure()
1382 this->FirstTimeProgress = 0.0f;
1383 this->ClearGeneratorMembers();
1384 this->NextDeferId = 0;
1386 cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot();
1388 snapshot.GetDirectory().SetCurrentSource(
1389 this->CMakeInstance->GetHomeDirectory());
1390 snapshot.GetDirectory().SetCurrentBinary(
1391 this->CMakeInstance->GetHomeOutputDirectory());
1393 auto dirMfu = cm::make_unique<cmMakefile>(this, snapshot);
1394 auto* dirMf = dirMfu.get();
1395 this->Makefiles.push_back(std::move(dirMfu));
1396 dirMf->SetRecursionDepth(this->RecursionDepth);
1397 this->IndexMakefile(dirMf);
1399 this->BinaryDirectories.insert(
1400 this->CMakeInstance->GetHomeOutputDirectory());
1402 if (this->ExtraGenerator && !this->CMakeInstance->GetIsInTryCompile()) {
1403 this->CMakeInstance->IssueMessage(
1404 MessageType::DEPRECATION_WARNING,
1405 cmStrCat("Support for \"Extra Generators\" like\n ",
1406 this->ExtraGenerator->GetName(),
1407 "\nis deprecated and will be removed from a future version "
1408 "of CMake. IDEs may use the cmake-file-api(7) to view "
1409 "CMake-generated project build trees."));
1412 // now do it
1413 this->ConfigureDoneCMP0026AndCMP0024 = false;
1414 dirMf->Configure();
1415 dirMf->EnforceDirectoryLevelRules();
1417 this->ConfigureDoneCMP0026AndCMP0024 = true;
1419 // Put a copy of each global target in every directory.
1421 std::vector<GlobalTargetInfo> globalTargets;
1422 this->CreateDefaultGlobalTargets(globalTargets);
1424 for (const auto& mf : this->Makefiles) {
1425 for (GlobalTargetInfo const& globalTarget : globalTargets) {
1426 this->CreateGlobalTarget(globalTarget, mf.get());
1431 this->ReserveGlobalTargetCodegen();
1433 // update the cache entry for the number of local generators, this is used
1434 // for progress
1435 this->GetCMakeInstance()->AddCacheEntry(
1436 "CMAKE_NUMBER_OF_MAKEFILES", std::to_string(this->Makefiles.size()),
1437 "number of local generators", cmStateEnums::INTERNAL);
1440 void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes)
1442 this->CreateLocalGenerators();
1443 // Commit side effects only if we are actually generating
1444 if (this->GetConfigureDoneCMP0026()) {
1445 this->CheckTargetProperties();
1447 this->CreateGeneratorTargets(targetTypes);
1448 if (targetTypes == TargetTypes::AllTargets) {
1449 this->ComputeBuildFileGenerators();
1453 void cmGlobalGenerator::CreateImportedGenerationObjects(
1454 cmMakefile* mf, const std::vector<std::string>& targets,
1455 std::vector<const cmGeneratorTarget*>& exports)
1457 this->CreateGenerationObjects(ImportedOnly);
1458 auto const mfit =
1459 std::find_if(this->Makefiles.begin(), this->Makefiles.end(),
1460 [mf](const std::unique_ptr<cmMakefile>& item) {
1461 return item.get() == mf;
1463 auto& lg =
1464 this->LocalGenerators[std::distance(this->Makefiles.begin(), mfit)];
1465 for (std::string const& t : targets) {
1466 cmGeneratorTarget* gt = lg->FindGeneratorTargetToUse(t);
1467 if (gt) {
1468 exports.push_back(gt);
1473 cmExportBuildFileGenerator* cmGlobalGenerator::GetExportedTargetsFile(
1474 const std::string& filename) const
1476 auto const it = this->BuildExportSets.find(filename);
1477 return it == this->BuildExportSets.end() ? nullptr : it->second;
1480 void cmGlobalGenerator::AddCMP0042WarnTarget(const std::string& target)
1482 this->CMP0042WarnTargets.insert(target);
1485 void cmGlobalGenerator::AddCMP0068WarnTarget(const std::string& target)
1487 this->CMP0068WarnTargets.insert(target);
1490 bool cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const
1492 // If the property is not enabled then okay.
1493 if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(
1494 "ALLOW_DUPLICATE_CUSTOM_TARGETS")) {
1495 return true;
1498 // This generator does not support duplicate custom targets.
1499 std::ostringstream e;
1500 e << "This project has enabled the ALLOW_DUPLICATE_CUSTOM_TARGETS "
1501 << "global property. "
1502 << "The \"" << this->GetName() << "\" generator does not support "
1503 << "duplicate custom targets. "
1504 << "Consider using a Makefiles generator or fix the project to not "
1505 << "use duplicate target names.";
1506 cmSystemTools::Error(e.str());
1507 return false;
1510 void cmGlobalGenerator::ComputeBuildFileGenerators()
1512 for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
1513 std::vector<std::unique_ptr<cmExportBuildFileGenerator>> const& gens =
1514 this->Makefiles[i]->GetExportBuildFileGenerators();
1515 for (std::unique_ptr<cmExportBuildFileGenerator> const& g : gens) {
1516 g->Compute(this->LocalGenerators[i].get());
1521 bool cmGlobalGenerator::UnsupportedVariableIsDefined(const std::string& name,
1522 bool supported) const
1524 if (!supported && this->Makefiles.front()->GetDefinition(name)) {
1525 std::ostringstream e;
1526 /* clang-format off */
1527 e <<
1528 "Generator\n"
1529 " " << this->GetName() << "\n"
1530 "does not support variable\n"
1531 " " << name << "\n"
1532 "but it has been specified."
1534 /* clang-format on */
1535 this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str());
1536 return true;
1539 return false;
1542 bool cmGlobalGenerator::Compute()
1544 // Make sure unsupported variables are not used.
1545 if (this->UnsupportedVariableIsDefined("CMAKE_DEFAULT_BUILD_TYPE",
1546 this->SupportsDefaultBuildType())) {
1547 return false;
1549 if (this->UnsupportedVariableIsDefined("CMAKE_CROSS_CONFIGS",
1550 this->SupportsCrossConfigs())) {
1551 return false;
1553 if (this->UnsupportedVariableIsDefined("CMAKE_DEFAULT_CONFIGS",
1554 this->SupportsDefaultConfigs())) {
1555 return false;
1557 if (!this->InspectConfigTypeVariables()) {
1558 return false;
1561 // Some generators track files replaced during the Generate.
1562 // Start with an empty vector:
1563 this->FilesReplacedDuringGenerate.clear();
1565 // clear targets to issue warning CMP0042 for
1566 this->CMP0042WarnTargets.clear();
1567 // clear targets to issue warning CMP0068 for
1568 this->CMP0068WarnTargets.clear();
1570 // Check whether this generator is allowed to run.
1571 if (!this->CheckALLOW_DUPLICATE_CUSTOM_TARGETS()) {
1572 return false;
1574 this->FinalizeTargetConfiguration();
1576 if (!this->AddBuildDatabaseTargets()) {
1577 return false;
1580 this->CreateGenerationObjects();
1582 // at this point this->LocalGenerators has been filled,
1583 // so create the map from project name to vector of local generators
1584 this->FillProjectMap();
1586 this->CreateFileGenerateOutputs();
1588 // Iterate through all targets and add verification targets for header sets
1589 if (!this->AddHeaderSetVerification()) {
1590 return false;
1593 #ifndef CMAKE_BOOTSTRAP
1594 this->QtAutoGen =
1595 cm::make_unique<cmQtAutoGenGlobalInitializer>(this->LocalGenerators);
1596 if (!this->QtAutoGen->InitializeCustomTargets()) {
1597 return false;
1599 #endif
1601 // Perform up-front computation in order to handle errors (such as unknown
1602 // features) at this point. While processing the compile features we also
1603 // calculate and cache the language standard required by the compile
1604 // features.
1606 // Synthetic targets performed this inside of
1607 // `cmLocalGenerator::DiscoverSyntheticTargets`
1608 for (const auto& localGen : this->LocalGenerators) {
1609 if (!localGen->ComputeTargetCompileFeatures()) {
1610 return false;
1614 // We now have all targets set up and std levels constructed. Add
1615 // `__CMAKE::CXX*` targets as link dependencies to all targets which need
1616 // them.
1618 // Synthetic targets performed this inside of
1619 // `cmLocalGenerator::DiscoverSyntheticTargets`
1620 if (!this->ApplyCXXStdTargets()) {
1621 return false;
1624 // Iterate through all targets and set up C++20 module targets.
1625 // Create target templates for each imported target with C++20 modules.
1626 // INTERFACE library with BMI-generating rules and a collation step?
1627 // Maybe INTERFACE libraries with modules files should just do BMI-only?
1628 // Make `add_dependencies(imported_target
1629 // $<$<TARGET_NAME_IF_EXISTS:uses_imported>:synth1>
1630 // $<$<TARGET_NAME_IF_EXISTS:other_uses_imported>:synth2>)`
1632 // Note that synthetic target creation performs the above marked
1633 // steps on the created targets.
1634 if (!this->DiscoverSyntheticTargets()) {
1635 return false;
1638 // Perform after-generator-target generator actions. These involve collecting
1639 // information gathered during the construction of generator targets.
1640 for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
1641 this->Makefiles[i]->GenerateAfterGeneratorTargets(
1642 *this->LocalGenerators[i]);
1645 // Add generator specific helper commands
1646 for (const auto& localGen : this->LocalGenerators) {
1647 localGen->AddHelperCommands();
1650 // Add automatically generated sources (e.g. unity build).
1651 // Add unity sources after computing compile features. Unity sources do
1652 // not change the set of languages or features, but we need to know them
1653 // to filter out sources that are scanned for C++ module dependencies.
1654 if (!this->AddAutomaticSources()) {
1655 return false;
1658 for (const auto& localGen : this->LocalGenerators) {
1659 cmMakefile* mf = localGen->GetMakefile();
1660 for (const auto& g : mf->GetInstallGenerators()) {
1661 if (!g->Compute(localGen.get())) {
1662 return false;
1667 this->AddExtraIDETargets();
1669 // Trace the dependencies, after that no custom commands should be added
1670 // because their dependencies might not be handled correctly
1671 for (const auto& localGen : this->LocalGenerators) {
1672 localGen->TraceDependencies();
1675 // Make sure that all (non-imported) targets have source files added!
1676 if (this->CheckTargetsForMissingSources()) {
1677 return false;
1680 this->ForceLinkerLanguages();
1682 // Compute the manifest of main targets generated.
1683 for (const auto& localGen : this->LocalGenerators) {
1684 localGen->ComputeTargetManifest();
1687 // Compute the inter-target dependencies.
1688 if (!this->ComputeTargetDepends()) {
1689 return false;
1691 this->ComputeTargetOrder();
1693 if (this->CheckTargetsForType()) {
1694 return false;
1697 if (this->CheckTargetsForPchCompilePdb()) {
1698 return false;
1701 for (const auto& localGen : this->LocalGenerators) {
1702 localGen->ComputeHomeRelativeOutputPath();
1705 return true;
1708 void cmGlobalGenerator::Generate()
1710 // Create a map from local generator to the complete set of targets
1711 // it builds by default.
1712 this->InitializeProgressMarks();
1714 this->ProcessEvaluationFiles();
1716 this->CMakeInstance->UpdateProgress("Generating", 0.1f);
1718 #ifndef CMAKE_BOOTSTRAP
1719 if (!this->QtAutoGen->SetupCustomTargets()) {
1720 if (!cmSystemTools::GetErrorOccurredFlag()) {
1721 this->GetCMakeInstance()->IssueMessage(
1722 MessageType::FATAL_ERROR,
1723 "Problem setting up custom targets for QtAutoGen");
1725 return;
1727 #endif
1729 // Generate project files
1730 for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
1731 this->SetCurrentMakefile(this->LocalGenerators[i]->GetMakefile());
1732 this->LocalGenerators[i]->Generate();
1733 if (!this->LocalGenerators[i]->GetMakefile()->IsOn(
1734 "CMAKE_SKIP_INSTALL_RULES")) {
1735 this->LocalGenerators[i]->GenerateInstallRules();
1737 this->LocalGenerators[i]->GenerateTestFiles();
1738 this->CMakeInstance->UpdateProgress(
1739 "Generating",
1740 0.1f +
1741 0.9f * (static_cast<float>(i) + 1.0f) /
1742 static_cast<float>(this->LocalGenerators.size()));
1744 this->SetCurrentMakefile(nullptr);
1746 if (!this->GenerateCPackPropertiesFile()) {
1747 this->GetCMakeInstance()->IssueMessage(
1748 MessageType::FATAL_ERROR, "Could not write CPack properties file.");
1751 for (auto& buildExpSet : this->BuildExportSets) {
1752 if (!buildExpSet.second->GenerateImportFile()) {
1753 if (!cmSystemTools::GetErrorOccurredFlag()) {
1754 this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
1755 "Could not write export file.");
1757 return;
1760 // Update rule hashes.
1761 this->CheckRuleHashes();
1763 this->WriteSummary();
1765 if (this->ExtraGenerator) {
1766 this->ExtraGenerator->Generate();
1769 // Perform validation checks on memoized link structures.
1770 this->CheckTargetLinkLibraries();
1772 if (!this->CMP0042WarnTargets.empty()) {
1773 std::ostringstream w;
1774 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0042) << "\n";
1775 w << "MACOSX_RPATH is not specified for"
1776 " the following targets:\n";
1777 for (std::string const& t : this->CMP0042WarnTargets) {
1778 w << " " << t << "\n";
1780 this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
1781 w.str());
1784 if (!this->CMP0068WarnTargets.empty()) {
1785 std::ostringstream w;
1786 /* clang-format off */
1787 w <<
1788 cmPolicies::GetPolicyWarning(cmPolicies::CMP0068) << "\n"
1789 "For compatibility with older versions of CMake, the install_name "
1790 "fields for the following targets are still affected by RPATH "
1791 "settings:\n"
1793 /* clang-format on */
1794 for (std::string const& t : this->CMP0068WarnTargets) {
1795 w << " " << t << "\n";
1797 this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
1798 w.str());
1802 #if !defined(CMAKE_BOOTSTRAP)
1803 void cmGlobalGenerator::WriteJsonContent(const std::string& path,
1804 const Json::Value& value) const
1806 cmsys::ofstream ftmp(path.c_str());
1807 this->JsonWriter->write(value, &ftmp);
1808 ftmp << '\n';
1809 ftmp.close();
1812 void cmGlobalGenerator::WriteInstallJson() const
1814 if (this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool(
1815 "INSTALL_PARALLEL")) {
1816 Json::Value index(Json::objectValue);
1817 index["InstallScripts"] = Json::arrayValue;
1818 for (const auto& file : this->InstallScripts) {
1819 index["InstallScripts"].append(file);
1821 this->WriteJsonContent("CMakeFiles/InstallScripts.json", index);
1824 #endif
1826 bool cmGlobalGenerator::ComputeTargetDepends()
1828 cmComputeTargetDepends ctd(this);
1829 if (!ctd.Compute()) {
1830 return false;
1832 for (cmGeneratorTarget const* target : ctd.GetTargets()) {
1833 ctd.GetTargetDirectDepends(target, this->TargetDependencies[target]);
1835 return true;
1838 std::vector<cmGeneratorTarget*>
1839 cmGlobalGenerator::GetLocalGeneratorTargetsInOrder(cmLocalGenerator* lg) const
1841 std::vector<cmGeneratorTarget*> gts;
1842 cm::append(gts, lg->GetGeneratorTargets());
1843 std::sort(gts.begin(), gts.end(),
1844 [this](cmGeneratorTarget const* l, cmGeneratorTarget const* r) {
1845 return this->TargetOrderIndexLess(l, r);
1847 return gts;
1850 void cmGlobalGenerator::ComputeTargetOrder()
1852 size_t index = 0;
1853 auto const& lgens = this->GetLocalGenerators();
1854 for (auto const& lgen : lgens) {
1855 const auto& targets = lgen->GetGeneratorTargets();
1856 for (const auto& gt : targets) {
1857 this->ComputeTargetOrder(gt.get(), index);
1860 assert(index == this->TargetOrderIndex.size());
1863 void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt,
1864 size_t& index)
1866 std::map<cmGeneratorTarget const*, size_t>::value_type value(gt, 0);
1867 auto insertion = this->TargetOrderIndex.insert(value);
1868 if (!insertion.second) {
1869 return;
1871 auto entry = insertion.first;
1873 const auto& deps = this->GetTargetDirectDepends(gt);
1874 for (const auto& d : deps) {
1875 this->ComputeTargetOrder(d, index);
1878 entry->second = index++;
1881 bool cmGlobalGenerator::ApplyCXXStdTargets()
1883 for (auto const& gen : this->LocalGenerators) {
1884 for (auto const& tgt : gen->GetGeneratorTargets()) {
1885 if (!tgt->ApplyCXXStdTargets()) {
1886 return false;
1891 return true;
1894 bool cmGlobalGenerator::DiscoverSyntheticTargets()
1896 cmSyntheticTargetCache cache;
1898 for (auto const& gen : this->LocalGenerators) {
1899 // Because DiscoverSyntheticTargets() adds generator targets, we need to
1900 // cache the existing list of generator targets before starting.
1901 std::vector<cmGeneratorTarget*> genTargets;
1902 genTargets.reserve(gen->GetGeneratorTargets().size());
1903 for (auto const& tgt : gen->GetGeneratorTargets()) {
1904 genTargets.push_back(tgt.get());
1907 for (auto* tgt : genTargets) {
1908 std::vector<std::string> const& configs =
1909 tgt->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
1911 for (auto const& config : configs) {
1912 if (!tgt->DiscoverSyntheticTargets(cache, config)) {
1913 return false;
1919 return true;
1922 bool cmGlobalGenerator::AddHeaderSetVerification()
1924 for (auto const& gen : this->LocalGenerators) {
1925 // Because AddHeaderSetVerification() adds generator targets, we need to
1926 // cache the existing list of generator targets before starting.
1927 std::vector<cmGeneratorTarget*> genTargets;
1928 genTargets.reserve(gen->GetGeneratorTargets().size());
1929 for (auto const& tgt : gen->GetGeneratorTargets()) {
1930 genTargets.push_back(tgt.get());
1933 for (auto* tgt : genTargets) {
1934 if (!tgt->AddHeaderSetVerification()) {
1935 return false;
1940 cmTarget* allVerifyTarget = this->Makefiles.front()->FindTargetToUse(
1941 "all_verify_interface_header_sets", true);
1942 if (allVerifyTarget) {
1943 this->LocalGenerators.front()->AddGeneratorTarget(
1944 cm::make_unique<cmGeneratorTarget>(allVerifyTarget,
1945 this->LocalGenerators.front().get()));
1948 return true;
1951 void cmGlobalGenerator::CreateFileGenerateOutputs()
1953 for (const auto& lg : this->LocalGenerators) {
1954 lg->CreateEvaluationFileOutputs();
1958 bool cmGlobalGenerator::AddAutomaticSources()
1960 for (const auto& lg : this->LocalGenerators) {
1961 for (const auto& gt : lg->GetGeneratorTargets()) {
1962 if (!gt->CanCompileSources()) {
1963 continue;
1965 lg->AddUnityBuild(gt.get());
1966 lg->AddISPCDependencies(gt.get());
1967 // Targets that reuse a PCH are handled below.
1968 if (!gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
1969 lg->AddPchDependencies(gt.get());
1971 lg->AddXCConfigSources(gt.get());
1974 for (const auto& lg : this->LocalGenerators) {
1975 for (const auto& gt : lg->GetGeneratorTargets()) {
1976 if (!gt->CanCompileSources()) {
1977 continue;
1979 // Handle targets that reuse a PCH from an above-handled target.
1980 if (gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
1981 lg->AddPchDependencies(gt.get());
1985 // The above transformations may have changed the classification of sources,
1986 // e.g., sources that go into unity builds become SourceKindUnityBatched.
1987 // Clear the source list and classification cache (KindedSources) of all
1988 // targets so that it will be recomputed correctly by the generators later
1989 // now that the above transformations are done for all targets.
1990 // Also clear the link interface cache to support $<TARGET_OBJECTS:objlib>
1991 // in INTERFACE_LINK_LIBRARIES because the list of object files may have
1992 // been changed by conversion to a unity build or addition of a PCH source.
1993 for (const auto& lg : this->LocalGenerators) {
1994 for (const auto& gt : lg->GetGeneratorTargets()) {
1995 gt->ClearSourcesCache();
1996 gt->ClearLinkInterfaceCache();
1999 return true;
2002 std::unique_ptr<cmLinkLineComputer> cmGlobalGenerator::CreateLinkLineComputer(
2003 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
2005 return cm::make_unique<cmLinkLineComputer>(outputConverter, stateDir);
2008 std::unique_ptr<cmLinkLineComputer>
2009 cmGlobalGenerator::CreateMSVC60LinkLineComputer(
2010 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
2012 return std::unique_ptr<cmLinkLineComputer>(
2013 cm::make_unique<cmMSVC60LinkLineComputer>(outputConverter, stateDir));
2016 void cmGlobalGenerator::FinalizeTargetConfiguration()
2018 std::vector<std::string> const langs =
2019 this->CMakeInstance->GetState()->GetEnabledLanguages();
2021 // Construct per-target generator information.
2022 for (const auto& mf : this->Makefiles) {
2023 const cmBTStringRange noConfigCompileDefinitions =
2024 mf->GetCompileDefinitionsEntries();
2025 cm::optional<std::map<std::string, cmValue>> perConfigCompileDefinitions;
2027 for (auto& target : mf->GetTargets()) {
2028 cmTarget* t = &target.second;
2029 t->FinalizeTargetConfiguration(noConfigCompileDefinitions,
2030 perConfigCompileDefinitions);
2033 // The standard include directories for each language
2034 // should be treated as system include directories.
2035 std::set<std::string> standardIncludesSet;
2036 for (std::string const& li : langs) {
2037 std::string const standardIncludesVar =
2038 cmStrCat("CMAKE_", li, "_STANDARD_INCLUDE_DIRECTORIES");
2039 std::string const& standardIncludesStr =
2040 mf->GetSafeDefinition(standardIncludesVar);
2041 cmList standardIncludesList{ standardIncludesStr };
2042 standardIncludesSet.insert(standardIncludesList.begin(),
2043 standardIncludesList.end());
2045 mf->AddSystemIncludeDirectories(standardIncludesSet);
2049 void cmGlobalGenerator::CreateGeneratorTargets(
2050 TargetTypes targetTypes, cmMakefile* mf, cmLocalGenerator* lg,
2051 std::map<cmTarget*, cmGeneratorTarget*> const& importedMap)
2053 if (targetTypes == AllTargets) {
2054 for (cmTarget* target : mf->GetOrderedTargets()) {
2055 lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(target, lg));
2059 for (cmTarget* t : mf->GetImportedTargets()) {
2060 lg->AddImportedGeneratorTarget(importedMap.find(t)->second);
2064 void cmGlobalGenerator::CreateGeneratorTargets(TargetTypes targetTypes)
2066 std::map<cmTarget*, cmGeneratorTarget*> importedMap;
2067 for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
2068 auto& mf = this->Makefiles[i];
2069 for (const auto& ownedImpTgt : mf->GetOwnedImportedTargets()) {
2070 cmLocalGenerator* lg = this->LocalGenerators[i].get();
2071 auto gt = cm::make_unique<cmGeneratorTarget>(ownedImpTgt.get(), lg);
2072 importedMap[ownedImpTgt.get()] = gt.get();
2073 lg->AddOwnedImportedGeneratorTarget(std::move(gt));
2077 // Construct per-target generator information.
2078 for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
2079 this->CreateGeneratorTargets(targetTypes, this->Makefiles[i].get(),
2080 this->LocalGenerators[i].get(), importedMap);
2084 void cmGlobalGenerator::ClearGeneratorMembers()
2086 this->BuildExportSets.clear();
2088 this->Makefiles.clear();
2090 this->LocalGenerators.clear();
2092 this->AliasTargets.clear();
2093 this->ExportSets.clear();
2094 this->InstallComponents.clear();
2095 this->TargetDependencies.clear();
2096 this->TargetSearchIndex.clear();
2097 this->GeneratorTargetSearchIndex.clear();
2098 this->MakefileSearchIndex.clear();
2099 this->LocalGeneratorSearchIndex.clear();
2100 this->TargetOrderIndex.clear();
2101 this->ProjectMap.clear();
2102 this->RuleHashes.clear();
2103 this->DirectoryContentMap.clear();
2104 this->BinaryDirectories.clear();
2105 this->GeneratedFiles.clear();
2106 this->RuntimeDependencySets.clear();
2107 this->RuntimeDependencySetsByName.clear();
2110 void cmGlobalGenerator::ComputeTargetObjectDirectory(
2111 cmGeneratorTarget* /*unused*/) const
2115 void cmGlobalGenerator::CheckTargetProperties()
2117 // check for link libraries and include directories containing "NOTFOUND"
2118 // and for infinite loops
2119 std::map<std::string, std::string> notFoundMap;
2120 cmState* state = this->GetCMakeInstance()->GetState();
2121 for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
2122 this->Makefiles[i]->Generate(*this->LocalGenerators[i]);
2123 for (auto const& target : this->Makefiles[i]->GetTargets()) {
2124 if (target.second.GetType() == cmStateEnums::INTERFACE_LIBRARY) {
2125 continue;
2127 for (auto const& lib : target.second.GetOriginalLinkLibraries()) {
2128 if (lib.first.size() > 9 && cmIsNOTFOUND(lib.first)) {
2129 std::string varName = lib.first.substr(0, lib.first.size() - 9);
2130 if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
2131 varName += " (ADVANCED)";
2133 std::string text =
2134 cmStrCat(notFoundMap[varName], "\n linked by target \"",
2135 target.second.GetName(), "\" in directory ",
2136 this->Makefiles[i]->GetCurrentSourceDirectory());
2137 notFoundMap[varName] = text;
2140 cmValue incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
2141 if (!incDirProp) {
2142 continue;
2145 std::string incDirs = cmGeneratorExpression::Preprocess(
2146 *incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions);
2148 cmList incs(incDirs);
2150 for (std::string const& incDir : incs) {
2151 if (incDir.size() > 9 && cmIsNOTFOUND(incDir)) {
2152 std::string varName = incDir.substr(0, incDir.size() - 9);
2153 if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
2154 varName += " (ADVANCED)";
2156 std::string text =
2157 cmStrCat(notFoundMap[varName],
2158 "\n used as include directory in directory ",
2159 this->Makefiles[i]->GetCurrentSourceDirectory());
2160 notFoundMap[varName] = text;
2166 if (!notFoundMap.empty()) {
2167 std::string notFoundVars;
2168 for (auto const& notFound : notFoundMap) {
2169 notFoundVars += notFound.first;
2170 notFoundVars += notFound.second;
2171 notFoundVars += '\n';
2173 cmSystemTools::Error(
2174 cmStrCat("The following variables are used in this project, "
2175 "but they are set to NOTFOUND.\n"
2176 "Please set them or make sure they are set and "
2177 "tested correctly in the CMake files:\n",
2178 notFoundVars));
2182 int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir,
2183 const std::string& bindir,
2184 const std::string& projectName,
2185 const std::string& target, bool fast,
2186 std::string& output, cmMakefile* mf)
2188 // if this is not set, then this is a first time configure
2189 // and there is a good chance that the try compile stuff will
2190 // take the bulk of the time, so try and guess some progress
2191 // by getting closer and closer to 100 without actually getting there.
2192 if (!this->CMakeInstance->GetState()->GetInitializedCacheValue(
2193 "CMAKE_NUMBER_OF_MAKEFILES")) {
2194 // If CMAKE_NUMBER_OF_MAKEFILES is not set
2195 // we are in the first time progress and we have no
2196 // idea how long it will be. So, just move 1/10th of the way
2197 // there each time, and don't go over 95%
2198 this->FirstTimeProgress += ((1.0f - this->FirstTimeProgress) / 30.0f);
2199 if (this->FirstTimeProgress > 0.95f) {
2200 this->FirstTimeProgress = 0.95f;
2202 this->CMakeInstance->UpdateProgress("Configuring",
2203 this->FirstTimeProgress);
2206 std::vector<std::string> newTarget = {};
2207 if (!target.empty()) {
2208 newTarget = { target };
2210 std::string config =
2211 mf->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
2212 cmBuildOptions defaultBuildOptions(false, fast, PackageResolveMode::Disable);
2214 std::stringstream ostr;
2215 auto ret =
2216 this->Build(jobs, srcdir, bindir, projectName, newTarget, ostr, "", config,
2217 defaultBuildOptions, true, this->TryCompileTimeout);
2218 output = ostr.str();
2219 return ret;
2222 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
2223 cmGlobalGenerator::GenerateBuildCommand(
2224 const std::string& /*unused*/, const std::string& /*unused*/,
2225 const std::string& /*unused*/, std::vector<std::string> const& /*unused*/,
2226 const std::string& /*unused*/, int /*unused*/, bool /*unused*/,
2227 const cmBuildOptions& /*unused*/, std::vector<std::string> const& /*unused*/)
2229 GeneratedMakeCommand makeCommand;
2230 makeCommand.Add("cmGlobalGenerator::GenerateBuildCommand not implemented");
2231 return { std::move(makeCommand) };
2234 void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/,
2235 int /*jobs*/) const
2237 // Subclasses override this method if they e.g want to give a warning that
2238 // they do not support certain build command line options
2241 int cmGlobalGenerator::Build(
2242 int jobs, const std::string& /*unused*/, const std::string& bindir,
2243 const std::string& projectName, const std::vector<std::string>& targets,
2244 std::ostream& ostr, const std::string& makeCommandCSTR,
2245 const std::string& config, const cmBuildOptions& buildOptions, bool verbose,
2246 cmDuration timeout, cmSystemTools::OutputOption outputflag,
2247 std::vector<std::string> const& nativeOptions)
2249 bool hideconsole = cmSystemTools::GetRunCommandHideConsole();
2252 * Run an executable command and put the stdout in output.
2254 cmWorkingDirectory workdir(bindir);
2255 ostr << "Change Dir: '" << bindir << '\'' << std::endl;
2256 if (workdir.Failed()) {
2257 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2258 std::string err = cmStrCat("Failed to change directory: ",
2259 std::strerror(workdir.GetLastResult()));
2260 cmSystemTools::Error(err);
2261 ostr << err << std::endl;
2262 return 1;
2264 std::string realConfig = config;
2265 if (realConfig.empty()) {
2266 realConfig = this->GetDefaultBuildConfig();
2269 int retVal = 0;
2270 cmSystemTools::SetRunCommandHideConsole(true);
2271 std::string outputBuffer;
2272 std::string* outputPtr = &outputBuffer;
2274 std::vector<GeneratedMakeCommand> makeCommand = this->GenerateBuildCommand(
2275 makeCommandCSTR, projectName, bindir, targets, realConfig, jobs, verbose,
2276 buildOptions, nativeOptions);
2278 // Workaround to convince some commands to produce output.
2279 if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH &&
2280 makeCommand.back().RequiresOutputForward) {
2281 outputflag = cmSystemTools::OUTPUT_FORWARD;
2284 // should we do a clean first?
2285 if (buildOptions.Clean) {
2286 std::vector<GeneratedMakeCommand> cleanCommand =
2287 this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir,
2288 { "clean" }, realConfig, jobs, verbose,
2289 buildOptions);
2290 ostr << "\nRun Clean Command: " << cleanCommand.front().QuotedPrintable()
2291 << std::endl;
2292 if (cleanCommand.size() != 1) {
2293 this->GetCMakeInstance()->IssueMessage(MessageType::INTERNAL_ERROR,
2294 "The generator did not produce "
2295 "exactly one command for the "
2296 "'clean' target");
2297 return 1;
2299 if (!cmSystemTools::RunSingleCommand(cleanCommand.front().PrimaryCommand,
2300 outputPtr, outputPtr, &retVal,
2301 nullptr, outputflag, timeout)) {
2302 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2303 cmSystemTools::Error("Generator: execution of make clean failed.");
2304 ostr << *outputPtr << "\nGenerator: execution of make clean failed."
2305 << std::endl;
2307 return 1;
2309 ostr << *outputPtr;
2312 // now build
2313 std::string makeCommandStr;
2314 std::string outputMakeCommandStr;
2315 bool isWatcomWMake = this->CMakeInstance->GetState()->UseWatcomWMake();
2316 bool needBuildOutput = isWatcomWMake;
2317 std::string buildOutput;
2318 ostr << "\nRun Build Command(s): ";
2320 retVal = 0;
2321 for (auto command = makeCommand.begin();
2322 command != makeCommand.end() && retVal == 0; ++command) {
2323 makeCommandStr = command->Printable();
2324 outputMakeCommandStr = command->QuotedPrintable();
2325 if ((command + 1) != makeCommand.end()) {
2326 makeCommandStr += " && ";
2327 outputMakeCommandStr += " && ";
2330 ostr << outputMakeCommandStr << std::endl;
2331 if (!cmSystemTools::RunSingleCommand(command->PrimaryCommand, outputPtr,
2332 outputPtr, &retVal, nullptr,
2333 outputflag, timeout)) {
2334 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2335 cmSystemTools::Error(
2336 cmStrCat("Generator: execution of make failed. Make command was: ",
2337 makeCommandStr));
2338 ostr << *outputPtr
2339 << "\nGenerator: execution of make failed. Make command was: "
2340 << outputMakeCommandStr << std::endl;
2342 return 1;
2344 ostr << *outputPtr << std::flush;
2345 if (needBuildOutput) {
2346 buildOutput += *outputPtr;
2349 ostr << std::endl;
2350 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2352 // The OpenWatcom tools do not return an error code when a link
2353 // library is not found!
2354 if (isWatcomWMake && retVal == 0 &&
2355 buildOutput.find("W1008: cannot open") != std::string::npos) {
2356 retVal = 1;
2359 return retVal;
2362 bool cmGlobalGenerator::Open(const std::string& bindir,
2363 const std::string& projectName, bool dryRun)
2365 if (this->ExtraGenerator) {
2366 return this->ExtraGenerator->Open(bindir, projectName, dryRun);
2369 return false;
2372 std::string cmGlobalGenerator::GenerateCMakeBuildCommand(
2373 const std::string& target, const std::string& config,
2374 const std::string& parallel, const std::string& native, bool ignoreErrors)
2376 std::string makeCommand = cmSystemTools::GetCMakeCommand();
2377 makeCommand =
2378 cmStrCat(cmSystemTools::ConvertToOutputPath(makeCommand), " --build .");
2379 if (!config.empty()) {
2380 makeCommand += " --config \"";
2381 makeCommand += config;
2382 makeCommand += "\"";
2384 if (!parallel.empty()) {
2385 makeCommand += " --parallel \"";
2386 makeCommand += parallel;
2387 makeCommand += "\"";
2389 if (!target.empty()) {
2390 makeCommand += " --target \"";
2391 makeCommand += target;
2392 makeCommand += "\"";
2394 const char* sep = " -- ";
2395 if (ignoreErrors) {
2396 const char* iflag = this->GetBuildIgnoreErrorsFlag();
2397 if (iflag && *iflag) {
2398 makeCommand += sep;
2399 makeCommand += iflag;
2400 sep = " ";
2403 if (!native.empty()) {
2404 makeCommand += sep;
2405 makeCommand += native;
2407 return makeCommand;
2410 void cmGlobalGenerator::AddMakefile(std::unique_ptr<cmMakefile> mf)
2412 this->IndexMakefile(mf.get());
2413 this->Makefiles.push_back(std::move(mf));
2415 // update progress
2416 // estimate how many lg there will be
2417 cmValue numGenC = this->CMakeInstance->GetState()->GetInitializedCacheValue(
2418 "CMAKE_NUMBER_OF_MAKEFILES");
2420 if (!numGenC) {
2421 // If CMAKE_NUMBER_OF_MAKEFILES is not set
2422 // we are in the first time progress and we have no
2423 // idea how long it will be. So, just move half way
2424 // there each time, and don't go over 95%
2425 this->FirstTimeProgress += ((1.0f - this->FirstTimeProgress) / 30.0f);
2426 if (this->FirstTimeProgress > 0.95f) {
2427 this->FirstTimeProgress = 0.95f;
2429 this->CMakeInstance->UpdateProgress("Configuring",
2430 this->FirstTimeProgress);
2431 return;
2434 int numGen = atoi(numGenC->c_str());
2435 float prog =
2436 static_cast<float>(this->Makefiles.size()) / static_cast<float>(numGen);
2437 if (prog > 1.0f) {
2438 prog = 1.0f;
2440 this->CMakeInstance->UpdateProgress("Configuring", prog);
2443 void cmGlobalGenerator::AddInstallComponent(const std::string& component)
2445 if (!component.empty()) {
2446 this->InstallComponents.insert(component);
2450 void cmGlobalGenerator::MarkAsGeneratedFile(const std::string& filepath)
2452 this->GeneratedFiles.insert(filepath);
2455 bool cmGlobalGenerator::IsGeneratedFile(const std::string& filepath)
2457 return this->GeneratedFiles.find(filepath) != this->GeneratedFiles.end();
2460 void cmGlobalGenerator::EnableInstallTarget()
2462 this->InstallTargetEnabled = true;
2465 std::unique_ptr<cmLocalGenerator> cmGlobalGenerator::CreateLocalGenerator(
2466 cmMakefile* mf)
2468 return cm::make_unique<cmLocalGenerator>(this, mf);
2471 void cmGlobalGenerator::EnableLanguagesFromGenerator(cmGlobalGenerator* gen,
2472 cmMakefile* mf)
2474 this->SetConfiguredFilesPath(gen);
2475 this->TryCompileOuterMakefile = mf;
2476 cmValue make =
2477 gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
2478 this->GetCMakeInstance()->AddCacheEntry(
2479 "CMAKE_MAKE_PROGRAM", make, "make program", cmStateEnums::FILEPATH);
2480 // copy the enabled languages
2481 this->GetCMakeInstance()->GetState()->SetEnabledLanguages(
2482 gen->GetCMakeInstance()->GetState()->GetEnabledLanguages());
2483 this->LanguagesReady = gen->LanguagesReady;
2484 this->ExtensionToLanguage = gen->ExtensionToLanguage;
2485 this->IgnoreExtensions = gen->IgnoreExtensions;
2486 this->LanguageToOutputExtension = gen->LanguageToOutputExtension;
2487 this->LanguageToLinkerPreference = gen->LanguageToLinkerPreference;
2488 this->OutputExtensions = gen->OutputExtensions;
2491 void cmGlobalGenerator::SetConfiguredFilesPath(cmGlobalGenerator* gen)
2493 if (!gen->ConfiguredFilesPath.empty()) {
2494 this->ConfiguredFilesPath = gen->ConfiguredFilesPath;
2495 } else {
2496 this->ConfiguredFilesPath =
2497 cmStrCat(gen->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
2501 bool cmGlobalGenerator::IsExcluded(cmStateSnapshot const& rootSnp,
2502 cmStateSnapshot const& snp_) const
2504 cmStateSnapshot snp = snp_;
2505 while (snp.IsValid()) {
2506 if (snp == rootSnp) {
2507 // No directory excludes itself.
2508 return false;
2511 if (snp.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
2512 // This directory is excluded from its parent.
2513 return true;
2515 snp = snp.GetBuildsystemDirectoryParent();
2517 return false;
2520 bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
2521 cmLocalGenerator* gen) const
2523 assert(gen);
2525 cmStateSnapshot rootSnp = root->GetStateSnapshot();
2526 cmStateSnapshot snp = gen->GetStateSnapshot();
2528 return this->IsExcluded(rootSnp, snp);
2531 bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
2532 const cmGeneratorTarget* target) const
2534 if (!target->IsInBuildSystem()) {
2535 return true;
2537 cmMakefile* mf = root->GetMakefile();
2538 const std::string EXCLUDE_FROM_ALL = "EXCLUDE_FROM_ALL";
2539 if (cmValue exclude = target->GetProperty(EXCLUDE_FROM_ALL)) {
2540 // Expand the property value per configuration.
2541 unsigned int trueCount = 0;
2542 unsigned int falseCount = 0;
2543 const std::vector<std::string>& configs =
2544 mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2545 for (const std::string& config : configs) {
2546 cmGeneratorExpressionInterpreter genexInterpreter(root, config, target);
2547 if (cmIsOn(genexInterpreter.Evaluate(*exclude, EXCLUDE_FROM_ALL))) {
2548 ++trueCount;
2549 } else {
2550 ++falseCount;
2554 // Check whether the genex expansion of the property agrees in all
2555 // configurations.
2556 if (trueCount > 0 && falseCount > 0) {
2557 std::ostringstream e;
2558 e << "The EXCLUDE_FROM_ALL property of target \"" << target->GetName()
2559 << "\" varies by configuration. This is not supported by the \""
2560 << root->GetGlobalGenerator()->GetName() << "\" generator.";
2561 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
2563 return trueCount;
2565 // This target is included in its directory. Check whether the
2566 // directory is excluded.
2567 return this->IsExcluded(root, target->GetLocalGenerator());
2570 void cmGlobalGenerator::GetEnabledLanguages(
2571 std::vector<std::string>& lang) const
2573 lang = this->CMakeInstance->GetState()->GetEnabledLanguages();
2576 int cmGlobalGenerator::GetLinkerPreference(const std::string& lang) const
2578 auto const it = this->LanguageToLinkerPreference.find(lang);
2579 if (it != this->LanguageToLinkerPreference.end()) {
2580 return it->second;
2582 return 0;
2585 void cmGlobalGenerator::FillProjectMap()
2587 this->ProjectMap.clear(); // make sure we start with a clean map
2588 for (const auto& localGen : this->LocalGenerators) {
2589 // for each local generator add all projects
2590 cmStateSnapshot snp = localGen->GetStateSnapshot();
2591 std::string name;
2592 do {
2593 std::string snpProjName = snp.GetProjectName();
2594 if (name != snpProjName) {
2595 name = snpProjName;
2596 this->ProjectMap[name].push_back(localGen.get());
2598 snp = snp.GetBuildsystemDirectoryParent();
2599 } while (snp.IsValid());
2603 cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const
2605 auto const it = this->MakefileSearchIndex.find(start_dir);
2606 if (it != this->MakefileSearchIndex.end()) {
2607 return it->second;
2609 return nullptr;
2612 cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(
2613 cmDirectoryId const& id) const
2615 auto const it = this->LocalGeneratorSearchIndex.find(id.String);
2616 if (it != this->LocalGeneratorSearchIndex.end()) {
2617 return it->second;
2619 return nullptr;
2622 void cmGlobalGenerator::AddAlias(const std::string& name,
2623 std::string const& tgtName)
2625 this->AliasTargets[name] = tgtName;
2628 bool cmGlobalGenerator::IsAlias(const std::string& name) const
2630 return cm::contains(this->AliasTargets, name);
2633 void cmGlobalGenerator::IndexTarget(cmTarget* t)
2635 if (!t->IsImported() || t->IsImportedGloballyVisible()) {
2636 this->TargetSearchIndex[t->GetName()] = t;
2640 void cmGlobalGenerator::IndexGeneratorTarget(cmGeneratorTarget* gt)
2642 if (!gt->IsImported() || gt->IsImportedGloballyVisible()) {
2643 this->GeneratorTargetSearchIndex[gt->GetName()] = gt;
2647 static char const hexDigits[] = "0123456789abcdef";
2649 std::string cmGlobalGenerator::IndexGeneratorTargetUniquely(
2650 cmGeneratorTarget const* gt)
2652 // Use the pointer value to uniquely identify the target instance.
2653 // Use a ":" prefix to avoid conflict with project-defined targets.
2654 // We must satisfy cmGeneratorExpression::IsValidTargetName so use no
2655 // other special characters.
2656 constexpr size_t sizeof_ptr =
2657 sizeof(gt); // NOLINT(bugprone-sizeof-expression)
2658 char buf[1 + sizeof_ptr * 2];
2659 char* b = buf;
2660 *b++ = ':';
2661 for (size_t i = 0; i < sizeof_ptr; ++i) {
2662 unsigned char const c = reinterpret_cast<unsigned char const*>(&gt)[i];
2663 *b++ = hexDigits[(c & 0xf0) >> 4];
2664 *b++ = hexDigits[(c & 0x0f)];
2666 std::string id(buf, sizeof(buf));
2667 // We internally index pointers to non-const generator targets
2668 // but our callers only have pointers to const generator targets.
2669 // They will give up non-const privileges when looking up anyway.
2670 this->GeneratorTargetSearchIndex[id] = const_cast<cmGeneratorTarget*>(gt);
2671 return id;
2674 void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
2676 // We index by both source and binary directory. add_subdirectory
2677 // supports multiple build directories sharing the same source directory.
2678 // The source directory index will reference only the first time it is used.
2679 this->MakefileSearchIndex.insert(
2680 MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf));
2681 this->MakefileSearchIndex.insert(
2682 MakefileMap::value_type(mf->GetCurrentBinaryDirectory(), mf));
2685 void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg)
2687 cmDirectoryId id = lg->GetMakefile()->GetDirectoryId();
2688 this->LocalGeneratorSearchIndex[id.String] = lg;
2691 cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const
2693 auto const it = this->TargetSearchIndex.find(name);
2694 if (it != this->TargetSearchIndex.end()) {
2695 return it->second;
2697 return nullptr;
2700 cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTargetImpl(
2701 std::string const& name) const
2703 auto const it = this->GeneratorTargetSearchIndex.find(name);
2704 if (it != this->GeneratorTargetSearchIndex.end()) {
2705 return it->second;
2707 return nullptr;
2710 cmTarget* cmGlobalGenerator::FindTarget(const std::string& name,
2711 bool excludeAliases) const
2713 if (!excludeAliases) {
2714 auto const ai = this->AliasTargets.find(name);
2715 if (ai != this->AliasTargets.end()) {
2716 return this->FindTargetImpl(ai->second);
2719 return this->FindTargetImpl(name);
2722 cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTarget(
2723 const std::string& name) const
2725 auto const ai = this->AliasTargets.find(name);
2726 if (ai != this->AliasTargets.end()) {
2727 return this->FindGeneratorTargetImpl(ai->second);
2729 return this->FindGeneratorTargetImpl(name);
2732 bool cmGlobalGenerator::NameResolvesToFramework(
2733 const std::string& libname) const
2735 if (cmSystemTools::IsPathToFramework(libname)) {
2736 return true;
2739 if (cmTarget* tgt = this->FindTarget(libname)) {
2740 if (tgt->IsFrameworkOnApple()) {
2741 return true;
2745 return false;
2748 // If the file has no extension it's either a raw executable or might
2749 // be a direct reference to a binary within a framework (bad practice!).
2750 // This is where we change the path to point to the framework directory.
2751 // .tbd files also can be located in SDK frameworks (they are
2752 // placeholders for actual libraries shipped with the OS)
2753 cm::optional<cmGlobalGenerator::FrameworkDescriptor>
2754 cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
2755 FrameworkFormat format) const
2757 // Check for framework structure:
2758 // (/path/to/)?FwName.framework
2759 // or (/path/to/)?FwName.framework/FwName(.tbd)?
2760 // or (/path/to/)?FwName.framework/Versions/*/FwName(.tbd)?
2761 static cmsys::RegularExpression frameworkPath(
2762 "((.+)/)?([^/]+)\\.framework(/Versions/([^/]+))?(/(.+))?$");
2764 auto ext = cmSystemTools::GetFilenameLastExtension(path);
2765 if ((ext.empty() || ext == ".tbd" || ext == ".framework") &&
2766 frameworkPath.find(path)) {
2767 auto name = frameworkPath.match(3);
2768 auto libname =
2769 cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(7));
2770 if (format == FrameworkFormat::Strict && libname.empty()) {
2771 return cm::nullopt;
2773 if (!libname.empty() && !cmHasPrefix(libname, name)) {
2774 return cm::nullopt;
2777 if (libname.empty() || name.size() == libname.size()) {
2778 return FrameworkDescriptor{ frameworkPath.match(2),
2779 frameworkPath.match(5), name };
2782 return FrameworkDescriptor{ frameworkPath.match(2), frameworkPath.match(5),
2783 name, libname.substr(name.size()) };
2786 if (format == FrameworkFormat::Extended) {
2787 // path format can be more flexible: (/path/to/)?fwName(.framework)?
2788 auto fwDir = cmSystemTools::GetParentDirectory(path);
2789 auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework"
2790 ? cmSystemTools::GetFilenameWithoutExtension(path)
2791 : cmSystemTools::GetFilenameName(path);
2793 return FrameworkDescriptor{ fwDir, name };
2796 return cm::nullopt;
2799 static bool RaiseCMP0037Message(cmake* cm, cmTarget* tgt,
2800 std::string const& targetNameAsWritten,
2801 std::string const& reason)
2803 MessageType messageType = MessageType::AUTHOR_WARNING;
2804 std::ostringstream e;
2805 bool issueMessage = false;
2806 switch (tgt->GetPolicyStatusCMP0037()) {
2807 case cmPolicies::WARN:
2808 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n";
2809 issueMessage = true;
2810 CM_FALLTHROUGH;
2811 case cmPolicies::OLD:
2812 break;
2813 case cmPolicies::NEW:
2814 case cmPolicies::REQUIRED_IF_USED:
2815 case cmPolicies::REQUIRED_ALWAYS:
2816 issueMessage = true;
2817 messageType = MessageType::FATAL_ERROR;
2818 break;
2820 if (issueMessage) {
2821 e << "The target name \"" << targetNameAsWritten << "\" is reserved "
2822 << reason << ".";
2823 if (messageType == MessageType::AUTHOR_WARNING) {
2824 e << " It may result in undefined behavior.";
2826 cm->IssueMessage(messageType, e.str(), tgt->GetBacktrace());
2827 if (messageType == MessageType::FATAL_ERROR) {
2828 return false;
2831 return true;
2834 bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
2835 std::string const& reason) const
2837 cmTarget* tgt = this->FindTarget(targetName);
2838 if (!tgt) {
2839 return true;
2841 return RaiseCMP0037Message(this->GetCMakeInstance(), tgt, targetName,
2842 reason);
2845 bool cmGlobalGenerator::CheckCMP0037Prefix(std::string const& targetPrefix,
2846 std::string const& reason) const
2848 bool ret = true;
2849 for (auto const& tgtPair : this->TargetSearchIndex) {
2850 if (cmHasPrefix(tgtPair.first, targetPrefix) &&
2851 !RaiseCMP0037Message(this->GetCMakeInstance(), tgtPair.second,
2852 tgtPair.first, reason)) {
2853 ret = false;
2856 return ret;
2859 void cmGlobalGenerator::CreateDefaultGlobalTargets(
2860 std::vector<GlobalTargetInfo>& targets)
2862 this->AddGlobalTarget_Package(targets);
2863 this->AddGlobalTarget_PackageSource(targets);
2864 this->AddGlobalTarget_Test(targets);
2865 this->AddGlobalTarget_EditCache(targets);
2866 this->AddGlobalTarget_RebuildCache(targets);
2867 this->AddGlobalTarget_Install(targets);
2870 void cmGlobalGenerator::AddGlobalTarget_Package(
2871 std::vector<GlobalTargetInfo>& targets)
2873 auto& mf = this->Makefiles[0];
2874 std::string configFile =
2875 cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackConfig.cmake");
2876 if (!cmSystemTools::FileExists(configFile)) {
2877 return;
2880 static const auto reservedTargets = { "package", "PACKAGE" };
2881 for (auto const& target : reservedTargets) {
2882 if (!this->CheckCMP0037(target, "when CPack packaging is enabled")) {
2883 return;
2887 const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
2888 GlobalTargetInfo gti;
2889 gti.Name = this->GetPackageTargetName();
2890 gti.Message = "Run CPack packaging tool...";
2891 gti.UsesTerminal = true;
2892 gti.WorkingDir = mf->GetCurrentBinaryDirectory();
2893 cmCustomCommandLine singleLine;
2894 singleLine.push_back(cmSystemTools::GetCPackCommand());
2895 if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') {
2896 singleLine.push_back("-C");
2897 singleLine.push_back(cmakeCfgIntDir);
2899 singleLine.push_back("--config");
2900 singleLine.push_back("./CPackConfig.cmake");
2901 gti.CommandLines.push_back(std::move(singleLine));
2902 if (this->GetPreinstallTargetName()) {
2903 gti.Depends.emplace_back(this->GetPreinstallTargetName());
2904 } else {
2905 cmValue noPackageAll =
2906 mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY");
2907 if (noPackageAll.IsOff()) {
2908 gti.Depends.emplace_back(this->GetAllTargetName());
2911 targets.push_back(std::move(gti));
2914 void cmGlobalGenerator::AddGlobalTarget_PackageSource(
2915 std::vector<GlobalTargetInfo>& targets)
2917 const char* packageSourceTargetName = this->GetPackageSourceTargetName();
2918 if (!packageSourceTargetName) {
2919 return;
2922 auto& mf = this->Makefiles[0];
2923 std::string configFile =
2924 cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackSourceConfig.cmake");
2925 if (!cmSystemTools::FileExists(configFile)) {
2926 return;
2929 static const auto reservedTargets = { "package_source" };
2930 for (auto const& target : reservedTargets) {
2931 if (!this->CheckCMP0037(target,
2932 "when CPack source packaging is enabled")) {
2933 return;
2937 GlobalTargetInfo gti;
2938 gti.Name = packageSourceTargetName;
2939 gti.Message = "Run CPack packaging tool for source...";
2940 gti.WorkingDir = mf->GetCurrentBinaryDirectory();
2941 gti.UsesTerminal = true;
2942 cmCustomCommandLine singleLine;
2943 singleLine.push_back(cmSystemTools::GetCPackCommand());
2944 singleLine.push_back("--config");
2945 singleLine.push_back("./CPackSourceConfig.cmake");
2946 singleLine.push_back(std::move(configFile));
2947 gti.CommandLines.push_back(std::move(singleLine));
2948 targets.push_back(std::move(gti));
2951 void cmGlobalGenerator::AddGlobalTarget_Test(
2952 std::vector<GlobalTargetInfo>& targets)
2954 auto& mf = this->Makefiles[0];
2955 if (!mf->IsOn("CMAKE_TESTING_ENABLED")) {
2956 return;
2959 static const auto reservedTargets = { "test", "RUN_TESTS" };
2960 for (auto const& target : reservedTargets) {
2961 if (!this->CheckCMP0037(target, "when CTest testing is enabled")) {
2962 return;
2966 const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
2967 GlobalTargetInfo gti;
2968 gti.Name = this->GetTestTargetName();
2969 gti.Message = "Running tests...";
2970 gti.UsesTerminal = true;
2971 // Unlike the 'install' target, the 'test' target does not depend on 'all'
2972 // by default. Enable it only if CMAKE_SKIP_TEST_ALL_DEPENDENCY is
2973 // explicitly set to OFF.
2974 if (cmValue noall = mf->GetDefinition("CMAKE_SKIP_TEST_ALL_DEPENDENCY")) {
2975 if (noall.IsOff()) {
2976 gti.Depends.emplace_back(this->GetAllTargetName());
2979 cmCustomCommandLine singleLine;
2980 singleLine.push_back(cmSystemTools::GetCTestCommand());
2981 singleLine.push_back("--force-new-ctest-process");
2982 cmList args(mf->GetDefinition("CMAKE_CTEST_ARGUMENTS"));
2983 for (auto const& arg : args) {
2984 singleLine.push_back(arg);
2986 if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') {
2987 singleLine.push_back("-C");
2988 singleLine.push_back(cmakeCfgIntDir);
2989 } else // TODO: This is a hack. Should be something to do with the
2990 // generator
2992 singleLine.push_back("$(ARGS)");
2994 gti.CommandLines.push_back(std::move(singleLine));
2995 targets.push_back(std::move(gti));
2998 void cmGlobalGenerator::ReserveGlobalTargetCodegen()
3000 // Read the policy value at the end of the top-level CMakeLists.txt file
3001 // since it's a global policy that affects the whole project.
3002 auto& mf = this->Makefiles[0];
3003 const auto policyStatus = mf->GetPolicyStatus(cmPolicies::CMP0171);
3005 this->AllowGlobalTargetCodegen = (policyStatus == cmPolicies::NEW);
3007 cmTarget* tgt = this->FindTarget("codegen");
3008 if (!tgt) {
3009 return;
3012 MessageType messageType = MessageType::AUTHOR_WARNING;
3013 std::ostringstream e;
3014 bool issueMessage = false;
3015 switch (policyStatus) {
3016 case cmPolicies::WARN:
3017 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0171) << "\n";
3018 issueMessage = true;
3019 CM_FALLTHROUGH;
3020 case cmPolicies::OLD:
3021 break;
3022 case cmPolicies::NEW:
3023 case cmPolicies::REQUIRED_IF_USED:
3024 case cmPolicies::REQUIRED_ALWAYS:
3025 issueMessage = true;
3026 messageType = MessageType::FATAL_ERROR;
3027 break;
3029 if (issueMessage) {
3030 e << "The target name \"codegen\" is reserved.";
3031 this->GetCMakeInstance()->IssueMessage(messageType, e.str(),
3032 tgt->GetBacktrace());
3033 if (messageType == MessageType::FATAL_ERROR) {
3034 cmSystemTools::SetFatalErrorOccurred();
3035 return;
3040 bool cmGlobalGenerator::CheckCMP0171() const
3042 return this->AllowGlobalTargetCodegen;
3045 void cmGlobalGenerator::AddGlobalTarget_EditCache(
3046 std::vector<GlobalTargetInfo>& targets) const
3048 const char* editCacheTargetName = this->GetEditCacheTargetName();
3049 if (!editCacheTargetName) {
3050 return;
3052 GlobalTargetInfo gti;
3053 gti.Name = editCacheTargetName;
3054 gti.PerConfig = cmTarget::PerConfig::No;
3055 cmCustomCommandLine singleLine;
3057 // Use generator preference for the edit_cache rule if it is defined.
3058 std::string edit_cmd = this->GetEditCacheCommand();
3059 if (!edit_cmd.empty()) {
3060 singleLine.push_back(std::move(edit_cmd));
3061 if (this->GetCMakeInstance()->GetIgnoreWarningAsError()) {
3062 singleLine.push_back("--compile-no-warning-as-error");
3064 singleLine.push_back("-S$(CMAKE_SOURCE_DIR)");
3065 singleLine.push_back("-B$(CMAKE_BINARY_DIR)");
3066 gti.Message = "Running CMake cache editor...";
3067 gti.UsesTerminal = true;
3068 } else {
3069 singleLine.push_back(cmSystemTools::GetCMakeCommand());
3070 singleLine.push_back("-E");
3071 singleLine.push_back("echo");
3072 singleLine.push_back("No interactive CMake dialog available.");
3073 gti.Message = "No interactive CMake dialog available...";
3074 gti.UsesTerminal = false;
3075 gti.StdPipesUTF8 = true;
3077 gti.CommandLines.push_back(std::move(singleLine));
3079 targets.push_back(std::move(gti));
3082 void cmGlobalGenerator::AddGlobalTarget_RebuildCache(
3083 std::vector<GlobalTargetInfo>& targets) const
3085 const char* rebuildCacheTargetName = this->GetRebuildCacheTargetName();
3086 if (!rebuildCacheTargetName) {
3087 return;
3089 GlobalTargetInfo gti;
3090 gti.Name = rebuildCacheTargetName;
3091 gti.Message = "Running CMake to regenerate build system...";
3092 gti.UsesTerminal = true;
3093 gti.PerConfig = cmTarget::PerConfig::No;
3094 cmCustomCommandLine singleLine;
3095 singleLine.push_back(cmSystemTools::GetCMakeCommand());
3096 singleLine.push_back("--regenerate-during-build");
3097 if (this->GetCMakeInstance()->GetIgnoreWarningAsError()) {
3098 singleLine.push_back("--compile-no-warning-as-error");
3100 singleLine.push_back("-S$(CMAKE_SOURCE_DIR)");
3101 singleLine.push_back("-B$(CMAKE_BINARY_DIR)");
3102 gti.CommandLines.push_back(std::move(singleLine));
3103 gti.StdPipesUTF8 = true;
3104 targets.push_back(std::move(gti));
3107 void cmGlobalGenerator::AddGlobalTarget_Install(
3108 std::vector<GlobalTargetInfo>& targets)
3110 auto& mf = this->Makefiles[0];
3111 const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
3112 bool skipInstallRules = mf->IsOn("CMAKE_SKIP_INSTALL_RULES");
3113 if (this->InstallTargetEnabled && skipInstallRules) {
3114 this->CMakeInstance->IssueMessage(
3115 MessageType::WARNING,
3116 "CMAKE_SKIP_INSTALL_RULES was enabled even though "
3117 "installation rules have been specified",
3118 mf->GetBacktrace());
3119 } else if (this->InstallTargetEnabled && !skipInstallRules) {
3120 if (!(cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.')) {
3121 std::set<std::string>* componentsSet = &this->InstallComponents;
3122 std::ostringstream ostr;
3123 if (!componentsSet->empty()) {
3124 ostr << "Available install components are: ";
3125 ostr << cmWrap('"', *componentsSet, '"', " ");
3126 } else {
3127 ostr << "Only default component available";
3129 GlobalTargetInfo gti;
3130 gti.Name = "list_install_components";
3131 gti.Message = ostr.str();
3132 gti.UsesTerminal = false;
3133 targets.push_back(std::move(gti));
3135 std::string cmd = cmSystemTools::GetCMakeCommand();
3136 GlobalTargetInfo gti;
3137 gti.Name = this->GetInstallTargetName();
3138 gti.Message = "Install the project...";
3139 gti.UsesTerminal = true;
3140 gti.StdPipesUTF8 = true;
3141 cmCustomCommandLine singleLine;
3142 if (this->GetPreinstallTargetName()) {
3143 gti.Depends.emplace_back(this->GetPreinstallTargetName());
3144 } else {
3145 cmValue noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
3146 if (noall.IsOff()) {
3147 gti.Depends.emplace_back(this->GetAllTargetName());
3150 if (mf->GetDefinition("CMake_BINARY_DIR") &&
3151 !mf->IsOn("CMAKE_CROSSCOMPILING")) {
3152 // We are building CMake itself. We cannot use the original
3153 // executable to install over itself. The generator will
3154 // automatically convert this name to the build-time location.
3155 cmd = "cmake";
3157 singleLine.push_back(cmd);
3158 if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') {
3159 std::string cfgArg = "-DBUILD_TYPE=";
3160 bool useEPN = this->UseEffectivePlatformName(mf.get());
3161 if (useEPN) {
3162 cfgArg += "$(CONFIGURATION)";
3163 singleLine.push_back(cfgArg);
3164 cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)";
3165 } else {
3166 cfgArg += this->GetCMakeCFGIntDir();
3168 singleLine.push_back(cfgArg);
3170 singleLine.push_back("-P");
3171 singleLine.push_back("cmake_install.cmake");
3172 gti.CommandLines.push_back(singleLine);
3173 targets.push_back(gti);
3175 // install_local
3176 if (const char* install_local = this->GetInstallLocalTargetName()) {
3177 gti.Name = install_local;
3178 gti.Message = "Installing only the local directory...";
3179 gti.UsesTerminal =
3180 !this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool(
3181 "INSTALL_PARALLEL");
3182 gti.CommandLines.clear();
3184 cmCustomCommandLine localCmdLine = singleLine;
3186 localCmdLine.insert(localCmdLine.begin() + 1,
3187 "-DCMAKE_INSTALL_LOCAL_ONLY=1");
3189 gti.CommandLines.push_back(std::move(localCmdLine));
3190 targets.push_back(gti);
3193 // install_strip
3194 const char* install_strip = this->GetInstallStripTargetName();
3195 if (install_strip && mf->IsSet("CMAKE_STRIP")) {
3196 gti.Name = install_strip;
3197 gti.Message = "Installing the project stripped...";
3198 gti.UsesTerminal = true;
3199 gti.CommandLines.clear();
3201 cmCustomCommandLine stripCmdLine = singleLine;
3203 stripCmdLine.insert(stripCmdLine.begin() + 1,
3204 "-DCMAKE_INSTALL_DO_STRIP=1");
3205 gti.CommandLines.push_back(std::move(stripCmdLine));
3206 targets.push_back(gti);
3211 class ModuleCompilationDatabaseCommandAction
3213 public:
3214 ModuleCompilationDatabaseCommandAction(
3215 std::string output, std::function<std::vector<std::string>()> inputs)
3216 : Output(std::move(output))
3217 , Inputs(std::move(inputs))
3220 void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3221 std::unique_ptr<cmCustomCommand> cc);
3223 private:
3224 std::string const Output;
3225 std::function<std::vector<std::string>()> const Inputs;
3228 void ModuleCompilationDatabaseCommandAction::operator()(
3229 cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3230 std::unique_ptr<cmCustomCommand> cc)
3232 auto inputs = this->Inputs();
3234 cmCustomCommandLines command_lines;
3235 cmCustomCommandLine command_line;
3237 command_line.emplace_back(cmSystemTools::GetCMakeCommand());
3238 command_line.emplace_back("-E");
3239 command_line.emplace_back("cmake_module_compile_db");
3240 command_line.emplace_back("merge");
3241 command_line.emplace_back("-o");
3242 command_line.emplace_back(this->Output);
3243 for (auto const& input : inputs) {
3244 command_line.emplace_back(input);
3247 command_lines.emplace_back(std::move(command_line));
3249 cc->SetBacktrace(lfbt);
3250 cc->SetCommandLines(command_lines);
3251 cc->SetWorkingDirectory(lg.GetBinaryDirectory().c_str());
3252 cc->SetDependsExplicitOnly(true);
3253 cc->SetOutputs(this->Output);
3254 if (!inputs.empty()) {
3255 cc->SetMainDependency(inputs[0]);
3257 cc->SetDepends(inputs);
3258 detail::AddCustomCommandToOutput(lg, cmCommandOrigin::Generator,
3259 std::move(cc), false);
3262 class ModuleCompilationDatabaseTargetAction
3264 public:
3265 ModuleCompilationDatabaseTargetAction(std::string output, cmTarget* target)
3266 : Output(std::move(output))
3267 , Target(target)
3270 void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3271 std::unique_ptr<cmCustomCommand> cc);
3273 private:
3274 std::string const Output;
3275 cmTarget* const Target;
3278 void ModuleCompilationDatabaseTargetAction::operator()(
3279 cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3280 std::unique_ptr<cmCustomCommand> cc)
3282 cc->SetBacktrace(lfbt);
3283 cc->SetWorkingDirectory(lg.GetBinaryDirectory().c_str());
3284 std::vector<std::string> target_inputs;
3285 target_inputs.emplace_back(this->Output);
3286 cc->SetDepends(target_inputs);
3287 detail::AddUtilityCommand(lg, cmCommandOrigin::Generator, this->Target,
3288 std::move(cc));
3291 void cmGlobalGenerator::AddBuildDatabaseFile(std::string const& lang,
3292 std::string const& config,
3293 std::string const& path)
3295 if (!config.empty()) {
3296 this->PerConfigModuleDbs[config][lang].push_back(path);
3298 this->PerLanguageModuleDbs[lang].push_back(path);
3301 bool cmGlobalGenerator::AddBuildDatabaseTargets()
3303 auto& mf = this->Makefiles[0];
3304 if (!mf->IsOn("CMAKE_EXPORT_BUILD_DATABASE")) {
3305 return true;
3307 if (!cmExperimental::HasSupportEnabled(
3308 *mf.get(), cmExperimental::Feature::ExportBuildDatabase)) {
3309 return {};
3312 static const auto reservedTargets = { "cmake_build_database" };
3313 for (auto const& target : reservedTargets) {
3314 if (!this->CheckCMP0037(target,
3315 "when exporting build databases are enabled")) {
3316 return false;
3319 static const auto reservedPrefixes = { "cmake_build_database-" };
3320 for (auto const& prefix : reservedPrefixes) {
3321 if (!this->CheckCMP0037Prefix(
3322 prefix, "when exporting build databases are enabled")) {
3323 return false;
3327 if (!this->SupportsBuildDatabase()) {
3328 return true;
3331 auto configs = mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
3333 static cm::static_string_view TargetPrefix = "cmake_build_database"_s;
3334 auto AddMergeTarget =
3335 [&mf](std::string const& name, const char* comment,
3336 std::string const& output,
3337 std::function<std::vector<std::string>()> inputs) {
3338 // Add the custom command.
3340 ModuleCompilationDatabaseCommandAction action{ output,
3341 std::move(inputs) };
3342 auto cc = cm::make_unique<cmCustomCommand>();
3343 cc->SetComment(comment);
3344 mf->AddGeneratorAction(
3345 std::move(cc), action,
3346 cmMakefile::GeneratorActionWhen::AfterGeneratorTargets);
3349 // Add a custom target with the given name.
3351 cmTarget* target = mf->AddNewUtilityTarget(name, true);
3352 ModuleCompilationDatabaseTargetAction action{ output, target };
3353 auto cc = cm::make_unique<cmCustomCommand>();
3354 mf->AddGeneratorAction(std::move(cc), action);
3358 std::string module_languages[] = { "CXX" };
3360 // Add per-configuration targets.
3361 for (auto const& config : configs) {
3362 // Add per-language targets.
3363 std::vector<std::string> all_config_paths;
3364 for (auto const& lang : module_languages) {
3365 auto comment = cmStrCat("Combining module command databases for ", lang,
3366 " and ", config);
3367 auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_",
3368 lang, '_', config, ".json");
3369 mf->GetOrCreateGeneratedSource(output);
3370 AddMergeTarget(cmStrCat(TargetPrefix, '-', lang, '-', config),
3371 comment.c_str(), output, [this, config, lang]() {
3372 return this->PerConfigModuleDbs[config][lang];
3374 all_config_paths.emplace_back(std::move(output));
3377 // Add the overall target.
3378 auto comment = cmStrCat("Combining module command databases for ", config);
3379 auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_",
3380 config, ".json");
3381 mf->GetOrCreateGeneratedSource(output);
3382 AddMergeTarget(cmStrCat(TargetPrefix, '-', config), comment.c_str(),
3383 output, [all_config_paths]() { return all_config_paths; });
3386 // NMC considerations
3387 // Add per-language targets.
3388 std::vector<std::string> all_config_paths;
3389 for (auto const& lang : module_languages) {
3390 auto comment = cmStrCat("Combining module command databases for ", lang);
3391 auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_",
3392 lang, ".json");
3393 mf->GetOrCreateGeneratedSource(output);
3394 AddMergeTarget(
3395 cmStrCat(TargetPrefix, '-', lang), comment.c_str(), output,
3396 [this, lang]() { return this->PerLanguageModuleDbs[lang]; });
3397 all_config_paths.emplace_back(std::move(output));
3400 // Add the overall target.
3401 auto const* comment = "Combining all module command databases";
3402 auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database.json");
3403 mf->GetOrCreateGeneratedSource(output);
3404 AddMergeTarget(std::string(TargetPrefix), comment, output,
3405 [all_config_paths]() { return all_config_paths; });
3407 return true;
3410 std::string cmGlobalGenerator::GetPredefinedTargetsFolder() const
3412 cmValue prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
3413 "PREDEFINED_TARGETS_FOLDER");
3415 if (prop) {
3416 return *prop;
3419 return "CMakePredefinedTargets";
3422 bool cmGlobalGenerator::UseFolderProperty() const
3424 const cmValue prop =
3425 this->GetCMakeInstance()->GetState()->GetGlobalProperty("USE_FOLDERS");
3427 // If this property is defined, let the setter turn this on or off.
3428 if (prop) {
3429 return prop.IsOn();
3432 // If CMP0143 is NEW `treat` "USE_FOLDERS" as ON. Otherwise `treat` it as OFF
3433 assert(!this->Makefiles.empty());
3434 return (this->Makefiles[0]->GetPolicyStatus(cmPolicies::CMP0143) ==
3435 cmPolicies::NEW);
3438 void cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti,
3439 cmMakefile* mf)
3441 // Package
3442 auto tb =
3443 mf->CreateNewTarget(gti.Name, cmStateEnums::GLOBAL_TARGET, gti.PerConfig);
3445 // Do nothing if gti.Name is already used
3446 if (!tb.second) {
3447 return;
3450 cmTarget& target = tb.first;
3451 target.SetProperty("EXCLUDE_FROM_ALL", "TRUE");
3453 // Store the custom command in the target.
3454 cmCustomCommand cc;
3455 cc.SetCommandLines(gti.CommandLines);
3456 cc.SetWorkingDirectory(gti.WorkingDir.c_str());
3457 cc.SetStdPipesUTF8(gti.StdPipesUTF8);
3458 cc.SetUsesTerminal(gti.UsesTerminal);
3459 target.AddPostBuildCommand(std::move(cc));
3460 if (!gti.Message.empty()) {
3461 target.SetProperty("EchoString", gti.Message);
3463 for (std::string const& d : gti.Depends) {
3464 target.AddUtility(d, false);
3467 // Organize in the "predefined targets" folder:
3469 if (this->UseFolderProperty()) {
3470 target.SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
3474 std::string cmGlobalGenerator::GenerateRuleFile(
3475 std::string const& output) const
3477 std::string ruleFile = cmStrCat(output, ".rule");
3478 const char* dir = this->GetCMakeCFGIntDir();
3479 if (dir && dir[0] == '$') {
3480 cmSystemTools::ReplaceString(ruleFile, dir, "/CMakeFiles");
3482 return ruleFile;
3485 bool cmGlobalGenerator::ShouldStripResourcePath(cmMakefile* mf) const
3487 return mf->PlatformIsAppleEmbedded();
3490 std::string cmGlobalGenerator::GetSharedLibFlagsForLanguage(
3491 std::string const& l) const
3493 auto const it = this->LanguageToOriginalSharedLibFlags.find(l);
3494 if (it != this->LanguageToOriginalSharedLibFlags.end()) {
3495 return it->second;
3497 return "";
3500 void cmGlobalGenerator::AppendDirectoryForConfig(const std::string& /*unused*/,
3501 const std::string& /*unused*/,
3502 const std::string& /*unused*/,
3503 std::string& /*unused*/)
3505 // Subclasses that support multiple configurations should implement
3506 // this method to append the subdirectory for the given build
3507 // configuration.
3510 cmGlobalGenerator::TargetDependSet const&
3511 cmGlobalGenerator::GetTargetDirectDepends(cmGeneratorTarget const* target)
3513 return this->TargetDependencies[target];
3516 bool cmGlobalGenerator::TargetOrderIndexLess(cmGeneratorTarget const* l,
3517 cmGeneratorTarget const* r) const
3519 return this->TargetOrderIndex.at(l) < this->TargetOrderIndex.at(r);
3522 bool cmGlobalGenerator::IsReservedTarget(std::string const& name)
3524 // The following is a list of targets reserved
3525 // by one or more of the cmake generators.
3527 // Adding additional targets to this list will require a policy!
3528 static const cm::static_string_view reservedTargets[] = {
3529 "all"_s, "ALL_BUILD"_s, "help"_s, "install"_s,
3530 "INSTALL"_s, "preinstall"_s, "clean"_s, "edit_cache"_s,
3531 "rebuild_cache"_s, "ZERO_CHECK"_s
3534 return cm::contains(reservedTargets, name);
3537 void cmGlobalGenerator::SetExternalMakefileProjectGenerator(
3538 std::unique_ptr<cmExternalMakefileProjectGenerator> extraGenerator)
3540 this->ExtraGenerator = std::move(extraGenerator);
3541 if (this->ExtraGenerator) {
3542 this->ExtraGenerator->SetGlobalGenerator(this);
3546 std::string cmGlobalGenerator::GetExtraGeneratorName() const
3548 return this->ExtraGenerator ? this->ExtraGenerator->GetName()
3549 : std::string();
3552 void cmGlobalGenerator::FileReplacedDuringGenerate(const std::string& filename)
3554 this->FilesReplacedDuringGenerate.push_back(filename);
3557 void cmGlobalGenerator::GetFilesReplacedDuringGenerate(
3558 std::vector<std::string>& filenames)
3560 filenames.clear();
3561 std::copy(this->FilesReplacedDuringGenerate.begin(),
3562 this->FilesReplacedDuringGenerate.end(),
3563 std::back_inserter(filenames));
3566 void cmGlobalGenerator::GetTargetSets(
3567 TargetDependSet& projectTargets, TargetDependSet& originalTargets,
3568 cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
3570 // loop over all local generators
3571 for (auto* generator : generators) {
3572 // check to make sure generator is not excluded
3573 if (this->IsExcluded(root, generator)) {
3574 continue;
3576 // loop over all the generator targets in the makefile
3577 for (const auto& target : generator->GetGeneratorTargets()) {
3578 if (this->IsRootOnlyTarget(target.get()) &&
3579 target->GetLocalGenerator() != root) {
3580 continue;
3582 // put the target in the set of original targets
3583 originalTargets.insert(target.get());
3584 // Get the set of targets that depend on target
3585 this->AddTargetDepends(target.get(), projectTargets);
3590 bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const
3592 return (target->GetType() == cmStateEnums::GLOBAL_TARGET ||
3593 target->GetName() == this->GetAllTargetName());
3596 void cmGlobalGenerator::AddTargetDepends(cmGeneratorTarget const* target,
3597 TargetDependSet& projectTargets)
3599 // add the target itself
3600 if (projectTargets.insert(target).second) {
3601 // This is the first time we have encountered the target.
3602 // Recursively follow its dependencies.
3603 for (auto const& t : this->GetTargetDirectDepends(target)) {
3604 this->AddTargetDepends(t, projectTargets);
3609 void cmGlobalGenerator::AddToManifest(std::string const& f)
3611 // Add to the content listing for the file's directory.
3612 std::string dir = cmSystemTools::GetFilenamePath(f);
3613 std::string file = cmSystemTools::GetFilenameName(f);
3614 DirectoryContent& dc = this->DirectoryContentMap[dir];
3615 dc.Generated.insert(file);
3616 dc.All.insert(file);
3619 std::set<std::string> const& cmGlobalGenerator::GetDirectoryContent(
3620 std::string const& dir, bool needDisk)
3622 DirectoryContent& dc = this->DirectoryContentMap[dir];
3623 if (needDisk) {
3624 long mt = cmSystemTools::ModifiedTime(dir);
3625 if (mt != dc.LastDiskTime) {
3626 // Reset to non-loaded directory content.
3627 dc.All = dc.Generated;
3629 // Load the directory content from disk.
3630 cmsys::Directory d;
3631 if (d.Load(dir)) {
3632 unsigned long n = d.GetNumberOfFiles();
3633 for (unsigned long i = 0; i < n; ++i) {
3634 const char* f = d.GetFile(i);
3635 if (strcmp(f, ".") != 0 && strcmp(f, "..") != 0) {
3636 dc.All.insert(f);
3640 dc.LastDiskTime = mt;
3643 return dc.All;
3646 void cmGlobalGenerator::AddRuleHash(const std::vector<std::string>& outputs,
3647 std::string const& content)
3649 // Ignore if there are no outputs.
3650 if (outputs.empty()) {
3651 return;
3654 // Compute a hash of the rule.
3655 RuleHash hash;
3657 cmCryptoHash md5(cmCryptoHash::AlgoMD5);
3658 std::string const md5_hex = md5.HashString(content);
3659 memcpy(hash.Data, md5_hex.c_str(), 32);
3662 // Shorten the output name (in expected use case).
3663 std::string fname =
3664 this->LocalGenerators[0]->MaybeRelativeToTopBinDir(outputs[0]);
3666 // Associate the hash with this output.
3667 this->RuleHashes[fname] = hash;
3670 void cmGlobalGenerator::CheckRuleHashes()
3672 std::string home = this->GetCMakeInstance()->GetHomeOutputDirectory();
3673 std::string pfile = cmStrCat(home, "/CMakeFiles/CMakeRuleHashes.txt");
3674 this->CheckRuleHashes(pfile, home);
3675 this->WriteRuleHashes(pfile);
3678 void cmGlobalGenerator::CheckRuleHashes(std::string const& pfile,
3679 std::string const& home)
3681 #if defined(_WIN32) || defined(__CYGWIN__)
3682 cmsys::ifstream fin(pfile.c_str(), std::ios::in | std::ios::binary);
3683 #else
3684 cmsys::ifstream fin(pfile.c_str());
3685 #endif
3686 if (!fin) {
3687 return;
3689 std::string line;
3690 std::string fname;
3691 while (cmSystemTools::GetLineFromStream(fin, line)) {
3692 // Line format is a 32-byte hex string followed by a space
3693 // followed by a file name (with no escaping).
3695 // Skip blank and comment lines.
3696 if (line.size() < 34 || line[0] == '#') {
3697 continue;
3700 // Get the filename.
3701 fname = line.substr(33);
3703 // Look for a hash for this file's rule.
3704 auto const rhi = this->RuleHashes.find(fname);
3705 if (rhi != this->RuleHashes.end()) {
3706 // Compare the rule hash in the file to that we were given.
3707 if (strncmp(line.c_str(), rhi->second.Data, 32) != 0) {
3708 // The rule has changed. Delete the output so it will be
3709 // built again.
3710 fname = cmSystemTools::CollapseFullPath(fname, home);
3711 cmSystemTools::RemoveFile(fname);
3713 } else {
3714 // We have no hash for a rule previously listed. This may be a
3715 // case where a user has turned off a build option and might
3716 // want to turn it back on later, so do not delete the file.
3717 // Instead, we keep the rule hash as long as the file exists so
3718 // that if the feature is turned back on and the rule has
3719 // changed the file is still rebuilt.
3720 std::string fpath = cmSystemTools::CollapseFullPath(fname, home);
3721 if (cmSystemTools::FileExists(fpath)) {
3722 RuleHash hash;
3723 memcpy(hash.Data, line.c_str(), 32);
3724 this->RuleHashes[fname] = hash;
3730 void cmGlobalGenerator::WriteRuleHashes(std::string const& pfile)
3732 // Now generate a new persistence file with the current hashes.
3733 if (this->RuleHashes.empty()) {
3734 cmSystemTools::RemoveFile(pfile);
3735 } else {
3736 cmGeneratedFileStream fout(pfile);
3737 fout << "# Hashes of file build rules.\n";
3738 for (auto const& rh : this->RuleHashes) {
3739 fout.write(rh.second.Data, 32);
3740 fout << " " << rh.first << "\n";
3745 void cmGlobalGenerator::WriteSummary()
3747 // Record all target directories in a central location.
3748 std::string fname = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
3749 "/CMakeFiles/TargetDirectories.txt");
3750 cmGeneratedFileStream fout(fname);
3752 for (const auto& lg : this->LocalGenerators) {
3753 for (const auto& tgt : lg->GetGeneratorTargets()) {
3754 if (!tgt->IsInBuildSystem()) {
3755 continue;
3757 this->WriteSummary(tgt.get());
3758 fout << tgt->GetSupportDirectory() << "\n";
3763 void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target)
3765 // Place the labels file in a per-target support directory.
3766 std::string dir = target->GetSupportDirectory();
3767 std::string file = cmStrCat(dir, "/Labels.txt");
3768 std::string json_file = cmStrCat(dir, "/Labels.json");
3770 #ifndef CMAKE_BOOTSTRAP
3771 // Check whether labels are enabled for this target.
3772 cmValue targetLabels = target->GetProperty("LABELS");
3773 cmValue directoryLabels =
3774 target->Target->GetMakefile()->GetProperty("LABELS");
3775 cmValue cmakeDirectoryLabels =
3776 target->Target->GetMakefile()->GetDefinition("CMAKE_DIRECTORY_LABELS");
3777 if (targetLabels || directoryLabels || cmakeDirectoryLabels) {
3778 Json::Value lj_root(Json::objectValue);
3779 Json::Value& lj_target = lj_root["target"] = Json::objectValue;
3780 lj_target["name"] = target->GetName();
3781 Json::Value& lj_target_labels = lj_target["labels"] = Json::arrayValue;
3782 Json::Value& lj_sources = lj_root["sources"] = Json::arrayValue;
3784 cmSystemTools::MakeDirectory(dir);
3785 cmGeneratedFileStream fout(file);
3787 cmList labels;
3789 // List the target-wide labels. All sources in the target get
3790 // these labels.
3791 if (targetLabels) {
3792 labels.assign(*targetLabels);
3793 if (!labels.empty()) {
3794 fout << "# Target labels\n";
3795 for (std::string const& l : labels) {
3796 fout << " " << l << "\n";
3797 lj_target_labels.append(l);
3802 // List directory labels
3803 cmList directoryLabelsList;
3804 cmList cmakeDirectoryLabelsList;
3806 if (directoryLabels) {
3807 directoryLabelsList.assign(*directoryLabels);
3810 if (cmakeDirectoryLabels) {
3811 cmakeDirectoryLabelsList.assign(*cmakeDirectoryLabels);
3814 if (!directoryLabelsList.empty() || !cmakeDirectoryLabelsList.empty()) {
3815 fout << "# Directory labels\n";
3818 for (auto const& li : directoryLabelsList) {
3819 fout << " " << li << "\n";
3820 lj_target_labels.append(li);
3823 for (auto const& li : cmakeDirectoryLabelsList) {
3824 fout << " " << li << "\n";
3825 lj_target_labels.append(li);
3828 // List the source files with any per-source labels.
3829 fout << "# Source files and their labels\n";
3830 std::vector<cmSourceFile*> sources;
3831 std::vector<std::string> const& configs =
3832 target->Target->GetMakefile()->GetGeneratorConfigs(
3833 cmMakefile::IncludeEmptyConfig);
3834 for (std::string const& c : configs) {
3835 target->GetSourceFiles(sources, c);
3837 auto const sourcesEnd = cmRemoveDuplicates(sources);
3838 for (cmSourceFile* sf : cmMakeRange(sources.cbegin(), sourcesEnd)) {
3839 Json::Value& lj_source = lj_sources.append(Json::objectValue);
3840 std::string const& sfp = sf->ResolveFullPath();
3841 fout << sfp << "\n";
3842 lj_source["file"] = sfp;
3843 if (cmValue svalue = sf->GetProperty("LABELS")) {
3844 Json::Value& lj_source_labels = lj_source["labels"] = Json::arrayValue;
3845 labels.assign(*svalue);
3846 for (auto const& label : labels) {
3847 fout << " " << label << "\n";
3848 lj_source_labels.append(label);
3852 cmGeneratedFileStream json_fout(json_file);
3853 json_fout << lj_root;
3854 } else
3855 #endif
3857 cmSystemTools::RemoveFile(file);
3858 cmSystemTools::RemoveFile(json_file);
3862 // static
3863 std::string cmGlobalGenerator::EscapeJSON(const std::string& s)
3865 std::string result;
3866 result.reserve(s.size());
3867 for (char i : s) {
3868 switch (i) {
3869 case '"':
3870 case '\\':
3871 result += '\\';
3872 result += i;
3873 break;
3874 case '\n':
3875 result += "\\n";
3876 break;
3877 case '\t':
3878 result += "\\t";
3879 break;
3880 default:
3881 result += i;
3884 return result;
3887 void cmGlobalGenerator::SetFilenameTargetDepends(
3888 cmSourceFile* sf, std::set<cmGeneratorTarget const*> const& tgts)
3890 this->FilenameTargetDepends[sf] = tgts;
3893 std::set<cmGeneratorTarget const*> const&
3894 cmGlobalGenerator::GetFilenameTargetDepends(cmSourceFile* sf) const
3896 return this->FilenameTargetDepends[sf];
3899 const std::string& cmGlobalGenerator::GetRealPath(const std::string& dir)
3901 auto i = this->RealPaths.lower_bound(dir);
3902 if (i == this->RealPaths.end() ||
3903 this->RealPaths.key_comp()(dir, i->first)) {
3904 i = this->RealPaths.emplace_hint(i, dir, cmSystemTools::GetRealPath(dir));
3906 return i->second;
3909 std::string cmGlobalGenerator::NewDeferId()
3911 return cmStrCat("__"_s, std::to_string(this->NextDeferId++));
3914 void cmGlobalGenerator::ProcessEvaluationFiles()
3916 std::vector<std::string> generatedFiles;
3917 for (auto& localGen : this->LocalGenerators) {
3918 localGen->ProcessEvaluationFiles(generatedFiles);
3922 std::string cmGlobalGenerator::ExpandCFGIntDir(
3923 const std::string& str, const std::string& /*config*/) const
3925 return str;
3928 bool cmGlobalGenerator::GenerateCPackPropertiesFile()
3930 cmake::InstalledFilesMap const& installedFiles =
3931 this->CMakeInstance->GetInstalledFiles();
3933 const auto& lg = this->LocalGenerators[0];
3934 cmMakefile* mf = lg->GetMakefile();
3936 std::vector<std::string> configs =
3937 mf->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig);
3938 std::string config = mf->GetDefaultConfiguration();
3940 std::string path = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
3941 "/CPackProperties.cmake");
3943 if (!cmSystemTools::FileExists(path) && installedFiles.empty()) {
3944 return true;
3947 cmGeneratedFileStream file(path);
3948 file << "# CPack properties\n";
3950 for (auto const& i : installedFiles) {
3951 cmInstalledFile const& installedFile = i.second;
3953 cmCPackPropertiesGenerator cpackPropertiesGenerator(
3954 lg.get(), installedFile, configs);
3956 cpackPropertiesGenerator.Generate(file, config, configs);
3959 return true;
3962 cmInstallRuntimeDependencySet*
3963 cmGlobalGenerator::CreateAnonymousRuntimeDependencySet()
3965 auto set = cm::make_unique<cmInstallRuntimeDependencySet>();
3966 auto* retval = set.get();
3967 this->RuntimeDependencySets.push_back(std::move(set));
3968 return retval;
3971 cmInstallRuntimeDependencySet* cmGlobalGenerator::GetNamedRuntimeDependencySet(
3972 const std::string& name)
3974 auto it = this->RuntimeDependencySetsByName.find(name);
3975 if (it == this->RuntimeDependencySetsByName.end()) {
3976 auto set = cm::make_unique<cmInstallRuntimeDependencySet>(name);
3977 it =
3978 this->RuntimeDependencySetsByName.insert(std::make_pair(name, set.get()))
3979 .first;
3980 this->RuntimeDependencySets.push_back(std::move(set));
3982 return it->second;
3985 cmGlobalGenerator::StripCommandStyle cmGlobalGenerator::GetStripCommandStyle(
3986 std::string const& strip)
3988 #ifdef __APPLE__
3989 auto i = this->StripCommandStyleMap.find(strip);
3990 if (i == this->StripCommandStyleMap.end()) {
3991 StripCommandStyle style = StripCommandStyle::Default;
3993 // Try running strip tool with Apple-specific options.
3994 std::vector<std::string> cmd{ strip, "-u", "-r" };
3995 std::string out;
3996 std::string err;
3997 int ret;
3998 if (cmSystemTools::RunSingleCommand(cmd, &out, &err, &ret, nullptr,
3999 cmSystemTools::OUTPUT_NONE) &&
4000 // Check for Apple-specific output.
4001 ret != 0 && cmHasLiteralPrefix(err, "fatal error: /") &&
4002 err.find("/usr/bin/strip: no files specified") != std::string::npos) {
4003 style = StripCommandStyle::Apple;
4005 i = this->StripCommandStyleMap.emplace(strip, style).first;
4007 return i->second;
4008 #else
4009 static_cast<void>(strip);
4010 return StripCommandStyle::Default;
4011 #endif
4014 void cmGlobalGenerator::AddInstallScript(std::string const& file)
4016 this->InstallScripts.push_back(file);