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