summaryrefslogtreecommitdiffstats
path: root/Source/cmQtAutoMocUic.cxx
diff options
context:
space:
mode:
authorSebastian Holtermann <sebholt@xwmw.org>2019-04-12 08:56:08 (GMT)
committerSebastian Holtermann <sebholt@xwmw.org>2019-04-15 14:07:13 (GMT)
commita3f062091f488237c0151f3f4753e0668f37c60d (patch)
treef38195bddc8e824cc3fab93aef0776010128da11 /Source/cmQtAutoMocUic.cxx
parent8cb26a0a2ad57ca9012f97c7437711ee94f1a9db (diff)
downloadCMake-a3f062091f488237c0151f3f4753e0668f37c60d.zip
CMake-a3f062091f488237c0151f3f4753e0668f37c60d.tar.gz
CMake-a3f062091f488237c0151f3f4753e0668f37c60d.tar.bz2
Autogen: Rename `cmQtAutoGeneratorMocUic` class to `cmQtAutoMocUic`
The class name `cmQtAutoGeneratorMocUic` is long and cumbersome. This renames it to `cmQtAutoMocUic`.
Diffstat (limited to 'Source/cmQtAutoMocUic.cxx')
-rw-r--r--Source/cmQtAutoMocUic.cxx1747
1 files changed, 1747 insertions, 0 deletions
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
new file mode 100644
index 0000000..75c5d8a
--- /dev/null
+++ b/Source/cmQtAutoMocUic.cxx
@@ -0,0 +1,1747 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmQtAutoMocUic.h"
+
+#include <algorithm>
+#include <array>
+#include <deque>
+#include <list>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include "cmAlgorithms.h"
+#include "cmCryptoHash.h"
+#include "cmMakefile.h"
+#include "cmQtAutoGen.h"
+#include "cmSystemTools.h"
+#include "cmake.h"
+
+#if defined(__APPLE__)
+# include <unistd.h>
+#endif
+
+// -- Class methods
+
+std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath(
+ std::string const& relativePath) const
+{
+ return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
+}
+
+/**
+ * @brief Tries to find the header file to the given file base path by
+ * appending different header extensions
+ * @return True on success
+ */
+bool cmQtAutoMocUic::BaseSettingsT::FindHeader(
+ std::string& header, std::string const& testBasePath) const
+{
+ for (std::string const& ext : HeaderExtensions) {
+ std::string testFilePath(testBasePath);
+ testFilePath.push_back('.');
+ testFilePath += ext;
+ if (FileSys->FileExists(testFilePath)) {
+ header = testFilePath;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
+{
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+/**
+ * @brief Returns the first relevant Qt macro name found in the given C++ code
+ * @return The name of the Qt macro or an empty string
+ */
+std::string cmQtAutoMocUic::MocSettingsT::FindMacro(
+ std::string const& content) const
+{
+ for (KeyExpT const& filter : MacroFilters) {
+ // Run a simple find string operation before the expensive
+ // regular expression check
+ if (content.find(filter.Key) != std::string::npos) {
+ cmsys::RegularExpressionMatch match;
+ if (filter.Exp.find(content.c_str(), match)) {
+ // Return macro name on demand
+ return filter.Key;
+ }
+ }
+ }
+ return std::string();
+}
+
+std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
+{
+ std::string res;
+ const auto itB = MacroFilters.cbegin();
+ const auto itE = MacroFilters.cend();
+ const auto itL = itE - 1;
+ auto itC = itB;
+ for (; itC != itE; ++itC) {
+ // Separator
+ if (itC != itB) {
+ if (itC != itL) {
+ res += ", ";
+ } else {
+ res += " or ";
+ }
+ }
+ // Key
+ res += itC->Key;
+ }
+ return res;
+}
+
+std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile(
+ std::string const& sourcePath, std::string const& includeString) const
+{
+ // Search in vicinity of the source
+ {
+ std::string testPath = sourcePath;
+ testPath += includeString;
+ if (FileSys->FileExists(testPath)) {
+ return FileSys->GetRealPath(testPath);
+ }
+ }
+ // Search in include directories
+ for (std::string const& path : IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeString;
+ if (FileSys->FileExists(fullPath)) {
+ return FileSys->GetRealPath(fullPath);
+ }
+ }
+ // Return empty string
+ return std::string();
+}
+
+void cmQtAutoMocUic::MocSettingsT::FindDependencies(
+ std::string const& content, std::set<std::string>& depends) const
+{
+ if (!DependFilters.empty() && !content.empty()) {
+ for (KeyExpT const& filter : DependFilters) {
+ // Run a simple find string check
+ if (content.find(filter.Key) != std::string::npos) {
+ // Run the expensive regular expression check loop
+ const char* contentChars = content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (filter.Exp.find(contentChars, match)) {
+ {
+ std::string dep = match.match(1);
+ if (!dep.empty()) {
+ depends.emplace(std::move(dep));
+ }
+ }
+ contentChars += match.end();
+ }
+ }
+ }
+ }
+}
+
+bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const
+{
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+void cmQtAutoMocUic::JobT::LogError(GenT genType,
+ std::string const& message) const
+{
+ Gen()->AbortError();
+ Gen()->Log().Error(genType, message);
+}
+
+void cmQtAutoMocUic::JobT::LogFileError(GenT genType,
+ std::string const& filename,
+ std::string const& message) const
+{
+ Gen()->AbortError();
+ Gen()->Log().ErrorFile(genType, filename, message);
+}
+
+void cmQtAutoMocUic::JobT::LogCommandError(
+ GenT genType, std::string const& message,
+ std::vector<std::string> const& command, std::string const& output) const
+{
+ Gen()->AbortError();
+ Gen()->Log().ErrorCommand(genType, message, command, output);
+}
+
+bool cmQtAutoMocUic::JobT::RunProcess(GenT genType,
+ cmWorkerPool::ProcessResultT& result,
+ std::vector<std::string> const& command)
+{
+ // Log command
+ if (Log().Verbose()) {
+ std::string msg = "Running command:\n";
+ msg += QuotedCommand(command);
+ msg += '\n';
+ Log().Info(genType, msg);
+ }
+ return cmWorkerPool::JobT::RunProcess(result, command,
+ Gen()->Base().AutogenBuildDir);
+}
+
+void cmQtAutoMocUic::JobMocPredefsT::Process()
+{
+ // (Re)generate moc_predefs.h on demand
+ bool generate(false);
+ bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs));
+ if (!fileExists) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(Gen()->Moc().PredefsFileRel);
+ reason += " because it doesn't exist";
+ Log().Info(GenT::MOC, reason);
+ }
+ generate = true;
+ } else if (Gen()->Moc().SettingsChanged) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(Gen()->Moc().PredefsFileRel);
+ reason += " because the settings changed.";
+ Log().Info(GenT::MOC, reason);
+ }
+ generate = true;
+ }
+ if (generate) {
+ cmWorkerPool::ProcessResultT result;
+ {
+ // Compose command
+ std::vector<std::string> cmd = Gen()->Moc().PredefsCmd;
+ // Add includes
+ cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(),
+ Gen()->Moc().Includes.end());
+ // Add definitions
+ for (std::string const& def : Gen()->Moc().Definitions) {
+ cmd.push_back("-D" + def);
+ }
+ // Execute command
+ if (!RunProcess(GenT::MOC, result, cmd)) {
+ std::string emsg = "The content generation command for ";
+ emsg += Quoted(Gen()->Moc().PredefsFileRel);
+ emsg += " failed.\n";
+ emsg += result.ErrorMessage;
+ LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+ }
+ }
+
+ // (Re)write predefs file only on demand
+ if (!result.error()) {
+ if (!fileExists ||
+ FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
+ if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
+ // Success
+ } else {
+ std::string emsg = "Writing ";
+ emsg += Quoted(Gen()->Moc().PredefsFileRel);
+ emsg += " failed.";
+ LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg);
+ }
+ } else {
+ // Touch to update the time stamp
+ if (Log().Verbose()) {
+ std::string msg = "Touching ";
+ msg += Quoted(Gen()->Moc().PredefsFileRel);
+ msg += ".";
+ Log().Info(GenT::MOC, msg);
+ }
+ FileSys().Touch(Gen()->Moc().PredefsFileAbs);
+ }
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobParseT::Process()
+{
+ if (AutoMoc && Header) {
+ // Don't parse header for moc if the file is included by a source already
+ if (Gen()->ParallelMocIncluded(FileName)) {
+ AutoMoc = false;
+ }
+ }
+
+ if (AutoMoc || AutoUic) {
+ std::string error;
+ MetaT meta;
+ if (FileSys().FileRead(meta.Content, FileName, &error)) {
+ if (!meta.Content.empty()) {
+ meta.FileDir = FileSys().SubDirPrefix(FileName);
+ meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName);
+
+ bool success = true;
+ if (AutoMoc) {
+ if (Header) {
+ success = ParseMocHeader(meta);
+ } else {
+ success = ParseMocSource(meta);
+ }
+ }
+ if (AutoUic && success) {
+ ParseUic(meta);
+ }
+ } else {
+ Log().WarningFile(GenT::GEN, FileName, "The source file is empty");
+ }
+ } else {
+ LogFileError(GenT::GEN, FileName, "Could not read the file: " + error);
+ }
+ }
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta)
+{
+ struct JobPre
+ {
+ bool self; // source file is self
+ bool underscore; // "moc_" style include
+ std::string SourceFile;
+ std::string IncludeString;
+ };
+
+ struct MocInclude
+ {
+ std::string Inc; // full include string
+ std::string Dir; // include string directory
+ std::string Base; // include string file base
+ };
+
+ // Check if this source file contains a relevant macro
+ std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content);
+
+ // Extract moc includes from file
+ std::deque<MocInclude> mocIncsUsc;
+ std::deque<MocInclude> mocIncsDot;
+ {
+ if (meta.Content.find("moc") != std::string::npos) {
+ const char* contentChars = meta.Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (Gen()->Moc().RegExpInclude.find(contentChars, match)) {
+ std::string incString = match.match(2);
+ std::string incDir(FileSys().SubDirPrefix(incString));
+ std::string incBase =
+ FileSys().GetFilenameWithoutLastExtension(incString);
+ if (cmHasLiteralPrefix(incBase, "moc_")) {
+ // moc_<BASE>.cxx
+ // Remove the moc_ part from the base name
+ mocIncsUsc.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), incBase.substr(4) });
+ } else {
+ // <BASE>.moc
+ mocIncsDot.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), std::move(incBase) });
+ }
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
+ }
+
+ // Check if there is anything to do
+ if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
+ return true;
+ }
+
+ bool ownDotMocIncluded = false;
+ bool ownMocUscIncluded = false;
+ std::deque<JobPre> jobs;
+
+ // Process moc_<BASE>.cxx includes
+ for (const MocInclude& mocInc : mocIncsUsc) {
+ std::string const header =
+ MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (Gen()->Moc().skipped(header)) {
+ continue;
+ }
+ // Register moc job
+ const bool ownMoc = (mocInc.Base == meta.FileBase);
+ jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc });
+ // Store meta information for relaxed mode
+ if (ownMoc) {
+ ownMocUscIncluded = true;
+ }
+ } else {
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but the header ";
+ emsg += Quoted(MocStringHeaders(mocInc.Base));
+ emsg += " could not be found.";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Process <BASE>.moc includes
+ for (const MocInclude& mocInc : mocIncsDot) {
+ const bool ownMoc = (mocInc.Base == meta.FileBase);
+ if (Gen()->Moc().RelaxedMode) {
+ // Relaxed mode
+ if (!ownMacro.empty() && ownMoc) {
+ // Add self
+ jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+ ownDotMocIncluded = true;
+ } else {
+ // In relaxed mode try to find a header instead but issue a warning.
+ // This is for KDE4 compatibility
+ std::string const header =
+ MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (Gen()->Moc().skipped(header)) {
+ continue;
+ }
+ // Register moc job
+ jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
+ if (ownMacro.empty()) {
+ if (ownMoc) {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but does not contain a ";
+ emsg += Gen()->Moc().MacrosString();
+ emsg += " macro.\nRunning moc on\n ";
+ emsg += Quoted(header);
+ emsg += "!\nBetter include ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += " for a compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ } else {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += " instead of ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += ".\nRunning moc on\n ";
+ emsg += Quoted(header);
+ emsg += "!\nBetter include ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += " for compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ }
+ }
+ } else {
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", which seems to be the moc file from a different "
+ "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
+ "matching header ";
+ emsg += Quoted(MocStringHeaders(mocInc.Base));
+ emsg += " could not be found.";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+ } else {
+ // Strict mode
+ if (ownMoc) {
+ // Include self
+ jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+ ownDotMocIncluded = true;
+ // Accept but issue a warning if moc isn't required
+ if (ownMacro.empty()) {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but does not contain a ";
+ emsg += Gen()->Moc().MacrosString();
+ emsg += " macro.";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ }
+ } else {
+ // Don't allow <BASE>.moc include other than self in strict mode
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", which seems to be the moc file from a different "
+ "source file.\nThis is not supported. Include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += " to run moc on this source file.";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+ }
+
+ if (!ownMacro.empty() && !ownDotMocIncluded) {
+ // In this case, check whether the scanned file itself contains a
+ // Q_OBJECT.
+ // If this is the case, the moc_foo.cpp should probably be generated from
+ // foo.cpp instead of foo.h, because otherwise it won't build.
+ // But warn, since this is not how it is supposed to be used.
+ // This is for KDE4 compatibility.
+ if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) {
+ JobPre uscJobPre;
+ // Remove underscore job request
+ {
+ auto itC = jobs.begin();
+ auto itE = jobs.end();
+ for (; itC != itE; ++itC) {
+ JobPre& job(*itC);
+ if (job.self && job.underscore) {
+ uscJobPre = std::move(job);
+ jobs.erase(itC);
+ break;
+ }
+ }
+ }
+ // Issue a warning
+ {
+ std::string emsg = "The file contains a ";
+ emsg += ownMacro;
+ emsg += " macro, but does not include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += ". Instead it includes ";
+ emsg += Quoted(uscJobPre.IncludeString);
+ emsg += ".\nRunning moc on\n ";
+ emsg += Quoted(FileName);
+ emsg += "!\nBetter include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += " for compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ }
+ // Add own source job
+ jobs.emplace_back(
+ JobPre{ true, false, FileName, uscJobPre.IncludeString });
+ } else {
+ // Otherwise always error out since it will not compile.
+ {
+ std::string emsg = "The file contains a ";
+ emsg += ownMacro;
+ emsg += " macro, but does not include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += "!\nConsider to\n - add #include \"";
+ emsg += meta.FileBase;
+ emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Convert pre jobs to actual jobs
+ for (JobPre& jobPre : jobs) {
+ cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
+ std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
+ if (jobPre.self) {
+ // Read dependencies from this source
+ JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
+ Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
+ jobMoc.DependsValid = true;
+ }
+ if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta)
+{
+ bool success = true;
+ std::string const macroName = Gen()->Moc().FindMacro(meta.Content);
+ if (!macroName.empty()) {
+ cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
+ std::string(FileName), std::string(), std::string());
+ // Read dependencies from this source
+ {
+ JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
+ Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
+ jobMoc.DependsValid = true;
+ }
+ success = Gen()->ParallelJobPushMoc(std::move(jobHandle));
+ }
+ return success;
+}
+
+std::string cmQtAutoMocUic::JobParseT::MocStringHeaders(
+ std::string const& fileBase) const
+{
+ std::string res = fileBase;
+ res += ".{";
+ res += cmJoin(Gen()->Base().HeaderExtensions, ",");
+ res += "}";
+ return res;
+}
+
+std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader(
+ std::string const& includerDir, std::string const& includeBase)
+{
+ std::string header;
+ // Search in vicinity of the source
+ if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) {
+ // Search in include directories
+ for (std::string const& path : Gen()->Moc().IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeBase;
+ if (Gen()->Base().FindHeader(header, fullPath)) {
+ break;
+ }
+ }
+ }
+ // Sanitize
+ if (!header.empty()) {
+ header = FileSys().GetRealPath(header);
+ }
+ return header;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta)
+{
+ bool success = true;
+ if (meta.Content.find("ui_") != std::string::npos) {
+ const char* contentChars = meta.Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (Gen()->Uic().RegExpInclude.find(contentChars, match)) {
+ if (!ParseUicInclude(meta, match.match(2))) {
+ success = false;
+ break;
+ }
+ contentChars += match.end();
+ }
+ }
+ return success;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta,
+ std::string&& includeString)
+{
+ bool success = false;
+ std::string uiInputFile = UicFindIncludedFile(meta, includeString);
+ if (!uiInputFile.empty()) {
+ if (!Gen()->Uic().skipped(uiInputFile)) {
+ cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>(
+ std::move(uiInputFile), FileName, std::move(includeString));
+ success = Gen()->ParallelJobPushUic(std::move(jobHandle));
+ } else {
+ // A skipped file is successful
+ success = true;
+ }
+ }
+ return success;
+}
+
+std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile(
+ MetaT const& meta, std::string const& includeString)
+{
+ std::string res;
+ std::string searchFile =
+ FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
+ searchFile += ".ui";
+ // Collect search paths list
+ std::deque<std::string> testFiles;
+ {
+ std::string const searchPath = FileSys().SubDirPrefix(includeString);
+
+ std::string searchFileFull;
+ if (!searchPath.empty()) {
+ searchFileFull = searchPath;
+ searchFileFull += searchFile;
+ }
+ // Vicinity of the source
+ {
+ std::string const sourcePath = meta.FileDir;
+ testFiles.push_back(sourcePath + searchFile);
+ if (!searchPath.empty()) {
+ testFiles.push_back(sourcePath + searchFileFull);
+ }
+ }
+ // AUTOUIC search paths
+ if (!Gen()->Uic().SearchPaths.empty()) {
+ for (std::string const& sPath : Gen()->Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFile));
+ }
+ if (!searchPath.empty()) {
+ for (std::string const& sPath : Gen()->Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFileFull));
+ }
+ }
+ }
+ }
+
+ // Search for the .ui file!
+ for (std::string const& testFile : testFiles) {
+ if (FileSys().FileExists(testFile)) {
+ res = FileSys().GetRealPath(testFile);
+ break;
+ }
+ }
+
+ // Log error
+ if (res.empty()) {
+ std::string emsg = "Could not find ";
+ emsg += Quoted(searchFile);
+ emsg += " in\n";
+ for (std::string const& testFile : testFiles) {
+ emsg += " ";
+ emsg += Quoted(testFile);
+ emsg += "\n";
+ }
+ LogFileError(GenT::UIC, FileName, emsg);
+ }
+
+ return res;
+}
+
+void cmQtAutoMocUic::JobPostParseT::Process()
+{
+ if (Gen()->Moc().Enabled) {
+ // Add mocs compilations fence job
+ Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
+ }
+ // Add finish job
+ Gen()->WorkerPool().EmplaceJob<JobFinishT>();
+}
+
+void cmQtAutoMocUic::JobMocsCompilationT::Process()
+{
+ // Compose mocs compilation file content
+ std::string content =
+ "// This file is autogenerated. Changes will be overwritten.\n";
+ if (Gen()->MocAutoFiles().empty()) {
+ // Placeholder content
+ content += "// No files found that require moc or the moc files are "
+ "included\n";
+ content += "enum some_compilers { need_more_than_nothing };\n";
+ } else {
+ // Valid content
+ char const sbeg = Gen()->Base().MultiConfig ? '<' : '"';
+ char const send = Gen()->Base().MultiConfig ? '>' : '"';
+ for (std::string const& mocfile : Gen()->MocAutoFiles()) {
+ content += "#include ";
+ content += sbeg;
+ content += mocfile;
+ content += send;
+ content += '\n';
+ }
+ }
+
+ std::string const& compAbs = Gen()->Moc().CompFileAbs;
+ if (FileSys().FileDiffers(compAbs, content)) {
+ // Actually write mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
+ }
+ if (!FileSys().FileWrite(compAbs, content)) {
+ LogFileError(GenT::MOC, compAbs,
+ "mocs compilation file writing failed.");
+ }
+ } else if (Gen()->MocAutoFileUpdated()) {
+ // Only touch mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
+ }
+ FileSys().Touch(compAbs);
+ }
+}
+
+void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content)
+{
+ Gen()->Moc().FindDependencies(content, Depends);
+ DependsValid = true;
+}
+
+void cmQtAutoMocUic::JobMocT::Process()
+{
+ // Compute build file name
+ if (!IncludeString.empty()) {
+ BuildFile = Gen()->Base().AutogenIncludeDir;
+ BuildFile += '/';
+ BuildFile += IncludeString;
+ } else {
+ // Relative build path
+ std::string relPath = FileSys().GetFilePathChecksum(SourceFile);
+ relPath += "/moc_";
+ relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile);
+
+ // Register relative file path with duplication check
+ relPath = Gen()->ParallelMocAutoRegister(relPath);
+
+ // Absolute build path
+ if (Gen()->Base().MultiConfig) {
+ BuildFile = Gen()->Base().AutogenIncludeDir;
+ BuildFile += '/';
+ BuildFile += relPath;
+ } else {
+ BuildFile = Gen()->Base().AbsoluteBuildPath(relPath);
+ }
+ }
+
+ if (UpdateRequired()) {
+ GenerateMoc();
+ }
+}
+
+bool cmQtAutoMocUic::JobMocT::UpdateRequired()
+{
+ bool const verbose = Log().Verbose();
+
+ // Test if the build file exists
+ if (!FileSys().FileExists(BuildFile)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from its source file ";
+ reason += Quoted(SourceFile);
+ reason += " because it doesn't exist";
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if any setting changed
+ if (Gen()->Moc().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the MOC settings changed";
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if the moc_predefs file is newer
+ if (!Gen()->Moc().PredefsFileAbs.empty()) {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(BuildFile,
+ Gen()->Moc().PredefsFileAbs, &error);
+ if (!isOlder && !error.empty()) {
+ LogError(GenT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than: ";
+ reason += Quoted(Gen()->Moc().PredefsFileAbs);
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ LogError(GenT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than its source file ";
+ reason += Quoted(SourceFile);
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if a dependency file is newer
+ {
+ // Read dependencies on demand
+ if (!DependsValid) {
+ std::string content;
+ {
+ std::string error;
+ if (!FileSys().FileRead(content, SourceFile, &error)) {
+ std::string emsg = "Could not read file\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\nrequired by moc include ";
+ emsg += Quoted(IncludeString);
+ emsg += " in\n ";
+ emsg += Quoted(IncluderFile);
+ emsg += ".\n";
+ emsg += error;
+ LogError(GenT::MOC, emsg);
+ return false;
+ }
+ }
+ FindDependencies(content);
+ }
+ // Check dependency timestamps
+ std::string error;
+ std::string sourceDir = FileSys().SubDirPrefix(SourceFile);
+ for (std::string const& depFileRel : Depends) {
+ std::string depFileAbs =
+ Gen()->Moc().FindIncludedFile(sourceDir, depFileRel);
+ if (!depFileAbs.empty()) {
+ if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because it is older than it's dependency file ";
+ reason += Quoted(depFileAbs);
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+ if (!error.empty()) {
+ LogError(GenT::MOC, error);
+ return false;
+ }
+ } else {
+ std::string message = "Could not find dependency file ";
+ message += Quoted(depFileRel);
+ Log().WarningFile(GenT::MOC, SourceFile, message);
+ }
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoMocUic::JobMocT::GenerateMoc()
+{
+ // Make sure the parent directory exists
+ if (!FileSys().MakeParentDirectory(BuildFile)) {
+ LogFileError(GenT::MOC, BuildFile, "Could not create parent directory.");
+ return;
+ }
+ {
+ // Compose moc command
+ std::vector<std::string> cmd;
+ cmd.push_back(Gen()->Moc().Executable);
+ // Add options
+ cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(),
+ Gen()->Moc().AllOptions.end());
+ // Add predefs include
+ if (!Gen()->Moc().PredefsFileAbs.empty()) {
+ cmd.emplace_back("--include");
+ cmd.push_back(Gen()->Moc().PredefsFileAbs);
+ }
+ cmd.emplace_back("-o");
+ cmd.push_back(BuildFile);
+ cmd.push_back(SourceFile);
+
+ // Execute moc command
+ cmWorkerPool::ProcessResultT result;
+ if (RunProcess(GenT::MOC, result, cmd)) {
+ // Moc command success
+ // Print moc output
+ if (!result.StdOut.empty()) {
+ Log().Info(GenT::MOC, result.StdOut);
+ }
+ // Notify the generator that a not included file changed (on demand)
+ if (IncludeString.empty()) {
+ Gen()->ParallelMocAutoUpdated();
+ }
+ } else {
+ // Moc command failed
+ {
+ std::string emsg = "The moc process failed to compile\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\ninto\n ";
+ emsg += Quoted(BuildFile);
+ emsg += ".\n";
+ emsg += result.ErrorMessage;
+ LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+ }
+ FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobUicT::Process()
+{
+ // Compute build file name
+ BuildFile = Gen()->Base().AutogenIncludeDir;
+ BuildFile += '/';
+ BuildFile += IncludeString;
+
+ if (UpdateRequired()) {
+ GenerateUic();
+ }
+}
+
+bool cmQtAutoMocUic::JobUicT::UpdateRequired()
+{
+ bool const verbose = Log().Verbose();
+
+ // Test if the build file exists
+ if (!FileSys().FileExists(BuildFile)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from its source file ";
+ reason += Quoted(SourceFile);
+ reason += " because it doesn't exist";
+ Log().Info(GenT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the uic settings changed
+ if (Gen()->Uic().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the UIC settings changed";
+ Log().Info(GenT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ LogError(GenT::UIC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than its source file ";
+ reason += Quoted(SourceFile);
+ Log().Info(GenT::UIC, reason);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoMocUic::JobUicT::GenerateUic()
+{
+ // Make sure the parent directory exists
+ if (!FileSys().MakeParentDirectory(BuildFile)) {
+ LogFileError(GenT::UIC, BuildFile, "Could not create parent directory.");
+ return;
+ }
+ {
+ // Compose uic command
+ std::vector<std::string> cmd;
+ cmd.push_back(Gen()->Uic().Executable);
+ {
+ std::vector<std::string> allOpts = Gen()->Uic().TargetOptions;
+ auto optionIt = Gen()->Uic().Options.find(SourceFile);
+ if (optionIt != Gen()->Uic().Options.end()) {
+ UicMergeOptions(allOpts, optionIt->second,
+ (Gen()->Base().QtVersionMajor == 5));
+ }
+ cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+ }
+ cmd.emplace_back("-o");
+ cmd.emplace_back(BuildFile);
+ cmd.emplace_back(SourceFile);
+
+ cmWorkerPool::ProcessResultT result;
+ if (RunProcess(GenT::UIC, result, cmd)) {
+ // Uic command success
+ // Print uic output
+ if (!result.StdOut.empty()) {
+ Log().Info(GenT::UIC, result.StdOut);
+ }
+ } else {
+ // Uic command failed
+ {
+ std::string emsg = "The uic process failed to compile\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\ninto\n ";
+ emsg += Quoted(BuildFile);
+ emsg += "\nincluded by\n ";
+ emsg += Quoted(IncluderFile);
+ emsg += ".\n";
+ emsg += result.ErrorMessage;
+ LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
+ }
+ FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobFinishT::Process()
+{
+ Gen()->AbortSuccess();
+}
+
+cmQtAutoMocUic::cmQtAutoMocUic()
+ : Base_(&FileSys())
+ , Moc_(&FileSys())
+{
+ // Precompile regular expressions
+ Moc_.RegExpInclude.compile(
+ "(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
+ Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+}
+
+cmQtAutoMocUic::~cmQtAutoMocUic() = default;
+
+bool cmQtAutoMocUic::Init(cmMakefile* makefile)
+{
+ // -- Meta
+ Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
+
+ // Utility lambdas
+ auto InfoGet = [makefile](const char* key) {
+ return makefile->GetSafeDefinition(key);
+ };
+ auto InfoGetBool = [makefile](const char* key) {
+ return makefile->IsOn(key);
+ };
+ auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
+ return list;
+ };
+ auto InfoGetLists =
+ [makefile](const char* key) -> std::vector<std::vector<std::string>> {
+ std::vector<std::vector<std::string>> lists;
+ {
+ std::string const value = makefile->GetSafeDefinition(key);
+ std::string::size_type pos = 0;
+ while (pos < value.size()) {
+ std::string::size_type next = value.find(ListSep, pos);
+ std::string::size_type length =
+ (next != std::string::npos) ? next - pos : value.size() - pos;
+ // Remove enclosing braces
+ if (length >= 2) {
+ std::string::const_iterator itBeg = value.begin() + (pos + 1);
+ std::string::const_iterator itEnd = itBeg + (length - 2);
+ {
+ std::string subValue(itBeg, itEnd);
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(subValue, list);
+ lists.push_back(std::move(list));
+ }
+ }
+ pos += length;
+ pos += ListSep.size();
+ }
+ }
+ return lists;
+ };
+ auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
+ const char* valueConf = nullptr;
+ {
+ std::string keyConf = key;
+ keyConf += '_';
+ keyConf += InfoConfig();
+ valueConf = makefile->GetDefinition(keyConf);
+ }
+ if (valueConf == nullptr) {
+ return makefile->GetSafeDefinition(key);
+ }
+ return std::string(valueConf);
+ };
+ auto InfoGetConfigList =
+ [&InfoGetConfig](const char* key) -> std::vector<std::string> {
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
+ return list;
+ };
+
+ // -- Read info file
+ if (!makefile->ReadListFile(InfoFile())) {
+ Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
+ return false;
+ }
+
+ // -- Meta
+ Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
+ Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
+ {
+ unsigned long num = Base_.NumThreads;
+ if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
+ num = std::max<unsigned long>(num, 1);
+ num = std::min<unsigned long>(num, ParallelMax);
+ Base_.NumThreads = static_cast<unsigned int>(num);
+ }
+ }
+
+ // - Files and directories
+ Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
+ Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
+ Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
+ Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
+ Base_.IncludeProjectDirsBefore =
+ InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
+ Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
+ if (Base_.AutogenBuildDir.empty()) {
+ Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
+ return false;
+ }
+ // include directory
+ Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
+ if (Base_.AutogenIncludeDir.empty()) {
+ Log().ErrorFile(GenT::GEN, InfoFile(),
+ "Autogen include directory missing");
+ return false;
+ }
+
+ // - Files
+ SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
+ if (SettingsFile_.empty()) {
+ Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
+ return false;
+ }
+
+ // - Qt environment
+ {
+ unsigned long qtv = Base_.QtVersionMajor;
+ if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
+ &qtv)) {
+ Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
+ }
+ }
+
+ // - Moc
+ Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+ Moc_.Enabled = !Moc().Executable.empty();
+ if (Moc().Enabled) {
+ for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
+ Moc_.SkipList.insert(std::move(sfl));
+ }
+ Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+ Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
+ Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
+ Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
+ for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
+ Moc_.MacroFilters.emplace_back(
+ item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
+ }
+ {
+ auto pushFilter = [this](std::string const& key, std::string const& exp,
+ std::string& error) {
+ if (!key.empty()) {
+ if (!exp.empty()) {
+ Moc_.DependFilters.emplace_back();
+ KeyExpT& filter(Moc_.DependFilters.back());
+ if (filter.Exp.compile(exp)) {
+ filter.Key = key;
+ } else {
+ error = "Regular expression compiling failed";
+ }
+ } else {
+ error = "Regular expression is empty";
+ }
+ } else {
+ error = "Key is empty";
+ }
+ if (!error.empty()) {
+ error = ("AUTOMOC_DEPEND_FILTERS: " + error);
+ error += "\n";
+ error += " Key: ";
+ error += Quoted(key);
+ error += "\n";
+ error += " Exp: ";
+ error += Quoted(exp);
+ error += "\n";
+ }
+ };
+
+ std::string error;
+ // Insert default filter for Q_PLUGIN_METADATA
+ if (Base().QtVersionMajor != 4) {
+ pushFilter("Q_PLUGIN_METADATA",
+ "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+ "[^\\)]*FILE[ \t]*\"([^\"]+)\"",
+ error);
+ }
+ // Insert user defined dependency filters
+ {
+ std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
+ if ((flts.size() % 2) == 0) {
+ for (std::vector<std::string>::iterator itC = flts.begin(),
+ itE = flts.end();
+ itC != itE; itC += 2) {
+ pushFilter(*itC, *(itC + 1), error);
+ if (!error.empty()) {
+ break;
+ }
+ }
+ } else {
+ Log().ErrorFile(
+ GenT::MOC, InfoFile(),
+ "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ return false;
+ }
+ }
+ if (!error.empty()) {
+ Log().ErrorFile(GenT::MOC, InfoFile(), error);
+ return false;
+ }
+ }
+ Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+ // Install moc predefs job
+ if (!Moc().PredefsCmd.empty()) {
+ WorkerPool().EmplaceJob<JobMocPredefsT>();
+ }
+ }
+
+ // - Uic
+ Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+ Uic_.Enabled = !Uic().Executable.empty();
+ if (Uic().Enabled) {
+ for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
+ Uic_.SkipList.insert(std::move(sfl));
+ }
+ Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
+ Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
+ {
+ auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
+ auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
+ // Compare list sizes
+ if (sources.size() != options.size()) {
+ std::ostringstream ost;
+ ost << "files/options lists sizes mismatch (" << sources.size() << "/"
+ << options.size() << ")";
+ Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
+ return false;
+ }
+ auto fitEnd = sources.cend();
+ auto fit = sources.begin();
+ auto oit = options.begin();
+ while (fit != fitEnd) {
+ Uic_.Options[*fit] = std::move(*oit);
+ ++fit;
+ ++oit;
+ }
+ }
+ }
+
+ // - Headers and sources
+ // Add sources
+ {
+ auto addSource = [this](std::string&& src, bool moc, bool uic) {
+ WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false);
+ };
+ for (std::string& src : InfoGetList("AM_SOURCES")) {
+ addSource(std::move(src), true, true);
+ }
+ if (Moc().Enabled) {
+ for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
+ addSource(std::move(src), true, false);
+ }
+ }
+ if (Uic().Enabled) {
+ for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
+ addSource(std::move(src), false, true);
+ }
+ }
+ }
+ // Add Fence job
+ WorkerPool().EmplaceJob<JobFenceT>();
+ // Add headers
+ {
+ auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
+ WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true);
+ };
+ for (std::string& hdr : InfoGetList("AM_HEADERS")) {
+ addHeader(std::move(hdr), true, true);
+ }
+ if (Moc().Enabled) {
+ for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
+ addHeader(std::move(hdr), true, false);
+ }
+ }
+ if (Uic().Enabled) {
+ for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
+ addHeader(std::move(hdr), false, true);
+ }
+ }
+ }
+ // Addpost parse fence job
+ WorkerPool().EmplaceJob<JobPostParseT>();
+
+ // Init derived information
+ // ------------------------
+
+ // Init file path checksum generator
+ FileSys().setupFilePathChecksum(
+ Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
+ Base().ProjectBinaryDir);
+
+ // Moc variables
+ if (Moc().Enabled) {
+ // Mocs compilation file
+ Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
+
+ // Moc predefs file
+ if (!Moc_.PredefsCmd.empty()) {
+ Moc_.PredefsFileRel = "moc_predefs";
+ if (Base_.MultiConfig) {
+ Moc_.PredefsFileRel += '_';
+ Moc_.PredefsFileRel += InfoConfig();
+ }
+ Moc_.PredefsFileRel += ".h";
+ Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
+ }
+
+ // Sort include directories on demand
+ if (Base().IncludeProjectDirsBefore) {
+ // Move strings to temporary list
+ std::list<std::string> includes;
+ includes.insert(includes.end(), Moc().IncludePaths.begin(),
+ Moc().IncludePaths.end());
+ Moc_.IncludePaths.clear();
+ Moc_.IncludePaths.reserve(includes.size());
+ // Append project directories only
+ {
+ std::array<std::string const*, 2> const movePaths = {
+ { &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
+ };
+ for (std::string const* ppath : movePaths) {
+ std::list<std::string>::iterator it = includes.begin();
+ while (it != includes.end()) {
+ std::string const& path = *it;
+ if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
+ Moc_.IncludePaths.push_back(path);
+ it = includes.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ }
+ // Append remaining directories
+ Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
+ includes.end());
+ }
+ // Compose moc includes list
+ {
+ std::set<std::string> frameworkPaths;
+ for (std::string const& path : Moc().IncludePaths) {
+ Moc_.Includes.push_back("-I" + path);
+ // Extract framework path
+ if (cmHasLiteralSuffix(path, ".framework/Headers")) {
+ // Go up twice to get to the framework root
+ std::vector<std::string> pathComponents;
+ FileSys().SplitPath(path, pathComponents);
+ std::string frameworkPath = FileSys().JoinPath(
+ pathComponents.begin(), pathComponents.end() - 2);
+ frameworkPaths.insert(frameworkPath);
+ }
+ }
+ // Append framework includes
+ for (std::string const& path : frameworkPaths) {
+ Moc_.Includes.emplace_back("-F");
+ Moc_.Includes.push_back(path);
+ }
+ }
+ // Setup single list with all options
+ {
+ // Add includes
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
+ Moc().Includes.end());
+ // Add definitions
+ for (std::string const& def : Moc().Definitions) {
+ Moc_.AllOptions.push_back("-D" + def);
+ }
+ // Add options
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
+ Moc().Options.end());
+ }
+ }
+
+ return true;
+}
+
+bool cmQtAutoMocUic::Process()
+{
+ SettingsFileRead();
+ if (!CreateDirectories()) {
+ return false;
+ }
+
+ if (!WorkerPool_.Process(Base().NumThreads, this)) {
+ return false;
+ }
+
+ if (JobError_) {
+ return false;
+ }
+
+ return SettingsFileWrite();
+}
+
+void cmQtAutoMocUic::SettingsFileRead()
+{
+ // Compose current settings strings
+ {
+ cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
+ std::string const sep(" ~~~ ");
+ if (Moc_.Enabled) {
+ std::string str;
+ str += Moc().Executable;
+ str += sep;
+ str += cmJoin(Moc().AllOptions, ";");
+ str += sep;
+ str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
+ str += sep;
+ str += cmJoin(Moc().PredefsCmd, ";");
+ str += sep;
+ SettingsStringMoc_ = crypt.HashString(str);
+ }
+ if (Uic().Enabled) {
+ std::string str;
+ str += Uic().Executable;
+ str += sep;
+ str += cmJoin(Uic().TargetOptions, ";");
+ for (const auto& item : Uic().Options) {
+ str += sep;
+ str += item.first;
+ str += sep;
+ str += cmJoin(item.second, ";");
+ }
+ str += sep;
+ SettingsStringUic_ = crypt.HashString(str);
+ }
+ }
+
+ // Read old settings and compare
+ {
+ std::string content;
+ if (FileSys().FileRead(content, SettingsFile_)) {
+ if (Moc().Enabled) {
+ if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
+ Moc_.SettingsChanged = true;
+ }
+ }
+ if (Uic().Enabled) {
+ if (SettingsStringUic_ != SettingsFind(content, "uic")) {
+ Uic_.SettingsChanged = true;
+ }
+ }
+ // In case any setting changed remove the old settings file.
+ // This triggers a full rebuild on the next run if the current
+ // build is aborted before writing the current settings in the end.
+ if (Moc().SettingsChanged || Uic().SettingsChanged) {
+ FileSys().FileRemove(SettingsFile_);
+ }
+ } else {
+ // Settings file read failed
+ if (Moc().Enabled) {
+ Moc_.SettingsChanged = true;
+ }
+ if (Uic().Enabled) {
+ Uic_.SettingsChanged = true;
+ }
+ }
+ }
+}
+
+bool cmQtAutoMocUic::SettingsFileWrite()
+{
+ // Only write if any setting changed
+ if (Moc().SettingsChanged || Uic().SettingsChanged) {
+ if (Log().Verbose()) {
+ Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
+ }
+ // Compose settings file content
+ std::string content;
+ {
+ auto SettingAppend = [&content](const char* key,
+ std::string const& value) {
+ if (!value.empty()) {
+ content += key;
+ content += ':';
+ content += value;
+ content += '\n';
+ }
+ };
+ SettingAppend("moc", SettingsStringMoc_);
+ SettingAppend("uic", SettingsStringUic_);
+ }
+ // Write settings file
+ std::string error;
+ if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
+ Log().ErrorFile(GenT::GEN, SettingsFile_,
+ "Settings file writing failed. " + error);
+ // Remove old settings file to trigger a full rebuild on the next run
+ FileSys().FileRemove(SettingsFile_);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoMocUic::CreateDirectories()
+{
+ // Create AUTOGEN include directory
+ if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
+ Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
+ "Could not create directory.");
+ return false;
+ }
+ return true;
+}
+
+// Private method that requires cmQtAutoMocUic::JobsMutex_ to be
+// locked
+void cmQtAutoMocUic::Abort(bool error)
+{
+ if (error) {
+ JobError_.store(true);
+ }
+ WorkerPool_.Abort();
+}
+
+bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle)
+{
+ JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle));
+ // Do additional tests if this is an included moc job
+ if (!mocJob.IncludeString.empty()) {
+ std::lock_guard<std::mutex> guard(MocMetaMutex_);
+ // Register included moc file
+ MocIncludedFiles_.emplace(mocJob.SourceFile);
+
+ // Check if the same moc file would be generated from a different
+ // source file.
+ auto const range = MocIncludes_.equal_range(mocJob.IncludeString);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second[0] == mocJob.SourceFile) {
+ // The output file already gets generated
+ return true;
+ }
+ {
+ // The output file already gets generated - from a different source
+ // file!
+ std::string error = "The two source files\n ";
+ error += Quoted(mocJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(it->second[1]);
+ error += "\ncontain the same moc include string ";
+ error += Quoted(mocJob.IncludeString);
+ error += "\nbut the moc file would be generated from different "
+ "source files\n ";
+ error += Quoted(mocJob.SourceFile);
+ error += " and\n ";
+ error += Quoted(it->second[0]);
+ error += ".\nConsider to\n"
+ "- not include the \"moc_<NAME>.cpp\" file\n"
+ "- add a directory prefix to a \"<NAME>.moc\" include "
+ "(e.g \"sub/<NAME>.moc\")\n"
+ "- rename the source file(s)\n";
+ Log().Error(GenT::MOC, error);
+ AbortError();
+ return false;
+ }
+ }
+
+ // We're still here so register this job
+ MocIncludes_.emplace_hint(range.first, mocJob.IncludeString,
+ std::array<std::string, 2>{
+ { mocJob.SourceFile, mocJob.IncluderFile } });
+ }
+ return WorkerPool_.PushJob(std::move(jobHandle));
+}
+
+bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle)
+{
+ const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
+ {
+ std::lock_guard<std::mutex> guard(UicMetaMutex_);
+ // Check if the same uic file would be generated from a different
+ // source file.
+ auto const range = UicIncludes_.equal_range(uicJob.IncludeString);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second[0] == uicJob.SourceFile) {
+ // The output file already gets generated
+ return true;
+ }
+ {
+ // The output file already gets generated - from a different .ui
+ // file!
+ std::string error = "The two source files\n ";
+ error += Quoted(uicJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(it->second[1]);
+ error += "\ncontain the same uic include string ";
+ error += Quoted(uicJob.IncludeString);
+ error += "\nbut the uic file would be generated from different "
+ "source files\n ";
+ error += Quoted(uicJob.SourceFile);
+ error += " and\n ";
+ error += Quoted(it->second[0]);
+ error +=
+ ".\nConsider to\n"
+ "- add a directory prefix to a \"ui_<NAME>.h\" include "
+ "(e.g \"sub/ui_<NAME>.h\")\n"
+ "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
+ "include(s)\n";
+ Log().Error(GenT::UIC, error);
+ AbortError();
+ return false;
+ }
+ }
+
+ // We're still here so register this job
+ UicIncludes_.emplace_hint(range.first, uicJob.IncludeString,
+ std::array<std::string, 2>{
+ { uicJob.SourceFile, uicJob.IncluderFile } });
+ }
+ return WorkerPool_.PushJob(std::move(jobHandle));
+}
+
+bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile)
+{
+ std::lock_guard<std::mutex> guard(MocMetaMutex_);
+ return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
+}
+
+std::string cmQtAutoMocUic::ParallelMocAutoRegister(
+ std::string const& baseName)
+{
+ std::string res;
+ {
+ std::lock_guard<std::mutex> mocLock(MocMetaMutex_);
+ res = baseName;
+ res += ".cpp";
+ if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
+ MocAutoFiles_.emplace(res);
+ } else {
+ // Append number suffix to the file name
+ for (unsigned int ii = 2; ii != 1024; ++ii) {
+ res = baseName;
+ res += '_';
+ res += std::to_string(ii);
+ res += ".cpp";
+ if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
+ MocAutoFiles_.emplace(res);
+ break;
+ }
+ }
+ }
+ }
+ return res;
+}