summaryrefslogtreecommitdiffstats
path: root/Source/cmQtAutoGeneratorMocUic.cxx
diff options
context:
space:
mode:
authorSebastian Holtermann <sebholt@xwmw.org>2018-01-03 15:59:40 (GMT)
committerSebastian Holtermann <sebholt@xwmw.org>2018-01-17 16:23:49 (GMT)
commita008578deebfa71b38786281450e3d9cf84f5847 (patch)
tree70173006b0adc6a62626e59d9cc653826f950336 /Source/cmQtAutoGeneratorMocUic.cxx
parent488baaf0d6144cd7cedfbbd3bb6eadcc72257fc4 (diff)
downloadCMake-a008578deebfa71b38786281450e3d9cf84f5847.zip
CMake-a008578deebfa71b38786281450e3d9cf84f5847.tar.gz
CMake-a008578deebfa71b38786281450e3d9cf84f5847.tar.bz2
Autogen: Process files concurrently in AUTOMOC and AUTOUIC
This introduces concurrent thread processing in the `_autogen` target wich processes AUTOMOC and AUTOUIC. Source file parsing is distributed among the threads by using a job queue from which the threads pull new parse jobs. Each thread might start an independent ``moc`` or ``uic`` process. Altogether this roughly speeds up the AUTOMOC and AUTOUIC build process by the number of physical CPUs on the host system. The exact number of threads to start in the `_autogen` target is controlled by the new AUTOGEN_PARALLEL target property which is initialized by the new CMAKE_AUTOGEN_PARALLEL variable. If AUTOGEN_PARALLEL is empty or unset (which is the default) the thread count is set to the number of physical CPUs on the host system. The AUTOMOC/AUTOUIC generator and the AUTORCC generator are refactored to use a libuv loop internally. Closes #17422.
Diffstat (limited to 'Source/cmQtAutoGeneratorMocUic.cxx')
-rw-r--r--Source/cmQtAutoGeneratorMocUic.cxx3029
1 files changed, 1660 insertions, 1369 deletions
diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx
index bce148e..4b02e0b 100644
--- a/Source/cmQtAutoGeneratorMocUic.cxx
+++ b/Source/cmQtAutoGeneratorMocUic.cxx
@@ -5,16 +5,15 @@
#include <algorithm>
#include <array>
+#include <functional>
#include <list>
#include <memory>
#include <sstream>
-#include <string.h>
#include <utility>
#include "cmAlgorithms.h"
#include "cmCryptoHash.h"
#include "cmMakefile.h"
-#include "cmOutputConverter.h"
#include "cmSystemTools.h"
#include "cmake.h"
@@ -22,51 +21,1126 @@
#include <unistd.h>
#endif
-// -- Static variables
+// -- Class methods
-static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH";
-static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH";
+std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
+ std::string const& relativePath) const
+{
+ return cmSystemTools::CollapseCombinedPath(AutogenBuildDir, relativePath);
+}
-// -- Static functions
+/**
+ * @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;
+}
-static std::string SubDirPrefix(std::string const& fileName)
+bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
+ std::string const& fileName) const
{
- std::string res(cmSystemTools::GetFilenamePath(fileName));
- if (!res.empty()) {
- res += '/';
+ 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;
}
-static bool ListContains(std::vector<std::string> const& list,
- std::string const& entry)
+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->RealPath(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->RealPath(fullPath);
+ }
+ }
+ // Return empty string
+ return std::string();
+}
+
+void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies(
+ std::string const& content, std::set<std::string>& depends) const
{
- return (std::find(list.begin(), list.end(), entry) != list.end());
+ 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();
+ }
+ }
+ }
+ }
}
-// -- Class methods
+bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped(
+ std::string const& fileName) const
+{
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk)
+{
+ if (AutoMoc && Header) {
+ // Don't parse header for moc if the file is included by a source already
+ if (wrk.Gen().ParallelMocIncluded(FileName)) {
+ AutoMoc = false;
+ }
+ }
+
+ if (AutoMoc || AutoUic) {
+ std::string error;
+ MetaT meta;
+ if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) {
+ if (!meta.Content.empty()) {
+ meta.FileDir = SubDirPrefix(FileName);
+ meta.FileBase =
+ cmSystemTools::GetFilenameWithoutLastExtension(FileName);
+
+ bool success = true;
+ if (AutoMoc) {
+ if (Header) {
+ success = ParseMocHeader(wrk, meta);
+ } else {
+ success = ParseMocSource(wrk, meta);
+ }
+ }
+ if (AutoUic && success) {
+ ParseUic(wrk, meta);
+ }
+ } else {
+ wrk.LogFileWarning(GeneratorT::GEN, FileName,
+ "The source file is empty");
+ }
+ } else {
+ wrk.LogFileError(GeneratorT::GEN, FileName,
+ "Could not read the file: " + error);
+ }
+ }
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk,
+ 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 = wrk.Moc().FindMacro(meta.Content);
+
+ // Extract moc includes from file
+ std::deque<MocInclude> mocIncsUsc;
+ std::deque<MocInclude> mocIncsDot;
+ {
+ if (meta.Content.find("moc") != std::string::npos) {
+ const char* contentChars = meta.Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (wrk.Moc().RegExpInclude.find(contentChars, match)) {
+ std::string incString = match.match(1);
+ std::string incDir(SubDirPrefix(incString));
+ std::string incBase =
+ cmSystemTools::GetFilenameWithoutLastExtension(incString);
+ if (cmHasLiteralPrefix(incBase, "moc_")) {
+ // moc_<BASE>.cxx
+ // Remove the moc_ part from the base name
+ mocIncsUsc.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), incBase.substr(4) });
+ } else {
+ // <BASE>.moc
+ mocIncsDot.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), std::move(incBase) });
+ }
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
+ }
+
+ // Check if there is anything to do
+ if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
+ return true;
+ }
+
+ bool ownDotMocIncluded = false;
+ bool ownMocUscIncluded = false;
+ std::deque<JobPre> jobs;
+
+ // Process moc_<BASE>.cxx includes
+ for (const MocInclude& mocInc : mocIncsUsc) {
+ std::string const header =
+ MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (wrk.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(wrk, mocInc.Base));
+ emsg += " could not be found.";
+ wrk.LogFileError(GeneratorT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Process <BASE>.moc includes
+ for (const MocInclude& mocInc : mocIncsDot) {
+ const bool ownMoc = (mocInc.Base == meta.FileBase);
+ if (wrk.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(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (wrk.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 += wrk.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";
+ wrk.LogFileWarning(GeneratorT::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";
+ wrk.LogFileWarning(GeneratorT::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(wrk, mocInc.Base));
+ emsg += " could not be found.";
+ wrk.LogFileError(GeneratorT::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 += wrk.Moc().MacrosString();
+ emsg += " macro.";
+ wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg);
+ }
+ } else {
+ // Don't allow <BASE>.moc include other than self in strict mode
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", which seems to be the moc file from a different "
+ "source file.\nThis is not supported. Include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += " to run moc on this source file.";
+ wrk.LogFileError(GeneratorT::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 (wrk.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)";
+ wrk.LogFileWarning(GeneratorT::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";
+ wrk.LogFileError(GeneratorT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Convert pre jobs to actual jobs
+ for (JobPre& jobPre : jobs) {
+ JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName,
+ std::move(jobPre.IncludeString)));
+ if (jobPre.self) {
+ // Read depdendencies from this source
+ static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
+ }
+ if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk,
+ MetaT const& meta)
+{
+ bool success = true;
+ std::string const macroName = wrk.Moc().FindMacro(meta.Content);
+ if (!macroName.empty()) {
+ JobHandleT jobHandle(
+ new JobMocT(std::string(FileName), std::string(), std::string()));
+ // Read depdendencies from this source
+ static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
+ success = wrk.Gen().ParallelJobPushMoc(jobHandle);
+ }
+ return success;
+}
+
+std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders(
+ WorkerT& wrk, std::string const& fileBase) const
+{
+ std::string res = fileBase;
+ res += ".{";
+ res += cmJoin(wrk.Base().HeaderExtensions, ",");
+ res += "}";
+ return res;
+}
+
+std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader(
+ WorkerT& wrk, std::string const& includerDir, std::string const& includeBase)
+{
+ std::string header;
+ // Search in vicinity of the source
+ if (!wrk.Base().FindHeader(header, includerDir + includeBase)) {
+ // Search in include directories
+ for (std::string const& path : wrk.Moc().IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeBase;
+ if (wrk.Base().FindHeader(header, fullPath)) {
+ break;
+ }
+ }
+ }
+ // Sanitize
+ if (!header.empty()) {
+ header = wrk.FileSys().RealPath(header);
+ }
+ return header;
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk,
+ 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 (wrk.Uic().RegExpInclude.find(contentChars, match)) {
+ if (!ParseUicInclude(wrk, meta, match.match(1))) {
+ success = false;
+ break;
+ }
+ contentChars += match.end();
+ }
+ }
+ return success;
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude(
+ WorkerT& wrk, MetaT const& meta, std::string&& includeString)
+{
+ bool success = false;
+ std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString);
+ if (!uiInputFile.empty()) {
+ if (!wrk.Uic().skipped(uiInputFile)) {
+ JobHandleT jobHandle(new JobUicT(std::move(uiInputFile), FileName,
+ std::move(includeString)));
+ success = wrk.Gen().ParallelJobPushUic(jobHandle);
+ } else {
+ // A skipped file is successful
+ success = true;
+ }
+ }
+ return success;
+}
+
+std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile(
+ WorkerT& wrk, MetaT const& meta, std::string const& includeString)
+{
+ std::string res;
+ std::string searchFile =
+ cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
+ searchFile += ".ui";
+ // Collect search paths list
+ std::deque<std::string> testFiles;
+ {
+ std::string const searchPath = 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 (!wrk.Uic().SearchPaths.empty()) {
+ for (std::string const& sPath : wrk.Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFile));
+ }
+ if (!searchPath.empty()) {
+ for (std::string const& sPath : wrk.Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFileFull));
+ }
+ }
+ }
+ }
+
+ // Search for the .ui file!
+ for (std::string const& testFile : testFiles) {
+ if (wrk.FileSys().FileExists(testFile)) {
+ res = wrk.FileSys().RealPath(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";
+ }
+ wrk.LogFileError(GeneratorT::UIC, FileName, emsg);
+ }
+
+ return res;
+}
+
+void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk)
+{
+ // (Re)generate moc_predefs.h on demand
+ bool generate(false);
+ bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs));
+ if (!fileExists) {
+ if (wrk.Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(wrk.Moc().PredefsFileRel);
+ reason += " because it doesn't exist";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ generate = true;
+ } else if (wrk.Moc().SettingsChanged) {
+ if (wrk.Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(wrk.Moc().PredefsFileRel);
+ reason += " because the settings changed.";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ generate = true;
+ }
+ if (generate) {
+ ProcessResultT result;
+ {
+ // Compose command
+ std::vector<std::string> cmd = wrk.Moc().PredefsCmd;
+ // Add includes
+ cmd.insert(cmd.end(), wrk.Moc().Includes.begin(),
+ wrk.Moc().Includes.end());
+ // Add definitions
+ for (std::string const& def : wrk.Moc().Definitions) {
+ cmd.push_back("-D" + def);
+ }
+ // Execute command
+ if (!wrk.RunProcess(GeneratorT::MOC, result, cmd)) {
+ std::string emsg = "The content generation command for ";
+ emsg += Quoted(wrk.Moc().PredefsFileRel);
+ emsg += " failed.\n";
+ emsg += result.ErrorMessage;
+ wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut);
+ }
+ }
+
+ // (Re)write predefs file only on demand
+ if (!result.error()) {
+ if (!fileExists ||
+ wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
+ if (wrk.FileSys().FileWrite(GeneratorT::MOC, wrk.Moc().PredefsFileAbs,
+ result.StdOut)) {
+ // Success
+ } else {
+ std::string emsg = "Writing ";
+ emsg += Quoted(wrk.Moc().PredefsFileRel);
+ emsg += " failed.";
+ wrk.LogFileError(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, emsg);
+ }
+ } else {
+ // Touch to update the time stamp
+ if (wrk.Log().Verbose()) {
+ std::string msg = "Touching ";
+ msg += Quoted(wrk.Moc().PredefsFileRel);
+ msg += ".";
+ wrk.LogInfo(GeneratorT::MOC, msg);
+ }
+ wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs);
+ }
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies(
+ WorkerT& wrk, std::string const& content)
+{
+ wrk.Moc().FindDependencies(content, Depends);
+ DependsValid = true;
+}
+
+void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk)
+{
+ // Compute build file name
+ if (!IncludeString.empty()) {
+ BuildFile = wrk.Base().AutogenIncludeDirAbs;
+ BuildFile += IncludeString;
+ } else {
+ std::string buildRel = wrk.Base().FilePathChecksum.getPart(SourceFile);
+ buildRel += '/';
+ buildRel += "moc_";
+ buildRel += cmSystemTools::GetFilenameWithoutLastExtension(SourceFile);
+ if (wrk.Base().MultiConfig != MultiConfigT::SINGLE) {
+ buildRel += wrk.Base().ConfigSuffix;
+ }
+ buildRel += ".cpp";
+ wrk.Gen().ParallelMocAutoRegister(buildRel);
+ BuildFile = wrk.Base().AbsoluteBuildPath(buildRel);
+ }
+
+ if (UpdateRequired(wrk)) {
+ GenerateMoc(wrk);
+ }
+}
+
+bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk)
+{
+ bool const verbose = wrk.Gen().Log().Verbose();
+
+ // Test if the build file exists
+ if (!wrk.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";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if any setting changed
+ if (wrk.Moc().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the MOC settings changed";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if the moc_predefs file is newer
+ if (!wrk.Moc().PredefsFileAbs.empty()) {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = wrk.FileSys().FileIsOlderThan(
+ BuildFile, wrk.Moc().PredefsFileAbs, &error);
+ if (!isOlder && !error.empty()) {
+ wrk.LogError(GeneratorT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than: ";
+ reason += Quoted(wrk.Moc().PredefsFileAbs);
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ wrk.LogError(GeneratorT::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);
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if a dependency file is newer
+ {
+ // Read dependencies on demand
+ if (!DependsValid) {
+ std::string content;
+ {
+ std::string error;
+ if (!wrk.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;
+ wrk.LogError(GeneratorT::MOC, emsg);
+ return false;
+ }
+ }
+ FindDependencies(wrk, content);
+ }
+ // Check dependency timestamps
+ std::string error;
+ std::string sourceDir = SubDirPrefix(SourceFile);
+ for (std::string const& depFileRel : Depends) {
+ std::string depFileAbs =
+ wrk.Moc().FindIncludedFile(sourceDir, depFileRel);
+ if (!depFileAbs.empty()) {
+ if (wrk.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);
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+ if (!error.empty()) {
+ wrk.LogError(GeneratorT::MOC, error);
+ return false;
+ }
+ } else {
+ std::string message = "Could not find dependency file ";
+ message += Quoted(depFileRel);
+ wrk.LogFileWarning(GeneratorT::MOC, SourceFile, message);
+ }
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
+{
+ // Make sure the parent directory exists
+ if (wrk.FileSys().MakeParentDirectory(GeneratorT::MOC, BuildFile)) {
+ // Compose moc command
+ std::vector<std::string> cmd;
+ cmd.push_back(wrk.Moc().Executable);
+ // Add options
+ cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(),
+ wrk.Moc().AllOptions.end());
+ // Add predefs include
+ if (!wrk.Moc().PredefsFileAbs.empty()) {
+ cmd.push_back("--include");
+ cmd.push_back(wrk.Moc().PredefsFileAbs);
+ }
+ cmd.push_back("-o");
+ cmd.push_back(BuildFile);
+ cmd.push_back(SourceFile);
+
+ // Execute moc command
+ ProcessResultT result;
+ if (wrk.RunProcess(GeneratorT::MOC, result, cmd)) {
+ // Moc command success
+ if (IncludeString.empty()) {
+ // Notify the generator that a not included file changed
+ wrk.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;
+ wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut);
+ }
+ wrk.FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk)
+{
+ // Compute build file name
+ BuildFile = wrk.Base().AutogenIncludeDirAbs;
+ BuildFile += IncludeString;
+
+ if (UpdateRequired(wrk)) {
+ GenerateUic(wrk);
+ }
+}
+
+bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk)
+{
+ bool const verbose = wrk.Gen().Log().Verbose();
+
+ // Test if the build file exists
+ if (!wrk.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";
+ wrk.LogInfo(GeneratorT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the uic settings changed
+ if (wrk.Uic().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the UIC settings changed";
+ wrk.LogInfo(GeneratorT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ wrk.LogError(GeneratorT::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);
+ wrk.LogInfo(GeneratorT::UIC, reason);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
+{
+ // Make sure the parent directory exists
+ if (wrk.FileSys().MakeParentDirectory(GeneratorT::UIC, BuildFile)) {
+ // Compose uic command
+ std::vector<std::string> cmd;
+ cmd.push_back(wrk.Uic().Executable);
+ {
+ std::vector<std::string> allOpts = wrk.Uic().TargetOptions;
+ auto optionIt = wrk.Uic().Options.find(SourceFile);
+ if (optionIt != wrk.Uic().Options.end()) {
+ UicMergeOptions(allOpts, optionIt->second,
+ (wrk.Base().QtVersionMajor == 5));
+ }
+ cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+ }
+ cmd.push_back("-o");
+ cmd.push_back(BuildFile);
+ cmd.push_back(SourceFile);
+
+ ProcessResultT result;
+ if (wrk.RunProcess(GeneratorT::UIC, result, cmd)) {
+ // Success
+ } else {
+ // 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;
+ wrk.LogCommandError(GeneratorT::UIC, emsg, cmd, result.StdOut);
+ }
+ wrk.FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::JobDeleterT::operator()(JobT* job)
+{
+ delete job;
+}
+
+cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen,
+ uv_loop_t* uvLoop)
+ : Gen_(gen)
+{
+ // Initialize uv asynchronous callback for process starting
+ ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this);
+ // Start thread
+ Thread_ = std::thread(&WorkerT::Loop, this);
+}
+
+cmQtAutoGeneratorMocUic::WorkerT::~WorkerT()
+{
+ // Join thread
+ if (Thread_.joinable()) {
+ Thread_.join();
+ }
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogInfo(
+ GeneratorT genType, std::string const& message) const
+{
+ return Log().Info(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogWarning(
+ GeneratorT genType, std::string const& message) const
+{
+ return Log().Warning(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning(
+ GeneratorT genType, std::string const& filename,
+ std::string const& message) const
+{
+ return Log().WarningFile(genType, filename, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogError(
+ GeneratorT genType, std::string const& message) const
+{
+ Gen().ParallelRegisterJobError();
+ Log().Error(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogFileError(
+ GeneratorT genType, std::string const& filename,
+ std::string const& message) const
+{
+ Gen().ParallelRegisterJobError();
+ Log().ErrorFile(genType, filename, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError(
+ GeneratorT genType, std::string const& message,
+ std::vector<std::string> const& command, std::string const& output) const
+{
+ Gen().ParallelRegisterJobError();
+ Log().ErrorCommand(genType, message, command, output);
+}
+
+bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess(
+ GeneratorT genType, ProcessResultT& result,
+ std::vector<std::string> const& command)
+{
+ if (command.empty()) {
+ return false;
+ }
+
+ // Create process instance
+ {
+ std::lock_guard<std::mutex> lock(ProcessMutex_);
+ Process_ = cm::make_unique<ReadOnlyProcessT>();
+ Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir);
+ }
+
+ // Send asynchronous process start request to libuv loop
+ ProcessRequest_.send();
+
+ // Log command
+ if (this->Log().Verbose()) {
+ std::string msg = "Running command:\n";
+ msg += QuotedCommand(command);
+ msg += '\n';
+ this->LogInfo(genType, msg);
+ }
+
+ // Wait until the process has been finished and destroyed
+ {
+ std::unique_lock<std::mutex> ulock(ProcessMutex_);
+ while (Process_) {
+ ProcessCondition_.wait(ulock);
+ }
+ }
+ return !result.error();
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::Loop()
+{
+ while (true) {
+ Gen().WorkerSwapJob(JobHandle_);
+ if (JobHandle_) {
+ JobHandle_->Process(*this);
+ } else {
+ break;
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle)
+{
+ auto& wrk = *reinterpret_cast<WorkerT*>(handle->data);
+ {
+ std::lock_guard<std::mutex> lock(wrk.ProcessMutex_);
+ if (wrk.Process_ && !wrk.Process_->IsStarted()) {
+ wrk.Process_->start(handle->loop,
+ std::bind(&WorkerT::UVProcessFinished, &wrk));
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished()
+{
+ {
+ std::lock_guard<std::mutex> lock(ProcessMutex_);
+ if (Process_ && Process_->IsFinished()) {
+ Process_.reset();
+ }
+ }
+ // Notify idling thread
+ ProcessCondition_.notify_one();
+}
cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
- : MultiConfig(cmQtAutoGen::WRAP)
- , IncludeProjectDirsBefore(false)
- , QtVersionMajor(4)
- , MocSettingsChanged(false)
- , MocPredefsChanged(false)
- , MocRelaxedMode(false)
- , UicSettingsChanged(false)
+ : Base_(&FileSys())
+ , Moc_(&FileSys())
+ , Stage_(StageT::SETTINGS_READ)
+ , JobsRemain_(0)
+ , JobError_(false)
+ , JobThreadsAbort_(false)
+ , MocAutoFileUpdated_(false)
{
// Precompile regular expressions
- this->MocRegExpInclude.compile(
+ Moc_.RegExpInclude.compile(
"[\n][ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
- this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+ Uic_.RegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+
+ // Initialize libuv asynchronous iteration request
+ UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
}
-bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
+cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic()
+{
+}
+
+bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
{
// -- Meta
- this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
+ Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
// Utility lambdas
auto InfoGet = [makefile](const char* key) {
@@ -87,7 +1161,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
std::string const value = makefile->GetSafeDefinition(key);
std::string::size_type pos = 0;
while (pos < value.size()) {
- std::string::size_type next = value.find(cmQtAutoGen::listSep, pos);
+ 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
@@ -102,7 +1176,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
}
}
pos += length;
- pos += cmQtAutoGen::listSep.size();
+ pos += ListSep.size();
}
}
return lists;
@@ -112,7 +1186,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
{
std::string keyConf = key;
keyConf += '_';
- keyConf += this->GetInfoConfig();
+ keyConf += InfoConfig();
valueConf = makefile->GetDefinition(keyConf);
}
if (valueConf == nullptr) {
@@ -128,122 +1202,177 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
};
// -- Read info file
- if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
- this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
- "File processing failed");
+ if (!makefile->ReadListFile(InfoFile().c_str())) {
+ Log().ErrorFile(GeneratorT::GEN, InfoFile(), "File processing failed");
return false;
}
// -- Meta
- this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
- this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
- if (this->ConfigSuffix.empty()) {
- this->ConfigSuffix = "_";
- this->ConfigSuffix += this->GetInfoConfig();
+ Base_.MultiConfig = MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
+
+ Base_.ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
+ if (Base_.ConfigSuffix.empty()) {
+ Base_.ConfigSuffix = "_";
+ Base_.ConfigSuffix += InfoConfig();
}
- this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE");
- if (this->SettingsFile.empty()) {
- this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
- "Settings file name missing");
+ SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
+ if (SettingsFile_.empty()) {
+ Log().ErrorFile(GeneratorT::GEN, InfoFile(), "Settings file name missing");
return false;
}
+ {
+ unsigned long num = Base_.NumThreads;
+ if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL"), &num)) {
+ num = std::max<unsigned long>(num, 1);
+ num = std::min<unsigned long>(num, ParallelMax);
+ Base_.NumThreads = static_cast<unsigned int>(num);
+ }
+ }
+
// - Files and directories
- this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
- this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
- this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
- this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
- this->IncludeProjectDirsBefore =
+ 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");
- this->AutogenBuildDir = InfoGet("AM_BUILD_DIR");
- if (this->AutogenBuildDir.empty()) {
- this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
- "Autogen build directory missing");
+ Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
+ if (Base_.AutogenBuildDir.empty()) {
+ Log().ErrorFile(GeneratorT::GEN, InfoFile(),
+ "Autogen build directory missing");
return false;
}
// - Qt environment
- if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"),
- &this->QtVersionMajor)) {
- this->QtVersionMajor = 4;
+ {
+ unsigned long qtv = Base_.QtVersionMajor;
+ if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) {
+ Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
+ }
}
- this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE");
- this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE");
// - Moc
- if (this->MocEnabled()) {
- this->MocSkipList = InfoGetList("AM_MOC_SKIP");
- this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+ Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+ Moc_.Enabled = !Moc().Executable.empty();
+ if (Moc().Enabled) {
+ {
+ auto lst = InfoGetList("AM_MOC_SKIP");
+ Moc_.SkipList.insert(lst.begin(), lst.end());
+ }
+ Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
#ifdef _WIN32
{
- std::string const win32("WIN32");
- if (!ListContains(this->MocDefinitions, win32)) {
- this->MocDefinitions.push_back(win32);
+ std::string win32("WIN32");
+ auto itB = Moc().Definitions.cbegin();
+ auto itE = Moc().Definitions.cend();
+ if (std::find(itB, itE, win32) == itE) {
+ Moc_.Definitions.emplace_back(std::move(win32));
}
}
#endif
- this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
- this->MocOptions = InfoGetList("AM_MOC_OPTIONS");
- this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
- {
- std::vector<std::string> const MocMacroNames =
- InfoGetList("AM_MOC_MACRO_NAMES");
- for (std::string const& item : MocMacroNames) {
- this->MocMacroFilters.emplace_back(
- item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
- }
+ 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_]"));
}
{
- std::vector<std::string> const mocDependFilters =
- InfoGetList("AM_MOC_DEPEND_FILTERS");
- // Insert Q_PLUGIN_METADATA dependency filter
- if (this->QtVersionMajor != 4) {
- this->MocDependFilterPush("Q_PLUGIN_METADATA",
- "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
- "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
+ auto pushFilter = [this](std::string const& key, std::string const& exp,
+ std::string& error) {
+ if (!key.empty()) {
+ if (!exp.empty()) {
+ Moc_.DependFilters.push_back(KeyExpT());
+ 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
- if ((mocDependFilters.size() % 2) == 0) {
- for (std::vector<std::string>::const_iterator
- dit = mocDependFilters.begin(),
- ditEnd = mocDependFilters.end();
- dit != ditEnd; dit += 2) {
- if (!this->MocDependFilterPush(*dit, *(dit + 1))) {
- return false;
+ {
+ std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
+ if ((flts.size() % 2) == 0) {
+ for (std::vector<std::string>::iterator itC = flts.begin(),
+ itE = flts.end();
+ itC != itE; itC += 2) {
+ pushFilter(*itC, *(itC + 1), error);
+ if (!error.empty()) {
+ break;
+ }
}
+ } else {
+ Log().ErrorFile(
+ GeneratorT::MOC, InfoFile(),
+ "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ return false;
}
- } else {
- this->LogFileError(
- cmQtAutoGen::MOC, this->GetInfoFile(),
- "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ }
+ if (!error.empty()) {
+ Log().ErrorFile(GeneratorT::MOC, InfoFile(), error);
return false;
}
}
- this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+ Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+ // Install moc predefs job
+ if (!Moc().PredefsCmd.empty()) {
+ JobQueues_.MocPredefs.emplace_back(new JobMocPredefsT());
+ }
}
// - Uic
- if (this->UicEnabled()) {
- this->UicSkipList = InfoGetList("AM_UIC_SKIP");
- this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
- this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
+ Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+ Uic_.Enabled = !Uic().Executable.empty();
+ if (Uic().Enabled) {
+ {
+ auto lst = InfoGetList("AM_UIC_SKIP");
+ Uic_.SkipList.insert(lst.begin(), lst.end());
+ }
+ 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() << "/"
+ ost << "files/options lists sizes missmatch (" << sources.size() << "/"
<< options.size() << ")";
- this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str());
+ Log().ErrorFile(GeneratorT::UIC, InfoFile(), ost.str());
return false;
}
auto fitEnd = sources.cend();
auto fit = sources.begin();
auto oit = options.begin();
while (fit != fitEnd) {
- this->UicOptions[*fit] = std::move(*oit);
+ Uic_.Options[*fit] = std::move(*oit);
++fit;
++oit;
}
@@ -252,54 +1381,51 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
// Initialize source file jobs
{
- // Utility lambdas
- auto AddJob = [this](std::map<std::string, SourceJob>& jobs,
- std::string&& sourceFile) {
- const bool moc = !this->MocSkip(sourceFile);
- const bool uic = !this->UicSkip(sourceFile);
- if (moc || uic) {
- SourceJob& job = jobs[std::move(sourceFile)];
- job.Moc = moc;
- job.Uic = uic;
- }
- };
+ std::hash<std::string> stringHash;
+ std::set<std::size_t> uniqueHeaders;
// Add header jobs
for (std::string& hdr : InfoGetList("AM_HEADERS")) {
- AddJob(this->HeaderJobs, std::move(hdr));
+ const bool moc = !Moc().skipped(hdr);
+ const bool uic = !Uic().skipped(hdr);
+ if ((moc || uic) && uniqueHeaders.emplace(stringHash(hdr)).second) {
+ JobQueues_.Headers.emplace_back(
+ new JobParseT(std::move(hdr), moc, uic, true));
+ }
}
// Add source jobs
{
std::vector<std::string> sources = InfoGetList("AM_SOURCES");
// Add header(s) for the source file
- for (std::string const& src : sources) {
- const bool srcMoc = !this->MocSkip(src);
- const bool srcUic = !this->UicSkip(src);
+ for (std::string& src : sources) {
+ const bool srcMoc = !Moc().skipped(src);
+ const bool srcUic = !Uic().skipped(src);
if (!srcMoc && !srcUic) {
continue;
}
// Search for the default header file and a private header
- std::array<std::string, 2> headerBases;
- headerBases[0] = SubDirPrefix(src);
- headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
- headerBases[1] = headerBases[0];
- headerBases[1] += "_p";
- for (std::string const& headerBase : headerBases) {
- std::string header;
- if (this->FindHeader(header, headerBase)) {
- const bool moc = srcMoc && !this->MocSkip(header);
- const bool uic = srcUic && !this->UicSkip(header);
- if (moc || uic) {
- SourceJob& job = this->HeaderJobs[std::move(header)];
- job.Moc = moc;
- job.Uic = uic;
+ {
+ std::array<std::string, 2> bases;
+ bases[0] = SubDirPrefix(src);
+ bases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
+ bases[1] = bases[0];
+ bases[1] += "_p";
+ for (std::string const& headerBase : bases) {
+ std::string header;
+ if (Base().FindHeader(header, headerBase)) {
+ const bool moc = srcMoc && !Moc().skipped(header);
+ const bool uic = srcUic && !Uic().skipped(header);
+ if ((moc || uic) &&
+ uniqueHeaders.emplace(stringHash(header)).second) {
+ JobQueues_.Headers.emplace_back(
+ new JobParseT(std::move(header), moc, uic, true));
+ }
}
}
}
- }
- // Add Source jobs
- for (std::string& src : sources) {
- AddJob(this->SourceJobs, std::move(src));
+ // Add source job
+ JobQueues_.Sources.emplace_back(
+ new JobParseT(std::move(src), srcMoc, srcUic));
}
}
}
@@ -308,58 +1434,58 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
// ------------------------
// Init file path checksum generator
- this->FilePathChecksum.setupParentDirs(
- this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
- this->ProjectBinaryDir);
+ Base_.FilePathChecksum.setupParentDirs(
+ Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
+ Base().ProjectBinaryDir);
// include directory
- this->AutogenIncludeDir = "include";
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
- this->AutogenIncludeDir += this->ConfigSuffix;
+ Base_.AutogenIncludeDirRel = "include";
+ if (Base().MultiConfig != MultiConfigT::SINGLE) {
+ Base_.AutogenIncludeDirRel += Base().ConfigSuffix;
}
- this->AutogenIncludeDir += "/";
+ Base_.AutogenIncludeDirRel += "/";
+ Base_.AutogenIncludeDirAbs =
+ Base_.AbsoluteBuildPath(Base().AutogenIncludeDirRel);
// Moc variables
- if (this->MocEnabled()) {
+ if (Moc().Enabled) {
// Mocs compilation file
- this->MocCompFileRel = "mocs_compilation";
- if (this->MultiConfig == cmQtAutoGen::FULL) {
- this->MocCompFileRel += this->ConfigSuffix;
+ Moc_.CompFileRel = "mocs_compilation";
+ if (Base_.MultiConfig == MultiConfigT::MULTI) {
+ Moc_.CompFileRel += Base().ConfigSuffix;
}
- this->MocCompFileRel += ".cpp";
- this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, this->MocCompFileRel);
+ Moc_.CompFileRel += ".cpp";
+ Moc_.CompFileAbs = Base_.AbsoluteBuildPath(Moc().CompFileRel);
// Moc predefs file
- if (!this->MocPredefsCmd.empty()) {
- this->MocPredefsFileRel = "moc_predefs";
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
- this->MocPredefsFileRel += this->ConfigSuffix;
+ if (!Moc_.PredefsCmd.empty()) {
+ Moc_.PredefsFileRel = "moc_predefs";
+ if (Base_.MultiConfig != MultiConfigT::SINGLE) {
+ Moc_.PredefsFileRel += Base().ConfigSuffix;
}
- this->MocPredefsFileRel += ".h";
- this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, this->MocPredefsFileRel);
+ Moc_.PredefsFileRel += ".h";
+ Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
}
// Sort include directories on demand
- if (this->IncludeProjectDirsBefore) {
+ if (Base().IncludeProjectDirsBefore) {
// Move strings to temporary list
std::list<std::string> includes;
- includes.insert(includes.end(), this->MocIncludePaths.begin(),
- this->MocIncludePaths.end());
- this->MocIncludePaths.clear();
- this->MocIncludePaths.reserve(includes.size());
+ includes.insert(includes.end(), Moc().IncludePaths.begin(),
+ Moc().IncludePaths.end());
+ Moc_.IncludePaths.clear();
+ Moc_.IncludePaths.reserve(includes.size());
// Append project directories only
{
std::array<std::string const*, 2> const movePaths = {
- { &this->ProjectBinaryDir, &this->ProjectSourceDir }
+ { &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
};
for (std::string const* ppath : movePaths) {
std::list<std::string>::iterator it = includes.begin();
while (it != includes.end()) {
std::string const& path = *it;
if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
- this->MocIncludePaths.push_back(path);
+ Moc_.IncludePaths.push_back(path);
it = includes.erase(it);
} else {
++it;
@@ -368,14 +1494,14 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
}
}
// Append remaining directories
- this->MocIncludePaths.insert(this->MocIncludePaths.end(),
- includes.begin(), includes.end());
+ Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
+ includes.end());
}
// Compose moc includes list
{
std::set<std::string> frameworkPaths;
- for (std::string const& path : this->MocIncludePaths) {
- this->MocIncludes.push_back("-I" + path);
+ 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
@@ -388,1346 +1514,511 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
}
// Append framework includes
for (std::string const& path : frameworkPaths) {
- this->MocIncludes.push_back("-F");
- this->MocIncludes.push_back(path);
+ Moc_.Includes.push_back("-F");
+ Moc_.Includes.push_back(path);
}
}
// Setup single list with all options
{
// Add includes
- this->MocAllOptions.insert(this->MocAllOptions.end(),
- this->MocIncludes.begin(),
- this->MocIncludes.end());
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
+ Moc().Includes.end());
// Add definitions
- for (std::string const& def : this->MocDefinitions) {
- this->MocAllOptions.push_back("-D" + def);
+ for (std::string const& def : Moc().Definitions) {
+ Moc_.AllOptions.push_back("-D" + def);
}
// Add options
- this->MocAllOptions.insert(this->MocAllOptions.end(),
- this->MocOptions.begin(),
- this->MocOptions.end());
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
+ Moc().Options.end());
}
}
return true;
}
-void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile)
+bool cmQtAutoGeneratorMocUic::Process()
+{
+ // Run libuv event loop
+ UVRequest().send();
+ if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
+ if (JobError_) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle)
+{
+ reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage();
+}
+
+void cmQtAutoGeneratorMocUic::PollStage()
+{
+ switch (Stage_) {
+ case StageT::SETTINGS_READ:
+ SettingsFileRead();
+ SetStage(StageT::CREATE_DIRECTORIES);
+ break;
+ case StageT::CREATE_DIRECTORIES:
+ CreateDirectories();
+ SetStage(StageT::PARSE_SOURCES);
+ break;
+ case StageT::PARSE_SOURCES:
+ if (ThreadsStartJobs(JobQueues_.Sources)) {
+ SetStage(StageT::PARSE_HEADERS);
+ }
+ break;
+ case StageT::PARSE_HEADERS:
+ if (ThreadsStartJobs(JobQueues_.Headers)) {
+ SetStage(StageT::MOC_PREDEFS);
+ }
+ break;
+ case StageT::MOC_PREDEFS:
+ if (ThreadsStartJobs(JobQueues_.MocPredefs)) {
+ SetStage(StageT::MOC_PROCESS);
+ }
+ break;
+ case StageT::MOC_PROCESS:
+ if (ThreadsStartJobs(JobQueues_.Moc)) {
+ SetStage(StageT::MOCS_COMPILATION);
+ }
+ break;
+ case StageT::MOCS_COMPILATION:
+ if (ThreadsJobsDone()) {
+ MocGenerateCompilation();
+ SetStage(StageT::UIC_PROCESS);
+ }
+ break;
+ case StageT::UIC_PROCESS:
+ if (ThreadsStartJobs(JobQueues_.Uic)) {
+ SetStage(StageT::SETTINGS_WRITE);
+ }
+ break;
+ case StageT::SETTINGS_WRITE:
+ SettingsFileWrite();
+ SetStage(StageT::FINISH);
+ break;
+ case StageT::FINISH:
+ if (ThreadsJobsDone()) {
+ // Clear all libuv handles
+ ThreadsStop();
+ UVRequest().reset();
+ // Set highest END stage manually
+ Stage_ = StageT::END;
+ }
+ break;
+ case StageT::END:
+ break;
+ }
+}
+
+void cmQtAutoGeneratorMocUic::SetStage(StageT stage)
+{
+ if (JobError_) {
+ stage = StageT::FINISH;
+ }
+ // Only allow to increase the stage
+ if (Stage_ < stage) {
+ Stage_ = stage;
+ UVRequest().send();
+ }
+}
+
+void cmQtAutoGeneratorMocUic::SettingsFileRead()
{
// Compose current settings strings
{
cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
std::string const sep(" ~~~ ");
- if (this->MocEnabled()) {
+ if (Moc_.Enabled) {
std::string str;
- str += this->MocExecutable;
+ str += Moc().Executable;
str += sep;
- str += cmJoin(this->MocAllOptions, ";");
+ str += cmJoin(Moc().AllOptions, ";");
str += sep;
- str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
+ str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
str += sep;
- str += cmJoin(this->MocPredefsCmd, ";");
+ str += cmJoin(Moc().PredefsCmd, ";");
str += sep;
- this->SettingsStringMoc = crypt.HashString(str);
+ SettingsStringMoc_ = crypt.HashString(str);
}
- if (this->UicEnabled()) {
+ if (Uic().Enabled) {
std::string str;
- str += this->UicExecutable;
+ str += Uic().Executable;
str += sep;
- str += cmJoin(this->UicTargetOptions, ";");
- for (const auto& item : this->UicOptions) {
+ str += cmJoin(Uic().TargetOptions, ";");
+ for (const auto& item : Uic().Options) {
str += sep;
str += item.first;
str += sep;
str += cmJoin(item.second, ";");
}
str += sep;
- this->SettingsStringUic = crypt.HashString(str);
+ SettingsStringUic_ = crypt.HashString(str);
}
}
- // Read old settings
- if (makefile->ReadListFile(this->SettingsFile.c_str())) {
- {
- auto SMatch = [makefile](const char* key, std::string const& value) {
- return (value == makefile->GetSafeDefinition(key));
- };
- if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) {
- this->MocSettingsChanged = true;
+ // 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 (!SMatch(SettingsKeyUic, this->SettingsStringUic)) {
- this->UicSettingsChanged = 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;
}
}
- // 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 (this->SettingsChanged()) {
- cmSystemTools::RemoveFile(this->SettingsFile);
- }
- } else {
- // If the file could not be read re-generate everythiung.
- this->MocSettingsChanged = true;
- this->UicSettingsChanged = true;
}
}
-bool cmQtAutoGeneratorMocUic::SettingsFileWrite()
+void cmQtAutoGeneratorMocUic::SettingsFileWrite()
{
- bool success = true;
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
// Only write if any setting changed
- if (this->SettingsChanged()) {
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " +
- cmQtAutoGen::Quoted(this->SettingsFile));
+ if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) {
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::GEN,
+ "Writing settings file " + Quoted(SettingsFile_));
}
// Compose settings file content
- std::string settings;
+ std::string content;
{
- auto SettingAppend = [&settings](const char* key,
- std::string const& value) {
- settings += "set(";
- settings += key;
- settings += " ";
- settings += cmOutputConverter::EscapeForCMake(value);
- settings += ")\n";
+ auto SettingAppend = [&content](const char* key,
+ std::string const& value) {
+ if (!value.empty()) {
+ content += key;
+ content += ':';
+ content += value;
+ content += '\n';
+ }
};
- SettingAppend(SettingsKeyMoc, this->SettingsStringMoc);
- SettingAppend(SettingsKeyUic, this->SettingsStringUic);
+ SettingAppend("moc", SettingsStringMoc_);
+ SettingAppend("uic", SettingsStringUic_);
}
// Write settings file
- if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) {
- this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile,
- "Settings file writing failed");
+ if (!FileSys().FileWrite(GeneratorT::GEN, SettingsFile_, content)) {
+ Log().ErrorFile(GeneratorT::GEN, SettingsFile_,
+ "Settings file writing failed");
// Remove old settings file to trigger a full rebuild on the next run
- cmSystemTools::RemoveFile(this->SettingsFile);
- success = false;
+ FileSys().FileRemove(SettingsFile_);
+ RegisterJobError();
}
}
- return success;
}
-bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile)
+void cmQtAutoGeneratorMocUic::CreateDirectories()
{
- // the program goes through all .cpp files to see which moc files are
- // included. It is not really interesting how the moc file is named, but
- // what file the moc is created from. Once a moc is included the same moc
- // may not be included in the mocs_compilation.cpp file anymore.
- // OTOH if there's a header containing Q_OBJECT where no corresponding
- // moc file is included anywhere a moc_<filename>.cpp file is created and
- // included in the mocs_compilation.cpp file.
-
- if (!this->InitInfoFile(makefile)) {
- return false;
- }
- // Read latest settings
- this->SettingsFileRead(makefile);
-
// Create AUTOGEN include directory
- {
- std::string const incDirAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, this->AutogenIncludeDir);
- if (!cmSystemTools::MakeDirectory(incDirAbs)) {
- this->LogFileError(cmQtAutoGen::GEN, incDirAbs,
- "Could not create directory");
- return false;
- }
- }
-
- // Parse source files
- for (const auto& item : this->SourceJobs) {
- if (!this->ParseSourceFile(item.first, item.second)) {
- return false;
- }
- }
- // Parse header files
- for (const auto& item : this->HeaderJobs) {
- if (!this->ParseHeaderFile(item.first, item.second)) {
- return false;
- }
- }
- // Read missing dependency information
- if (!this->ParsePostprocess()) {
- return false;
+ if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDirAbs)) {
+ RegisterJobError();
}
-
- // Generate files
- if (!this->MocGenerateAll()) {
- return false;
- }
- if (!this->UicGenerateAll()) {
- return false;
- }
-
- if (!this->SettingsFileWrite()) {
- return false;
- }
-
- return true;
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename,
- const SourceJob& job)
+bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
{
- std::string contentText;
- std::string error;
- bool success = this->FileRead(contentText, absFilename, &error);
- if (success) {
- if (!contentText.empty()) {
- if (job.Moc) {
- success = this->MocParseSourceContent(absFilename, contentText);
- }
- if (success && job.Uic) {
- success = this->UicParseContent(absFilename, contentText);
+ bool done = false;
+ std::size_t queueSize = queue.size();
+
+ // Change the active queue
+ {
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ // Check if there are still unfinished jobs from the previous queue
+ if (JobsRemain_ == 0) {
+ if (!JobThreadsAbort_) {
+ JobQueue_.swap(queue);
+ JobsRemain_ = queueSize;
+ } else {
+ // Abort requested
+ queue.clear();
+ queueSize = 0;
}
- } else {
- this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
- "The source file is empty");
+ done = true;
}
- } else {
- this->LogFileError(cmQtAutoGen::GEN, absFilename,
- "Could not read the source file: " + error);
}
- return success;
-}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename,
- const SourceJob& job)
-{
- std::string contentText;
- std::string error;
- bool success = this->FileRead(contentText, absFilename, &error);
- if (success) {
- if (!contentText.empty()) {
- if (job.Moc) {
- this->MocParseHeaderContent(absFilename, contentText);
- }
- if (job.Uic) {
- success = this->UicParseContent(absFilename, contentText);
+ if (done && (queueSize != 0)) {
+ // Start new threads on demand
+ if (Workers_.empty()) {
+ Workers_.resize(Base().NumThreads);
+ for (auto& item : Workers_) {
+ item = cm::make_unique<WorkerT>(this, UVLoop());
}
} else {
- this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
- "The header file is empty");
- }
- } else {
- this->LogFileError(cmQtAutoGen::GEN, absFilename,
- "Could not read the header file: " + error);
- }
- return success;
-}
-
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParsePostprocess()
-{
- bool success = true;
- // Read missing dependencies
- for (auto& item : this->MocJobsIncluded) {
- if (!item->DependsValid) {
- std::string content;
- std::string error;
- if (this->FileRead(content, item->SourceFile, &error)) {
- this->MocFindDepends(item->SourceFile, content, item->Depends);
- item->DependsValid = true;
+ // Notify threads
+ if (queueSize == 1) {
+ JobsConditionRead_.notify_one();
} else {
- std::string emsg = "Could not read file\n ";
- emsg += item->SourceFile;
- emsg += "\nrequired by moc include \"";
- emsg += item->IncludeString;
- emsg += "\".\n";
- emsg += error;
- this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg);
- success = false;
- break;
+ JobsConditionRead_.notify_all();
}
}
}
- return success;
-}
-
-/**
- * @brief Tests if the file should be ignored for moc scanning
- * @return True if the file should be ignored
- */
-bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const
-{
- if (this->MocEnabled()) {
- // Test if the file name is on the skip list
- if (!ListContains(this->MocSkipList, absFilename)) {
- return false;
- }
- }
- return true;
-}
-/**
- * @brief Tests if the C++ content requires moc processing
- * @return True if moc is required
- */
-bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText,
- std::string* macroName)
-{
- for (KeyRegExp& filter : this->MocMacroFilters) {
- // Run a simple find string operation before the expensive
- // regular expression check
- if (contentText.find(filter.Key) != std::string::npos) {
- if (filter.RegExp.find(contentText)) {
- // Return macro name on demand
- if (macroName != nullptr) {
- *macroName = filter.Key;
- }
- return true;
- }
- }
- }
- return false;
+ return done;
}
-std::string cmQtAutoGeneratorMocUic::MocStringMacros() const
+void cmQtAutoGeneratorMocUic::ThreadsStop()
{
- std::string res;
- const auto itB = this->MocMacroFilters.cbegin();
- const auto itE = this->MocMacroFilters.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;
+ if (!Workers_.empty()) {
+ // Clear all jobs
+ {
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ JobThreadsAbort_ = true;
+ JobsRemain_ -= JobQueue_.size();
+ JobQueue_.clear();
+
+ JobQueues_.Sources.clear();
+ JobQueues_.Headers.clear();
+ JobQueues_.MocPredefs.clear();
+ JobQueues_.Moc.clear();
+ JobQueues_.Uic.clear();
+ }
+ // Wake threads
+ JobsConditionRead_.notify_all();
+ // Join and clear threads
+ Workers_.clear();
}
- return res;
}
-std::string cmQtAutoGeneratorMocUic::MocStringHeaders(
- std::string const& fileBase) const
+bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
{
- std::string res = fileBase;
- res += ".{";
- res += cmJoin(this->HeaderExtensions, ",");
- res += "}";
- return res;
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ return (JobsRemain_ == 0);
}
-std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader(
- std::string const& sourcePath, std::string const& includeBase) const
+void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
{
- std::string header;
- // Search in vicinity of the source
- if (!this->FindHeader(header, sourcePath + includeBase)) {
- // Search in include directories
- for (std::string const& path : this->MocIncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeBase;
- if (this->FindHeader(header, fullPath)) {
- break;
- }
- }
- }
- // Sanitize
- if (!header.empty()) {
- header = cmSystemTools::GetRealPath(header);
+ bool const jobProcessed(jobHandle);
+ if (jobProcessed) {
+ jobHandle.reset(nullptr);
}
- return header;
-}
-
-bool cmQtAutoGeneratorMocUic::MocFindIncludedFile(
- std::string& absFile, std::string const& sourcePath,
- std::string const& includeString) const
-{
- bool success = false;
- // Search in vicinity of the source
{
- std::string testPath = sourcePath;
- testPath += includeString;
- if (cmSystemTools::FileExists(testPath.c_str())) {
- absFile = cmSystemTools::GetRealPath(testPath);
- success = true;
- }
- }
- // Search in include directories
- if (!success) {
- for (std::string const& path : this->MocIncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeString;
- if (cmSystemTools::FileExists(fullPath.c_str())) {
- absFile = cmSystemTools::GetRealPath(fullPath);
- success = true;
- break;
+ std::unique_lock<std::mutex> jobsLock(JobsMutex_);
+ // Reduce the remaining job count and notify the libuv loop
+ // when all jobs are done
+ if (jobProcessed) {
+ --JobsRemain_;
+ if (JobsRemain_ == 0) {
+ UVRequest().send();
}
}
- }
- return success;
-}
-
-bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key,
- std::string const& regExp)
-{
- std::string error;
- if (!key.empty()) {
- if (!regExp.empty()) {
- KeyRegExp filter;
- filter.Key = key;
- if (filter.RegExp.compile(regExp)) {
- this->MocDependFilters.push_back(std::move(filter));
- } else {
- error = "Regular expression compiling failed";
- }
- } else {
- error = "Regular expression is empty";
- }
- } else {
- error = "Key is empty";
- }
- if (!error.empty()) {
- std::string emsg = "AUTOMOC_DEPEND_FILTERS: ";
- emsg += error;
- emsg += "\n";
- emsg += " Key: ";
- emsg += cmQtAutoGen::Quoted(key);
- emsg += "\n";
- emsg += " RegExp: ";
- emsg += cmQtAutoGen::Quoted(regExp);
- emsg += "\n";
- this->LogError(cmQtAutoGen::MOC, emsg);
- return false;
- }
- return true;
-}
-
-void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename,
- std::string const& contentText,
- std::set<std::string>& depends)
-{
- if (this->MocDependFilters.empty() && contentText.empty()) {
- return;
- }
-
- std::vector<std::string> matches;
- for (KeyRegExp& filter : this->MocDependFilters) {
- // Run a simple find string check
- if (contentText.find(filter.Key) != std::string::npos) {
- // Run the expensive regular expression check loop
- const char* contentChars = contentText.c_str();
- while (filter.RegExp.find(contentChars)) {
- std::string match = filter.RegExp.match(1);
- if (!match.empty()) {
- matches.emplace_back(std::move(match));
- }
- contentChars += filter.RegExp.end();
- }
+ // Wait for new jobs
+ while (!JobThreadsAbort_ && JobQueue_.empty()) {
+ JobsConditionRead_.wait(jobsLock);
}
- }
-
- if (!matches.empty()) {
- std::string const sourcePath = SubDirPrefix(absFilename);
- for (std::string const& match : matches) {
- // Find the dependency file
- std::string incFile;
- if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
- depends.insert(incFile);
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " +
- cmQtAutoGen::Quoted(absFilename) + "\n " +
- cmQtAutoGen::Quoted(incFile));
- }
- } else {
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
- "Could not find dependency file " +
- cmQtAutoGen::Quoted(match));
- }
+ // Try to pick up a new job handle
+ if (!JobThreadsAbort_ && !JobQueue_.empty()) {
+ jobHandle = std::move(JobQueue_.front());
+ JobQueue_.pop_front();
}
}
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::MocParseSourceContent(
- std::string const& absFilename, std::string const& contentText)
+void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
{
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
- }
-
- auto AddJob = [this, &absFilename](std::string const& sourceFile,
- std::string const& includeString,
- std::string const* content) {
- auto job = cm::make_unique<MocJobIncluded>();
- job->SourceFile = sourceFile;
- job->BuildFileRel = this->AutogenIncludeDir;
- job->BuildFileRel += includeString;
- job->Includer = absFilename;
- job->IncludeString = includeString;
- job->DependsValid = (content != nullptr);
- if (job->DependsValid) {
- this->MocFindDepends(sourceFile, *content, job->Depends);
- }
- this->MocJobsIncluded.push_back(std::move(job));
- };
-
- struct MocInc
- {
- std::string Inc; // full include string
- std::string Dir; // include string directory
- std::string Base; // include string file base
- };
-
- // Extract moc includes from file
- std::vector<MocInc> mocIncsUsc;
- std::vector<MocInc> mocIncsDot;
- {
- const char* contentChars = contentText.c_str();
- if (strstr(contentChars, "moc") != nullptr) {
- while (this->MocRegExpInclude.find(contentChars)) {
- std::string incString = this->MocRegExpInclude.match(1);
- std::string incDir(SubDirPrefix(incString));
- std::string incBase =
- cmSystemTools::GetFilenameWithoutLastExtension(incString);
- if (cmHasLiteralPrefix(incBase, "moc_")) {
- // moc_<BASE>.cxx
- // Remove the moc_ part from the base name
- mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir),
- incBase.substr(4) });
- } else {
- // <BASE>.moc
- mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir),
- std::move(incBase) });
- }
- // Forward content pointer
- contentChars += this->MocRegExpInclude.end();
- }
- }
- }
-
- std::string selfMacroName;
- const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName);
-
- // Check if there is anything to do
- if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) {
- return true;
- }
-
- // Scan file variables
- std::string const scanFileDir = SubDirPrefix(absFilename);
- std::string const scanFileBase =
- cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
- // Relaxed mode variables
- bool ownDotMocIncluded = false;
- std::string ownMocUscInclude;
- std::string ownMocUscHeader;
-
- // Process moc_<BASE>.cxx includes
- for (const MocInc& mocInc : mocIncsUsc) {
- std::string const header =
- this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
- if (!header.empty()) {
- // Check if header is skipped
- if (this->MocSkip(header)) {
- continue;
- }
- // Register moc job
- AddJob(header, mocInc.Inc, nullptr);
- // Store meta information for relaxed mode
- if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) {
- ownMocUscInclude = mocInc.Inc;
- ownMocUscHeader = header;
- }
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", but could not find the header ";
- emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base));
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
-
- // Process <BASE>.moc includes
- for (const MocInc& mocInc : mocIncsDot) {
- const bool ownMoc = (mocInc.Base == scanFileBase);
- if (this->MocRelaxedMode) {
- // Relaxed mode
- if (selfRequiresMoc && ownMoc) {
- // Add self
- AddJob(absFilename, mocInc.Inc, &contentText);
- 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 =
- this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
- if (!header.empty()) {
- // Check if header is skipped
- if (this->MocSkip(header)) {
- continue;
- }
- // Register moc job
- AddJob(header, mocInc.Inc, nullptr);
- if (!selfRequiresMoc) {
- if (ownMoc) {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", but does not contain a ";
- emsg += this->MocStringMacros();
- emsg += " macro.\nRunning moc on\n ";
- emsg += cmQtAutoGen::Quoted(header);
- emsg += "!\nBetter include ";
- emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for a compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += " instead of ";
- emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += ".\nRunning moc on\n ";
- emsg += cmQtAutoGen::Quoted(header);
- emsg += "!\nBetter include ";
- emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
- }
- }
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file. CMake also could not find a matching "
- "header.";
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
- } else {
- // Strict mode
- if (ownMoc) {
- // Include self
- AddJob(absFilename, mocInc.Inc, &contentText);
- ownDotMocIncluded = true;
- // Accept but issue a warning if moc isn't required
- if (!selfRequiresMoc) {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", but does not contain a ";
- emsg += this->MocStringMacros();
- emsg += " macro.";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
- }
- } else {
- // Don't allow <BASE>.moc include other than self in strict mode
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file.\nThis is not supported. Include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += " to run moc on this source file.";
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
- }
-
- if (selfRequiresMoc && !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.
- if (this->MocRelaxedMode && !ownMocUscInclude.empty()) {
- // This is for KDE4 compatibility:
- std::string emsg = "The file contains a ";
- emsg += selfMacroName;
- emsg += " macro, but does not include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += ". Instead it includes ";
- emsg += cmQtAutoGen::Quoted(ownMocUscInclude);
- emsg += ".\nRunning moc on\n ";
- emsg += cmQtAutoGen::Quoted(absFilename);
- emsg += "!\nBetter include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
-
- // Remove own header job
- {
- auto itC = this->MocJobsIncluded.begin();
- auto itE = this->MocJobsIncluded.end();
- for (; itC != itE; ++itC) {
- if ((*itC)->SourceFile == ownMocUscHeader) {
- if ((*itC)->IncludeString == ownMocUscInclude) {
- this->MocJobsIncluded.erase(itC);
- break;
- }
- }
- }
- }
- // Add own source job
- AddJob(absFilename, ownMocUscInclude, &contentText);
- } else {
- // Otherwise always error out since it will not compile:
- std::string emsg = "The file contains a ";
- emsg += selfMacroName;
- emsg += " macro, but does not include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += "!\nConsider to\n - add #include \"";
- emsg += scanFileBase;
- emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
- return true;
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ RegisterJobError();
}
-void cmQtAutoGeneratorMocUic::MocParseHeaderContent(
- std::string const& absFilename, std::string const& contentText)
+// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
+// locked
+void cmQtAutoGeneratorMocUic::RegisterJobError()
{
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
- }
-
- auto const fit =
- std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(),
- [&absFilename](std::unique_ptr<MocJobIncluded> const& job) {
- return job->SourceFile == absFilename;
- });
- if (fit == this->MocJobsIncluded.cend()) {
- if (this->MocRequired(contentText)) {
- auto job = cm::make_unique<MocJobAuto>();
- job->SourceFile = absFilename;
- {
- std::string& bld = job->BuildFileRel;
- bld = this->FilePathChecksum.getPart(absFilename);
- bld += '/';
- bld += "moc_";
- bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
- bld += this->ConfigSuffix;
- }
- bld += ".cpp";
- }
- this->MocFindDepends(absFilename, contentText, job->Depends);
- this->MocJobsAuto.push_back(std::move(job));
+ JobError_ = true;
+ if (!JobThreadsAbort_) {
+ JobThreadsAbort_ = true;
+ // Clear remaining jobs
+ if (JobsRemain_ != 0) {
+ JobsRemain_ -= JobQueue_.size();
+ JobQueue_.clear();
}
}
}
-bool cmQtAutoGeneratorMocUic::MocGenerateAll()
+bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle)
{
- if (!this->MocEnabled()) {
- return true;
- }
-
- // Look for name collisions in included moc files
- {
- bool collision = false;
- std::map<std::string, std::vector<MocJobIncluded const*>> collisions;
- for (auto const& job : this->MocJobsIncluded) {
- auto& list = collisions[job->IncludeString];
- if (!list.empty()) {
- collision = true;
- }
- list.push_back(job.get());
- }
- if (collision) {
- std::string emsg =
- "Included moc files with the same name will be "
- "generated from different sources.\n"
- "Consider to\n"
- " - not include the \"moc_<NAME>.cpp\" file\n"
- " - add a directory prefix to a \"<NAME>.moc\" include "
- "(e.g \"sub/<NAME>.moc\")\n"
- " - rename the source file(s)\n"
- "Include conflicts\n"
- "-----------------\n";
- const auto& colls = collisions;
- for (auto const& coll : colls) {
- if (coll.second.size() > 1) {
- emsg += cmQtAutoGen::Quoted(coll.first);
- emsg += " included in\n";
- for (const MocJobIncluded* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->Includer);
- emsg += "\n";
- }
- emsg += "would be generated from\n";
- for (const MocJobIncluded* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->SourceFile);
- emsg += "\n";
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ if (!JobThreadsAbort_) {
+ bool pushJobHandle = true;
+ // Do additional tests if this is an included moc job
+ const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle));
+ if (!mocJob.IncludeString.empty()) {
+ // Register included moc file and look for collisions
+ MocIncludedFiles_.emplace(mocJob.SourceFile);
+ if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) {
+ // Another source file includes the same moc file!
+ for (const JobHandleT& otherHandle : JobQueues_.Moc) {
+ const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle));
+ if (otherJob.IncludeString == mocJob.IncludeString) {
+ // Check if the same moc file would be generated from different
+ // source files which is an error.
+ if (otherJob.SourceFile != mocJob.SourceFile) {
+ // Include string collision
+ std::string error = "The two source files\n ";
+ error += Quoted(mocJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(otherJob.IncluderFile);
+ error += "\ncontain the 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(otherJob.SourceFile);
+ error += ".\nConsider to\n"
+ "- not include the \"moc_<NAME>.cpp\" file\n"
+ "- add a directory prefix to a \"<NAME>.moc\" include "
+ "(e.g \"sub/<NAME>.moc\")\n"
+ "- rename the source file(s)\n";
+ Log().Error(GeneratorT::MOC, error);
+ RegisterJobError();
+ }
+ // Do not push this job in since the included moc file already
+ // gets generated by an other job.
+ pushJobHandle = false;
+ break;
}
}
}
- this->LogError(cmQtAutoGen::MOC, emsg);
- return false;
}
- }
-
- // (Re)generate moc_predefs.h on demand
- if (!this->MocPredefsCmd.empty()) {
- if (this->MocSettingsChanged ||
- !cmSystemTools::FileExists(this->MocPredefsFileAbs)) {
- if (this->GetVerbose()) {
- this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
- }
-
- std::string output;
- {
- // Compose command
- std::vector<std::string> cmd = this->MocPredefsCmd;
- // Add includes
- cmd.insert(cmd.end(), this->MocIncludes.begin(),
- this->MocIncludes.end());
- // Add definitions
- for (std::string const& def : this->MocDefinitions) {
- cmd.push_back("-D" + def);
- }
- // Execute command
- if (!this->RunCommand(cmd, output)) {
- this->LogCommandError(cmQtAutoGen::MOC,
- "moc_predefs generation failed", cmd, output);
- return false;
- }
- }
-
- // (Re)write predefs file only on demand
- if (this->FileDiffers(this->MocPredefsFileAbs, output)) {
- if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
- output)) {
- this->MocPredefsChanged = true;
- } else {
- this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
- "moc_predefs file writing failed");
- return false;
- }
- } else {
- // Touch to update the time stamp
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC,
- "Touching moc_predefs " + this->MocPredefsFileRel);
- }
- cmSystemTools::Touch(this->MocPredefsFileAbs, false);
- }
- }
-
- // Add moc_predefs.h to moc file dependencies
- for (auto const& item : this->MocJobsIncluded) {
- item->Depends.insert(this->MocPredefsFileAbs);
- }
- for (auto const& item : this->MocJobsAuto) {
- item->Depends.insert(this->MocPredefsFileAbs);
- }
- }
-
- // Generate moc files that are included by source files.
- for (auto const& item : this->MocJobsIncluded) {
- if (!this->MocGenerateFile(*item)) {
- return false;
- }
- }
- // Generate moc files that are _not_ included by source files.
- bool autoNameGenerated = false;
- for (auto const& item : this->MocJobsAuto) {
- if (!this->MocGenerateFile(*item, &autoNameGenerated)) {
- return false;
- }
- }
-
- // Compose mocs compilation file content
- {
- std::string mocs =
- "// This file is autogenerated. Changes will be overwritten.\n";
- if (this->MocJobsAuto.empty()) {
- // Placeholder content
- mocs +=
- "// No files found that require moc or the moc files are included\n";
- mocs += "enum some_compilers { need_more_than_nothing };\n";
- } else {
- // Valid content
- for (const auto& item : this->MocJobsAuto) {
- mocs += "#include \"";
- mocs += item->BuildFileRel;
- mocs += "\"\n";
- }
- }
-
- if (this->FileDiffers(this->MocCompFileAbs, mocs)) {
- // Actually write mocs compilation file
- if (this->GetVerbose()) {
- this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
- }
- if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) {
- this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs,
- "mocs compilation file writing failed");
- return false;
- }
- } else if (autoNameGenerated) {
- // Only touch mocs compilation file
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC,
- "Touching mocs compilation " + this->MocCompFileRel);
- }
- cmSystemTools::Touch(this->MocCompFileAbs, false);
+ // Push job on demand
+ if (pushJobHandle) {
+ JobQueues_.Moc.emplace_back(std::move(jobHandle));
}
}
-
- return true;
+ return !JobError_;
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob,
- bool* generated)
+bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle)
{
- bool success = true;
-
- std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, mocJob.BuildFileRel);
-
- bool generate = false;
- std::string generateReason;
- if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from its source file ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because it doesn't exist";
- }
- generate = true;
- }
- if (!generate && this->MocSettingsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because the MOC settings changed";
- }
- generate = true;
- }
- if (!generate && this->MocPredefsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because moc_predefs.h changed";
- }
- generate = true;
- }
- if (!generate) {
- std::string error;
- if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " because it's older than its source file ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- }
- generate = true;
- } else {
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::MOC, error);
- success = false;
- }
- }
- }
- if (success && !generate) {
- // Test if a dependency file is newer
- std::string error;
- for (std::string const& depFile : mocJob.Depends) {
- if (FileIsOlderThan(mocFileAbs, depFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because it is older than ";
- generateReason += cmQtAutoGen::Quoted(depFile);
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ if (!JobThreadsAbort_) {
+ bool pushJobHandle = true;
+ // Look for include collisions.
+ const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
+ for (const JobHandleT& otherHandle : JobQueues_.Uic) {
+ const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle));
+ if (otherJob.IncludeString == uicJob.IncludeString) {
+ // Check if the same uic file would be generated from different
+ // source files which would be an error.
+ if (otherJob.SourceFile != uicJob.SourceFile) {
+ // Include string collision
+ std::string error = "The two source files\n ";
+ error += Quoted(uicJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(otherJob.IncluderFile);
+ error += "\ncontain the 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(otherJob.SourceFile);
+ error +=
+ ".\nConsider to\n"
+ "- add a directory prefix to a \"ui_<NAME>.h\" include "
+ "(e.g \"sub/ui_<NAME>.h\")\n"
+ "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
+ "include(s)\n";
+ Log().Error(GeneratorT::UIC, error);
+ RegisterJobError();
}
- generate = true;
- break;
- }
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::MOC, error);
- success = false;
+ // Do not push this job in since the uic file already
+ // gets generated by an other job.
+ pushJobHandle = false;
break;
}
}
- }
-
- if (generate) {
- // Log
- if (this->GetVerbose()) {
- this->LogBold("Generating MOC source " + mocJob.BuildFileRel);
- this->LogInfo(cmQtAutoGen::MOC, generateReason);
- }
-
- // Make sure the parent directory exists
- if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) {
- // Compose moc command
- std::vector<std::string> cmd;
- cmd.push_back(this->MocExecutable);
- // Add options
- cmd.insert(cmd.end(), this->MocAllOptions.begin(),
- this->MocAllOptions.end());
- // Add predefs include
- if (!this->MocPredefsFileAbs.empty()) {
- cmd.push_back("--include");
- cmd.push_back(this->MocPredefsFileAbs);
- }
- cmd.push_back("-o");
- cmd.push_back(mocFileAbs);
- cmd.push_back(mocJob.SourceFile);
-
- // Execute moc command
- std::string output;
- if (this->RunCommand(cmd, output)) {
- // Success
- if (generated != nullptr) {
- *generated = true;
- }
- } else {
- // Moc command failed
- {
- std::string emsg = "moc failed for\n ";
- emsg += cmQtAutoGen::Quoted(mocJob.SourceFile);
- this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output);
- }
- cmSystemTools::RemoveFile(mocFileAbs);
- success = false;
- }
- } else {
- // Parent directory creation failed
- success = false;
+ if (pushJobHandle) {
+ JobQueues_.Uic.emplace_back(std::move(jobHandle));
}
}
- return success;
+ return !JobError_;
}
-/**
- * @brief Tests if the file name is in the skip list
- */
-bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const
+bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
+ std::string const& sourceFile)
{
- if (this->UicEnabled()) {
- // Test if the file name is on the skip list
- if (!ListContains(this->UicSkipList, absFilename)) {
- return false;
- }
- }
- return true;
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
}
-bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename,
- std::string const& contentText)
+void cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
+ std::string const& mocFile)
{
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename);
- }
-
- std::vector<std::string> includes;
- // Extracte includes
- {
- const char* contentChars = contentText.c_str();
- if (strstr(contentChars, "ui_") != nullptr) {
- while (this->UicRegExpInclude.find(contentChars)) {
- includes.push_back(this->UicRegExpInclude.match(1));
- contentChars += this->UicRegExpInclude.end();
- }
- }
- }
-
- for (std::string const& includeString : includes) {
- std::string uiInputFile;
- if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) {
- return false;
- }
- // Check if this file should be skipped
- if (this->UicSkip(uiInputFile)) {
- continue;
- }
- // Check if the job already exists
- bool jobExists = false;
- for (const auto& job : this->UicJobs) {
- if ((job->SourceFile == uiInputFile) &&
- (job->IncludeString == includeString)) {
- jobExists = true;
- break;
- }
- }
- if (!jobExists) {
- auto job = cm::make_unique<UicJob>();
- job->SourceFile = uiInputFile;
- job->BuildFileRel = this->AutogenIncludeDir;
- job->BuildFileRel += includeString;
- job->Includer = absFilename;
- job->IncludeString = includeString;
- this->UicJobs.push_back(std::move(job));
- }
- }
-
- return true;
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ MocAutoFiles_.emplace(mocFile);
}
-bool cmQtAutoGeneratorMocUic::UicFindIncludedFile(
- std::string& absFile, std::string const& sourceFile,
- std::string const& includeString)
+void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
{
- bool success = false;
- std::string searchFile =
- cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
- searchFile += ".ui";
- // Collect search paths list
- std::vector<std::string> testFiles;
- {
- std::string const searchPath = SubDirPrefix(includeString);
-
- std::string searchFileFull;
- if (!searchPath.empty()) {
- searchFileFull = searchPath;
- searchFileFull += searchFile;
- }
- // Vicinity of the source
- {
- std::string const sourcePath = SubDirPrefix(sourceFile);
- testFiles.push_back(sourcePath + searchFile);
- if (!searchPath.empty()) {
- testFiles.push_back(sourcePath + searchFileFull);
- }
- }
- // AUTOUIC search paths
- if (!this->UicSearchPaths.empty()) {
- for (std::string const& sPath : this->UicSearchPaths) {
- testFiles.push_back((sPath + "/").append(searchFile));
- }
- if (!searchPath.empty()) {
- for (std::string const& sPath : this->UicSearchPaths) {
- testFiles.push_back((sPath + "/").append(searchFileFull));
- }
- }
- }
- }
-
- // Search for the .ui file!
- for (std::string const& testFile : testFiles) {
- if (cmSystemTools::FileExists(testFile.c_str())) {
- absFile = cmSystemTools::GetRealPath(testFile);
- success = true;
- break;
- }
- }
-
- // Log error
- if (!success) {
- std::string emsg = "Could not find ";
- emsg += cmQtAutoGen::Quoted(searchFile);
- emsg += " in\n";
- for (std::string const& testFile : testFiles) {
- emsg += " ";
- emsg += cmQtAutoGen::Quoted(testFile);
- emsg += "\n";
- }
- this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg);
- }
-
- return success;
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ MocAutoFileUpdated_ = true;
}
-bool cmQtAutoGeneratorMocUic::UicGenerateAll()
+void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
{
- if (!this->UicEnabled()) {
- return true;
- }
-
- // Look for name collisions in included uic files
- {
- bool collision = false;
- std::map<std::string, std::vector<UicJob const*>> collisions;
- for (auto const& job : this->UicJobs) {
- auto& list = collisions[job->IncludeString];
- if (!list.empty()) {
- collision = true;
- }
- list.push_back(job.get());
- }
- if (collision) {
- std::string emsg =
- "Included uic files with the same name will be "
- "generated from different sources.\n"
- "Consider to\n"
- " - add a directory prefix to a \"ui_<NAME>.h\" include "
- "(e.g \"sub/ui_<NAME>.h\")\n"
- " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
- "include(s)\n"
- "Include conflicts\n"
- "-----------------\n";
- const auto& colls = collisions;
- for (auto const& coll : colls) {
- if (coll.second.size() > 1) {
- emsg += cmQtAutoGen::Quoted(coll.first);
- emsg += " included in\n";
- for (const UicJob* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->Includer);
- emsg += "\n";
- }
- emsg += "would be generated from\n";
- for (const UicJob* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->SourceFile);
- emsg += "\n";
- }
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ if (!JobThreadsAbort_ && Moc().Enabled) {
+ // Compose mocs compilation file content
+ {
+ std::string content =
+ "// This file is autogenerated. Changes will be overwritten.\n";
+ if (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
+ for (std::string const& mocfile : MocAutoFiles_) {
+ content += "#include \"";
+ content += mocfile;
+ content += "\"\n";
}
}
- this->LogError(cmQtAutoGen::UIC, emsg);
- return false;
- }
- }
-
- // Generate ui header files
- for (const auto& item : this->UicJobs) {
- if (!this->UicGenerateFile(*item)) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob)
-{
- bool success = true;
-
- std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, uicJob.BuildFileRel);
-
- bool generate = false;
- std::string generateReason;
- if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(uicFileAbs);
- generateReason += " from its source file ";
- generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
- generateReason += " because it doesn't exist";
- }
- generate = true;
- }
- if (!generate && this->UicSettingsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(uicFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
- generateReason += " because the UIC settings changed";
- }
- generate = true;
- }
- if (!generate) {
- std::string error;
- if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(uicFileAbs);
- generateReason += " because it's older than its source file ";
- generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
- }
- generate = true;
- } else {
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::UIC, error);
- success = false;
- }
- }
- }
- if (generate) {
- // Log
- if (this->GetVerbose()) {
- this->LogBold("Generating UIC header " + uicJob.BuildFileRel);
- this->LogInfo(cmQtAutoGen::UIC, generateReason);
- }
- // Make sure the parent directory exists
- if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) {
- // Compose uic command
- std::vector<std::string> cmd;
- cmd.push_back(this->UicExecutable);
- {
- std::vector<std::string> allOpts = this->UicTargetOptions;
- auto optionIt = this->UicOptions.find(uicJob.SourceFile);
- if (optionIt != this->UicOptions.end()) {
- cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second,
- (this->QtVersionMajor == 5));
+ std::string const& compRel = Moc().CompFileRel;
+ std::string const& compAbs = Moc().CompFileAbs;
+ if (FileSys().FileDiffers(compAbs, content)) {
+ // Actually write mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::MOC, "Generating MOC compilation " + compRel);
}
- cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
- }
- cmd.push_back("-o");
- cmd.push_back(uicFileAbs);
- cmd.push_back(uicJob.SourceFile);
-
- std::string output;
- if (this->RunCommand(cmd, output)) {
- // Success
- } else {
- // Command failed
- {
- std::string emsg = "uic failed for\n ";
- emsg += cmQtAutoGen::Quoted(uicJob.SourceFile);
- emsg += "\nincluded by\n ";
- emsg += cmQtAutoGen::Quoted(uicJob.Includer);
- this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output);
+ if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) {
+ Log().ErrorFile(GeneratorT::MOC, compAbs,
+ "mocs compilation file writing failed");
+ RegisterJobError();
+ return;
}
- cmSystemTools::RemoveFile(uicFileAbs);
- success = false;
+ } else if (MocAutoFileUpdated_) {
+ // Only touch mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compRel);
+ }
+ FileSys().Touch(compAbs);
}
- } else {
- // Parent directory creation failed
- success = false;
}
}
- return success;
-}
-
-/**
- * @brief Tries to find the header file to the given file base path by
- * appending different header extensions
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header,
- std::string const& testBasePath) const
-{
- for (std::string const& ext : this->HeaderExtensions) {
- std::string testFilePath(testBasePath);
- testFilePath.push_back('.');
- testFilePath += ext;
- if (cmSystemTools::FileExists(testFilePath.c_str())) {
- header = testFilePath;
- return true;
- }
- }
- return false;
}