From 7d50e1c6118b292ea3061ec9fee0230c5d50a5ff Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Thu, 2 May 2019 11:03:13 +0200 Subject: Autogen: Refactor AUTOMOC and AUTOUIC and add source file parse data caching New features ------------ CMake's `AUTOMOC` and `AUTOUIC` now cache information extracted when parsing source files in `CMakeFiles/_autogen.dir/ParseCache.txt`. This leads to faster `_autogen` target rebuilds, because source files will be parsed again only if they're newer than the `ParseCache.txt` file. The parse cache will be recomputed if it is older than the CMake executable. `AUTOMOC` and `AUTOUIC` now check if `moc` or `uic` output files are older than the `moc` or `uic` executable. If an output file is older than the compiler, it will be regenerated. Therefore if a new `moc` or `uic` version is installed, all output files will be regenerated. `AUTOMOC` and `AUTOUIC` error and warning messages are more detailed. Internal changes ---------------- `moc` and `uic` output file names are not computed in the `_autogen` target anymore but in `cmQtAutoGenInitializer`. This makes the available at the configuration stage for improved dependency computations (to be done). In `AutogenInfo.cmake`, equally sized lists for "source file names", "source file flags" and "compiler output file names" are passed to the `_autogen` target. This replaces the separate file lists for `AUTOMOC` and `AUTOUIC`. Files times are read from the file system only once by using `cmFileTime` instances instead of `cmQtAutoGenerator::FileSystem::FileIsOlderThan` calls. All calls to not thread safe file system functions are moved to non concurrent fence jobs (see `cmWorkerPool::JobT::IsFence()`). This renders the `cmQtAutoGenerator::FileSystem` wrapper class obsolete and it is removed. Instead of composing a single large settings string that is fed to the `cmCryptoHash`, now all setting sub strings are fed one by one to the `cmCryptoHash` and the finalized result is stored. The `std::mutex` in `cmQtAutoGenerator::Logger` is tagged `mutable` and most `cmQtAutoGenerator::Logger` methods become `const`. Outlook ------- This patch provides the framework required to - extract dependencies from `.ui` files in `AUTOUIC`. These will help to address issue #15420 "AUTOUIC: Track uic external inputs". - generate adaptive `make` and `ninja` files in the `_autogen` target. These will help to address issue #16776 "AUTOUIC: Ninja needs two passes to correctly build Qt project". - generate (possibly empty) `moc` and `uic` files for all headers instead of a `mocs_compilation.cpp` file. This will help to address issue #17277 "AUTOMOC: Provide a option to allow AUTOMOC to compile individual " "moc_x.cxx instead of including all in mocs_compilation.cxx" --- Source/cmQtAutoGen.cxx | 8 +- Source/cmQtAutoGenInitializer.cxx | 147 +- Source/cmQtAutoGenInitializer.h | 1 + Source/cmQtAutoGenerator.cxx | 201 +-- Source/cmQtAutoGenerator.h | 94 +- Source/cmQtAutoMocUic.cxx | 2733 +++++++++++++++++++++---------------- Source/cmQtAutoMocUic.h | 485 ++++--- Source/cmQtAutoRcc.cxx | 2 +- Source/cmQtAutoRcc.h | 2 +- 9 files changed, 2071 insertions(+), 1602 deletions(-) diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 9918c35..a245b54 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -194,11 +194,11 @@ std::string cmQtAutoGen::QuotedCommand(std::vector const& command) std::string cmQtAutoGen::SubDirPrefix(std::string const& filename) { - std::string res(cmSystemTools::GetFilenamePath(filename)); - if (!res.empty()) { - res += '/'; + std::string::size_type slash_pos = filename.rfind('/'); + if (slash_pos == std::string::npos) { + return std::string(); } - return res; + return filename.substr(0, slash_pos + 1); } std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename, diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index be96f1a..38f39fb 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -77,7 +78,8 @@ static std::string FileProjectRelativePath(cmMakefile* makefile, return res; } -/* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its +/** + * Tests if targetDepend is a STATIC_LIBRARY and if any of its * recursive STATIC_LIBRARY dependencies depends on targetOrigin * (STATIC_LIBRARY cycle). */ @@ -384,6 +386,10 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } else { AddCleanFile(makefile, this->AutogenTarget.SettingsFile); } + + this->AutogenTarget.ParseCacheFile = this->Dir.Info; + this->AutogenTarget.ParseCacheFile += "/ParseCache.txt"; + AddCleanFile(makefile, this->AutogenTarget.ParseCacheFile); } // Autogen target: Compute user defined dependencies @@ -1255,53 +1261,107 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() ofs.Write("AM_INCLUDE_DIR", this->Dir.Include); ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude); - // Use sorted sets - std::set headers; - std::set sources; - std::set moc_headers; - std::set moc_sources; + std::vector headers; + std::vector headersFlags; + std::vector headersBuildPaths; + std::vector sources; + std::vector sourcesFlags; std::set moc_skip; - std::set uic_headers; - std::set uic_sources; std::set uic_skip; + // Filter headers - for (auto const& pair : this->AutogenTarget.Headers) { - MUFile const& muf = *pair.second; - if (muf.Generated && !this->CMP0071Accept) { - continue; - } - if (muf.SkipMoc) { - moc_skip.insert(muf.RealPath); + { + auto headerCount = this->AutogenTarget.Headers.size(); + headers.reserve(headerCount); + headersFlags.reserve(headerCount); + + std::vector sortedHeaders; + { + sortedHeaders.reserve(headerCount); + for (auto const& pair : this->AutogenTarget.Headers) { + sortedHeaders.emplace_back(pair.second.get()); + } + std::sort(sortedHeaders.begin(), sortedHeaders.end(), + [](MUFile const* a, MUFile const* b) { + return (a->RealPath < b->RealPath); + }); } - if (muf.SkipUic) { - uic_skip.insert(muf.RealPath); + + for (MUFile const* const muf : sortedHeaders) { + if (muf->Generated && !this->CMP0071Accept) { + continue; + } + if (muf->SkipMoc) { + moc_skip.insert(muf->RealPath); + } + if (muf->SkipUic) { + uic_skip.insert(muf->RealPath); + } + if (muf->MocIt || muf->UicIt) { + headers.emplace_back(muf->RealPath); + std::string flags; + flags += muf->MocIt ? 'M' : 'm'; + flags += muf->UicIt ? 'U' : 'u'; + headersFlags.emplace_back(std::move(flags)); + } } - if (muf.MocIt && muf.UicIt) { - headers.insert(muf.RealPath); - } else if (muf.MocIt) { - moc_headers.insert(muf.RealPath); - } else if (muf.UicIt) { - uic_headers.insert(muf.RealPath); + } + // Header build paths + { + cmFilePathChecksum const fpathCheckSum(makefile); + std::unordered_set emitted; + for (std::string const& hdr : headers) { + std::string basePath = fpathCheckSum.getPart(hdr); + basePath += "/moc_"; + basePath += cmSystemTools::GetFilenameWithoutLastExtension(hdr); + for (unsigned int ii = 1; ii != 1024; ++ii) { + std::string path = basePath; + if (ii > 1) { + path += '_'; + path += std::to_string(ii); + } + path += ".cpp"; + if (emitted.emplace(path).second) { + headersBuildPaths.emplace_back(std::move(path)); + break; + } + } } } + // Filter sources - for (auto const& pair : this->AutogenTarget.Sources) { - MUFile const& muf = *pair.second; - if (muf.Generated && !this->CMP0071Accept) { - continue; - } - if (muf.SkipMoc) { - moc_skip.insert(muf.RealPath); - } - if (muf.SkipUic) { - uic_skip.insert(muf.RealPath); + { + auto sourcesCount = this->AutogenTarget.Sources.size(); + sources.reserve(sourcesCount); + sourcesFlags.reserve(sourcesCount); + + std::vector sorted; + sorted.reserve(sourcesCount); + for (auto const& pair : this->AutogenTarget.Sources) { + sorted.emplace_back(pair.second.get()); } - if (muf.MocIt && muf.UicIt) { - sources.insert(muf.RealPath); - } else if (muf.MocIt) { - moc_sources.insert(muf.RealPath); - } else if (muf.UicIt) { - uic_sources.insert(muf.RealPath); + std::sort(sorted.begin(), sorted.end(), + [](MUFile const* a, MUFile const* b) { + return (a->RealPath < b->RealPath); + }); + + for (MUFile const* const muf : sorted) { + if (muf->Generated && !this->CMP0071Accept) { + continue; + } + if (muf->SkipMoc) { + moc_skip.insert(muf->RealPath); + } + if (muf->SkipUic) { + uic_skip.insert(muf->RealPath); + } + if (muf->MocIt || muf->UicIt) { + sources.emplace_back(muf->RealPath); + std::string flags; + flags += muf->MocIt ? 'M' : 'm'; + flags += muf->UicIt ? 'U' : 'u'; + sourcesFlags.emplace_back(std::move(flags)); + } } } @@ -1311,17 +1371,20 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable); ofs.Write("# Files\n"); + ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); ofs.WriteConfig("AM_SETTINGS_FILE", this->AutogenTarget.ConfigSettingsFile); + ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); ofs.WriteStrings("AM_HEADERS", headers); + ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags); + ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths); ofs.WriteStrings("AM_SOURCES", sources); + ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags); // Write moc settings if (this->Moc.Enabled) { ofs.Write("# MOC settings\n"); - ofs.WriteStrings("AM_MOC_HEADERS", moc_headers); - ofs.WriteStrings("AM_MOC_SOURCES", moc_sources); ofs.WriteStrings("AM_MOC_SKIP", moc_skip); ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines); ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines); @@ -1343,8 +1406,6 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); ofs.Write("# UIC settings\n"); - ofs.WriteStrings("AM_UIC_HEADERS", uic_headers); - ofs.WriteStrings("AM_UIC_SOURCES", uic_sources); ofs.WriteStrings("AM_UIC_SKIP", uic_skip); ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options); ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions); diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 7ff33c3..153f56d 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -183,6 +183,7 @@ private: // Configuration files std::string InfoFile; std::string SettingsFile; + std::string ParseCacheFile; std::map ConfigSettingsFile; // Dependencies bool DependOrigin = false; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index 6fbea82..f7e377d 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -66,7 +66,8 @@ std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title) return head; } -void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message) +void cmQtAutoGenerator::Logger::Info(GenT genType, + std::string const& message) const { std::string msg = GeneratorName(genType); msg += ": "; @@ -81,7 +82,7 @@ void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message) } void cmQtAutoGenerator::Logger::Warning(GenT genType, - std::string const& message) + std::string const& message) const { std::string msg; if (message.find('\n') == std::string::npos) { @@ -106,7 +107,7 @@ void cmQtAutoGenerator::Logger::Warning(GenT genType, void cmQtAutoGenerator::Logger::WarningFile(GenT genType, std::string const& filename, - std::string const& message) + std::string const& message) const { std::string msg = " "; msg += Quoted(filename); @@ -116,7 +117,8 @@ void cmQtAutoGenerator::Logger::WarningFile(GenT genType, Warning(genType, msg); } -void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message) +void cmQtAutoGenerator::Logger::Error(GenT genType, + std::string const& message) const { std::string msg; msg += HeadLine(GeneratorName(genType) + " error"); @@ -134,7 +136,7 @@ void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message) void cmQtAutoGenerator::Logger::ErrorFile(GenT genType, std::string const& filename, - std::string const& message) + std::string const& message) const { std::string emsg = " "; emsg += Quoted(filename); @@ -146,7 +148,7 @@ void cmQtAutoGenerator::Logger::ErrorFile(GenT genType, void cmQtAutoGenerator::Logger::ErrorCommand( GenT genType, std::string const& message, - std::vector const& command, std::string const& output) + std::vector const& command, std::string const& output) const { std::string msg; msg.push_back('\n'); @@ -191,7 +193,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, content.clear(); if (!cmSystemTools::FileExists(filename, true)) { if (error != nullptr) { - error->append("Not a file."); + *error = "Not a file."; } return false; } @@ -203,7 +205,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, return [&ifs, length, &content, error]() -> bool { if (!ifs) { if (error != nullptr) { - error->append("Opening the file for reading failed."); + *error = "Opening the file for reading failed."; } return false; } @@ -213,7 +215,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, if (!ifs) { content.clear(); if (error != nullptr) { - error->append("Reading from the file failed."); + *error = "Reading from the file failed."; } return false; } @@ -228,7 +230,7 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, // Make sure the parent directory exists if (!cmQtAutoGenerator::MakeParentDirectory(filename)) { if (error != nullptr) { - error->assign("Could not create parent directory."); + *error = "Could not create parent directory."; } return false; } @@ -240,14 +242,14 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, return [&ofs, &content, error]() -> bool { if (!ofs) { if (error != nullptr) { - error->assign("Opening file for writing failed."); + *error = "Opening file for writing failed."; } return false; } ofs << content; if (!ofs.good()) { if (error != nullptr) { - error->assign("File writing failed."); + *error = "File writing failed."; } return false; } @@ -255,176 +257,17 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, }(); } -cmQtAutoGenerator::FileSystem::FileSystem() = default; - -cmQtAutoGenerator::FileSystem::~FileSystem() = default; - -std::string cmQtAutoGenerator::FileSystem::GetRealPath( - std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::GetRealPath(filename); -} - -std::string cmQtAutoGenerator::FileSystem::CollapseFullPath( - std::string const& file, std::string const& dir) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::CollapseFullPath(file, dir); -} - -void cmQtAutoGenerator::FileSystem::SplitPath( - const std::string& p, std::vector& components, - bool expand_home_dir) -{ - std::lock_guard lock(Mutex_); - cmSystemTools::SplitPath(p, components, expand_home_dir); -} - -std::string cmQtAutoGenerator::FileSystem::JoinPath( - const std::vector& components) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::JoinPath(components); -} - -std::string cmQtAutoGenerator::FileSystem::JoinPath( - std::vector::const_iterator first, - std::vector::const_iterator last) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::JoinPath(first, last); -} - -std::string cmQtAutoGenerator::FileSystem::GetFilenameWithoutLastExtension( - const std::string& filename) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::GetFilenameWithoutLastExtension(filename); -} - -std::string cmQtAutoGenerator::FileSystem::SubDirPrefix( - std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return cmQtAutoGen::SubDirPrefix(filename); -} - -void cmQtAutoGenerator::FileSystem::setupFilePathChecksum( - std::string const& currentSrcDir, std::string const& currentBinDir, - std::string const& projectSrcDir, std::string const& projectBinDir) -{ - std::lock_guard lock(Mutex_); - FilePathChecksum_.setupParentDirs(currentSrcDir, currentBinDir, - projectSrcDir, projectBinDir); -} - -std::string cmQtAutoGenerator::FileSystem::GetFilePathChecksum( - std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return FilePathChecksum_.getPart(filename); -} - -bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::FileExists(filename); -} - -bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename, - bool isFile) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::FileExists(filename, isFile); -} - -unsigned long cmQtAutoGenerator::FileSystem::FileLength( - std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::FileLength(filename); -} - -bool cmQtAutoGenerator::FileSystem::FileIsOlderThan( - std::string const& buildFile, std::string const& sourceFile, - std::string* error) -{ - bool res(false); - int result = 0; - { - std::lock_guard lock(Mutex_); - res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result); - } - if (res) { - res = (result < 0); - } else { - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(Quoted(buildFile)); - error->append("\nand\n "); - error->append(Quoted(sourceFile)); - } - } - return res; -} - -bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content, - std::string const& filename, - std::string* error) -{ - std::lock_guard lock(Mutex_); - return cmQtAutoGenerator::FileRead(content, filename, error); -} - -bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename, - std::string const& content, - std::string* error) -{ - std::lock_guard lock(Mutex_); - return cmQtAutoGenerator::FileWrite(filename, content, error); -} - -bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename, - std::string const& content) +bool cmQtAutoGenerator::FileDiffers(std::string const& filename, + std::string const& content) { bool differs = true; - { - std::string oldContents; - if (FileRead(oldContents, filename)) { - differs = (oldContents != content); - } + std::string oldContents; + if (FileRead(oldContents, filename) && (oldContents == content)) { + differs = false; } return differs; } -bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::RemoveFile(filename); -} - -bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename, - bool create) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::Touch(filename, create); -} - -bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname) -{ - std::lock_guard lock(Mutex_); - return cmSystemTools::MakeDirectory(dirname); -} - -bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( - std::string const& filename) -{ - std::lock_guard lock(Mutex_); - return cmQtAutoGenerator::MakeParentDirectory(filename); -} - cmQtAutoGenerator::cmQtAutoGenerator() = default; cmQtAutoGenerator::~cmQtAutoGenerator() = default; @@ -435,6 +278,12 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile, // Info settings InfoFile_ = infoFile; cmSystemTools::ConvertToUnixSlashes(InfoFile_); + if (!InfoFileTime_.Load(InfoFile_)) { + std::string msg = "Autogen: The info file "; + msg += Quoted(InfoFile_); + msg += " is not readable\n"; + cmSystemTools::Stderr(msg); + } InfoDir_ = cmSystemTools::GetFilenamePath(infoFile); InfoConfig_ = config; diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 437fa20..ff4c4c9 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -5,7 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmFilePathChecksum.h" +#include "cmFileTime.h" #include "cmQtAutoGen.h" #include @@ -14,13 +14,17 @@ class cmMakefile; -/// @brief Base class for QtAutoGen gernerators +/** \class cmQtAutoGenerator + * \brief Base class for QtAutoGen generators + */ class cmQtAutoGenerator : public cmQtAutoGen { public: // -- Types - /// @brief Thread safe logging + /** + * Thread safe logger + */ class Logger { public: @@ -37,24 +41,24 @@ public: bool ColorOutput() const { return this->ColorOutput_; } void SetColorOutput(bool value); // -- Log info - void Info(GenT genType, std::string const& message); + void Info(GenT genType, std::string const& message) const; // -- Log warning - void Warning(GenT genType, std::string const& message); + void Warning(GenT genType, std::string const& message) const; void WarningFile(GenT genType, std::string const& filename, - std::string const& message); + std::string const& message) const; // -- Log error - void Error(GenT genType, std::string const& message); + void Error(GenT genType, std::string const& message) const; void ErrorFile(GenT genType, std::string const& filename, - std::string const& message); + std::string const& message) const; void ErrorCommand(GenT genType, std::string const& message, std::vector const& command, - std::string const& output); + std::string const& output) const; private: static std::string HeadLine(std::string const& title); private: - std::mutex Mutex_; + mutable std::mutex Mutex_; unsigned int Verbosity_ = 0; bool ColorOutput_ = false; }; @@ -66,70 +70,8 @@ public: static bool FileWrite(std::string const& filename, std::string const& content, std::string* error = nullptr); - - /// @brief Thread safe file system interface - class FileSystem - { - public: - FileSystem(); - ~FileSystem(); - - // -- Paths - /// @brief Wrapper for cmSystemTools::GetRealPath - std::string GetRealPath(std::string const& filename); - /// @brief Wrapper for cmSystemTools::CollapseFullPath - std::string CollapseFullPath(std::string const& file, - std::string const& dir); - /// @brief Wrapper for cmSystemTools::SplitPath - void SplitPath(const std::string& p, std::vector& components, - bool expand_home_dir = true); - /// @brief Wrapper for cmSystemTools::JoinPath - std::string JoinPath(const std::vector& components); - /// @brief Wrapper for cmSystemTools::JoinPath - std::string JoinPath(std::vector::const_iterator first, - std::vector::const_iterator last); - /// @brief Wrapper for cmSystemTools::GetFilenameWithoutLastExtension - std::string GetFilenameWithoutLastExtension(const std::string& filename); - /// @brief Wrapper for cmQtAutoGen::SubDirPrefix - std::string SubDirPrefix(std::string const& filename); - /// @brief Wrapper for cmFilePathChecksum::setupParentDirs - void setupFilePathChecksum(std::string const& currentSrcDir, - std::string const& currentBinDir, - std::string const& projectSrcDir, - std::string const& projectBinDir); - /// @brief Wrapper for cmFilePathChecksum::getPart - std::string GetFilePathChecksum(std::string const& filename); - - // -- File access - /// @brief Wrapper for cmSystemTools::FileExists - bool FileExists(std::string const& filename); - /// @brief Wrapper for cmSystemTools::FileExists - bool FileExists(std::string const& filename, bool isFile); - /// @brief Wrapper for cmSystemTools::FileLength - unsigned long FileLength(std::string const& filename); - bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr); - - bool FileRead(std::string& content, std::string const& filename, - std::string* error = nullptr); - - bool FileWrite(std::string const& filename, std::string const& content, - std::string* error = nullptr); - - bool FileDiffers(std::string const& filename, std::string const& content); - - bool FileRemove(std::string const& filename); - bool Touch(std::string const& filename, bool create = false); - - // -- Directory access - bool MakeDirectory(std::string const& dirname); - bool MakeParentDirectory(std::string const& filename); - - private: - std::mutex Mutex_; - cmFilePathChecksum FilePathChecksum_; - }; + static bool FileDiffers(std::string const& filename, + std::string const& content); public: // -- Constructors @@ -142,8 +84,9 @@ public: // -- Run bool Run(std::string const& infoFile, std::string const& config); - // InfoFile + // -- InfoFile std::string const& InfoFile() const { return InfoFile_; } + cmFileTime const& InfoFileTime() const { return InfoFileTime_; } std::string const& InfoDir() const { return InfoDir_; } std::string const& InfoConfig() const { return InfoConfig_; } @@ -158,6 +101,7 @@ protected: private: // -- Info settings std::string InfoFile_; + cmFileTime InfoFileTime_; std::string InfoDir_; std::string InfoConfig_; }; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 005c27d..36794d6 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -13,67 +12,187 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" +#include "cmGeneratedFileStream.h" #include "cmMakefile.h" #include "cmQtAutoGen.h" #include "cmSystemTools.h" #include "cmake.h" +#include "cmsys/FStream.hxx" #if defined(__APPLE__) # include #endif -// -- Class methods +static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" +static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" -std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath( - std::string const& relativePath) const +cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key, + std::size_t basePrefixLength) + : Key(key) + , Dir(SubDirPrefix(key)) + , Base(cmSystemTools::GetFilenameWithoutLastExtension(key)) { - return FileSys->CollapseFullPath(relativePath, AutogenBuildDir); + if (basePrefixLength != 0) { + Base = Base.substr(basePrefixLength); + } } -/** - * @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 +void cmQtAutoMocUic::ParseCacheT::FileT::Clear() { - for (std::string const& ext : HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (FileSys->FileExists(testFilePath)) { - header = testFilePath; - return true; + Moc.Macro.clear(); + Moc.Include.Underscore.clear(); + Moc.Include.Dot.clear(); + Moc.Depends.clear(); + + Uic.Include.clear(); + Uic.Depends.clear(); +} + +cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get( + std::string const& fileName) const +{ + auto it = Map_.find(fileName); + if (it != Map_.end()) { + return it->second; + } + return FileHandleT(); +} + +cmQtAutoMocUic::ParseCacheT::GetOrInsertT +cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName) +{ + // Find existing entry + { + auto it = Map_.find(fileName); + if (it != Map_.end()) { + return GetOrInsertT{ it->second, false }; } } - return false; + + // Insert new entry + return GetOrInsertT{ + Map_.emplace(fileName, std::make_shared()).first->second, true + }; } -bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const +cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default; +cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default; + +void cmQtAutoMocUic::ParseCacheT::Clear() { - return (!Enabled || (SkipList.find(fileName) != SkipList.end())); + Map_.clear(); } -/** - * @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 +bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName) { - 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; + cmsys::ifstream fin(fileName.c_str()); + if (!fin) { + return false; + } + FileHandleT fileHandle; + + std::string line; + while (std::getline(fin, line)) { + // Check if this an empty or a comment line + if (line.empty() || line.front() == '#') { + continue; + } + // Drop carriage return character at the end + if (line.back() == '\r') { + line.pop_back(); + if (line.empty()) { + continue; } } + // Check if this a file name line + if (line.front() != ' ') { + fileHandle = GetOrInsert(line).first; + continue; + } + + // Bad line or bad file handle + if (!fileHandle || (line.size() < 6)) { + continue; + } + + constexpr std::size_t offset = 5; + if (cmHasLiteralPrefix(line, " mmc:")) { + fileHandle->Moc.Macro = line.substr(offset); + continue; + } + if (cmHasLiteralPrefix(line, " miu:")) { + fileHandle->Moc.Include.Underscore.emplace_back(line.substr(offset), + MocUnderscoreLength); + continue; + } + if (cmHasLiteralPrefix(line, " mid:")) { + fileHandle->Moc.Include.Dot.emplace_back(line.substr(offset), 0); + continue; + } + if (cmHasLiteralPrefix(line, " mdp:")) { + fileHandle->Moc.Depends.emplace_back(line.substr(offset)); + continue; + } + if (cmHasLiteralPrefix(line, " uic:")) { + fileHandle->Uic.Include.emplace_back(line.substr(offset), + UiUnderscoreLength); + continue; + } + if (cmHasLiteralPrefix(line, " udp:")) { + fileHandle->Uic.Depends.emplace_back(line.substr(offset)); + continue; + } } - return std::string(); + return true; +} + +bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName) +{ + cmGeneratedFileStream ofs(fileName); + if (!ofs) { + return false; + } + ofs << "# Generated by CMake. Changes will be overwritten." << std::endl; + for (auto const& pair : Map_) { + ofs << pair.first << std::endl; + FileT const& file = *pair.second; + if (!file.Moc.Macro.empty()) { + ofs << " mmc:" << file.Moc.Macro << std::endl; + } + for (IncludeKeyT const& item : file.Moc.Include.Underscore) { + ofs << " miu:" << item.Key << std::endl; + } + for (IncludeKeyT const& item : file.Moc.Include.Dot) { + ofs << " mid:" << item.Key << std::endl; + } + for (std::string const& item : file.Moc.Depends) { + ofs << " mdp:" << item << std::endl; + } + for (IncludeKeyT const& item : file.Uic.Include) { + ofs << " uic:" << item.Key << std::endl; + } + for (std::string const& item : file.Uic.Depends) { + ofs << " udp:" << item << std::endl; + } + } + return ofs.Close(); +} + +cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default; +cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default; + +cmQtAutoMocUic::MocSettingsT::MocSettingsT() +{ + RegExpInclude.compile( + "(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); +} + +cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default; + +bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const +{ + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const @@ -98,53 +217,13 @@ std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const return res; } -std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile( - std::string const& sourcePath, std::string const& includeString) const +cmQtAutoMocUic::UicSettingsT::UicSettingsT() { - // 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(); + RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); } -void cmQtAutoMocUic::MocSettingsT::FindDependencies( - std::string const& content, std::set& 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(); - } - } - } - } -} +cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default; bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const { @@ -176,498 +255,844 @@ void cmQtAutoMocUic::JobT::LogCommandError( bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector const& command) + std::vector const& command, + std::string* infoMessage) { // Log command if (Log().Verbose()) { - std::string msg = "Running command:\n"; + std::string msg; + if ((infoMessage != nullptr) && !infoMessage->empty()) { + msg = *infoMessage; + if (msg.back() != '\n') { + msg += '\n'; + } + } msg += QuotedCommand(command); msg += '\n'; Log().Info(genType, msg); } return cmWorkerPool::JobT::RunProcess(result, command, - Gen()->Base().AutogenBuildDir); + BaseConst().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; + std::unique_ptr reason; + if (Log().Verbose()) { + reason = cm::make_unique(); + } + if (!Update(reason.get())) { + return; } - if (generate) { + std::string const& predefsFileRel = MocConst().PredefsFileRel; + std::string const& predefsFileAbs = MocConst().PredefsFileAbs; + { cmWorkerPool::ProcessResultT result; { // Compose command - std::vector cmd = Gen()->Moc().PredefsCmd; + std::vector cmd = MocConst().PredefsCmd; // Add includes - cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(), - Gen()->Moc().Includes.end()); + cmd.insert(cmd.end(), MocConst().Includes.begin(), + MocConst().Includes.end()); // Add definitions - for (std::string const& def : Gen()->Moc().Definitions) { + for (std::string const& def : MocConst().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); + if (!RunProcess(GenT::MOC, result, cmd, reason.get())) { + std::string msg = "The content generation command for "; + msg += Quoted(predefsFileRel); + msg += " failed.\n"; + msg += result.ErrorMessage; + LogCommandError(GenT::MOC, msg, cmd, result.StdOut); + return; } } // (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); + if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) { + if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) { + std::string msg = "Writing "; + msg += Quoted(predefsFileRel); + msg += " failed."; + LogFileError(GenT::MOC, predefsFileAbs, msg); + return; + } + } else { + // Touch to update the time stamp + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching " + Quoted(predefsFileRel)); + } + if (!cmSystemTools::Touch(predefsFileAbs, false)) { + std::string msg = "Touching "; + msg += Quoted(predefsFileAbs); + msg += " failed."; + LogFileError(GenT::MOC, predefsFileAbs, msg); + return; } } } + + // Read file time afterwards + if (!MocEval().PredefsTime.Load(predefsFileAbs)) { + LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed."); + return; + } } -void cmQtAutoMocUic::JobParseT::Process() +bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const { - if (AutoMoc && Header) { - // Don't parse header for moc if the file is included by a source already - if (Gen()->ParallelMocIncluded(FileName)) { - AutoMoc = false; + // Test if the file exists + if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(MocConst().PredefsFileRel); + *reason += ", because it doesn't exist."; + } + return true; + } + + // Test if the settings changed + if (MocConst().SettingsChanged) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(MocConst().PredefsFileRel); + *reason += ", because the moc settings changed."; } + return true; } - 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); + // Test if the executable is newer + { + std::string const& exec = MocConst().PredefsCmd.at(0); + cmFileTime execTime; + if (execTime.Load(exec)) { + if (MocEval().PredefsTime.Older(execTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(MocConst().PredefsFileRel); + *reason += " because it is older than "; + *reason += Quoted(exec); + *reason += "."; } - } else { - Log().WarningFile(GenT::GEN, FileName, "The source file is empty"); + return true; } - } else { - LogFileError(GenT::GEN, FileName, "Could not read the file: " + error); } } + + return false; } -bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ReadFile() { - struct JobPre + // Clear old parse information + FileHandle->ParseData->Clear(); + std::string const& fileName = FileHandle->FileName; + // Write info + if (Log().Verbose()) { + Log().Info(GenT::GEN, "Parsing " + Quoted(fileName)); + } + // Read file content { - bool self; // source file is self - bool underscore; // "moc_" style include - std::string SourceFile; - std::string IncludeString; - }; + std::string error; + if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) { + LogFileError(GenT::GEN, fileName, "Could not read the file: " + error); + return false; + } + } + // Warn if empty + if (Content.empty()) { + Log().WarningFile(GenT::GEN, fileName, "The file is empty."); + return false; + } + return true; +} + +void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector& container, + std::set const& source, + std::size_t basePrefixLength) +{ + if (source.empty()) { + return; + } + container.reserve(source.size()); + for (std::string const& src : source) { + container.emplace_back(src, basePrefixLength); + } +} + +void cmQtAutoMocUic::JobParseT::MocMacro() +{ + for (KeyExpT const& filter : MocConst().MacroFilters) { + // Run a simple find string check + if (Content.find(filter.Key) == std::string::npos) { + continue; + } + // Run the expensive regular expression check loop + cmsys::RegularExpressionMatch match; + if (filter.Exp.find(Content.c_str(), match)) { + // Keep detected macro name + FileHandle->ParseData->Moc.Macro = filter.Key; + return; + } + } +} + +void cmQtAutoMocUic::JobParseT::MocDependecies() +{ + if (MocConst().DependFilters.empty()) { + return; + } + + // Find dependency strings + std::set parseDepends; + for (KeyExpT const& filter : MocConst().DependFilters) { + // Run a simple find string check + if (Content.find(filter.Key) == std::string::npos) { + continue; + } + // 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()) { + parseDepends.emplace(std::move(dep)); + } + } + contentChars += match.end(); + } + } - struct MocInclude + // Store dependency strings { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; + auto& Depends = FileHandle->ParseData->Moc.Depends; + Depends.reserve(parseDepends.size()); + for (std::string const& item : parseDepends) { + Depends.emplace_back(item); + // Replace end of line characters in filenames + std::string& path = Depends.back(); + std::replace(path.begin(), path.end(), '\n', ' '); + std::replace(path.begin(), path.end(), '\r', ' '); + } + } +} - // Check if this source file contains a relevant macro - std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content); +void cmQtAutoMocUic::JobParseT::MocIncludes() +{ + if (Content.find("moc") == std::string::npos) { + return; + } - // Extract moc includes from file - std::deque mocIncsUsc; - std::deque mocIncsDot; + std::set underscore; + std::set dot; { - 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_.cxx - // Remove the moc_ part from the base name - mocIncsUsc.emplace_back(MocInclude{ - std::move(incString), std::move(incDir), incBase.substr(4) }); - } else { - // .moc - mocIncsDot.emplace_back(MocInclude{ - std::move(incString), std::move(incDir), std::move(incBase) }); - } - // Forward content pointer - contentChars += match.end(); + const char* contentChars = Content.c_str(); + cmsys::RegularExpression const& regExp = MocConst().RegExpInclude; + cmsys::RegularExpressionMatch match; + while (regExp.find(contentChars, match)) { + std::string incString = match.match(2); + std::string const incBase = + cmSystemTools::GetFilenameWithoutLastExtension(incString); + if (cmHasLiteralPrefix(incBase, "moc_")) { + // moc_.cpp + // Remove the moc_ part from the base name + underscore.emplace(std::move(incString)); + } else { + // .moc + dot.emplace(std::move(incString)); } + // Forward content pointer + contentChars += match.end(); } } + auto& Include = FileHandle->ParseData->Moc.Include; + CreateKeys(Include.Underscore, underscore, MocUnderscoreLength); + CreateKeys(Include.Dot, dot, 0); +} - // Check if there is anything to do - if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) { - return true; +void cmQtAutoMocUic::JobParseT::UicIncludes() +{ + if (Content.find("ui_") == std::string::npos) { + return; } - bool ownDotMocIncluded = false; - bool ownMocUscIncluded = false; - std::deque jobs; + std::set includes; + { + const char* contentChars = Content.c_str(); + cmsys::RegularExpression const& regExp = UicConst().RegExpInclude; + cmsys::RegularExpressionMatch match; + while (regExp.find(contentChars, match)) { + includes.emplace(match.match(2)); + // Forward content pointer + contentChars += match.end(); + } + } + CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength); +} - // Process moc_.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; +void cmQtAutoMocUic::JobParseHeaderT::Process() +{ + if (!ReadFile()) { + return; + } + // Moc parsing + if (FileHandle->Moc) { + MocMacro(); + MocDependecies(); + } + // Uic parsing + if (FileHandle->Uic) { + UicIncludes(); + } +} + +void cmQtAutoMocUic::JobParseSourceT::Process() +{ + if (!ReadFile()) { + return; + } + // Moc parsing + if (FileHandle->Moc) { + MocMacro(); + MocDependecies(); + MocIncludes(); + } + // Uic parsing + if (FileHandle->Uic) { + UicIncludes(); + } +} + +void cmQtAutoMocUic::JobEvaluateT::Process() +{ + // Evaluate for moc + if (MocConst().Enabled) { + // Evaluate headers + for (auto const& pair : BaseEval().Headers) { + if (!MocEvalHeader(pair.second)) { + return; } - // 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; + } + // Evaluate sources + for (auto const& pair : BaseEval().Sources) { + if (!MocEvalSource(pair.second)) { + return; } + } + } + // Evaluate for uic + if (UicConst().Enabled) { + if (!UicEval(BaseEval().Headers) || !UicEval(BaseEval().Sources)) { + return; + } + } + + // Add discovered header parse jobs + Gen()->CreateParseJobs(MocEval().HeadersDiscovered); + // Add generate job after + Gen()->WorkerPool().EmplaceJob(); +} + +bool cmQtAutoMocUic::JobEvaluateT::MocEvalHeader(SourceFileHandleT source) +{ + SourceFileT const& sourceFile = *source; + auto const& parseData = sourceFile.ParseData->Moc; + if (!source->Moc) { + return true; + } + + if (!parseData.Macro.empty()) { + // Create a new mapping + MappingHandleT handle = std::make_shared(); + handle->SourceFile = std::move(source); + + // Absolute build path + if (BaseConst().MultiConfig) { + handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath); } else { + handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath); + } + + // Register mapping in headers map + MocRegisterMapping(handle, true); + } + + return true; +} + +bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource( + SourceFileHandleT const& source) +{ + SourceFileT const& sourceFile = *source; + auto const& parseData = sourceFile.ParseData->Moc; + if (!sourceFile.Moc || + (parseData.Macro.empty() && parseData.Include.Underscore.empty() && + parseData.Include.Dot.empty())) { + return true; + } + + std::string const sourceDir = SubDirPrefix(sourceFile.FileName); + std::string const sourceBase = + cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName); + + // For relaxed mode check if the own "moc_" or ".moc" file is included + bool const relaxedMode = MocConst().RelaxedMode; + bool sourceIncludesMocUnderscore = false; + bool sourceIncludesDotMoc = false; + // Check if the sources own "moc_" or ".moc" file is included + if (relaxedMode) { + for (IncludeKeyT const& incKey : parseData.Include.Underscore) { + if (incKey.Base == sourceBase) { + sourceIncludesMocUnderscore = true; + break; + } + } + } + for (IncludeKeyT const& incKey : parseData.Include.Dot) { + if (incKey.Base == sourceBase) { + sourceIncludesDotMoc = true; + break; + } + } + + // Check if this source needs to be moc processed but doesn't. + if (!sourceIncludesDotMoc && !parseData.Macro.empty() && + !(relaxedMode && sourceIncludesMocUnderscore)) { + { + std::string emsg = "The file contains a "; + emsg += Quoted(parseData.Macro); + emsg += " macro, but does not include "; + emsg += Quoted(sourceBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += sourceBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + LogFileError(GenT::MOC, sourceFile.FileName, emsg); + } + return false; + } + + // Evaluate "moc_" includes + for (IncludeKeyT const& incKey : parseData.Include.Underscore) { + std::string const headerBase = incKey.Dir + incKey.Base; + SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase); + if (!header) { { - 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); + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ",\nbut the header could not be found " + "in the following locations\n"; + msg += MocMessageTestHeaders(headerBase); + LogFileError(GenT::MOC, sourceFile.FileName, msg); } return false; } + // The include might be handled differently in relaxed mode + if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() && + (incKey.Base == sourceBase)) { + // The .cpp file includes a Qt macro but does not include the + // .moc file. In this case, the moc_.cpp should probably + // be generated from .cpp instead of .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. + { + // Issue a warning + std::string msg = "The file contains a "; + msg += Quoted(parseData.Macro); + msg += " macro, but does not include "; + msg += Quoted(sourceBase + ".moc"); + msg += ".\nInstead it includes "; + msg += Quoted(incKey.Key); + msg += ".\nRunning moc on the source\n "; + msg += Quoted(sourceFile.FileName); + msg += "!\nBetter include "; + msg += Quoted(sourceBase + ".moc"); + msg += " for compatibility with strict mode.\n"; + msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, source, false)) { + return false; + } + continue; + } + + // Check if header is skipped + if (MocConst().skipped(header->FileName)) { + continue; + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) { + return false; + } } - // Process .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); - } + // Evaluate ".moc" includes + if (relaxedMode) { + // Relaxed mode + for (IncludeKeyT const& incKey : parseData.Include.Dot) { + // Check if this is the sources own .moc file + bool const ownMoc = (incKey.Base == sourceBase); + if (ownMoc && !parseData.Macro.empty()) { + // Create mapping for the regular use case + if (!MocRegisterIncluded(incKey.Key, source, source, false)) { return false; } + continue; } - } 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); - } + // Try to find a header instead but issue a warning. + // This is for KDE4 compatibility. + std::string const headerBase = incKey.Dir + incKey.Base; + SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase); + if (!header) { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ",\nwhich seems to be the moc file from a different source " + "file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a matching header" + "could not be found in the following locations\n"; + msg += MocMessageTestHeaders(headerBase); + LogFileError(GenT::MOC, sourceFile.FileName, msg); + return false; + } + // Check if header is skipped + if (MocConst().skipped(header->FileName)) { + continue; + } + // Issue a warning + if (ownMoc && parseData.Macro.empty()) { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ", but does not contain a\n"; + msg += MocConst().MacrosString(); + msg += " macro.\nRunning moc on the header\n "; + msg += Quoted(header->FileName); + msg += "!\nBetter include "; + msg += Quoted("moc_" + incKey.Base + ".cpp"); + msg += " for a compatibility with strict mode.\n"; + msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); } else { - // Don't allow .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); - } + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += " instead of "; + msg += Quoted("moc_" + incKey.Base + ".cpp"); + msg += ".\nRunning moc on the header\n "; + msg += Quoted(header->FileName); + msg += "!\nBetter include "; + msg += Quoted("moc_" + incKey.Base + ".cpp"); + msg += " for compatibility with strict mode.\n"; + msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) { 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; - } - } + } else { + // Strict mode + for (IncludeKeyT const& incKey : parseData.Include.Dot) { + // Check if this is the sources own .moc file + bool const ownMoc = (incKey.Base == sourceBase); + if (!ownMoc) { + // Don't allow .moc include other than own in strict mode + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ",\nwhich seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + msg += Quoted(sourceBase + ".moc"); + msg += " to run moc on this source file."; + LogFileError(GenT::MOC, sourceFile.FileName, msg); + return false; } - // 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); + // Accept but issue a warning if moc isn't required + if (parseData.Macro.empty()) { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ", but does not contain a "; + msg += MocConst().MacrosString(); + msg += " macro."; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, source, false)) { + return false; } - return false; } } - // Convert pre jobs to actual jobs - for (JobPre& jobPre : jobs) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique( - std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString)); - if (jobPre.self) { - // Read dependencies from this source - JobMocT& jobMoc = static_cast(*jobHandle); - Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); - jobMoc.DependsValid = true; + return true; +} + +cmQtAutoMocUic::SourceFileHandleT +cmQtAutoMocUic::JobEvaluateT::MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) const +{ + // Search in vicinity of the source + { + SourceFileHandleT res = MocFindHeader(includerDir + includeBase); + if (res) { + return res; } - if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) { - return false; + } + // Search in include directories + for (std::string const& path : MocConst().IncludePaths) { + std::string testPath = path; + testPath += '/'; + testPath += includeBase; + SourceFileHandleT res = MocFindHeader(testPath); + if (res) { + return res; } } - return true; + // Return without success + return SourceFileHandleT(); } -bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta) +cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader( + std::string const& basePath) const { - bool success = true; - std::string const macroName = Gen()->Moc().FindMacro(meta.Content); - if (!macroName.empty()) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique( - std::string(FileName), std::string(), std::string()); - // Read dependencies from this source - { - JobMocT& jobMoc = static_cast(*jobHandle); - Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); - jobMoc.DependsValid = true; + std::string testPath; + testPath.reserve(basePath.size() + 8); + for (std::string const& ext : BaseConst().HeaderExtensions) { + testPath.clear(); + testPath += basePath; + testPath += '.'; + testPath += ext; + cmFileTime fileTime; + if (fileTime.Load(testPath)) { + // Compute real path of the file + testPath = cmSystemTools::GetRealPath(testPath); + // Return a known file if it exists already + { + auto it = BaseEval().Headers.find(testPath); + if (it != BaseEval().Headers.end()) { + return it->second; + } + } + // Created and return discovered file entry + SourceFileHandleT& res = MocEval().HeadersDiscovered[testPath]; + if (!res) { + res = std::make_shared(testPath); + res->FileTime = fileTime; + res->Moc = true; + } + return res; } - success = Gen()->ParallelJobPushMoc(std::move(jobHandle)); } - return success; + // Return without success + return SourceFileHandleT(); } -std::string cmQtAutoMocUic::JobParseT::MocStringHeaders( +std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders( std::string const& fileBase) const { - std::string res = fileBase; - res += ".{"; - res += cmJoin(Gen()->Base().HeaderExtensions, ","); - res += "}"; - return res; + std::ostringstream res; + { + std::string exts = ".{"; + exts += cmJoin(BaseConst().HeaderExtensions, ","); + exts += '}'; + // Compose result string + res << " " << fileBase << exts << '\n'; + for (std::string const& path : MocConst().IncludePaths) { + res << " " << path << '/' << fileBase << exts << '\n'; + } + } + return res.str(); } -std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader( - std::string const& includerDir, std::string const& includeBase) +bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded( + std::string const& includeString, SourceFileHandleT includerFileHandle, + SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const { - 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; + // Check if this file is already included + MappingHandleT& handle = MocEval().Includes[includeString]; + if (handle) { + // Check if the output file would be generated from different source files + if (handle->SourceFile != sourceFileHandle) { + std::string msg = "The source files\n "; + msg += Quoted(includerFileHandle->FileName); + msg += '\n'; + for (auto const& item : handle->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; } + msg += "contain the same include string "; + msg += Quoted(includeString); + msg += ", but\nthe moc file would be generated from different " + "source files\n "; + msg += Quoted(sourceFileHandle->FileName); + msg += " and\n "; + msg += Quoted(handle->SourceFile->FileName); + msg += ".\nConsider to\n" + " - not include the \"moc_.cpp\" file\n" + " - add a directory prefix to a \".moc\" include " + "(e.g \"sub/.moc\")\n" + " - rename the source file(s)\n"; + LogError(GenT::MOC, msg); + return false; } + + // The same mapping already exists. Just add to the includers list. + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + return true; } - // Sanitize - if (!header.empty()) { - header = FileSys().GetRealPath(header); + + // Create a new mapping + handle = std::make_shared(); + handle->IncludeString = includeString; + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + handle->SourceFile = std::move(sourceFileHandle); + handle->OutputFile += Gen()->AbsoluteIncludePath(includeString); + + // Register mapping in sources/headers map + MocRegisterMapping(handle, sourceIsHeader); + return true; +} + +void cmQtAutoMocUic::JobEvaluateT::MocRegisterMapping( + MappingHandleT mappingHandle, bool sourceIsHeader) const +{ + auto& regMap = + sourceIsHeader ? MocEval().HeaderMappings : MocEval().SourceMappings; + // Check if source file already gets mapped + auto& regHandle = regMap[mappingHandle->SourceFile->FileName]; + if (!regHandle) { + // Yet unknown mapping + regHandle = std::move(mappingHandle); + } else { + // Mappings with include string override those without + if (!mappingHandle->IncludeString.empty()) { + regHandle = std::move(mappingHandle); + } } - return header; } -bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta) +bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap) { - 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(); + for (auto const& pair : fileMap) { + if (!UicEvalFile(pair.second)) { + return false; } } - return success; + return true; } -bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta, - std::string&& includeString) +bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile( + SourceFileHandleT sourceFileHandle) { - bool success = false; - std::string uiInputFile = UicFindIncludedFile(meta, includeString); - if (!uiInputFile.empty()) { - if (!Gen()->Uic().skipped(uiInputFile)) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique( - std::move(uiInputFile), FileName, std::move(includeString)); - success = Gen()->ParallelJobPushUic(std::move(jobHandle)); - } else { - // A skipped file is successful - success = true; + SourceFileT const& sourceFile = *sourceFileHandle; + auto const& Include = sourceFile.ParseData->Uic.Include; + if (!sourceFile.Uic || Include.empty()) { + return true; + } + + std::string const sourceDir = SubDirPrefix(sourceFile.FileName); + for (IncludeKeyT const& incKey : Include) { + // Find .ui file name + SourceFileHandleT uiFileHandle = + UicFindIncludedUi(sourceFile.FileName, sourceDir, incKey); + if (!uiFileHandle || UicConst().skipped(uiFileHandle->FileName)) { + continue; + } + // Register mapping + if (!UicRegisterMapping(incKey.Key, std::move(uiFileHandle), + std::move(sourceFileHandle))) { + return false; } } - return success; + + return true; } -std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( - MetaT const& meta, std::string const& includeString) +bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping( + std::string const& includeString, SourceFileHandleT uiFileHandle, + SourceFileHandleT includerFileHandle) { - std::string res; - std::string searchFile = - FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; + auto& Includes = Gen()->UicEval().Includes; + auto it = Includes.find(includeString); + if (it != Includes.end()) { + MappingHandleT const& handle = it->second; + if (handle->SourceFile != uiFileHandle) { + // The output file already gets generated - from a different .ui file! + std::string msg = "The source files\n "; + msg += Quoted(includerFileHandle->FileName); + msg += '\n'; + for (auto const& item : handle->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; + } + msg += "contain the same include string "; + msg += Quoted(includeString); + msg += ", but\nthe uic file would be generated from different " + "user interface files\n "; + msg += Quoted(uiFileHandle->FileName); + msg += " and\n "; + msg += Quoted(handle->SourceFile->FileName); + msg += ".\nConsider to\n" + " - add a directory prefix to a \"ui_.h\" include " + "(e.g \"sub/ui_.h\")\n" + " - rename the .ui file(s) and adjust the \"ui_.h\" " + "include(s)\n"; + LogError(GenT::UIC, msg); + return false; + } + // Add includer file to existing mapping + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + } else { + // New mapping handle + MappingHandleT handle = std::make_shared(); + handle->IncludeString = includeString; + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + handle->SourceFile = std::move(uiFileHandle); + handle->OutputFile += Gen()->AbsoluteIncludePath(includeString); + // Register mapping + Includes.emplace(includeString, std::move(handle)); + } + return true; +} + +cmQtAutoMocUic::SourceFileHandleT +cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi( + std::string const& sourceFile, std::string const& sourceDir, + IncludeKeyT const& incKey) const +{ + std::string searchFileName = incKey.Base; + searchFileName += ".ui"; // Collect search paths list - std::deque testFiles; + std::vector testFiles; { - std::string const searchPath = FileSys().SubDirPrefix(includeString); + auto& searchPaths = UicConst().SearchPaths; + testFiles.reserve((searchPaths.size() + 1) * 2); - 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); - } + testFiles.emplace_back(sourceDir + searchFileName); + if (!incKey.Dir.empty()) { + std::string path = sourceDir; + path += incKey.Dir; + path += searchFileName; + testFiles.emplace_back(path); } // AUTOUIC search paths - if (!Gen()->Uic().SearchPaths.empty()) { - for (std::string const& sPath : Gen()->Uic().SearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); + if (!searchPaths.empty()) { + for (std::string const& sPath : searchPaths) { + std::string path = sPath; + path += '/'; + path += searchFileName; + testFiles.emplace_back(std::move(path)); } - if (!searchPath.empty()) { - for (std::string const& sPath : Gen()->Uic().SearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); + if (!incKey.Dir.empty()) { + for (std::string const& sPath : searchPaths) { + std::string path = sPath; + path += '/'; + path += incKey.Dir; + path += searchFileName; + testFiles.emplace_back(std::move(path)); } } } @@ -675,243 +1100,189 @@ std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( // Search for the .ui file! for (std::string const& testFile : testFiles) { - if (FileSys().FileExists(testFile)) { - res = FileSys().GetRealPath(testFile); - break; + cmFileTime fileTime; + if (fileTime.Load(testFile)) { + // .ui file found in files system! + std::string realPath = cmSystemTools::GetRealPath(testFile); + // Get or create .ui file handle + SourceFileHandleT& handle = Gen()->UicEval().UiFiles[realPath]; + if (!handle) { + // The file wasn't registered, yet + handle = std::make_shared(realPath); + handle->FileTime = fileTime; + } + return handle; } } // Log error - if (res.empty()) { - std::string emsg = "Could not find "; - emsg += Quoted(searchFile); - emsg += " in\n"; + { + std::string msg = "The file includes the uic file "; + msg += Quoted(incKey.Key); + msg += ",\nbut the user interface file "; + msg += Quoted(searchFileName); + msg += "\ncould not be found in the following locations\n"; for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += Quoted(testFile); - emsg += "\n"; + msg += " "; + msg += Quoted(testFile); + msg += '\n'; } - LogFileError(GenT::UIC, FileName, emsg); + LogFileError(GenT::UIC, sourceFile, msg); } - return res; -} - -void cmQtAutoMocUic::JobPostParseT::Process() -{ - if (Gen()->Moc().Enabled) { - // Add mocs compilations fence job - Gen()->WorkerPool().EmplaceJob(); - } - // Add finish job - Gen()->WorkerPool().EmplaceJob(); + return SourceFileHandleT(); } -void cmQtAutoMocUic::JobMocsCompilationT::Process() +void cmQtAutoMocUic::JobGenerateT::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'; + // Add moc compile jobs + if (MocConst().Enabled) { + for (auto const& pair : MocEval().HeaderMappings) { + // Register if this mapping is a candidate for mocs_compilation.cpp + bool const compFile = pair.second->IncludeString.empty(); + if (compFile) { + MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath); + } + if (!MocGenerate(pair.second, compFile)) { + return; + } } + for (auto const& pair : MocEval().SourceMappings) { + if (!MocGenerate(pair.second, false)) { + return; + } + } + + // Add mocs compilations job on demand + Gen()->WorkerPool().EmplaceJob(); } - 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); + // Add uic compile jobs + if (UicConst().Enabled) { + for (auto const& pair : Gen()->UicEval().Includes) { + if (!UicGenerate(pair.second)) { + return; + } } - FileSys().Touch(compAbs); } -} -void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content) -{ - Gen()->Moc().FindDependencies(content, Depends); - DependsValid = true; + // Add finish job + Gen()->WorkerPool().EmplaceJob(); } -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::JobGenerateT::MocGenerate(MappingHandleT const& mapping, + bool compFile) const +{ + std::unique_ptr reason; + if (Log().Verbose()) { + reason = cm::make_unique(); + } + if (MocUpdate(*mapping, reason.get())) { + // Create the parent directory + if (!MakeParentDirectory(mapping->OutputFile)) { + LogFileError(GenT::MOC, mapping->OutputFile, + "Could not create parent directory."); + return false; + } + // Add moc job + Gen()->WorkerPool().EmplaceJob(mapping, std::move(reason)); + // Check if a moc job for a mocs_compilation.cpp entry was generated + if (compFile) { + MocEval().CompUpdated = true; + } } + return true; } -bool cmQtAutoMocUic::JobMocT::UpdateRequired() +bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping, + std::string* reason) const { - 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); + std::string const& sourceFile = mapping.SourceFile->FileName; + std::string const& outputFile = mapping.OutputFile; + + // Test if the output file exists + cmFileTime outputFileTime; + if (!outputFileTime.Load(outputFile)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it doesn't exist, from "; + *reason += Quoted(sourceFile); } 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); + if (MocConst().SettingsChanged) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because the uic settings changed, from "; + *reason += Quoted(sourceFile); } 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; - } + // Test if the source file is newer + if (outputFileTime.Older(mapping.SourceFile->FileTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than its source file, from "; + *reason += Quoted(sourceFile); } - 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 moc_predefs file is newer + if (!MocConst().PredefsFileAbs.empty()) { + if (outputFileTime.Older(MocEval().PredefsTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than "; + *reason += Quoted(MocConst().PredefsFileAbs); + *reason += ", from "; + *reason += Quoted(sourceFile); } 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 the moc executable is newer + if (outputFileTime.Older(MocConst().ExecutableTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than the moc executable, from "; + *reason += Quoted(sourceFile); } + 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; + std::string const sourceDir = SubDirPrefix(sourceFile); + for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) { + // Find dependency file + auto const depMatch = MocFindDependency(sourceDir, dep); + if (depMatch.first.empty()) { + Log().WarningFile(GenT::MOC, sourceFile, + "Could not find dependency file " + Quoted(dep)); + continue; + } + // Test if dependency file is older + if (outputFileTime.Older(depMatch.second)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than its dependency file "; + *reason += Quoted(depMatch.first); + *reason += ", from "; + *reason += Quoted(sourceFile); } - } else { - std::string message = "Could not find dependency file "; - message += Quoted(depFileRel); - Log().WarningFile(GenT::MOC, SourceFile, message); + return true; } } } @@ -919,198 +1290,254 @@ bool cmQtAutoMocUic::JobMocT::UpdateRequired() return false; } -void cmQtAutoMocUic::JobMocT::GenerateMoc() +std::pair +cmQtAutoMocUic::JobGenerateT::MocFindDependency( + std::string const& sourceDir, std::string const& includeString) const { - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(BuildFile)) { - LogFileError(GenT::MOC, BuildFile, "Could not create parent directory."); - return; - } + typedef std::pair ResPair; + // Search in vicinity of the source { - // Compose moc command - std::vector 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); + ResPair res{ sourceDir + includeString, {} }; + if (res.second.Load(res.first)) { + return res; + } + } + // Search in include directories + for (std::string const& includePath : MocConst().IncludePaths) { + ResPair res{ includePath, {} }; + res.first += '/'; + res.first += includeString; + if (res.second.Load(res.first)) { + return res; } } + // Return empty + return ResPair(); } -void cmQtAutoMocUic::JobUicT::Process() +bool cmQtAutoMocUic::JobGenerateT::UicGenerate( + MappingHandleT const& mapping) const { - // Compute build file name - BuildFile = Gen()->Base().AutogenIncludeDir; - BuildFile += '/'; - BuildFile += IncludeString; - - if (UpdateRequired()) { - GenerateUic(); + std::unique_ptr reason; + if (Log().Verbose()) { + reason = cm::make_unique(); + } + if (UicUpdate(*mapping, reason.get())) { + // Create the parent directory + if (!MakeParentDirectory(mapping->OutputFile)) { + LogFileError(GenT::UIC, mapping->OutputFile, + "Could not create parent directory."); + return false; + } + // Add uic job + Gen()->WorkerPool().EmplaceJob(mapping, std::move(reason)); } + return true; } -bool cmQtAutoMocUic::JobUicT::UpdateRequired() +bool cmQtAutoMocUic::JobGenerateT::UicUpdate(MappingT const& mapping, + std::string* reason) const { - bool const verbose = Log().Verbose(); + std::string const& sourceFile = mapping.SourceFile->FileName; + std::string const& outputFile = mapping.OutputFile; // 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); + cmFileTime outputFileTime; + if (!outputFileTime.Load(outputFile)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it doesn't exist, from "; + *reason += Quoted(sourceFile); } 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); + if (UicConst().SettingsChanged) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because the uic settings changed, from "; + *reason += Quoted(sourceFile); } 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 (outputFileTime.Older(mapping.SourceFile->FileTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += " because it's older than the source file "; + *reason += Quoted(sourceFile); } - 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 true; + } + + // Test if the uic executable is newer + if (outputFileTime.Older(UicConst().ExecutableTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than the uic executable, from "; + *reason += Quoted(sourceFile); } + return true; } return false; } -void cmQtAutoMocUic::JobUicT::GenerateUic() +void cmQtAutoMocUic::JobMocT::Process() { - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(BuildFile)) { - LogFileError(GenT::UIC, BuildFile, "Could not create parent directory."); - return; + std::string const& sourceFile = Mapping->SourceFile->FileName; + std::string const& outputFile = Mapping->OutputFile; + + // Compose moc command + std::vector cmd; + cmd.push_back(MocConst().Executable); + // Add options + cmd.insert(cmd.end(), MocConst().AllOptions.begin(), + MocConst().AllOptions.end()); + // Add predefs include + if (!MocConst().PredefsFileAbs.empty()) { + cmd.emplace_back("--include"); + cmd.push_back(MocConst().PredefsFileAbs); + } + cmd.emplace_back("-o"); + cmd.push_back(outputFile); + cmd.push_back(sourceFile); + + // Execute moc command + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::MOC, result, cmd, Reason.get())) { + // Moc command success. Print moc output. + if (!result.StdOut.empty()) { + Log().Info(GenT::MOC, result.StdOut); + } + } else { + // Moc command failed + std::string msg = "The moc process failed to compile\n "; + msg += Quoted(sourceFile); + msg += "\ninto\n "; + msg += Quoted(outputFile); + if (Mapping->IncluderFiles.empty()) { + msg += ".\n"; + } else { + msg += "\nincluded by\n"; + for (auto const& item : Mapping->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; + } + } + msg += result.ErrorMessage; + LogCommandError(GenT::MOC, msg, cmd, result.StdOut); } +} + +void cmQtAutoMocUic::JobUicT::Process() +{ + std::string const& sourceFile = Mapping->SourceFile->FileName; + std::string const& outputFile = Mapping->OutputFile; + + // Compose uic command + std::vector cmd; + cmd.push_back(UicConst().Executable); { - // Compose uic command - std::vector cmd; - cmd.push_back(Gen()->Uic().Executable); - { - std::vector 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()); + std::vector allOpts = UicConst().TargetOptions; + auto optionIt = UicConst().Options.find(sourceFile); + if (optionIt != UicConst().Options.end()) { + UicMergeOptions(allOpts, optionIt->second, + (BaseConst().QtVersionMajor == 5)); } - cmd.emplace_back("-o"); - cmd.emplace_back(BuildFile); - cmd.emplace_back(SourceFile); + cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + } + cmd.emplace_back("-o"); + cmd.emplace_back(outputFile); + 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); + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::UIC, result, cmd, Reason.get())) { + // Uic command success + // Print uic output + if (!result.StdOut.empty()) { + Log().Info(GenT::UIC, result.StdOut); } + } else { + // Uic command failed + std::string msg = "The uic process failed to compile\n "; + msg += Quoted(sourceFile); + msg += "\ninto\n "; + msg += Quoted(outputFile); + msg += "\nincluded by\n"; + for (auto const& item : Mapping->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; + } + msg += result.ErrorMessage; + LogCommandError(GenT::UIC, msg, cmd, result.StdOut); } } -void cmQtAutoMocUic::JobFinishT::Process() +void cmQtAutoMocUic::JobMocsCompilationT::Process() { - Gen()->AbortSuccess(); + // Compose mocs compilation file content + std::string content = + "// This file is autogenerated. Changes will be overwritten.\n"; + + if (MocEval().CompFiles.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 clampB = BaseConst().MultiConfig ? '<' : '"'; + char const clampE = BaseConst().MultiConfig ? '>' : '"'; + for (std::string const& mocfile : MocEval().CompFiles) { + content += "#include "; + content += clampB; + content += mocfile; + content += clampE; + content += '\n'; + } + } + + std::string const& compAbs = MocConst().CompFileAbs; + if (cmQtAutoGenerator::FileDiffers(compAbs, content)) { + // Actually write mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); + } + if (!FileWrite(compAbs, content)) { + LogFileError(GenT::MOC, compAbs, + "mocs compilation file writing failed."); + } + } else if (MocEval().CompUpdated) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); + } + if (!cmSystemTools::Touch(compAbs, false)) { + LogFileError(GenT::MOC, compAbs, + "mocs compilation file touching failed."); + } + } } -cmQtAutoMocUic::cmQtAutoMocUic() - : Base_(&FileSys()) - , Moc_(&FileSys()) +void cmQtAutoMocUic::JobFinishT::Process() { - // Precompile regular expressions - Moc_.RegExpInclude.compile( - "(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + Gen()->AbortSuccess(); } +cmQtAutoMocUic::cmQtAutoMocUic() = default; 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); @@ -1169,171 +1596,185 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); return list; }; + auto LogInfoError = [this](std::string const& msg) -> bool { + std::ostringstream err; + err << "In " << Quoted(this->InfoFile()) << ":\n" << msg; + this->Log().Error(GenT::GEN, err.str()); + return false; + }; + auto MatchSizes = [&LogInfoError](const char* keyA, const char* keyB, + std::size_t sizeA, + std::size_t sizeB) -> bool { + if (sizeA == sizeB) { + return true; + } + std::ostringstream err; + err << "Lists sizes mismatch " << keyA << '(' << sizeA << ") " << keyB + << '(' << sizeB << ')'; + return LogInfoError(err.str()); + }; // -- Read info file if (!makefile->ReadListFile(InfoFile())) { - Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed"); - return false; + return LogInfoError("File processing failed"); } // -- Meta - Log().RaiseVerbosity(InfoGet("AM_VERBOSITY")); - Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); + Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY")); + BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); { - unsigned long num = Base_.NumThreads; + unsigned long num = 1; if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) { num = std::max(num, 1); num = std::min(num, ParallelMax); - Base_.NumThreads = static_cast(num); } - WorkerPool_.SetThreadCount(Base_.NumThreads); + WorkerPool_.SetThreadCount(static_cast(num)); } + BaseConst_.HeaderExtensions = + makefile->GetCMakeInstance()->GetHeaderExtensions(); // - 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 = + BaseConst_.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 + BaseConst_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + BaseConst_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + BaseConst_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + BaseConst_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (BaseConst_.AutogenBuildDir.empty()) { + return LogInfoError("Autogen build directory missing."); + } + BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); + if (BaseConst_.AutogenIncludeDir.empty()) { + return LogInfoError("Autogen include directory missing."); + } + BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE"); + if (BaseConst_.CMakeExecutable.empty()) { + return LogInfoError("CMake executable file name missing."); + } + if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) { + std::string error = "The CMake executable "; + error += Quoted(BaseConst_.CMakeExecutable); + error += " does not exist."; + return LogInfoError(error); + } + BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE"); + if (BaseConst_.ParseCacheFile.empty()) { + return LogInfoError("Parse cache file name missing."); + } + + // - Settings file SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); if (SettingsFile_.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing"); - return false; + return LogInfoError("Settings file name missing."); } // - Qt environment { - unsigned long qtv = Base_.QtVersionMajor; + unsigned long qtv = BaseConst_.QtVersionMajor; if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(), &qtv)) { - Base_.QtVersionMajor = static_cast(qtv); + BaseConst_.QtVersionMajor = static_cast(qtv); } } // - Moc - Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); - Moc_.Enabled = !Moc().Executable.empty(); - if (Moc().Enabled) { + MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); + if (!MocConst().Executable.empty()) { + MocConst_.Enabled = true; + // Load the executable file time + if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { + std::string error = "The moc executable "; + error += Quoted(MocConst_.Executable); + error += " does not exist."; + return LogInfoError(error); + } for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { - Moc_.SkipList.insert(std::move(sfl)); + MocConst_.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"); + MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); + MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + MocConst_.Options = InfoGetList("AM_MOC_OPTIONS"); + MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { - Moc_.MacroFilters.emplace_back( + MocConst_.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"; + auto addFilter = [this, &LogInfoError](std::string const& key, + std::string const& exp) -> bool { + auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool { + std::ostringstream ferr; + ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n'; + ferr << " Key: " << Quoted(key) << '\n'; + ferr << " Exp: " << Quoted(exp) << '\n'; + return LogInfoError(ferr.str()); + }; + if (key.empty()) { + return filterErr("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"; + if (exp.empty()) { + return filterErr("Regular expression is empty"); } + this->MocConst_.DependFilters.emplace_back(key, exp); + if (!this->MocConst_.DependFilters.back().Exp.is_valid()) { + return filterErr("Regular expression compiling failed"); + } + return true; }; - 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); + if (BaseConst().QtVersionMajor != 4) { + if (!addFilter("Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) { + return false; + } } // Insert user defined dependency filters - { - std::vector flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); - if ((flts.size() % 2) == 0) { - for (std::vector::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"); + std::vector flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); + if ((flts.size() % 2) != 0) { + return LogInfoError( + "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + } + for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) { + if (!addFilter(*itC, *(itC + 1))) { 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(); } + MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); } // - Uic - Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); - Uic_.Enabled = !Uic().Executable.empty(); - if (Uic().Enabled) { + UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); + if (!UicConst().Executable.empty()) { + UicConst_.Enabled = true; + // Load the executable file time + if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) { + std::string error = "The uic executable "; + error += Quoted(UicConst_.Executable); + error += " does not exist."; + return LogInfoError(error); + } for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { - Uic_.SkipList.insert(std::move(sfl)); + UicConst_.SkipList.insert(std::move(sfl)); } - Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + UicConst_.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()); + const char* keyFiles = "AM_UIC_OPTIONS_FILES"; + const char* keyOpts = "AM_UIC_OPTIONS_OPTIONS"; + auto sources = InfoGetList(keyFiles); + auto options = InfoGetLists(keyOpts); + if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) { return false; } auto fitEnd = sources.cend(); auto fit = sources.begin(); auto oit = options.begin(); while (fit != fitEnd) { - Uic_.Options[*fit] = std::move(*oit); + UicConst_.Options[*fit] = std::move(*oit); ++fit; ++oit; } @@ -1341,92 +1782,121 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) } // - Headers and sources - // Add sources { - auto addSource = [this](std::string&& src, bool moc, bool uic) { - WorkerPool().EmplaceJob(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); + auto makeSource = + [&LogInfoError](std::string const& fileName, + std::string const& fileFlags) -> SourceFileHandleT { + if (fileFlags.size() != 2) { + LogInfoError("Invalid file flags string size"); + return SourceFileHandleT(); } - } - if (Uic().Enabled) { - for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { - addSource(std::move(src), false, true); + cmFileTime fileTime; + if (!fileTime.Load(fileName)) { + LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) + + " does not exist."); + return SourceFileHandleT(); } - } - } - // Add Fence job - WorkerPool().EmplaceJob(); - // Add headers - { - auto addHeader = [this](std::string&& hdr, bool moc, bool uic) { - WorkerPool().EmplaceJob(std::move(hdr), moc, uic, true); + SourceFileHandleT sfh = std::make_shared(fileName); + sfh->FileTime = fileTime; + sfh->Moc = (fileFlags[0] == 'M'); + sfh->Uic = (fileFlags[1] == 'U'); + return sfh; }; - 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); + + // Headers + { + // Get file lists + const char *keyFiles = "AM_HEADERS", *keyFlags = "AM_HEADERS_FLAGS"; + std::vector files = InfoGetList(keyFiles); + std::vector flags = InfoGetList(keyFlags); + std::vector builds; + if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { + return false; + } + if (MocConst().Enabled) { + const char* keyPaths = "AM_HEADERS_BUILD_PATHS"; + builds = InfoGetList(keyPaths); + if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) { + return false; + } + } + // Process file lists + for (std::size_t ii = 0; ii != files.size(); ++ii) { + std::string& fileName(files[ii]); + SourceFileHandleT sfh = makeSource(fileName, flags[ii]); + if (!sfh) { + return false; + } + if (MocConst().Enabled) { + sfh->BuildPath = std::move(builds[ii]); + if (sfh->BuildPath.empty()) { + Log().ErrorFile(GenT::GEN, this->InfoFile(), + "Header file build path is empty"); + return false; + } + } + BaseEval().Headers.emplace(std::move(fileName), std::move(sfh)); } } - if (Uic().Enabled) { - for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { - addHeader(std::move(hdr), false, true); + + // Sources + { + const char *keyFiles = "AM_SOURCES", *keyFlags = "AM_SOURCES_FLAGS"; + std::vector files = InfoGetList(keyFiles); + std::vector flags = InfoGetList(keyFlags); + if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { + return false; + } + // Process file lists + for (std::size_t ii = 0; ii != files.size(); ++ii) { + std::string& fileName(files[ii]); + SourceFileHandleT sfh = makeSource(fileName, flags[ii]); + if (!sfh) { + return false; + } + BaseEval().Sources.emplace(std::move(fileName), std::move(sfh)); } } } - // Addpost parse fence job - WorkerPool().EmplaceJob(); // Init derived information // ------------------------ - // Init file path checksum generator - FileSys().setupFilePathChecksum( - Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir, - Base().ProjectBinaryDir); - // Moc variables - if (Moc().Enabled) { + if (MocConst().Enabled) { // Mocs compilation file - Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp"); + MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp"); // Moc predefs file - if (!Moc_.PredefsCmd.empty()) { - Moc_.PredefsFileRel = "moc_predefs"; - if (Base_.MultiConfig) { - Moc_.PredefsFileRel += '_'; - Moc_.PredefsFileRel += InfoConfig(); + if (!MocConst_.PredefsCmd.empty()) { + MocConst_.PredefsFileRel = "moc_predefs"; + if (BaseConst_.MultiConfig) { + MocConst_.PredefsFileRel += '_'; + MocConst_.PredefsFileRel += InfoConfig(); } - Moc_.PredefsFileRel += ".h"; - Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel); + MocConst_.PredefsFileRel += ".h"; + MocConst_.PredefsFileAbs = AbsoluteBuildPath(MocConst().PredefsFileRel); } // Sort include directories on demand - if (Base().IncludeProjectDirsBefore) { + if (BaseConst().IncludeProjectDirsBefore) { // Move strings to temporary list std::list includes; - includes.insert(includes.end(), Moc().IncludePaths.begin(), - Moc().IncludePaths.end()); - Moc_.IncludePaths.clear(); - Moc_.IncludePaths.reserve(includes.size()); + includes.insert(includes.end(), MocConst().IncludePaths.begin(), + MocConst().IncludePaths.end()); + MocConst_.IncludePaths.clear(); + MocConst_.IncludePaths.reserve(includes.size()); // Append project directories only { std::array const movePaths = { - { &Base().ProjectBinaryDir, &Base().ProjectSourceDir } + { &BaseConst().ProjectBinaryDir, &BaseConst().ProjectSourceDir } }; for (std::string const* ppath : movePaths) { std::list::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); + MocConst_.IncludePaths.push_back(path); it = includes.erase(it); } else { ++it; @@ -1435,124 +1905,176 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) } } // Append remaining directories - Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(), - includes.end()); + MocConst_.IncludePaths.insert(MocConst_.IncludePaths.end(), + includes.begin(), includes.end()); } // Compose moc includes list { std::set frameworkPaths; - for (std::string const& path : Moc().IncludePaths) { - Moc_.Includes.push_back("-I" + path); + for (std::string const& path : MocConst().IncludePaths) { + MocConst_.Includes.push_back("-I" + path); // Extract framework path if (cmHasLiteralSuffix(path, ".framework/Headers")) { // Go up twice to get to the framework root std::vector pathComponents; - FileSys().SplitPath(path, pathComponents); - std::string frameworkPath = FileSys().JoinPath( - pathComponents.begin(), pathComponents.end() - 2); - frameworkPaths.insert(frameworkPath); + cmSystemTools::SplitPath(path, pathComponents); + frameworkPaths.emplace(cmSystemTools::JoinPath( + pathComponents.begin(), pathComponents.end() - 2)); } } // Append framework includes for (std::string const& path : frameworkPaths) { - Moc_.Includes.emplace_back("-F"); - Moc_.Includes.push_back(path); + MocConst_.Includes.emplace_back("-F"); + MocConst_.Includes.push_back(path); } } // Setup single list with all options { // Add includes - Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(), - Moc().Includes.end()); + MocConst_.AllOptions.insert(MocConst_.AllOptions.end(), + MocConst().Includes.begin(), + MocConst().Includes.end()); // Add definitions - for (std::string const& def : Moc().Definitions) { - Moc_.AllOptions.push_back("-D" + def); + for (std::string const& def : MocConst().Definitions) { + MocConst_.AllOptions.push_back("-D" + def); } // Add options - Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(), - Moc().Options.end()); + MocConst_.AllOptions.insert(MocConst_.AllOptions.end(), + MocConst().Options.begin(), + MocConst().Options.end()); } } return true; } +template +void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap) +{ + cmFileTime const parseCacheTime = BaseEval().ParseCacheTime; + ParseCacheT& parseCache = BaseEval().ParseCache; + for (auto& src : sourceMap) { + // Get or create the file parse data reference + ParseCacheT::GetOrInsertT cacheEntry = parseCache.GetOrInsert(src.first); + src.second->ParseData = std::move(cacheEntry.first); + // Create a parse job if the cache file was missing or is older + if (cacheEntry.second || src.second->FileTime.Newer(parseCacheTime)) { + BaseEval().ParseCacheChanged = true; + WorkerPool().EmplaceJob(src.second); + } + } +} + +void cmQtAutoMocUic::InitJobs() +{ + // Add moc_predefs.h job + if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) { + WorkerPool().EmplaceJob(); + } + // Add header parse jobs + CreateParseJobs(BaseEval().Headers); + // Add source parse jobs + CreateParseJobs(BaseEval().Sources); + // Add evaluate job + WorkerPool().EmplaceJob(); +} + bool cmQtAutoMocUic::Process() { SettingsFileRead(); + ParseCacheRead(); if (!CreateDirectories()) { return false; } + InitJobs(); if (!WorkerPool_.Process(this)) { return false; } if (JobError_) { return false; } - return SettingsFileWrite(); + if (!ParseCacheWrite()) { + return false; + } + if (!SettingsFileWrite()) { + return false; + } + return true; } 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); + cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256); + std::string const sep(";"); + auto cha = [&cryptoHash, &sep](std::string const& value) { + cryptoHash.Append(value); + cryptoHash.Append(sep); + }; + + if (MocConst_.Enabled) { + cryptoHash.Initialize(); + cha(MocConst().Executable); + for (auto const& value : MocConst().AllOptions) { + cha(value); + } + cha(BaseConst().IncludeProjectDirsBefore ? "TRUE" : "FALSE"); + for (auto const& value : MocConst().PredefsCmd) { + cha(value); + } + for (auto const& filter : MocConst().DependFilters) { + cha(filter.Key); + } + for (auto const& filter : MocConst().MacroFilters) { + cha(filter.Key); + } + SettingsStringMoc_ = cryptoHash.FinalizeHex(); + } + + if (UicConst().Enabled) { + cryptoHash.Initialize(); + cha(UicConst().Executable); + for (auto const& value : UicConst().TargetOptions) { + cha(value); + } + for (const auto& item : UicConst().Options) { + cha(item.first); + for (auto const& svalue : item.second) { + cha(svalue); + } + } + SettingsStringUic_ = cryptoHash.FinalizeHex(); } } // Read old settings and compare { std::string content; - if (FileSys().FileRead(content, SettingsFile_)) { - if (Moc().Enabled) { + if (cmQtAutoGenerator::FileRead(content, SettingsFile_)) { + if (MocConst().Enabled) { if (SettingsStringMoc_ != SettingsFind(content, "moc")) { - Moc_.SettingsChanged = true; + MocConst_.SettingsChanged = true; } } - if (Uic().Enabled) { + if (UicConst().Enabled) { if (SettingsStringUic_ != SettingsFind(content, "uic")) { - Uic_.SettingsChanged = true; + UicConst_.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_); + if (MocConst().SettingsChanged || UicConst().SettingsChanged) { + cmSystemTools::RemoveFile(SettingsFile_); } } else { // Settings file read failed - if (Moc().Enabled) { - Moc_.SettingsChanged = true; + if (MocConst().Enabled) { + MocConst_.SettingsChanged = true; } - if (Uic().Enabled) { - Uic_.SettingsChanged = true; + if (UicConst().Enabled) { + UicConst_.SettingsChanged = true; } } } @@ -1561,7 +2083,7 @@ void cmQtAutoMocUic::SettingsFileRead() bool cmQtAutoMocUic::SettingsFileWrite() { // Only write if any setting changed - if (Moc().SettingsChanged || Uic().SettingsChanged) { + if (MocConst().SettingsChanged || UicConst().SettingsChanged) { if (Log().Verbose()) { Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); } @@ -1582,11 +2104,54 @@ bool cmQtAutoMocUic::SettingsFileWrite() } // Write settings file std::string error; - if (!FileSys().FileWrite(SettingsFile_, content, &error)) { + if (!cmQtAutoGenerator::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_); + cmSystemTools::RemoveFile(SettingsFile_); + return false; + } + } + return true; +} + +void cmQtAutoMocUic::ParseCacheRead() +{ + const char* reason = nullptr; + // Don't read the cache if it is invalid + if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) { + reason = "Refreshing parse cache because it doesn't exist."; + } else if (MocConst().SettingsChanged || UicConst().SettingsChanged) { + reason = "Refreshing parse cache because the settings changed."; + } else if (BaseEval().ParseCacheTime.Older( + BaseConst().CMakeExecutableTime)) { + reason = + "Refreshing parse cache because it is older than the CMake executable."; + } + + if (reason != nullptr) { + // Don't read but refresh the complete parse cache + if (Log().Verbose()) { + Log().Info(GenT::GEN, reason); + } + BaseEval().ParseCacheChanged = true; + } else { + // Read parse cache + BaseEval().ParseCache.ReadFromFile(BaseConst().ParseCacheFile); + } +} + +bool cmQtAutoMocUic::ParseCacheWrite() +{ + if (BaseEval().ParseCacheChanged) { + if (Log().Verbose()) { + Log().Info(GenT::GEN, + "Writing parse cache file " + + Quoted(BaseConst().ParseCacheFile)); + } + if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) { + Log().ErrorFile(GenT::GEN, BaseConst().ParseCacheFile, + "Parse cache file writing failed."); return false; } } @@ -1596,16 +2161,14 @@ bool cmQtAutoMocUic::SettingsFileWrite() bool cmQtAutoMocUic::CreateDirectories() { // Create AUTOGEN include directory - if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) { - Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir, + if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) { + Log().ErrorFile(GenT::GEN, BaseConst().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) { @@ -1614,132 +2177,20 @@ void cmQtAutoMocUic::Abort(bool error) WorkerPool_.Abort(); } -bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle) -{ - JobMocT const& mocJob(static_cast(*jobHandle)); - // Do additional tests if this is an included moc job - if (!mocJob.IncludeString.empty()) { - std::lock_guard 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_.cpp\" file\n" - "- add a directory prefix to a \".moc\" include " - "(e.g \"sub/.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{ - { mocJob.SourceFile, mocJob.IncluderFile } }); - } - return WorkerPool_.PushJob(std::move(jobHandle)); -} - -bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle) -{ - const JobUicT& uicJob(static_cast(*jobHandle)); - { - std::lock_guard 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_.h\" include " - "(e.g \"sub/ui_.h\")\n" - "- rename the .ui file(s) and adjust the \"ui_.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{ - { uicJob.SourceFile, uicJob.IncluderFile } }); - } - return WorkerPool_.PushJob(std::move(jobHandle)); -} - -bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile) +std::string cmQtAutoMocUic::AbsoluteBuildPath( + std::string const& relativePath) const { - std::lock_guard guard(MocMetaMutex_); - return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); + std::string res(BaseConst().AutogenBuildDir); + res += '/'; + res += relativePath; + return res; } -std::string cmQtAutoMocUic::ParallelMocAutoRegister( - std::string const& baseName) +std::string cmQtAutoMocUic::AbsoluteIncludePath( + std::string const& relativePath) const { - std::string res; - { - std::lock_guard 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; - } - } - } - } + std::string res(BaseConst().AutogenIncludeDir); + res += '/'; + res += relativePath; return res; } diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h index 3902abb..8061c13 100644 --- a/Source/cmQtAutoMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -5,25 +5,28 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmFileTime.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" #include "cmWorkerPool.h" #include "cmsys/RegularExpression.hxx" -#include #include +#include #include #include // IWYU pragma: keep -#include #include #include +#include #include #include #include class cmMakefile; -// @brief AUTOMOC and AUTOUIC generator +/** \class cmQtAutoMocUic + * \brief AUTOMOC and AUTOUIC generator + */ class cmQtAutoMocUic : public cmQtAutoGenerator { public: @@ -35,10 +38,10 @@ public: public: // -- Types - typedef std::multimap> IncludesMap; - /// @brief Search key plus regular expression pair - /// + /** + * Search key plus regular expression pair + */ struct KeyExpT { KeyExpT() = default; @@ -59,35 +62,123 @@ public: cmsys::RegularExpression Exp; }; - /// @brief Common settings - /// - class BaseSettingsT + /** + * Include string with sub parts + */ + struct IncludeKeyT + { + IncludeKeyT(std::string const& key, std::size_t basePrefixLength); + + std::string Key; // Full include string + std::string Dir; // Include directory + std::string Base; // Base part of the include file name + }; + + /** + * Source file parsing cache + */ + class ParseCacheT + { + public: + // -- Types + /** + * Entry of the file parsing cache + */ + struct FileT + { + void Clear(); + + struct MocT + { + std::string Macro; + struct IncludeT + { + std::vector Underscore; + std::vector Dot; + } Include; + std::vector Depends; + } Moc; + + struct UicT + { + std::vector Include; + std::vector Depends; + } Uic; + }; + typedef std::shared_ptr FileHandleT; + typedef std::pair GetOrInsertT; + + public: + ParseCacheT(); + ~ParseCacheT(); + + void Clear(); + + bool ReadFromFile(std::string const& fileName); + bool WriteToFile(std::string const& fileName); + + //! Might return an invalid handle + FileHandleT Get(std::string const& fileName) const; + //! Always returns a valid handle + GetOrInsertT GetOrInsert(std::string const& fileName); + + private: + std::unordered_map Map_; + }; + + /** + * Source file data + */ + class SourceFileT { public: - // -- Volatile methods - BaseSettingsT(FileSystem* fileSystem) - : MultiConfig(false) - , IncludeProjectDirsBefore(false) - , QtVersionMajor(4) - , NumThreads(1) - , FileSys(fileSystem) + SourceFileT(std::string fileName) + : FileName(std::move(fileName)) { } + public: + std::string FileName; + cmFileTime FileTime; + ParseCacheT::FileHandleT ParseData; + std::string BuildPath; + bool Moc = false; + bool Uic = false; + }; + typedef std::shared_ptr SourceFileHandleT; + typedef std::map SourceFileMapT; + + /** + * Meta compiler file mapping information + */ + struct MappingT + { + SourceFileHandleT SourceFile; + std::string OutputFile; + std::string IncludeString; + std::vector IncluderFiles; + }; + typedef std::shared_ptr MappingHandleT; + typedef std::map MappingMapT; + + /** + * Common settings + */ + class BaseSettingsT + { + public: + // -- Constructors + BaseSettingsT(); + ~BaseSettingsT(); + BaseSettingsT(BaseSettingsT const&) = delete; BaseSettingsT& operator=(BaseSettingsT const&) = delete; - // -- Const methods - std::string AbsoluteBuildPath(std::string const& relativePath) const; - bool FindHeader(std::string& header, - std::string const& testBasePath) const; - // -- Attributes // - Config - bool MultiConfig; - bool IncludeProjectDirsBefore; - unsigned int QtVersionMajor; - unsigned int NumThreads; + bool MultiConfig = false; + bool IncludeProjectDirsBefore = false; + unsigned int QtVersionMajor = 4; // - Directories std::string ProjectSourceDir; std::string ProjectBinaryDir; @@ -96,37 +187,50 @@ public: std::string AutogenBuildDir; std::string AutogenIncludeDir; // - Files + std::string CMakeExecutable; + cmFileTime CMakeExecutableTime; + std::string ParseCacheFile; std::vector HeaderExtensions; - // - File system - FileSystem* FileSys; }; - /// @brief Moc settings - /// + /** + * Shared common variables + */ + class BaseEvalT + { + public: + // -- Parse Cache + bool ParseCacheChanged = false; + cmFileTime ParseCacheTime; + ParseCacheT ParseCache; + + // -- Sources + SourceFileMapT Headers; + SourceFileMapT Sources; + }; + + /** + * Moc settings + */ class MocSettingsT { public: - MocSettingsT(FileSystem* fileSys) - : FileSys(fileSys) - { - } + // -- Constructors + MocSettingsT(); + ~MocSettingsT(); MocSettingsT(MocSettingsT const&) = delete; MocSettingsT& operator=(MocSettingsT const&) = delete; // -- Const methods bool skipped(std::string const& fileName) const; - std::string FindMacro(std::string const& content) const; std::string MacrosString() const; - std::string FindIncludedFile(std::string const& sourcePath, - std::string const& includeString) const; - void FindDependencies(std::string const& content, - std::set& depends) const; // -- Attributes bool Enabled = false; bool SettingsChanged = false; bool RelaxedMode = false; + cmFileTime ExecutableTime; std::string Executable; std::string CompFileAbs; std::string PredefsFileRel; @@ -141,16 +245,35 @@ public: std::vector DependFilters; std::vector MacroFilters; cmsys::RegularExpression RegExpInclude; - // - File system - FileSystem* FileSys; }; - /// @brief Uic settings - /// + /** + * Moc shared variables + */ + class MocEvalT + { + public: + // -- predefines file + cmFileTime PredefsTime; + // -- Mappings + MappingMapT HeaderMappings; + MappingMapT SourceMappings; + MappingMapT Includes; + // -- Discovered files + SourceFileMapT HeadersDiscovered; + // -- Mocs compilation + bool CompUpdated = false; + std::vector CompFiles; + }; + + /** + * Uic settings + */ class UicSettingsT { public: - UicSettingsT() = default; + UicSettingsT(); + ~UicSettingsT(); UicSettingsT(UicSettingsT const&) = delete; UicSettingsT& operator=(UicSettingsT const&) = delete; @@ -161,6 +284,7 @@ public: // -- Attributes bool Enabled = false; bool SettingsChanged = false; + cmFileTime ExecutableTime; std::string Executable; std::unordered_set SkipList; std::vector TargetOptions; @@ -169,8 +293,19 @@ public: cmsys::RegularExpression RegExpInclude; }; - /// @brief Abstract job class for concurrent job processing - /// + /** + * Uic shared variables + */ + class UicEvalT + { + public: + SourceFileMapT UiFiles; + MappingMapT Includes; + }; + + /** + * Abstract job class for concurrent job processing + */ class JobT : public cmWorkerPool::JobT { protected: @@ -188,10 +323,14 @@ public: return static_cast(UserData()); }; - //! Get the file system interface. Only valid during Process() call! - FileSystem& FileSys() { return Gen()->FileSys(); } - //! Get the logger. Only valid during Process() call! - Logger& Log() { return Gen()->Log(); } + // -- Accessors. Only valid during Process() call! + Logger const& Log() const { return Gen()->Log(); } + BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); } + BaseEvalT& BaseEval() const { return Gen()->BaseEval(); } + MocSettingsT const& MocConst() const { return Gen()->MocConst(); } + MocEvalT& MocEval() const { return Gen()->MocEval(); } + UicSettingsT const& UicConst() const { return Gen()->UicConst(); } + UicEvalT& UicEval() const { return Gen()->UicEval(); } // -- Error logging with automatic abort void LogError(GenT genType, std::string const& message) const; @@ -205,11 +344,13 @@ public: * @brief Run an external process. Use only during Process() call! */ bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector const& command); + std::vector const& command, + std::string* infoMessage = nullptr); }; - /// @brief Fence job utility class - /// + /** + * Fence job utility class + */ class JobFenceT : public JobT { public: @@ -220,121 +361,152 @@ public: void Process() override{}; }; - /// @brief Generate moc_predefs.h - /// - class JobMocPredefsT : public JobT + /** + * Generate moc_predefs.h + */ + class JobMocPredefsT : public JobFenceT { - private: void Process() override; + bool Update(std::string* reason) const; }; - /// @brief Parses a source file - /// + /** + * File parse job base class + */ class JobParseT : public JobT { public: - JobParseT(std::string fileName, bool moc, bool uic, bool header = false) - : FileName(std::move(fileName)) - , AutoMoc(moc) - , AutoUic(uic) - , Header(header) + JobParseT(SourceFileHandleT fileHandle) + : FileHandle(std::move(fileHandle)) { } - private: - struct MetaT - { - std::string Content; - std::string FileDir; - std::string FileBase; - }; + protected: + bool ReadFile(); + void CreateKeys(std::vector& container, + std::set const& source, + std::size_t basePrefixLength); + void MocMacro(); + void MocDependecies(); + void MocIncludes(); + void UicIncludes(); + + protected: + SourceFileHandleT FileHandle; + std::string Content; + }; + /** + * Header file parse job + */ + class JobParseHeaderT : public JobParseT + { + public: + using JobParseT::JobParseT; void Process() override; - bool ParseMocSource(MetaT const& meta); - bool ParseMocHeader(MetaT const& meta); - std::string MocStringHeaders(std::string const& fileBase) const; - std::string MocFindIncludedHeader(std::string const& includerDir, - std::string const& includeBase); - bool ParseUic(MetaT const& meta); - bool ParseUicInclude(MetaT const& meta, std::string&& includeString); - std::string UicFindIncludedFile(MetaT const& meta, - std::string const& includeString); + }; - private: - std::string FileName; - bool AutoMoc = false; - bool AutoUic = false; - bool Header = false; + /** + * Source file parse job + */ + class JobParseSourceT : public JobParseT + { + public: + using JobParseT::JobParseT; + void Process() override; }; - /// @brief Generates additional jobs after all files have been parsed - /// - class JobPostParseT : public JobFenceT + /** + * Evaluate parsed files + */ + class JobEvaluateT : public JobFenceT { - private: void Process() override; + + // -- Moc + bool MocEvalHeader(SourceFileHandleT source); + bool MocEvalSource(SourceFileHandleT const& source); + SourceFileHandleT MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) const; + SourceFileHandleT MocFindHeader(std::string const& basePath) const; + std::string MocMessageTestHeaders(std::string const& fileBase) const; + bool MocRegisterIncluded(std::string const& includeString, + SourceFileHandleT includerFileHandle, + SourceFileHandleT sourceFileHandle, + bool sourceIsHeader) const; + void MocRegisterMapping(MappingHandleT mappingHandle, + bool sourceIsHeader) const; + + // -- Uic + bool UicEval(SourceFileMapT const& fileMap); + bool UicEvalFile(SourceFileHandleT sourceFileHandle); + SourceFileHandleT UicFindIncludedUi(std::string const& sourceFile, + std::string const& sourceDir, + IncludeKeyT const& incKey) const; + bool UicRegisterMapping(std::string const& includeString, + SourceFileHandleT uiFileHandle, + SourceFileHandleT includerFileHandle); }; - /// @brief Generate mocs_compilation.cpp - /// - class JobMocsCompilationT : public JobFenceT + /** + * Generates moc/uic jobs + */ + class JobGenerateT : public JobFenceT { - private: void Process() override; + // -- Moc + bool MocGenerate(MappingHandleT const& mapping, bool compFile) const; + bool MocUpdate(MappingT const& mapping, std::string* reason) const; + std::pair MocFindDependency( + std::string const& sourceDir, std::string const& includeString) const; + // -- Uic + bool UicGenerate(MappingHandleT const& mapping) const; + bool UicUpdate(MappingT const& mapping, std::string* reason) const; }; - /// @brief Moc a file job - /// - class JobMocT : public JobT + /** + * File compiling base job + */ + class JobCompileT : public JobT { public: - JobMocT(std::string sourceFile, std::string includerFile, - std::string includeString) - : SourceFile(std::move(sourceFile)) - , IncluderFile(std::move(includerFile)) - , IncludeString(std::move(includeString)) + JobCompileT(MappingHandleT uicMapping, std::unique_ptr reason) + : Mapping(std::move(uicMapping)) + , Reason(std::move(reason)) { } - void FindDependencies(std::string const& content); + protected: + MappingHandleT Mapping; + std::unique_ptr Reason; + }; - private: + /** + * moc compiles a file + */ + class JobMocT : public JobCompileT + { + public: + using JobCompileT::JobCompileT; void Process() override; - bool UpdateRequired(); - void GenerateMoc(); + }; + /** + * uic compiles a file + */ + class JobUicT : public JobCompileT + { public: - std::string SourceFile; - std::string IncluderFile; - std::string IncludeString; - std::string BuildFile; - bool DependsValid = false; - std::set Depends; + using JobCompileT::JobCompileT; + void Process() override; }; - /// @brief Uic a file job + /// @brief Generate mocs_compilation.cpp /// - class JobUicT : public JobT + class JobMocsCompilationT : public JobFenceT { - public: - JobUicT(std::string sourceFile, std::string includerFile, - std::string includeString) - : SourceFile(std::move(sourceFile)) - , IncluderFile(std::move(includerFile)) - , IncludeString(std::move(includeString)) - { - } - private: void Process() override; - bool UpdateRequired(); - void GenerateUic(); - - public: - std::string SourceFile; - std::string IncluderFile; - std::string IncludeString; - std::string BuildFile; }; /// @brief The last job @@ -346,39 +518,37 @@ public: }; // -- Const settings interface - const BaseSettingsT& Base() const { return this->Base_; } - const MocSettingsT& Moc() const { return this->Moc_; } - const UicSettingsT& Uic() const { return this->Uic_; } + BaseSettingsT const& BaseConst() const { return this->BaseConst_; } + BaseEvalT& BaseEval() { return this->BaseEval_; } + MocSettingsT const& MocConst() const { return this->MocConst_; } + MocEvalT& MocEval() { return this->MocEval_; } + UicSettingsT const& UicConst() const { return this->UicConst_; } + UicEvalT& UicEval() { return this->UicEval_; } // -- Parallel job processing interface cmWorkerPool& WorkerPool() { return WorkerPool_; } void AbortError() { Abort(true); } void AbortSuccess() { Abort(false); } - bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle); - bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle); - // -- Mocs compilation include file updated flag - void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); } - bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); } - - // -- Mocs compilation file register - std::string ParallelMocAutoRegister(std::string const& baseName); - bool ParallelMocIncluded(std::string const& sourceFile); - std::set const& MocAutoFiles() const - { - return this->MocAutoFiles_; - } + // -- Utility + std::string AbsoluteBuildPath(std::string const& relativePath) const; + std::string AbsoluteIncludePath(std::string const& relativePath) const; + template + void CreateParseJobs(SourceFileMapT const& sourceMap); private: // -- Utility accessors - Logger& Log() { return Logger_; } - FileSystem& FileSys() { return FileSys_; } + Logger const& Log() const { return Logger_; } // -- Abstract processing interface bool Init(cmMakefile* makefile) override; + void InitJobs(); bool Process() override; // -- Settings file void SettingsFileRead(); bool SettingsFileWrite(); + // -- Parse cache + void ParseCacheRead(); + bool ParseCacheWrite(); // -- Thread processing void Abort(bool error); // -- Generation @@ -387,25 +557,18 @@ private: private: // -- Utility Logger Logger_; - FileSystem FileSys_; // -- Settings - BaseSettingsT Base_; - MocSettingsT Moc_; - UicSettingsT Uic_; - // -- Moc meta - std::mutex MocMetaMutex_; - std::set MocIncludedFiles_; - IncludesMap MocIncludes_; - std::set MocAutoFiles_; - std::atomic MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false); - // -- Uic meta - std::mutex UicMetaMutex_; - IncludesMap UicIncludes_; + BaseSettingsT BaseConst_; + BaseEvalT BaseEval_; + MocSettingsT MocConst_; + MocEvalT MocEval_; + UicSettingsT UicConst_; + UicEvalT UicEval_; // -- Settings file std::string SettingsFile_; std::string SettingsStringMoc_; std::string SettingsStringUic_; - // -- Thread pool and job queue + // -- Worker thread pool std::atomic JobError_ = ATOMIC_VAR_INIT(false); cmWorkerPool WorkerPool_; }; diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx index e58324d..bb40c39 100644 --- a/Source/cmQtAutoRcc.cxx +++ b/Source/cmQtAutoRcc.cxx @@ -57,7 +57,7 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile) } // - Configurations - Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); + Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); // - Directories diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index 8dc9179..01c3fb9 100644 --- a/Source/cmQtAutoRcc.h +++ b/Source/cmQtAutoRcc.h @@ -26,7 +26,7 @@ public: private: // -- Utility - Logger& Log() { return Logger_; } + Logger const& Log() const { return Logger_; } bool IsMultiConfig() const { return MultiConfig_; } std::string MultiConfigOutput() const; -- cgit v0.12