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