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