From a3f062091f488237c0151f3f4753e0668f37c60d Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Fri, 12 Apr 2019 10:56:08 +0200 Subject: Autogen: Rename `cmQtAutoGeneratorMocUic` class to `cmQtAutoMocUic` The class name `cmQtAutoGeneratorMocUic` is long and cumbersome. This renames it to `cmQtAutoMocUic`. --- Source/CMakeLists.txt | 4 +- Source/cmQtAutoGeneratorMocUic.cxx | 1752 ------------------------------------ Source/cmQtAutoGeneratorMocUic.h | 413 --------- Source/cmQtAutoMocUic.cxx | 1747 +++++++++++++++++++++++++++++++++++ Source/cmQtAutoMocUic.h | 413 +++++++++ Source/cmcmd.cxx | 4 +- 6 files changed, 2164 insertions(+), 2169 deletions(-) delete mode 100644 Source/cmQtAutoGeneratorMocUic.cxx delete mode 100644 Source/cmQtAutoGeneratorMocUic.h create mode 100644 Source/cmQtAutoMocUic.cxx create mode 100644 Source/cmQtAutoMocUic.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index fcea2e3..49f237f 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -350,8 +350,8 @@ set(SRCS cmQtAutoGenGlobalInitializer.h cmQtAutoGenInitializer.cxx cmQtAutoGenInitializer.h - cmQtAutoGeneratorMocUic.cxx - cmQtAutoGeneratorMocUic.h + cmQtAutoMocUic.cxx + cmQtAutoMocUic.h cmQtAutoRcc.cxx cmQtAutoRcc.h cmRST.cxx diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx deleted file mode 100644 index 80684b6..0000000 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ /dev/null @@ -1,1752 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmQtAutoGeneratorMocUic.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cmAlgorithms.h" -#include "cmCryptoHash.h" -#include "cmMakefile.h" -#include "cmQtAutoGen.h" -#include "cmSystemTools.h" -#include "cmake.h" - -#if defined(__APPLE__) -# include -#endif - -// -- Class methods - -std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( - std::string const& relativePath) const -{ - return FileSys->CollapseFullPath(relativePath, AutogenBuildDir); -} - -/** - * @brief Tries to find the header file to the given file base path by - * appending different header extensions - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( - std::string& header, std::string const& testBasePath) const -{ - for (std::string const& ext : HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (FileSys->FileExists(testFilePath)) { - header = testFilePath; - return true; - } - } - return false; -} - -bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( - std::string const& fileName) const -{ - return (!Enabled || (SkipList.find(fileName) != SkipList.end())); -} - -/** - * @brief Returns the first relevant Qt macro name found in the given C++ code - * @return The name of the Qt macro or an empty string - */ -std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro( - std::string const& content) const -{ - for (KeyExpT const& filter : MacroFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (content.find(filter.Key) != std::string::npos) { - cmsys::RegularExpressionMatch match; - if (filter.Exp.find(content.c_str(), match)) { - // Return macro name on demand - return filter.Key; - } - } - } - return std::string(); -} - -std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const -{ - std::string res; - const auto itB = MacroFilters.cbegin(); - const auto itE = MacroFilters.cend(); - const auto itL = itE - 1; - auto itC = itB; - for (; itC != itE; ++itC) { - // Separator - if (itC != itB) { - if (itC != itL) { - res += ", "; - } else { - res += " or "; - } - } - // Key - res += itC->Key; - } - return res; -} - -std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( - std::string const& sourcePath, std::string const& includeString) const -{ - // Search in vicinity of the source - { - std::string testPath = sourcePath; - testPath += includeString; - if (FileSys->FileExists(testPath)) { - return FileSys->GetRealPath(testPath); - } - } - // Search in include directories - for (std::string const& path : IncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeString; - if (FileSys->FileExists(fullPath)) { - return FileSys->GetRealPath(fullPath); - } - } - // Return empty string - return std::string(); -} - -void cmQtAutoGeneratorMocUic::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(); - } - } - } - } -} - -bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped( - std::string const& fileName) const -{ - return (!Enabled || (SkipList.find(fileName) != SkipList.end())); -} - -void cmQtAutoGeneratorMocUic::JobT::LogError(GenT genType, - std::string const& message) const -{ - Gen()->AbortError(); - Gen()->Log().Error(genType, message); -} - -void cmQtAutoGeneratorMocUic::JobT::LogFileError( - GenT genType, std::string const& filename, std::string const& message) const -{ - Gen()->AbortError(); - Gen()->Log().ErrorFile(genType, filename, message); -} - -void cmQtAutoGeneratorMocUic::JobT::LogCommandError( - GenT genType, std::string const& message, - std::vector const& command, std::string const& output) const -{ - Gen()->AbortError(); - Gen()->Log().ErrorCommand(genType, message, command, output); -} - -bool cmQtAutoGeneratorMocUic::JobT::RunProcess( - GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector const& command) -{ - // Log command - if (Log().Verbose()) { - std::string msg = "Running command:\n"; - msg += QuotedCommand(command); - msg += '\n'; - Log().Info(genType, msg); - } - return cmWorkerPool::JobT::RunProcess(result, command, - Gen()->Base().AutogenBuildDir); -} - -void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process() -{ - // (Re)generate moc_predefs.h on demand - bool generate(false); - bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs)); - if (!fileExists) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(Gen()->Moc().PredefsFileRel); - reason += " because it doesn't exist"; - Log().Info(GenT::MOC, reason); - } - generate = true; - } else if (Gen()->Moc().SettingsChanged) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(Gen()->Moc().PredefsFileRel); - reason += " because the settings changed."; - Log().Info(GenT::MOC, reason); - } - generate = true; - } - if (generate) { - cmWorkerPool::ProcessResultT result; - { - // Compose command - std::vector cmd = Gen()->Moc().PredefsCmd; - // Add includes - cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(), - Gen()->Moc().Includes.end()); - // Add definitions - for (std::string const& def : Gen()->Moc().Definitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!RunProcess(GenT::MOC, result, cmd)) { - std::string emsg = "The content generation command for "; - emsg += Quoted(Gen()->Moc().PredefsFileRel); - emsg += " failed.\n"; - emsg += result.ErrorMessage; - LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); - } - } - - // (Re)write predefs file only on demand - if (!result.error()) { - if (!fileExists || - FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) { - if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) { - // Success - } else { - std::string emsg = "Writing "; - emsg += Quoted(Gen()->Moc().PredefsFileRel); - emsg += " failed."; - LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg); - } - } else { - // Touch to update the time stamp - if (Log().Verbose()) { - std::string msg = "Touching "; - msg += Quoted(Gen()->Moc().PredefsFileRel); - msg += "."; - Log().Info(GenT::MOC, msg); - } - FileSys().Touch(Gen()->Moc().PredefsFileAbs); - } - } - } -} - -void cmQtAutoGeneratorMocUic::JobParseT::Process() -{ - if (AutoMoc && Header) { - // Don't parse header for moc if the file is included by a source already - if (Gen()->ParallelMocIncluded(FileName)) { - AutoMoc = false; - } - } - - if (AutoMoc || AutoUic) { - std::string error; - MetaT meta; - if (FileSys().FileRead(meta.Content, FileName, &error)) { - if (!meta.Content.empty()) { - meta.FileDir = FileSys().SubDirPrefix(FileName); - meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName); - - bool success = true; - if (AutoMoc) { - if (Header) { - success = ParseMocHeader(meta); - } else { - success = ParseMocSource(meta); - } - } - if (AutoUic && success) { - ParseUic(meta); - } - } else { - Log().WarningFile(GenT::GEN, FileName, "The source file is empty"); - } - } else { - LogFileError(GenT::GEN, FileName, "Could not read the file: " + error); - } - } -} - -bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(MetaT const& meta) -{ - struct JobPre - { - bool self; // source file is self - bool underscore; // "moc_" style include - std::string SourceFile; - std::string IncludeString; - }; - - struct MocInclude - { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; - - // Check if this source file contains a relevant macro - std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content); - - // Extract moc includes from file - std::deque mocIncsUsc; - std::deque mocIncsDot; - { - if (meta.Content.find("moc") != std::string::npos) { - const char* contentChars = meta.Content.c_str(); - cmsys::RegularExpressionMatch match; - while (Gen()->Moc().RegExpInclude.find(contentChars, match)) { - std::string incString = match.match(2); - std::string incDir(FileSys().SubDirPrefix(incString)); - std::string incBase = - FileSys().GetFilenameWithoutLastExtension(incString); - if (cmHasLiteralPrefix(incBase, "moc_")) { - // moc_.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(); - } - } - } - - // Check if there is anything to do - if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) { - return true; - } - - bool ownDotMocIncluded = false; - bool ownMocUscIncluded = false; - std::deque jobs; - - // 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; - } - // Register moc job - const bool ownMoc = (mocInc.Base == meta.FileBase); - jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc }); - // Store meta information for relaxed mode - if (ownMoc) { - ownMocUscIncluded = true; - } - } else { - { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", but the header "; - emsg += Quoted(MocStringHeaders(mocInc.Base)); - emsg += " could not be found."; - LogFileError(GenT::MOC, FileName, emsg); - } - return false; - } - } - - // Process .moc includes - for (const MocInclude& mocInc : mocIncsDot) { - const bool ownMoc = (mocInc.Base == meta.FileBase); - if (Gen()->Moc().RelaxedMode) { - // Relaxed mode - if (!ownMacro.empty() && ownMoc) { - // Add self - jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning. - // This is for KDE4 compatibility - std::string const header = - MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (Gen()->Moc().skipped(header)) { - continue; - } - // Register moc job - jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc }); - if (ownMacro.empty()) { - if (ownMoc) { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += Gen()->Moc().MacrosString(); - emsg += " macro.\nRunning moc on\n "; - emsg += Quoted(header); - emsg += "!\nBetter include "; - emsg += Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - Log().WarningFile(GenT::MOC, FileName, emsg); - } else { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += " instead of "; - emsg += Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += ".\nRunning moc on\n "; - emsg += Quoted(header); - emsg += "!\nBetter include "; - emsg += Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - Log().WarningFile(GenT::MOC, FileName, emsg); - } - } - } else { - { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " - "matching header "; - emsg += Quoted(MocStringHeaders(mocInc.Base)); - emsg += " could not be found."; - LogFileError(GenT::MOC, FileName, emsg); - } - return false; - } - } - } else { - // Strict mode - if (ownMoc) { - // Include self - jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (ownMacro.empty()) { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += Gen()->Moc().MacrosString(); - emsg += " macro."; - Log().WarningFile(GenT::MOC, FileName, emsg); - } - } else { - // Don't allow .moc include other than self in strict mode - { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nThis is not supported. Include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += " to run moc on this source file."; - LogFileError(GenT::MOC, FileName, emsg); - } - return false; - } - } - } - - if (!ownMacro.empty() && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a - // Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. - // This is for KDE4 compatibility. - if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) { - JobPre uscJobPre; - // Remove underscore job request - { - auto itC = jobs.begin(); - auto itE = jobs.end(); - for (; itC != itE; ++itC) { - JobPre& job(*itC); - if (job.self && job.underscore) { - uscJobPre = std::move(job); - jobs.erase(itC); - break; - } - } - } - // Issue a warning - { - std::string emsg = "The file contains a "; - emsg += ownMacro; - emsg += " macro, but does not include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += ". Instead it includes "; - emsg += Quoted(uscJobPre.IncludeString); - emsg += ".\nRunning moc on\n "; - emsg += Quoted(FileName); - emsg += "!\nBetter include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - Log().WarningFile(GenT::MOC, FileName, emsg); - } - // Add own source job - jobs.emplace_back( - JobPre{ true, false, FileName, uscJobPre.IncludeString }); - } else { - // Otherwise always error out since it will not compile. - { - std::string emsg = "The file contains a "; - emsg += ownMacro; - emsg += " macro, but does not include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += "!\nConsider to\n - add #include \""; - emsg += meta.FileBase; - emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - LogFileError(GenT::MOC, FileName, emsg); - } - return false; - } - } - - // Convert pre jobs to actual jobs - for (JobPre& jobPre : jobs) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique( - 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; - } - if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) { - return false; - } - } - return true; -} - -bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(MetaT const& meta) -{ - bool success = true; - std::string const macroName = Gen()->Moc().FindMacro(meta.Content); - if (!macroName.empty()) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique( - 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; - } - success = Gen()->ParallelJobPushMoc(std::move(jobHandle)); - } - return success; -} - -std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders( - std::string const& fileBase) const -{ - std::string res = fileBase; - res += ".{"; - res += cmJoin(Gen()->Base().HeaderExtensions, ","); - res += "}"; - return res; -} - -std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader( - std::string const& includerDir, std::string const& includeBase) -{ - std::string header; - // Search in vicinity of the source - if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) { - // Search in include directories - for (std::string const& path : Gen()->Moc().IncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeBase; - if (Gen()->Base().FindHeader(header, fullPath)) { - break; - } - } - } - // Sanitize - if (!header.empty()) { - header = FileSys().GetRealPath(header); - } - return header; -} - -bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(MetaT const& meta) -{ - bool success = true; - if (meta.Content.find("ui_") != std::string::npos) { - const char* contentChars = meta.Content.c_str(); - cmsys::RegularExpressionMatch match; - while (Gen()->Uic().RegExpInclude.find(contentChars, match)) { - if (!ParseUicInclude(meta, match.match(2))) { - success = false; - break; - } - contentChars += match.end(); - } - } - return success; -} - -bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( - MetaT const& meta, std::string&& includeString) -{ - bool success = false; - std::string uiInputFile = UicFindIncludedFile(meta, includeString); - if (!uiInputFile.empty()) { - if (!Gen()->Uic().skipped(uiInputFile)) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique( - std::move(uiInputFile), FileName, std::move(includeString)); - success = Gen()->ParallelJobPushUic(std::move(jobHandle)); - } else { - // A skipped file is successful - success = true; - } - } - return success; -} - -std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( - MetaT const& meta, std::string const& includeString) -{ - std::string res; - std::string searchFile = - FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; - // Collect search paths list - std::deque testFiles; - { - std::string const searchPath = FileSys().SubDirPrefix(includeString); - - std::string searchFileFull; - if (!searchPath.empty()) { - searchFileFull = searchPath; - searchFileFull += searchFile; - } - // Vicinity of the source - { - std::string const sourcePath = meta.FileDir; - testFiles.push_back(sourcePath + searchFile); - if (!searchPath.empty()) { - testFiles.push_back(sourcePath + searchFileFull); - } - } - // AUTOUIC search paths - if (!Gen()->Uic().SearchPaths.empty()) { - for (std::string const& sPath : Gen()->Uic().SearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); - } - if (!searchPath.empty()) { - for (std::string const& sPath : Gen()->Uic().SearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); - } - } - } - } - - // Search for the .ui file! - for (std::string const& testFile : testFiles) { - if (FileSys().FileExists(testFile)) { - res = FileSys().GetRealPath(testFile); - break; - } - } - - // Log error - if (res.empty()) { - std::string emsg = "Could not find "; - emsg += Quoted(searchFile); - emsg += " in\n"; - for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += Quoted(testFile); - emsg += "\n"; - } - LogFileError(GenT::UIC, FileName, emsg); - } - - return res; -} - -void cmQtAutoGeneratorMocUic::JobPostParseT::Process() -{ - if (Gen()->Moc().Enabled) { - // Add mocs compilations fence job - Gen()->WorkerPool().EmplaceJob(); - } - // Add finish job - Gen()->WorkerPool().EmplaceJob(); -} - -void cmQtAutoGeneratorMocUic::JobMocsCompilationT::Process() -{ - // Compose mocs compilation file content - std::string content = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (Gen()->MocAutoFiles().empty()) { - // Placeholder content - content += "// No files found that require moc or the moc files are " - "included\n"; - content += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - char const sbeg = Gen()->Base().MultiConfig ? '<' : '"'; - char const send = Gen()->Base().MultiConfig ? '>' : '"'; - for (std::string const& mocfile : Gen()->MocAutoFiles()) { - content += "#include "; - content += sbeg; - content += mocfile; - content += send; - content += '\n'; - } - } - - std::string const& compAbs = Gen()->Moc().CompFileAbs; - if (FileSys().FileDiffers(compAbs, content)) { - // Actually write mocs compilation file - if (Log().Verbose()) { - Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); - } - if (!FileSys().FileWrite(compAbs, content)) { - LogFileError(GenT::MOC, compAbs, - "mocs compilation file writing failed."); - } - } else if (Gen()->MocAutoFileUpdated()) { - // Only touch mocs compilation file - if (Log().Verbose()) { - Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); - } - FileSys().Touch(compAbs); - } -} - -void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies( - std::string const& content) -{ - Gen()->Moc().FindDependencies(content, Depends); - DependsValid = true; -} - -void cmQtAutoGeneratorMocUic::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 cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired() -{ - bool const verbose = Log().Verbose(); - - // Test if the build file exists - if (!FileSys().FileExists(BuildFile)) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from its source file "; - reason += Quoted(SourceFile); - reason += " because it doesn't exist"; - Log().Info(GenT::MOC, reason); - } - return true; - } - - // Test if any setting changed - if (Gen()->Moc().SettingsChanged) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from "; - reason += Quoted(SourceFile); - reason += " because the MOC settings changed"; - Log().Info(GenT::MOC, reason); - } - return true; - } - - // Test if the moc_predefs file is newer - if (!Gen()->Moc().PredefsFileAbs.empty()) { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(BuildFile, - Gen()->Moc().PredefsFileAbs, &error); - if (!isOlder && !error.empty()) { - LogError(GenT::MOC, error); - return false; - } - } - if (isOlder) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " because it's older than: "; - reason += Quoted(Gen()->Moc().PredefsFileAbs); - Log().Info(GenT::MOC, reason); - } - return true; - } - } - - // Test if the source file is newer - { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); - if (!isOlder && !error.empty()) { - LogError(GenT::MOC, error); - return false; - } - } - if (isOlder) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " because it's older than its source file "; - reason += Quoted(SourceFile); - Log().Info(GenT::MOC, reason); - } - return true; - } - } - - // Test if a dependency file is newer - { - // Read dependencies on demand - if (!DependsValid) { - std::string content; - { - std::string error; - if (!FileSys().FileRead(content, SourceFile, &error)) { - std::string emsg = "Could not read file\n "; - emsg += Quoted(SourceFile); - emsg += "\nrequired by moc include "; - emsg += Quoted(IncludeString); - emsg += " in\n "; - emsg += Quoted(IncluderFile); - emsg += ".\n"; - emsg += error; - LogError(GenT::MOC, emsg); - return false; - } - } - FindDependencies(content); - } - // Check dependency timestamps - std::string error; - std::string sourceDir = FileSys().SubDirPrefix(SourceFile); - for (std::string const& depFileRel : Depends) { - std::string depFileAbs = - Gen()->Moc().FindIncludedFile(sourceDir, depFileRel); - if (!depFileAbs.empty()) { - if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from "; - reason += Quoted(SourceFile); - reason += " because it is older than it's dependency file "; - reason += Quoted(depFileAbs); - Log().Info(GenT::MOC, reason); - } - return true; - } - if (!error.empty()) { - LogError(GenT::MOC, error); - return false; - } - } else { - std::string message = "Could not find dependency file "; - message += Quoted(depFileRel); - Log().WarningFile(GenT::MOC, SourceFile, message); - } - } - } - - return false; -} - -void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc() -{ - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(BuildFile)) { - LogFileError(GenT::MOC, BuildFile, "Could not create parent directory."); - return; - } - { - // Compose moc command - std::vector cmd; - cmd.push_back(Gen()->Moc().Executable); - // Add options - cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(), - Gen()->Moc().AllOptions.end()); - // Add predefs include - if (!Gen()->Moc().PredefsFileAbs.empty()) { - cmd.emplace_back("--include"); - cmd.push_back(Gen()->Moc().PredefsFileAbs); - } - cmd.emplace_back("-o"); - cmd.push_back(BuildFile); - cmd.push_back(SourceFile); - - // Execute moc command - cmWorkerPool::ProcessResultT result; - if (RunProcess(GenT::MOC, result, cmd)) { - // Moc command success - // Print moc output - if (!result.StdOut.empty()) { - Log().Info(GenT::MOC, result.StdOut); - } - // Notify the generator that a not included file changed (on demand) - if (IncludeString.empty()) { - Gen()->ParallelMocAutoUpdated(); - } - } else { - // Moc command failed - { - std::string emsg = "The moc process failed to compile\n "; - emsg += Quoted(SourceFile); - emsg += "\ninto\n "; - emsg += Quoted(BuildFile); - emsg += ".\n"; - emsg += result.ErrorMessage; - LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); - } - FileSys().FileRemove(BuildFile); - } - } -} - -void cmQtAutoGeneratorMocUic::JobUicT::Process() -{ - // Compute build file name - BuildFile = Gen()->Base().AutogenIncludeDir; - BuildFile += '/'; - BuildFile += IncludeString; - - if (UpdateRequired()) { - GenerateUic(); - } -} - -bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired() -{ - bool const verbose = Log().Verbose(); - - // Test if the build file exists - if (!FileSys().FileExists(BuildFile)) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from its source file "; - reason += Quoted(SourceFile); - reason += " because it doesn't exist"; - Log().Info(GenT::UIC, reason); - } - return true; - } - - // Test if the uic settings changed - if (Gen()->Uic().SettingsChanged) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from "; - reason += Quoted(SourceFile); - reason += " because the UIC settings changed"; - Log().Info(GenT::UIC, reason); - } - return true; - } - - // Test if the source file is newer - { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); - if (!isOlder && !error.empty()) { - LogError(GenT::UIC, error); - return false; - } - } - if (isOlder) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " because it's older than its source file "; - reason += Quoted(SourceFile); - Log().Info(GenT::UIC, reason); - } - return true; - } - } - - return false; -} - -void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic() -{ - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(BuildFile)) { - LogFileError(GenT::UIC, BuildFile, "Could not create parent directory."); - return; - } - { - // Compose uic command - std::vector 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()); - } - cmd.emplace_back("-o"); - cmd.emplace_back(BuildFile); - cmd.emplace_back(SourceFile); - - cmWorkerPool::ProcessResultT result; - if (RunProcess(GenT::UIC, result, cmd)) { - // Uic command success - // Print uic output - if (!result.StdOut.empty()) { - Log().Info(GenT::UIC, result.StdOut); - } - } else { - // Uic command failed - { - std::string emsg = "The uic process failed to compile\n "; - emsg += Quoted(SourceFile); - emsg += "\ninto\n "; - emsg += Quoted(BuildFile); - emsg += "\nincluded by\n "; - emsg += Quoted(IncluderFile); - emsg += ".\n"; - emsg += result.ErrorMessage; - LogCommandError(GenT::UIC, emsg, cmd, result.StdOut); - } - FileSys().FileRemove(BuildFile); - } - } -} - -void cmQtAutoGeneratorMocUic::JobFinishT::Process() -{ - Gen()->AbortSuccess(); -} - -cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() - : Base_(&FileSys()) - , Moc_(&FileSys()) -{ - // Precompile regular expressions - Moc_.RegExpInclude.compile( - "(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); -} - -cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() = default; - -bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) -{ - // -- Meta - Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); - - // Utility lambdas - auto InfoGet = [makefile](const char* key) { - return makefile->GetSafeDefinition(key); - }; - auto InfoGetBool = [makefile](const char* key) { - return makefile->IsOn(key); - }; - auto InfoGetList = [makefile](const char* key) -> std::vector { - std::vector list; - cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); - return list; - }; - auto InfoGetLists = - [makefile](const char* key) -> std::vector> { - std::vector> lists; - { - std::string const value = makefile->GetSafeDefinition(key); - std::string::size_type pos = 0; - while (pos < value.size()) { - std::string::size_type next = value.find(ListSep, pos); - std::string::size_type length = - (next != std::string::npos) ? next - pos : value.size() - pos; - // Remove enclosing braces - if (length >= 2) { - std::string::const_iterator itBeg = value.begin() + (pos + 1); - std::string::const_iterator itEnd = itBeg + (length - 2); - { - std::string subValue(itBeg, itEnd); - std::vector list; - cmSystemTools::ExpandListArgument(subValue, list); - lists.push_back(std::move(list)); - } - } - pos += length; - pos += ListSep.size(); - } - } - return lists; - }; - auto InfoGetConfig = [makefile, this](const char* key) -> std::string { - const char* valueConf = nullptr; - { - std::string keyConf = key; - keyConf += '_'; - keyConf += InfoConfig(); - valueConf = makefile->GetDefinition(keyConf); - } - if (valueConf == nullptr) { - return makefile->GetSafeDefinition(key); - } - return std::string(valueConf); - }; - auto InfoGetConfigList = - [&InfoGetConfig](const char* key) -> std::vector { - std::vector list; - cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); - return list; - }; - - // -- Read info file - if (!makefile->ReadListFile(InfoFile())) { - Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed"); - return false; - } - - // -- Meta - Log().RaiseVerbosity(InfoGet("AM_VERBOSITY")); - Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); - { - unsigned long num = Base_.NumThreads; - if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) { - num = std::max(num, 1); - num = std::min(num, ParallelMax); - Base_.NumThreads = static_cast(num); - } - } - - // - Files and directories - Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); - Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); - Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - Base_.IncludeProjectDirsBefore = - InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (Base_.AutogenBuildDir.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing"); - return false; - } - // include directory - Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); - if (Base_.AutogenIncludeDir.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), - "Autogen include directory missing"); - return false; - } - - // - Files - SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); - if (SettingsFile_.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing"); - return false; - } - - // - Qt environment - { - unsigned long qtv = Base_.QtVersionMajor; - if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(), - &qtv)) { - Base_.QtVersionMajor = static_cast(qtv); - } - } - - // - Moc - Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); - Moc_.Enabled = !Moc().Executable.empty(); - if (Moc().Enabled) { - for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { - Moc_.SkipList.insert(std::move(sfl)); - } - Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); - Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - Moc_.Options = InfoGetList("AM_MOC_OPTIONS"); - Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { - Moc_.MacroFilters.emplace_back( - item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); - } - { - auto pushFilter = [this](std::string const& key, std::string const& exp, - std::string& error) { - if (!key.empty()) { - if (!exp.empty()) { - Moc_.DependFilters.emplace_back(); - KeyExpT& filter(Moc_.DependFilters.back()); - if (filter.Exp.compile(exp)) { - filter.Key = key; - } else { - error = "Regular expression compiling failed"; - } - } else { - error = "Regular expression is empty"; - } - } else { - error = "Key is empty"; - } - if (!error.empty()) { - error = ("AUTOMOC_DEPEND_FILTERS: " + error); - error += "\n"; - error += " Key: "; - error += Quoted(key); - error += "\n"; - error += " Exp: "; - error += Quoted(exp); - error += "\n"; - } - }; - - std::string error; - // Insert default filter for Q_PLUGIN_METADATA - if (Base().QtVersionMajor != 4) { - pushFilter("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\"", - error); - } - // Insert user defined dependency filters - { - std::vector 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"); - 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(); - } - } - - // - Uic - Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); - Uic_.Enabled = !Uic().Executable.empty(); - if (Uic().Enabled) { - for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { - Uic_.SkipList.insert(std::move(sfl)); - } - Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); - { - auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); - auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); - // Compare list sizes - if (sources.size() != options.size()) { - std::ostringstream ost; - ost << "files/options lists sizes mismatch (" << sources.size() << "/" - << options.size() << ")"; - Log().ErrorFile(GenT::UIC, InfoFile(), ost.str()); - return false; - } - auto fitEnd = sources.cend(); - auto fit = sources.begin(); - auto oit = options.begin(); - while (fit != fitEnd) { - Uic_.Options[*fit] = std::move(*oit); - ++fit; - ++oit; - } - } - } - - // - Headers and sources - // Add sources - { - auto addSource = [this](std::string&& src, bool moc, bool uic) { - WorkerPool().EmplaceJob(std::move(src), moc, uic, false); - }; - for (std::string& src : InfoGetList("AM_SOURCES")) { - addSource(std::move(src), true, true); - } - if (Moc().Enabled) { - for (std::string& src : InfoGetList("AM_MOC_SOURCES")) { - addSource(std::move(src), true, false); - } - } - if (Uic().Enabled) { - for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { - addSource(std::move(src), false, true); - } - } - } - // Add Fence job - WorkerPool().EmplaceJob(); - // Add headers - { - auto addHeader = [this](std::string&& hdr, bool moc, bool uic) { - WorkerPool().EmplaceJob(std::move(hdr), moc, uic, true); - }; - for (std::string& hdr : InfoGetList("AM_HEADERS")) { - addHeader(std::move(hdr), true, true); - } - if (Moc().Enabled) { - for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) { - addHeader(std::move(hdr), true, false); - } - } - if (Uic().Enabled) { - for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { - addHeader(std::move(hdr), false, true); - } - } - } - // Addpost parse fence job - WorkerPool().EmplaceJob(); - - // Init derived information - // ------------------------ - - // Init file path checksum generator - FileSys().setupFilePathChecksum( - Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir, - Base().ProjectBinaryDir); - - // Moc variables - if (Moc().Enabled) { - // Mocs compilation file - Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp"); - - // Moc predefs file - if (!Moc_.PredefsCmd.empty()) { - Moc_.PredefsFileRel = "moc_predefs"; - if (Base_.MultiConfig) { - Moc_.PredefsFileRel += '_'; - Moc_.PredefsFileRel += InfoConfig(); - } - Moc_.PredefsFileRel += ".h"; - Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel); - } - - // Sort include directories on demand - if (Base().IncludeProjectDirsBefore) { - // Move strings to temporary list - std::list includes; - includes.insert(includes.end(), Moc().IncludePaths.begin(), - Moc().IncludePaths.end()); - Moc_.IncludePaths.clear(); - Moc_.IncludePaths.reserve(includes.size()); - // Append project directories only - { - std::array const movePaths = { - { &Base().ProjectBinaryDir, &Base().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); - it = includes.erase(it); - } else { - ++it; - } - } - } - } - // Append remaining directories - Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(), - includes.end()); - } - // Compose moc includes list - { - std::set frameworkPaths; - for (std::string const& path : Moc().IncludePaths) { - Moc_.Includes.push_back("-I" + path); - // Extract framework path - if (cmHasLiteralSuffix(path, ".framework/Headers")) { - // Go up twice to get to the framework root - std::vector pathComponents; - FileSys().SplitPath(path, pathComponents); - std::string frameworkPath = FileSys().JoinPath( - pathComponents.begin(), pathComponents.end() - 2); - frameworkPaths.insert(frameworkPath); - } - } - // Append framework includes - for (std::string const& path : frameworkPaths) { - Moc_.Includes.emplace_back("-F"); - Moc_.Includes.push_back(path); - } - } - // Setup single list with all options - { - // Add includes - Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(), - Moc().Includes.end()); - // Add definitions - for (std::string const& def : Moc().Definitions) { - Moc_.AllOptions.push_back("-D" + def); - } - // Add options - Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(), - Moc().Options.end()); - } - } - - return true; -} - -bool cmQtAutoGeneratorMocUic::Process() -{ - SettingsFileRead(); - if (!CreateDirectories()) { - return false; - } - - if (!WorkerPool_.Process(Base().NumThreads, this)) { - return false; - } - - if (JobError_) { - return false; - } - - return SettingsFileWrite(); -} - -void cmQtAutoGeneratorMocUic::SettingsFileRead() -{ - // Compose current settings strings - { - cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - std::string const sep(" ~~~ "); - if (Moc_.Enabled) { - std::string str; - str += Moc().Executable; - str += sep; - str += cmJoin(Moc().AllOptions, ";"); - str += sep; - str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE"; - str += sep; - str += cmJoin(Moc().PredefsCmd, ";"); - str += sep; - SettingsStringMoc_ = crypt.HashString(str); - } - if (Uic().Enabled) { - std::string str; - str += Uic().Executable; - str += sep; - str += cmJoin(Uic().TargetOptions, ";"); - for (const auto& item : Uic().Options) { - str += sep; - str += item.first; - str += sep; - str += cmJoin(item.second, ";"); - } - str += sep; - SettingsStringUic_ = crypt.HashString(str); - } - } - - // Read old settings and compare - { - std::string content; - if (FileSys().FileRead(content, SettingsFile_)) { - if (Moc().Enabled) { - if (SettingsStringMoc_ != SettingsFind(content, "moc")) { - Moc_.SettingsChanged = true; - } - } - if (Uic().Enabled) { - if (SettingsStringUic_ != SettingsFind(content, "uic")) { - Uic_.SettingsChanged = true; - } - } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (Moc().SettingsChanged || Uic().SettingsChanged) { - FileSys().FileRemove(SettingsFile_); - } - } else { - // Settings file read failed - if (Moc().Enabled) { - Moc_.SettingsChanged = true; - } - if (Uic().Enabled) { - Uic_.SettingsChanged = true; - } - } - } -} - -bool cmQtAutoGeneratorMocUic::SettingsFileWrite() -{ - // Only write if any setting changed - if (Moc().SettingsChanged || Uic().SettingsChanged) { - if (Log().Verbose()) { - Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); - } - // Compose settings file content - std::string content; - { - auto SettingAppend = [&content](const char* key, - std::string const& value) { - if (!value.empty()) { - content += key; - content += ':'; - content += value; - content += '\n'; - } - }; - SettingAppend("moc", SettingsStringMoc_); - SettingAppend("uic", SettingsStringUic_); - } - // Write settings file - std::string error; - if (!FileSys().FileWrite(SettingsFile_, content, &error)) { - Log().ErrorFile(GenT::GEN, SettingsFile_, - "Settings file writing failed. " + error); - // Remove old settings file to trigger a full rebuild on the next run - FileSys().FileRemove(SettingsFile_); - return false; - } - } - return true; -} - -bool cmQtAutoGeneratorMocUic::CreateDirectories() -{ - // Create AUTOGEN include directory - if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) { - Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir, - "Could not create directory."); - return false; - } - return true; -} - -// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be -// locked -void cmQtAutoGeneratorMocUic::Abort(bool error) -{ - if (error) { - JobError_.store(true); - } - WorkerPool_.Abort(); -} - -bool cmQtAutoGeneratorMocUic::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 cmQtAutoGeneratorMocUic::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 cmQtAutoGeneratorMocUic::ParallelMocIncluded( - std::string const& sourceFile) -{ - std::lock_guard guard(MocMetaMutex_); - return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); -} - -std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( - std::string const& baseName) -{ - 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; - } - } - } - } - return res; -} diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h deleted file mode 100644 index 4efc2c6..0000000 --- a/Source/cmQtAutoGeneratorMocUic.h +++ /dev/null @@ -1,413 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGeneratorMocUic_h -#define cmQtAutoGeneratorMocUic_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include "cmQtAutoGen.h" -#include "cmQtAutoGenerator.h" -#include "cmWorkerPool.h" -#include "cmsys/RegularExpression.hxx" - -#include -#include -#include -#include // IWYU pragma: keep -#include -#include -#include -#include -#include -#include - -class cmMakefile; - -// @brief AUTOMOC and AUTOUIC generator -class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator -{ -public: - cmQtAutoGeneratorMocUic(); - ~cmQtAutoGeneratorMocUic() override; - - cmQtAutoGeneratorMocUic(cmQtAutoGeneratorMocUic const&) = delete; - cmQtAutoGeneratorMocUic& operator=(cmQtAutoGeneratorMocUic const&) = delete; - -public: - // -- Types - typedef std::multimap> IncludesMap; - - /// @brief Search key plus regular expression pair - /// - struct KeyExpT - { - KeyExpT() = default; - - KeyExpT(const char* key, const char* exp) - : Key(key) - , Exp(exp) - { - } - - KeyExpT(std::string key, std::string const& exp) - : Key(std::move(key)) - , Exp(exp) - { - } - - std::string Key; - cmsys::RegularExpression Exp; - }; - - /// @brief Common settings - /// - class BaseSettingsT - { - public: - // -- Volatile methods - BaseSettingsT(FileSystem* fileSystem) - : MultiConfig(false) - , IncludeProjectDirsBefore(false) - , QtVersionMajor(4) - , NumThreads(1) - , FileSys(fileSystem) - { - } - - 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; - // - Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - std::string AutogenIncludeDir; - // - Files - std::vector HeaderExtensions; - // - File system - FileSystem* FileSys; - }; - - /// @brief Moc settings - /// - class MocSettingsT - { - public: - MocSettingsT(FileSystem* fileSys) - : FileSys(fileSys) - { - } - - 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; - std::string Executable; - std::string CompFileAbs; - std::string PredefsFileRel; - std::string PredefsFileAbs; - std::unordered_set SkipList; - std::vector IncludePaths; - std::vector Includes; - std::vector Definitions; - std::vector Options; - std::vector AllOptions; - std::vector PredefsCmd; - std::vector DependFilters; - std::vector MacroFilters; - cmsys::RegularExpression RegExpInclude; - // - File system - FileSystem* FileSys; - }; - - /// @brief Uic settings - /// - class UicSettingsT - { - public: - UicSettingsT() = default; - - UicSettingsT(UicSettingsT const&) = delete; - UicSettingsT& operator=(UicSettingsT const&) = delete; - - // -- Const methods - bool skipped(std::string const& fileName) const; - - // -- Attributes - bool Enabled = false; - bool SettingsChanged = false; - std::string Executable; - std::unordered_set SkipList; - std::vector TargetOptions; - std::map> Options; - std::vector SearchPaths; - cmsys::RegularExpression RegExpInclude; - }; - - /// @brief Abstract job class for concurrent job processing - /// - class JobT : public cmWorkerPool::JobT - { - protected: - /** - * @brief Protected default constructor - */ - JobT(bool fence = false) - : cmWorkerPool::JobT(fence) - { - } - - //! Get the generator. Only valid during Process() call! - cmQtAutoGeneratorMocUic* Gen() const - { - 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(); } - - // -- Error logging with automatic abort - void LogError(GenT genType, std::string const& message) const; - void LogFileError(GenT genType, std::string const& filename, - std::string const& message) const; - void LogCommandError(GenT genType, std::string const& message, - std::vector const& command, - std::string const& output) const; - - /** - * @brief Run an external process. Use only during Process() call! - */ - bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector const& command); - }; - - /// @brief Fence job utility class - /// - class JobFenceT : public JobT - { - public: - JobFenceT() - : JobT(true) - { - } - void Process() override{}; - }; - - /// @brief Generate moc_predefs.h - /// - class JobMocPredefsT : public JobT - { - private: - void Process() override; - }; - - /// @brief Parses a source file - /// - 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) - { - } - - private: - struct MetaT - { - std::string Content; - std::string FileDir; - std::string FileBase; - }; - - 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; - }; - - /// @brief Generates additional jobs after all files have been parsed - /// - class JobPostParseT : public JobFenceT - { - private: - void Process() override; - }; - - /// @brief Generate mocs_compilation.cpp - /// - class JobMocsCompilationT : public JobFenceT - { - private: - void Process() override; - }; - - /// @brief Moc a file job - /// - class JobMocT : 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)) - { - } - - void FindDependencies(std::string const& content); - - private: - void Process() override; - bool UpdateRequired(); - void GenerateMoc(); - - public: - std::string SourceFile; - std::string IncluderFile; - std::string IncludeString; - std::string BuildFile; - bool DependsValid = false; - std::set Depends; - }; - - /// @brief Uic a file job - /// - class JobUicT : public JobT - { - 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 - /// - class JobFinishT : public JobFenceT - { - private: - void Process() override; - }; - - // -- Const settings interface - const BaseSettingsT& Base() const { return this->Base_; } - const MocSettingsT& Moc() const { return this->Moc_; } - const UicSettingsT& Uic() const { return this->Uic_; } - - // -- 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_; - } - -private: - // -- Utility accessors - Logger& Log() { return Logger_; } - FileSystem& FileSys() { return FileSys_; } - // -- Abstract processing interface - bool Init(cmMakefile* makefile) override; - bool Process() override; - // -- Settings file - void SettingsFileRead(); - bool SettingsFileWrite(); - // -- Thread processing - void Abort(bool error); - // -- Generation - bool CreateDirectories(); - -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_; - // -- Settings file - std::string SettingsFile_; - std::string SettingsStringMoc_; - std::string SettingsStringUic_; - // -- Thread pool and job queue - std::atomic JobError_ = ATOMIC_VAR_INIT(false); - cmWorkerPool WorkerPool_; -}; - -#endif diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx new file mode 100644 index 0000000..75c5d8a --- /dev/null +++ b/Source/cmQtAutoMocUic.cxx @@ -0,0 +1,1747 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoMocUic.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmAlgorithms.h" +#include "cmCryptoHash.h" +#include "cmMakefile.h" +#include "cmQtAutoGen.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#if defined(__APPLE__) +# include +#endif + +// -- Class methods + +std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath( + std::string const& relativePath) const +{ + return FileSys->CollapseFullPath(relativePath, AutogenBuildDir); +} + +/** + * @brief Tries to find the header file to the given file base path by + * appending different header extensions + * @return True on success + */ +bool cmQtAutoMocUic::BaseSettingsT::FindHeader( + std::string& header, std::string const& testBasePath) const +{ + for (std::string const& ext : HeaderExtensions) { + std::string testFilePath(testBasePath); + testFilePath.push_back('.'); + testFilePath += ext; + if (FileSys->FileExists(testFilePath)) { + header = testFilePath; + return true; + } + } + return false; +} + +bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const +{ + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); +} + +/** + * @brief Returns the first relevant Qt macro name found in the given C++ code + * @return The name of the Qt macro or an empty string + */ +std::string cmQtAutoMocUic::MocSettingsT::FindMacro( + std::string const& content) const +{ + for (KeyExpT const& filter : MacroFilters) { + // Run a simple find string operation before the expensive + // regular expression check + if (content.find(filter.Key) != std::string::npos) { + cmsys::RegularExpressionMatch match; + if (filter.Exp.find(content.c_str(), match)) { + // Return macro name on demand + return filter.Key; + } + } + } + return std::string(); +} + +std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const +{ + std::string res; + const auto itB = MacroFilters.cbegin(); + const auto itE = MacroFilters.cend(); + const auto itL = itE - 1; + auto itC = itB; + for (; itC != itE; ++itC) { + // Separator + if (itC != itB) { + if (itC != itL) { + res += ", "; + } else { + res += " or "; + } + } + // Key + res += itC->Key; + } + return res; +} + +std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile( + std::string const& sourcePath, std::string const& includeString) const +{ + // Search in vicinity of the source + { + std::string testPath = sourcePath; + testPath += includeString; + if (FileSys->FileExists(testPath)) { + return FileSys->GetRealPath(testPath); + } + } + // Search in include directories + for (std::string const& path : IncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeString; + if (FileSys->FileExists(fullPath)) { + return FileSys->GetRealPath(fullPath); + } + } + // Return empty string + return std::string(); +} + +void cmQtAutoMocUic::MocSettingsT::FindDependencies( + std::string const& content, std::set& depends) const +{ + if (!DependFilters.empty() && !content.empty()) { + for (KeyExpT const& filter : DependFilters) { + // Run a simple find string check + if (content.find(filter.Key) != std::string::npos) { + // Run the expensive regular expression check loop + const char* contentChars = content.c_str(); + cmsys::RegularExpressionMatch match; + while (filter.Exp.find(contentChars, match)) { + { + std::string dep = match.match(1); + if (!dep.empty()) { + depends.emplace(std::move(dep)); + } + } + contentChars += match.end(); + } + } + } + } +} + +bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const +{ + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); +} + +void cmQtAutoMocUic::JobT::LogError(GenT genType, + std::string const& message) const +{ + Gen()->AbortError(); + Gen()->Log().Error(genType, message); +} + +void cmQtAutoMocUic::JobT::LogFileError(GenT genType, + std::string const& filename, + std::string const& message) const +{ + Gen()->AbortError(); + Gen()->Log().ErrorFile(genType, filename, message); +} + +void cmQtAutoMocUic::JobT::LogCommandError( + GenT genType, std::string const& message, + std::vector const& command, std::string const& output) const +{ + Gen()->AbortError(); + Gen()->Log().ErrorCommand(genType, message, command, output); +} + +bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, + cmWorkerPool::ProcessResultT& result, + std::vector const& command) +{ + // Log command + if (Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(command); + msg += '\n'; + Log().Info(genType, msg); + } + return cmWorkerPool::JobT::RunProcess(result, command, + Gen()->Base().AutogenBuildDir); +} + +void cmQtAutoMocUic::JobMocPredefsT::Process() +{ + // (Re)generate moc_predefs.h on demand + bool generate(false); + bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs)); + if (!fileExists) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(Gen()->Moc().PredefsFileRel); + reason += " because it doesn't exist"; + Log().Info(GenT::MOC, reason); + } + generate = true; + } else if (Gen()->Moc().SettingsChanged) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(Gen()->Moc().PredefsFileRel); + reason += " because the settings changed."; + Log().Info(GenT::MOC, reason); + } + generate = true; + } + if (generate) { + cmWorkerPool::ProcessResultT result; + { + // Compose command + std::vector cmd = Gen()->Moc().PredefsCmd; + // Add includes + cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(), + Gen()->Moc().Includes.end()); + // Add definitions + for (std::string const& def : Gen()->Moc().Definitions) { + cmd.push_back("-D" + def); + } + // Execute command + if (!RunProcess(GenT::MOC, result, cmd)) { + std::string emsg = "The content generation command for "; + emsg += Quoted(Gen()->Moc().PredefsFileRel); + emsg += " failed.\n"; + emsg += result.ErrorMessage; + LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); + } + } + + // (Re)write predefs file only on demand + if (!result.error()) { + if (!fileExists || + FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) { + if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) { + // Success + } else { + std::string emsg = "Writing "; + emsg += Quoted(Gen()->Moc().PredefsFileRel); + emsg += " failed."; + LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg); + } + } else { + // Touch to update the time stamp + if (Log().Verbose()) { + std::string msg = "Touching "; + msg += Quoted(Gen()->Moc().PredefsFileRel); + msg += "."; + Log().Info(GenT::MOC, msg); + } + FileSys().Touch(Gen()->Moc().PredefsFileAbs); + } + } + } +} + +void cmQtAutoMocUic::JobParseT::Process() +{ + if (AutoMoc && Header) { + // Don't parse header for moc if the file is included by a source already + if (Gen()->ParallelMocIncluded(FileName)) { + AutoMoc = false; + } + } + + if (AutoMoc || AutoUic) { + std::string error; + MetaT meta; + if (FileSys().FileRead(meta.Content, FileName, &error)) { + if (!meta.Content.empty()) { + meta.FileDir = FileSys().SubDirPrefix(FileName); + meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName); + + bool success = true; + if (AutoMoc) { + if (Header) { + success = ParseMocHeader(meta); + } else { + success = ParseMocSource(meta); + } + } + if (AutoUic && success) { + ParseUic(meta); + } + } else { + Log().WarningFile(GenT::GEN, FileName, "The source file is empty"); + } + } else { + LogFileError(GenT::GEN, FileName, "Could not read the file: " + error); + } + } +} + +bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta) +{ + struct JobPre + { + bool self; // source file is self + bool underscore; // "moc_" style include + std::string SourceFile; + std::string IncludeString; + }; + + struct MocInclude + { + std::string Inc; // full include string + std::string Dir; // include string directory + std::string Base; // include string file base + }; + + // Check if this source file contains a relevant macro + std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content); + + // Extract moc includes from file + std::deque mocIncsUsc; + std::deque mocIncsDot; + { + if (meta.Content.find("moc") != std::string::npos) { + const char* contentChars = meta.Content.c_str(); + cmsys::RegularExpressionMatch match; + while (Gen()->Moc().RegExpInclude.find(contentChars, match)) { + std::string incString = match.match(2); + std::string incDir(FileSys().SubDirPrefix(incString)); + std::string incBase = + FileSys().GetFilenameWithoutLastExtension(incString); + if (cmHasLiteralPrefix(incBase, "moc_")) { + // moc_.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(); + } + } + } + + // Check if there is anything to do + if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) { + return true; + } + + bool ownDotMocIncluded = false; + bool ownMocUscIncluded = false; + std::deque jobs; + + // 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; + } + // Register moc job + const bool ownMoc = (mocInc.Base == meta.FileBase); + jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc }); + // Store meta information for relaxed mode + if (ownMoc) { + ownMocUscIncluded = true; + } + } else { + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but the header "; + emsg += Quoted(MocStringHeaders(mocInc.Base)); + emsg += " could not be found."; + LogFileError(GenT::MOC, FileName, emsg); + } + return false; + } + } + + // Process .moc includes + for (const MocInclude& mocInc : mocIncsDot) { + const bool ownMoc = (mocInc.Base == meta.FileBase); + if (Gen()->Moc().RelaxedMode) { + // Relaxed mode + if (!ownMacro.empty() && ownMoc) { + // Add self + jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); + ownDotMocIncluded = true; + } else { + // In relaxed mode try to find a header instead but issue a warning. + // This is for KDE4 compatibility + std::string const header = + MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (Gen()->Moc().skipped(header)) { + continue; + } + // Register moc job + jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc }); + if (ownMacro.empty()) { + if (ownMoc) { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += Gen()->Moc().MacrosString(); + emsg += " macro.\nRunning moc on\n "; + emsg += Quoted(header); + emsg += "!\nBetter include "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for a compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + Log().WarningFile(GenT::MOC, FileName, emsg); + } else { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += " instead of "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += ".\nRunning moc on\n "; + emsg += Quoted(header); + emsg += "!\nBetter include "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + Log().WarningFile(GenT::MOC, FileName, emsg); + } + } + } else { + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " + "matching header "; + emsg += Quoted(MocStringHeaders(mocInc.Base)); + emsg += " could not be found."; + LogFileError(GenT::MOC, FileName, emsg); + } + return false; + } + } + } else { + // Strict mode + if (ownMoc) { + // Include self + jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); + ownDotMocIncluded = true; + // Accept but issue a warning if moc isn't required + if (ownMacro.empty()) { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += Gen()->Moc().MacrosString(); + emsg += " macro."; + Log().WarningFile(GenT::MOC, FileName, emsg); + } + } else { + // Don't allow .moc include other than self in strict mode + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += " to run moc on this source file."; + LogFileError(GenT::MOC, FileName, emsg); + } + return false; + } + } + } + + if (!ownMacro.empty() && !ownDotMocIncluded) { + // In this case, check whether the scanned file itself contains a + // Q_OBJECT. + // If this is the case, the moc_foo.cpp should probably be generated from + // foo.cpp instead of foo.h, because otherwise it won't build. + // But warn, since this is not how it is supposed to be used. + // This is for KDE4 compatibility. + if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) { + JobPre uscJobPre; + // Remove underscore job request + { + auto itC = jobs.begin(); + auto itE = jobs.end(); + for (; itC != itE; ++itC) { + JobPre& job(*itC); + if (job.self && job.underscore) { + uscJobPre = std::move(job); + jobs.erase(itC); + break; + } + } + } + // Issue a warning + { + std::string emsg = "The file contains a "; + emsg += ownMacro; + emsg += " macro, but does not include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += ". Instead it includes "; + emsg += Quoted(uscJobPre.IncludeString); + emsg += ".\nRunning moc on\n "; + emsg += Quoted(FileName); + emsg += "!\nBetter include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; + Log().WarningFile(GenT::MOC, FileName, emsg); + } + // Add own source job + jobs.emplace_back( + JobPre{ true, false, FileName, uscJobPre.IncludeString }); + } else { + // Otherwise always error out since it will not compile. + { + std::string emsg = "The file contains a "; + emsg += ownMacro; + emsg += " macro, but does not include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += meta.FileBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + LogFileError(GenT::MOC, FileName, emsg); + } + return false; + } + } + + // Convert pre jobs to actual jobs + for (JobPre& jobPre : jobs) { + cmWorkerPool::JobHandleT jobHandle = cm::make_unique( + 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; + } + if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) { + return false; + } + } + return true; +} + +bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta) +{ + bool success = true; + std::string const macroName = Gen()->Moc().FindMacro(meta.Content); + if (!macroName.empty()) { + cmWorkerPool::JobHandleT jobHandle = cm::make_unique( + 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; + } + success = Gen()->ParallelJobPushMoc(std::move(jobHandle)); + } + return success; +} + +std::string cmQtAutoMocUic::JobParseT::MocStringHeaders( + std::string const& fileBase) const +{ + std::string res = fileBase; + res += ".{"; + res += cmJoin(Gen()->Base().HeaderExtensions, ","); + res += "}"; + return res; +} + +std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) +{ + std::string header; + // Search in vicinity of the source + if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) { + // Search in include directories + for (std::string const& path : Gen()->Moc().IncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeBase; + if (Gen()->Base().FindHeader(header, fullPath)) { + break; + } + } + } + // Sanitize + if (!header.empty()) { + header = FileSys().GetRealPath(header); + } + return header; +} + +bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta) +{ + bool success = true; + if (meta.Content.find("ui_") != std::string::npos) { + const char* contentChars = meta.Content.c_str(); + cmsys::RegularExpressionMatch match; + while (Gen()->Uic().RegExpInclude.find(contentChars, match)) { + if (!ParseUicInclude(meta, match.match(2))) { + success = false; + break; + } + contentChars += match.end(); + } + } + return success; +} + +bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta, + std::string&& includeString) +{ + bool success = false; + std::string uiInputFile = UicFindIncludedFile(meta, includeString); + if (!uiInputFile.empty()) { + if (!Gen()->Uic().skipped(uiInputFile)) { + cmWorkerPool::JobHandleT jobHandle = cm::make_unique( + std::move(uiInputFile), FileName, std::move(includeString)); + success = Gen()->ParallelJobPushUic(std::move(jobHandle)); + } else { + // A skipped file is successful + success = true; + } + } + return success; +} + +std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( + MetaT const& meta, std::string const& includeString) +{ + std::string res; + std::string searchFile = + FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); + searchFile += ".ui"; + // Collect search paths list + std::deque testFiles; + { + std::string const searchPath = FileSys().SubDirPrefix(includeString); + + std::string searchFileFull; + if (!searchPath.empty()) { + searchFileFull = searchPath; + searchFileFull += searchFile; + } + // Vicinity of the source + { + std::string const sourcePath = meta.FileDir; + testFiles.push_back(sourcePath + searchFile); + if (!searchPath.empty()) { + testFiles.push_back(sourcePath + searchFileFull); + } + } + // AUTOUIC search paths + if (!Gen()->Uic().SearchPaths.empty()) { + for (std::string const& sPath : Gen()->Uic().SearchPaths) { + testFiles.push_back((sPath + "/").append(searchFile)); + } + if (!searchPath.empty()) { + for (std::string const& sPath : Gen()->Uic().SearchPaths) { + testFiles.push_back((sPath + "/").append(searchFileFull)); + } + } + } + } + + // Search for the .ui file! + for (std::string const& testFile : testFiles) { + if (FileSys().FileExists(testFile)) { + res = FileSys().GetRealPath(testFile); + break; + } + } + + // Log error + if (res.empty()) { + std::string emsg = "Could not find "; + emsg += Quoted(searchFile); + emsg += " in\n"; + for (std::string const& testFile : testFiles) { + emsg += " "; + emsg += Quoted(testFile); + emsg += "\n"; + } + LogFileError(GenT::UIC, FileName, emsg); + } + + return res; +} + +void cmQtAutoMocUic::JobPostParseT::Process() +{ + if (Gen()->Moc().Enabled) { + // Add mocs compilations fence job + Gen()->WorkerPool().EmplaceJob(); + } + // Add finish job + Gen()->WorkerPool().EmplaceJob(); +} + +void cmQtAutoMocUic::JobMocsCompilationT::Process() +{ + // Compose mocs compilation file content + std::string content = + "// This file is autogenerated. Changes will be overwritten.\n"; + if (Gen()->MocAutoFiles().empty()) { + // Placeholder content + content += "// No files found that require moc or the moc files are " + "included\n"; + content += "enum some_compilers { need_more_than_nothing };\n"; + } else { + // Valid content + char const sbeg = Gen()->Base().MultiConfig ? '<' : '"'; + char const send = Gen()->Base().MultiConfig ? '>' : '"'; + for (std::string const& mocfile : Gen()->MocAutoFiles()) { + content += "#include "; + content += sbeg; + content += mocfile; + content += send; + content += '\n'; + } + } + + std::string const& compAbs = Gen()->Moc().CompFileAbs; + if (FileSys().FileDiffers(compAbs, content)) { + // Actually write mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); + } + if (!FileSys().FileWrite(compAbs, content)) { + LogFileError(GenT::MOC, compAbs, + "mocs compilation file writing failed."); + } + } else if (Gen()->MocAutoFileUpdated()) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); + } + FileSys().Touch(compAbs); + } +} + +void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content) +{ + Gen()->Moc().FindDependencies(content, Depends); + DependsValid = true; +} + +void cmQtAutoMocUic::JobMocT::Process() +{ + // Compute build file name + if (!IncludeString.empty()) { + BuildFile = Gen()->Base().AutogenIncludeDir; + BuildFile += '/'; + BuildFile += IncludeString; + } else { + // Relative build path + std::string relPath = FileSys().GetFilePathChecksum(SourceFile); + relPath += "/moc_"; + relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile); + + // Register relative file path with duplication check + relPath = Gen()->ParallelMocAutoRegister(relPath); + + // Absolute build path + if (Gen()->Base().MultiConfig) { + BuildFile = Gen()->Base().AutogenIncludeDir; + BuildFile += '/'; + BuildFile += relPath; + } else { + BuildFile = Gen()->Base().AbsoluteBuildPath(relPath); + } + } + + if (UpdateRequired()) { + GenerateMoc(); + } +} + +bool cmQtAutoMocUic::JobMocT::UpdateRequired() +{ + bool const verbose = Log().Verbose(); + + // Test if the build file exists + if (!FileSys().FileExists(BuildFile)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from its source file "; + reason += Quoted(SourceFile); + reason += " because it doesn't exist"; + Log().Info(GenT::MOC, reason); + } + return true; + } + + // Test if any setting changed + if (Gen()->Moc().SettingsChanged) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because the MOC settings changed"; + Log().Info(GenT::MOC, reason); + } + return true; + } + + // Test if the moc_predefs file is newer + if (!Gen()->Moc().PredefsFileAbs.empty()) { + bool isOlder = false; + { + std::string error; + isOlder = FileSys().FileIsOlderThan(BuildFile, + Gen()->Moc().PredefsFileAbs, &error); + if (!isOlder && !error.empty()) { + LogError(GenT::MOC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than: "; + reason += Quoted(Gen()->Moc().PredefsFileAbs); + Log().Info(GenT::MOC, reason); + } + return true; + } + } + + // Test if the source file is newer + { + bool isOlder = false; + { + std::string error; + isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + if (!isOlder && !error.empty()) { + LogError(GenT::MOC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than its source file "; + reason += Quoted(SourceFile); + Log().Info(GenT::MOC, reason); + } + return true; + } + } + + // Test if a dependency file is newer + { + // Read dependencies on demand + if (!DependsValid) { + std::string content; + { + std::string error; + if (!FileSys().FileRead(content, SourceFile, &error)) { + std::string emsg = "Could not read file\n "; + emsg += Quoted(SourceFile); + emsg += "\nrequired by moc include "; + emsg += Quoted(IncludeString); + emsg += " in\n "; + emsg += Quoted(IncluderFile); + emsg += ".\n"; + emsg += error; + LogError(GenT::MOC, emsg); + return false; + } + } + FindDependencies(content); + } + // Check dependency timestamps + std::string error; + std::string sourceDir = FileSys().SubDirPrefix(SourceFile); + for (std::string const& depFileRel : Depends) { + std::string depFileAbs = + Gen()->Moc().FindIncludedFile(sourceDir, depFileRel); + if (!depFileAbs.empty()) { + if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because it is older than it's dependency file "; + reason += Quoted(depFileAbs); + Log().Info(GenT::MOC, reason); + } + return true; + } + if (!error.empty()) { + LogError(GenT::MOC, error); + return false; + } + } else { + std::string message = "Could not find dependency file "; + message += Quoted(depFileRel); + Log().WarningFile(GenT::MOC, SourceFile, message); + } + } + } + + return false; +} + +void cmQtAutoMocUic::JobMocT::GenerateMoc() +{ + // Make sure the parent directory exists + if (!FileSys().MakeParentDirectory(BuildFile)) { + LogFileError(GenT::MOC, BuildFile, "Could not create parent directory."); + return; + } + { + // Compose moc command + std::vector cmd; + cmd.push_back(Gen()->Moc().Executable); + // Add options + cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(), + Gen()->Moc().AllOptions.end()); + // Add predefs include + if (!Gen()->Moc().PredefsFileAbs.empty()) { + cmd.emplace_back("--include"); + cmd.push_back(Gen()->Moc().PredefsFileAbs); + } + cmd.emplace_back("-o"); + cmd.push_back(BuildFile); + cmd.push_back(SourceFile); + + // Execute moc command + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::MOC, result, cmd)) { + // Moc command success + // Print moc output + if (!result.StdOut.empty()) { + Log().Info(GenT::MOC, result.StdOut); + } + // Notify the generator that a not included file changed (on demand) + if (IncludeString.empty()) { + Gen()->ParallelMocAutoUpdated(); + } + } else { + // Moc command failed + { + std::string emsg = "The moc process failed to compile\n "; + emsg += Quoted(SourceFile); + emsg += "\ninto\n "; + emsg += Quoted(BuildFile); + emsg += ".\n"; + emsg += result.ErrorMessage; + LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); + } + FileSys().FileRemove(BuildFile); + } + } +} + +void cmQtAutoMocUic::JobUicT::Process() +{ + // Compute build file name + BuildFile = Gen()->Base().AutogenIncludeDir; + BuildFile += '/'; + BuildFile += IncludeString; + + if (UpdateRequired()) { + GenerateUic(); + } +} + +bool cmQtAutoMocUic::JobUicT::UpdateRequired() +{ + bool const verbose = Log().Verbose(); + + // Test if the build file exists + if (!FileSys().FileExists(BuildFile)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from its source file "; + reason += Quoted(SourceFile); + reason += " because it doesn't exist"; + Log().Info(GenT::UIC, reason); + } + return true; + } + + // Test if the uic settings changed + if (Gen()->Uic().SettingsChanged) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because the UIC settings changed"; + Log().Info(GenT::UIC, reason); + } + return true; + } + + // Test if the source file is newer + { + bool isOlder = false; + { + std::string error; + isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + if (!isOlder && !error.empty()) { + LogError(GenT::UIC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than its source file "; + reason += Quoted(SourceFile); + Log().Info(GenT::UIC, reason); + } + return true; + } + } + + return false; +} + +void cmQtAutoMocUic::JobUicT::GenerateUic() +{ + // Make sure the parent directory exists + if (!FileSys().MakeParentDirectory(BuildFile)) { + LogFileError(GenT::UIC, BuildFile, "Could not create parent directory."); + return; + } + { + // Compose uic command + std::vector 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()); + } + cmd.emplace_back("-o"); + cmd.emplace_back(BuildFile); + cmd.emplace_back(SourceFile); + + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::UIC, result, cmd)) { + // Uic command success + // Print uic output + if (!result.StdOut.empty()) { + Log().Info(GenT::UIC, result.StdOut); + } + } else { + // Uic command failed + { + std::string emsg = "The uic process failed to compile\n "; + emsg += Quoted(SourceFile); + emsg += "\ninto\n "; + emsg += Quoted(BuildFile); + emsg += "\nincluded by\n "; + emsg += Quoted(IncluderFile); + emsg += ".\n"; + emsg += result.ErrorMessage; + LogCommandError(GenT::UIC, emsg, cmd, result.StdOut); + } + FileSys().FileRemove(BuildFile); + } + } +} + +void cmQtAutoMocUic::JobFinishT::Process() +{ + Gen()->AbortSuccess(); +} + +cmQtAutoMocUic::cmQtAutoMocUic() + : Base_(&FileSys()) + , Moc_(&FileSys()) +{ + // Precompile regular expressions + Moc_.RegExpInclude.compile( + "(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); + Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); +} + +cmQtAutoMocUic::~cmQtAutoMocUic() = default; + +bool cmQtAutoMocUic::Init(cmMakefile* makefile) +{ + // -- Meta + Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); + + // Utility lambdas + auto InfoGet = [makefile](const char* key) { + return makefile->GetSafeDefinition(key); + }; + auto InfoGetBool = [makefile](const char* key) { + return makefile->IsOn(key); + }; + auto InfoGetList = [makefile](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); + return list; + }; + auto InfoGetLists = + [makefile](const char* key) -> std::vector> { + std::vector> lists; + { + std::string const value = makefile->GetSafeDefinition(key); + std::string::size_type pos = 0; + while (pos < value.size()) { + std::string::size_type next = value.find(ListSep, pos); + std::string::size_type length = + (next != std::string::npos) ? next - pos : value.size() - pos; + // Remove enclosing braces + if (length >= 2) { + std::string::const_iterator itBeg = value.begin() + (pos + 1); + std::string::const_iterator itEnd = itBeg + (length - 2); + { + std::string subValue(itBeg, itEnd); + std::vector list; + cmSystemTools::ExpandListArgument(subValue, list); + lists.push_back(std::move(list)); + } + } + pos += length; + pos += ListSep.size(); + } + } + return lists; + }; + auto InfoGetConfig = [makefile, this](const char* key) -> std::string { + const char* valueConf = nullptr; + { + std::string keyConf = key; + keyConf += '_'; + keyConf += InfoConfig(); + valueConf = makefile->GetDefinition(keyConf); + } + if (valueConf == nullptr) { + return makefile->GetSafeDefinition(key); + } + return std::string(valueConf); + }; + auto InfoGetConfigList = + [&InfoGetConfig](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); + return list; + }; + + // -- Read info file + if (!makefile->ReadListFile(InfoFile())) { + Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed"); + return false; + } + + // -- Meta + Log().RaiseVerbosity(InfoGet("AM_VERBOSITY")); + Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); + { + unsigned long num = Base_.NumThreads; + if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) { + num = std::max(num, 1); + num = std::min(num, ParallelMax); + Base_.NumThreads = static_cast(num); + } + } + + // - Files and directories + Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + Base_.IncludeProjectDirsBefore = + InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); + Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (Base_.AutogenBuildDir.empty()) { + Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing"); + return false; + } + // include directory + Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); + if (Base_.AutogenIncludeDir.empty()) { + Log().ErrorFile(GenT::GEN, InfoFile(), + "Autogen include directory missing"); + return false; + } + + // - Files + SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); + if (SettingsFile_.empty()) { + Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing"); + return false; + } + + // - Qt environment + { + unsigned long qtv = Base_.QtVersionMajor; + if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(), + &qtv)) { + Base_.QtVersionMajor = static_cast(qtv); + } + } + + // - Moc + Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); + Moc_.Enabled = !Moc().Executable.empty(); + if (Moc().Enabled) { + for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { + Moc_.SkipList.insert(std::move(sfl)); + } + Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); + Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + Moc_.Options = InfoGetList("AM_MOC_OPTIONS"); + Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); + for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { + Moc_.MacroFilters.emplace_back( + item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); + } + { + auto pushFilter = [this](std::string const& key, std::string const& exp, + std::string& error) { + if (!key.empty()) { + if (!exp.empty()) { + Moc_.DependFilters.emplace_back(); + KeyExpT& filter(Moc_.DependFilters.back()); + if (filter.Exp.compile(exp)) { + filter.Key = key; + } else { + error = "Regular expression compiling failed"; + } + } else { + error = "Regular expression is empty"; + } + } else { + error = "Key is empty"; + } + if (!error.empty()) { + error = ("AUTOMOC_DEPEND_FILTERS: " + error); + error += "\n"; + error += " Key: "; + error += Quoted(key); + error += "\n"; + error += " Exp: "; + error += Quoted(exp); + error += "\n"; + } + }; + + std::string error; + // Insert default filter for Q_PLUGIN_METADATA + if (Base().QtVersionMajor != 4) { + pushFilter("Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\"", + error); + } + // Insert user defined dependency filters + { + std::vector 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"); + 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(); + } + } + + // - Uic + Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); + Uic_.Enabled = !Uic().Executable.empty(); + if (Uic().Enabled) { + for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { + Uic_.SkipList.insert(std::move(sfl)); + } + Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + { + auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); + auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); + // Compare list sizes + if (sources.size() != options.size()) { + std::ostringstream ost; + ost << "files/options lists sizes mismatch (" << sources.size() << "/" + << options.size() << ")"; + Log().ErrorFile(GenT::UIC, InfoFile(), ost.str()); + return false; + } + auto fitEnd = sources.cend(); + auto fit = sources.begin(); + auto oit = options.begin(); + while (fit != fitEnd) { + Uic_.Options[*fit] = std::move(*oit); + ++fit; + ++oit; + } + } + } + + // - Headers and sources + // Add sources + { + auto addSource = [this](std::string&& src, bool moc, bool uic) { + WorkerPool().EmplaceJob(std::move(src), moc, uic, false); + }; + for (std::string& src : InfoGetList("AM_SOURCES")) { + addSource(std::move(src), true, true); + } + if (Moc().Enabled) { + for (std::string& src : InfoGetList("AM_MOC_SOURCES")) { + addSource(std::move(src), true, false); + } + } + if (Uic().Enabled) { + for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { + addSource(std::move(src), false, true); + } + } + } + // Add Fence job + WorkerPool().EmplaceJob(); + // Add headers + { + auto addHeader = [this](std::string&& hdr, bool moc, bool uic) { + WorkerPool().EmplaceJob(std::move(hdr), moc, uic, true); + }; + for (std::string& hdr : InfoGetList("AM_HEADERS")) { + addHeader(std::move(hdr), true, true); + } + if (Moc().Enabled) { + for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) { + addHeader(std::move(hdr), true, false); + } + } + if (Uic().Enabled) { + for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { + addHeader(std::move(hdr), false, true); + } + } + } + // Addpost parse fence job + WorkerPool().EmplaceJob(); + + // Init derived information + // ------------------------ + + // Init file path checksum generator + FileSys().setupFilePathChecksum( + Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir, + Base().ProjectBinaryDir); + + // Moc variables + if (Moc().Enabled) { + // Mocs compilation file + Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp"); + + // Moc predefs file + if (!Moc_.PredefsCmd.empty()) { + Moc_.PredefsFileRel = "moc_predefs"; + if (Base_.MultiConfig) { + Moc_.PredefsFileRel += '_'; + Moc_.PredefsFileRel += InfoConfig(); + } + Moc_.PredefsFileRel += ".h"; + Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel); + } + + // Sort include directories on demand + if (Base().IncludeProjectDirsBefore) { + // Move strings to temporary list + std::list includes; + includes.insert(includes.end(), Moc().IncludePaths.begin(), + Moc().IncludePaths.end()); + Moc_.IncludePaths.clear(); + Moc_.IncludePaths.reserve(includes.size()); + // Append project directories only + { + std::array const movePaths = { + { &Base().ProjectBinaryDir, &Base().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); + it = includes.erase(it); + } else { + ++it; + } + } + } + } + // Append remaining directories + Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(), + includes.end()); + } + // Compose moc includes list + { + std::set frameworkPaths; + for (std::string const& path : Moc().IncludePaths) { + Moc_.Includes.push_back("-I" + path); + // Extract framework path + if (cmHasLiteralSuffix(path, ".framework/Headers")) { + // Go up twice to get to the framework root + std::vector pathComponents; + FileSys().SplitPath(path, pathComponents); + std::string frameworkPath = FileSys().JoinPath( + pathComponents.begin(), pathComponents.end() - 2); + frameworkPaths.insert(frameworkPath); + } + } + // Append framework includes + for (std::string const& path : frameworkPaths) { + Moc_.Includes.emplace_back("-F"); + Moc_.Includes.push_back(path); + } + } + // Setup single list with all options + { + // Add includes + Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(), + Moc().Includes.end()); + // Add definitions + for (std::string const& def : Moc().Definitions) { + Moc_.AllOptions.push_back("-D" + def); + } + // Add options + Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(), + Moc().Options.end()); + } + } + + return true; +} + +bool cmQtAutoMocUic::Process() +{ + SettingsFileRead(); + if (!CreateDirectories()) { + return false; + } + + if (!WorkerPool_.Process(Base().NumThreads, this)) { + return false; + } + + if (JobError_) { + return false; + } + + return SettingsFileWrite(); +} + +void cmQtAutoMocUic::SettingsFileRead() +{ + // Compose current settings strings + { + cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); + std::string const sep(" ~~~ "); + if (Moc_.Enabled) { + std::string str; + str += Moc().Executable; + str += sep; + str += cmJoin(Moc().AllOptions, ";"); + str += sep; + str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE"; + str += sep; + str += cmJoin(Moc().PredefsCmd, ";"); + str += sep; + SettingsStringMoc_ = crypt.HashString(str); + } + if (Uic().Enabled) { + std::string str; + str += Uic().Executable; + str += sep; + str += cmJoin(Uic().TargetOptions, ";"); + for (const auto& item : Uic().Options) { + str += sep; + str += item.first; + str += sep; + str += cmJoin(item.second, ";"); + } + str += sep; + SettingsStringUic_ = crypt.HashString(str); + } + } + + // Read old settings and compare + { + std::string content; + if (FileSys().FileRead(content, SettingsFile_)) { + if (Moc().Enabled) { + if (SettingsStringMoc_ != SettingsFind(content, "moc")) { + Moc_.SettingsChanged = true; + } + } + if (Uic().Enabled) { + if (SettingsStringUic_ != SettingsFind(content, "uic")) { + Uic_.SettingsChanged = true; + } + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (Moc().SettingsChanged || Uic().SettingsChanged) { + FileSys().FileRemove(SettingsFile_); + } + } else { + // Settings file read failed + if (Moc().Enabled) { + Moc_.SettingsChanged = true; + } + if (Uic().Enabled) { + Uic_.SettingsChanged = true; + } + } + } +} + +bool cmQtAutoMocUic::SettingsFileWrite() +{ + // Only write if any setting changed + if (Moc().SettingsChanged || Uic().SettingsChanged) { + if (Log().Verbose()) { + Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); + } + // Compose settings file content + std::string content; + { + auto SettingAppend = [&content](const char* key, + std::string const& value) { + if (!value.empty()) { + content += key; + content += ':'; + content += value; + content += '\n'; + } + }; + SettingAppend("moc", SettingsStringMoc_); + SettingAppend("uic", SettingsStringUic_); + } + // Write settings file + std::string error; + if (!FileSys().FileWrite(SettingsFile_, content, &error)) { + Log().ErrorFile(GenT::GEN, SettingsFile_, + "Settings file writing failed. " + error); + // Remove old settings file to trigger a full rebuild on the next run + FileSys().FileRemove(SettingsFile_); + return false; + } + } + return true; +} + +bool cmQtAutoMocUic::CreateDirectories() +{ + // Create AUTOGEN include directory + if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) { + Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir, + "Could not create directory."); + return false; + } + return true; +} + +// Private method that requires cmQtAutoMocUic::JobsMutex_ to be +// locked +void cmQtAutoMocUic::Abort(bool error) +{ + if (error) { + JobError_.store(true); + } + WorkerPool_.Abort(); +} + +bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle) +{ + JobMocT const& mocJob(static_cast(*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::lock_guard guard(MocMetaMutex_); + return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); +} + +std::string cmQtAutoMocUic::ParallelMocAutoRegister( + std::string const& baseName) +{ + 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; + } + } + } + } + return res; +} diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h new file mode 100644 index 0000000..3902abb --- /dev/null +++ b/Source/cmQtAutoMocUic.h @@ -0,0 +1,413 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmQtAutoMocUic_h +#define cmQtAutoMocUic_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmQtAutoGen.h" +#include "cmQtAutoGenerator.h" +#include "cmWorkerPool.h" +#include "cmsys/RegularExpression.hxx" + +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include + +class cmMakefile; + +// @brief AUTOMOC and AUTOUIC generator +class cmQtAutoMocUic : public cmQtAutoGenerator +{ +public: + cmQtAutoMocUic(); + ~cmQtAutoMocUic() override; + + cmQtAutoMocUic(cmQtAutoMocUic const&) = delete; + cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete; + +public: + // -- Types + typedef std::multimap> IncludesMap; + + /// @brief Search key plus regular expression pair + /// + struct KeyExpT + { + KeyExpT() = default; + + KeyExpT(const char* key, const char* exp) + : Key(key) + , Exp(exp) + { + } + + KeyExpT(std::string key, std::string const& exp) + : Key(std::move(key)) + , Exp(exp) + { + } + + std::string Key; + cmsys::RegularExpression Exp; + }; + + /// @brief Common settings + /// + class BaseSettingsT + { + public: + // -- Volatile methods + BaseSettingsT(FileSystem* fileSystem) + : MultiConfig(false) + , IncludeProjectDirsBefore(false) + , QtVersionMajor(4) + , NumThreads(1) + , FileSys(fileSystem) + { + } + + 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; + // - Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildDir; + std::string AutogenIncludeDir; + // - Files + std::vector HeaderExtensions; + // - File system + FileSystem* FileSys; + }; + + /// @brief Moc settings + /// + class MocSettingsT + { + public: + MocSettingsT(FileSystem* fileSys) + : FileSys(fileSys) + { + } + + 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; + std::string Executable; + std::string CompFileAbs; + std::string PredefsFileRel; + std::string PredefsFileAbs; + std::unordered_set SkipList; + std::vector IncludePaths; + std::vector Includes; + std::vector Definitions; + std::vector Options; + std::vector AllOptions; + std::vector PredefsCmd; + std::vector DependFilters; + std::vector MacroFilters; + cmsys::RegularExpression RegExpInclude; + // - File system + FileSystem* FileSys; + }; + + /// @brief Uic settings + /// + class UicSettingsT + { + public: + UicSettingsT() = default; + + UicSettingsT(UicSettingsT const&) = delete; + UicSettingsT& operator=(UicSettingsT const&) = delete; + + // -- Const methods + bool skipped(std::string const& fileName) const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + std::string Executable; + std::unordered_set SkipList; + std::vector TargetOptions; + std::map> Options; + std::vector SearchPaths; + cmsys::RegularExpression RegExpInclude; + }; + + /// @brief Abstract job class for concurrent job processing + /// + class JobT : public cmWorkerPool::JobT + { + protected: + /** + * @brief Protected default constructor + */ + JobT(bool fence = false) + : cmWorkerPool::JobT(fence) + { + } + + //! Get the generator. Only valid during Process() call! + cmQtAutoMocUic* Gen() const + { + 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(); } + + // -- Error logging with automatic abort + void LogError(GenT genType, std::string const& message) const; + void LogFileError(GenT genType, std::string const& filename, + std::string const& message) const; + void LogCommandError(GenT genType, std::string const& message, + std::vector const& command, + std::string const& output) const; + + /** + * @brief Run an external process. Use only during Process() call! + */ + bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, + std::vector const& command); + }; + + /// @brief Fence job utility class + /// + class JobFenceT : public JobT + { + public: + JobFenceT() + : JobT(true) + { + } + void Process() override{}; + }; + + /// @brief Generate moc_predefs.h + /// + class JobMocPredefsT : public JobT + { + private: + void Process() override; + }; + + /// @brief Parses a source file + /// + 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) + { + } + + private: + struct MetaT + { + std::string Content; + std::string FileDir; + std::string FileBase; + }; + + 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; + }; + + /// @brief Generates additional jobs after all files have been parsed + /// + class JobPostParseT : public JobFenceT + { + private: + void Process() override; + }; + + /// @brief Generate mocs_compilation.cpp + /// + class JobMocsCompilationT : public JobFenceT + { + private: + void Process() override; + }; + + /// @brief Moc a file job + /// + class JobMocT : 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)) + { + } + + void FindDependencies(std::string const& content); + + private: + void Process() override; + bool UpdateRequired(); + void GenerateMoc(); + + public: + std::string SourceFile; + std::string IncluderFile; + std::string IncludeString; + std::string BuildFile; + bool DependsValid = false; + std::set Depends; + }; + + /// @brief Uic a file job + /// + class JobUicT : public JobT + { + 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 + /// + class JobFinishT : public JobFenceT + { + private: + void Process() override; + }; + + // -- Const settings interface + const BaseSettingsT& Base() const { return this->Base_; } + const MocSettingsT& Moc() const { return this->Moc_; } + const UicSettingsT& Uic() const { return this->Uic_; } + + // -- 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_; + } + +private: + // -- Utility accessors + Logger& Log() { return Logger_; } + FileSystem& FileSys() { return FileSys_; } + // -- Abstract processing interface + bool Init(cmMakefile* makefile) override; + bool Process() override; + // -- Settings file + void SettingsFileRead(); + bool SettingsFileWrite(); + // -- Thread processing + void Abort(bool error); + // -- Generation + bool CreateDirectories(); + +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_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsStringMoc_; + std::string SettingsStringUic_; + // -- Thread pool and job queue + std::atomic JobError_ = ATOMIC_VAR_INIT(false); + cmWorkerPool WorkerPool_; +}; + +#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index c18c256..3c75957 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -7,7 +7,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmQtAutoGeneratorMocUic.h" +#include "cmQtAutoMocUic.h" #include "cmQtAutoRcc.h" #include "cmRange.h" #include "cmState.h" @@ -1018,7 +1018,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args) #ifdef CMAKE_BUILD_WITH_CMAKE if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { - cmQtAutoGeneratorMocUic autoGen; + cmQtAutoMocUic autoGen; std::string const& infoDir = args[2]; std::string const& config = args[3]; return autoGen.Run(infoDir, config) ? 0 : 1; -- cgit v0.12