Imported Upstream version 3.17.5
[platform/upstream/cmake.git] / Source / cmQtAutoMocUic.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 "cmQtAutoMocUic.h"
4
5 #include <algorithm>
6 #include <atomic>
7 #include <cstddef>
8 #include <map>
9 #include <mutex>
10 #include <set>
11 #include <string>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <utility>
15 #include <vector>
16
17 #include <cm/memory>
18 #include <cm/string_view>
19 #include <cmext/algorithm>
20
21 #include "cmsys/FStream.hxx"
22 #include "cmsys/RegularExpression.hxx"
23
24 #include "cm_jsoncpp_value.h"
25
26 #include "cmCryptoHash.h"
27 #include "cmFileTime.h"
28 #include "cmGccDepfileReader.h"
29 #include "cmGccDepfileReaderTypes.h"
30 #include "cmGeneratedFileStream.h"
31 #include "cmQtAutoGen.h"
32 #include "cmQtAutoGenerator.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
35 #include "cmWorkerPool.h"
36
37 #if defined(__APPLE__)
38 #  include <unistd.h>
39 #endif
40
41 namespace {
42
43 constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
44 constexpr std::size_t UiUnderscoreLength = 3;  // Length of "ui_"
45
46 /** \class cmQtAutoMocUicT
47  * \brief AUTOMOC and AUTOUIC generator
48  */
49 class cmQtAutoMocUicT : public cmQtAutoGenerator
50 {
51 public:
52   cmQtAutoMocUicT();
53   ~cmQtAutoMocUicT() override;
54
55   cmQtAutoMocUicT(cmQtAutoMocUicT const&) = delete;
56   cmQtAutoMocUicT& operator=(cmQtAutoMocUicT const&) = delete;
57
58 public:
59   // -- Types
60
61   /** Include string with sub parts.  */
62   struct IncludeKeyT
63   {
64     IncludeKeyT(std::string const& key, std::size_t basePrefixLength);
65
66     std::string Key;  // Full include string
67     std::string Dir;  // Include directory
68     std::string Base; // Base part of the include file name
69   };
70
71   /** Search key plus regular expression pair.  */
72   struct KeyExpT
73   {
74     KeyExpT(std::string key, std::string const& exp)
75       : Key(std::move(key))
76       , Exp(exp)
77     {
78     }
79
80     std::string Key;
81     cmsys::RegularExpression Exp;
82   };
83
84   /** Source file parsing cache.  */
85   class ParseCacheT
86   {
87   public:
88     // -- Types
89
90     /** Entry of the file parsing cache.  */
91     struct FileT
92     {
93       void Clear();
94
95       struct MocT
96       {
97         std::string Macro;
98         struct IncludeT
99         {
100           std::vector<IncludeKeyT> Underscore;
101           std::vector<IncludeKeyT> Dot;
102         } Include;
103         std::vector<std::string> Depends;
104       } Moc;
105
106       struct UicT
107       {
108         std::vector<IncludeKeyT> Include;
109         std::vector<std::string> Depends;
110       } Uic;
111     };
112     using FileHandleT = std::shared_ptr<FileT>;
113     using GetOrInsertT = std::pair<FileHandleT, bool>;
114
115   public:
116     ParseCacheT();
117     ~ParseCacheT();
118
119     bool ReadFromFile(std::string const& fileName);
120     bool WriteToFile(std::string const& fileName);
121
122     //! Always returns a valid handle
123     GetOrInsertT GetOrInsert(std::string const& fileName);
124
125   private:
126     std::unordered_map<std::string, FileHandleT> Map_;
127   };
128
129   /** Source file data.  */
130   class SourceFileT
131   {
132   public:
133     SourceFileT(std::string fileName)
134       : FileName(std::move(fileName))
135     {
136     }
137
138   public:
139     std::string FileName;
140     cmFileTime FileTime;
141     ParseCacheT::FileHandleT ParseData;
142     std::string BuildPath;
143     bool IsHeader = false;
144     bool Moc = false;
145     bool Uic = false;
146   };
147   using SourceFileHandleT = std::shared_ptr<SourceFileT>;
148   using SourceFileMapT = std::map<std::string, SourceFileHandleT>;
149
150   /** Meta compiler file mapping information.  */
151   struct MappingT
152   {
153     SourceFileHandleT SourceFile;
154     std::string OutputFile;
155     std::string IncludeString;
156     std::vector<SourceFileHandleT> IncluderFiles;
157   };
158   using MappingHandleT = std::shared_ptr<MappingT>;
159   using MappingMapT = std::map<std::string, MappingHandleT>;
160
161   /** Common settings.  */
162   class BaseSettingsT
163   {
164   public:
165     // -- Constructors
166     BaseSettingsT();
167     ~BaseSettingsT();
168
169     BaseSettingsT(BaseSettingsT const&) = delete;
170     BaseSettingsT& operator=(BaseSettingsT const&) = delete;
171
172     // -- Attributes
173     // - Config
174     bool MultiConfig = false;
175     IntegerVersion QtVersion = { 4, 0 };
176     unsigned int ThreadCount = 0;
177     // - Directories
178     std::string AutogenBuildDir;
179     std::string AutogenIncludeDir;
180     // - Files
181     std::string CMakeExecutable;
182     cmFileTime CMakeExecutableTime;
183     std::string ParseCacheFile;
184     std::string DepFile;
185     std::string DepFileRuleName;
186     std::vector<std::string> HeaderExtensions;
187     std::vector<std::string> ListFiles;
188   };
189
190   /** Shared common variables.  */
191   class BaseEvalT
192   {
193   public:
194     // -- Parse Cache
195     bool ParseCacheChanged = false;
196     cmFileTime ParseCacheTime;
197     ParseCacheT ParseCache;
198
199     // -- Sources
200     SourceFileMapT Headers;
201     SourceFileMapT Sources;
202   };
203
204   /** Moc settings.  */
205   class MocSettingsT
206   {
207   public:
208     // -- Constructors
209     MocSettingsT();
210     ~MocSettingsT();
211
212     MocSettingsT(MocSettingsT const&) = delete;
213     MocSettingsT& operator=(MocSettingsT const&) = delete;
214
215     // -- Const methods
216     bool skipped(std::string const& fileName) const;
217     std::string MacrosString() const;
218
219     // -- Attributes
220     bool Enabled = false;
221     bool SettingsChanged = false;
222     bool RelaxedMode = false;
223     bool PathPrefix = false;
224     bool CanOutputDependencies = false;
225     cmFileTime ExecutableTime;
226     std::string Executable;
227     std::string CompFileAbs;
228     std::string PredefsFileAbs;
229     std::unordered_set<std::string> SkipList;
230     std::vector<std::string> IncludePaths;
231     std::vector<std::string> Definitions;
232     std::vector<std::string> OptionsIncludes;
233     std::vector<std::string> OptionsDefinitions;
234     std::vector<std::string> OptionsExtra;
235     std::vector<std::string> PredefsCmd;
236     std::vector<KeyExpT> DependFilters;
237     std::vector<KeyExpT> MacroFilters;
238     cmsys::RegularExpression RegExpInclude;
239   };
240
241   /** Moc shared variables.  */
242   class MocEvalT
243   {
244   public:
245     // -- predefines file
246     cmFileTime PredefsTime;
247     // -- Mappings
248     MappingMapT HeaderMappings;
249     MappingMapT SourceMappings;
250     MappingMapT Includes;
251     // -- Discovered files
252     SourceFileMapT HeadersDiscovered;
253     // -- Output directories
254     std::unordered_set<std::string> OutputDirs;
255     // -- Mocs compilation
256     bool CompUpdated = false;
257     std::vector<std::string> CompFiles;
258   };
259
260   /** Uic settings.  */
261   class UicSettingsT
262   {
263   public:
264     struct UiFile
265     {
266       std::vector<std::string> Options;
267     };
268
269   public:
270     UicSettingsT();
271     ~UicSettingsT();
272
273     UicSettingsT(UicSettingsT const&) = delete;
274     UicSettingsT& operator=(UicSettingsT const&) = delete;
275
276     // -- Const methods
277     bool skipped(std::string const& fileName) const;
278
279     // -- Attributes
280     bool Enabled = false;
281     bool SettingsChanged = false;
282     cmFileTime ExecutableTime;
283     std::string Executable;
284     std::unordered_set<std::string> SkipList;
285     std::vector<std::string> Options;
286     std::unordered_map<std::string, UiFile> UiFiles;
287     std::vector<std::string> SearchPaths;
288     cmsys::RegularExpression RegExpInclude;
289   };
290
291   /** Uic shared variables.  */
292   class UicEvalT
293   {
294   public:
295     // -- Discovered files
296     SourceFileMapT UiFiles;
297     // -- Mappings
298     MappingMapT Includes;
299     // -- Output directories
300     std::unordered_set<std::string> OutputDirs;
301   };
302
303   /** Abstract job class for concurrent job processing.  */
304   class JobT : public cmWorkerPool::JobT
305   {
306   protected:
307     /** Protected default constructor.  */
308     JobT(bool fence = false)
309       : cmWorkerPool::JobT(fence)
310     {
311     }
312
313     //! Get the generator. Only valid during Process() call!
314     cmQtAutoMocUicT* Gen() const
315     {
316       return static_cast<cmQtAutoMocUicT*>(UserData());
317     };
318
319     // -- Accessors. Only valid during Process() call!
320     Logger const& Log() const { return Gen()->Log(); }
321     BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); }
322     BaseEvalT& BaseEval() const { return Gen()->BaseEval(); }
323     MocSettingsT const& MocConst() const { return Gen()->MocConst(); }
324     MocEvalT& MocEval() const { return Gen()->MocEval(); }
325     UicSettingsT const& UicConst() const { return Gen()->UicConst(); }
326     UicEvalT& UicEval() const { return Gen()->UicEval(); }
327
328     // -- Logging
329     std::string MessagePath(cm::string_view path) const
330     {
331       return Gen()->MessagePath(path);
332     }
333     // - Error logging with automatic abort
334     void LogError(GenT genType, cm::string_view message) const;
335     void LogCommandError(GenT genType, cm::string_view message,
336                          std::vector<std::string> const& command,
337                          std::string const& output) const;
338
339     /** @brief Run an external process. Use only during Process() call!  */
340     bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
341                     std::vector<std::string> const& command,
342                     std::string* infoMessage = nullptr);
343   };
344
345   /** Fence job utility class.  */
346   class JobFenceT : public JobT
347   {
348   public:
349     JobFenceT()
350       : JobT(true)
351     {
352     }
353     void Process() override{};
354   };
355
356   /** Generate moc_predefs.h.  */
357   class JobMocPredefsT : public JobFenceT
358   {
359     void Process() override;
360     bool Update(std::string* reason) const;
361   };
362
363   /** File parse job base class.  */
364   class JobParseT : public JobT
365   {
366   public:
367     JobParseT(SourceFileHandleT fileHandle)
368       : FileHandle(std::move(fileHandle))
369     {
370     }
371
372   protected:
373     bool ReadFile();
374     void CreateKeys(std::vector<IncludeKeyT>& container,
375                     std::set<std::string> const& source,
376                     std::size_t basePrefixLength);
377     void MocMacro();
378     void MocDependecies();
379     void MocIncludes();
380     void UicIncludes();
381
382   protected:
383     SourceFileHandleT FileHandle;
384     std::string Content;
385   };
386
387   /** Header file parse job.  */
388   class JobParseHeaderT : public JobParseT
389   {
390   public:
391     using JobParseT::JobParseT;
392     void Process() override;
393   };
394
395   /** Source file parse job.  */
396   class JobParseSourceT : public JobParseT
397   {
398   public:
399     using JobParseT::JobParseT;
400     void Process() override;
401   };
402
403   /** Evaluate cached file parse data - moc.  */
404   class JobEvalCacheT : public JobT
405   {
406   protected:
407     std::string MessageSearchLocations() const;
408     std::vector<std::string> SearchLocations;
409   };
410
411   /** Evaluate cached file parse data - moc.  */
412   class JobEvalCacheMocT : public JobEvalCacheT
413   {
414     void Process() override;
415     bool EvalHeader(SourceFileHandleT source);
416     bool EvalSource(SourceFileHandleT const& source);
417     bool FindIncludedHeader(SourceFileHandleT& headerHandle,
418                             cm::string_view includerDir,
419                             cm::string_view includeBase);
420     bool RegisterIncluded(std::string const& includeString,
421                           SourceFileHandleT includerFileHandle,
422                           SourceFileHandleT sourceFileHandle) const;
423     void RegisterMapping(MappingHandleT mappingHandle) const;
424     std::string MessageHeader(cm::string_view headerBase) const;
425   };
426
427   /** Evaluate cached file parse data - uic.  */
428   class JobEvalCacheUicT : public JobEvalCacheT
429   {
430     void Process() override;
431     bool EvalFile(SourceFileHandleT const& sourceFileHandle);
432     bool FindIncludedUi(cm::string_view sourceDirPrefix,
433                         cm::string_view includePrefix);
434     bool RegisterMapping(std::string const& includeString,
435                          SourceFileHandleT includerFileHandle);
436
437     std::string UiName;
438     SourceFileHandleT UiFileHandle;
439   };
440
441   /** Evaluate cached file parse data - finish  */
442   class JobEvalCacheFinishT : public JobFenceT
443   {
444     void Process() override;
445   };
446
447   /** Dependency probing base job.  */
448   class JobProbeDepsT : public JobT
449   {
450   };
451
452   /** Probes file dependencies and generates moc compile jobs.  */
453   class JobProbeDepsMocT : public JobProbeDepsT
454   {
455     void Process() override;
456     bool Generate(MappingHandleT const& mapping, bool compFile) const;
457     bool Probe(MappingT const& mapping, std::string* reason) const;
458     std::pair<std::string, cmFileTime> FindDependency(
459       std::string const& sourceDir, std::string const& includeString) const;
460   };
461
462   /** Probes file dependencies and generates uic compile jobs.  */
463   class JobProbeDepsUicT : public JobProbeDepsT
464   {
465     void Process() override;
466     bool Probe(MappingT const& mapping, std::string* reason) const;
467   };
468
469   /** Dependency probing finish job.  */
470   class JobProbeDepsFinishT : public JobFenceT
471   {
472     void Process() override;
473   };
474
475   /** Meta compiler base job.  */
476   class JobCompileT : public JobT
477   {
478   public:
479     JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason)
480       : Mapping(std::move(uicMapping))
481       , Reason(std::move(reason))
482     {
483     }
484
485   protected:
486     MappingHandleT Mapping;
487     std::unique_ptr<std::string> Reason;
488   };
489
490   /** moc compiles a file.  */
491   class JobCompileMocT : public JobCompileT
492   {
493   public:
494     JobCompileMocT(MappingHandleT uicMapping,
495                    std::unique_ptr<std::string> reason,
496                    ParseCacheT::FileHandleT cacheEntry)
497       : JobCompileT(std::move(uicMapping), std::move(reason))
498       , CacheEntry(std::move(cacheEntry))
499     {
500     }
501     void Process() override;
502
503   protected:
504     ParseCacheT::FileHandleT CacheEntry;
505   };
506
507   /** uic compiles a file.  */
508   class JobCompileUicT : public JobCompileT
509   {
510   public:
511     using JobCompileT::JobCompileT;
512     void Process() override;
513   };
514
515   /** Generate mocs_compilation.cpp.  */
516   class JobMocsCompilationT : public JobFenceT
517   {
518   private:
519     void Process() override;
520   };
521
522   class JobDepFilesMergeT : public JobFenceT
523   {
524   private:
525     void Process() override;
526   };
527
528   /** @brief The last job.  */
529   class JobFinishT : public JobFenceT
530   {
531   private:
532     void Process() override;
533   };
534
535   // -- Const settings interface
536   BaseSettingsT const& BaseConst() const { return this->BaseConst_; }
537   BaseEvalT& BaseEval() { return this->BaseEval_; }
538   MocSettingsT const& MocConst() const { return this->MocConst_; }
539   MocEvalT& MocEval() { return this->MocEval_; }
540   UicSettingsT const& UicConst() const { return this->UicConst_; }
541   UicEvalT& UicEval() { return this->UicEval_; }
542
543   // -- Parallel job processing interface
544   cmWorkerPool& WorkerPool() { return WorkerPool_; }
545   void AbortError() { Abort(true); }
546   void AbortSuccess() { Abort(false); }
547
548   // -- Utility
549   std::string AbsoluteBuildPath(cm::string_view relativePath) const;
550   std::string AbsoluteIncludePath(cm::string_view relativePath) const;
551   template <class JOBTYPE>
552   void CreateParseJobs(SourceFileMapT const& sourceMap);
553   std::string CollapseFullPathTS(std::string const& path) const;
554
555 private:
556   // -- Abstract processing interface
557   bool InitFromInfo(InfoT const& info) override;
558   void InitJobs();
559   bool Process() override;
560   // -- Settings file
561   void SettingsFileRead();
562   bool SettingsFileWrite();
563   // -- Parse cache
564   void ParseCacheRead();
565   bool ParseCacheWrite();
566   // -- Thread processing
567   void Abort(bool error);
568   // -- Generation
569   bool CreateDirectories();
570   // -- Support for depfiles
571   static std::vector<std::string> dependenciesFromDepFile(
572     const char* filePath);
573
574 private:
575   // -- Settings
576   BaseSettingsT BaseConst_;
577   BaseEvalT BaseEval_;
578   MocSettingsT MocConst_;
579   MocEvalT MocEval_;
580   UicSettingsT UicConst_;
581   UicEvalT UicEval_;
582   // -- Settings file
583   std::string SettingsFile_;
584   std::string SettingsStringMoc_;
585   std::string SettingsStringUic_;
586   // -- Worker thread pool
587   std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
588   cmWorkerPool WorkerPool_;
589   // -- Concurrent processing
590   mutable std::mutex CMakeLibMutex_;
591 };
592
593 cmQtAutoMocUicT::IncludeKeyT::IncludeKeyT(std::string const& key,
594                                           std::size_t basePrefixLength)
595   : Key(key)
596   , Dir(SubDirPrefix(key))
597   , Base(cmSystemTools::GetFilenameWithoutLastExtension(key))
598 {
599   if (basePrefixLength != 0) {
600     Base = Base.substr(basePrefixLength);
601   }
602 }
603
604 void cmQtAutoMocUicT::ParseCacheT::FileT::Clear()
605 {
606   Moc.Macro.clear();
607   Moc.Include.Underscore.clear();
608   Moc.Include.Dot.clear();
609   Moc.Depends.clear();
610
611   Uic.Include.clear();
612   Uic.Depends.clear();
613 }
614
615 cmQtAutoMocUicT::ParseCacheT::GetOrInsertT
616 cmQtAutoMocUicT::ParseCacheT::GetOrInsert(std::string const& fileName)
617 {
618   // Find existing entry
619   {
620     auto it = Map_.find(fileName);
621     if (it != Map_.end()) {
622       return GetOrInsertT{ it->second, false };
623     }
624   }
625
626   // Insert new entry
627   return GetOrInsertT{
628     Map_.emplace(fileName, std::make_shared<FileT>()).first->second, true
629   };
630 }
631
632 cmQtAutoMocUicT::ParseCacheT::ParseCacheT() = default;
633 cmQtAutoMocUicT::ParseCacheT::~ParseCacheT() = default;
634
635 bool cmQtAutoMocUicT::ParseCacheT::ReadFromFile(std::string const& fileName)
636 {
637   cmsys::ifstream fin(fileName.c_str());
638   if (!fin) {
639     return false;
640   }
641   FileHandleT fileHandle;
642
643   std::string line;
644   while (std::getline(fin, line)) {
645     // Check if this an empty or a comment line
646     if (line.empty() || line.front() == '#') {
647       continue;
648     }
649     // Drop carriage return character at the end
650     if (line.back() == '\r') {
651       line.pop_back();
652       if (line.empty()) {
653         continue;
654       }
655     }
656     // Check if this a file name line
657     if (line.front() != ' ') {
658       fileHandle = GetOrInsert(line).first;
659       continue;
660     }
661
662     // Bad line or bad file handle
663     if (!fileHandle || (line.size() < 6)) {
664       continue;
665     }
666
667     constexpr std::size_t offset = 5;
668     if (cmHasLiteralPrefix(line, " mmc:")) {
669       fileHandle->Moc.Macro = line.substr(offset);
670       continue;
671     }
672     if (cmHasLiteralPrefix(line, " miu:")) {
673       fileHandle->Moc.Include.Underscore.emplace_back(line.substr(offset),
674                                                       MocUnderscoreLength);
675       continue;
676     }
677     if (cmHasLiteralPrefix(line, " mid:")) {
678       fileHandle->Moc.Include.Dot.emplace_back(line.substr(offset), 0);
679       continue;
680     }
681     if (cmHasLiteralPrefix(line, " mdp:")) {
682       fileHandle->Moc.Depends.emplace_back(line.substr(offset));
683       continue;
684     }
685     if (cmHasLiteralPrefix(line, " uic:")) {
686       fileHandle->Uic.Include.emplace_back(line.substr(offset),
687                                            UiUnderscoreLength);
688       continue;
689     }
690     if (cmHasLiteralPrefix(line, " udp:")) {
691       fileHandle->Uic.Depends.emplace_back(line.substr(offset));
692       continue;
693     }
694   }
695   return true;
696 }
697
698 bool cmQtAutoMocUicT::ParseCacheT::WriteToFile(std::string const& fileName)
699 {
700   cmGeneratedFileStream ofs(fileName);
701   if (!ofs) {
702     return false;
703   }
704   ofs << "# Generated by CMake. Changes will be overwritten." << std::endl;
705   for (auto const& pair : Map_) {
706     ofs << pair.first << std::endl;
707     FileT const& file = *pair.second;
708     if (!file.Moc.Macro.empty()) {
709       ofs << " mmc:" << file.Moc.Macro << std::endl;
710     }
711     for (IncludeKeyT const& item : file.Moc.Include.Underscore) {
712       ofs << " miu:" << item.Key << std::endl;
713     }
714     for (IncludeKeyT const& item : file.Moc.Include.Dot) {
715       ofs << " mid:" << item.Key << std::endl;
716     }
717     for (std::string const& item : file.Moc.Depends) {
718       ofs << " mdp:" << item << std::endl;
719     }
720     for (IncludeKeyT const& item : file.Uic.Include) {
721       ofs << " uic:" << item.Key << std::endl;
722     }
723     for (std::string const& item : file.Uic.Depends) {
724       ofs << " udp:" << item << std::endl;
725     }
726   }
727   return ofs.Close();
728 }
729
730 cmQtAutoMocUicT::BaseSettingsT::BaseSettingsT() = default;
731 cmQtAutoMocUicT::BaseSettingsT::~BaseSettingsT() = default;
732
733 cmQtAutoMocUicT::MocSettingsT::MocSettingsT()
734 {
735   RegExpInclude.compile(
736     "(^|\n)[ \t]*#[ \t]*include[ \t]+"
737     "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
738 }
739
740 cmQtAutoMocUicT::MocSettingsT::~MocSettingsT() = default;
741
742 bool cmQtAutoMocUicT::MocSettingsT::skipped(std::string const& fileName) const
743 {
744   return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
745 }
746
747 std::string cmQtAutoMocUicT::MocSettingsT::MacrosString() const
748 {
749   std::string res;
750   const auto itB = MacroFilters.cbegin();
751   const auto itE = MacroFilters.cend();
752   const auto itL = itE - 1;
753   auto itC = itB;
754   for (; itC != itE; ++itC) {
755     // Separator
756     if (itC != itB) {
757       if (itC != itL) {
758         res += ", ";
759       } else {
760         res += " or ";
761       }
762     }
763     // Key
764     res += itC->Key;
765   }
766   return res;
767 }
768
769 cmQtAutoMocUicT::UicSettingsT::UicSettingsT()
770 {
771   RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
772                         "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
773 }
774
775 cmQtAutoMocUicT::UicSettingsT::~UicSettingsT() = default;
776
777 bool cmQtAutoMocUicT::UicSettingsT::skipped(std::string const& fileName) const
778 {
779   return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
780 }
781
782 void cmQtAutoMocUicT::JobT::LogError(GenT genType,
783                                      cm::string_view message) const
784 {
785   Gen()->AbortError();
786   Gen()->Log().Error(genType, message);
787 }
788
789 void cmQtAutoMocUicT::JobT::LogCommandError(
790   GenT genType, cm::string_view message,
791   std::vector<std::string> const& command, std::string const& output) const
792 {
793   Gen()->AbortError();
794   Gen()->Log().ErrorCommand(genType, message, command, output);
795 }
796
797 bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType,
798                                        cmWorkerPool::ProcessResultT& result,
799                                        std::vector<std::string> const& command,
800                                        std::string* infoMessage)
801 {
802   // Log command
803   if (Log().Verbose()) {
804     cm::string_view info;
805     if (infoMessage != nullptr) {
806       info = *infoMessage;
807     }
808     Log().Info(genType,
809                cmStrCat(info,
810                         info.empty() || cmHasSuffix(info, '\n') ? "" : "\n",
811                         QuotedCommand(command), '\n'));
812   }
813   // Run command
814   return cmWorkerPool::JobT::RunProcess(result, command,
815                                         BaseConst().AutogenBuildDir);
816 }
817
818 void cmQtAutoMocUicT::JobMocPredefsT::Process()
819 {
820   // (Re)generate moc_predefs.h on demand
821   std::unique_ptr<std::string> reason;
822   if (Log().Verbose()) {
823     reason = cm::make_unique<std::string>();
824   }
825   if (!Update(reason.get())) {
826     return;
827   }
828   std::string const& predefsFileAbs = MocConst().PredefsFileAbs;
829   {
830     cmWorkerPool::ProcessResultT result;
831     {
832       // Compose command
833       std::vector<std::string> cmd = MocConst().PredefsCmd;
834       // Add definitions
835       cm::append(cmd, MocConst().OptionsDefinitions);
836       // Add includes
837       cm::append(cmd, MocConst().OptionsIncludes);
838       // Execute command
839       if (!RunProcess(GenT::MOC, result, cmd, reason.get())) {
840         LogCommandError(GenT::MOC,
841                         cmStrCat("The content generation command for ",
842                                  MessagePath(predefsFileAbs), " failed.\n",
843                                  result.ErrorMessage),
844                         cmd, result.StdOut);
845         return;
846       }
847     }
848
849     // (Re)write predefs file only on demand
850     if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) {
851       if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) {
852         LogError(
853           GenT::MOC,
854           cmStrCat("Writing ", MessagePath(predefsFileAbs), " failed."));
855         return;
856       }
857     } else {
858       // Touch to update the time stamp
859       if (Log().Verbose()) {
860         Log().Info(GenT::MOC, "Touching " + MessagePath(predefsFileAbs));
861       }
862       if (!cmSystemTools::Touch(predefsFileAbs, false)) {
863         LogError(
864           GenT::MOC,
865           cmStrCat("Touching ", MessagePath(predefsFileAbs), " failed."));
866         return;
867       }
868     }
869   }
870
871   // Read file time afterwards
872   if (!MocEval().PredefsTime.Load(predefsFileAbs)) {
873     LogError(GenT::MOC,
874              cmStrCat("Reading the file time of ", MessagePath(predefsFileAbs),
875                       " failed."));
876     return;
877   }
878 }
879
880 bool cmQtAutoMocUicT::JobMocPredefsT::Update(std::string* reason) const
881 {
882   // Test if the file exists
883   if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) {
884     if (reason != nullptr) {
885       *reason = cmStrCat("Generating ", MessagePath(MocConst().PredefsFileAbs),
886                          ", because it doesn't exist.");
887     }
888     return true;
889   }
890
891   // Test if the settings changed
892   if (MocConst().SettingsChanged) {
893     if (reason != nullptr) {
894       *reason = cmStrCat("Generating ", MessagePath(MocConst().PredefsFileAbs),
895                          ", because the moc settings changed.");
896     }
897     return true;
898   }
899
900   // Test if the executable is newer
901   {
902     std::string const& exec = MocConst().PredefsCmd.at(0);
903     cmFileTime execTime;
904     if (execTime.Load(exec)) {
905       if (MocEval().PredefsTime.Older(execTime)) {
906         if (reason != nullptr) {
907           *reason =
908             cmStrCat("Generating ", MessagePath(MocConst().PredefsFileAbs),
909                      " because it is older than ", MessagePath(exec), '.');
910         }
911         return true;
912       }
913     }
914   }
915
916   return false;
917 }
918
919 bool cmQtAutoMocUicT::JobParseT::ReadFile()
920 {
921   // Clear old parse information
922   FileHandle->ParseData->Clear();
923   std::string const& fileName = FileHandle->FileName;
924   // Write info
925   if (Log().Verbose()) {
926     Log().Info(GenT::GEN, cmStrCat("Parsing ", MessagePath(fileName)));
927   }
928   // Read file content
929   {
930     std::string error;
931     if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) {
932       LogError(
933         GenT::GEN,
934         cmStrCat("Could not read ", MessagePath(fileName), ".\n", error));
935       return false;
936     }
937   }
938   // Warn if empty
939   if (Content.empty()) {
940     Log().Warning(GenT::GEN, cmStrCat(MessagePath(fileName), " is empty."));
941     return false;
942   }
943   return true;
944 }
945
946 void cmQtAutoMocUicT::JobParseT::CreateKeys(
947   std::vector<IncludeKeyT>& container, std::set<std::string> const& source,
948   std::size_t basePrefixLength)
949 {
950   if (source.empty()) {
951     return;
952   }
953   container.reserve(source.size());
954   for (std::string const& src : source) {
955     container.emplace_back(src, basePrefixLength);
956   }
957 }
958
959 void cmQtAutoMocUicT::JobParseT::MocMacro()
960 {
961   for (KeyExpT const& filter : MocConst().MacroFilters) {
962     // Run a simple find string check
963     if (Content.find(filter.Key) == std::string::npos) {
964       continue;
965     }
966     // Run the expensive regular expression check loop
967     cmsys::RegularExpressionMatch match;
968     if (filter.Exp.find(Content.c_str(), match)) {
969       // Keep detected macro name
970       FileHandle->ParseData->Moc.Macro = filter.Key;
971       return;
972     }
973   }
974 }
975
976 void cmQtAutoMocUicT::JobParseT::MocDependecies()
977 {
978   if (MocConst().DependFilters.empty() || MocConst().CanOutputDependencies) {
979     return;
980   }
981
982   // Find dependency strings
983   std::set<std::string> parseDepends;
984   for (KeyExpT const& filter : MocConst().DependFilters) {
985     // Run a simple find string check
986     if (Content.find(filter.Key) == std::string::npos) {
987       continue;
988     }
989     // Run the expensive regular expression check loop
990     const char* contentChars = Content.c_str();
991     cmsys::RegularExpressionMatch match;
992     while (filter.Exp.find(contentChars, match)) {
993       {
994         std::string dep = match.match(1);
995         if (!dep.empty()) {
996           parseDepends.emplace(std::move(dep));
997         }
998       }
999       contentChars += match.end();
1000     }
1001   }
1002
1003   // Store dependency strings
1004   {
1005     auto& Depends = FileHandle->ParseData->Moc.Depends;
1006     Depends.reserve(parseDepends.size());
1007     for (std::string const& item : parseDepends) {
1008       Depends.emplace_back(item);
1009       // Replace end of line characters in filenames
1010       std::string& path = Depends.back();
1011       std::replace(path.begin(), path.end(), '\n', ' ');
1012       std::replace(path.begin(), path.end(), '\r', ' ');
1013     }
1014   }
1015 }
1016
1017 void cmQtAutoMocUicT::JobParseT::MocIncludes()
1018 {
1019   if (Content.find("moc") == std::string::npos) {
1020     return;
1021   }
1022
1023   std::set<std::string> underscore;
1024   std::set<std::string> dot;
1025   {
1026     const char* contentChars = Content.c_str();
1027     cmsys::RegularExpression const& regExp = MocConst().RegExpInclude;
1028     cmsys::RegularExpressionMatch match;
1029     while (regExp.find(contentChars, match)) {
1030       std::string incString = match.match(2);
1031       std::string const incBase =
1032         cmSystemTools::GetFilenameWithoutLastExtension(incString);
1033       if (cmHasLiteralPrefix(incBase, "moc_")) {
1034         // moc_<BASE>.cpp
1035         // Remove the moc_ part from the base name
1036         underscore.emplace(std::move(incString));
1037       } else {
1038         // <BASE>.moc
1039         dot.emplace(std::move(incString));
1040       }
1041       // Forward content pointer
1042       contentChars += match.end();
1043     }
1044   }
1045   auto& Include = FileHandle->ParseData->Moc.Include;
1046   CreateKeys(Include.Underscore, underscore, MocUnderscoreLength);
1047   CreateKeys(Include.Dot, dot, 0);
1048 }
1049
1050 void cmQtAutoMocUicT::JobParseT::UicIncludes()
1051 {
1052   if (Content.find("ui_") == std::string::npos) {
1053     return;
1054   }
1055
1056   std::set<std::string> includes;
1057   {
1058     const char* contentChars = Content.c_str();
1059     cmsys::RegularExpression const& regExp = UicConst().RegExpInclude;
1060     cmsys::RegularExpressionMatch match;
1061     while (regExp.find(contentChars, match)) {
1062       includes.emplace(match.match(2));
1063       // Forward content pointer
1064       contentChars += match.end();
1065     }
1066   }
1067   CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength);
1068 }
1069
1070 void cmQtAutoMocUicT::JobParseHeaderT::Process()
1071 {
1072   if (!ReadFile()) {
1073     return;
1074   }
1075   // Moc parsing
1076   if (FileHandle->Moc) {
1077     MocMacro();
1078     MocDependecies();
1079   }
1080   // Uic parsing
1081   if (FileHandle->Uic) {
1082     UicIncludes();
1083   }
1084 }
1085
1086 void cmQtAutoMocUicT::JobParseSourceT::Process()
1087 {
1088   if (!ReadFile()) {
1089     return;
1090   }
1091   // Moc parsing
1092   if (FileHandle->Moc) {
1093     MocMacro();
1094     MocDependecies();
1095     MocIncludes();
1096   }
1097   // Uic parsing
1098   if (FileHandle->Uic) {
1099     UicIncludes();
1100   }
1101 }
1102
1103 std::string cmQtAutoMocUicT::JobEvalCacheT::MessageSearchLocations() const
1104 {
1105   std::string res;
1106   res.reserve(512);
1107   for (std::string const& path : SearchLocations) {
1108     res += "  ";
1109     res += MessagePath(path);
1110     res += '\n';
1111   }
1112   return res;
1113 }
1114
1115 void cmQtAutoMocUicT::JobEvalCacheMocT::Process()
1116 {
1117   // Evaluate headers
1118   for (auto const& pair : BaseEval().Headers) {
1119     if (!EvalHeader(pair.second)) {
1120       return;
1121     }
1122   }
1123   // Evaluate sources
1124   for (auto const& pair : BaseEval().Sources) {
1125     if (!EvalSource(pair.second)) {
1126       return;
1127     }
1128   }
1129 }
1130
1131 bool cmQtAutoMocUicT::JobEvalCacheMocT::EvalHeader(SourceFileHandleT source)
1132 {
1133   SourceFileT const& sourceFile = *source;
1134   auto const& parseData = sourceFile.ParseData->Moc;
1135   if (!source->Moc) {
1136     return true;
1137   }
1138
1139   if (!parseData.Macro.empty()) {
1140     // Create a new mapping
1141     MappingHandleT handle = std::make_shared<MappingT>();
1142     handle->SourceFile = std::move(source);
1143
1144     // Absolute build path
1145     if (BaseConst().MultiConfig) {
1146       handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath);
1147     } else {
1148       handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath);
1149     }
1150
1151     // Register mapping in headers map
1152     RegisterMapping(handle);
1153   }
1154
1155   return true;
1156 }
1157
1158 bool cmQtAutoMocUicT::JobEvalCacheMocT::EvalSource(
1159   SourceFileHandleT const& source)
1160 {
1161   SourceFileT const& sourceFile = *source;
1162   auto const& parseData = sourceFile.ParseData->Moc;
1163   if (!sourceFile.Moc ||
1164       (parseData.Macro.empty() && parseData.Include.Underscore.empty() &&
1165        parseData.Include.Dot.empty())) {
1166     return true;
1167   }
1168
1169   std::string const sourceDirPrefix = SubDirPrefix(sourceFile.FileName);
1170   std::string const sourceBase =
1171     cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName);
1172
1173   // For relaxed mode check if the own "moc_" or ".moc" file is included
1174   bool const relaxedMode = MocConst().RelaxedMode;
1175   bool sourceIncludesMocUnderscore = false;
1176   bool sourceIncludesDotMoc = false;
1177   // Check if the sources own "moc_" or ".moc" file is included
1178   if (relaxedMode) {
1179     for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
1180       if (incKey.Base == sourceBase) {
1181         sourceIncludesMocUnderscore = true;
1182         break;
1183       }
1184     }
1185   }
1186   for (IncludeKeyT const& incKey : parseData.Include.Dot) {
1187     if (incKey.Base == sourceBase) {
1188       sourceIncludesDotMoc = true;
1189       break;
1190     }
1191   }
1192
1193   // Check if this source needs to be moc processed but doesn't.
1194   if (!sourceIncludesDotMoc && !parseData.Macro.empty() &&
1195       !(relaxedMode && sourceIncludesMocUnderscore)) {
1196     LogError(GenT::MOC,
1197              cmStrCat(MessagePath(sourceFile.FileName), "\ncontains a ",
1198                       Quoted(parseData.Macro), " macro, but does not include ",
1199                       MessagePath(sourceBase + ".moc"),
1200                       "!\nConsider to\n  - add #include \"", sourceBase,
1201                       ".moc\"\n  - enable SKIP_AUTOMOC for this file"));
1202     return false;
1203   }
1204
1205   // Evaluate "moc_" includes
1206   for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
1207     SourceFileHandleT headerHandle;
1208     {
1209       std::string const headerBase = cmStrCat(incKey.Dir, incKey.Base);
1210       if (!FindIncludedHeader(headerHandle, sourceDirPrefix, headerBase)) {
1211         LogError(GenT::MOC,
1212                  cmStrCat(MessagePath(sourceFile.FileName),
1213                           "\nincludes the moc file ", MessagePath(incKey.Key),
1214                           ",\nbut a header ", MessageHeader(headerBase),
1215                           "\ncould not be found "
1216                           "in the following directories\n",
1217                           MessageSearchLocations()));
1218         return false;
1219       }
1220     }
1221     // The include might be handled differently in relaxed mode
1222     if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() &&
1223         (incKey.Base == sourceBase)) {
1224       // The <BASE>.cpp file includes a Qt macro but does not include the
1225       // <BASE>.moc file. In this case, the moc_<BASE>.cpp should probably
1226       // be generated from <BASE>.cpp instead of <BASE>.h, because otherwise
1227       // it won't build. But warn, since this is not how it is supposed to be
1228       // used. This is for KDE4 compatibility.
1229
1230       // Issue a warning
1231       Log().Warning(
1232         GenT::MOC,
1233         cmStrCat(MessagePath(sourceFile.FileName), "\ncontains a ",
1234                  Quoted(parseData.Macro), " macro, but does not include ",
1235                  MessagePath(sourceBase + ".moc"), ".\nInstead it includes ",
1236                  MessagePath(incKey.Key), ".\nRunning moc on the source\n  ",
1237                  MessagePath(sourceFile.FileName), "!\nBetter include ",
1238                  MessagePath(sourceBase + ".moc"),
1239                  " for compatibility with regular mode.\n",
1240                  "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"));
1241
1242       // Create mapping
1243       if (!RegisterIncluded(incKey.Key, source, source)) {
1244         return false;
1245       }
1246       continue;
1247     }
1248
1249     // Check if header is skipped
1250     if (MocConst().skipped(headerHandle->FileName)) {
1251       continue;
1252     }
1253     // Create mapping
1254     if (!RegisterIncluded(incKey.Key, source, std::move(headerHandle))) {
1255       return false;
1256     }
1257   }
1258
1259   // Evaluate ".moc" includes
1260   if (relaxedMode) {
1261     // Relaxed mode
1262     for (IncludeKeyT const& incKey : parseData.Include.Dot) {
1263       // Check if this is the sources own .moc file
1264       bool const ownMoc = (incKey.Base == sourceBase);
1265       if (ownMoc && !parseData.Macro.empty()) {
1266         // Create mapping for the regular use case
1267         if (!RegisterIncluded(incKey.Key, source, source)) {
1268           return false;
1269         }
1270         continue;
1271       }
1272       // Try to find a header instead but issue a warning.
1273       // This is for KDE4 compatibility.
1274       SourceFileHandleT headerHandle;
1275       {
1276         std::string const headerBase = cmStrCat(incKey.Dir, incKey.Base);
1277         if (!FindIncludedHeader(headerHandle, sourceDirPrefix, headerBase)) {
1278           LogError(
1279             GenT::MOC,
1280             cmStrCat(
1281               MessagePath(sourceFile.FileName), "\nincludes the moc file ",
1282               MessagePath(incKey.Key),
1283               ",\nwhich seems to be the moc file from a different source "
1284               "file.\nCMAKE_AUTOMOC_RELAXED_MODE:\nAlso a matching header ",
1285               MessageHeader(headerBase),
1286               "\ncould not be found in the following directories\n",
1287               MessageSearchLocations()));
1288           return false;
1289         }
1290       }
1291       // Check if header is skipped
1292       if (MocConst().skipped(headerHandle->FileName)) {
1293         continue;
1294       }
1295       // Issue a warning
1296       if (ownMoc && parseData.Macro.empty()) {
1297         Log().Warning(
1298           GenT::MOC,
1299           cmStrCat(MessagePath(sourceFile.FileName),
1300                    "\nincludes the moc file ", MessagePath(incKey.Key),
1301                    ", but does not contain a\n", MocConst().MacrosString(),
1302                    " macro.\nRunning moc on the header\n  ",
1303                    MessagePath(headerHandle->FileName), "!\nBetter include ",
1304                    MessagePath("moc_" + incKey.Base + ".cpp"),
1305                    " for a compatibility with regular mode.\n",
1306                    "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"));
1307       } else {
1308         Log().Warning(
1309           GenT::MOC,
1310           cmStrCat(MessagePath(sourceFile.FileName),
1311                    "\nincludes the moc file ", MessagePath(incKey.Key),
1312                    " instead of ", MessagePath("moc_" + incKey.Base + ".cpp"),
1313                    ".\nRunning moc on the header\n  ",
1314                    MessagePath(headerHandle->FileName), "!\nBetter include ",
1315                    MessagePath("moc_" + incKey.Base + ".cpp"),
1316                    " for compatibility with regular mode.\n",
1317                    "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"));
1318       }
1319       // Create mapping
1320       if (!RegisterIncluded(incKey.Key, source, std::move(headerHandle))) {
1321         return false;
1322       }
1323     }
1324   } else {
1325     // Strict mode
1326     for (IncludeKeyT const& incKey : parseData.Include.Dot) {
1327       // Check if this is the sources own .moc file
1328       bool const ownMoc = (incKey.Base == sourceBase);
1329       if (!ownMoc) {
1330         // Don't allow <BASE>.moc include other than own in regular mode
1331         LogError(GenT::MOC,
1332                  cmStrCat(MessagePath(sourceFile.FileName),
1333                           "\nincludes the moc file ", MessagePath(incKey.Key),
1334                           ",\nwhich seems to be the moc file from a different "
1335                           "source file.\nThis is not supported.  Include ",
1336                           MessagePath(sourceBase + ".moc"),
1337                           " to run moc on this source file."));
1338         return false;
1339       }
1340       // Accept but issue a warning if moc isn't required
1341       if (parseData.Macro.empty()) {
1342         Log().Warning(GenT::MOC,
1343                       cmStrCat(MessagePath(sourceFile.FileName),
1344                                "\nincludes the moc file ",
1345                                MessagePath(incKey.Key),
1346                                ", but does not contain a ",
1347                                MocConst().MacrosString(), " macro."));
1348       }
1349       // Create mapping
1350       if (!RegisterIncluded(incKey.Key, source, source)) {
1351         return false;
1352       }
1353     }
1354   }
1355
1356   return true;
1357 }
1358
1359 bool cmQtAutoMocUicT::JobEvalCacheMocT::FindIncludedHeader(
1360   SourceFileHandleT& headerHandle, cm::string_view includerDir,
1361   cm::string_view includeBase)
1362 {
1363   // Clear search locations
1364   SearchLocations.clear();
1365
1366   auto findHeader = [this,
1367                      &headerHandle](std::string const& basePath) -> bool {
1368     bool found = false;
1369     for (std::string const& ext : this->BaseConst().HeaderExtensions) {
1370       std::string const testPath =
1371         this->Gen()->CollapseFullPathTS(cmStrCat(basePath, '.', ext));
1372       cmFileTime fileTime;
1373       if (!fileTime.Load(testPath)) {
1374         // File not found
1375         continue;
1376       }
1377
1378       // Return a known file if it exists already
1379       {
1380         auto it = BaseEval().Headers.find(testPath);
1381         if (it != BaseEval().Headers.end()) {
1382           headerHandle = it->second;
1383           found = true;
1384           break;
1385         }
1386       }
1387
1388       // Created and return discovered file entry
1389       {
1390         SourceFileHandleT& handle = MocEval().HeadersDiscovered[testPath];
1391         if (!handle) {
1392           handle = std::make_shared<SourceFileT>(testPath);
1393           handle->FileTime = fileTime;
1394           handle->IsHeader = true;
1395           handle->Moc = true;
1396         }
1397         headerHandle = handle;
1398         found = true;
1399         break;
1400       }
1401     }
1402     if (!found) {
1403       this->SearchLocations.emplace_back(cmQtAutoGen::ParentDir(basePath));
1404     }
1405     return found;
1406   };
1407
1408   // Search in vicinity of the source
1409   if (findHeader(cmStrCat(includerDir, includeBase))) {
1410     return true;
1411   }
1412   // Search in include directories
1413   for (std::string const& path : MocConst().IncludePaths) {
1414     if (findHeader(cmStrCat(path, '/', includeBase))) {
1415       return true;
1416     }
1417   }
1418   // Return without success
1419   return false;
1420 }
1421
1422 bool cmQtAutoMocUicT::JobEvalCacheMocT::RegisterIncluded(
1423   std::string const& includeString, SourceFileHandleT includerFileHandle,
1424   SourceFileHandleT sourceFileHandle) const
1425 {
1426   // Check if this file is already included
1427   MappingHandleT& handle = MocEval().Includes[includeString];
1428   if (handle) {
1429     // Check if the output file would be generated from different source files
1430     if (handle->SourceFile != sourceFileHandle) {
1431       std::string files =
1432         cmStrCat("  ", MessagePath(includerFileHandle->FileName), '\n');
1433       for (auto const& item : handle->IncluderFiles) {
1434         files += cmStrCat("  ", MessagePath(item->FileName), '\n');
1435       }
1436       LogError(
1437         GenT::MOC,
1438         cmStrCat("The source files\n", files,
1439                  "contain the same include string ",
1440                  MessagePath(includeString),
1441                  ", but\nthe moc file would be generated from different "
1442                  "source files\n  ",
1443                  MessagePath(sourceFileHandle->FileName), " and\n  ",
1444                  MessagePath(handle->SourceFile->FileName),
1445                  ".\nConsider to\n"
1446                  "  - not include the \"moc_<NAME>.cpp\" file\n"
1447                  "  - add a directory prefix to a \"<NAME>.moc\" include "
1448                  "(e.g \"sub/<NAME>.moc\")\n"
1449                  "  - rename the source file(s)\n"));
1450       return false;
1451     }
1452
1453     // The same mapping already exists. Just add to the includers list.
1454     handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
1455     return true;
1456   }
1457
1458   // Create a new mapping
1459   handle = std::make_shared<MappingT>();
1460   handle->IncludeString = includeString;
1461   handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
1462   handle->SourceFile = std::move(sourceFileHandle);
1463   handle->OutputFile = Gen()->AbsoluteIncludePath(includeString);
1464
1465   // Register mapping in sources/headers map
1466   RegisterMapping(handle);
1467   return true;
1468 }
1469
1470 void cmQtAutoMocUicT::JobEvalCacheMocT::RegisterMapping(
1471   MappingHandleT mappingHandle) const
1472 {
1473   auto& regMap = mappingHandle->SourceFile->IsHeader
1474     ? MocEval().HeaderMappings
1475     : MocEval().SourceMappings;
1476   // Check if source file already gets mapped
1477   auto& regHandle = regMap[mappingHandle->SourceFile->FileName];
1478   if (!regHandle) {
1479     // Yet unknown mapping
1480     regHandle = std::move(mappingHandle);
1481   } else {
1482     // Mappings with include string override those without
1483     if (!mappingHandle->IncludeString.empty()) {
1484       regHandle = std::move(mappingHandle);
1485     }
1486   }
1487 }
1488
1489 std::string cmQtAutoMocUicT::JobEvalCacheMocT::MessageHeader(
1490   cm::string_view headerBase) const
1491 {
1492   return MessagePath(cmStrCat(
1493     headerBase, ".{", cmJoin(this->BaseConst().HeaderExtensions, ","), '}'));
1494 }
1495
1496 void cmQtAutoMocUicT::JobEvalCacheUicT::Process()
1497 {
1498   // Prepare buffers
1499   SearchLocations.reserve((UicConst().SearchPaths.size() + 1) * 2);
1500
1501   // Evaluate headers
1502   for (auto const& pair : BaseEval().Headers) {
1503     if (!EvalFile(pair.second)) {
1504       return;
1505     }
1506   }
1507   // Evaluate sources
1508   for (auto const& pair : BaseEval().Sources) {
1509     if (!EvalFile(pair.second)) {
1510       return;
1511     }
1512   }
1513 }
1514
1515 bool cmQtAutoMocUicT::JobEvalCacheUicT::EvalFile(
1516   SourceFileHandleT const& sourceFileHandle)
1517 {
1518   SourceFileT const& sourceFile = *sourceFileHandle;
1519   auto const& Include = sourceFile.ParseData->Uic.Include;
1520   if (!sourceFile.Uic || Include.empty()) {
1521     return true;
1522   }
1523
1524   std::string const sourceDirPrefix = SubDirPrefix(sourceFile.FileName);
1525   for (IncludeKeyT const& incKey : Include) {
1526     // Find .ui file
1527     UiName = cmStrCat(incKey.Base, ".ui");
1528     if (!FindIncludedUi(sourceDirPrefix, incKey.Dir)) {
1529       LogError(GenT::UIC,
1530                cmStrCat(MessagePath(sourceFile.FileName),
1531                         "\nincludes the uic file ", MessagePath(incKey.Key),
1532                         ",\nbut the user interface file ", MessagePath(UiName),
1533                         "\ncould not be found in the following directories\n",
1534                         MessageSearchLocations()));
1535       return false;
1536     }
1537     // Check if the file is skipped
1538     if (UicConst().skipped(UiFileHandle->FileName)) {
1539       continue;
1540     }
1541     // Register mapping
1542     if (!RegisterMapping(incKey.Key, sourceFileHandle)) {
1543       return false;
1544     }
1545   }
1546
1547   return true;
1548 }
1549
1550 bool cmQtAutoMocUicT::JobEvalCacheUicT::FindIncludedUi(
1551   cm::string_view sourceDirPrefix, cm::string_view includePrefix)
1552 {
1553   // Clear locations buffer
1554   SearchLocations.clear();
1555
1556   auto findUi = [this](std::string const& testPath) -> bool {
1557     std::string const fullPath = this->Gen()->CollapseFullPathTS(testPath);
1558     cmFileTime fileTime;
1559     if (!fileTime.Load(fullPath)) {
1560       this->SearchLocations.emplace_back(cmQtAutoGen::ParentDir(fullPath));
1561       return false;
1562     }
1563     // .ui file found in files system!
1564     // Get or create .ui file handle
1565     SourceFileHandleT& handle = this->UicEval().UiFiles[fullPath];
1566     if (!handle) {
1567       // The file wasn't registered, yet
1568       handle = std::make_shared<SourceFileT>(fullPath);
1569       handle->FileTime = fileTime;
1570     }
1571     this->UiFileHandle = handle;
1572     return true;
1573   };
1574
1575   // Vicinity of the source
1576   if (findUi(cmStrCat(sourceDirPrefix, UiName))) {
1577     return true;
1578   }
1579   if (!includePrefix.empty()) {
1580     if (findUi(cmStrCat(sourceDirPrefix, includePrefix, UiName))) {
1581       return true;
1582     }
1583   }
1584   // Additional AUTOUIC search paths
1585   auto const& searchPaths = UicConst().SearchPaths;
1586   if (!searchPaths.empty()) {
1587     for (std::string const& sPath : searchPaths) {
1588       if (findUi(cmStrCat(sPath, '/', UiName))) {
1589         return true;
1590       }
1591     }
1592     if (!includePrefix.empty()) {
1593       for (std::string const& sPath : searchPaths) {
1594         if (findUi(cmStrCat(sPath, '/', includePrefix, UiName))) {
1595           return true;
1596         }
1597       }
1598     }
1599   }
1600
1601   return false;
1602 }
1603
1604 bool cmQtAutoMocUicT::JobEvalCacheUicT::RegisterMapping(
1605   std::string const& includeString, SourceFileHandleT includerFileHandle)
1606 {
1607   auto& Includes = Gen()->UicEval().Includes;
1608   auto it = Includes.find(includeString);
1609   if (it != Includes.end()) {
1610     MappingHandleT const& handle = it->second;
1611     if (handle->SourceFile != UiFileHandle) {
1612       // The output file already gets generated - from a different .ui file!
1613       std::string files =
1614         cmStrCat("  ", MessagePath(includerFileHandle->FileName), '\n');
1615       for (auto const& item : handle->IncluderFiles) {
1616         files += cmStrCat("  ", MessagePath(item->FileName), '\n');
1617       }
1618       LogError(
1619         GenT::UIC,
1620         cmStrCat(
1621           "The source files\n", files, "contain the same include string ",
1622           Quoted(includeString),
1623           ", but\nthe uic file would be generated from different "
1624           "user interface files\n  ",
1625           MessagePath(UiFileHandle->FileName), " and\n  ",
1626           MessagePath(handle->SourceFile->FileName),
1627           ".\nConsider to\n"
1628           "  - add a directory prefix to a \"ui_<NAME>.h\" include "
1629           "(e.g \"sub/ui_<NAME>.h\")\n"
1630           "  - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
1631           "include(s)\n"));
1632       return false;
1633     }
1634     // Add includer file to existing mapping
1635     handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
1636   } else {
1637     // New mapping handle
1638     MappingHandleT handle = std::make_shared<MappingT>();
1639     handle->IncludeString = includeString;
1640     handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
1641     handle->SourceFile = UiFileHandle;
1642     handle->OutputFile = Gen()->AbsoluteIncludePath(includeString);
1643     // Register mapping
1644     Includes.emplace(includeString, std::move(handle));
1645   }
1646   return true;
1647 }
1648
1649 void cmQtAutoMocUicT::JobEvalCacheFinishT::Process()
1650 {
1651   // Add discovered header parse jobs
1652   Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered);
1653
1654   // Add dependency probing jobs
1655   {
1656     // Add fence job to ensure all parsing has finished
1657     Gen()->WorkerPool().EmplaceJob<JobFenceT>();
1658     if (MocConst().Enabled) {
1659       Gen()->WorkerPool().EmplaceJob<JobProbeDepsMocT>();
1660     }
1661     if (UicConst().Enabled) {
1662       Gen()->WorkerPool().EmplaceJob<JobProbeDepsUicT>();
1663     }
1664     // Add probe finish job
1665     Gen()->WorkerPool().EmplaceJob<JobProbeDepsFinishT>();
1666   }
1667 }
1668
1669 void cmQtAutoMocUicT::JobProbeDepsMocT::Process()
1670 {
1671   // Create moc header jobs
1672   for (auto const& pair : MocEval().HeaderMappings) {
1673     // Register if this mapping is a candidate for mocs_compilation.cpp
1674     bool const compFile = pair.second->IncludeString.empty();
1675     if (compFile) {
1676       MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath);
1677     }
1678     if (!Generate(pair.second, compFile)) {
1679       return;
1680     }
1681   }
1682
1683   // Create moc source jobs
1684   for (auto const& pair : MocEval().SourceMappings) {
1685     if (!Generate(pair.second, false)) {
1686       return;
1687     }
1688   }
1689 }
1690
1691 bool cmQtAutoMocUicT::JobProbeDepsMocT::Generate(MappingHandleT const& mapping,
1692                                                  bool compFile) const
1693 {
1694   std::unique_ptr<std::string> reason;
1695   if (Log().Verbose()) {
1696     reason = cm::make_unique<std::string>();
1697   }
1698   if (Probe(*mapping, reason.get())) {
1699     // Register the parent directory for creation
1700     MocEval().OutputDirs.emplace(cmQtAutoGen::ParentDir(mapping->OutputFile));
1701     // Fetch the cache entry for the source file
1702     std::string const& sourceFile = mapping->SourceFile->FileName;
1703     ParseCacheT::GetOrInsertT cacheEntry =
1704       BaseEval().ParseCache.GetOrInsert(sourceFile);
1705     // Add moc job
1706     Gen()->WorkerPool().EmplaceJob<JobCompileMocT>(
1707       mapping, std::move(reason), std::move(cacheEntry.first));
1708     // Check if a moc job for a mocs_compilation.cpp entry was generated
1709     if (compFile) {
1710       MocEval().CompUpdated = true;
1711     }
1712   }
1713   return true;
1714 }
1715
1716 bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping,
1717                                               std::string* reason) const
1718 {
1719   std::string const& sourceFile = mapping.SourceFile->FileName;
1720   std::string const& outputFile = mapping.OutputFile;
1721
1722   // Test if the output file exists
1723   cmFileTime outputFileTime;
1724   if (!outputFileTime.Load(outputFile)) {
1725     if (reason != nullptr) {
1726       *reason =
1727         cmStrCat("Generating ", MessagePath(outputFile),
1728                  ", because it doesn't exist, from ", MessagePath(sourceFile));
1729     }
1730     return true;
1731   }
1732
1733   // Test if any setting changed
1734   if (MocConst().SettingsChanged) {
1735     if (reason != nullptr) {
1736       *reason = cmStrCat("Generating ", MessagePath(outputFile),
1737                          ", because the uic settings changed, from ",
1738                          MessagePath(sourceFile));
1739     }
1740     return true;
1741   }
1742
1743   // Test if the source file is newer
1744   if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
1745     if (reason != nullptr) {
1746       *reason = cmStrCat("Generating ", MessagePath(outputFile),
1747                          ", because it's older than its source file, from ",
1748                          MessagePath(sourceFile));
1749     }
1750     return true;
1751   }
1752
1753   // Test if the moc_predefs file is newer
1754   if (!MocConst().PredefsFileAbs.empty()) {
1755     if (outputFileTime.Older(MocEval().PredefsTime)) {
1756       if (reason != nullptr) {
1757         *reason = cmStrCat("Generating ", MessagePath(outputFile),
1758                            ", because it's older than ",
1759                            MessagePath(MocConst().PredefsFileAbs), ", from ",
1760                            MessagePath(sourceFile));
1761       }
1762       return true;
1763     }
1764   }
1765
1766   // Test if the moc executable is newer
1767   if (outputFileTime.Older(MocConst().ExecutableTime)) {
1768     if (reason != nullptr) {
1769       *reason = cmStrCat("Generating ", MessagePath(outputFile),
1770                          ", because it's older than the moc executable, from ",
1771                          MessagePath(sourceFile));
1772     }
1773     return true;
1774   }
1775
1776   // Test if a dependency file is newer
1777   {
1778     // Check dependency timestamps
1779     std::string const sourceDir = SubDirPrefix(sourceFile);
1780     for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) {
1781       // Find dependency file
1782       auto const depMatch = FindDependency(sourceDir, dep);
1783       if (depMatch.first.empty()) {
1784         Log().Warning(GenT::MOC,
1785                       cmStrCat(MessagePath(sourceFile), " depends on ",
1786                                MessagePath(dep),
1787                                " but the file does not exist."));
1788         continue;
1789       }
1790       // Test if dependency file is older
1791       if (outputFileTime.Older(depMatch.second)) {
1792         if (reason != nullptr) {
1793           *reason = cmStrCat("Generating ", MessagePath(outputFile),
1794                              ", because it's older than its dependency file ",
1795                              MessagePath(depMatch.first), ", from ",
1796                              MessagePath(sourceFile));
1797         }
1798         return true;
1799       }
1800     }
1801   }
1802
1803   return false;
1804 }
1805
1806 std::pair<std::string, cmFileTime>
1807 cmQtAutoMocUicT::JobProbeDepsMocT::FindDependency(
1808   std::string const& sourceDir, std::string const& includeString) const
1809 {
1810   using ResPair = std::pair<std::string, cmFileTime>;
1811   // moc's dependency file contains absolute paths
1812   if (MocConst().CanOutputDependencies) {
1813     ResPair res{ includeString, {} };
1814     if (res.second.Load(res.first)) {
1815       return res;
1816     }
1817     return {};
1818   }
1819   // Search in vicinity of the source
1820   {
1821     ResPair res{ sourceDir + includeString, {} };
1822     if (res.second.Load(res.first)) {
1823       return res;
1824     }
1825   }
1826   // Search in include directories
1827   for (std::string const& includePath : MocConst().IncludePaths) {
1828     ResPair res{ cmStrCat(includePath, '/', includeString), {} };
1829     if (res.second.Load(res.first)) {
1830       return res;
1831     }
1832   }
1833   // Return empty
1834   return ResPair();
1835 }
1836
1837 void cmQtAutoMocUicT::JobProbeDepsUicT::Process()
1838 {
1839   for (auto const& pair : Gen()->UicEval().Includes) {
1840     MappingHandleT const& mapping = pair.second;
1841     std::unique_ptr<std::string> reason;
1842     if (Log().Verbose()) {
1843       reason = cm::make_unique<std::string>();
1844     }
1845     if (!Probe(*mapping, reason.get())) {
1846       continue;
1847     }
1848
1849     // Register the parent directory for creation
1850     UicEval().OutputDirs.emplace(cmQtAutoGen::ParentDir(mapping->OutputFile));
1851     // Add uic job
1852     Gen()->WorkerPool().EmplaceJob<JobCompileUicT>(mapping, std::move(reason));
1853   }
1854 }
1855
1856 bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping,
1857                                               std::string* reason) const
1858 {
1859   std::string const& sourceFile = mapping.SourceFile->FileName;
1860   std::string const& outputFile = mapping.OutputFile;
1861
1862   // Test if the build file exists
1863   cmFileTime outputFileTime;
1864   if (!outputFileTime.Load(outputFile)) {
1865     if (reason != nullptr) {
1866       *reason =
1867         cmStrCat("Generating ", MessagePath(outputFile),
1868                  ", because it doesn't exist, from ", MessagePath(sourceFile));
1869     }
1870     return true;
1871   }
1872
1873   // Test if the uic settings changed
1874   if (UicConst().SettingsChanged) {
1875     if (reason != nullptr) {
1876       *reason = cmStrCat("Generating ", MessagePath(outputFile),
1877                          ", because the uic settings changed, from ",
1878                          MessagePath(sourceFile));
1879     }
1880     return true;
1881   }
1882
1883   // Test if the source file is newer
1884   if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
1885     if (reason != nullptr) {
1886       *reason = cmStrCat("Generating ", MessagePath(outputFile),
1887                          " because it's older than the source file ",
1888                          MessagePath(sourceFile));
1889     }
1890     return true;
1891   }
1892
1893   // Test if the uic executable is newer
1894   if (outputFileTime.Older(UicConst().ExecutableTime)) {
1895     if (reason != nullptr) {
1896       *reason = cmStrCat("Generating ", MessagePath(outputFile),
1897                          ", because it's older than the uic executable, from ",
1898                          MessagePath(sourceFile));
1899     }
1900     return true;
1901   }
1902
1903   return false;
1904 }
1905
1906 void cmQtAutoMocUicT::JobProbeDepsFinishT::Process()
1907 {
1908   // Create output directories
1909   {
1910     using StringSet = std::unordered_set<std::string>;
1911     auto createDirs = [this](GenT genType, StringSet const& dirSet) {
1912       for (std::string const& dirName : dirSet) {
1913         if (!cmSystemTools::MakeDirectory(dirName)) {
1914           this->LogError(
1915             genType,
1916             cmStrCat("Creating directory ", MessagePath(dirName), " failed."));
1917           return;
1918         }
1919       }
1920     };
1921     if (MocConst().Enabled && UicConst().Enabled) {
1922       StringSet outputDirs = MocEval().OutputDirs;
1923       outputDirs.insert(UicEval().OutputDirs.begin(),
1924                         UicEval().OutputDirs.end());
1925       createDirs(GenT::GEN, outputDirs);
1926     } else if (MocConst().Enabled) {
1927       createDirs(GenT::MOC, MocEval().OutputDirs);
1928     } else if (UicConst().Enabled) {
1929       createDirs(GenT::UIC, UicEval().OutputDirs);
1930     }
1931   }
1932
1933   if (MocConst().Enabled) {
1934     // Add mocs compilations job
1935     Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
1936   }
1937
1938   if (!BaseConst().DepFile.empty()) {
1939     // Add job to merge dep files
1940     Gen()->WorkerPool().EmplaceJob<JobDepFilesMergeT>();
1941   }
1942
1943   // Add finish job
1944   Gen()->WorkerPool().EmplaceJob<JobFinishT>();
1945 }
1946
1947 void cmQtAutoMocUicT::JobCompileMocT::Process()
1948 {
1949   std::string const& sourceFile = Mapping->SourceFile->FileName;
1950   std::string const& outputFile = Mapping->OutputFile;
1951
1952   // Compose moc command
1953   std::vector<std::string> cmd;
1954   {
1955     // Reserve large enough
1956     cmd.reserve(MocConst().OptionsDefinitions.size() +
1957                 MocConst().OptionsIncludes.size() +
1958                 MocConst().OptionsExtra.size() + 16);
1959     cmd.push_back(MocConst().Executable);
1960     // Add definitions
1961     cm::append(cmd, MocConst().OptionsDefinitions);
1962     // Add includes
1963     cm::append(cmd, MocConst().OptionsIncludes);
1964     // Add predefs include
1965     if (!MocConst().PredefsFileAbs.empty()) {
1966       cmd.emplace_back("--include");
1967       cmd.push_back(MocConst().PredefsFileAbs);
1968     }
1969     // Add path prefix on demand
1970     if (MocConst().PathPrefix && Mapping->SourceFile->IsHeader) {
1971       for (std::string const& dir : MocConst().IncludePaths) {
1972         cm::string_view prefix = sourceFile;
1973         if (cmHasPrefix(prefix, dir)) {
1974           prefix.remove_prefix(dir.size());
1975           if (cmHasPrefix(prefix, '/')) {
1976             prefix.remove_prefix(1);
1977             auto slashPos = prefix.rfind('/');
1978             if (slashPos != cm::string_view::npos) {
1979               cmd.emplace_back("-p");
1980               cmd.emplace_back(prefix.substr(0, slashPos));
1981             } else {
1982               cmd.emplace_back("-p");
1983               cmd.emplace_back("./");
1984             }
1985             break;
1986           }
1987         }
1988       }
1989     }
1990     // Add extra options
1991     cm::append(cmd, MocConst().OptionsExtra);
1992     if (MocConst().CanOutputDependencies) {
1993       cmd.emplace_back("--output-dep-file");
1994     }
1995     // Add output file
1996     cmd.emplace_back("-o");
1997     cmd.push_back(outputFile);
1998     // Add source file
1999     cmd.push_back(sourceFile);
2000   }
2001
2002   // Execute moc command
2003   cmWorkerPool::ProcessResultT result;
2004   if (!RunProcess(GenT::MOC, result, cmd, Reason.get())) {
2005     // Moc command failed
2006     std::string includers;
2007     if (!Mapping->IncluderFiles.empty()) {
2008       includers = "included by\n";
2009       for (auto const& item : Mapping->IncluderFiles) {
2010         includers += cmStrCat("  ", MessagePath(item->FileName), '\n');
2011       }
2012     }
2013     LogCommandError(GenT::MOC,
2014                     cmStrCat("The moc process failed to compile\n  ",
2015                              MessagePath(sourceFile), "\ninto\n  ",
2016                              MessagePath(outputFile), '\n', includers,
2017                              result.ErrorMessage),
2018                     cmd, result.StdOut);
2019     return;
2020   }
2021
2022   // Moc command success. Print moc output.
2023   if (!result.StdOut.empty()) {
2024     Log().Info(GenT::MOC, result.StdOut);
2025   }
2026
2027   // Extract dependencies from the dep file moc generated for us
2028   if (MocConst().CanOutputDependencies) {
2029     const std::string depfile = outputFile + ".d";
2030     if (Log().Verbose()) {
2031       Log().Info(GenT::MOC,
2032                  "Reading dependencies from " + MessagePath(depfile));
2033     }
2034     if (!cmSystemTools::FileExists(depfile)) {
2035       Log().Warning(GenT::MOC,
2036                     "Dependency file " + MessagePath(depfile) +
2037                       " does not exist.");
2038       return;
2039     }
2040     CacheEntry->Moc.Depends = dependenciesFromDepFile(depfile.c_str());
2041   }
2042 }
2043
2044 void cmQtAutoMocUicT::JobCompileUicT::Process()
2045 {
2046   std::string const& sourceFile = Mapping->SourceFile->FileName;
2047   std::string const& outputFile = Mapping->OutputFile;
2048
2049   // Compose uic command
2050   std::vector<std::string> cmd;
2051   cmd.push_back(UicConst().Executable);
2052   {
2053     std::vector<std::string> allOpts = UicConst().Options;
2054     auto optionIt = UicConst().UiFiles.find(sourceFile);
2055     if (optionIt != UicConst().UiFiles.end()) {
2056       UicMergeOptions(allOpts, optionIt->second.Options,
2057                       (BaseConst().QtVersion.Major == 5));
2058     }
2059     cm::append(cmd, allOpts);
2060   }
2061   cmd.emplace_back("-o");
2062   cmd.emplace_back(outputFile);
2063   cmd.emplace_back(sourceFile);
2064
2065   cmWorkerPool::ProcessResultT result;
2066   if (RunProcess(GenT::UIC, result, cmd, Reason.get())) {
2067     // Uic command success
2068     // Print uic output
2069     if (!result.StdOut.empty()) {
2070       Log().Info(GenT::UIC, result.StdOut);
2071     }
2072   } else {
2073     // Uic command failed
2074     std::string includers;
2075     for (auto const& item : Mapping->IncluderFiles) {
2076       includers += cmStrCat("  ", MessagePath(item->FileName), '\n');
2077     }
2078     LogCommandError(GenT::UIC,
2079                     cmStrCat("The uic process failed to compile\n  ",
2080                              MessagePath(sourceFile), "\ninto\n  ",
2081                              MessagePath(outputFile), "\nincluded by\n",
2082                              includers, result.ErrorMessage),
2083                     cmd, result.StdOut);
2084   }
2085 }
2086
2087 void cmQtAutoMocUicT::JobMocsCompilationT::Process()
2088 {
2089   // Compose mocs compilation file content
2090   std::string content =
2091     "// This file is autogenerated. Changes will be overwritten.\n";
2092
2093   if (MocEval().CompFiles.empty()) {
2094     // Placeholder content
2095     content += "// No files found that require moc or the moc files are "
2096                "included\n"
2097                "enum some_compilers { need_more_than_nothing };\n";
2098   } else {
2099     // Valid content
2100     const bool mc = BaseConst().MultiConfig;
2101     cm::string_view const wrapFront = mc ? "#include <" : "#include \"";
2102     cm::string_view const wrapBack = mc ? ">\n" : "\"\n";
2103     content += cmWrap(wrapFront, MocEval().CompFiles, wrapBack, "");
2104   }
2105
2106   std::string const& compAbs = MocConst().CompFileAbs;
2107   if (cmQtAutoGenerator::FileDiffers(compAbs, content)) {
2108     // Actually write mocs compilation file
2109     if (Log().Verbose()) {
2110       Log().Info(GenT::MOC,
2111                  "Generating MOC compilation " + MessagePath(compAbs));
2112     }
2113     if (!FileWrite(compAbs, content)) {
2114       LogError(GenT::MOC,
2115                cmStrCat("Writing MOC compilation ", MessagePath(compAbs),
2116                         " failed."));
2117     }
2118   } else if (MocEval().CompUpdated) {
2119     // Only touch mocs compilation file
2120     if (Log().Verbose()) {
2121       Log().Info(GenT::MOC,
2122                  "Touching MOC compilation " + MessagePath(compAbs));
2123     }
2124     if (!cmSystemTools::Touch(compAbs, false)) {
2125       LogError(GenT::MOC,
2126                cmStrCat("Touching MOC compilation ", MessagePath(compAbs),
2127                         " failed."));
2128     }
2129   }
2130 }
2131
2132 /*
2133  * Escapes paths for Ninja depfiles.
2134  * This is a re-implementation of what moc does when writing depfiles.
2135  */
2136 std::string escapeDependencyPath(cm::string_view path)
2137 {
2138   std::string escapedPath;
2139   escapedPath.reserve(path.size());
2140   const size_t s = path.size();
2141   int backslashCount = 0;
2142   for (size_t i = 0; i < s; ++i) {
2143     if (path[i] == '\\') {
2144       ++backslashCount;
2145     } else {
2146       if (path[i] == '$') {
2147         escapedPath.push_back('$');
2148       } else if (path[i] == '#') {
2149         escapedPath.push_back('\\');
2150       } else if (path[i] == ' ') {
2151         // Double the amount of written backslashes,
2152         // and add one more to escape the space.
2153         while (backslashCount-- >= 0) {
2154           escapedPath.push_back('\\');
2155         }
2156       }
2157       backslashCount = 0;
2158     }
2159     escapedPath.push_back(path[i]);
2160   }
2161   return escapedPath;
2162 }
2163
2164 void cmQtAutoMocUicT::JobDepFilesMergeT::Process()
2165 {
2166   if (Log().Verbose()) {
2167     Log().Info(GenT::MOC,
2168                cmStrCat("Merging MOC dependencies into ",
2169                         MessagePath(BaseConst().DepFile.c_str())));
2170   }
2171   auto processDepFile =
2172     [](const std::string& mocOutputFile) -> std::vector<std::string> {
2173     std::string f = mocOutputFile + ".d";
2174     if (!cmSystemTools::FileExists(f)) {
2175       return {};
2176     }
2177     return dependenciesFromDepFile(f.c_str());
2178   };
2179
2180   std::vector<std::string> dependencies = BaseConst().ListFiles;
2181   ParseCacheT& parseCache = BaseEval().ParseCache;
2182   auto processMappingEntry = [&](const MappingMapT::value_type& m) {
2183     auto cacheEntry = parseCache.GetOrInsert(m.first);
2184     if (cacheEntry.first->Moc.Depends.empty()) {
2185       cacheEntry.first->Moc.Depends = processDepFile(m.second->OutputFile);
2186     }
2187     dependencies.insert(dependencies.end(),
2188                         cacheEntry.first->Moc.Depends.begin(),
2189                         cacheEntry.first->Moc.Depends.end());
2190   };
2191
2192   std::for_each(MocEval().HeaderMappings.begin(),
2193                 MocEval().HeaderMappings.end(), processMappingEntry);
2194   std::for_each(MocEval().SourceMappings.begin(),
2195                 MocEval().SourceMappings.end(), processMappingEntry);
2196
2197   // Remove duplicates to make the depfile smaller
2198   std::sort(dependencies.begin(), dependencies.end());
2199   dependencies.erase(std::unique(dependencies.begin(), dependencies.end()),
2200                      dependencies.end());
2201
2202   // Add form files
2203   for (const auto& uif : UicEval().UiFiles) {
2204     dependencies.push_back(uif.first);
2205   }
2206
2207   // Write the file
2208   cmsys::ofstream ofs;
2209   ofs.open(BaseConst().DepFile.c_str(),
2210            (std::ios::out | std::ios::binary | std::ios::trunc));
2211   if (!ofs) {
2212     LogError(GenT::GEN,
2213              cmStrCat("Cannot open ", MessagePath(BaseConst().DepFile),
2214                       " for writing."));
2215     return;
2216   }
2217   ofs << BaseConst().DepFileRuleName << ": \\" << std::endl;
2218   for (const std::string& file : dependencies) {
2219     ofs << '\t' << escapeDependencyPath(file) << " \\" << std::endl;
2220     if (!ofs.good()) {
2221       LogError(GenT::GEN,
2222                cmStrCat("Writing depfile", MessagePath(BaseConst().DepFile),
2223                         " failed."));
2224       return;
2225     }
2226   }
2227
2228   // Add the CMake executable to re-new cache data if necessary.
2229   // Also, this is the last entry, so don't add a backslash.
2230   ofs << '\t' << escapeDependencyPath(BaseConst().CMakeExecutable)
2231       << std::endl;
2232 }
2233
2234 void cmQtAutoMocUicT::JobFinishT::Process()
2235 {
2236   Gen()->AbortSuccess();
2237 }
2238
2239 cmQtAutoMocUicT::cmQtAutoMocUicT()
2240   : cmQtAutoGenerator(GenT::GEN)
2241 {
2242 }
2243 cmQtAutoMocUicT::~cmQtAutoMocUicT() = default;
2244
2245 bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
2246 {
2247   // -- Required settings
2248   if (!info.GetBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) ||
2249       !info.GetUInt("QT_VERSION_MAJOR", BaseConst_.QtVersion.Major, true) ||
2250       !info.GetUInt("QT_VERSION_MINOR", BaseConst_.QtVersion.Minor, true) ||
2251       !info.GetUInt("PARALLEL", BaseConst_.ThreadCount, false) ||
2252       !info.GetString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) ||
2253       !info.GetStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir,
2254                             true) ||
2255       !info.GetString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) ||
2256       !info.GetStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile,
2257                             true) ||
2258       !info.GetString("DEP_FILE", BaseConst_.DepFile, false) ||
2259       !info.GetString("DEP_FILE_RULE_NAME", BaseConst_.DepFileRuleName,
2260                       false) ||
2261       !info.GetStringConfig("SETTINGS_FILE", SettingsFile_, true) ||
2262       !info.GetArray("CMAKE_LIST_FILES", BaseConst_.ListFiles, true) ||
2263       !info.GetArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) ||
2264       !info.GetString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) ||
2265       !info.GetString("QT_UIC_EXECUTABLE", UicConst_.Executable, false)) {
2266     return false;
2267   }
2268
2269   // -- Checks
2270   if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) {
2271     return info.LogError(cmStrCat("The CMake executable ",
2272                                   MessagePath(BaseConst_.CMakeExecutable),
2273                                   " does not exist."));
2274   }
2275
2276   // -- Evaluate values
2277   BaseConst_.ThreadCount = std::min(BaseConst_.ThreadCount, ParallelMax);
2278   WorkerPool_.SetThreadCount(BaseConst_.ThreadCount);
2279
2280   // -- Moc
2281   if (!MocConst_.Executable.empty()) {
2282     // -- Moc is enabled
2283     MocConst_.Enabled = true;
2284
2285     // -- Temporary buffers
2286     struct
2287     {
2288       std::vector<std::string> MacroNames;
2289       std::vector<std::string> DependFilters;
2290     } tmp;
2291
2292     // -- Required settings
2293     if (!info.GetBool("MOC_RELAXED_MODE", MocConst_.RelaxedMode, false) ||
2294         !info.GetBool("MOC_PATH_PREFIX", MocConst_.PathPrefix, true) ||
2295         !info.GetArray("MOC_SKIP", MocConst_.SkipList, false) ||
2296         !info.GetArrayConfig("MOC_DEFINITIONS", MocConst_.Definitions,
2297                              false) ||
2298         !info.GetArrayConfig("MOC_INCLUDES", MocConst_.IncludePaths, false) ||
2299         !info.GetArray("MOC_OPTIONS", MocConst_.OptionsExtra, false) ||
2300         !info.GetStringConfig("MOC_COMPILATION_FILE", MocConst_.CompFileAbs,
2301                               true) ||
2302         !info.GetArray("MOC_PREDEFS_CMD", MocConst_.PredefsCmd, false) ||
2303         !info.GetStringConfig("MOC_PREDEFS_FILE", MocConst_.PredefsFileAbs,
2304                               !MocConst_.PredefsCmd.empty()) ||
2305         !info.GetArray("MOC_MACRO_NAMES", tmp.MacroNames, true) ||
2306         !info.GetArray("MOC_DEPEND_FILTERS", tmp.DependFilters, false)) {
2307       return false;
2308     }
2309
2310     // -- Evaluate settings
2311     for (std::string const& item : tmp.MacroNames) {
2312       MocConst_.MacroFilters.emplace_back(
2313         item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
2314     }
2315     // Can moc output dependencies or do we need to setup dependency filters?
2316     if (BaseConst_.QtVersion >= IntegerVersion(5, 15)) {
2317       MocConst_.CanOutputDependencies = true;
2318     } else {
2319       Json::Value const& val = info.GetValue("MOC_DEPEND_FILTERS");
2320       if (!val.isArray()) {
2321         return info.LogError("MOC_DEPEND_FILTERS JSON value is not an array.");
2322       }
2323       Json::ArrayIndex const arraySize = val.size();
2324       for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
2325         // Test entry closure
2326         auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
2327           if (!test) {
2328             info.LogError(
2329               cmStrCat("MOC_DEPEND_FILTERS filter ", ii, ": ", msg));
2330           }
2331           return !test;
2332         };
2333
2334         Json::Value const& pairVal = val[ii];
2335
2336         if (testEntry(pairVal.isArray(), "JSON value is not an array.") ||
2337             testEntry(pairVal.size() == 2, "JSON array size invalid.")) {
2338           return false;
2339         }
2340
2341         Json::Value const& keyVal = pairVal[0u];
2342         Json::Value const& expVal = pairVal[1u];
2343         if (testEntry(keyVal.isString(),
2344                       "JSON value for keyword is not a string.") ||
2345             testEntry(expVal.isString(),
2346                       "JSON value for regular expression is not a string.")) {
2347           return false;
2348         }
2349
2350         std::string const key = keyVal.asString();
2351         std::string const exp = expVal.asString();
2352         if (testEntry(!key.empty(), "Keyword is empty.") ||
2353             testEntry(!exp.empty(), "Regular expression is empty.")) {
2354           return false;
2355         }
2356
2357         this->MocConst_.DependFilters.emplace_back(key, exp);
2358         if (testEntry(
2359               this->MocConst_.DependFilters.back().Exp.is_valid(),
2360               cmStrCat("Regular expression compilation failed.\nKeyword: ",
2361                        Quoted(key), "\nExpression: ", Quoted(exp)))) {
2362           return false;
2363         }
2364       }
2365     }
2366     // Check if moc executable exists (by reading the file time)
2367     if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
2368       return info.LogError(cmStrCat("The moc executable ",
2369                                     MessagePath(MocConst_.Executable),
2370                                     " does not exist."));
2371     }
2372   }
2373
2374   // -- Uic
2375   if (!UicConst_.Executable.empty()) {
2376     // Uic is enabled
2377     UicConst_.Enabled = true;
2378
2379     // -- Required settings
2380     if (!info.GetArray("UIC_SKIP", UicConst_.SkipList, false) ||
2381         !info.GetArray("UIC_SEARCH_PATHS", UicConst_.SearchPaths, false) ||
2382         !info.GetArrayConfig("UIC_OPTIONS", UicConst_.Options, false)) {
2383       return false;
2384     }
2385     // .ui files
2386     {
2387       Json::Value const& val = info.GetValue("UIC_UI_FILES");
2388       if (!val.isArray()) {
2389         return info.LogError("UIC_UI_FILES JSON value is not an array.");
2390       }
2391       Json::ArrayIndex const arraySize = val.size();
2392       for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
2393         // Test entry closure
2394         auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
2395           if (!test) {
2396             info.LogError(cmStrCat("UIC_UI_FILES entry ", ii, ": ", msg));
2397           }
2398           return !test;
2399         };
2400
2401         Json::Value const& entry = val[ii];
2402         if (testEntry(entry.isArray(), "JSON value is not an array.") ||
2403             testEntry(entry.size() == 2, "JSON array size invalid.")) {
2404           return false;
2405         }
2406
2407         Json::Value const& entryName = entry[0u];
2408         Json::Value const& entryOptions = entry[1u];
2409         if (testEntry(entryName.isString(),
2410                       "JSON value for name is not a string.") ||
2411             testEntry(entryOptions.isArray(),
2412                       "JSON value for options is not an array.")) {
2413           return false;
2414         }
2415
2416         auto& uiFile = UicConst_.UiFiles[entryName.asString()];
2417         InfoT::GetJsonArray(uiFile.Options, entryOptions);
2418       }
2419     }
2420
2421     // -- Evaluate settings
2422     // Check if uic executable exists (by reading the file time)
2423     if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) {
2424       return info.LogError(cmStrCat("The uic executable ",
2425                                     MessagePath(UicConst_.Executable),
2426                                     " does not exist."));
2427     }
2428   }
2429
2430   // -- Headers
2431   {
2432     Json::Value const& val = info.GetValue("HEADERS");
2433     if (!val.isArray()) {
2434       return info.LogError("HEADERS JSON value is not an array.");
2435     }
2436     Json::ArrayIndex const arraySize = val.size();
2437     for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
2438       // Test entry closure
2439       auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
2440         if (!test) {
2441           info.LogError(cmStrCat("HEADERS entry ", ii, ": ", msg));
2442         }
2443         return !test;
2444       };
2445
2446       Json::Value const& entry = val[ii];
2447       if (testEntry(entry.isArray(), "JSON value is not an array.") ||
2448           testEntry(entry.size() == 3, "JSON array size invalid.")) {
2449         return false;
2450       }
2451
2452       Json::Value const& entryName = entry[0u];
2453       Json::Value const& entryFlags = entry[1u];
2454       Json::Value const& entryBuild = entry[2u];
2455       if (testEntry(entryName.isString(),
2456                     "JSON value for name is not a string.") ||
2457           testEntry(entryFlags.isString(),
2458                     "JSON value for flags is not a string.") ||
2459           testEntry(entryBuild.isString(),
2460                     "JSON value for build path is not a string.")) {
2461         return false;
2462       }
2463
2464       std::string name = entryName.asString();
2465       std::string flags = entryFlags.asString();
2466       std::string build = entryBuild.asString();
2467       if (testEntry(flags.size() == 2, "Invalid flags string size")) {
2468         return false;
2469       }
2470
2471       cmFileTime fileTime;
2472       if (!fileTime.Load(name)) {
2473         return info.LogError(cmStrCat(
2474           "The header file ", this->MessagePath(name), " does not exist."));
2475       }
2476
2477       SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name);
2478       sourceHandle->FileTime = fileTime;
2479       sourceHandle->IsHeader = true;
2480       sourceHandle->Moc = (flags[0] == 'M');
2481       sourceHandle->Uic = (flags[1] == 'U');
2482       if (sourceHandle->Moc && MocConst().Enabled) {
2483         if (build.empty()) {
2484           return info.LogError(
2485             cmStrCat("Header file ", ii, " build path is empty"));
2486         }
2487         sourceHandle->BuildPath = std::move(build);
2488       }
2489       BaseEval().Headers.emplace(std::move(name), std::move(sourceHandle));
2490     }
2491   }
2492
2493   // -- Sources
2494   {
2495     Json::Value const& val = info.GetValue("SOURCES");
2496     if (!val.isArray()) {
2497       return info.LogError("SOURCES JSON value is not an array.");
2498     }
2499     Json::ArrayIndex const arraySize = val.size();
2500     for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
2501       // Test entry closure
2502       auto testEntry = [&info, ii](bool test, cm::string_view msg) -> bool {
2503         if (!test) {
2504           info.LogError(cmStrCat("SOURCES entry ", ii, ": ", msg));
2505         }
2506         return !test;
2507       };
2508
2509       Json::Value const& entry = val[ii];
2510       if (testEntry(entry.isArray(), "JSON value is not an array.") ||
2511           testEntry(entry.size() == 2, "JSON array size invalid.")) {
2512         return false;
2513       }
2514
2515       Json::Value const& entryName = entry[0u];
2516       Json::Value const& entryFlags = entry[1u];
2517       if (testEntry(entryName.isString(),
2518                     "JSON value for name is not a string.") ||
2519           testEntry(entryFlags.isString(),
2520                     "JSON value for flags is not a string.")) {
2521         return false;
2522       }
2523
2524       std::string name = entryName.asString();
2525       std::string flags = entryFlags.asString();
2526       if (testEntry(flags.size() == 2, "Invalid flags string size")) {
2527         return false;
2528       }
2529
2530       cmFileTime fileTime;
2531       if (!fileTime.Load(name)) {
2532         return info.LogError(cmStrCat(
2533           "The source file ", this->MessagePath(name), " does not exist."));
2534       }
2535
2536       SourceFileHandleT sourceHandle = std::make_shared<SourceFileT>(name);
2537       sourceHandle->FileTime = fileTime;
2538       sourceHandle->IsHeader = false;
2539       sourceHandle->Moc = (flags[0] == 'M');
2540       sourceHandle->Uic = (flags[1] == 'U');
2541       BaseEval().Sources.emplace(std::move(name), std::move(sourceHandle));
2542     }
2543   }
2544
2545   // -- Init derived information
2546   // Moc variables
2547   if (MocConst().Enabled) {
2548     // Compose moc includes list
2549     {
2550       // Compute framework paths
2551       std::set<std::string> frameworkPaths;
2552       for (std::string const& path : MocConst().IncludePaths) {
2553         // Extract framework path
2554         if (cmHasLiteralSuffix(path, ".framework/Headers")) {
2555           // Go up twice to get to the framework root
2556           std::vector<std::string> pathComponents;
2557           cmSystemTools::SplitPath(path, pathComponents);
2558           frameworkPaths.emplace(cmSystemTools::JoinPath(
2559             pathComponents.begin(), pathComponents.end() - 2));
2560         }
2561       }
2562       // Reserve options
2563       MocConst_.OptionsIncludes.reserve(MocConst().IncludePaths.size() +
2564                                         frameworkPaths.size() * 2);
2565       // Append includes
2566       for (std::string const& path : MocConst().IncludePaths) {
2567         MocConst_.OptionsIncludes.emplace_back("-I" + path);
2568       }
2569       // Append framework includes
2570       for (std::string const& path : frameworkPaths) {
2571         MocConst_.OptionsIncludes.emplace_back("-F");
2572         MocConst_.OptionsIncludes.push_back(path);
2573       }
2574     }
2575
2576     // Compose moc definitions list
2577     {
2578       MocConst_.OptionsDefinitions.reserve(MocConst().Definitions.size());
2579       for (std::string const& def : MocConst().Definitions) {
2580         MocConst_.OptionsDefinitions.emplace_back("-D" + def);
2581       }
2582     }
2583   }
2584
2585   return true;
2586 }
2587
2588 template <class JOBTYPE>
2589 void cmQtAutoMocUicT::CreateParseJobs(SourceFileMapT const& sourceMap)
2590 {
2591   cmFileTime const parseCacheTime = BaseEval().ParseCacheTime;
2592   ParseCacheT& parseCache = BaseEval().ParseCache;
2593   for (auto& src : sourceMap) {
2594     // Get or create the file parse data reference
2595     ParseCacheT::GetOrInsertT cacheEntry = parseCache.GetOrInsert(src.first);
2596     src.second->ParseData = std::move(cacheEntry.first);
2597     // Create a parse job if the cache file was missing or is older
2598     if (cacheEntry.second || src.second->FileTime.Newer(parseCacheTime)) {
2599       BaseEval().ParseCacheChanged = true;
2600       WorkerPool().EmplaceJob<JOBTYPE>(src.second);
2601     }
2602   }
2603 }
2604
2605 /** Concurrently callable implementation of cmSystemTools::CollapseFullPath */
2606 std::string cmQtAutoMocUicT::CollapseFullPathTS(std::string const& path) const
2607 {
2608   std::lock_guard<std::mutex> guard(CMakeLibMutex_);
2609   return cmSystemTools::CollapseFullPath(path, ProjectDirs().CurrentSource);
2610 }
2611
2612 void cmQtAutoMocUicT::InitJobs()
2613 {
2614   // Add moc_predefs.h job
2615   if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) {
2616     WorkerPool().EmplaceJob<JobMocPredefsT>();
2617   }
2618
2619   // Add header parse jobs
2620   CreateParseJobs<JobParseHeaderT>(BaseEval().Headers);
2621   // Add source parse jobs
2622   CreateParseJobs<JobParseSourceT>(BaseEval().Sources);
2623
2624   // Add parse cache evaluations jobs
2625   {
2626     // Add a fence job to ensure all parsing has finished
2627     WorkerPool().EmplaceJob<JobFenceT>();
2628     if (MocConst().Enabled) {
2629       WorkerPool().EmplaceJob<JobEvalCacheMocT>();
2630     }
2631     if (UicConst().Enabled) {
2632       WorkerPool().EmplaceJob<JobEvalCacheUicT>();
2633     }
2634     // Add evaluate job
2635     WorkerPool().EmplaceJob<JobEvalCacheFinishT>();
2636   }
2637 }
2638
2639 bool cmQtAutoMocUicT::Process()
2640 {
2641   SettingsFileRead();
2642   ParseCacheRead();
2643   if (!CreateDirectories()) {
2644     return false;
2645   }
2646   InitJobs();
2647   if (!WorkerPool_.Process(this)) {
2648     return false;
2649   }
2650   if (JobError_) {
2651     return false;
2652   }
2653   if (!ParseCacheWrite()) {
2654     return false;
2655   }
2656   if (!SettingsFileWrite()) {
2657     return false;
2658   }
2659   return true;
2660 }
2661
2662 void cmQtAutoMocUicT::SettingsFileRead()
2663 {
2664   // Compose current settings strings
2665   {
2666     cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256);
2667     auto cha = [&cryptoHash](cm::string_view value) {
2668       cryptoHash.Append(value);
2669       cryptoHash.Append(";");
2670     };
2671
2672     if (MocConst_.Enabled) {
2673       cryptoHash.Initialize();
2674       cha(MocConst().Executable);
2675       for (auto const& item : MocConst().OptionsDefinitions) {
2676         cha(item);
2677       }
2678       for (auto const& item : MocConst().OptionsIncludes) {
2679         cha(item);
2680       }
2681       for (auto const& item : MocConst().OptionsExtra) {
2682         cha(item);
2683       }
2684       for (auto const& item : MocConst().PredefsCmd) {
2685         cha(item);
2686       }
2687       for (auto const& filter : MocConst().DependFilters) {
2688         cha(filter.Key);
2689       }
2690       for (auto const& filter : MocConst().MacroFilters) {
2691         cha(filter.Key);
2692       }
2693       SettingsStringMoc_ = cryptoHash.FinalizeHex();
2694     }
2695
2696     if (UicConst().Enabled) {
2697       cryptoHash.Initialize();
2698       cha(UicConst().Executable);
2699       std::for_each(UicConst().Options.begin(), UicConst().Options.end(), cha);
2700       for (const auto& item : UicConst().UiFiles) {
2701         cha(item.first);
2702         auto const& opts = item.second.Options;
2703         std::for_each(opts.begin(), opts.end(), cha);
2704       }
2705       SettingsStringUic_ = cryptoHash.FinalizeHex();
2706     }
2707   }
2708
2709   // Read old settings and compare
2710   {
2711     std::string content;
2712     if (cmQtAutoGenerator::FileRead(content, SettingsFile_)) {
2713       if (MocConst().Enabled) {
2714         if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
2715           MocConst_.SettingsChanged = true;
2716         }
2717       }
2718       if (UicConst().Enabled) {
2719         if (SettingsStringUic_ != SettingsFind(content, "uic")) {
2720           UicConst_.SettingsChanged = true;
2721         }
2722       }
2723       // In case any setting changed remove the old settings file.
2724       // This triggers a full rebuild on the next run if the current
2725       // build is aborted before writing the current settings in the end.
2726       if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
2727         cmSystemTools::RemoveFile(SettingsFile_);
2728       }
2729     } else {
2730       // Settings file read failed
2731       if (MocConst().Enabled) {
2732         MocConst_.SettingsChanged = true;
2733       }
2734       if (UicConst().Enabled) {
2735         UicConst_.SettingsChanged = true;
2736       }
2737     }
2738   }
2739 }
2740
2741 bool cmQtAutoMocUicT::SettingsFileWrite()
2742 {
2743   // Only write if any setting changed
2744   if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
2745     if (Log().Verbose()) {
2746       Log().Info(
2747         GenT::GEN,
2748         cmStrCat("Writing the settings file ", MessagePath(SettingsFile_)));
2749     }
2750     // Compose settings file content
2751     std::string content;
2752     {
2753       auto SettingAppend = [&content](cm::string_view key,
2754                                       cm::string_view value) {
2755         if (!value.empty()) {
2756           content += cmStrCat(key, ':', value, '\n');
2757         }
2758       };
2759       SettingAppend("moc", SettingsStringMoc_);
2760       SettingAppend("uic", SettingsStringUic_);
2761     }
2762     // Write settings file
2763     std::string error;
2764     if (!cmQtAutoGenerator::FileWrite(SettingsFile_, content, &error)) {
2765       Log().Error(GenT::GEN,
2766                   cmStrCat("Writing the settings file ",
2767                            MessagePath(SettingsFile_), " failed.\n", error));
2768       // Remove old settings file to trigger a full rebuild on the next run
2769       cmSystemTools::RemoveFile(SettingsFile_);
2770       return false;
2771     }
2772   }
2773   return true;
2774 }
2775
2776 void cmQtAutoMocUicT::ParseCacheRead()
2777 {
2778   cm::string_view reason;
2779   // Don't read the cache if it is invalid
2780   if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) {
2781     reason = "Refreshing parse cache because it doesn't exist.";
2782   } else if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
2783     reason = "Refreshing parse cache because the settings changed.";
2784   } else if (BaseEval().ParseCacheTime.Older(
2785                BaseConst().CMakeExecutableTime)) {
2786     reason =
2787       "Refreshing parse cache because it is older than the CMake executable.";
2788   }
2789
2790   if (!reason.empty()) {
2791     // Don't read but refresh the complete parse cache
2792     if (Log().Verbose()) {
2793       Log().Info(GenT::GEN, reason);
2794     }
2795     BaseEval().ParseCacheChanged = true;
2796   } else {
2797     // Read parse cache
2798     BaseEval().ParseCache.ReadFromFile(BaseConst().ParseCacheFile);
2799   }
2800 }
2801
2802 bool cmQtAutoMocUicT::ParseCacheWrite()
2803 {
2804   if (BaseEval().ParseCacheChanged) {
2805     if (Log().Verbose()) {
2806       Log().Info(GenT::GEN,
2807                  cmStrCat("Writing the parse cache file ",
2808                           MessagePath(BaseConst().ParseCacheFile)));
2809     }
2810     if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) {
2811       Log().Error(GenT::GEN,
2812                   cmStrCat("Writing the parse cache file ",
2813                            MessagePath(BaseConst().ParseCacheFile),
2814                            " failed."));
2815       return false;
2816     }
2817   }
2818   return true;
2819 }
2820
2821 bool cmQtAutoMocUicT::CreateDirectories()
2822 {
2823   // Create AUTOGEN include directory
2824   if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) {
2825     Log().Error(GenT::GEN,
2826                 cmStrCat("Creating the AUTOGEN include directory ",
2827                          MessagePath(BaseConst().AutogenIncludeDir),
2828                          " failed."));
2829     return false;
2830   }
2831   return true;
2832 }
2833
2834 std::vector<std::string> cmQtAutoMocUicT::dependenciesFromDepFile(
2835   const char* filePath)
2836 {
2837   cmGccDepfileContent content = cmReadGccDepfile(filePath);
2838   if (content.empty()) {
2839     return {};
2840   }
2841
2842   // Moc outputs a depfile with exactly one rule.
2843   // Discard the rule and return the dependencies.
2844   return content.front().paths;
2845 }
2846
2847 void cmQtAutoMocUicT::Abort(bool error)
2848 {
2849   if (error) {
2850     JobError_.store(true);
2851   }
2852   WorkerPool_.Abort();
2853 }
2854
2855 std::string cmQtAutoMocUicT::AbsoluteBuildPath(
2856   cm::string_view relativePath) const
2857 {
2858   return cmStrCat(BaseConst().AutogenBuildDir, '/', relativePath);
2859 }
2860
2861 std::string cmQtAutoMocUicT::AbsoluteIncludePath(
2862   cm::string_view relativePath) const
2863 {
2864   return cmStrCat(BaseConst().AutogenIncludeDir, '/', relativePath);
2865 }
2866
2867 } // End of unnamed namespace
2868
2869 bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config)
2870 {
2871   return cmQtAutoMocUicT().Run(infoFile, config);
2872 }