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 "cmQtAutoGenInitializer.h"
4
5 #include <cstddef>
6 #include <deque>
7 #include <initializer_list>
8 #include <map>
9 #include <set>
10 #include <sstream> // for basic_ios, istringstream
11 #include <string>
12 #include <unordered_set>
13 #include <utility>
14 #include <vector>
15
16 #include <cm/algorithm>
17 #include <cm/iterator>
18 #include <cm/memory>
19 #include <cmext/algorithm>
20 #include <cmext/string_view>
21
22 #include <cm3p/json/value.h>
23 #include <cm3p/json/writer.h>
24
25 #include "cmsys/SystemInformation.hxx"
26
27 #include "cmAlgorithms.h"
28 #include "cmCustomCommand.h"
29 #include "cmCustomCommandLines.h"
30 #include "cmGeneratedFileStream.h"
31 #include "cmGeneratorExpression.h"
32 #include "cmGeneratorTarget.h"
33 #include "cmGlobalGenerator.h"
34 #include "cmLinkItem.h"
35 #include "cmListFileCache.h"
36 #include "cmLocalGenerator.h"
37 #include "cmMakefile.h"
38 #include "cmMessageType.h"
39 #include "cmPolicies.h"
40 #include "cmQtAutoGen.h"
41 #include "cmQtAutoGenGlobalInitializer.h"
42 #include "cmSourceFile.h"
43 #include "cmSourceFileLocationKind.h"
44 #include "cmSourceGroup.h"
45 #include "cmState.h"
46 #include "cmStateTypes.h"
47 #include "cmStringAlgorithms.h"
48 #include "cmSystemTools.h"
49 #include "cmTarget.h"
50 #include "cmValue.h"
51 #include "cmake.h"
52
53 namespace {
54
GetParallelCPUCount()55 unsigned int GetParallelCPUCount()
56 {
57 static unsigned int count = 0;
58 // Detect only on the first call
59 if (count == 0) {
60 cmsys::SystemInformation info;
61 info.RunCPUCheck();
62 count =
63 cm::clamp(info.GetNumberOfPhysicalCPU(), 1u, cmQtAutoGen::ParallelMax);
64 }
65 return count;
66 }
67
FileProjectRelativePath(cmMakefile * makefile,std::string const & fileName)68 std::string FileProjectRelativePath(cmMakefile* makefile,
69 std::string const& fileName)
70 {
71 std::string res;
72 {
73 std::string pSource = cmSystemTools::RelativePath(
74 makefile->GetCurrentSourceDirectory(), fileName);
75 std::string pBinary = cmSystemTools::RelativePath(
76 makefile->GetCurrentBinaryDirectory(), fileName);
77 if (pSource.size() < pBinary.size()) {
78 res = std::move(pSource);
79 } else if (pBinary.size() < fileName.size()) {
80 res = std::move(pBinary);
81 } else {
82 res = fileName;
83 }
84 }
85 return res;
86 }
87
88 /**
89 * Tests if targetDepend is a STATIC_LIBRARY and if any of its
90 * recursive STATIC_LIBRARY dependencies depends on targetOrigin
91 * (STATIC_LIBRARY cycle).
92 */
StaticLibraryCycle(cmGeneratorTarget const * targetOrigin,cmGeneratorTarget const * targetDepend,std::string const & config)93 bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
94 cmGeneratorTarget const* targetDepend,
95 std::string const& config)
96 {
97 bool cycle = false;
98 if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
99 (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) {
100 std::set<cmGeneratorTarget const*> knownLibs;
101 std::deque<cmGeneratorTarget const*> testLibs;
102
103 // Insert initial static_library dependency
104 knownLibs.insert(targetDepend);
105 testLibs.push_back(targetDepend);
106
107 while (!testLibs.empty()) {
108 cmGeneratorTarget const* testTarget = testLibs.front();
109 testLibs.pop_front();
110 // Check if the test target is the origin target (cycle)
111 if (testTarget == targetOrigin) {
112 cycle = true;
113 break;
114 }
115 // Collect all static_library dependencies from the test target
116 cmLinkImplementationLibraries const* libs =
117 testTarget->GetLinkImplementationLibraries(config);
118 if (libs) {
119 for (cmLinkItem const& item : libs->Libraries) {
120 cmGeneratorTarget const* depTarget = item.Target;
121 if (depTarget &&
122 (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
123 knownLibs.insert(depTarget).second) {
124 testLibs.push_back(depTarget);
125 }
126 }
127 }
128 }
129 }
130 return cycle;
131 }
132
133 /** Sanitizes file search paths. */
134 class SearchPathSanitizer
135 {
136 public:
SearchPathSanitizer(cmMakefile * makefile)137 SearchPathSanitizer(cmMakefile* makefile)
138 : SourcePath_(makefile->GetCurrentSourceDirectory())
139 {
140 }
141 std::vector<std::string> operator()(
142 std::vector<std::string> const& paths) const;
143
144 private:
145 std::string SourcePath_;
146 };
147
operator ()(std::vector<std::string> const & paths) const148 std::vector<std::string> SearchPathSanitizer::operator()(
149 std::vector<std::string> const& paths) const
150 {
151 std::vector<std::string> res;
152 res.reserve(paths.size());
153 for (std::string const& srcPath : paths) {
154 // Collapse relative paths
155 std::string path =
156 cmSystemTools::CollapseFullPath(srcPath, this->SourcePath_);
157 // Remove suffix slashes
158 while (cmHasSuffix(path, '/')) {
159 path.pop_back();
160 }
161 // Accept only non empty paths
162 if (!path.empty()) {
163 res.emplace_back(std::move(path));
164 }
165 }
166 return res;
167 }
168
169 /** @brief Writes a CMake info file. */
170 class InfoWriter
171 {
172 public:
173 // -- Single value
Set(std::string const & key,std::string const & value)174 void Set(std::string const& key, std::string const& value)
175 {
176 this->Value_[key] = value;
177 }
178 void SetConfig(std::string const& key,
179 cmQtAutoGenInitializer::ConfigString const& cfgStr);
SetBool(std::string const & key,bool value)180 void SetBool(std::string const& key, bool value)
181 {
182 this->Value_[key] = value;
183 }
SetUInt(std::string const & key,unsigned int value)184 void SetUInt(std::string const& key, unsigned int value)
185 {
186 this->Value_[key] = value;
187 }
188
189 // -- Array utility
190 template <typename CONT>
191 static bool MakeArray(Json::Value& jval, CONT const& container);
192
193 template <typename CONT>
194 static void MakeStringArray(Json::Value& jval, CONT const& container);
195
196 // -- Array value
197 template <typename CONT>
198 void SetArray(std::string const& key, CONT const& container);
199 template <typename CONT>
200 void SetConfigArray(
201 std::string const& key,
202 cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr);
203
204 // -- Array of arrays
205 template <typename CONT, typename FUNC>
206 void SetArrayArray(std::string const& key, CONT const& container, FUNC func);
207
208 // -- Save to json file
209 bool Save(std::string const& filename);
210
211 private:
212 Json::Value Value_;
213 };
214
SetConfig(std::string const & key,cmQtAutoGenInitializer::ConfigString const & cfgStr)215 void InfoWriter::SetConfig(std::string const& key,
216 cmQtAutoGenInitializer::ConfigString const& cfgStr)
217 {
218 this->Set(key, cfgStr.Default);
219 for (auto const& item : cfgStr.Config) {
220 this->Set(cmStrCat(key, '_', item.first), item.second);
221 }
222 }
223
224 template <typename CONT>
MakeArray(Json::Value & jval,CONT const & container)225 bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container)
226 {
227 jval = Json::arrayValue;
228 std::size_t const listSize = cm::size(container);
229 if (listSize == 0) {
230 return false;
231 }
232 jval.resize(static_cast<unsigned int>(listSize));
233 return true;
234 }
235
236 template <typename CONT>
MakeStringArray(Json::Value & jval,CONT const & container)237 void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container)
238 {
239 if (MakeArray(jval, container)) {
240 Json::ArrayIndex ii = 0;
241 for (std::string const& item : container) {
242 jval[ii++] = item;
243 }
244 }
245 }
246
247 template <typename CONT>
SetArray(std::string const & key,CONT const & container)248 void InfoWriter::SetArray(std::string const& key, CONT const& container)
249 {
250 MakeStringArray(this->Value_[key], container);
251 }
252
253 template <typename CONT, typename FUNC>
SetArrayArray(std::string const & key,CONT const & container,FUNC func)254 void InfoWriter::SetArrayArray(std::string const& key, CONT const& container,
255 FUNC func)
256 {
257 Json::Value& jval = this->Value_[key];
258 if (MakeArray(jval, container)) {
259 Json::ArrayIndex ii = 0;
260 for (auto const& citem : container) {
261 Json::Value& aval = jval[ii++];
262 aval = Json::arrayValue;
263 func(aval, citem);
264 }
265 }
266 }
267
268 template <typename CONT>
SetConfigArray(std::string const & key,cmQtAutoGenInitializer::ConfigStrings<CONT> const & cfgStr)269 void InfoWriter::SetConfigArray(
270 std::string const& key,
271 cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr)
272 {
273 this->SetArray(key, cfgStr.Default);
274 for (auto const& item : cfgStr.Config) {
275 this->SetArray(cmStrCat(key, '_', item.first), item.second);
276 }
277 }
278
Save(std::string const & filename)279 bool InfoWriter::Save(std::string const& filename)
280 {
281 cmGeneratedFileStream fileStream;
282 fileStream.SetCopyIfDifferent(true);
283 fileStream.Open(filename, false, true);
284 if (!fileStream) {
285 return false;
286 }
287
288 Json::StyledStreamWriter jsonWriter;
289 try {
290 jsonWriter.write(fileStream, this->Value_);
291 } catch (...) {
292 return false;
293 }
294
295 return fileStream.Close();
296 }
297
AddAutogenExecutableToDependencies(cmQtAutoGenInitializer::GenVarsT const & genVars,std::vector<std::string> & dependencies)298 void AddAutogenExecutableToDependencies(
299 cmQtAutoGenInitializer::GenVarsT const& genVars,
300 std::vector<std::string>& dependencies)
301 {
302 if (genVars.ExecutableTarget != nullptr) {
303 dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
304 } else if (!genVars.Executable.empty()) {
305 dependencies.push_back(genVars.Executable);
306 }
307 }
308
309 } // End of unnamed namespace
310
cmQtAutoGenInitializer(cmQtAutoGenGlobalInitializer * globalInitializer,cmGeneratorTarget * genTarget,IntegerVersion const & qtVersion,bool mocEnabled,bool uicEnabled,bool rccEnabled,bool globalAutogenTarget,bool globalAutoRccTarget)311 cmQtAutoGenInitializer::cmQtAutoGenInitializer(
312 cmQtAutoGenGlobalInitializer* globalInitializer,
313 cmGeneratorTarget* genTarget, IntegerVersion const& qtVersion,
314 bool mocEnabled, bool uicEnabled, bool rccEnabled, bool globalAutogenTarget,
315 bool globalAutoRccTarget)
316 : GlobalInitializer(globalInitializer)
317 , GenTarget(genTarget)
318 , GlobalGen(genTarget->GetGlobalGenerator())
319 , LocalGen(genTarget->GetLocalGenerator())
320 , Makefile(genTarget->Makefile)
321 , PathCheckSum(genTarget->Makefile)
322 , QtVersion(qtVersion)
323 {
324 this->AutogenTarget.GlobalTarget = globalAutogenTarget;
325 this->Moc.Enabled = mocEnabled;
326 this->Uic.Enabled = uicEnabled;
327 this->Rcc.Enabled = rccEnabled;
328 this->Rcc.GlobalTarget = globalAutoRccTarget;
329 }
330
InitCustomTargets()331 bool cmQtAutoGenInitializer::InitCustomTargets()
332 {
333 // Configurations
334 this->MultiConfig = this->GlobalGen->IsMultiConfig();
335 this->ConfigDefault = this->Makefile->GetDefaultConfiguration();
336 this->ConfigsList =
337 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
338
339 // Verbosity
340 {
341 std::string def =
342 this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
343 if (!def.empty()) {
344 unsigned long iVerb = 0;
345 if (cmStrToULong(def, &iVerb)) {
346 // Numeric verbosity
347 this->Verbosity = static_cast<unsigned int>(iVerb);
348 } else {
349 // Non numeric verbosity
350 if (cmIsOn(def)) {
351 this->Verbosity = 1;
352 }
353 }
354 }
355 }
356
357 // Targets FOLDER
358 {
359 cmValue folder =
360 this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
361 if (!folder) {
362 folder = this->Makefile->GetState()->GetGlobalProperty(
363 "AUTOGEN_TARGETS_FOLDER");
364 }
365 // Inherit FOLDER property from target (#13688)
366 if (!folder) {
367 folder = this->GenTarget->GetProperty("FOLDER");
368 }
369 if (folder) {
370 this->TargetsFolder = *folder;
371 }
372 }
373
374 // Check status of policy CMP0071 regarding handling of GENERATED files
375 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0071)) {
376 case cmPolicies::WARN:
377 // Ignore GENERATED files but warn
378 this->CMP0071Warn = true;
379 CM_FALLTHROUGH;
380 case cmPolicies::OLD:
381 // Ignore GENERATED files
382 break;
383 case cmPolicies::REQUIRED_IF_USED:
384 case cmPolicies::REQUIRED_ALWAYS:
385 case cmPolicies::NEW:
386 // Process GENERATED files
387 this->CMP0071Accept = true;
388 break;
389 }
390
391 // Check status of policy CMP0100 regarding handling of .hh headers
392 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0100)) {
393 case cmPolicies::WARN:
394 // Ignore but .hh files but warn
395 this->CMP0100Warn = true;
396 CM_FALLTHROUGH;
397 case cmPolicies::OLD:
398 // Ignore .hh files
399 break;
400 case cmPolicies::REQUIRED_IF_USED:
401 case cmPolicies::REQUIRED_ALWAYS:
402 case cmPolicies::NEW:
403 // Process .hh file
404 this->CMP0100Accept = true;
405 break;
406 }
407
408 // Common directories
409 {
410 // Collapsed current binary directory
411 std::string const cbd = cmSystemTools::CollapseFullPath(
412 std::string(), this->Makefile->GetCurrentBinaryDirectory());
413
414 // Info directory
415 this->Dir.Info = cmStrCat(cbd, "/CMakeFiles/", this->GenTarget->GetName(),
416 "_autogen.dir");
417 cmSystemTools::ConvertToUnixSlashes(this->Dir.Info);
418
419 // Build directory
420 this->Dir.Build = this->GenTarget->GetSafeProperty("AUTOGEN_BUILD_DIR");
421 if (this->Dir.Build.empty()) {
422 this->Dir.Build =
423 cmStrCat(cbd, '/', this->GenTarget->GetName(), "_autogen");
424 }
425 cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
426 // Cleanup build directory
427 this->AddCleanFile(this->Dir.Build);
428
429 // Working directory
430 this->Dir.Work = cbd;
431 cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
432
433 // Include directory
434 this->ConfigFileNamesAndGenex(this->Dir.Include, this->Dir.IncludeGenExp,
435 cmStrCat(this->Dir.Build, "/include"), "");
436 }
437
438 // Moc, Uic and _autogen target settings
439 if (this->MocOrUicEnabled()) {
440 // Init moc specific settings
441 if (this->Moc.Enabled && !this->InitMoc()) {
442 return false;
443 }
444
445 // Init uic specific settings
446 if (this->Uic.Enabled && !this->InitUic()) {
447 return false;
448 }
449
450 // Autogen target name
451 this->AutogenTarget.Name =
452 cmStrCat(this->GenTarget->GetName(), "_autogen");
453
454 // Autogen target parallel processing
455 {
456 std::string const& prop =
457 this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
458 if (prop.empty() || (prop == "AUTO")) {
459 // Autodetect number of CPUs
460 this->AutogenTarget.Parallel = GetParallelCPUCount();
461 } else {
462 this->AutogenTarget.Parallel = 1;
463 }
464 }
465
466 // Autogen target info and settings files
467 {
468 // Info file
469 this->AutogenTarget.InfoFile =
470 cmStrCat(this->Dir.Info, "/AutogenInfo.json");
471
472 // Used settings file
473 this->ConfigFileNames(this->AutogenTarget.SettingsFile,
474 cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt");
475 this->ConfigFileClean(this->AutogenTarget.SettingsFile);
476
477 // Parse cache file
478 this->ConfigFileNames(this->AutogenTarget.ParseCacheFile,
479 cmStrCat(this->Dir.Info, "/ParseCache"), ".txt");
480 this->ConfigFileClean(this->AutogenTarget.ParseCacheFile);
481 }
482
483 // Autogen target: Compute user defined dependencies
484 {
485 this->AutogenTarget.DependOrigin =
486 this->GenTarget->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");
487
488 std::string const& deps =
489 this->GenTarget->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
490 if (!deps.empty()) {
491 for (std::string const& depName : cmExpandedList(deps)) {
492 // Allow target and file dependencies
493 auto* depTarget = this->Makefile->FindTargetToUse(depName);
494 if (depTarget) {
495 this->AutogenTarget.DependTargets.insert(depTarget);
496 } else {
497 this->AutogenTarget.DependFiles.insert(depName);
498 }
499 }
500 }
501 }
502
503 if (this->Moc.Enabled) {
504 // Path prefix
505 if (cmIsOn(this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX"))) {
506 this->Moc.PathPrefix = true;
507 }
508
509 // CMAKE_AUTOMOC_RELAXED_MODE
510 if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) {
511 this->Moc.RelaxedMode = true;
512 this->Makefile->IssueMessage(
513 MessageType::AUTHOR_WARNING,
514 cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is "
515 "deprecated an will be removed in the future. Consider "
516 "disabling it and converting the target ",
517 this->GenTarget->GetName(), " to regular mode."));
518 }
519
520 // Options
521 cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"),
522 this->Moc.Options);
523 // Filters
524 cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"),
525 this->Moc.MacroNames);
526 this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
527 this->Moc.MacroNames.end());
528 {
529 auto filterList = cmExpandedList(
530 this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
531 if ((filterList.size() % 2) != 0) {
532 cmSystemTools::Error(
533 cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ",
534 filterList.size(), " is not a multiple of 2."));
535 return false;
536 }
537 this->Moc.DependFilters.reserve(1 + (filterList.size() / 2));
538 this->Moc.DependFilters.emplace_back(
539 "Q_PLUGIN_METADATA",
540 "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
541 "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
542 for (std::size_t ii = 0; ii != filterList.size(); ii += 2) {
543 this->Moc.DependFilters.emplace_back(filterList[ii],
544 filterList[ii + 1]);
545 }
546 }
547 }
548 }
549
550 // Init rcc specific settings
551 if (this->Rcc.Enabled && !this->InitRcc()) {
552 return false;
553 }
554
555 // Add autogen include directory to the origin target INCLUDE_DIRECTORIES
556 if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) {
557 this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, true);
558 }
559
560 // Scan files
561 if (!this->InitScanFiles()) {
562 return false;
563 }
564
565 // Create autogen target
566 if (this->MocOrUicEnabled() && !this->InitAutogenTarget()) {
567 return false;
568 }
569
570 // Create rcc targets
571 if (this->Rcc.Enabled && !this->InitRccTargets()) {
572 return false;
573 }
574
575 return true;
576 }
577
InitMoc()578 bool cmQtAutoGenInitializer::InitMoc()
579 {
580 // Mocs compilation file
581 if (this->GlobalGen->IsXcode()) {
582 // XXX(xcode-per-cfg-src): Drop this Xcode-specific code path
583 // when the Xcode generator supports per-config sources.
584 this->Moc.CompilationFile.Default =
585 cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
586 this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default;
587 } else {
588 this->ConfigFileNamesAndGenex(
589 this->Moc.CompilationFile, this->Moc.CompilationFileGenex,
590 cmStrCat(this->Dir.Build, "/mocs_compilation"_s), ".cpp"_s);
591 }
592
593 // Moc predefs
594 if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
595 (this->QtVersion >= IntegerVersion(5, 8))) {
596 // Command
597 this->Makefile->GetDefExpandList("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND",
598 this->Moc.PredefsCmd);
599 // Header
600 if (!this->Moc.PredefsCmd.empty()) {
601 this->ConfigFileNames(this->Moc.PredefsFile,
602 cmStrCat(this->Dir.Build, "/moc_predefs"), ".h");
603 }
604 }
605
606 // Moc includes
607 {
608 SearchPathSanitizer sanitizer(this->Makefile);
609 auto getDirs =
610 [this, &sanitizer](std::string const& cfg) -> std::vector<std::string> {
611 // Get the include dirs for this target, without stripping the implicit
612 // include dirs off, see issue #13667.
613 std::vector<std::string> dirs;
614 bool const appendImplicit = (this->QtVersion.Major >= 5);
615 this->LocalGen->GetIncludeDirectoriesImplicit(
616 dirs, this->GenTarget, "CXX", cfg, false, appendImplicit);
617 return sanitizer(dirs);
618 };
619
620 // Default configuration include directories
621 this->Moc.Includes.Default = getDirs(this->ConfigDefault);
622 // Other configuration settings
623 if (this->MultiConfig) {
624 for (std::string const& cfg : this->ConfigsList) {
625 std::vector<std::string> dirs = getDirs(cfg);
626 if (dirs == this->Moc.Includes.Default) {
627 continue;
628 }
629 this->Moc.Includes.Config[cfg] = std::move(dirs);
630 }
631 }
632 }
633
634 // Moc compile definitions
635 {
636 auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
637 std::set<std::string> defines;
638 this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines);
639 #ifdef _WIN32
640 if (this->Moc.PredefsCmd.empty()) {
641 // Add WIN32 definition if we don't have a moc_predefs.h
642 defines.insert("WIN32");
643 }
644 #endif
645 return defines;
646 };
647
648 // Default configuration defines
649 this->Moc.Defines.Default = getDefs(this->ConfigDefault);
650 // Other configuration defines
651 if (this->MultiConfig) {
652 for (std::string const& cfg : this->ConfigsList) {
653 std::set<std::string> defines = getDefs(cfg);
654 if (defines == this->Moc.Defines.Default) {
655 continue;
656 }
657 this->Moc.Defines.Config[cfg] = std::move(defines);
658 }
659 }
660 }
661
662 // Moc executable
663 {
664 if (!this->GetQtExecutable(this->Moc, "moc", false)) {
665 return false;
666 }
667 // Let the _autogen target depend on the moc executable
668 if (this->Moc.ExecutableTarget) {
669 this->AutogenTarget.DependTargets.insert(
670 this->Moc.ExecutableTarget->Target);
671 }
672 }
673
674 return true;
675 }
676
InitUic()677 bool cmQtAutoGenInitializer::InitUic()
678 {
679 // Uic search paths
680 {
681 std::string const& usp =
682 this->GenTarget->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
683 if (!usp.empty()) {
684 this->Uic.SearchPaths =
685 SearchPathSanitizer(this->Makefile)(cmExpandedList(usp));
686 }
687 }
688 // Uic target options
689 {
690 auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> {
691 std::vector<std::string> opts;
692 this->GenTarget->GetAutoUicOptions(opts, cfg);
693 return opts;
694 };
695
696 // Default options
697 this->Uic.Options.Default = getOpts(this->ConfigDefault);
698 // Configuration specific options
699 if (this->MultiConfig) {
700 for (std::string const& cfg : this->ConfigsList) {
701 std::vector<std::string> options = getOpts(cfg);
702 if (options == this->Uic.Options.Default) {
703 continue;
704 }
705 this->Uic.Options.Config[cfg] = std::move(options);
706 }
707 }
708 }
709
710 // Uic executable
711 {
712 if (!this->GetQtExecutable(this->Uic, "uic", true)) {
713 return false;
714 }
715 // Let the _autogen target depend on the uic executable
716 if (this->Uic.ExecutableTarget) {
717 this->AutogenTarget.DependTargets.insert(
718 this->Uic.ExecutableTarget->Target);
719 }
720 }
721
722 return true;
723 }
724
InitRcc()725 bool cmQtAutoGenInitializer::InitRcc()
726 {
727 // Rcc executable
728 {
729 if (!this->GetQtExecutable(this->Rcc, "rcc", false)) {
730 return false;
731 }
732 // Evaluate test output on demand
733 CompilerFeatures& features = *this->Rcc.ExecutableFeatures;
734 if (!features.Evaluated) {
735 // Look for list options
736 if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
737 if (features.HelpOutput.find("--list") != std::string::npos) {
738 features.ListOptions.emplace_back("--list");
739 } else if (features.HelpOutput.find("-list") != std::string::npos) {
740 features.ListOptions.emplace_back("-list");
741 }
742 }
743 // Evaluation finished
744 features.Evaluated = true;
745 }
746 }
747
748 return true;
749 }
750
InitScanFiles()751 bool cmQtAutoGenInitializer::InitScanFiles()
752 {
753 cmake const* cm = this->Makefile->GetCMakeInstance();
754 auto const& kw = this->GlobalInitializer->kw();
755
756 auto makeMUFile = [this, &kw](cmSourceFile* sf, std::string const& fullPath,
757 std::vector<size_t> const& configs,
758 bool muIt) -> MUFileHandle {
759 MUFileHandle muf = cm::make_unique<MUFile>();
760 muf->FullPath = fullPath;
761 muf->SF = sf;
762 if (!configs.empty() && configs.size() != this->ConfigsList.size()) {
763 muf->Configs = configs;
764 }
765 muf->Generated = sf->GetIsGenerated();
766 bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
767 muf->SkipMoc = this->Moc.Enabled &&
768 (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOMOC));
769 muf->SkipUic = this->Uic.Enabled &&
770 (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
771 if (muIt) {
772 muf->MocIt = this->Moc.Enabled && !muf->SkipMoc;
773 muf->UicIt = this->Uic.Enabled && !muf->SkipUic;
774 }
775 return muf;
776 };
777
778 auto addMUHeader = [this](MUFileHandle&& muf, cm::string_view extension) {
779 cmSourceFile* sf = muf->SF;
780 const bool muIt = (muf->MocIt || muf->UicIt);
781 if (this->CMP0100Accept || (extension != "hh")) {
782 // Accept
783 if (muIt && muf->Generated) {
784 this->AutogenTarget.FilesGenerated.emplace_back(muf.get());
785 }
786 this->AutogenTarget.Headers.emplace(sf, std::move(muf));
787 } else if (muIt && this->CMP0100Warn) {
788 // Store file for warning message
789 this->AutogenTarget.CMP0100HeadersWarn.push_back(sf);
790 }
791 };
792
793 auto addMUSource = [this](MUFileHandle&& muf) {
794 if ((muf->MocIt || muf->UicIt) && muf->Generated) {
795 this->AutogenTarget.FilesGenerated.emplace_back(muf.get());
796 }
797 this->AutogenTarget.Sources.emplace(muf->SF, std::move(muf));
798 };
799
800 // Scan through target files
801 {
802 // Scan through target files
803 for (cmGeneratorTarget::AllConfigSource const& acs :
804 this->GenTarget->GetAllConfigSources()) {
805 std::string const& fullPath = acs.Source->GetFullPath();
806 std::string const& extLower =
807 cmSystemTools::LowerCase(acs.Source->GetExtension());
808
809 // Register files that will be scanned by moc or uic
810 if (this->MocOrUicEnabled()) {
811 if (cm->IsAHeaderExtension(extLower)) {
812 addMUHeader(makeMUFile(acs.Source, fullPath, acs.Configs, true),
813 extLower);
814 } else if (cm->IsACLikeSourceExtension(extLower)) {
815 addMUSource(makeMUFile(acs.Source, fullPath, acs.Configs, true));
816 }
817 }
818
819 // Register rcc enabled files
820 if (this->Rcc.Enabled) {
821 if ((extLower == kw.qrc) &&
822 !acs.Source->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
823 !acs.Source->GetPropertyAsBool(kw.SKIP_AUTORCC)) {
824 // Register qrc file
825 Qrc qrc;
826 qrc.QrcFile = fullPath;
827 qrc.QrcName =
828 cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
829 qrc.Generated = acs.Source->GetIsGenerated();
830 // RCC options
831 {
832 std::string const& opts =
833 acs.Source->GetSafeProperty(kw.AUTORCC_OPTIONS);
834 if (!opts.empty()) {
835 cmExpandList(opts, qrc.Options);
836 }
837 }
838 this->Rcc.Qrcs.push_back(std::move(qrc));
839 }
840 }
841 }
842 }
843 // cmGeneratorTarget::GetAllConfigSources computes the target's
844 // sources meta data cache. Clear it so that OBJECT library targets that
845 // are AUTOGEN initialized after this target get their added
846 // mocs_compilation.cpp source acknowledged by this target.
847 this->GenTarget->ClearSourcesCache();
848
849 // For source files find additional headers and private headers
850 if (this->MocOrUicEnabled()) {
851 // Header search suffixes and extensions
852 static std::initializer_list<cm::string_view> const suffixes{ "", "_p" };
853 auto const& exts = cm->GetHeaderExtensions();
854 // Scan through sources
855 for (auto const& pair : this->AutogenTarget.Sources) {
856 MUFile const& muf = *pair.second;
857 if (muf.MocIt || muf.UicIt) {
858 // Search for the default header file and a private header
859 std::string const& srcFullPath = muf.SF->ResolveFullPath();
860 std::string basePath = cmStrCat(
861 cmQtAutoGen::SubDirPrefix(srcFullPath),
862 cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath));
863 for (auto const& suffix : suffixes) {
864 std::string const suffixedPath = cmStrCat(basePath, suffix);
865 for (auto const& ext : exts) {
866 std::string fullPath = cmStrCat(suffixedPath, '.', ext);
867
868 auto constexpr locationKind = cmSourceFileLocationKind::Known;
869 cmSourceFile* sf =
870 this->Makefile->GetSource(fullPath, locationKind);
871 if (sf) {
872 // Check if we know about this header already
873 if (cm::contains(this->AutogenTarget.Headers, sf)) {
874 continue;
875 }
876 // We only accept not-GENERATED files that do exist.
877 if (!sf->GetIsGenerated() &&
878 !cmSystemTools::FileExists(fullPath)) {
879 continue;
880 }
881 } else if (cmSystemTools::FileExists(fullPath)) {
882 // Create a new source file for the existing file
883 sf = this->Makefile->CreateSource(fullPath, false, locationKind);
884 }
885
886 if (sf) {
887 auto eMuf = makeMUFile(sf, fullPath, muf.Configs, true);
888 // Only process moc/uic when the parent is processed as well
889 if (!muf.MocIt) {
890 eMuf->MocIt = false;
891 }
892 if (!muf.UicIt) {
893 eMuf->UicIt = false;
894 }
895 addMUHeader(std::move(eMuf), ext);
896 }
897 }
898 }
899 }
900 }
901 }
902
903 // Scan through all source files in the makefile to extract moc and uic
904 // parameters. Historically we support non target source file parameters.
905 // The reason is that their file names might be discovered from source files
906 // at generation time.
907 if (this->MocOrUicEnabled()) {
908 for (const auto& sf : this->Makefile->GetSourceFiles()) {
909 // sf->GetExtension() is only valid after sf->ResolveFullPath() ...
910 // Since we're iterating over source files that might be not in the
911 // target we need to check for path errors (not existing files).
912 std::string pathError;
913 std::string const& fullPath = sf->ResolveFullPath(&pathError);
914 if (!pathError.empty() || fullPath.empty()) {
915 continue;
916 }
917 std::string const& extLower =
918 cmSystemTools::LowerCase(sf->GetExtension());
919
920 if (cm->IsAHeaderExtension(extLower)) {
921 if (!cm::contains(this->AutogenTarget.Headers, sf.get())) {
922 auto muf = makeMUFile(sf.get(), fullPath, {}, false);
923 if (muf->SkipMoc || muf->SkipUic) {
924 addMUHeader(std::move(muf), extLower);
925 }
926 }
927 } else if (cm->IsACLikeSourceExtension(extLower)) {
928 if (!cm::contains(this->AutogenTarget.Sources, sf.get())) {
929 auto muf = makeMUFile(sf.get(), fullPath, {}, false);
930 if (muf->SkipMoc || muf->SkipUic) {
931 addMUSource(std::move(muf));
932 }
933 }
934 } else if (this->Uic.Enabled && (extLower == kw.ui)) {
935 // .ui file
936 bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
937 bool const skipUic =
938 (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
939 if (!skipUic) {
940 // Check if the .ui file has uic options
941 std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
942 if (uicOpts.empty()) {
943 this->Uic.UiFilesNoOptions.emplace_back(fullPath);
944 } else {
945 this->Uic.UiFilesWithOptions.emplace_back(fullPath,
946 cmExpandedList(uicOpts));
947 }
948
949 auto uiHeaderRelativePath = cmSystemTools::RelativePath(
950 this->LocalGen->GetCurrentSourceDirectory(),
951 cmSystemTools::GetFilenamePath(fullPath));
952
953 // Avoid creating a path containing adjacent slashes
954 if (!uiHeaderRelativePath.empty() &&
955 uiHeaderRelativePath.back() != '/') {
956 uiHeaderRelativePath += '/';
957 }
958
959 auto uiHeaderFilePath = cmStrCat(
960 '/', uiHeaderRelativePath, "ui_"_s,
961 cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s);
962
963 ConfigString uiHeader;
964 std::string uiHeaderGenex;
965 this->ConfigFileNamesAndGenex(
966 uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s),
967 uiHeaderFilePath);
968
969 this->Uic.UiHeaders.emplace_back(
970 std::make_pair(uiHeader, uiHeaderGenex));
971 } else {
972 // Register skipped .ui file
973 this->Uic.SkipUi.insert(fullPath);
974 }
975 }
976 }
977 }
978
979 // Process GENERATED sources and headers
980 if (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) {
981 if (this->CMP0071Accept) {
982 // Let the autogen target depend on the GENERATED files
983 for (MUFile* muf : this->AutogenTarget.FilesGenerated) {
984 this->AutogenTarget.DependFiles.insert(muf->FullPath);
985 }
986 } else if (this->CMP0071Warn) {
987 cm::string_view property;
988 if (this->Moc.Enabled && this->Uic.Enabled) {
989 property = "SKIP_AUTOGEN";
990 } else if (this->Moc.Enabled) {
991 property = "SKIP_AUTOMOC";
992 } else if (this->Uic.Enabled) {
993 property = "SKIP_AUTOUIC";
994 }
995 std::string files;
996 for (MUFile* muf : this->AutogenTarget.FilesGenerated) {
997 files += cmStrCat(" ", Quoted(muf->FullPath), '\n');
998 }
999 this->Makefile->IssueMessage(
1000 MessageType::AUTHOR_WARNING,
1001 cmStrCat(
1002 cmPolicies::GetPolicyWarning(cmPolicies::CMP0071), '\n',
1003 "For compatibility, CMake is excluding the GENERATED source "
1004 "file(s):\n",
1005 files, "from processing by ",
1006 cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
1007 ". If any of the files should be processed, set CMP0071 to NEW. "
1008 "If any of the files should not be processed, "
1009 "explicitly exclude them by setting the source file property ",
1010 property, ":\n set_property(SOURCE file.h PROPERTY ", property,
1011 " ON)\n"));
1012 }
1013 }
1014
1015 // Generate CMP0100 warning
1016 if (this->MocOrUicEnabled() &&
1017 !this->AutogenTarget.CMP0100HeadersWarn.empty()) {
1018 cm::string_view property;
1019 if (this->Moc.Enabled && this->Uic.Enabled) {
1020 property = "SKIP_AUTOGEN";
1021 } else if (this->Moc.Enabled) {
1022 property = "SKIP_AUTOMOC";
1023 } else if (this->Uic.Enabled) {
1024 property = "SKIP_AUTOUIC";
1025 }
1026 std::string files;
1027 for (cmSourceFile* sf : this->AutogenTarget.CMP0100HeadersWarn) {
1028 files += cmStrCat(" ", Quoted(sf->GetFullPath()), '\n');
1029 }
1030 this->Makefile->IssueMessage(
1031 MessageType::AUTHOR_WARNING,
1032 cmStrCat(
1033 cmPolicies::GetPolicyWarning(cmPolicies::CMP0100), '\n',
1034 "For compatibility, CMake is excluding the header file(s):\n", files,
1035 "from processing by ",
1036 cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
1037 ". If any of the files should be processed, set CMP0100 to NEW. "
1038 "If any of the files should not be processed, "
1039 "explicitly exclude them by setting the source file property ",
1040 property, ":\n set_property(SOURCE file.hh PROPERTY ", property,
1041 " ON)\n"));
1042 }
1043
1044 // Process qrc files
1045 if (!this->Rcc.Qrcs.empty()) {
1046 const bool modernQt = (this->QtVersion.Major >= 5);
1047 // Target rcc options
1048 std::vector<std::string> optionsTarget =
1049 cmExpandedList(this->GenTarget->GetSafeProperty(kw.AUTORCC_OPTIONS));
1050
1051 // Check if file name is unique
1052 for (Qrc& qrc : this->Rcc.Qrcs) {
1053 qrc.Unique = true;
1054 for (Qrc const& qrc2 : this->Rcc.Qrcs) {
1055 if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
1056 qrc.Unique = false;
1057 break;
1058 }
1059 }
1060 }
1061 // Path checksum and file names
1062 for (Qrc& qrc : this->Rcc.Qrcs) {
1063 // Path checksum
1064 qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
1065 // Output file name
1066 qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
1067 "/qrc_", qrc.QrcName, ".cpp");
1068 std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
1069 qrc.QrcName, '_', qrc.QrcPathChecksum);
1070 qrc.LockFile = cmStrCat(base, "_Lock.lock");
1071 qrc.InfoFile = cmStrCat(base, "_Info.json");
1072 this->ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt");
1073 }
1074 // rcc options
1075 for (Qrc& qrc : this->Rcc.Qrcs) {
1076 // Target options
1077 std::vector<std::string> opts = optionsTarget;
1078 // Merge computed "-name XYZ" option
1079 {
1080 std::string name = qrc.QrcName;
1081 // Replace '-' with '_'. The former is not valid for symbol names.
1082 std::replace(name.begin(), name.end(), '-', '_');
1083 if (!qrc.Unique) {
1084 name += cmStrCat('_', qrc.QrcPathChecksum);
1085 }
1086 std::vector<std::string> nameOpts;
1087 nameOpts.emplace_back("-name");
1088 nameOpts.emplace_back(std::move(name));
1089 RccMergeOptions(opts, nameOpts, modernQt);
1090 }
1091 // Merge file option
1092 RccMergeOptions(opts, qrc.Options, modernQt);
1093 qrc.Options = std::move(opts);
1094 }
1095 // rcc resources
1096 for (Qrc& qrc : this->Rcc.Qrcs) {
1097 if (!qrc.Generated) {
1098 std::string error;
1099 RccLister const lister(this->Rcc.Executable,
1100 this->Rcc.ExecutableFeatures->ListOptions);
1101 if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
1102 cmSystemTools::Error(error);
1103 return false;
1104 }
1105 }
1106 }
1107 }
1108
1109 return true;
1110 }
1111
InitAutogenTarget()1112 bool cmQtAutoGenInitializer::InitAutogenTarget()
1113 {
1114 // Register info file as generated by CMake
1115 this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
1116
1117 // Determine whether to use a depfile for the AUTOGEN target.
1118 const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) &&
1119 this->GlobalGen->GetName().find("Ninja") != std::string::npos;
1120
1121 // Files provided by the autogen target
1122 std::vector<std::string> autogenByproducts;
1123 std::vector<std::string> timestampByproducts;
1124 if (this->Moc.Enabled) {
1125 this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
1126 if (useNinjaDepfile) {
1127 if (this->MultiConfig) {
1128 // Make all mocs_compilation_<CONFIG>.cpp files byproducts of the
1129 // ${target}_autogen/timestamp custom command.
1130 // We cannot just use Moc.CompilationFileGenex here, because that
1131 // custom command runs cmake_autogen for each configuration.
1132 for (const auto& p : this->Moc.CompilationFile.Config) {
1133 timestampByproducts.push_back(p.second);
1134 }
1135 } else {
1136 timestampByproducts.push_back(this->Moc.CompilationFileGenex);
1137 }
1138 } else {
1139 autogenByproducts.push_back(this->Moc.CompilationFileGenex);
1140 }
1141 }
1142
1143 if (this->Uic.Enabled) {
1144 for (const auto& file : this->Uic.UiHeaders) {
1145 this->AddGeneratedSource(file.first, this->Uic);
1146 autogenByproducts.push_back(file.second);
1147 }
1148 }
1149
1150 // Compose target comment
1151 std::string autogenComment;
1152 {
1153 std::string tools;
1154 if (this->Moc.Enabled) {
1155 tools += "MOC";
1156 }
1157 if (this->Uic.Enabled) {
1158 if (!tools.empty()) {
1159 tools += " and ";
1160 }
1161 tools += "UIC";
1162 }
1163 autogenComment = cmStrCat("Automatic ", tools, " for target ",
1164 this->GenTarget->GetName());
1165 }
1166
1167 // Compose command lines
1168 // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
1169 // instead of fiddling with the include directories
1170 std::vector<std::string> configs;
1171 this->GlobalGen->GetQtAutoGenConfigs(configs);
1172 bool stdPipesUTF8 = true;
1173 cmCustomCommandLines commandLines;
1174 for (auto const& config : configs) {
1175 commandLines.push_back(cmMakeCommandLine(
1176 { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
1177 this->AutogenTarget.InfoFile, config }));
1178 }
1179
1180 // Use PRE_BUILD on demand
1181 bool usePRE_BUILD = false;
1182 if (this->GlobalGen->GetName().find("Visual Studio") != std::string::npos) {
1183 // Under VS use a PRE_BUILD event instead of a separate target to
1184 // reduce the number of targets loaded into the IDE.
1185 // This also works around a VS 11 bug that may skip updating the target:
1186 // https://connect.microsoft.com/VisualStudio/feedback/details/769495
1187 usePRE_BUILD = true;
1188 }
1189 // Disable PRE_BUILD in some cases
1190 if (usePRE_BUILD) {
1191 // Cannot use PRE_BUILD with file depends
1192 if (!this->AutogenTarget.DependFiles.empty()) {
1193 usePRE_BUILD = false;
1194 }
1195 // Cannot use PRE_BUILD when a global autogen target is in place
1196 if (this->AutogenTarget.GlobalTarget) {
1197 usePRE_BUILD = false;
1198 }
1199 }
1200 // Create the autogen target/command
1201 if (usePRE_BUILD) {
1202 // Add additional autogen target dependencies to origin target
1203 for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1204 this->GenTarget->Target->AddUtility(depTarget->GetName(), false,
1205 this->Makefile);
1206 }
1207
1208 if (!this->Uic.UiFilesNoOptions.empty() ||
1209 !this->Uic.UiFilesWithOptions.empty()) {
1210 // Add a generated timestamp file
1211 ConfigString timestampFile;
1212 std::string timestampFileGenex;
1213 ConfigFileNamesAndGenex(timestampFile, timestampFileGenex,
1214 cmStrCat(this->Dir.Build, "/autouic"_s),
1215 ".stamp"_s);
1216 this->AddGeneratedSource(timestampFile, this->Uic);
1217
1218 // Add a step in the pre-build command to touch the timestamp file
1219 commandLines.push_back(
1220 cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "touch",
1221 timestampFileGenex }));
1222
1223 // UIC needs to be re-run if any of the known UI files change or the
1224 // executable itself has been updated
1225 auto uicDependencies = this->Uic.UiFilesNoOptions;
1226 for (auto const& uiFile : this->Uic.UiFilesWithOptions) {
1227 uicDependencies.push_back(uiFile.first);
1228 }
1229 AddAutogenExecutableToDependencies(this->Uic, uicDependencies);
1230
1231 // Add a rule file to cause the target to build if a dependency has
1232 // changed, which will trigger the pre-build command to run autogen
1233 std::string no_main_dependency;
1234 cmCustomCommandLines no_command_lines;
1235 this->LocalGen->AddCustomCommandToOutput(
1236 timestampFileGenex, uicDependencies, no_main_dependency,
1237 no_command_lines, /*comment=*/"", this->Dir.Work.c_str(),
1238 /*cmp0116=*/cmPolicies::NEW, /*replace=*/false,
1239 /*escapeOldStyle=*/false, /*uses_terminal=*/false,
1240 /*command_expand_lists=*/false, /*depfile=*/"", /*job_pool=*/"",
1241 stdPipesUTF8);
1242 }
1243
1244 // Add the pre-build command directly to bypass the OBJECT_LIBRARY
1245 // rejection in cmMakefile::AddCustomCommandToTarget because we know
1246 // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
1247 //
1248 // PRE_BUILD does not support file dependencies!
1249 const std::vector<std::string> no_output;
1250 const std::vector<std::string> no_deps;
1251 cmCustomCommand cc(no_output, autogenByproducts, no_deps, commandLines,
1252 this->Makefile->GetBacktrace(), autogenComment.c_str(),
1253 this->Dir.Work.c_str(), stdPipesUTF8);
1254 cc.SetEscapeOldStyle(false);
1255 cc.SetEscapeAllowMakeVars(true);
1256 this->GenTarget->Target->AddPreBuildCommand(std::move(cc));
1257 } else {
1258
1259 // Add link library target dependencies to the autogen target
1260 // dependencies
1261 if (this->AutogenTarget.DependOrigin) {
1262 // add_dependencies/addUtility do not support generator expressions.
1263 // We depend only on the libraries found in all configs therefore.
1264 std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
1265 for (std::string const& config : this->ConfigsList) {
1266 cmLinkImplementationLibraries const* libs =
1267 this->GenTarget->GetLinkImplementationLibraries(config);
1268 if (libs) {
1269 for (cmLinkItem const& item : libs->Libraries) {
1270 cmGeneratorTarget const* libTarget = item.Target;
1271 if (libTarget &&
1272 !StaticLibraryCycle(this->GenTarget, libTarget, config)) {
1273 // Increment target config count
1274 commonTargets[libTarget]++;
1275 }
1276 }
1277 }
1278 }
1279 for (auto const& item : commonTargets) {
1280 if (item.second == this->ConfigsList.size()) {
1281 this->AutogenTarget.DependTargets.insert(item.first->Target);
1282 }
1283 }
1284 }
1285
1286 std::vector<std::string> dependencies(
1287 this->AutogenTarget.DependFiles.begin(),
1288 this->AutogenTarget.DependFiles.end());
1289
1290 if (useNinjaDepfile) {
1291 // Create a custom command that generates a timestamp file and
1292 // has a depfile assigned. The depfile is created by JobDepFilesMergeT.
1293 //
1294 // Also create an additional '_autogen_timestamp_deps' that the custom
1295 // command will depend on. It will have no sources or commands to
1296 // execute, but it will have dependencies that would originally be
1297 // assigned to the pre-Qt 5.15 'autogen' target. These dependencies will
1298 // serve as a list of order-only dependencies for the custom command,
1299 // without forcing the custom command to re-execute.
1300 //
1301 // The dependency tree would then look like
1302 // '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <-
1303 // '_autogen' target.
1304 const auto timestampTargetName =
1305 cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps");
1306 std::vector<std::string> timestampTargetProvides;
1307 cmCustomCommandLines timestampTargetCommandLines;
1308
1309 // Add additional autogen target dependencies to
1310 // '_autogen_timestamp_deps'.
1311 for (const cmTarget* t : this->AutogenTarget.DependTargets) {
1312 std::string depname = t->GetName();
1313 if (t->IsImported()) {
1314 auto ttype = t->GetType();
1315 if (ttype == cmStateEnums::TargetType::STATIC_LIBRARY ||
1316 ttype == cmStateEnums::TargetType::SHARED_LIBRARY ||
1317 ttype == cmStateEnums::TargetType::UNKNOWN_LIBRARY) {
1318 depname = cmStrCat("$<TARGET_LINKER_FILE:", t->GetName(), ">");
1319 }
1320 }
1321 dependencies.push_back(depname);
1322 }
1323
1324 cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand(
1325 timestampTargetName, true, this->Dir.Work.c_str(),
1326 /*byproducts=*/timestampTargetProvides,
1327 /*depends=*/dependencies, timestampTargetCommandLines, cmPolicies::NEW,
1328 false, nullptr);
1329 this->LocalGen->AddGeneratorTarget(
1330 cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen));
1331
1332 // Set FOLDER property on the timestamp target, so it appears in the
1333 // appropriate folder in an IDE or in the file api.
1334 if (!this->TargetsFolder.empty()) {
1335 timestampTarget->SetProperty("FOLDER", this->TargetsFolder);
1336 }
1337
1338 // Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the
1339 // moc and uic executables (whichever are enabled).
1340 dependencies.clear();
1341 dependencies.push_back(timestampTargetName);
1342
1343 AddAutogenExecutableToDependencies(this->Moc, dependencies);
1344 AddAutogenExecutableToDependencies(this->Uic, dependencies);
1345
1346 // Create the custom command that outputs the timestamp file.
1347 const char timestampFileName[] = "timestamp";
1348 const std::string outputFile =
1349 cmStrCat(this->Dir.Build, "/", timestampFileName);
1350 this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
1351 this->AutogenTarget.DepFileRuleName =
1352 cmStrCat(this->GenTarget->GetName(), "_autogen/", timestampFileName);
1353 commandLines.push_back(cmMakeCommandLine(
1354 { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
1355
1356 this->AddGeneratedSource(outputFile, this->Moc);
1357 const std::string no_main_dependency;
1358 this->LocalGen->AddCustomCommandToOutput(
1359 { outputFile }, timestampByproducts, dependencies, no_main_dependency,
1360 /*implicit_depends=*/{}, commandLines, autogenComment.c_str(),
1361 this->Dir.Work.c_str(),
1362 /*cmp0116=*/cmPolicies::NEW, /*replace=*/false,
1363 /*escapeOldStyle=*/false,
1364 /*uses_terminal=*/false,
1365 /*command_expand_lists=*/false, this->AutogenTarget.DepFile, "",
1366 stdPipesUTF8);
1367
1368 // Alter variables for the autogen target which now merely wraps the
1369 // custom command
1370 dependencies.clear();
1371 dependencies.push_back(outputFile);
1372 commandLines.clear();
1373 autogenComment.clear();
1374 }
1375
1376 // Create autogen target
1377 cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
1378 this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
1379 /*byproducts=*/autogenByproducts,
1380 /*depends=*/dependencies, commandLines, cmPolicies::NEW, false,
1381 autogenComment.c_str());
1382 // Create autogen generator target
1383 this->LocalGen->AddGeneratorTarget(
1384 cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
1385
1386 // Forward origin utilities to autogen target
1387 if (this->AutogenTarget.DependOrigin) {
1388 for (BT<std::pair<std::string, bool>> const& depName :
1389 this->GenTarget->GetUtilities()) {
1390 autogenTarget->AddUtility(depName.Value.first, false, this->Makefile);
1391 }
1392 }
1393 if (!useNinjaDepfile) {
1394 // Add additional autogen target dependencies to autogen target
1395 for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1396 autogenTarget->AddUtility(depTarget->GetName(), false, this->Makefile);
1397 }
1398 }
1399
1400 // Set FOLDER property in autogen target
1401 if (!this->TargetsFolder.empty()) {
1402 autogenTarget->SetProperty("FOLDER", this->TargetsFolder);
1403 }
1404
1405 // Add autogen target to the origin target dependencies
1406 this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false,
1407 this->Makefile);
1408
1409 // Add autogen target to the global autogen target dependencies
1410 if (this->AutogenTarget.GlobalTarget) {
1411 this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen,
1412 this->AutogenTarget.Name);
1413 }
1414 }
1415
1416 return true;
1417 }
1418
InitRccTargets()1419 bool cmQtAutoGenInitializer::InitRccTargets()
1420 {
1421 for (Qrc const& qrc : this->Rcc.Qrcs) {
1422 // Register info file as generated by CMake
1423 this->Makefile->AddCMakeOutputFile(qrc.InfoFile);
1424 // Register file at target
1425 {
1426 cmSourceFile* sf = this->AddGeneratedSource(qrc.OutputFile, this->Rcc);
1427 sf->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "On");
1428 }
1429
1430 std::vector<std::string> ccOutput;
1431 ccOutput.push_back(qrc.OutputFile);
1432
1433 std::vector<std::string> ccDepends;
1434 // Add the .qrc and info file to the custom command dependencies
1435 ccDepends.push_back(qrc.QrcFile);
1436 ccDepends.push_back(qrc.InfoFile);
1437
1438 bool stdPipesUTF8 = true;
1439 cmCustomCommandLines commandLines;
1440 if (this->MultiConfig) {
1441 // Build for all configurations
1442 for (std::string const& config : this->ConfigsList) {
1443 commandLines.push_back(
1444 cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
1445 "cmake_autorcc", qrc.InfoFile, config }));
1446 }
1447 } else {
1448 commandLines.push_back(
1449 cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
1450 "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
1451 }
1452 std::string ccComment =
1453 cmStrCat("Automatic RCC for ",
1454 FileProjectRelativePath(this->Makefile, qrc.QrcFile));
1455
1456 if (qrc.Generated || this->Rcc.GlobalTarget) {
1457 // Create custom rcc target
1458 std::string ccName;
1459 {
1460 ccName = cmStrCat(this->GenTarget->GetName(), "_arcc_", qrc.QrcName);
1461 if (!qrc.Unique) {
1462 ccName += cmStrCat('_', qrc.QrcPathChecksum);
1463 }
1464
1465 cmTarget* autoRccTarget = this->LocalGen->AddUtilityCommand(
1466 ccName, true, this->Dir.Work.c_str(), ccOutput, ccDepends,
1467 commandLines, cmPolicies::NEW, false, ccComment.c_str(), false,
1468 false, "", stdPipesUTF8);
1469
1470 // Create autogen generator target
1471 this->LocalGen->AddGeneratorTarget(
1472 cm::make_unique<cmGeneratorTarget>(autoRccTarget, this->LocalGen));
1473
1474 // Set FOLDER property in autogen target
1475 if (!this->TargetsFolder.empty()) {
1476 autoRccTarget->SetProperty("FOLDER", this->TargetsFolder);
1477 }
1478 if (!this->Rcc.ExecutableTargetName.empty()) {
1479 autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName, false,
1480 this->Makefile);
1481 }
1482 }
1483 // Add autogen target to the origin target dependencies
1484 this->GenTarget->Target->AddUtility(ccName, false, this->Makefile);
1485
1486 // Add autogen target to the global autogen target dependencies
1487 if (this->Rcc.GlobalTarget) {
1488 this->GlobalInitializer->AddToGlobalAutoRcc(this->LocalGen, ccName);
1489 }
1490 } else {
1491 // Create custom rcc command
1492 {
1493 std::vector<std::string> ccByproducts;
1494
1495 // Add the resource files to the dependencies
1496 for (std::string const& fileName : qrc.Resources) {
1497 // Add resource file to the custom command dependencies
1498 ccDepends.push_back(fileName);
1499 }
1500 if (!this->Rcc.ExecutableTargetName.empty()) {
1501 ccDepends.push_back(this->Rcc.ExecutableTargetName);
1502 }
1503 std::string no_main_dependency;
1504 cmImplicitDependsList no_implicit_depends;
1505 this->LocalGen->AddCustomCommandToOutput(
1506 ccOutput, ccByproducts, ccDepends, no_main_dependency,
1507 no_implicit_depends, commandLines, ccComment.c_str(),
1508 this->Dir.Work.c_str(), cmPolicies::NEW, false, true, false, false,
1509 "", "", stdPipesUTF8);
1510 }
1511 // Reconfigure when .qrc file changes
1512 this->Makefile->AddCMakeDependFile(qrc.QrcFile);
1513 }
1514 }
1515
1516 return true;
1517 }
1518
SetupCustomTargets()1519 bool cmQtAutoGenInitializer::SetupCustomTargets()
1520 {
1521 // Create info directory on demand
1522 if (!cmSystemTools::MakeDirectory(this->Dir.Info)) {
1523 cmSystemTools::Error(cmStrCat("AutoGen: Could not create directory: ",
1524 Quoted(this->Dir.Info)));
1525 return false;
1526 }
1527
1528 // Generate autogen target info file
1529 if (this->MocOrUicEnabled()) {
1530 // Write autogen target info files
1531 if (!this->SetupWriteAutogenInfo()) {
1532 return false;
1533 }
1534 }
1535
1536 // Write AUTORCC info files
1537 return !this->Rcc.Enabled || this->SetupWriteRccInfo();
1538 }
1539
SetupWriteAutogenInfo()1540 bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
1541 {
1542 // Utility lambdas
1543 auto MfDef = [this](std::string const& key) {
1544 return this->Makefile->GetSafeDefinition(key);
1545 };
1546
1547 // Filtered headers and sources
1548 std::set<std::string> moc_skip;
1549 std::set<std::string> uic_skip;
1550 std::vector<MUFile const*> headers;
1551 std::vector<MUFile const*> sources;
1552
1553 // Filter headers
1554 {
1555 headers.reserve(this->AutogenTarget.Headers.size());
1556 for (auto const& pair : this->AutogenTarget.Headers) {
1557 MUFile const* const muf = pair.second.get();
1558 if (muf->SkipMoc) {
1559 moc_skip.insert(muf->FullPath);
1560 }
1561 if (muf->SkipUic) {
1562 uic_skip.insert(muf->FullPath);
1563 }
1564 if (muf->Generated && !this->CMP0071Accept) {
1565 continue;
1566 }
1567 if (muf->MocIt || muf->UicIt) {
1568 headers.emplace_back(muf);
1569 }
1570 }
1571 std::sort(headers.begin(), headers.end(),
1572 [](MUFile const* a, MUFile const* b) {
1573 return (a->FullPath < b->FullPath);
1574 });
1575 }
1576
1577 // Filter sources
1578 {
1579 sources.reserve(this->AutogenTarget.Sources.size());
1580 for (auto const& pair : this->AutogenTarget.Sources) {
1581 MUFile const* const muf = pair.second.get();
1582 if (muf->Generated && !this->CMP0071Accept) {
1583 continue;
1584 }
1585 if (muf->SkipMoc) {
1586 moc_skip.insert(muf->FullPath);
1587 }
1588 if (muf->SkipUic) {
1589 uic_skip.insert(muf->FullPath);
1590 }
1591 if (muf->MocIt || muf->UicIt) {
1592 sources.emplace_back(muf);
1593 }
1594 }
1595 std::sort(sources.begin(), sources.end(),
1596 [](MUFile const* a, MUFile const* b) {
1597 return (a->FullPath < b->FullPath);
1598 });
1599 }
1600
1601 // Info writer
1602 InfoWriter info;
1603
1604 // General
1605 info.SetBool("MULTI_CONFIG", this->MultiConfig);
1606 info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
1607 info.SetUInt("VERBOSITY", this->Verbosity);
1608
1609 // Directories
1610 info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
1611 info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
1612 info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
1613 info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
1614 info.Set("BUILD_DIR", this->Dir.Build);
1615 info.SetConfig("INCLUDE_DIR", this->Dir.Include);
1616
1617 info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
1618 info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
1619 info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
1620 info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
1621
1622 info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
1623 info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
1624 info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
1625 info.Set("DEP_FILE", this->AutogenTarget.DepFile);
1626 info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
1627 info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles());
1628 info.SetArray("HEADER_EXTENSIONS",
1629 this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
1630 auto cfgArray = [this](std::vector<size_t> const& configs) -> Json::Value {
1631 Json::Value value;
1632 if (!configs.empty()) {
1633 value = Json::arrayValue;
1634 for (size_t ci : configs) {
1635 value.append(this->ConfigsList[ci]);
1636 }
1637 }
1638 return value;
1639 };
1640 info.SetArrayArray("HEADERS", headers,
1641 [this, &cfgArray](Json::Value& jval, MUFile const* muf) {
1642 jval.resize(4u);
1643 jval[0u] = muf->FullPath;
1644 jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm',
1645 muf->UicIt ? 'U' : 'u');
1646 jval[2u] = this->GetMocBuildPath(*muf);
1647 jval[3u] = cfgArray(muf->Configs);
1648 });
1649 info.SetArrayArray(
1650 "SOURCES", sources, [&cfgArray](Json::Value& jval, MUFile const* muf) {
1651 jval.resize(3u);
1652 jval[0u] = muf->FullPath;
1653 jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
1654 jval[2u] = cfgArray(muf->Configs);
1655 });
1656
1657 // Write moc settings
1658 if (this->Moc.Enabled) {
1659 info.SetArray("MOC_SKIP", moc_skip);
1660 info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines);
1661 info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes);
1662 info.SetArray("MOC_OPTIONS", this->Moc.Options);
1663 info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
1664 info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
1665 info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
1666 info.SetArrayArray(
1667 "MOC_DEPEND_FILTERS", this->Moc.DependFilters,
1668 [](Json::Value& jval, std::pair<std::string, std::string> const& pair) {
1669 jval.resize(2u);
1670 jval[0u] = pair.first;
1671 jval[1u] = pair.second;
1672 });
1673 info.SetConfig("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
1674 info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
1675 info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
1676 }
1677
1678 // Write uic settings
1679 if (this->Uic.Enabled) {
1680 // Add skipped .ui files
1681 uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
1682
1683 info.SetArray("UIC_SKIP", uic_skip);
1684 info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFilesWithOptions,
1685 [](Json::Value& jval, UicT::UiFileT const& uiFile) {
1686 jval.resize(2u);
1687 jval[0u] = uiFile.first;
1688 InfoWriter::MakeStringArray(jval[1u], uiFile.second);
1689 });
1690 info.SetConfigArray("UIC_OPTIONS", this->Uic.Options);
1691 info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths);
1692 }
1693
1694 info.Save(this->AutogenTarget.InfoFile);
1695
1696 return true;
1697 }
1698
SetupWriteRccInfo()1699 bool cmQtAutoGenInitializer::SetupWriteRccInfo()
1700 {
1701 for (Qrc const& qrc : this->Rcc.Qrcs) {
1702 // Utility lambdas
1703 auto MfDef = [this](std::string const& key) {
1704 return this->Makefile->GetSafeDefinition(key);
1705 };
1706
1707 InfoWriter info;
1708
1709 // General
1710 info.SetBool("MULTI_CONFIG", this->MultiConfig);
1711 info.SetUInt("VERBOSITY", this->Verbosity);
1712
1713 // Files
1714 info.Set("LOCK_FILE", qrc.LockFile);
1715 info.SetConfig("SETTINGS_FILE", qrc.SettingsFile);
1716
1717 // Directories
1718 info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
1719 info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
1720 info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
1721 info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
1722 info.Set("BUILD_DIR", this->Dir.Build);
1723 info.SetConfig("INCLUDE_DIR", this->Dir.Include);
1724
1725 // rcc executable
1726 info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
1727 info.SetArray("RCC_LIST_OPTIONS",
1728 this->Rcc.ExecutableFeatures->ListOptions);
1729
1730 // qrc file
1731 info.Set("SOURCE", qrc.QrcFile);
1732 info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
1733 info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
1734 info.SetArray("OPTIONS", qrc.Options);
1735 info.SetArray("INPUTS", qrc.Resources);
1736
1737 info.Save(qrc.InfoFile);
1738 }
1739
1740 return true;
1741 }
1742
RegisterGeneratedSource(std::string const & filename)1743 cmSourceFile* cmQtAutoGenInitializer::RegisterGeneratedSource(
1744 std::string const& filename)
1745 {
1746 cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true);
1747 gFile->MarkAsGenerated();
1748 gFile->SetProperty("SKIP_AUTOGEN", "1");
1749 return gFile;
1750 }
1751
AddGeneratedSource(std::string const & filename,GenVarsT const & genVars,bool prepend)1752 cmSourceFile* cmQtAutoGenInitializer::AddGeneratedSource(
1753 std::string const& filename, GenVarsT const& genVars, bool prepend)
1754 {
1755 // Register source at makefile
1756 cmSourceFile* gFile = this->RegisterGeneratedSource(filename);
1757 // Add source file to target
1758 this->GenTarget->AddSource(filename, prepend);
1759
1760 // Add source file to source group
1761 this->AddToSourceGroup(filename, genVars.GenNameUpper);
1762
1763 return gFile;
1764 }
1765
AddGeneratedSource(ConfigString const & filename,GenVarsT const & genVars,bool prepend)1766 void cmQtAutoGenInitializer::AddGeneratedSource(ConfigString const& filename,
1767 GenVarsT const& genVars,
1768 bool prepend)
1769 {
1770 // XXX(xcode-per-cfg-src): Drop the Xcode-specific part of the condition
1771 // when the Xcode generator supports per-config sources.
1772 if (!this->MultiConfig || this->GlobalGen->IsXcode()) {
1773 this->AddGeneratedSource(filename.Default, genVars, prepend);
1774 return;
1775 }
1776 for (auto const& cfg : this->ConfigsList) {
1777 std::string const& filenameCfg = filename.Config.at(cfg);
1778 // Register source at makefile
1779 this->RegisterGeneratedSource(filenameCfg);
1780 // Add source file to target for this configuration.
1781 this->GenTarget->AddSource(
1782 cmStrCat("$<$<CONFIG:"_s, cfg, ">:"_s, filenameCfg, ">"_s), prepend);
1783 // Add source file to source group
1784 this->AddToSourceGroup(filenameCfg, genVars.GenNameUpper);
1785 }
1786 }
1787
AddToSourceGroup(std::string const & fileName,cm::string_view genNameUpper)1788 void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
1789 cm::string_view genNameUpper)
1790 {
1791 cmSourceGroup* sourceGroup = nullptr;
1792 // Acquire source group
1793 {
1794 std::string property;
1795 std::string groupName;
1796 {
1797 // Prefer generator specific source group name
1798 std::initializer_list<std::string> const props{
1799 cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP"
1800 };
1801 for (std::string const& prop : props) {
1802 cmValue propName = this->Makefile->GetState()->GetGlobalProperty(prop);
1803 if (cmNonempty(propName)) {
1804 groupName = *propName;
1805 property = prop;
1806 break;
1807 }
1808 }
1809 }
1810 // Generate a source group on demand
1811 if (!groupName.empty()) {
1812 sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName);
1813 if (!sourceGroup) {
1814 cmSystemTools::Error(
1815 cmStrCat(genNameUpper, " error in ", property,
1816 ": Could not find or create the source group ",
1817 cmQtAutoGen::Quoted(groupName)));
1818 }
1819 }
1820 }
1821 if (sourceGroup) {
1822 sourceGroup->AddGroupFile(fileName);
1823 }
1824 }
1825
AddCleanFile(std::string const & fileName)1826 void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
1827 {
1828 this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName,
1829 false);
1830 }
1831
ConfigFileNames(ConfigString & configString,cm::string_view prefix,cm::string_view suffix)1832 void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
1833 cm::string_view prefix,
1834 cm::string_view suffix)
1835 {
1836 configString.Default = cmStrCat(prefix, suffix);
1837 if (this->MultiConfig) {
1838 for (auto const& cfg : this->ConfigsList) {
1839 configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix);
1840 }
1841 }
1842 }
1843
ConfigFileNamesAndGenex(ConfigString & configString,std::string & genex,cm::string_view const prefix,cm::string_view const suffix)1844 void cmQtAutoGenInitializer::ConfigFileNamesAndGenex(
1845 ConfigString& configString, std::string& genex, cm::string_view const prefix,
1846 cm::string_view const suffix)
1847 {
1848 this->ConfigFileNames(configString, prefix, suffix);
1849 if (this->MultiConfig) {
1850 genex = cmStrCat(prefix, "_$<CONFIG>"_s, suffix);
1851 } else {
1852 genex = configString.Default;
1853 }
1854 }
1855
ConfigFileClean(ConfigString & configString)1856 void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
1857 {
1858 this->AddCleanFile(configString.Default);
1859 if (this->MultiConfig) {
1860 for (auto const& pair : configString.Config) {
1861 this->AddCleanFile(pair.second);
1862 }
1863 }
1864 }
1865
parseMocVersion(std::string str)1866 static cmQtAutoGen::IntegerVersion parseMocVersion(std::string str)
1867 {
1868 cmQtAutoGen::IntegerVersion result;
1869
1870 static const std::string prelude = "moc ";
1871 size_t pos = str.find(prelude);
1872 if (pos == std::string::npos) {
1873 return result;
1874 }
1875
1876 str.erase(0, prelude.size() + pos);
1877 std::istringstream iss(str);
1878 std::string major;
1879 std::string minor;
1880 if (!std::getline(iss, major, '.') || !std::getline(iss, minor, '.')) {
1881 return result;
1882 }
1883
1884 result.Major = static_cast<unsigned int>(std::stoi(major));
1885 result.Minor = static_cast<unsigned int>(std::stoi(minor));
1886 return result;
1887 }
1888
GetMocVersion(const std::string & mocExecutablePath)1889 static cmQtAutoGen::IntegerVersion GetMocVersion(
1890 const std::string& mocExecutablePath)
1891 {
1892 std::string capturedStdOut;
1893 int exitCode;
1894 if (!cmSystemTools::RunSingleCommand({ mocExecutablePath, "--version" },
1895 &capturedStdOut, nullptr, &exitCode,
1896 nullptr, cmSystemTools::OUTPUT_NONE)) {
1897 return {};
1898 }
1899
1900 if (exitCode != 0) {
1901 return {};
1902 }
1903
1904 return parseMocVersion(capturedStdOut);
1905 }
1906
FindMocExecutableFromMocTarget(cmMakefile * makefile,unsigned int qtMajorVersion)1907 static std::string FindMocExecutableFromMocTarget(cmMakefile* makefile,
1908 unsigned int qtMajorVersion)
1909 {
1910 std::string result;
1911 const std::string mocTargetName =
1912 "Qt" + std::to_string(qtMajorVersion) + "::moc";
1913 cmTarget* mocTarget = makefile->FindTargetToUse(mocTargetName);
1914 if (mocTarget) {
1915 result = mocTarget->GetSafeProperty("IMPORTED_LOCATION");
1916 }
1917 return result;
1918 }
1919
1920 std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
GetQtVersion(cmGeneratorTarget const * target,std::string mocExecutable)1921 cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target,
1922 std::string mocExecutable)
1923 {
1924 // Converts a char ptr to an unsigned int value
1925 auto toUInt = [](const char* const input) -> unsigned int {
1926 unsigned long tmp = 0;
1927 if (input && cmStrToULong(input, &tmp)) {
1928 return static_cast<unsigned int>(tmp);
1929 }
1930 return 0u;
1931 };
1932 auto toUInt2 = [](cmValue input) -> unsigned int {
1933 unsigned long tmp = 0;
1934 if (input && cmStrToULong(*input, &tmp)) {
1935 return static_cast<unsigned int>(tmp);
1936 }
1937 return 0u;
1938 };
1939
1940 // Initialize return value to a default
1941 std::pair<IntegerVersion, unsigned int> res(
1942 IntegerVersion(),
1943 toUInt(target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION",
1944 "")));
1945
1946 // Acquire known Qt versions
1947 std::vector<cmQtAutoGen::IntegerVersion> knownQtVersions;
1948 {
1949 // Qt version variable prefixes
1950 static std::initializer_list<
1951 std::pair<cm::string_view, cm::string_view>> const keys{
1952 { "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" },
1953 { "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" },
1954 { "QT_VERSION_MAJOR", "QT_VERSION_MINOR" },
1955 };
1956
1957 knownQtVersions.reserve(keys.size() * 2);
1958
1959 // Adds a version to the result (nullptr safe)
1960 auto addVersion = [&knownQtVersions, &toUInt2](cmValue major,
1961 cmValue minor) {
1962 cmQtAutoGen::IntegerVersion ver(toUInt2(major), toUInt2(minor));
1963 if (ver.Major != 0) {
1964 knownQtVersions.emplace_back(ver);
1965 }
1966 };
1967
1968 // Read versions from variables
1969 for (auto const& keyPair : keys) {
1970 addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)),
1971 target->Makefile->GetDefinition(std::string(keyPair.second)));
1972 }
1973
1974 // Read versions from directory properties
1975 for (auto const& keyPair : keys) {
1976 addVersion(target->Makefile->GetProperty(std::string(keyPair.first)),
1977 target->Makefile->GetProperty(std::string(keyPair.second)));
1978 }
1979 }
1980
1981 // Evaluate known Qt versions
1982 if (!knownQtVersions.empty()) {
1983 if (res.second == 0) {
1984 // No specific version was requested by the target:
1985 // Use highest known Qt version.
1986 res.first = knownQtVersions.at(0);
1987 } else {
1988 // Pick a version from the known versions:
1989 for (auto it : knownQtVersions) {
1990 if (it.Major == res.second) {
1991 res.first = it;
1992 break;
1993 }
1994 }
1995 }
1996 }
1997
1998 if (res.first.Major == 0) {
1999 // We could not get the version number from variables or directory
2000 // properties. This might happen if the find_package call for Qt is wrapped
2001 // in a function. Try to find the moc executable path from the available
2002 // targets and call "moc --version" to get the Qt version.
2003 if (mocExecutable.empty()) {
2004 mocExecutable =
2005 FindMocExecutableFromMocTarget(target->Makefile, res.second);
2006 }
2007 if (!mocExecutable.empty()) {
2008 res.first = GetMocVersion(mocExecutable);
2009 }
2010 }
2011
2012 return res;
2013 }
2014
GetMocBuildPath(MUFile const & muf)2015 std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf)
2016 {
2017 std::string res;
2018 if (!muf.MocIt) {
2019 return res;
2020 }
2021
2022 std::string basePath =
2023 cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_",
2024 FileNameWithoutLastExtension(muf.FullPath));
2025
2026 res = cmStrCat(basePath, ".cpp");
2027 if (this->Moc.EmittedBuildPaths.emplace(res).second) {
2028 return res;
2029 }
2030
2031 // File name already emitted.
2032 // Try appending the header suffix to the base path.
2033 basePath = cmStrCat(basePath, '_', muf.SF->GetExtension());
2034 res = cmStrCat(basePath, ".cpp");
2035 if (this->Moc.EmittedBuildPaths.emplace(res).second) {
2036 return res;
2037 }
2038
2039 // File name with header extension already emitted.
2040 // Try adding a number to the base path.
2041 constexpr std::size_t number_begin = 2;
2042 constexpr std::size_t number_end = 256;
2043 for (std::size_t ii = number_begin; ii != number_end; ++ii) {
2044 res = cmStrCat(basePath, '_', ii, ".cpp");
2045 if (this->Moc.EmittedBuildPaths.emplace(res).second) {
2046 return res;
2047 }
2048 }
2049
2050 // Output file name conflict (unlikely, but still...)
2051 cmSystemTools::Error(
2052 cmStrCat("moc output file name conflict for ", muf.FullPath));
2053
2054 return res;
2055 }
2056
GetQtExecutable(GenVarsT & genVars,const std::string & executable,bool ignoreMissingTarget) const2057 bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
2058 const std::string& executable,
2059 bool ignoreMissingTarget) const
2060 {
2061 auto print_err = [this, &genVars](std::string const& err) {
2062 cmSystemTools::Error(cmStrCat(genVars.GenNameUpper, " for target ",
2063 this->GenTarget->GetName(), ": ", err));
2064 };
2065
2066 // Custom executable
2067 {
2068 std::string const prop = cmStrCat(genVars.GenNameUpper, "_EXECUTABLE");
2069 std::string const& val = this->GenTarget->Target->GetSafeProperty(prop);
2070 if (!val.empty()) {
2071 // Evaluate generator expression
2072 {
2073 cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
2074 cmGeneratorExpression ge(lfbt);
2075 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
2076 genVars.Executable = cge->Evaluate(this->LocalGen, "");
2077 }
2078 if (genVars.Executable.empty() && !ignoreMissingTarget) {
2079 print_err(prop + " evaluates to an empty value");
2080 return false;
2081 }
2082
2083 // Create empty compiler features.
2084 genVars.ExecutableFeatures =
2085 std::make_shared<cmQtAutoGen::CompilerFeatures>();
2086 return true;
2087 }
2088 }
2089
2090 // Find executable target
2091 {
2092 // Find executable target name
2093 cm::string_view prefix;
2094 if (this->QtVersion.Major == 4) {
2095 prefix = "Qt4::";
2096 } else if (this->QtVersion.Major == 5) {
2097 prefix = "Qt5::";
2098 } else if (this->QtVersion.Major == 6) {
2099 prefix = "Qt6::";
2100 }
2101 std::string const targetName = cmStrCat(prefix, executable);
2102
2103 // Find target
2104 cmGeneratorTarget* genTarget =
2105 this->LocalGen->FindGeneratorTargetToUse(targetName);
2106 if (genTarget) {
2107 genVars.ExecutableTargetName = targetName;
2108 genVars.ExecutableTarget = genTarget;
2109 if (genTarget->IsImported()) {
2110 genVars.Executable = genTarget->ImportedGetLocation("");
2111 } else {
2112 genVars.Executable = genTarget->GetLocation("");
2113 }
2114 } else {
2115 if (ignoreMissingTarget) {
2116 // Create empty compiler features.
2117 genVars.ExecutableFeatures =
2118 std::make_shared<cmQtAutoGen::CompilerFeatures>();
2119 return true;
2120 }
2121 print_err(cmStrCat("Could not find ", executable, " executable target ",
2122 targetName));
2123 return false;
2124 }
2125 }
2126
2127 // Get executable features
2128 {
2129 std::string err;
2130 genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures(
2131 executable, genVars.Executable, err);
2132 if (!genVars.ExecutableFeatures) {
2133 print_err(err);
2134 return false;
2135 }
2136 }
2137
2138 return true;
2139 }
2140