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"
4
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 <utility>
15
16 #include <cm/memory>
17 #include <cmext/algorithm>
18 #include <cmext/string_view>
19
20 #include "cmsys/Directory.hxx"
21 #include "cmsys/FStream.hxx"
22
23 #if defined(_WIN32) && !defined(__CYGWIN__)
24 # include <windows.h>
25 #endif
26
27 #include "cmAlgorithms.h"
28 #include "cmCPackPropertiesGenerator.h"
29 #include "cmComputeTargetDepends.h"
30 #include "cmCustomCommand.h"
31 #include "cmCustomCommandLines.h"
32 #include "cmDuration.h"
33 #include "cmExportBuildFileGenerator.h"
34 #include "cmExternalMakefileProjectGenerator.h"
35 #include "cmGeneratedFileStream.h"
36 #include "cmGeneratorExpression.h"
37 #include "cmGeneratorTarget.h"
38 #include "cmInstallGenerator.h"
39 #include "cmInstallRuntimeDependencySet.h"
40 #include "cmLinkLineComputer.h"
41 #include "cmListFileCache.h"
42 #include "cmLocalGenerator.h"
43 #include "cmMSVC60LinkLineComputer.h"
44 #include "cmMakefile.h"
45 #include "cmMessageType.h"
46 #include "cmPolicies.h"
47 #include "cmRange.h"
48 #include "cmSourceFile.h"
49 #include "cmState.h"
50 #include "cmStateDirectory.h"
51 #include "cmStateTypes.h"
52 #include "cmValue.h"
53 #include "cmVersion.h"
54 #include "cmWorkingDirectory.h"
55 #include "cmake.h"
56
57 #if !defined(CMAKE_BOOTSTRAP)
58 # include <cm3p/json/value.h>
59 # include <cm3p/json/writer.h>
60
61 # include "cmCryptoHash.h"
62 # include "cmQtAutoGenGlobalInitializer.h"
63 #endif
64
65 #if defined(_MSC_VER) && _MSC_VER >= 1800
66 # define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
67 #endif
68
69 const std::string kCMAKE_PLATFORM_INFO_INITIALIZED =
70 "CMAKE_PLATFORM_INFO_INITIALIZED";
71
72 class cmInstalledFile;
73
operator ()(cmTarget const * t1,cmTarget const * t2) const74 bool cmTarget::StrictTargetComparison::operator()(cmTarget const* t1,
75 cmTarget const* t2) const
76 {
77 int nameResult = strcmp(t1->GetName().c_str(), t2->GetName().c_str());
78 if (nameResult == 0) {
79 return strcmp(t1->GetMakefile()->GetCurrentBinaryDirectory().c_str(),
80 t2->GetMakefile()->GetCurrentBinaryDirectory().c_str()) < 0;
81 }
82 return nameResult < 0;
83 }
84
cmGlobalGenerator(cmake * cm)85 cmGlobalGenerator::cmGlobalGenerator(cmake* cm)
86 : CMakeInstance(cm)
87 {
88 // By default the .SYMBOLIC dependency is not needed on symbolic rules.
89 this->NeedSymbolicMark = false;
90
91 // by default use the native paths
92 this->ForceUnixPaths = false;
93
94 // By default do not try to support color.
95 this->ToolSupportsColor = false;
96
97 // By default do not use link scripts.
98 this->UseLinkScript = false;
99
100 // Whether an install target is needed.
101 this->InstallTargetEnabled = false;
102
103 // how long to let try compiles run
104 this->TryCompileTimeout = cmDuration::zero();
105
106 this->CurrentConfigureMakefile = nullptr;
107 this->TryCompileOuterMakefile = nullptr;
108
109 this->ConfigureDoneCMP0026AndCMP0024 = false;
110 this->FirstTimeProgress = 0.0f;
111
112 this->RecursionDepth = 0;
113
114 cm->GetState()->SetIsGeneratorMultiConfig(false);
115 cm->GetState()->SetMinGWMake(false);
116 cm->GetState()->SetMSYSShell(false);
117 cm->GetState()->SetNMake(false);
118 cm->GetState()->SetWatcomWMake(false);
119 cm->GetState()->SetWindowsShell(false);
120 cm->GetState()->SetWindowsVSIDE(false);
121 }
122
~cmGlobalGenerator()123 cmGlobalGenerator::~cmGlobalGenerator()
124 {
125 this->ClearGeneratorMembers();
126 }
127
128 #if !defined(CMAKE_BOOTSTRAP)
GetJson() const129 Json::Value cmGlobalGenerator::GetJson() const
130 {
131 Json::Value generator = Json::objectValue;
132 generator["name"] = this->GetName();
133 generator["multiConfig"] = this->IsMultiConfig();
134 return generator;
135 }
136 #endif
137
SetGeneratorInstance(std::string const & i,cmMakefile * mf)138 bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i,
139 cmMakefile* mf)
140 {
141 if (i.empty()) {
142 return true;
143 }
144
145 std::ostringstream e;
146 /* clang-format off */
147 e <<
148 "Generator\n"
149 " " << this->GetName() << "\n"
150 "does not support instance specification, but instance\n"
151 " " << i << "\n"
152 "was specified.";
153 /* clang-format on */
154 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
155 return false;
156 }
157
SetGeneratorPlatform(std::string const & p,cmMakefile * mf)158 bool cmGlobalGenerator::SetGeneratorPlatform(std::string const& p,
159 cmMakefile* mf)
160 {
161 if (p.empty()) {
162 return true;
163 }
164
165 std::ostringstream e;
166 /* clang-format off */
167 e <<
168 "Generator\n"
169 " " << this->GetName() << "\n"
170 "does not support platform specification, but platform\n"
171 " " << p << "\n"
172 "was specified.";
173 /* clang-format on */
174 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
175 return false;
176 }
177
SetGeneratorToolset(std::string const & ts,bool,cmMakefile * mf)178 bool cmGlobalGenerator::SetGeneratorToolset(std::string const& ts, bool,
179 cmMakefile* mf)
180 {
181 if (ts.empty()) {
182 return true;
183 }
184 std::ostringstream e;
185 /* clang-format off */
186 e <<
187 "Generator\n"
188 " " << this->GetName() << "\n"
189 "does not support toolset specification, but toolset\n"
190 " " << ts << "\n"
191 "was specified.";
192 /* clang-format on */
193 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
194 return false;
195 }
196
SelectMakeProgram(const std::string & inMakeProgram,const std::string & makeDefault) const197 std::string cmGlobalGenerator::SelectMakeProgram(
198 const std::string& inMakeProgram, const std::string& makeDefault) const
199 {
200 std::string makeProgram = inMakeProgram;
201 if (cmIsOff(makeProgram)) {
202 cmValue makeProgramCSTR =
203 this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
204 if (cmIsOff(makeProgramCSTR)) {
205 makeProgram = makeDefault;
206 } else {
207 makeProgram = *makeProgramCSTR;
208 }
209 if (cmIsOff(makeProgram) && !makeProgram.empty()) {
210 makeProgram = "CMAKE_MAKE_PROGRAM-NOTFOUND";
211 }
212 }
213 return makeProgram;
214 }
215
ResolveLanguageCompiler(const std::string & lang,cmMakefile * mf,bool optional) const216 void cmGlobalGenerator::ResolveLanguageCompiler(const std::string& lang,
217 cmMakefile* mf,
218 bool optional) const
219 {
220 std::string langComp = cmStrCat("CMAKE_", lang, "_COMPILER");
221
222 if (!mf->GetDefinition(langComp)) {
223 if (!optional) {
224 cmSystemTools::Error(langComp + " not set, after EnableLanguage");
225 }
226 return;
227 }
228 std::string const& name = mf->GetRequiredDefinition(langComp);
229 std::string path;
230 if (!cmSystemTools::FileIsFullPath(name)) {
231 path = cmSystemTools::FindProgram(name);
232 } else {
233 path = name;
234 }
235 if (!optional && (path.empty() || !cmSystemTools::FileExists(path))) {
236 return;
237 }
238 cmValue cname =
239 this->GetCMakeInstance()->GetState()->GetInitializedCacheValue(langComp);
240
241 // Split compiler from arguments
242 std::vector<std::string> cnameArgVec;
243 if (cname && !cname->empty()) {
244 cmExpandList(*cname, cnameArgVec);
245 cname = cmValue(cnameArgVec.front());
246 }
247
248 std::string changeVars;
249 if (cname && !optional) {
250 std::string cnameString;
251 if (!cmSystemTools::FileIsFullPath(*cname)) {
252 cnameString = cmSystemTools::FindProgram(*cname);
253 } else {
254 cnameString = *cname;
255 }
256 std::string pathString = path;
257 // get rid of potentially multiple slashes:
258 cmSystemTools::ConvertToUnixSlashes(cnameString);
259 cmSystemTools::ConvertToUnixSlashes(pathString);
260 if (cnameString != pathString) {
261 cmValue cvars = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
262 "__CMAKE_DELETE_CACHE_CHANGE_VARS_");
263 if (cvars) {
264 changeVars += *cvars;
265 changeVars += ";";
266 }
267 changeVars += langComp;
268 changeVars += ";";
269 changeVars += *cname;
270 this->GetCMakeInstance()->GetState()->SetGlobalProperty(
271 "__CMAKE_DELETE_CACHE_CHANGE_VARS_", changeVars.c_str());
272 }
273 }
274 }
275
AddBuildExportSet(cmExportBuildFileGenerator * gen)276 void cmGlobalGenerator::AddBuildExportSet(cmExportBuildFileGenerator* gen)
277 {
278 this->BuildExportSets[gen->GetMainExportFileName()] = gen;
279 }
280
AddBuildExportExportSet(cmExportBuildFileGenerator * gen)281 void cmGlobalGenerator::AddBuildExportExportSet(
282 cmExportBuildFileGenerator* gen)
283 {
284 this->BuildExportExportSets[gen->GetMainExportFileName()] = gen;
285 this->AddBuildExportSet(gen);
286 }
287
GenerateImportFile(const std::string & file)288 bool cmGlobalGenerator::GenerateImportFile(const std::string& file)
289 {
290 auto const it = this->BuildExportSets.find(file);
291 if (it != this->BuildExportSets.end()) {
292 bool result = it->second->GenerateImportFile();
293
294 if (!this->ConfigureDoneCMP0026AndCMP0024) {
295 for (const auto& m : this->Makefiles) {
296 m->RemoveExportBuildFileGeneratorCMP0024(it->second);
297 }
298 }
299
300 this->BuildExportSets.erase(it);
301 return result;
302 }
303 return false;
304 }
305
ForceLinkerLanguages()306 void cmGlobalGenerator::ForceLinkerLanguages()
307 {
308 }
309
CheckTargetsForMissingSources() const310 bool cmGlobalGenerator::CheckTargetsForMissingSources() const
311 {
312 bool failed = false;
313 for (const auto& localGen : this->LocalGenerators) {
314 for (const auto& target : localGen->GetGeneratorTargets()) {
315 if (!target->CanCompileSources() ||
316 cmIsOn(target->GetProperty("ghs_integrity_app"))) {
317 continue;
318 }
319
320 if (target->GetAllConfigSources().empty()) {
321 std::ostringstream e;
322 e << "No SOURCES given to target: " << target->GetName();
323 this->GetCMakeInstance()->IssueMessage(
324 MessageType::FATAL_ERROR, e.str(), target->GetBacktrace());
325 failed = true;
326 }
327 }
328 }
329 return failed;
330 }
331
CheckTargetsForType() const332 bool cmGlobalGenerator::CheckTargetsForType() const
333 {
334 if (!this->GetLanguageEnabled("Swift")) {
335 return false;
336 }
337 bool failed = false;
338 for (const auto& generator : this->LocalGenerators) {
339 for (const auto& target : generator->GetGeneratorTargets()) {
340 if (target->GetType() == cmStateEnums::EXECUTABLE) {
341 std::vector<std::string> const& configs =
342 target->Makefile->GetGeneratorConfigs(
343 cmMakefile::IncludeEmptyConfig);
344 for (std::string const& config : configs) {
345 if (target->IsWin32Executable(config) &&
346 target->GetLinkerLanguage(config) == "Swift") {
347 this->GetCMakeInstance()->IssueMessage(
348 MessageType::FATAL_ERROR,
349 "WIN32_EXECUTABLE property is not supported on Swift "
350 "executables",
351 target->GetBacktrace());
352 failed = true;
353 }
354 }
355 }
356 }
357 }
358 return failed;
359 }
360
CheckTargetsForPchCompilePdb() const361 bool cmGlobalGenerator::CheckTargetsForPchCompilePdb() const
362 {
363 if (!this->GetLanguageEnabled("C") && !this->GetLanguageEnabled("CXX")) {
364 return false;
365 }
366 bool failed = false;
367 for (const auto& generator : this->LocalGenerators) {
368 for (const auto& target : generator->GetGeneratorTargets()) {
369 if (!target->CanCompileSources() ||
370 cmIsOn(target->GetProperty("ghs_integrity_app"))) {
371 continue;
372 }
373
374 std::string const& reuseFrom =
375 target->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM");
376 std::string const& compilePdb =
377 target->GetSafeProperty("COMPILE_PDB_NAME");
378
379 if (!reuseFrom.empty() && reuseFrom != compilePdb) {
380 const std::string e = cmStrCat(
381 "PRECOMPILE_HEADERS_REUSE_FROM property is set on target (\"",
382 target->GetName(),
383 "\"). Reusable precompile headers requires the COMPILE_PDB_NAME"
384 " property to have the value \"",
385 reuseFrom, "\"\n");
386 this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
387 target->GetBacktrace());
388 failed = true;
389 }
390 }
391 }
392 return failed;
393 }
394
IsExportedTargetsFile(const std::string & filename) const395 bool cmGlobalGenerator::IsExportedTargetsFile(
396 const std::string& filename) const
397 {
398 auto const it = this->BuildExportSets.find(filename);
399 if (it == this->BuildExportSets.end()) {
400 return false;
401 }
402 return !cm::contains(this->BuildExportExportSets, filename);
403 }
404
405 // Find the make program for the generator, required for try compiles
FindMakeProgram(cmMakefile * mf)406 bool cmGlobalGenerator::FindMakeProgram(cmMakefile* mf)
407 {
408 if (this->FindMakeProgramFile.empty()) {
409 cmSystemTools::Error(
410 "Generator implementation error, "
411 "all generators must specify this->FindMakeProgramFile");
412 return false;
413 }
414 if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
415 std::string setMakeProgram = mf->GetModulesFile(this->FindMakeProgramFile);
416 if (!setMakeProgram.empty()) {
417 mf->ReadListFile(setMakeProgram);
418 }
419 }
420 if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
421 std::ostringstream err;
422 err << "CMake was unable to find a build program corresponding to \""
423 << this->GetName() << "\". CMAKE_MAKE_PROGRAM is not set. You "
424 << "probably need to select a different build tool.";
425 cmSystemTools::Error(err.str());
426 cmSystemTools::SetFatalErrorOccured();
427 return false;
428 }
429 std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
430 // if there are spaces in the make program use short path
431 // but do not short path the actual program name, as
432 // this can cause trouble with VSExpress
433 if (makeProgram.find(' ') != std::string::npos) {
434 std::string dir;
435 std::string file;
436 cmSystemTools::SplitProgramPath(makeProgram, dir, file);
437 std::string saveFile = file;
438 cmSystemTools::GetShortPath(makeProgram, makeProgram);
439 cmSystemTools::SplitProgramPath(makeProgram, dir, file);
440 makeProgram = cmStrCat(dir, '/', saveFile);
441 mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", makeProgram, "make program",
442 cmStateEnums::FILEPATH);
443 }
444 return true;
445 }
446
CheckLanguages(std::vector<std::string> const &,cmMakefile *) const447 bool cmGlobalGenerator::CheckLanguages(
448 std::vector<std::string> const& /* languages */, cmMakefile* /* mf */) const
449 {
450 return true;
451 }
452
453 // enable the given language
454 //
455 // The following files are loaded in this order:
456 //
457 // First figure out what OS we are running on:
458 //
459 // CMakeSystem.cmake - configured file created by CMakeDetermineSystem.cmake
460 // CMakeDetermineSystem.cmake - figure out os info and create
461 // CMakeSystem.cmake IF CMAKE_SYSTEM
462 // not set
463 // CMakeSystem.cmake - configured file created by
464 // CMakeDetermineSystem.cmake IF CMAKE_SYSTEM_LOADED
465
466 // CMakeSystemSpecificInitialize.cmake
467 // - includes Platform/${CMAKE_SYSTEM_NAME}-Initialize.cmake
468
469 // Next try and enable all languages found in the languages vector
470 //
471 // FOREACH LANG in languages
472 // CMake(LANG)Compiler.cmake - configured file create by
473 // CMakeDetermine(LANG)Compiler.cmake
474 // CMakeDetermine(LANG)Compiler.cmake - Finds compiler for LANG and
475 // creates CMake(LANG)Compiler.cmake
476 // CMake(LANG)Compiler.cmake - configured file created by
477 // CMakeDetermine(LANG)Compiler.cmake
478 //
479 // CMakeSystemSpecificInformation.cmake
480 // - includes Platform/${CMAKE_SYSTEM_NAME}.cmake
481 // may use compiler stuff
482
483 // FOREACH LANG in languages
484 // CMake(LANG)Information.cmake
485 // - loads Platform/${CMAKE_SYSTEM_NAME}-${COMPILER}.cmake
486 // CMakeTest(LANG)Compiler.cmake
487 // - Make sure the compiler works with a try compile if
488 // CMakeDetermine(LANG) was loaded
489 //
490 // Now load a few files that can override values set in any of the above
491 // (PROJECTNAME)Compatibility.cmake
492 // - load any backwards compatibility stuff for current project
493 // ${CMAKE_USER_MAKE_RULES_OVERRIDE}
494 // - allow users a chance to override system variables
495 //
496 //
497
EnableLanguage(std::vector<std::string> const & languages,cmMakefile * mf,bool optional)498 void cmGlobalGenerator::EnableLanguage(
499 std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
500 {
501 if (!this->IsMultiConfig() &&
502 !this->GetCMakeInstance()->GetIsInTryCompile()) {
503 std::string envBuildType;
504 if (!mf->GetDefinition("CMAKE_BUILD_TYPE") &&
505 cmSystemTools::GetEnv("CMAKE_BUILD_TYPE", envBuildType)) {
506 mf->AddCacheDefinition(
507 "CMAKE_BUILD_TYPE", envBuildType,
508 "Choose the type of build. Options include: empty, "
509 "Debug, Release, RelWithDebInfo, MinSizeRel.",
510 cmStateEnums::STRING);
511 }
512 }
513
514 if (languages.empty()) {
515 cmSystemTools::Error("EnableLanguage must have a lang specified!");
516 cmSystemTools::SetFatalErrorOccured();
517 return;
518 }
519
520 std::set<std::string> cur_languages(languages.begin(), languages.end());
521 for (std::string const& li : cur_languages) {
522 if (!this->LanguagesInProgress.insert(li).second) {
523 std::ostringstream e;
524 e << "Language '" << li
525 << "' is currently being enabled. "
526 "Recursive call not allowed.";
527 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
528 cmSystemTools::SetFatalErrorOccured();
529 return;
530 }
531 }
532
533 if (this->TryCompileOuterMakefile) {
534 // In a try-compile we can only enable languages provided by caller.
535 for (std::string const& lang : languages) {
536 if (lang == "NONE") {
537 this->SetLanguageEnabled("NONE", mf);
538 } else {
539 if (!cm::contains(this->LanguagesReady, lang)) {
540 std::ostringstream e;
541 e << "The test project needs language " << lang
542 << " which is not enabled.";
543 this->TryCompileOuterMakefile->IssueMessage(MessageType::FATAL_ERROR,
544 e.str());
545 cmSystemTools::SetFatalErrorOccured();
546 return;
547 }
548 }
549 }
550 }
551
552 bool fatalError = false;
553
554 mf->AddDefinitionBool("RUN_CONFIGURE", true);
555 std::string rootBin =
556 cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
557
558 // If the configuration files path has been set,
559 // then we are in a try compile and need to copy the enable language
560 // files from the parent cmake bin dir, into the try compile bin dir
561 if (!this->ConfiguredFilesPath.empty()) {
562 rootBin = this->ConfiguredFilesPath;
563 }
564 rootBin += '/';
565 rootBin += cmVersion::GetCMakeVersion();
566
567 // set the dir for parent files so they can be used by modules
568 mf->AddDefinition("CMAKE_PLATFORM_INFO_DIR", rootBin);
569
570 if (!this->CMakeInstance->GetIsInTryCompile()) {
571 // Keep a mark in the cache to indicate that we've initialized the
572 // platform information directory. If the platform information
573 // directory exists but the mark is missing then CMakeCache.txt
574 // has been removed or replaced without also removing the CMakeFiles/
575 // directory. In this case remove the platform information directory
576 // so that it will be re-initialized and the relevant information
577 // restored in the cache.
578 if (cmSystemTools::FileIsDirectory(rootBin) &&
579 !mf->IsOn(kCMAKE_PLATFORM_INFO_INITIALIZED)) {
580 cmSystemTools::RemoveADirectory(rootBin);
581 }
582 this->GetCMakeInstance()->AddCacheEntry(
583 kCMAKE_PLATFORM_INFO_INITIALIZED, "1",
584 "Platform information initialized", cmStateEnums::INTERNAL);
585 }
586
587 // try and load the CMakeSystem.cmake if it is there
588 std::string fpath = rootBin;
589 bool const readCMakeSystem = !mf->GetDefinition("CMAKE_SYSTEM_LOADED");
590 if (readCMakeSystem) {
591 fpath += "/CMakeSystem.cmake";
592 if (cmSystemTools::FileExists(fpath)) {
593 mf->ReadListFile(fpath);
594 }
595 }
596
597 // Load the CMakeDetermineSystem.cmake file and find out
598 // what platform we are running on
599 if (!mf->GetDefinition("CMAKE_SYSTEM")) {
600 #if defined(_WIN32) && !defined(__CYGWIN__)
601 /* Windows version number data. */
602 OSVERSIONINFOEXW osviex;
603 ZeroMemory(&osviex, sizeof(osviex));
604 osviex.dwOSVersionInfoSize = sizeof(osviex);
605
606 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
607 # pragma warning(push)
608 # ifdef __INTEL_COMPILER
609 # pragma warning(disable : 1478)
610 # elif defined __clang__
611 # pragma clang diagnostic push
612 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
613 # else
614 # pragma warning(disable : 4996)
615 # endif
616 # endif
617 GetVersionExW((OSVERSIONINFOW*)&osviex);
618 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
619 # ifdef __clang__
620 # pragma clang diagnostic pop
621 # else
622 # pragma warning(pop)
623 # endif
624 # endif
625 std::ostringstream windowsVersionString;
626 windowsVersionString << osviex.dwMajorVersion << "."
627 << osviex.dwMinorVersion << "."
628 << osviex.dwBuildNumber;
629 mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str());
630 #endif
631 // Read the DetermineSystem file
632 std::string systemFile = mf->GetModulesFile("CMakeDetermineSystem.cmake");
633 mf->ReadListFile(systemFile);
634 // load the CMakeSystem.cmake from the binary directory
635 // this file is configured by the CMakeDetermineSystem.cmake file
636 fpath = cmStrCat(rootBin, "/CMakeSystem.cmake");
637 mf->ReadListFile(fpath);
638 }
639
640 if (readCMakeSystem) {
641 // Tell the generator about the instance, if any.
642 std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
643 if (!this->SetGeneratorInstance(instance, mf)) {
644 cmSystemTools::SetFatalErrorOccured();
645 return;
646 }
647
648 // Tell the generator about the target system.
649 std::string system = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
650 if (!this->SetSystemName(system, mf)) {
651 cmSystemTools::SetFatalErrorOccured();
652 return;
653 }
654
655 // Tell the generator about the platform, if any.
656 std::string platform = mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM");
657 if (!this->SetGeneratorPlatform(platform, mf)) {
658 cmSystemTools::SetFatalErrorOccured();
659 return;
660 }
661
662 // Tell the generator about the toolset, if any.
663 std::string toolset = mf->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET");
664 if (!this->SetGeneratorToolset(toolset, false, mf)) {
665 cmSystemTools::SetFatalErrorOccured();
666 return;
667 }
668
669 // Find the native build tool for this generator.
670 if (!this->FindMakeProgram(mf)) {
671 return;
672 }
673 }
674
675 // Check that the languages are supported by the generator and its
676 // native build tool found above.
677 if (!this->CheckLanguages(languages, mf)) {
678 return;
679 }
680
681 // **** Load the system specific initialization if not yet loaded
682 if (!mf->GetDefinition("CMAKE_SYSTEM_SPECIFIC_INITIALIZE_LOADED")) {
683 fpath = mf->GetModulesFile("CMakeSystemSpecificInitialize.cmake");
684 if (!mf->ReadListFile(fpath)) {
685 cmSystemTools::Error("Could not find cmake module file: "
686 "CMakeSystemSpecificInitialize.cmake");
687 }
688 }
689
690 std::map<std::string, bool> needTestLanguage;
691 std::map<std::string, bool> needSetLanguageEnabledMaps;
692 // foreach language
693 // load the CMakeDetermine(LANG)Compiler.cmake file to find
694 // the compiler
695
696 for (std::string const& lang : languages) {
697 needSetLanguageEnabledMaps[lang] = false;
698 if (lang == "NONE") {
699 this->SetLanguageEnabled("NONE", mf);
700 continue;
701 }
702 std::string loadedLang = cmStrCat("CMAKE_", lang, "_COMPILER_LOADED");
703 if (!mf->GetDefinition(loadedLang)) {
704 fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
705
706 // If the existing build tree was already configured with this
707 // version of CMake then try to load the configured file first
708 // to avoid duplicate compiler tests.
709 if (cmSystemTools::FileExists(fpath)) {
710 if (!mf->ReadListFile(fpath)) {
711 cmSystemTools::Error("Could not find cmake module file: " + fpath);
712 }
713 // if this file was found then the language was already determined
714 // to be working
715 needTestLanguage[lang] = false;
716 this->SetLanguageEnabledFlag(lang, mf);
717 needSetLanguageEnabledMaps[lang] = true;
718 // this can only be called after loading CMake(LANG)Compiler.cmake
719 }
720 }
721
722 if (!this->GetLanguageEnabled(lang)) {
723 if (this->CMakeInstance->GetIsInTryCompile()) {
724 cmSystemTools::Error("This should not have happened. "
725 "If you see this message, you are probably "
726 "using a broken CMakeLists.txt file or a "
727 "problematic release of CMake");
728 }
729 // if the CMake(LANG)Compiler.cmake file was not found then
730 // load CMakeDetermine(LANG)Compiler.cmake
731 std::string determineCompiler =
732 cmStrCat("CMakeDetermine", lang, "Compiler.cmake");
733 std::string determineFile = mf->GetModulesFile(determineCompiler);
734 if (!mf->ReadListFile(determineFile)) {
735 cmSystemTools::Error("Could not find cmake module file: " +
736 determineCompiler);
737 }
738 if (cmSystemTools::GetFatalErrorOccured()) {
739 return;
740 }
741 needTestLanguage[lang] = true;
742 // Some generators like visual studio should not use the env variables
743 // So the global generator can specify that in this variable
744 if (!mf->GetDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV")) {
745 // put ${CMake_(LANG)_COMPILER_ENV_VAR}=${CMAKE_(LANG)_COMPILER
746 // into the environment, in case user scripts want to run
747 // configure, or sub cmakes
748 std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
749 std::string compilerEnv =
750 cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
751 const std::string& envVar = mf->GetRequiredDefinition(compilerEnv);
752 const std::string& envVarValue =
753 mf->GetRequiredDefinition(compilerName);
754 std::string env = cmStrCat(envVar, '=', envVarValue);
755 cmSystemTools::PutEnv(env);
756 }
757
758 // if determineLanguage was called then load the file it
759 // configures CMake(LANG)Compiler.cmake
760 fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
761 if (!mf->ReadListFile(fpath)) {
762 cmSystemTools::Error("Could not find cmake module file: " + fpath);
763 }
764 this->SetLanguageEnabledFlag(lang, mf);
765 needSetLanguageEnabledMaps[lang] = true;
766 // this can only be called after loading CMake(LANG)Compiler.cmake
767 // the language must be enabled for try compile to work, but we do
768 // not know if it is a working compiler yet so set the test language
769 // flag
770 needTestLanguage[lang] = true;
771 } // end if(!this->GetLanguageEnabled(lang) )
772 } // end loop over languages
773
774 // **** Load the system specific information if not yet loaded
775 if (!mf->GetDefinition("CMAKE_SYSTEM_SPECIFIC_INFORMATION_LOADED")) {
776 fpath = mf->GetModulesFile("CMakeSystemSpecificInformation.cmake");
777 if (!mf->ReadListFile(fpath)) {
778 cmSystemTools::Error("Could not find cmake module file: "
779 "CMakeSystemSpecificInformation.cmake");
780 }
781 }
782 // loop over languages again loading CMake(LANG)Information.cmake
783 //
784 for (std::string const& lang : languages) {
785 if (lang == "NONE") {
786 this->SetLanguageEnabled("NONE", mf);
787 continue;
788 }
789
790 // Check that the compiler was found.
791 std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
792 std::string compilerEnv = cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
793 std::ostringstream noCompiler;
794 cmValue compilerFile = mf->GetDefinition(compilerName);
795 if (!cmNonempty(compilerFile) || cmIsNOTFOUND(*compilerFile)) {
796 /* clang-format off */
797 noCompiler <<
798 "No " << compilerName << " could be found.\n"
799 ;
800 /* clang-format on */
801 } else if ((lang != "RC") && (lang != "ASM_MASM")) {
802 if (!cmSystemTools::FileIsFullPath(*compilerFile)) {
803 /* clang-format off */
804 noCompiler <<
805 "The " << compilerName << ":\n"
806 " " << *compilerFile << "\n"
807 "is not a full path and was not found in the PATH.\n"
808 ;
809 /* clang-format on */
810 } else if (!cmSystemTools::FileExists(*compilerFile)) {
811 /* clang-format off */
812 noCompiler <<
813 "The " << compilerName << ":\n"
814 " " << *compilerFile << "\n"
815 "is not a full path to an existing compiler tool.\n"
816 ;
817 /* clang-format on */
818 }
819 }
820 if (!noCompiler.str().empty()) {
821 // Skip testing this language since the compiler is not found.
822 needTestLanguage[lang] = false;
823 if (!optional) {
824 // The compiler was not found and it is not optional. Remove
825 // CMake(LANG)Compiler.cmake so we try again next time CMake runs.
826 std::string compilerLangFile =
827 cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
828 cmSystemTools::RemoveFile(compilerLangFile);
829 if (!this->CMakeInstance->GetIsInTryCompile()) {
830 this->PrintCompilerAdvice(noCompiler, lang,
831 mf->GetDefinition(compilerEnv));
832 mf->IssueMessage(MessageType::FATAL_ERROR, noCompiler.str());
833 fatalError = true;
834 }
835 }
836 }
837
838 std::string langLoadedVar =
839 cmStrCat("CMAKE_", lang, "_INFORMATION_LOADED");
840 if (!mf->GetDefinition(langLoadedVar)) {
841 fpath = cmStrCat("CMake", lang, "Information.cmake");
842 std::string informationFile = mf->GetModulesFile(fpath);
843 if (informationFile.empty()) {
844 cmSystemTools::Error("Could not find cmake module file: " + fpath);
845 } else if (!mf->ReadListFile(informationFile)) {
846 cmSystemTools::Error("Could not process cmake module file: " +
847 informationFile);
848 }
849 }
850 if (needSetLanguageEnabledMaps[lang]) {
851 this->SetLanguageEnabledMaps(lang, mf);
852 }
853 this->LanguagesReady.insert(lang);
854
855 // Test the compiler for the language just setup
856 // (but only if a compiler has been actually found)
857 // At this point we should have enough info for a try compile
858 // which is used in the backward stuff
859 // If the language is untested then test it now with a try compile.
860 if (needTestLanguage[lang]) {
861 if (!this->CMakeInstance->GetIsInTryCompile()) {
862 std::string testLang = cmStrCat("CMakeTest", lang, "Compiler.cmake");
863 std::string ifpath = mf->GetModulesFile(testLang);
864 if (!mf->ReadListFile(ifpath)) {
865 cmSystemTools::Error("Could not find cmake module file: " +
866 testLang);
867 }
868 std::string compilerWorks =
869 cmStrCat("CMAKE_", lang, "_COMPILER_WORKS");
870 // if the compiler did not work, then remove the
871 // CMake(LANG)Compiler.cmake file so that it will get tested the
872 // next time cmake is run
873 if (!mf->IsOn(compilerWorks)) {
874 std::string compilerLangFile =
875 cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
876 cmSystemTools::RemoveFile(compilerLangFile);
877 }
878 } // end if in try compile
879 } // end need test language
880 // Store the shared library flags so that we can satisfy CMP0018
881 std::string sharedLibFlagsVar =
882 cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS");
883 this->LanguageToOriginalSharedLibFlags[lang] =
884 mf->GetSafeDefinition(sharedLibFlagsVar);
885
886 // Translate compiler ids for compatibility.
887 this->CheckCompilerIdCompatibility(mf, lang);
888 } // end for each language
889
890 // Now load files that can override any settings on the platform or for
891 // the project First load the project compatibility file if it is in
892 // cmake
893 std::string projectCompatibility =
894 cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/",
895 mf->GetSafeDefinition("PROJECT_NAME"), "Compatibility.cmake");
896 if (cmSystemTools::FileExists(projectCompatibility)) {
897 mf->ReadListFile(projectCompatibility);
898 }
899 // Inform any extra generator of the new language.
900 if (this->ExtraGenerator) {
901 this->ExtraGenerator->EnableLanguage(languages, mf, false);
902 }
903
904 if (fatalError) {
905 cmSystemTools::SetFatalErrorOccured();
906 }
907
908 for (std::string const& lang : cur_languages) {
909 this->LanguagesInProgress.erase(lang);
910 }
911 }
912
PrintCompilerAdvice(std::ostream & os,std::string const & lang,cmValue envVar) const913 void cmGlobalGenerator::PrintCompilerAdvice(std::ostream& os,
914 std::string const& lang,
915 cmValue envVar) const
916 {
917 // Subclasses override this method if they do not support this advice.
918 os << "Tell CMake where to find the compiler by setting ";
919 if (envVar) {
920 os << "either the environment variable \"" << envVar << "\" or ";
921 }
922 os << "the CMake cache entry CMAKE_" << lang
923 << "_COMPILER "
924 "to the full path to the compiler, or to the compiler name "
925 "if it is in the PATH.";
926 }
927
CheckCompilerIdCompatibility(cmMakefile * mf,std::string const & lang) const928 void cmGlobalGenerator::CheckCompilerIdCompatibility(
929 cmMakefile* mf, std::string const& lang) const
930 {
931 std::string compilerIdVar = "CMAKE_" + lang + "_COMPILER_ID";
932 std::string const compilerId = mf->GetSafeDefinition(compilerIdVar);
933
934 if (compilerId == "AppleClang") {
935 switch (mf->GetPolicyStatus(cmPolicies::CMP0025)) {
936 case cmPolicies::WARN:
937 if (!this->CMakeInstance->GetIsInTryCompile() &&
938 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0025")) {
939 std::ostringstream w;
940 /* clang-format off */
941 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0025) << "\n"
942 "Converting " << lang <<
943 R"( compiler id "AppleClang" to "Clang" for compatibility.)"
944 ;
945 /* clang-format on */
946 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
947 }
948 CM_FALLTHROUGH;
949 case cmPolicies::OLD:
950 // OLD behavior is to convert AppleClang to Clang.
951 mf->AddDefinition(compilerIdVar, "Clang");
952 break;
953 case cmPolicies::REQUIRED_IF_USED:
954 case cmPolicies::REQUIRED_ALWAYS:
955 mf->IssueMessage(
956 MessageType::FATAL_ERROR,
957 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0025));
958 break;
959 case cmPolicies::NEW:
960 // NEW behavior is to keep AppleClang.
961 break;
962 }
963 }
964
965 if (compilerId == "QCC") {
966 switch (mf->GetPolicyStatus(cmPolicies::CMP0047)) {
967 case cmPolicies::WARN:
968 if (!this->CMakeInstance->GetIsInTryCompile() &&
969 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0047")) {
970 std::ostringstream w;
971 /* clang-format off */
972 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0047) << "\n"
973 "Converting " << lang <<
974 R"( compiler id "QCC" to "GNU" for compatibility.)"
975 ;
976 /* clang-format on */
977 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
978 }
979 CM_FALLTHROUGH;
980 case cmPolicies::OLD:
981 // OLD behavior is to convert QCC to GNU.
982 mf->AddDefinition(compilerIdVar, "GNU");
983 if (lang == "C") {
984 mf->AddDefinition("CMAKE_COMPILER_IS_GNUCC", "1");
985 } else if (lang == "CXX") {
986 mf->AddDefinition("CMAKE_COMPILER_IS_GNUCXX", "1");
987 }
988 break;
989 case cmPolicies::REQUIRED_IF_USED:
990 case cmPolicies::REQUIRED_ALWAYS:
991 mf->IssueMessage(
992 MessageType::FATAL_ERROR,
993 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0047));
994 CM_FALLTHROUGH;
995 case cmPolicies::NEW:
996 // NEW behavior is to keep QCC.
997 break;
998 }
999 }
1000
1001 if (compilerId == "XLClang") {
1002 switch (mf->GetPolicyStatus(cmPolicies::CMP0089)) {
1003 case cmPolicies::WARN:
1004 if (!this->CMakeInstance->GetIsInTryCompile() &&
1005 mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0089")) {
1006 std::ostringstream w;
1007 /* clang-format off */
1008 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0089) << "\n"
1009 "Converting " << lang <<
1010 R"( compiler id "XLClang" to "XL" for compatibility.)"
1011 ;
1012 /* clang-format on */
1013 mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
1014 }
1015 CM_FALLTHROUGH;
1016 case cmPolicies::OLD:
1017 // OLD behavior is to convert XLClang to XL.
1018 mf->AddDefinition(compilerIdVar, "XL");
1019 break;
1020 case cmPolicies::REQUIRED_IF_USED:
1021 case cmPolicies::REQUIRED_ALWAYS:
1022 mf->IssueMessage(
1023 MessageType::FATAL_ERROR,
1024 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0089));
1025 break;
1026 case cmPolicies::NEW:
1027 // NEW behavior is to keep AppleClang.
1028 break;
1029 }
1030 }
1031 }
1032
GetLanguageOutputExtension(cmSourceFile const & source) const1033 std::string cmGlobalGenerator::GetLanguageOutputExtension(
1034 cmSourceFile const& source) const
1035 {
1036 const std::string& lang = source.GetLanguage();
1037 if (!lang.empty()) {
1038 auto const it = this->LanguageToOutputExtension.find(lang);
1039 if (it != this->LanguageToOutputExtension.end()) {
1040 return it->second;
1041 }
1042 } else {
1043 // if no language is found then check to see if it is already an
1044 // output extension for some language. In that case it should be ignored
1045 // and in this map, so it will not be compiled but will just be used.
1046 std::string const& ext = source.GetExtension();
1047 if (!ext.empty()) {
1048 if (this->OutputExtensions.count(ext)) {
1049 return ext;
1050 }
1051 }
1052 }
1053 return "";
1054 }
1055
GetLanguageFromExtension(const char * ext) const1056 std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const
1057 {
1058 // if there is an extension and it starts with . then move past the
1059 // . because the extensions are not stored with a . in the map
1060 if (ext && *ext == '.') {
1061 ++ext;
1062 }
1063 auto const it = this->ExtensionToLanguage.find(ext);
1064 if (it != this->ExtensionToLanguage.end()) {
1065 return it->second;
1066 }
1067 return "";
1068 }
1069
1070 /* SetLanguageEnabled() is now split in two parts:
1071 at first the enabled-flag is set. This can then be used in EnabledLanguage()
1072 for checking whether the language is already enabled. After setting this
1073 flag still the values from the cmake variables have to be copied into the
1074 internal maps, this is done in SetLanguageEnabledMaps() which is called
1075 after the system- and compiler specific files have been loaded.
1076
1077 This split was done originally so that compiler-specific configuration
1078 files could change the object file extension
1079 (CMAKE_<LANG>_OUTPUT_EXTENSION) before the CMake variables were copied
1080 to the C++ maps.
1081 */
SetLanguageEnabled(const std::string & l,cmMakefile * mf)1082 void cmGlobalGenerator::SetLanguageEnabled(const std::string& l,
1083 cmMakefile* mf)
1084 {
1085 this->SetLanguageEnabledFlag(l, mf);
1086 this->SetLanguageEnabledMaps(l, mf);
1087 }
1088
SetLanguageEnabledFlag(const std::string & l,cmMakefile * mf)1089 void cmGlobalGenerator::SetLanguageEnabledFlag(const std::string& l,
1090 cmMakefile* mf)
1091 {
1092 this->CMakeInstance->GetState()->SetLanguageEnabled(l);
1093
1094 // Fill the language-to-extension map with the current variable
1095 // settings to make sure it is available for the try_compile()
1096 // command source file signature. In SetLanguageEnabledMaps this
1097 // will be done again to account for any compiler- or
1098 // platform-specific entries.
1099 this->FillExtensionToLanguageMap(l, mf);
1100 }
1101
SetLanguageEnabledMaps(const std::string & l,cmMakefile * mf)1102 void cmGlobalGenerator::SetLanguageEnabledMaps(const std::string& l,
1103 cmMakefile* mf)
1104 {
1105 // use LanguageToLinkerPreference to detect whether this functions has
1106 // run before
1107 if (cm::contains(this->LanguageToLinkerPreference, l)) {
1108 return;
1109 }
1110
1111 std::string linkerPrefVar = "CMAKE_" + l + "_LINKER_PREFERENCE";
1112 cmValue linkerPref = mf->GetDefinition(linkerPrefVar);
1113 int preference = 0;
1114 if (cmNonempty(linkerPref)) {
1115 if (sscanf(linkerPref->c_str(), "%d", &preference) != 1) {
1116 // backward compatibility: before 2.6 LINKER_PREFERENCE
1117 // was either "None" or "Preferred", and only the first character was
1118 // tested. So if there is a custom language out there and it is
1119 // "Preferred", set its preference high
1120 if ((*linkerPref)[0] == 'P') {
1121 preference = 100;
1122 } else {
1123 preference = 0;
1124 }
1125 }
1126 }
1127
1128 if (preference < 0) {
1129 std::string msg =
1130 cmStrCat(linkerPrefVar, " is negative, adjusting it to 0");
1131 cmSystemTools::Message(msg, "Warning");
1132 preference = 0;
1133 }
1134
1135 this->LanguageToLinkerPreference[l] = preference;
1136
1137 std::string outputExtensionVar = "CMAKE_" + l + "_OUTPUT_EXTENSION";
1138 if (cmValue p = mf->GetDefinition(outputExtensionVar)) {
1139 std::string outputExtension = *p;
1140 this->LanguageToOutputExtension[l] = outputExtension;
1141 this->OutputExtensions[outputExtension] = outputExtension;
1142 if (cmHasPrefix(outputExtension, ".")) {
1143 outputExtension = outputExtension.substr(1);
1144 this->OutputExtensions[outputExtension] = outputExtension;
1145 }
1146 }
1147
1148 // The map was originally filled by SetLanguageEnabledFlag, but
1149 // since then the compiler- and platform-specific files have been
1150 // loaded which might have added more entries.
1151 this->FillExtensionToLanguageMap(l, mf);
1152
1153 std::string ignoreExtensionsVar =
1154 std::string("CMAKE_") + std::string(l) + std::string("_IGNORE_EXTENSIONS");
1155 std::string ignoreExts = mf->GetSafeDefinition(ignoreExtensionsVar);
1156 std::vector<std::string> extensionList = cmExpandedList(ignoreExts);
1157 for (std::string const& i : extensionList) {
1158 this->IgnoreExtensions[i] = true;
1159 }
1160 }
1161
FillExtensionToLanguageMap(const std::string & l,cmMakefile * mf)1162 void cmGlobalGenerator::FillExtensionToLanguageMap(const std::string& l,
1163 cmMakefile* mf)
1164 {
1165 std::string extensionsVar = std::string("CMAKE_") + std::string(l) +
1166 std::string("_SOURCE_FILE_EXTENSIONS");
1167 const std::string& exts = mf->GetSafeDefinition(extensionsVar);
1168 std::vector<std::string> extensionList = cmExpandedList(exts);
1169 for (std::string const& i : extensionList) {
1170 this->ExtensionToLanguage[i] = l;
1171 }
1172 }
1173
GetGlobalSetting(std::string const & name) const1174 cmValue cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
1175 {
1176 assert(!this->Makefiles.empty());
1177 return this->Makefiles[0]->GetDefinition(name);
1178 }
1179
GlobalSettingIsOn(std::string const & name) const1180 bool cmGlobalGenerator::GlobalSettingIsOn(std::string const& name) const
1181 {
1182 assert(!this->Makefiles.empty());
1183 return this->Makefiles[0]->IsOn(name);
1184 }
1185
GetSafeGlobalSetting(std::string const & name) const1186 std::string cmGlobalGenerator::GetSafeGlobalSetting(
1187 std::string const& name) const
1188 {
1189 assert(!this->Makefiles.empty());
1190 return this->Makefiles[0]->GetDefinition(name);
1191 }
1192
IgnoreFile(const char * ext) const1193 bool cmGlobalGenerator::IgnoreFile(const char* ext) const
1194 {
1195 if (!this->GetLanguageFromExtension(ext).empty()) {
1196 return false;
1197 }
1198 return (this->IgnoreExtensions.count(ext) > 0);
1199 }
1200
GetLanguageEnabled(const std::string & l) const1201 bool cmGlobalGenerator::GetLanguageEnabled(const std::string& l) const
1202 {
1203 return this->CMakeInstance->GetState()->GetLanguageEnabled(l);
1204 }
1205
ClearEnabledLanguages()1206 void cmGlobalGenerator::ClearEnabledLanguages()
1207 {
1208 this->CMakeInstance->GetState()->ClearEnabledLanguages();
1209 }
1210
CreateLocalGenerators()1211 void cmGlobalGenerator::CreateLocalGenerators()
1212 {
1213 this->LocalGeneratorSearchIndex.clear();
1214 this->LocalGenerators.clear();
1215 this->LocalGenerators.reserve(this->Makefiles.size());
1216 for (const auto& m : this->Makefiles) {
1217 auto lg = this->CreateLocalGenerator(m.get());
1218 this->IndexLocalGenerator(lg.get());
1219 this->LocalGenerators.push_back(std::move(lg));
1220 }
1221 }
1222
Configure()1223 void cmGlobalGenerator::Configure()
1224 {
1225 this->FirstTimeProgress = 0.0f;
1226 this->ClearGeneratorMembers();
1227 this->NextDeferId = 0;
1228
1229 cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot();
1230
1231 snapshot.GetDirectory().SetCurrentSource(
1232 this->CMakeInstance->GetHomeDirectory());
1233 snapshot.GetDirectory().SetCurrentBinary(
1234 this->CMakeInstance->GetHomeOutputDirectory());
1235
1236 auto dirMfu = cm::make_unique<cmMakefile>(this, snapshot);
1237 auto* dirMf = dirMfu.get();
1238 this->Makefiles.push_back(std::move(dirMfu));
1239 dirMf->SetRecursionDepth(this->RecursionDepth);
1240 this->IndexMakefile(dirMf);
1241
1242 this->BinaryDirectories.insert(
1243 this->CMakeInstance->GetHomeOutputDirectory());
1244
1245 // now do it
1246 this->ConfigureDoneCMP0026AndCMP0024 = false;
1247 dirMf->Configure();
1248 dirMf->EnforceDirectoryLevelRules();
1249
1250 this->ConfigureDoneCMP0026AndCMP0024 = true;
1251
1252 // Put a copy of each global target in every directory.
1253 {
1254 std::vector<GlobalTargetInfo> globalTargets;
1255 this->CreateDefaultGlobalTargets(globalTargets);
1256
1257 for (const auto& mf : this->Makefiles) {
1258 for (GlobalTargetInfo const& globalTarget : globalTargets) {
1259 this->CreateGlobalTarget(globalTarget, mf.get());
1260 }
1261 }
1262 }
1263
1264 // update the cache entry for the number of local generators, this is used
1265 // for progress
1266 char num[100];
1267 sprintf(num, "%d", static_cast<int>(this->Makefiles.size()));
1268 this->GetCMakeInstance()->AddCacheEntry("CMAKE_NUMBER_OF_MAKEFILES", num,
1269 "number of local generators",
1270 cmStateEnums::INTERNAL);
1271
1272 if (this->CMakeInstance->GetWorkingMode() == cmake::NORMAL_MODE) {
1273 std::ostringstream msg;
1274 if (cmSystemTools::GetErrorOccuredFlag()) {
1275 msg << "Configuring incomplete, errors occurred!";
1276 const char* logs[] = { "CMakeOutput.log", "CMakeError.log", nullptr };
1277 for (const char** log = logs; *log; ++log) {
1278 std::string f = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
1279 "/CMakeFiles/", *log);
1280 if (cmSystemTools::FileExists(f)) {
1281 msg << "\nSee also \"" << f << "\".";
1282 }
1283 }
1284 } else {
1285 msg << "Configuring done";
1286 }
1287 this->CMakeInstance->UpdateProgress(msg.str(), -1);
1288 }
1289 }
1290
CreateGenerationObjects(TargetTypes targetTypes)1291 void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes)
1292 {
1293 this->CreateLocalGenerators();
1294 // Commit side effects only if we are actually generating
1295 if (this->GetConfigureDoneCMP0026()) {
1296 this->CheckTargetProperties();
1297 }
1298 this->CreateGeneratorTargets(targetTypes);
1299 this->ComputeBuildFileGenerators();
1300 }
1301
CreateImportedGenerationObjects(cmMakefile * mf,const std::vector<std::string> & targets,std::vector<const cmGeneratorTarget * > & exports)1302 void cmGlobalGenerator::CreateImportedGenerationObjects(
1303 cmMakefile* mf, const std::vector<std::string>& targets,
1304 std::vector<const cmGeneratorTarget*>& exports)
1305 {
1306 this->CreateGenerationObjects(ImportedOnly);
1307 auto const mfit =
1308 std::find_if(this->Makefiles.begin(), this->Makefiles.end(),
1309 [mf](const std::unique_ptr<cmMakefile>& item) {
1310 return item.get() == mf;
1311 });
1312 auto& lg =
1313 this->LocalGenerators[std::distance(this->Makefiles.begin(), mfit)];
1314 for (std::string const& t : targets) {
1315 cmGeneratorTarget* gt = lg->FindGeneratorTargetToUse(t);
1316 if (gt) {
1317 exports.push_back(gt);
1318 }
1319 }
1320 }
1321
GetExportedTargetsFile(const std::string & filename) const1322 cmExportBuildFileGenerator* cmGlobalGenerator::GetExportedTargetsFile(
1323 const std::string& filename) const
1324 {
1325 auto const it = this->BuildExportSets.find(filename);
1326 return it == this->BuildExportSets.end() ? nullptr : it->second;
1327 }
1328
AddCMP0042WarnTarget(const std::string & target)1329 void cmGlobalGenerator::AddCMP0042WarnTarget(const std::string& target)
1330 {
1331 this->CMP0042WarnTargets.insert(target);
1332 }
1333
AddCMP0068WarnTarget(const std::string & target)1334 void cmGlobalGenerator::AddCMP0068WarnTarget(const std::string& target)
1335 {
1336 this->CMP0068WarnTargets.insert(target);
1337 }
1338
CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const1339 bool cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const
1340 {
1341 // If the property is not enabled then okay.
1342 if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(
1343 "ALLOW_DUPLICATE_CUSTOM_TARGETS")) {
1344 return true;
1345 }
1346
1347 // This generator does not support duplicate custom targets.
1348 std::ostringstream e;
1349 e << "This project has enabled the ALLOW_DUPLICATE_CUSTOM_TARGETS "
1350 << "global property. "
1351 << "The \"" << this->GetName() << "\" generator does not support "
1352 << "duplicate custom targets. "
1353 << "Consider using a Makefiles generator or fix the project to not "
1354 << "use duplicate target names.";
1355 cmSystemTools::Error(e.str());
1356 return false;
1357 }
1358
ComputeBuildFileGenerators()1359 void cmGlobalGenerator::ComputeBuildFileGenerators()
1360 {
1361 for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
1362 std::vector<std::unique_ptr<cmExportBuildFileGenerator>> const& gens =
1363 this->Makefiles[i]->GetExportBuildFileGenerators();
1364 for (std::unique_ptr<cmExportBuildFileGenerator> const& g : gens) {
1365 g->Compute(this->LocalGenerators[i].get());
1366 }
1367 }
1368 }
1369
UnsupportedVariableIsDefined(const std::string & name,bool supported) const1370 bool cmGlobalGenerator::UnsupportedVariableIsDefined(const std::string& name,
1371 bool supported) const
1372 {
1373 if (!supported && this->Makefiles.front()->GetDefinition(name)) {
1374 std::ostringstream e;
1375 /* clang-format off */
1376 e <<
1377 "Generator\n"
1378 " " << this->GetName() << "\n"
1379 "does not support variable\n"
1380 " " << name << "\n"
1381 "but it has been specified."
1382 ;
1383 /* clang-format on */
1384 this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str());
1385 return true;
1386 }
1387
1388 return false;
1389 }
1390
Compute()1391 bool cmGlobalGenerator::Compute()
1392 {
1393 // Make sure unsupported variables are not used.
1394 if (this->UnsupportedVariableIsDefined("CMAKE_DEFAULT_BUILD_TYPE",
1395 this->SupportsDefaultBuildType())) {
1396 return false;
1397 }
1398 if (this->UnsupportedVariableIsDefined("CMAKE_CROSS_CONFIGS",
1399 this->SupportsCrossConfigs())) {
1400 return false;
1401 }
1402 if (this->UnsupportedVariableIsDefined("CMAKE_DEFAULT_CONFIGS",
1403 this->SupportsDefaultConfigs())) {
1404 return false;
1405 }
1406 if (!this->InspectConfigTypeVariables()) {
1407 return false;
1408 }
1409
1410 // Some generators track files replaced during the Generate.
1411 // Start with an empty vector:
1412 this->FilesReplacedDuringGenerate.clear();
1413
1414 // clear targets to issue warning CMP0042 for
1415 this->CMP0042WarnTargets.clear();
1416 // clear targets to issue warning CMP0068 for
1417 this->CMP0068WarnTargets.clear();
1418
1419 // Check whether this generator is allowed to run.
1420 if (!this->CheckALLOW_DUPLICATE_CUSTOM_TARGETS()) {
1421 return false;
1422 }
1423 this->FinalizeTargetCompileInfo();
1424
1425 this->CreateGenerationObjects();
1426
1427 // at this point this->LocalGenerators has been filled,
1428 // so create the map from project name to vector of local generators
1429 this->FillProjectMap();
1430
1431 // Add automatically generated sources (e.g. unity build).
1432 if (!this->AddAutomaticSources()) {
1433 return false;
1434 }
1435
1436 // Iterate through all targets and set up AUTOMOC, AUTOUIC and AUTORCC
1437 if (!this->QtAutoGen()) {
1438 return false;
1439 }
1440
1441 // Add generator specific helper commands
1442 for (const auto& localGen : this->LocalGenerators) {
1443 localGen->AddHelperCommands();
1444 }
1445
1446 // Perform up-front computation in order to handle errors (such as unknown
1447 // features) at this point. While processing the compile features we also
1448 // calculate and cache the language standard required by the compile
1449 // features.
1450 for (const auto& localGen : this->LocalGenerators) {
1451 if (!localGen->ComputeTargetCompileFeatures()) {
1452 return false;
1453 }
1454 }
1455
1456 for (const auto& localGen : this->LocalGenerators) {
1457 cmMakefile* mf = localGen->GetMakefile();
1458 for (const auto& g : mf->GetInstallGenerators()) {
1459 if (!g->Compute(localGen.get())) {
1460 return false;
1461 }
1462 }
1463 }
1464
1465 this->AddExtraIDETargets();
1466
1467 // Trace the dependencies, after that no custom commands should be added
1468 // because their dependencies might not be handled correctly
1469 for (const auto& localGen : this->LocalGenerators) {
1470 localGen->TraceDependencies();
1471 }
1472
1473 // Make sure that all (non-imported) targets have source files added!
1474 if (this->CheckTargetsForMissingSources()) {
1475 return false;
1476 }
1477
1478 this->ForceLinkerLanguages();
1479
1480 // Compute the manifest of main targets generated.
1481 for (const auto& localGen : this->LocalGenerators) {
1482 localGen->ComputeTargetManifest();
1483 }
1484
1485 // Compute the inter-target dependencies.
1486 if (!this->ComputeTargetDepends()) {
1487 return false;
1488 }
1489 this->ComputeTargetOrder();
1490
1491 if (this->CheckTargetsForType()) {
1492 return false;
1493 }
1494
1495 if (this->CheckTargetsForPchCompilePdb()) {
1496 return false;
1497 }
1498
1499 for (const auto& localGen : this->LocalGenerators) {
1500 localGen->ComputeHomeRelativeOutputPath();
1501 }
1502
1503 return true;
1504 }
1505
Generate()1506 void cmGlobalGenerator::Generate()
1507 {
1508 // Create a map from local generator to the complete set of targets
1509 // it builds by default.
1510 this->InitializeProgressMarks();
1511
1512 this->ProcessEvaluationFiles();
1513
1514 this->CMakeInstance->UpdateProgress("Generating", 0.1f);
1515
1516 // Generate project files
1517 for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
1518 this->SetCurrentMakefile(this->LocalGenerators[i]->GetMakefile());
1519 this->LocalGenerators[i]->Generate();
1520 if (!this->LocalGenerators[i]->GetMakefile()->IsOn(
1521 "CMAKE_SKIP_INSTALL_RULES")) {
1522 this->LocalGenerators[i]->GenerateInstallRules();
1523 }
1524 this->LocalGenerators[i]->GenerateTestFiles();
1525 this->CMakeInstance->UpdateProgress(
1526 "Generating",
1527 0.1f +
1528 0.9f * (static_cast<float>(i) + 1.0f) /
1529 static_cast<float>(this->LocalGenerators.size()));
1530 }
1531 this->SetCurrentMakefile(nullptr);
1532
1533 if (!this->GenerateCPackPropertiesFile()) {
1534 this->GetCMakeInstance()->IssueMessage(
1535 MessageType::FATAL_ERROR, "Could not write CPack properties file.");
1536 }
1537
1538 for (auto& buildExpSet : this->BuildExportSets) {
1539 if (!buildExpSet.second->GenerateImportFile()) {
1540 if (!cmSystemTools::GetErrorOccuredFlag()) {
1541 this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
1542 "Could not write export file.");
1543 }
1544 return;
1545 }
1546 }
1547 // Update rule hashes.
1548 this->CheckRuleHashes();
1549
1550 this->WriteSummary();
1551
1552 if (this->ExtraGenerator) {
1553 this->ExtraGenerator->Generate();
1554 }
1555
1556 if (!this->CMP0042WarnTargets.empty()) {
1557 std::ostringstream w;
1558 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0042) << "\n";
1559 w << "MACOSX_RPATH is not specified for"
1560 " the following targets:\n";
1561 for (std::string const& t : this->CMP0042WarnTargets) {
1562 w << " " << t << "\n";
1563 }
1564 this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
1565 w.str());
1566 }
1567
1568 if (!this->CMP0068WarnTargets.empty()) {
1569 std::ostringstream w;
1570 /* clang-format off */
1571 w <<
1572 cmPolicies::GetPolicyWarning(cmPolicies::CMP0068) << "\n"
1573 "For compatibility with older versions of CMake, the install_name "
1574 "fields for the following targets are still affected by RPATH "
1575 "settings:\n"
1576 ;
1577 /* clang-format on */
1578 for (std::string const& t : this->CMP0068WarnTargets) {
1579 w << " " << t << "\n";
1580 }
1581 this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
1582 w.str());
1583 }
1584
1585 this->CMakeInstance->UpdateProgress("Generating done", -1);
1586 }
1587
ComputeTargetDepends()1588 bool cmGlobalGenerator::ComputeTargetDepends()
1589 {
1590 cmComputeTargetDepends ctd(this);
1591 if (!ctd.Compute()) {
1592 return false;
1593 }
1594 for (cmGeneratorTarget const* target : ctd.GetTargets()) {
1595 ctd.GetTargetDirectDepends(target, this->TargetDependencies[target]);
1596 }
1597 return true;
1598 }
1599
1600 std::vector<cmGeneratorTarget*>
GetLocalGeneratorTargetsInOrder(cmLocalGenerator * lg) const1601 cmGlobalGenerator::GetLocalGeneratorTargetsInOrder(cmLocalGenerator* lg) const
1602 {
1603 std::vector<cmGeneratorTarget*> gts;
1604 cm::append(gts, lg->GetGeneratorTargets());
1605 std::sort(gts.begin(), gts.end(),
1606 [this](cmGeneratorTarget const* l, cmGeneratorTarget const* r) {
1607 return this->TargetOrderIndex.at(l) <
1608 this->TargetOrderIndex.at(r);
1609 });
1610 return gts;
1611 }
1612
ComputeTargetOrder()1613 void cmGlobalGenerator::ComputeTargetOrder()
1614 {
1615 size_t index = 0;
1616 auto const& lgens = this->GetLocalGenerators();
1617 for (auto const& lgen : lgens) {
1618 const auto& targets = lgen->GetGeneratorTargets();
1619 for (const auto& gt : targets) {
1620 this->ComputeTargetOrder(gt.get(), index);
1621 }
1622 }
1623 assert(index == this->TargetOrderIndex.size());
1624 }
1625
ComputeTargetOrder(cmGeneratorTarget const * gt,size_t & index)1626 void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt,
1627 size_t& index)
1628 {
1629 std::map<cmGeneratorTarget const*, size_t>::value_type value(gt, 0);
1630 auto insertion = this->TargetOrderIndex.insert(value);
1631 if (!insertion.second) {
1632 return;
1633 }
1634 auto entry = insertion.first;
1635
1636 const auto& deps = this->GetTargetDirectDepends(gt);
1637 for (const auto& d : deps) {
1638 this->ComputeTargetOrder(d, index);
1639 }
1640
1641 entry->second = index++;
1642 }
1643
QtAutoGen()1644 bool cmGlobalGenerator::QtAutoGen()
1645 {
1646 #ifndef CMAKE_BOOTSTRAP
1647 cmQtAutoGenGlobalInitializer initializer(this->LocalGenerators);
1648 return initializer.generate();
1649 #else
1650 return true;
1651 #endif
1652 }
1653
AddAutomaticSources()1654 bool cmGlobalGenerator::AddAutomaticSources()
1655 {
1656 for (const auto& lg : this->LocalGenerators) {
1657 lg->CreateEvaluationFileOutputs();
1658 }
1659 for (const auto& lg : this->LocalGenerators) {
1660 for (const auto& gt : lg->GetGeneratorTargets()) {
1661 if (!gt->CanCompileSources()) {
1662 continue;
1663 }
1664 lg->AddUnityBuild(gt.get());
1665 lg->AddISPCDependencies(gt.get());
1666 // Targets that re-use a PCH are handled below.
1667 if (!gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
1668 lg->AddPchDependencies(gt.get());
1669 }
1670 }
1671 }
1672 for (const auto& lg : this->LocalGenerators) {
1673 for (const auto& gt : lg->GetGeneratorTargets()) {
1674 if (!gt->CanCompileSources()) {
1675 continue;
1676 }
1677 // Handle targets that re-use a PCH from an above-handled target.
1678 if (gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
1679 lg->AddPchDependencies(gt.get());
1680 }
1681 }
1682 }
1683 // The above transformations may have changed the classification of sources.
1684 // Clear the source list and classification cache (KindedSources) of all
1685 // targets so that it will be recomputed correctly by the generators later
1686 // now that the above transformations are done for all targets.
1687 for (const auto& lg : this->LocalGenerators) {
1688 for (const auto& gt : lg->GetGeneratorTargets()) {
1689 gt->ClearSourcesCache();
1690 }
1691 }
1692 return true;
1693 }
1694
CreateLinkLineComputer(cmOutputConverter * outputConverter,cmStateDirectory const & stateDir) const1695 std::unique_ptr<cmLinkLineComputer> cmGlobalGenerator::CreateLinkLineComputer(
1696 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
1697 {
1698 return cm::make_unique<cmLinkLineComputer>(outputConverter, stateDir);
1699 }
1700
1701 std::unique_ptr<cmLinkLineComputer>
CreateMSVC60LinkLineComputer(cmOutputConverter * outputConverter,cmStateDirectory const & stateDir) const1702 cmGlobalGenerator::CreateMSVC60LinkLineComputer(
1703 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
1704 {
1705 return std::unique_ptr<cmLinkLineComputer>(
1706 cm::make_unique<cmMSVC60LinkLineComputer>(outputConverter, stateDir));
1707 }
1708
FinalizeTargetCompileInfo()1709 void cmGlobalGenerator::FinalizeTargetCompileInfo()
1710 {
1711 std::vector<std::string> const langs =
1712 this->CMakeInstance->GetState()->GetEnabledLanguages();
1713
1714 // Construct per-target generator information.
1715 for (const auto& mf : this->Makefiles) {
1716 const cmBTStringRange noconfig_compile_definitions =
1717 mf->GetCompileDefinitionsEntries();
1718
1719 for (auto& target : mf->GetTargets()) {
1720 cmTarget* t = &target.second;
1721 if (t->GetType() == cmStateEnums::GLOBAL_TARGET) {
1722 continue;
1723 }
1724
1725 t->AppendBuildInterfaceIncludes();
1726
1727 if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
1728 continue;
1729 }
1730
1731 for (auto const& def : noconfig_compile_definitions) {
1732 t->InsertCompileDefinition(def);
1733 }
1734
1735 cmPolicies::PolicyStatus polSt =
1736 mf->GetPolicyStatus(cmPolicies::CMP0043);
1737 if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
1738 std::vector<std::string> configs =
1739 mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
1740
1741 for (std::string const& c : configs) {
1742 std::string defPropName =
1743 cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c));
1744 if (cmValue val = mf->GetProperty(defPropName)) {
1745 t->AppendProperty(defPropName, *val);
1746 }
1747 }
1748 }
1749 }
1750
1751 // The standard include directories for each language
1752 // should be treated as system include directories.
1753 std::set<std::string> standardIncludesSet;
1754 for (std::string const& li : langs) {
1755 std::string const standardIncludesVar =
1756 "CMAKE_" + li + "_STANDARD_INCLUDE_DIRECTORIES";
1757 std::string const& standardIncludesStr =
1758 mf->GetSafeDefinition(standardIncludesVar);
1759 std::vector<std::string> standardIncludesVec =
1760 cmExpandedList(standardIncludesStr);
1761 standardIncludesSet.insert(standardIncludesVec.begin(),
1762 standardIncludesVec.end());
1763 }
1764 mf->AddSystemIncludeDirectories(standardIncludesSet);
1765 }
1766 }
1767
CreateGeneratorTargets(TargetTypes targetTypes,cmMakefile * mf,cmLocalGenerator * lg,std::map<cmTarget *,cmGeneratorTarget * > const & importedMap)1768 void cmGlobalGenerator::CreateGeneratorTargets(
1769 TargetTypes targetTypes, cmMakefile* mf, cmLocalGenerator* lg,
1770 std::map<cmTarget*, cmGeneratorTarget*> const& importedMap)
1771 {
1772 if (targetTypes == AllTargets) {
1773 for (cmTarget* target : mf->GetOrderedTargets()) {
1774 lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(target, lg));
1775 }
1776 }
1777
1778 for (cmTarget* t : mf->GetImportedTargets()) {
1779 lg->AddImportedGeneratorTarget(importedMap.find(t)->second);
1780 }
1781 }
1782
CreateGeneratorTargets(TargetTypes targetTypes)1783 void cmGlobalGenerator::CreateGeneratorTargets(TargetTypes targetTypes)
1784 {
1785 std::map<cmTarget*, cmGeneratorTarget*> importedMap;
1786 for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
1787 auto& mf = this->Makefiles[i];
1788 for (const auto& ownedImpTgt : mf->GetOwnedImportedTargets()) {
1789 cmLocalGenerator* lg = this->LocalGenerators[i].get();
1790 auto gt = cm::make_unique<cmGeneratorTarget>(ownedImpTgt.get(), lg);
1791 importedMap[ownedImpTgt.get()] = gt.get();
1792 lg->AddOwnedImportedGeneratorTarget(std::move(gt));
1793 }
1794 }
1795
1796 // Construct per-target generator information.
1797 for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
1798 this->CreateGeneratorTargets(targetTypes, this->Makefiles[i].get(),
1799 this->LocalGenerators[i].get(), importedMap);
1800 }
1801 }
1802
ClearGeneratorMembers()1803 void cmGlobalGenerator::ClearGeneratorMembers()
1804 {
1805 this->BuildExportSets.clear();
1806
1807 this->Makefiles.clear();
1808
1809 this->LocalGenerators.clear();
1810
1811 this->AliasTargets.clear();
1812 this->ExportSets.clear();
1813 this->InstallComponents.clear();
1814 this->TargetDependencies.clear();
1815 this->TargetSearchIndex.clear();
1816 this->GeneratorTargetSearchIndex.clear();
1817 this->MakefileSearchIndex.clear();
1818 this->LocalGeneratorSearchIndex.clear();
1819 this->TargetOrderIndex.clear();
1820 this->ProjectMap.clear();
1821 this->RuleHashes.clear();
1822 this->DirectoryContentMap.clear();
1823 this->BinaryDirectories.clear();
1824 this->GeneratedFiles.clear();
1825 }
1826
ComputeTargetObjectDirectory(cmGeneratorTarget *) const1827 void cmGlobalGenerator::ComputeTargetObjectDirectory(
1828 cmGeneratorTarget* /*unused*/) const
1829 {
1830 }
1831
CheckTargetProperties()1832 void cmGlobalGenerator::CheckTargetProperties()
1833 {
1834 // check for link libraries and include directories containing "NOTFOUND"
1835 // and for infinite loops
1836 std::map<std::string, std::string> notFoundMap;
1837 cmState* state = this->GetCMakeInstance()->GetState();
1838 for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
1839 this->Makefiles[i]->Generate(*this->LocalGenerators[i]);
1840 for (auto const& target : this->Makefiles[i]->GetTargets()) {
1841 if (target.second.GetType() == cmStateEnums::INTERFACE_LIBRARY) {
1842 continue;
1843 }
1844 for (auto const& lib : target.second.GetOriginalLinkLibraries()) {
1845 if (lib.first.size() > 9 && cmIsNOTFOUND(lib.first)) {
1846 std::string varName = lib.first.substr(0, lib.first.size() - 9);
1847 if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
1848 varName += " (ADVANCED)";
1849 }
1850 std::string text =
1851 cmStrCat(notFoundMap[varName], "\n linked by target \"",
1852 target.second.GetName(), "\" in directory ",
1853 this->Makefiles[i]->GetCurrentSourceDirectory());
1854 notFoundMap[varName] = text;
1855 }
1856 }
1857 std::vector<std::string> incs;
1858 cmValue incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
1859 if (!incDirProp) {
1860 continue;
1861 }
1862
1863 std::string incDirs = cmGeneratorExpression::Preprocess(
1864 *incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions);
1865
1866 cmExpandList(incDirs, incs);
1867
1868 for (std::string const& incDir : incs) {
1869 if (incDir.size() > 9 && cmIsNOTFOUND(incDir)) {
1870 std::string varName = incDir.substr(0, incDir.size() - 9);
1871 if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
1872 varName += " (ADVANCED)";
1873 }
1874 std::string text =
1875 cmStrCat(notFoundMap[varName],
1876 "\n used as include directory in directory ",
1877 this->Makefiles[i]->GetCurrentSourceDirectory());
1878 notFoundMap[varName] = text;
1879 }
1880 }
1881 }
1882 }
1883
1884 if (!notFoundMap.empty()) {
1885 std::string notFoundVars;
1886 for (auto const& notFound : notFoundMap) {
1887 notFoundVars += notFound.first;
1888 notFoundVars += notFound.second;
1889 notFoundVars += "\n";
1890 }
1891 cmSystemTools::Error("The following variables are used in this project, "
1892 "but they are set to NOTFOUND.\n"
1893 "Please set them or make sure they are set and "
1894 "tested correctly in the CMake files:\n" +
1895 notFoundVars);
1896 }
1897 }
1898
TryCompile(int jobs,const std::string & srcdir,const std::string & bindir,const std::string & projectName,const std::string & target,bool fast,std::string & output,cmMakefile * mf)1899 int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir,
1900 const std::string& bindir,
1901 const std::string& projectName,
1902 const std::string& target, bool fast,
1903 std::string& output, cmMakefile* mf)
1904 {
1905 // if this is not set, then this is a first time configure
1906 // and there is a good chance that the try compile stuff will
1907 // take the bulk of the time, so try and guess some progress
1908 // by getting closer and closer to 100 without actually getting there.
1909 if (!this->CMakeInstance->GetState()->GetInitializedCacheValue(
1910 "CMAKE_NUMBER_OF_MAKEFILES")) {
1911 // If CMAKE_NUMBER_OF_MAKEFILES is not set
1912 // we are in the first time progress and we have no
1913 // idea how long it will be. So, just move 1/10th of the way
1914 // there each time, and don't go over 95%
1915 this->FirstTimeProgress += ((1.0f - this->FirstTimeProgress) / 30.0f);
1916 if (this->FirstTimeProgress > 0.95f) {
1917 this->FirstTimeProgress = 0.95f;
1918 }
1919 this->CMakeInstance->UpdateProgress("Configuring",
1920 this->FirstTimeProgress);
1921 }
1922
1923 std::vector<std::string> newTarget = {};
1924 if (!target.empty()) {
1925 newTarget = { target };
1926 }
1927 std::string config =
1928 mf->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
1929 return this->Build(jobs, srcdir, bindir, projectName, newTarget, output, "",
1930 config, false, fast, false, this->TryCompileTimeout);
1931 }
1932
1933 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
GenerateBuildCommand(const std::string &,const std::string &,const std::string &,std::vector<std::string> const &,const std::string &,bool,int,bool,std::vector<std::string> const &)1934 cmGlobalGenerator::GenerateBuildCommand(
1935 const std::string& /*unused*/, const std::string& /*unused*/,
1936 const std::string& /*unused*/, std::vector<std::string> const& /*unused*/,
1937 const std::string& /*unused*/, bool /*unused*/, int /*unused*/,
1938 bool /*unused*/, std::vector<std::string> const& /*unused*/)
1939 {
1940 GeneratedMakeCommand makeCommand;
1941 makeCommand.Add("cmGlobalGenerator::GenerateBuildCommand not implemented");
1942 return { std::move(makeCommand) };
1943 }
1944
PrintBuildCommandAdvice(std::ostream &,int) const1945 void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/,
1946 int /*jobs*/) const
1947 {
1948 // Subclasses override this method if they e.g want to give a warning that
1949 // they do not support certain build command line options
1950 }
1951
Build(int jobs,const std::string &,const std::string & bindir,const std::string & projectName,const std::vector<std::string> & targets,std::string & output,const std::string & makeCommandCSTR,const std::string & config,bool clean,bool fast,bool verbose,cmDuration timeout,cmSystemTools::OutputOption outputflag,std::vector<std::string> const & nativeOptions)1952 int cmGlobalGenerator::Build(
1953 int jobs, const std::string& /*unused*/, const std::string& bindir,
1954 const std::string& projectName, const std::vector<std::string>& targets,
1955 std::string& output, const std::string& makeCommandCSTR,
1956 const std::string& config, bool clean, bool fast, bool verbose,
1957 cmDuration timeout, cmSystemTools::OutputOption outputflag,
1958 std::vector<std::string> const& nativeOptions)
1959 {
1960 bool hideconsole = cmSystemTools::GetRunCommandHideConsole();
1961
1962 /**
1963 * Run an executable command and put the stdout in output.
1964 */
1965 cmWorkingDirectory workdir(bindir);
1966 output += "Change Dir: ";
1967 output += bindir;
1968 output += "\n";
1969 if (workdir.Failed()) {
1970 cmSystemTools::SetRunCommandHideConsole(hideconsole);
1971 std::string err = cmStrCat("Failed to change directory: ",
1972 std::strerror(workdir.GetLastResult()));
1973 cmSystemTools::Error(err);
1974 output += err;
1975 output += "\n";
1976 return 1;
1977 }
1978 std::string realConfig = config;
1979 if (realConfig.empty()) {
1980 realConfig = this->GetDefaultBuildConfig();
1981 }
1982
1983 int retVal = 0;
1984 cmSystemTools::SetRunCommandHideConsole(true);
1985 std::string outputBuffer;
1986 std::string* outputPtr = &outputBuffer;
1987
1988 std::vector<GeneratedMakeCommand> makeCommand =
1989 this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, targets,
1990 realConfig, fast, jobs, verbose, nativeOptions);
1991
1992 // Workaround to convince some commands to produce output.
1993 if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH &&
1994 makeCommand.back().RequiresOutputForward) {
1995 outputflag = cmSystemTools::OUTPUT_FORWARD;
1996 }
1997
1998 // should we do a clean first?
1999 if (clean) {
2000 std::vector<GeneratedMakeCommand> cleanCommand =
2001 this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir,
2002 { "clean" }, realConfig, fast, jobs, verbose);
2003 output += "\nRun Clean Command:";
2004 output += cleanCommand.front().Printable();
2005 output += "\n";
2006 if (cleanCommand.size() != 1) {
2007 this->GetCMakeInstance()->IssueMessage(MessageType::INTERNAL_ERROR,
2008 "The generator did not produce "
2009 "exactly one command for the "
2010 "'clean' target");
2011 return 1;
2012 }
2013 if (!cmSystemTools::RunSingleCommand(cleanCommand.front().PrimaryCommand,
2014 outputPtr, outputPtr, &retVal,
2015 nullptr, outputflag, timeout)) {
2016 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2017 cmSystemTools::Error("Generator: execution of make clean failed.");
2018 output += *outputPtr;
2019 output += "\nGenerator: execution of make clean failed.\n";
2020
2021 return 1;
2022 }
2023 output += *outputPtr;
2024 }
2025
2026 // now build
2027 std::string makeCommandStr;
2028 output += "\nRun Build Command(s):";
2029
2030 retVal = 0;
2031 for (auto command = makeCommand.begin();
2032 command != makeCommand.end() && retVal == 0; ++command) {
2033 makeCommandStr = command->Printable();
2034 if (command != makeCommand.end()) {
2035 makeCommandStr += " && ";
2036 }
2037
2038 output += makeCommandStr;
2039 if (!cmSystemTools::RunSingleCommand(command->PrimaryCommand, outputPtr,
2040 outputPtr, &retVal, nullptr,
2041 outputflag, timeout)) {
2042 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2043 cmSystemTools::Error(
2044 "Generator: execution of make failed. Make command was: " +
2045 makeCommandStr);
2046 output += *outputPtr;
2047 output += "\nGenerator: execution of make failed. Make command was: " +
2048 makeCommandStr + "\n";
2049
2050 return 1;
2051 }
2052 output += *outputPtr;
2053 }
2054 output += "\n";
2055 cmSystemTools::SetRunCommandHideConsole(hideconsole);
2056
2057 // The OpenWatcom tools do not return an error code when a link
2058 // library is not found!
2059 if (this->CMakeInstance->GetState()->UseWatcomWMake() && retVal == 0 &&
2060 output.find("W1008: cannot open") != std::string::npos) {
2061 retVal = 1;
2062 }
2063
2064 return retVal;
2065 }
2066
Open(const std::string & bindir,const std::string & projectName,bool dryRun)2067 bool cmGlobalGenerator::Open(const std::string& bindir,
2068 const std::string& projectName, bool dryRun)
2069 {
2070 if (this->ExtraGenerator) {
2071 return this->ExtraGenerator->Open(bindir, projectName, dryRun);
2072 }
2073
2074 return false;
2075 }
2076
GenerateCMakeBuildCommand(const std::string & target,const std::string & config,const std::string & parallel,const std::string & native,bool ignoreErrors)2077 std::string cmGlobalGenerator::GenerateCMakeBuildCommand(
2078 const std::string& target, const std::string& config,
2079 const std::string& parallel, const std::string& native, bool ignoreErrors)
2080 {
2081 std::string makeCommand = cmSystemTools::GetCMakeCommand();
2082 makeCommand =
2083 cmStrCat(cmSystemTools::ConvertToOutputPath(makeCommand), " --build .");
2084 if (!config.empty()) {
2085 makeCommand += " --config \"";
2086 makeCommand += config;
2087 makeCommand += "\"";
2088 }
2089 if (!parallel.empty()) {
2090 makeCommand += " --parallel \"";
2091 makeCommand += parallel;
2092 makeCommand += "\"";
2093 }
2094 if (!target.empty()) {
2095 makeCommand += " --target \"";
2096 makeCommand += target;
2097 makeCommand += "\"";
2098 }
2099 const char* sep = " -- ";
2100 if (ignoreErrors) {
2101 const char* iflag = this->GetBuildIgnoreErrorsFlag();
2102 if (iflag && *iflag) {
2103 makeCommand += sep;
2104 makeCommand += iflag;
2105 sep = " ";
2106 }
2107 }
2108 if (!native.empty()) {
2109 makeCommand += sep;
2110 makeCommand += native;
2111 }
2112 return makeCommand;
2113 }
2114
AddMakefile(std::unique_ptr<cmMakefile> mf)2115 void cmGlobalGenerator::AddMakefile(std::unique_ptr<cmMakefile> mf)
2116 {
2117 this->IndexMakefile(mf.get());
2118 this->Makefiles.push_back(std::move(mf));
2119
2120 // update progress
2121 // estimate how many lg there will be
2122 cmValue numGenC = this->CMakeInstance->GetState()->GetInitializedCacheValue(
2123 "CMAKE_NUMBER_OF_MAKEFILES");
2124
2125 if (!numGenC) {
2126 // If CMAKE_NUMBER_OF_MAKEFILES is not set
2127 // we are in the first time progress and we have no
2128 // idea how long it will be. So, just move half way
2129 // there each time, and don't go over 95%
2130 this->FirstTimeProgress += ((1.0f - this->FirstTimeProgress) / 30.0f);
2131 if (this->FirstTimeProgress > 0.95f) {
2132 this->FirstTimeProgress = 0.95f;
2133 }
2134 this->CMakeInstance->UpdateProgress("Configuring",
2135 this->FirstTimeProgress);
2136 return;
2137 }
2138
2139 int numGen = atoi(numGenC->c_str());
2140 float prog =
2141 static_cast<float>(this->Makefiles.size()) / static_cast<float>(numGen);
2142 if (prog > 1.0f) {
2143 prog = 1.0f;
2144 }
2145 this->CMakeInstance->UpdateProgress("Configuring", prog);
2146 }
2147
AddInstallComponent(const std::string & component)2148 void cmGlobalGenerator::AddInstallComponent(const std::string& component)
2149 {
2150 if (!component.empty()) {
2151 this->InstallComponents.insert(component);
2152 }
2153 }
2154
MarkAsGeneratedFile(const std::string & filepath)2155 void cmGlobalGenerator::MarkAsGeneratedFile(const std::string& filepath)
2156 {
2157 this->GeneratedFiles.insert(filepath);
2158 }
2159
IsGeneratedFile(const std::string & filepath)2160 bool cmGlobalGenerator::IsGeneratedFile(const std::string& filepath)
2161 {
2162 return this->GeneratedFiles.find(filepath) != this->GeneratedFiles.end();
2163 }
2164
EnableInstallTarget()2165 void cmGlobalGenerator::EnableInstallTarget()
2166 {
2167 this->InstallTargetEnabled = true;
2168 }
2169
CreateLocalGenerator(cmMakefile * mf)2170 std::unique_ptr<cmLocalGenerator> cmGlobalGenerator::CreateLocalGenerator(
2171 cmMakefile* mf)
2172 {
2173 return cm::make_unique<cmLocalGenerator>(this, mf);
2174 }
2175
EnableLanguagesFromGenerator(cmGlobalGenerator * gen,cmMakefile * mf)2176 void cmGlobalGenerator::EnableLanguagesFromGenerator(cmGlobalGenerator* gen,
2177 cmMakefile* mf)
2178 {
2179 this->SetConfiguredFilesPath(gen);
2180 this->TryCompileOuterMakefile = mf;
2181 cmValue make =
2182 gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
2183 this->GetCMakeInstance()->AddCacheEntry(
2184 "CMAKE_MAKE_PROGRAM", make, "make program", cmStateEnums::FILEPATH);
2185 // copy the enabled languages
2186 this->GetCMakeInstance()->GetState()->SetEnabledLanguages(
2187 gen->GetCMakeInstance()->GetState()->GetEnabledLanguages());
2188 this->LanguagesReady = gen->LanguagesReady;
2189 this->ExtensionToLanguage = gen->ExtensionToLanguage;
2190 this->IgnoreExtensions = gen->IgnoreExtensions;
2191 this->LanguageToOutputExtension = gen->LanguageToOutputExtension;
2192 this->LanguageToLinkerPreference = gen->LanguageToLinkerPreference;
2193 this->OutputExtensions = gen->OutputExtensions;
2194 }
2195
SetConfiguredFilesPath(cmGlobalGenerator * gen)2196 void cmGlobalGenerator::SetConfiguredFilesPath(cmGlobalGenerator* gen)
2197 {
2198 if (!gen->ConfiguredFilesPath.empty()) {
2199 this->ConfiguredFilesPath = gen->ConfiguredFilesPath;
2200 } else {
2201 this->ConfiguredFilesPath =
2202 cmStrCat(gen->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
2203 }
2204 }
2205
IsExcluded(cmStateSnapshot const & rootSnp,cmStateSnapshot const & snp_) const2206 bool cmGlobalGenerator::IsExcluded(cmStateSnapshot const& rootSnp,
2207 cmStateSnapshot const& snp_) const
2208 {
2209 cmStateSnapshot snp = snp_;
2210 while (snp.IsValid()) {
2211 if (snp == rootSnp) {
2212 // No directory excludes itself.
2213 return false;
2214 }
2215
2216 if (snp.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
2217 // This directory is excluded from its parent.
2218 return true;
2219 }
2220 snp = snp.GetBuildsystemDirectoryParent();
2221 }
2222 return false;
2223 }
2224
IsExcluded(cmLocalGenerator * root,cmLocalGenerator * gen) const2225 bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
2226 cmLocalGenerator* gen) const
2227 {
2228 assert(gen);
2229
2230 cmStateSnapshot rootSnp = root->GetStateSnapshot();
2231 cmStateSnapshot snp = gen->GetStateSnapshot();
2232
2233 return this->IsExcluded(rootSnp, snp);
2234 }
2235
IsExcluded(cmLocalGenerator * root,const cmGeneratorTarget * target) const2236 bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
2237 const cmGeneratorTarget* target) const
2238 {
2239 if (!target->IsInBuildSystem()) {
2240 return true;
2241 }
2242 cmMakefile* mf = root->GetMakefile();
2243 const std::string EXCLUDE_FROM_ALL = "EXCLUDE_FROM_ALL";
2244 if (cmValue exclude = target->GetProperty(EXCLUDE_FROM_ALL)) {
2245 // Expand the property value per configuration.
2246 unsigned int trueCount = 0;
2247 unsigned int falseCount = 0;
2248 const std::vector<std::string>& configs =
2249 mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2250 for (const std::string& config : configs) {
2251 cmGeneratorExpressionInterpreter genexInterpreter(root, config, target);
2252 if (cmIsOn(genexInterpreter.Evaluate(*exclude, EXCLUDE_FROM_ALL))) {
2253 ++trueCount;
2254 } else {
2255 ++falseCount;
2256 }
2257 }
2258
2259 // Check whether the genex expansion of the property agrees in all
2260 // configurations.
2261 if (trueCount > 0 && falseCount > 0) {
2262 std::ostringstream e;
2263 e << "The EXCLUDE_FROM_ALL property of target \"" << target->GetName()
2264 << "\" varies by configuration. This is not supported by the \""
2265 << root->GetGlobalGenerator()->GetName() << "\" generator.";
2266 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
2267 }
2268 return trueCount;
2269 }
2270 // This target is included in its directory. Check whether the
2271 // directory is excluded.
2272 return this->IsExcluded(root, target->GetLocalGenerator());
2273 }
2274
GetEnabledLanguages(std::vector<std::string> & lang) const2275 void cmGlobalGenerator::GetEnabledLanguages(
2276 std::vector<std::string>& lang) const
2277 {
2278 lang = this->CMakeInstance->GetState()->GetEnabledLanguages();
2279 }
2280
GetLinkerPreference(const std::string & lang) const2281 int cmGlobalGenerator::GetLinkerPreference(const std::string& lang) const
2282 {
2283 auto const it = this->LanguageToLinkerPreference.find(lang);
2284 if (it != this->LanguageToLinkerPreference.end()) {
2285 return it->second;
2286 }
2287 return 0;
2288 }
2289
FillProjectMap()2290 void cmGlobalGenerator::FillProjectMap()
2291 {
2292 this->ProjectMap.clear(); // make sure we start with a clean map
2293 for (const auto& localGen : this->LocalGenerators) {
2294 // for each local generator add all projects
2295 cmStateSnapshot snp = localGen->GetStateSnapshot();
2296 std::string name;
2297 do {
2298 std::string snpProjName = snp.GetProjectName();
2299 if (name != snpProjName) {
2300 name = snpProjName;
2301 this->ProjectMap[name].push_back(localGen.get());
2302 }
2303 snp = snp.GetBuildsystemDirectoryParent();
2304 } while (snp.IsValid());
2305 }
2306 }
2307
FindMakefile(const std::string & start_dir) const2308 cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const
2309 {
2310 auto const it = this->MakefileSearchIndex.find(start_dir);
2311 if (it != this->MakefileSearchIndex.end()) {
2312 return it->second;
2313 }
2314 return nullptr;
2315 }
2316
FindLocalGenerator(cmDirectoryId const & id) const2317 cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(
2318 cmDirectoryId const& id) const
2319 {
2320 auto const it = this->LocalGeneratorSearchIndex.find(id.String);
2321 if (it != this->LocalGeneratorSearchIndex.end()) {
2322 return it->second;
2323 }
2324 return nullptr;
2325 }
2326
AddAlias(const std::string & name,std::string const & tgtName)2327 void cmGlobalGenerator::AddAlias(const std::string& name,
2328 std::string const& tgtName)
2329 {
2330 this->AliasTargets[name] = tgtName;
2331 }
2332
IsAlias(const std::string & name) const2333 bool cmGlobalGenerator::IsAlias(const std::string& name) const
2334 {
2335 return cm::contains(this->AliasTargets, name);
2336 }
2337
IndexTarget(cmTarget * t)2338 void cmGlobalGenerator::IndexTarget(cmTarget* t)
2339 {
2340 if (!t->IsImported() || t->IsImportedGloballyVisible()) {
2341 this->TargetSearchIndex[t->GetName()] = t;
2342 }
2343 }
2344
IndexGeneratorTarget(cmGeneratorTarget * gt)2345 void cmGlobalGenerator::IndexGeneratorTarget(cmGeneratorTarget* gt)
2346 {
2347 if (!gt->IsImported() || gt->IsImportedGloballyVisible()) {
2348 this->GeneratorTargetSearchIndex[gt->GetName()] = gt;
2349 }
2350 }
2351
2352 static char const hexDigits[] = "0123456789abcdef";
2353
IndexGeneratorTargetUniquely(cmGeneratorTarget const * gt)2354 std::string cmGlobalGenerator::IndexGeneratorTargetUniquely(
2355 cmGeneratorTarget const* gt)
2356 {
2357 // Use the pointer value to uniquely identify the target instance.
2358 // Use a ":" prefix to avoid conflict with project-defined targets.
2359 // We must satisfy cmGeneratorExpression::IsValidTargetName so use no
2360 // other special characters.
2361 constexpr size_t sizeof_ptr =
2362 sizeof(gt); // NOLINT(bugprone-sizeof-expression)
2363 char buf[1 + sizeof_ptr * 2];
2364 char* b = buf;
2365 *b++ = ':';
2366 for (size_t i = 0; i < sizeof_ptr; ++i) {
2367 unsigned char const c = reinterpret_cast<unsigned char const*>(>)[i];
2368 *b++ = hexDigits[(c & 0xf0) >> 4];
2369 *b++ = hexDigits[(c & 0x0f)];
2370 }
2371 std::string id(buf, sizeof(buf));
2372 // We internally index pointers to non-const generator targets
2373 // but our callers only have pointers to const generator targets.
2374 // They will give up non-const privileges when looking up anyway.
2375 this->GeneratorTargetSearchIndex[id] = const_cast<cmGeneratorTarget*>(gt);
2376 return id;
2377 }
2378
IndexMakefile(cmMakefile * mf)2379 void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
2380 {
2381 // We index by both source and binary directory. add_subdirectory
2382 // supports multiple build directories sharing the same source directory.
2383 // The source directory index will reference only the first time it is used.
2384 this->MakefileSearchIndex.insert(
2385 MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf));
2386 this->MakefileSearchIndex.insert(
2387 MakefileMap::value_type(mf->GetCurrentBinaryDirectory(), mf));
2388 }
2389
IndexLocalGenerator(cmLocalGenerator * lg)2390 void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg)
2391 {
2392 cmDirectoryId id = lg->GetMakefile()->GetDirectoryId();
2393 this->LocalGeneratorSearchIndex[id.String] = lg;
2394 }
2395
FindTargetImpl(std::string const & name) const2396 cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const
2397 {
2398 auto const it = this->TargetSearchIndex.find(name);
2399 if (it != this->TargetSearchIndex.end()) {
2400 return it->second;
2401 }
2402 return nullptr;
2403 }
2404
FindGeneratorTargetImpl(std::string const & name) const2405 cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTargetImpl(
2406 std::string const& name) const
2407 {
2408 auto const it = this->GeneratorTargetSearchIndex.find(name);
2409 if (it != this->GeneratorTargetSearchIndex.end()) {
2410 return it->second;
2411 }
2412 return nullptr;
2413 }
2414
FindTarget(const std::string & name,bool excludeAliases) const2415 cmTarget* cmGlobalGenerator::FindTarget(const std::string& name,
2416 bool excludeAliases) const
2417 {
2418 if (!excludeAliases) {
2419 auto const ai = this->AliasTargets.find(name);
2420 if (ai != this->AliasTargets.end()) {
2421 return this->FindTargetImpl(ai->second);
2422 }
2423 }
2424 return this->FindTargetImpl(name);
2425 }
2426
FindGeneratorTarget(const std::string & name) const2427 cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTarget(
2428 const std::string& name) const
2429 {
2430 auto const ai = this->AliasTargets.find(name);
2431 if (ai != this->AliasTargets.end()) {
2432 return this->FindGeneratorTargetImpl(ai->second);
2433 }
2434 return this->FindGeneratorTargetImpl(name);
2435 }
2436
NameResolvesToFramework(const std::string & libname) const2437 bool cmGlobalGenerator::NameResolvesToFramework(
2438 const std::string& libname) const
2439 {
2440 if (cmSystemTools::IsPathToFramework(libname)) {
2441 return true;
2442 }
2443
2444 if (cmTarget* tgt = this->FindTarget(libname)) {
2445 if (tgt->IsFrameworkOnApple()) {
2446 return true;
2447 }
2448 }
2449
2450 return false;
2451 }
2452
CheckCMP0037(std::string const & targetName,std::string const & reason) const2453 bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
2454 std::string const& reason) const
2455 {
2456 cmTarget* tgt = this->FindTarget(targetName);
2457 if (!tgt) {
2458 return true;
2459 }
2460 MessageType messageType = MessageType::AUTHOR_WARNING;
2461 std::ostringstream e;
2462 bool issueMessage = false;
2463 switch (tgt->GetPolicyStatusCMP0037()) {
2464 case cmPolicies::WARN:
2465 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n";
2466 issueMessage = true;
2467 CM_FALLTHROUGH;
2468 case cmPolicies::OLD:
2469 break;
2470 case cmPolicies::NEW:
2471 case cmPolicies::REQUIRED_IF_USED:
2472 case cmPolicies::REQUIRED_ALWAYS:
2473 issueMessage = true;
2474 messageType = MessageType::FATAL_ERROR;
2475 break;
2476 }
2477 if (issueMessage) {
2478 e << "The target name \"" << targetName << "\" is reserved " << reason
2479 << ".";
2480 if (messageType == MessageType::AUTHOR_WARNING) {
2481 e << " It may result in undefined behavior.";
2482 }
2483 this->GetCMakeInstance()->IssueMessage(messageType, e.str(),
2484 tgt->GetBacktrace());
2485 if (messageType == MessageType::FATAL_ERROR) {
2486 return false;
2487 }
2488 }
2489 return true;
2490 }
2491
CreateDefaultGlobalTargets(std::vector<GlobalTargetInfo> & targets)2492 void cmGlobalGenerator::CreateDefaultGlobalTargets(
2493 std::vector<GlobalTargetInfo>& targets)
2494 {
2495 this->AddGlobalTarget_Package(targets);
2496 this->AddGlobalTarget_PackageSource(targets);
2497 this->AddGlobalTarget_Test(targets);
2498 this->AddGlobalTarget_EditCache(targets);
2499 this->AddGlobalTarget_RebuildCache(targets);
2500 this->AddGlobalTarget_Install(targets);
2501 }
2502
AddGlobalTarget_Package(std::vector<GlobalTargetInfo> & targets)2503 void cmGlobalGenerator::AddGlobalTarget_Package(
2504 std::vector<GlobalTargetInfo>& targets)
2505 {
2506 auto& mf = this->Makefiles[0];
2507 std::string configFile =
2508 cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackConfig.cmake");
2509 if (!cmSystemTools::FileExists(configFile)) {
2510 return;
2511 }
2512
2513 static const auto reservedTargets = { "package", "PACKAGE" };
2514 for (auto const& target : reservedTargets) {
2515 if (!this->CheckCMP0037(target, "when CPack packaging is enabled")) {
2516 return;
2517 }
2518 }
2519
2520 const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
2521 GlobalTargetInfo gti;
2522 gti.Name = this->GetPackageTargetName();
2523 gti.Message = "Run CPack packaging tool...";
2524 gti.UsesTerminal = true;
2525 gti.WorkingDir = mf->GetCurrentBinaryDirectory();
2526 cmCustomCommandLine singleLine;
2527 singleLine.push_back(cmSystemTools::GetCPackCommand());
2528 if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') {
2529 singleLine.push_back("-C");
2530 singleLine.push_back(cmakeCfgIntDir);
2531 }
2532 singleLine.push_back("--config");
2533 singleLine.push_back("./CPackConfig.cmake");
2534 gti.CommandLines.push_back(std::move(singleLine));
2535 if (this->GetPreinstallTargetName()) {
2536 gti.Depends.emplace_back(this->GetPreinstallTargetName());
2537 } else {
2538 cmValue noPackageAll =
2539 mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY");
2540 if (cmIsOff(noPackageAll)) {
2541 gti.Depends.emplace_back(this->GetAllTargetName());
2542 }
2543 }
2544 targets.push_back(std::move(gti));
2545 }
2546
AddGlobalTarget_PackageSource(std::vector<GlobalTargetInfo> & targets)2547 void cmGlobalGenerator::AddGlobalTarget_PackageSource(
2548 std::vector<GlobalTargetInfo>& targets)
2549 {
2550 const char* packageSourceTargetName = this->GetPackageSourceTargetName();
2551 if (!packageSourceTargetName) {
2552 return;
2553 }
2554
2555 auto& mf = this->Makefiles[0];
2556 std::string configFile =
2557 cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackSourceConfig.cmake");
2558 if (!cmSystemTools::FileExists(configFile)) {
2559 return;
2560 }
2561
2562 static const auto reservedTargets = { "package_source" };
2563 for (auto const& target : reservedTargets) {
2564 if (!this->CheckCMP0037(target,
2565 "when CPack source packaging is enabled")) {
2566 return;
2567 }
2568 }
2569
2570 GlobalTargetInfo gti;
2571 gti.Name = packageSourceTargetName;
2572 gti.Message = "Run CPack packaging tool for source...";
2573 gti.WorkingDir = mf->GetCurrentBinaryDirectory();
2574 gti.UsesTerminal = true;
2575 cmCustomCommandLine singleLine;
2576 singleLine.push_back(cmSystemTools::GetCPackCommand());
2577 singleLine.push_back("--config");
2578 singleLine.push_back("./CPackSourceConfig.cmake");
2579 singleLine.push_back(std::move(configFile));
2580 gti.CommandLines.push_back(std::move(singleLine));
2581 targets.push_back(std::move(gti));
2582 }
2583
AddGlobalTarget_Test(std::vector<GlobalTargetInfo> & targets)2584 void cmGlobalGenerator::AddGlobalTarget_Test(
2585 std::vector<GlobalTargetInfo>& targets)
2586 {
2587 auto& mf = this->Makefiles[0];
2588 if (!mf->IsOn("CMAKE_TESTING_ENABLED")) {
2589 return;
2590 }
2591
2592 static const auto reservedTargets = { "test", "RUN_TESTS" };
2593 for (auto const& target : reservedTargets) {
2594 if (!this->CheckCMP0037(target, "when CTest testing is enabled")) {
2595 return;
2596 }
2597 }
2598
2599 const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
2600 GlobalTargetInfo gti;
2601 gti.Name = this->GetTestTargetName();
2602 gti.Message = "Running tests...";
2603 gti.UsesTerminal = true;
2604 cmCustomCommandLine singleLine;
2605 singleLine.push_back(cmSystemTools::GetCTestCommand());
2606 singleLine.push_back("--force-new-ctest-process");
2607 std::vector<std::string> args;
2608 if (mf->GetDefExpandList("CMAKE_CTEST_ARGUMENTS", args)) {
2609 for (auto const& arg : args) {
2610 singleLine.push_back(arg);
2611 }
2612 }
2613 if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') {
2614 singleLine.push_back("-C");
2615 singleLine.push_back(cmakeCfgIntDir);
2616 } else // TODO: This is a hack. Should be something to do with the
2617 // generator
2618 {
2619 singleLine.push_back("$(ARGS)");
2620 }
2621 gti.CommandLines.push_back(std::move(singleLine));
2622 targets.push_back(std::move(gti));
2623 }
2624
AddGlobalTarget_EditCache(std::vector<GlobalTargetInfo> & targets) const2625 void cmGlobalGenerator::AddGlobalTarget_EditCache(
2626 std::vector<GlobalTargetInfo>& targets) const
2627 {
2628 const char* editCacheTargetName = this->GetEditCacheTargetName();
2629 if (!editCacheTargetName) {
2630 return;
2631 }
2632 GlobalTargetInfo gti;
2633 gti.Name = editCacheTargetName;
2634 gti.PerConfig = cmTarget::PerConfig::No;
2635 cmCustomCommandLine singleLine;
2636
2637 // Use generator preference for the edit_cache rule if it is defined.
2638 std::string edit_cmd = this->GetEditCacheCommand();
2639 if (!edit_cmd.empty()) {
2640 singleLine.push_back(std::move(edit_cmd));
2641 singleLine.push_back("-S$(CMAKE_SOURCE_DIR)");
2642 singleLine.push_back("-B$(CMAKE_BINARY_DIR)");
2643 gti.Message = "Running CMake cache editor...";
2644 gti.UsesTerminal = true;
2645 } else {
2646 singleLine.push_back(cmSystemTools::GetCMakeCommand());
2647 singleLine.push_back("-E");
2648 singleLine.push_back("echo");
2649 singleLine.push_back("No interactive CMake dialog available.");
2650 gti.Message = "No interactive CMake dialog available...";
2651 gti.UsesTerminal = false;
2652 gti.StdPipesUTF8 = true;
2653 }
2654 gti.CommandLines.push_back(std::move(singleLine));
2655
2656 targets.push_back(std::move(gti));
2657 }
2658
AddGlobalTarget_RebuildCache(std::vector<GlobalTargetInfo> & targets) const2659 void cmGlobalGenerator::AddGlobalTarget_RebuildCache(
2660 std::vector<GlobalTargetInfo>& targets) const
2661 {
2662 const char* rebuildCacheTargetName = this->GetRebuildCacheTargetName();
2663 if (!rebuildCacheTargetName) {
2664 return;
2665 }
2666 GlobalTargetInfo gti;
2667 gti.Name = rebuildCacheTargetName;
2668 gti.Message = "Running CMake to regenerate build system...";
2669 gti.UsesTerminal = true;
2670 gti.PerConfig = cmTarget::PerConfig::No;
2671 cmCustomCommandLine singleLine;
2672 singleLine.push_back(cmSystemTools::GetCMakeCommand());
2673 singleLine.push_back("--regenerate-during-build");
2674 singleLine.push_back("-S$(CMAKE_SOURCE_DIR)");
2675 singleLine.push_back("-B$(CMAKE_BINARY_DIR)");
2676 gti.CommandLines.push_back(std::move(singleLine));
2677 gti.StdPipesUTF8 = true;
2678 targets.push_back(std::move(gti));
2679 }
2680
AddGlobalTarget_Install(std::vector<GlobalTargetInfo> & targets)2681 void cmGlobalGenerator::AddGlobalTarget_Install(
2682 std::vector<GlobalTargetInfo>& targets)
2683 {
2684 auto& mf = this->Makefiles[0];
2685 const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
2686 bool skipInstallRules = mf->IsOn("CMAKE_SKIP_INSTALL_RULES");
2687 if (this->InstallTargetEnabled && skipInstallRules) {
2688 this->CMakeInstance->IssueMessage(
2689 MessageType::WARNING,
2690 "CMAKE_SKIP_INSTALL_RULES was enabled even though "
2691 "installation rules have been specified",
2692 mf->GetBacktrace());
2693 } else if (this->InstallTargetEnabled && !skipInstallRules) {
2694 if (!(cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.')) {
2695 std::set<std::string>* componentsSet = &this->InstallComponents;
2696 std::ostringstream ostr;
2697 if (!componentsSet->empty()) {
2698 ostr << "Available install components are: ";
2699 ostr << cmWrap('"', *componentsSet, '"', " ");
2700 } else {
2701 ostr << "Only default component available";
2702 }
2703 GlobalTargetInfo gti;
2704 gti.Name = "list_install_components";
2705 gti.Message = ostr.str();
2706 gti.UsesTerminal = false;
2707 targets.push_back(std::move(gti));
2708 }
2709 std::string cmd = cmSystemTools::GetCMakeCommand();
2710 GlobalTargetInfo gti;
2711 gti.Name = this->GetInstallTargetName();
2712 gti.Message = "Install the project...";
2713 gti.UsesTerminal = true;
2714 gti.StdPipesUTF8 = true;
2715 cmCustomCommandLine singleLine;
2716 if (this->GetPreinstallTargetName()) {
2717 gti.Depends.emplace_back(this->GetPreinstallTargetName());
2718 } else {
2719 cmValue noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
2720 if (cmIsOff(noall)) {
2721 gti.Depends.emplace_back(this->GetAllTargetName());
2722 }
2723 }
2724 if (mf->GetDefinition("CMake_BINARY_DIR") &&
2725 !mf->IsOn("CMAKE_CROSSCOMPILING")) {
2726 // We are building CMake itself. We cannot use the original
2727 // executable to install over itself. The generator will
2728 // automatically convert this name to the build-time location.
2729 cmd = "cmake";
2730 }
2731 singleLine.push_back(cmd);
2732 if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') {
2733 std::string cfgArg = "-DBUILD_TYPE=";
2734 bool useEPN = this->UseEffectivePlatformName(mf.get());
2735 if (useEPN) {
2736 cfgArg += "$(CONFIGURATION)";
2737 singleLine.push_back(cfgArg);
2738 cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)";
2739 } else {
2740 cfgArg += *mf->GetDefinition("CMAKE_CFG_INTDIR");
2741 }
2742 singleLine.push_back(cfgArg);
2743 }
2744 singleLine.push_back("-P");
2745 singleLine.push_back("cmake_install.cmake");
2746 gti.CommandLines.push_back(singleLine);
2747 targets.push_back(gti);
2748
2749 // install_local
2750 if (const char* install_local = this->GetInstallLocalTargetName()) {
2751 gti.Name = install_local;
2752 gti.Message = "Installing only the local directory...";
2753 gti.UsesTerminal = true;
2754 gti.CommandLines.clear();
2755
2756 cmCustomCommandLine localCmdLine = singleLine;
2757
2758 localCmdLine.insert(localCmdLine.begin() + 1,
2759 "-DCMAKE_INSTALL_LOCAL_ONLY=1");
2760
2761 gti.CommandLines.push_back(std::move(localCmdLine));
2762 targets.push_back(gti);
2763 }
2764
2765 // install_strip
2766 const char* install_strip = this->GetInstallStripTargetName();
2767 if ((install_strip != nullptr) && (mf->IsSet("CMAKE_STRIP"))) {
2768 gti.Name = install_strip;
2769 gti.Message = "Installing the project stripped...";
2770 gti.UsesTerminal = true;
2771 gti.CommandLines.clear();
2772
2773 cmCustomCommandLine stripCmdLine = singleLine;
2774
2775 stripCmdLine.insert(stripCmdLine.begin() + 1,
2776 "-DCMAKE_INSTALL_DO_STRIP=1");
2777 gti.CommandLines.push_back(std::move(stripCmdLine));
2778 targets.push_back(gti);
2779 }
2780 }
2781 }
2782
GetPredefinedTargetsFolder() const2783 std::string cmGlobalGenerator::GetPredefinedTargetsFolder() const
2784 {
2785 cmValue prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
2786 "PREDEFINED_TARGETS_FOLDER");
2787
2788 if (prop) {
2789 return *prop;
2790 }
2791
2792 return "CMakePredefinedTargets";
2793 }
2794
UseFolderProperty() const2795 bool cmGlobalGenerator::UseFolderProperty() const
2796 {
2797 cmValue prop =
2798 this->GetCMakeInstance()->GetState()->GetGlobalProperty("USE_FOLDERS");
2799
2800 // If this property is defined, let the setter turn this on or off...
2801 //
2802 if (prop) {
2803 return cmIsOn(*prop);
2804 }
2805
2806 // By default, this feature is OFF, since it is not supported in the
2807 // Visual Studio Express editions until VS11:
2808 //
2809 return false;
2810 }
2811
CreateGlobalTarget(GlobalTargetInfo const & gti,cmMakefile * mf)2812 void cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti,
2813 cmMakefile* mf)
2814 {
2815 // Package
2816 auto tb =
2817 mf->CreateNewTarget(gti.Name, cmStateEnums::GLOBAL_TARGET, gti.PerConfig);
2818
2819 // Do nothing if gti.Name is already used
2820 if (!tb.second) {
2821 return;
2822 }
2823
2824 cmTarget& target = tb.first;
2825 target.SetProperty("EXCLUDE_FROM_ALL", "TRUE");
2826
2827 std::vector<std::string> no_outputs;
2828 std::vector<std::string> no_byproducts;
2829 std::vector<std::string> no_depends;
2830 // Store the custom command in the target.
2831 cmCustomCommand cc(no_outputs, no_byproducts, no_depends, gti.CommandLines,
2832 cmListFileBacktrace(), nullptr, gti.WorkingDir.c_str(),
2833 gti.StdPipesUTF8);
2834 cc.SetUsesTerminal(gti.UsesTerminal);
2835 target.AddPostBuildCommand(std::move(cc));
2836 if (!gti.Message.empty()) {
2837 target.SetProperty("EchoString", gti.Message);
2838 }
2839 for (std::string const& d : gti.Depends) {
2840 target.AddUtility(d, false);
2841 }
2842
2843 // Organize in the "predefined targets" folder:
2844 //
2845 if (this->UseFolderProperty()) {
2846 target.SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
2847 }
2848 }
2849
GenerateRuleFile(std::string const & output) const2850 std::string cmGlobalGenerator::GenerateRuleFile(
2851 std::string const& output) const
2852 {
2853 std::string ruleFile = cmStrCat(output, ".rule");
2854 const char* dir = this->GetCMakeCFGIntDir();
2855 if (dir && dir[0] == '$') {
2856 cmSystemTools::ReplaceString(ruleFile, dir, "/CMakeFiles");
2857 }
2858 return ruleFile;
2859 }
2860
ShouldStripResourcePath(cmMakefile * mf) const2861 bool cmGlobalGenerator::ShouldStripResourcePath(cmMakefile* mf) const
2862 {
2863 return mf->PlatformIsAppleEmbedded();
2864 }
2865
GetSharedLibFlagsForLanguage(std::string const & l) const2866 std::string cmGlobalGenerator::GetSharedLibFlagsForLanguage(
2867 std::string const& l) const
2868 {
2869 auto const it = this->LanguageToOriginalSharedLibFlags.find(l);
2870 if (it != this->LanguageToOriginalSharedLibFlags.end()) {
2871 return it->second;
2872 }
2873 return "";
2874 }
2875
AppendDirectoryForConfig(const std::string &,const std::string &,const std::string &,std::string &)2876 void cmGlobalGenerator::AppendDirectoryForConfig(const std::string& /*unused*/,
2877 const std::string& /*unused*/,
2878 const std::string& /*unused*/,
2879 std::string& /*unused*/)
2880 {
2881 // Subclasses that support multiple configurations should implement
2882 // this method to append the subdirectory for the given build
2883 // configuration.
2884 }
2885
2886 cmGlobalGenerator::TargetDependSet const&
GetTargetDirectDepends(cmGeneratorTarget const * target)2887 cmGlobalGenerator::GetTargetDirectDepends(cmGeneratorTarget const* target)
2888 {
2889 return this->TargetDependencies[target];
2890 }
2891
IsReservedTarget(std::string const & name)2892 bool cmGlobalGenerator::IsReservedTarget(std::string const& name)
2893 {
2894 // The following is a list of targets reserved
2895 // by one or more of the cmake generators.
2896
2897 // Adding additional targets to this list will require a policy!
2898 const char* reservedTargets[] = { "all", "ALL_BUILD", "help",
2899 "install", "INSTALL", "preinstall",
2900 "clean", "edit_cache", "rebuild_cache",
2901 "ZERO_CHECK" };
2902
2903 return cm::contains(reservedTargets, name);
2904 }
2905
SetExternalMakefileProjectGenerator(std::unique_ptr<cmExternalMakefileProjectGenerator> extraGenerator)2906 void cmGlobalGenerator::SetExternalMakefileProjectGenerator(
2907 std::unique_ptr<cmExternalMakefileProjectGenerator> extraGenerator)
2908 {
2909 this->ExtraGenerator = std::move(extraGenerator);
2910 if (this->ExtraGenerator) {
2911 this->ExtraGenerator->SetGlobalGenerator(this);
2912 }
2913 }
2914
GetExtraGeneratorName() const2915 std::string cmGlobalGenerator::GetExtraGeneratorName() const
2916 {
2917 return this->ExtraGenerator ? this->ExtraGenerator->GetName()
2918 : std::string();
2919 }
2920
FileReplacedDuringGenerate(const std::string & filename)2921 void cmGlobalGenerator::FileReplacedDuringGenerate(const std::string& filename)
2922 {
2923 this->FilesReplacedDuringGenerate.push_back(filename);
2924 }
2925
GetFilesReplacedDuringGenerate(std::vector<std::string> & filenames)2926 void cmGlobalGenerator::GetFilesReplacedDuringGenerate(
2927 std::vector<std::string>& filenames)
2928 {
2929 filenames.clear();
2930 std::copy(this->FilesReplacedDuringGenerate.begin(),
2931 this->FilesReplacedDuringGenerate.end(),
2932 std::back_inserter(filenames));
2933 }
2934
GetTargetSets(TargetDependSet & projectTargets,TargetDependSet & originalTargets,cmLocalGenerator * root,std::vector<cmLocalGenerator * > & generators)2935 void cmGlobalGenerator::GetTargetSets(
2936 TargetDependSet& projectTargets, TargetDependSet& originalTargets,
2937 cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
2938 {
2939 // loop over all local generators
2940 for (auto* generator : generators) {
2941 // check to make sure generator is not excluded
2942 if (this->IsExcluded(root, generator)) {
2943 continue;
2944 }
2945 // loop over all the generator targets in the makefile
2946 for (const auto& target : generator->GetGeneratorTargets()) {
2947 if (this->IsRootOnlyTarget(target.get()) &&
2948 target->GetLocalGenerator() != root) {
2949 continue;
2950 }
2951 // put the target in the set of original targets
2952 originalTargets.insert(target.get());
2953 // Get the set of targets that depend on target
2954 this->AddTargetDepends(target.get(), projectTargets);
2955 }
2956 }
2957 }
2958
IsRootOnlyTarget(cmGeneratorTarget * target) const2959 bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const
2960 {
2961 return (target->GetType() == cmStateEnums::GLOBAL_TARGET ||
2962 target->GetName() == this->GetAllTargetName());
2963 }
2964
AddTargetDepends(cmGeneratorTarget const * target,TargetDependSet & projectTargets)2965 void cmGlobalGenerator::AddTargetDepends(cmGeneratorTarget const* target,
2966 TargetDependSet& projectTargets)
2967 {
2968 // add the target itself
2969 if (projectTargets.insert(target).second) {
2970 // This is the first time we have encountered the target.
2971 // Recursively follow its dependencies.
2972 for (auto const& t : this->GetTargetDirectDepends(target)) {
2973 this->AddTargetDepends(t, projectTargets);
2974 }
2975 }
2976 }
2977
AddToManifest(std::string const & f)2978 void cmGlobalGenerator::AddToManifest(std::string const& f)
2979 {
2980 // Add to the content listing for the file's directory.
2981 std::string dir = cmSystemTools::GetFilenamePath(f);
2982 std::string file = cmSystemTools::GetFilenameName(f);
2983 DirectoryContent& dc = this->DirectoryContentMap[dir];
2984 dc.Generated.insert(file);
2985 dc.All.insert(file);
2986 }
2987
GetDirectoryContent(std::string const & dir,bool needDisk)2988 std::set<std::string> const& cmGlobalGenerator::GetDirectoryContent(
2989 std::string const& dir, bool needDisk)
2990 {
2991 DirectoryContent& dc = this->DirectoryContentMap[dir];
2992 if (needDisk) {
2993 long mt = cmSystemTools::ModifiedTime(dir);
2994 if (mt != dc.LastDiskTime) {
2995 // Reset to non-loaded directory content.
2996 dc.All = dc.Generated;
2997
2998 // Load the directory content from disk.
2999 cmsys::Directory d;
3000 if (d.Load(dir)) {
3001 unsigned long n = d.GetNumberOfFiles();
3002 for (unsigned long i = 0; i < n; ++i) {
3003 const char* f = d.GetFile(i);
3004 if (strcmp(f, ".") != 0 && strcmp(f, "..") != 0) {
3005 dc.All.insert(f);
3006 }
3007 }
3008 }
3009 dc.LastDiskTime = mt;
3010 }
3011 }
3012 return dc.All;
3013 }
3014
AddRuleHash(const std::vector<std::string> & outputs,std::string const & content)3015 void cmGlobalGenerator::AddRuleHash(const std::vector<std::string>& outputs,
3016 std::string const& content)
3017 {
3018 #if !defined(CMAKE_BOOTSTRAP)
3019 // Ignore if there are no outputs.
3020 if (outputs.empty()) {
3021 return;
3022 }
3023
3024 // Compute a hash of the rule.
3025 RuleHash hash;
3026 {
3027 cmCryptoHash md5(cmCryptoHash::AlgoMD5);
3028 std::string const md5_hex = md5.HashString(content);
3029 memcpy(hash.Data, md5_hex.c_str(), 32);
3030 }
3031
3032 // Shorten the output name (in expected use case).
3033 std::string fname =
3034 this->LocalGenerators[0]->MaybeRelativeToTopBinDir(outputs[0]);
3035
3036 // Associate the hash with this output.
3037 this->RuleHashes[fname] = hash;
3038 #else
3039 (void)outputs;
3040 (void)content;
3041 #endif
3042 }
3043
CheckRuleHashes()3044 void cmGlobalGenerator::CheckRuleHashes()
3045 {
3046 #if !defined(CMAKE_BOOTSTRAP)
3047 std::string home = this->GetCMakeInstance()->GetHomeOutputDirectory();
3048 std::string pfile = cmStrCat(home, "/CMakeFiles/CMakeRuleHashes.txt");
3049 this->CheckRuleHashes(pfile, home);
3050 this->WriteRuleHashes(pfile);
3051 #endif
3052 }
3053
CheckRuleHashes(std::string const & pfile,std::string const & home)3054 void cmGlobalGenerator::CheckRuleHashes(std::string const& pfile,
3055 std::string const& home)
3056 {
3057 #if defined(_WIN32) || defined(__CYGWIN__)
3058 cmsys::ifstream fin(pfile.c_str(), std::ios::in | std::ios::binary);
3059 #else
3060 cmsys::ifstream fin(pfile.c_str());
3061 #endif
3062 if (!fin) {
3063 return;
3064 }
3065 std::string line;
3066 std::string fname;
3067 while (cmSystemTools::GetLineFromStream(fin, line)) {
3068 // Line format is a 32-byte hex string followed by a space
3069 // followed by a file name (with no escaping).
3070
3071 // Skip blank and comment lines.
3072 if (line.size() < 34 || line[0] == '#') {
3073 continue;
3074 }
3075
3076 // Get the filename.
3077 fname = line.substr(33);
3078
3079 // Look for a hash for this file's rule.
3080 auto const rhi = this->RuleHashes.find(fname);
3081 if (rhi != this->RuleHashes.end()) {
3082 // Compare the rule hash in the file to that we were given.
3083 if (strncmp(line.c_str(), rhi->second.Data, 32) != 0) {
3084 // The rule has changed. Delete the output so it will be
3085 // built again.
3086 fname = cmSystemTools::CollapseFullPath(fname, home);
3087 cmSystemTools::RemoveFile(fname);
3088 }
3089 } else {
3090 // We have no hash for a rule previously listed. This may be a
3091 // case where a user has turned off a build option and might
3092 // want to turn it back on later, so do not delete the file.
3093 // Instead, we keep the rule hash as long as the file exists so
3094 // that if the feature is turned back on and the rule has
3095 // changed the file is still rebuilt.
3096 std::string fpath = cmSystemTools::CollapseFullPath(fname, home);
3097 if (cmSystemTools::FileExists(fpath)) {
3098 RuleHash hash;
3099 memcpy(hash.Data, line.c_str(), 32);
3100 this->RuleHashes[fname] = hash;
3101 }
3102 }
3103 }
3104 }
3105
WriteRuleHashes(std::string const & pfile)3106 void cmGlobalGenerator::WriteRuleHashes(std::string const& pfile)
3107 {
3108 // Now generate a new persistence file with the current hashes.
3109 if (this->RuleHashes.empty()) {
3110 cmSystemTools::RemoveFile(pfile);
3111 } else {
3112 cmGeneratedFileStream fout(pfile);
3113 fout << "# Hashes of file build rules.\n";
3114 for (auto const& rh : this->RuleHashes) {
3115 fout.write(rh.second.Data, 32);
3116 fout << " " << rh.first << "\n";
3117 }
3118 }
3119 }
3120
WriteSummary()3121 void cmGlobalGenerator::WriteSummary()
3122 {
3123 // Record all target directories in a central location.
3124 std::string fname = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
3125 "/CMakeFiles/TargetDirectories.txt");
3126 cmGeneratedFileStream fout(fname);
3127
3128 for (const auto& lg : this->LocalGenerators) {
3129 for (const auto& tgt : lg->GetGeneratorTargets()) {
3130 if (!tgt->IsInBuildSystem()) {
3131 continue;
3132 }
3133 this->WriteSummary(tgt.get());
3134 fout << tgt->GetSupportDirectory() << "\n";
3135 }
3136 }
3137 }
3138
WriteSummary(cmGeneratorTarget * target)3139 void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target)
3140 {
3141 // Place the labels file in a per-target support directory.
3142 std::string dir = target->GetSupportDirectory();
3143 std::string file = cmStrCat(dir, "/Labels.txt");
3144 std::string json_file = dir + "/Labels.json";
3145
3146 #ifndef CMAKE_BOOTSTRAP
3147 // Check whether labels are enabled for this target.
3148 cmValue targetLabels = target->GetProperty("LABELS");
3149 cmValue directoryLabels =
3150 target->Target->GetMakefile()->GetProperty("LABELS");
3151 cmValue cmakeDirectoryLabels =
3152 target->Target->GetMakefile()->GetDefinition("CMAKE_DIRECTORY_LABELS");
3153 if (targetLabels || directoryLabels || cmakeDirectoryLabels) {
3154 Json::Value lj_root(Json::objectValue);
3155 Json::Value& lj_target = lj_root["target"] = Json::objectValue;
3156 lj_target["name"] = target->GetName();
3157 Json::Value& lj_target_labels = lj_target["labels"] = Json::arrayValue;
3158 Json::Value& lj_sources = lj_root["sources"] = Json::arrayValue;
3159
3160 cmSystemTools::MakeDirectory(dir);
3161 cmGeneratedFileStream fout(file);
3162
3163 std::vector<std::string> labels;
3164
3165 // List the target-wide labels. All sources in the target get
3166 // these labels.
3167 if (targetLabels) {
3168 cmExpandList(*targetLabels, labels);
3169 if (!labels.empty()) {
3170 fout << "# Target labels\n";
3171 for (std::string const& l : labels) {
3172 fout << " " << l << "\n";
3173 lj_target_labels.append(l);
3174 }
3175 }
3176 }
3177
3178 // List directory labels
3179 std::vector<std::string> directoryLabelsList;
3180 std::vector<std::string> cmakeDirectoryLabelsList;
3181
3182 if (directoryLabels) {
3183 cmExpandList(*directoryLabels, directoryLabelsList);
3184 }
3185
3186 if (cmakeDirectoryLabels) {
3187 cmExpandList(*cmakeDirectoryLabels, cmakeDirectoryLabelsList);
3188 }
3189
3190 if (!directoryLabelsList.empty() || !cmakeDirectoryLabelsList.empty()) {
3191 fout << "# Directory labels\n";
3192 }
3193
3194 for (std::string const& li : directoryLabelsList) {
3195 fout << " " << li << "\n";
3196 lj_target_labels.append(li);
3197 }
3198
3199 for (std::string const& li : cmakeDirectoryLabelsList) {
3200 fout << " " << li << "\n";
3201 lj_target_labels.append(li);
3202 }
3203
3204 // List the source files with any per-source labels.
3205 fout << "# Source files and their labels\n";
3206 std::vector<cmSourceFile*> sources;
3207 std::vector<std::string> const& configs =
3208 target->Target->GetMakefile()->GetGeneratorConfigs(
3209 cmMakefile::IncludeEmptyConfig);
3210 for (std::string const& c : configs) {
3211 target->GetSourceFiles(sources, c);
3212 }
3213 auto const sourcesEnd = cmRemoveDuplicates(sources);
3214 for (cmSourceFile* sf : cmMakeRange(sources.cbegin(), sourcesEnd)) {
3215 Json::Value& lj_source = lj_sources.append(Json::objectValue);
3216 std::string const& sfp = sf->ResolveFullPath();
3217 fout << sfp << "\n";
3218 lj_source["file"] = sfp;
3219 if (cmValue svalue = sf->GetProperty("LABELS")) {
3220 labels.clear();
3221 Json::Value& lj_source_labels = lj_source["labels"] = Json::arrayValue;
3222 cmExpandList(*svalue, labels);
3223 for (std::string const& label : labels) {
3224 fout << " " << label << "\n";
3225 lj_source_labels.append(label);
3226 }
3227 }
3228 }
3229 cmGeneratedFileStream json_fout(json_file);
3230 json_fout << lj_root;
3231 } else
3232 #endif
3233 {
3234 cmSystemTools::RemoveFile(file);
3235 cmSystemTools::RemoveFile(json_file);
3236 }
3237 }
3238
3239 // static
EscapeJSON(const std::string & s)3240 std::string cmGlobalGenerator::EscapeJSON(const std::string& s)
3241 {
3242 std::string result;
3243 result.reserve(s.size());
3244 for (char i : s) {
3245 switch (i) {
3246 case '"':
3247 case '\\':
3248 result += '\\';
3249 result += i;
3250 break;
3251 case '\n':
3252 result += "\\n";
3253 break;
3254 case '\t':
3255 result += "\\t";
3256 break;
3257 default:
3258 result += i;
3259 }
3260 }
3261 return result;
3262 }
3263
SetFilenameTargetDepends(cmSourceFile * sf,std::set<cmGeneratorTarget const * > const & tgts)3264 void cmGlobalGenerator::SetFilenameTargetDepends(
3265 cmSourceFile* sf, std::set<cmGeneratorTarget const*> const& tgts)
3266 {
3267 this->FilenameTargetDepends[sf] = tgts;
3268 }
3269
3270 std::set<cmGeneratorTarget const*> const&
GetFilenameTargetDepends(cmSourceFile * sf) const3271 cmGlobalGenerator::GetFilenameTargetDepends(cmSourceFile* sf) const
3272 {
3273 return this->FilenameTargetDepends[sf];
3274 }
3275
GetRealPath(const std::string & dir)3276 const std::string& cmGlobalGenerator::GetRealPath(const std::string& dir)
3277 {
3278 auto i = this->RealPaths.lower_bound(dir);
3279 if (i == this->RealPaths.end() ||
3280 this->RealPaths.key_comp()(dir, i->first)) {
3281 i = this->RealPaths.emplace_hint(i, dir, cmSystemTools::GetRealPath(dir));
3282 }
3283 return i->second;
3284 }
3285
NewDeferId()3286 std::string cmGlobalGenerator::NewDeferId()
3287 {
3288 return cmStrCat("__"_s, std::to_string(this->NextDeferId++));
3289 }
3290
ProcessEvaluationFiles()3291 void cmGlobalGenerator::ProcessEvaluationFiles()
3292 {
3293 std::vector<std::string> generatedFiles;
3294 for (auto& localGen : this->LocalGenerators) {
3295 localGen->ProcessEvaluationFiles(generatedFiles);
3296 }
3297 }
3298
ExpandCFGIntDir(const std::string & str,const std::string &) const3299 std::string cmGlobalGenerator::ExpandCFGIntDir(
3300 const std::string& str, const std::string& /*config*/) const
3301 {
3302 return str;
3303 }
3304
GenerateCPackPropertiesFile()3305 bool cmGlobalGenerator::GenerateCPackPropertiesFile()
3306 {
3307 cmake::InstalledFilesMap const& installedFiles =
3308 this->CMakeInstance->GetInstalledFiles();
3309
3310 const auto& lg = this->LocalGenerators[0];
3311 cmMakefile* mf = lg->GetMakefile();
3312
3313 std::vector<std::string> configs =
3314 mf->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig);
3315 std::string config = mf->GetDefaultConfiguration();
3316
3317 std::string path = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
3318 "/CPackProperties.cmake");
3319
3320 if (!cmSystemTools::FileExists(path) && installedFiles.empty()) {
3321 return true;
3322 }
3323
3324 cmGeneratedFileStream file(path);
3325 file << "# CPack properties\n";
3326
3327 for (auto const& i : installedFiles) {
3328 cmInstalledFile const& installedFile = i.second;
3329
3330 cmCPackPropertiesGenerator cpackPropertiesGenerator(
3331 lg.get(), installedFile, configs);
3332
3333 cpackPropertiesGenerator.Generate(file, config, configs);
3334 }
3335
3336 return true;
3337 }
3338
3339 cmInstallRuntimeDependencySet*
CreateAnonymousRuntimeDependencySet()3340 cmGlobalGenerator::CreateAnonymousRuntimeDependencySet()
3341 {
3342 auto set = cm::make_unique<cmInstallRuntimeDependencySet>();
3343 auto* retval = set.get();
3344 this->RuntimeDependencySets.push_back(std::move(set));
3345 return retval;
3346 }
3347
GetNamedRuntimeDependencySet(const std::string & name)3348 cmInstallRuntimeDependencySet* cmGlobalGenerator::GetNamedRuntimeDependencySet(
3349 const std::string& name)
3350 {
3351 auto it = this->RuntimeDependencySetsByName.find(name);
3352 if (it == this->RuntimeDependencySetsByName.end()) {
3353 auto set = cm::make_unique<cmInstallRuntimeDependencySet>(name);
3354 it =
3355 this->RuntimeDependencySetsByName.insert(std::make_pair(name, set.get()))
3356 .first;
3357 this->RuntimeDependencySets.push_back(std::move(set));
3358 }
3359 return it->second;
3360 }
3361