summaryrefslogtreecommitdiffstats
path: root/Source/cmQtAutoMocUic.cxx
diff options
context:
space:
mode:
authorSebastian Holtermann <sebholt@xwmw.org>2019-05-02 09:03:13 (GMT)
committerSebastian Holtermann <sebholt@xwmw.org>2019-05-07 10:42:19 (GMT)
commit7d50e1c6118b292ea3061ec9fee0230c5d50a5ff (patch)
tree48d3f4c459c88cc2c02fa78b0279324e2fbb5cce /Source/cmQtAutoMocUic.cxx
parentc6f6e2b3053de02de149e80bd6a0f686a6731f68 (diff)
downloadCMake-7d50e1c6118b292ea3061ec9fee0230c5d50a5ff.zip
CMake-7d50e1c6118b292ea3061ec9fee0230c5d50a5ff.tar.gz
CMake-7d50e1c6118b292ea3061ec9fee0230c5d50a5ff.tar.bz2
Autogen: Refactor AUTOMOC and AUTOUIC and add source file parse data caching
New features ------------ CMake's `AUTOMOC` and `AUTOUIC` now cache information extracted when parsing source files in `CMakeFiles/<ORIGIN>_autogen.dir/ParseCache.txt`. This leads to faster `<ORIGIN>_autogen` target rebuilds, because source files will be parsed again only if they're newer than the `ParseCache.txt` file. The parse cache will be recomputed if it is older than the CMake executable. `AUTOMOC` and `AUTOUIC` now check if `moc` or `uic` output files are older than the `moc` or `uic` executable. If an output file is older than the compiler, it will be regenerated. Therefore if a new `moc` or `uic` version is installed, all output files will be regenerated. `AUTOMOC` and `AUTOUIC` error and warning messages are more detailed. Internal changes ---------------- `moc` and `uic` output file names are not computed in the `_autogen` target anymore but in `cmQtAutoGenInitializer`. This makes the available at the configuration stage for improved dependency computations (to be done). In `AutogenInfo.cmake`, equally sized lists for "source file names", "source file flags" and "compiler output file names" are passed to the `_autogen` target. This replaces the separate file lists for `AUTOMOC` and `AUTOUIC`. Files times are read from the file system only once by using `cmFileTime` instances instead of `cmQtAutoGenerator::FileSystem::FileIsOlderThan` calls. All calls to not thread safe file system functions are moved to non concurrent fence jobs (see `cmWorkerPool::JobT::IsFence()`). This renders the `cmQtAutoGenerator::FileSystem` wrapper class obsolete and it is removed. Instead of composing a single large settings string that is fed to the `cmCryptoHash`, now all setting sub strings are fed one by one to the `cmCryptoHash` and the finalized result is stored. The `std::mutex` in `cmQtAutoGenerator::Logger` is tagged `mutable` and most `cmQtAutoGenerator::Logger` methods become `const`. Outlook ------- This patch provides the framework required to - extract dependencies from `.ui` files in `AUTOUIC`. These will help to address issue #15420 "AUTOUIC: Track uic external inputs". - generate adaptive `make` and `ninja` files in the `_autogen` target. These will help to address issue #16776 "AUTOUIC: Ninja needs two passes to correctly build Qt project". - generate (possibly empty) `moc` and `uic` files for all headers instead of a `mocs_compilation.cpp` file. This will help to address issue #17277 "AUTOMOC: Provide a option to allow AUTOMOC to compile individual " "moc_x.cxx instead of including all in mocs_compilation.cxx"
Diffstat (limited to 'Source/cmQtAutoMocUic.cxx')
-rw-r--r--Source/cmQtAutoMocUic.cxx2725
1 files changed, 1588 insertions, 1137 deletions
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 005c27d..36794d6 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -4,7 +4,6 @@
#include <algorithm>
#include <array>
-#include <deque>
#include <list>
#include <memory>
#include <set>
@@ -13,67 +12,187 @@
#include "cmAlgorithms.h"
#include "cmCryptoHash.h"
+#include "cmGeneratedFileStream.h"
#include "cmMakefile.h"
#include "cmQtAutoGen.h"
#include "cmSystemTools.h"
#include "cmake.h"
+#include "cmsys/FStream.hxx"
#if defined(__APPLE__)
# include <unistd.h>
#endif
-// -- Class methods
+static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
+static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_"
-std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath(
- std::string const& relativePath) const
+cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key,
+ std::size_t basePrefixLength)
+ : Key(key)
+ , Dir(SubDirPrefix(key))
+ , Base(cmSystemTools::GetFilenameWithoutLastExtension(key))
{
- return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
+ if (basePrefixLength != 0) {
+ Base = Base.substr(basePrefixLength);
+ }
}
-/**
- * @brief Tries to find the header file to the given file base path by
- * appending different header extensions
- * @return True on success
- */
-bool cmQtAutoMocUic::BaseSettingsT::FindHeader(
- std::string& header, std::string const& testBasePath) const
+void cmQtAutoMocUic::ParseCacheT::FileT::Clear()
{
- for (std::string const& ext : HeaderExtensions) {
- std::string testFilePath(testBasePath);
- testFilePath.push_back('.');
- testFilePath += ext;
- if (FileSys->FileExists(testFilePath)) {
- header = testFilePath;
- return true;
+ Moc.Macro.clear();
+ Moc.Include.Underscore.clear();
+ Moc.Include.Dot.clear();
+ Moc.Depends.clear();
+
+ Uic.Include.clear();
+ Uic.Depends.clear();
+}
+
+cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get(
+ std::string const& fileName) const
+{
+ auto it = Map_.find(fileName);
+ if (it != Map_.end()) {
+ return it->second;
+ }
+ return FileHandleT();
+}
+
+cmQtAutoMocUic::ParseCacheT::GetOrInsertT
+cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName)
+{
+ // Find existing entry
+ {
+ auto it = Map_.find(fileName);
+ if (it != Map_.end()) {
+ return GetOrInsertT{ it->second, false };
}
}
- return false;
+
+ // Insert new entry
+ return GetOrInsertT{
+ Map_.emplace(fileName, std::make_shared<FileT>()).first->second, true
+ };
}
-bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
+cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default;
+cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default;
+
+void cmQtAutoMocUic::ParseCacheT::Clear()
{
- return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+ Map_.clear();
}
-/**
- * @brief Returns the first relevant Qt macro name found in the given C++ code
- * @return The name of the Qt macro or an empty string
- */
-std::string cmQtAutoMocUic::MocSettingsT::FindMacro(
- std::string const& content) const
+bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName)
{
- for (KeyExpT const& filter : MacroFilters) {
- // Run a simple find string operation before the expensive
- // regular expression check
- if (content.find(filter.Key) != std::string::npos) {
- cmsys::RegularExpressionMatch match;
- if (filter.Exp.find(content.c_str(), match)) {
- // Return macro name on demand
- return filter.Key;
+ cmsys::ifstream fin(fileName.c_str());
+ if (!fin) {
+ return false;
+ }
+ FileHandleT fileHandle;
+
+ std::string line;
+ while (std::getline(fin, line)) {
+ // Check if this an empty or a comment line
+ if (line.empty() || line.front() == '#') {
+ continue;
+ }
+ // Drop carriage return character at the end
+ if (line.back() == '\r') {
+ line.pop_back();
+ if (line.empty()) {
+ continue;
}
}
+ // Check if this a file name line
+ if (line.front() != ' ') {
+ fileHandle = GetOrInsert(line).first;
+ continue;
+ }
+
+ // Bad line or bad file handle
+ if (!fileHandle || (line.size() < 6)) {
+ continue;
+ }
+
+ constexpr std::size_t offset = 5;
+ if (cmHasLiteralPrefix(line, " mmc:")) {
+ fileHandle->Moc.Macro = line.substr(offset);
+ continue;
+ }
+ if (cmHasLiteralPrefix(line, " miu:")) {
+ fileHandle->Moc.Include.Underscore.emplace_back(line.substr(offset),
+ MocUnderscoreLength);
+ continue;
+ }
+ if (cmHasLiteralPrefix(line, " mid:")) {
+ fileHandle->Moc.Include.Dot.emplace_back(line.substr(offset), 0);
+ continue;
+ }
+ if (cmHasLiteralPrefix(line, " mdp:")) {
+ fileHandle->Moc.Depends.emplace_back(line.substr(offset));
+ continue;
+ }
+ if (cmHasLiteralPrefix(line, " uic:")) {
+ fileHandle->Uic.Include.emplace_back(line.substr(offset),
+ UiUnderscoreLength);
+ continue;
+ }
+ if (cmHasLiteralPrefix(line, " udp:")) {
+ fileHandle->Uic.Depends.emplace_back(line.substr(offset));
+ continue;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName)
+{
+ cmGeneratedFileStream ofs(fileName);
+ if (!ofs) {
+ return false;
+ }
+ ofs << "# Generated by CMake. Changes will be overwritten." << std::endl;
+ for (auto const& pair : Map_) {
+ ofs << pair.first << std::endl;
+ FileT const& file = *pair.second;
+ if (!file.Moc.Macro.empty()) {
+ ofs << " mmc:" << file.Moc.Macro << std::endl;
+ }
+ for (IncludeKeyT const& item : file.Moc.Include.Underscore) {
+ ofs << " miu:" << item.Key << std::endl;
+ }
+ for (IncludeKeyT const& item : file.Moc.Include.Dot) {
+ ofs << " mid:" << item.Key << std::endl;
+ }
+ for (std::string const& item : file.Moc.Depends) {
+ ofs << " mdp:" << item << std::endl;
+ }
+ for (IncludeKeyT const& item : file.Uic.Include) {
+ ofs << " uic:" << item.Key << std::endl;
+ }
+ for (std::string const& item : file.Uic.Depends) {
+ ofs << " udp:" << item << std::endl;
+ }
}
- return std::string();
+ return ofs.Close();
+}
+
+cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default;
+cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default;
+
+cmQtAutoMocUic::MocSettingsT::MocSettingsT()
+{
+ RegExpInclude.compile(
+ "(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
+}
+
+cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default;
+
+bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
+{
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
}
std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
@@ -98,53 +217,13 @@ std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
return res;
}
-std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile(
- std::string const& sourcePath, std::string const& includeString) const
+cmQtAutoMocUic::UicSettingsT::UicSettingsT()
{
- // Search in vicinity of the source
- {
- std::string testPath = sourcePath;
- testPath += includeString;
- if (FileSys->FileExists(testPath)) {
- return FileSys->GetRealPath(testPath);
- }
- }
- // Search in include directories
- for (std::string const& path : IncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeString;
- if (FileSys->FileExists(fullPath)) {
- return FileSys->GetRealPath(fullPath);
- }
- }
- // Return empty string
- return std::string();
+ RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
}
-void cmQtAutoMocUic::MocSettingsT::FindDependencies(
- std::string const& content, std::set<std::string>& depends) const
-{
- if (!DependFilters.empty() && !content.empty()) {
- for (KeyExpT const& filter : DependFilters) {
- // Run a simple find string check
- if (content.find(filter.Key) != std::string::npos) {
- // Run the expensive regular expression check loop
- const char* contentChars = content.c_str();
- cmsys::RegularExpressionMatch match;
- while (filter.Exp.find(contentChars, match)) {
- {
- std::string dep = match.match(1);
- if (!dep.empty()) {
- depends.emplace(std::move(dep));
- }
- }
- contentChars += match.end();
- }
- }
- }
- }
-}
+cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default;
bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const
{
@@ -176,498 +255,844 @@ void cmQtAutoMocUic::JobT::LogCommandError(
bool cmQtAutoMocUic::JobT::RunProcess(GenT genType,
cmWorkerPool::ProcessResultT& result,
- std::vector<std::string> const& command)
+ std::vector<std::string> const& command,
+ std::string* infoMessage)
{
// Log command
if (Log().Verbose()) {
- std::string msg = "Running command:\n";
+ std::string msg;
+ if ((infoMessage != nullptr) && !infoMessage->empty()) {
+ msg = *infoMessage;
+ if (msg.back() != '\n') {
+ msg += '\n';
+ }
+ }
msg += QuotedCommand(command);
msg += '\n';
Log().Info(genType, msg);
}
return cmWorkerPool::JobT::RunProcess(result, command,
- Gen()->Base().AutogenBuildDir);
+ BaseConst().AutogenBuildDir);
}
void cmQtAutoMocUic::JobMocPredefsT::Process()
{
// (Re)generate moc_predefs.h on demand
- bool generate(false);
- bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs));
- if (!fileExists) {
- if (Log().Verbose()) {
- std::string reason = "Generating ";
- reason += Quoted(Gen()->Moc().PredefsFileRel);
- reason += " because it doesn't exist";
- Log().Info(GenT::MOC, reason);
- }
- generate = true;
- } else if (Gen()->Moc().SettingsChanged) {
- if (Log().Verbose()) {
- std::string reason = "Generating ";
- reason += Quoted(Gen()->Moc().PredefsFileRel);
- reason += " because the settings changed.";
- Log().Info(GenT::MOC, reason);
- }
- generate = true;
+ std::unique_ptr<std::string> reason;
+ if (Log().Verbose()) {
+ reason = cm::make_unique<std::string>();
+ }
+ if (!Update(reason.get())) {
+ return;
}
- if (generate) {
+ std::string const& predefsFileRel = MocConst().PredefsFileRel;
+ std::string const& predefsFileAbs = MocConst().PredefsFileAbs;
+ {
cmWorkerPool::ProcessResultT result;
{
// Compose command
- std::vector<std::string> cmd = Gen()->Moc().PredefsCmd;
+ std::vector<std::string> cmd = MocConst().PredefsCmd;
// Add includes
- cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(),
- Gen()->Moc().Includes.end());
+ cmd.insert(cmd.end(), MocConst().Includes.begin(),
+ MocConst().Includes.end());
// Add definitions
- for (std::string const& def : Gen()->Moc().Definitions) {
+ for (std::string const& def : MocConst().Definitions) {
cmd.push_back("-D" + def);
}
// Execute command
- if (!RunProcess(GenT::MOC, result, cmd)) {
- std::string emsg = "The content generation command for ";
- emsg += Quoted(Gen()->Moc().PredefsFileRel);
- emsg += " failed.\n";
- emsg += result.ErrorMessage;
- LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+ if (!RunProcess(GenT::MOC, result, cmd, reason.get())) {
+ std::string msg = "The content generation command for ";
+ msg += Quoted(predefsFileRel);
+ msg += " failed.\n";
+ msg += result.ErrorMessage;
+ LogCommandError(GenT::MOC, msg, cmd, result.StdOut);
+ return;
}
}
// (Re)write predefs file only on demand
- if (!result.error()) {
- if (!fileExists ||
- FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
- if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
- // Success
- } else {
- std::string emsg = "Writing ";
- emsg += Quoted(Gen()->Moc().PredefsFileRel);
- emsg += " failed.";
- LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg);
- }
- } else {
- // Touch to update the time stamp
- if (Log().Verbose()) {
- std::string msg = "Touching ";
- msg += Quoted(Gen()->Moc().PredefsFileRel);
- msg += ".";
- Log().Info(GenT::MOC, msg);
- }
- FileSys().Touch(Gen()->Moc().PredefsFileAbs);
+ if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) {
+ if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) {
+ std::string msg = "Writing ";
+ msg += Quoted(predefsFileRel);
+ msg += " failed.";
+ LogFileError(GenT::MOC, predefsFileAbs, msg);
+ return;
+ }
+ } else {
+ // Touch to update the time stamp
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Touching " + Quoted(predefsFileRel));
+ }
+ if (!cmSystemTools::Touch(predefsFileAbs, false)) {
+ std::string msg = "Touching ";
+ msg += Quoted(predefsFileAbs);
+ msg += " failed.";
+ LogFileError(GenT::MOC, predefsFileAbs, msg);
+ return;
}
}
}
+
+ // Read file time afterwards
+ if (!MocEval().PredefsTime.Load(predefsFileAbs)) {
+ LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed.");
+ return;
+ }
}
-void cmQtAutoMocUic::JobParseT::Process()
+bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const
{
- if (AutoMoc && Header) {
- // Don't parse header for moc if the file is included by a source already
- if (Gen()->ParallelMocIncluded(FileName)) {
- AutoMoc = false;
+ // Test if the file exists
+ if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(MocConst().PredefsFileRel);
+ *reason += ", because it doesn't exist.";
}
+ return true;
}
- if (AutoMoc || AutoUic) {
- std::string error;
- MetaT meta;
- if (FileSys().FileRead(meta.Content, FileName, &error)) {
- if (!meta.Content.empty()) {
- meta.FileDir = FileSys().SubDirPrefix(FileName);
- meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName);
-
- bool success = true;
- if (AutoMoc) {
- if (Header) {
- success = ParseMocHeader(meta);
- } else {
- success = ParseMocSource(meta);
- }
- }
- if (AutoUic && success) {
- ParseUic(meta);
+ // Test if the settings changed
+ if (MocConst().SettingsChanged) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(MocConst().PredefsFileRel);
+ *reason += ", because the moc settings changed.";
+ }
+ return true;
+ }
+
+ // Test if the executable is newer
+ {
+ std::string const& exec = MocConst().PredefsCmd.at(0);
+ cmFileTime execTime;
+ if (execTime.Load(exec)) {
+ if (MocEval().PredefsTime.Older(execTime)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(MocConst().PredefsFileRel);
+ *reason += " because it is older than ";
+ *reason += Quoted(exec);
+ *reason += ".";
}
- } else {
- Log().WarningFile(GenT::GEN, FileName, "The source file is empty");
+ return true;
}
- } else {
- LogFileError(GenT::GEN, FileName, "Could not read the file: " + error);
}
}
+
+ return false;
}
-bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta)
+bool cmQtAutoMocUic::JobParseT::ReadFile()
{
- struct JobPre
+ // Clear old parse information
+ FileHandle->ParseData->Clear();
+ std::string const& fileName = FileHandle->FileName;
+ // Write info
+ if (Log().Verbose()) {
+ Log().Info(GenT::GEN, "Parsing " + Quoted(fileName));
+ }
+ // Read file content
{
- bool self; // source file is self
- bool underscore; // "moc_" style include
- std::string SourceFile;
- std::string IncludeString;
- };
+ std::string error;
+ if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) {
+ LogFileError(GenT::GEN, fileName, "Could not read the file: " + error);
+ return false;
+ }
+ }
+ // Warn if empty
+ if (Content.empty()) {
+ Log().WarningFile(GenT::GEN, fileName, "The file is empty.");
+ return false;
+ }
+ return true;
+}
+
+void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container,
+ std::set<std::string> const& source,
+ std::size_t basePrefixLength)
+{
+ if (source.empty()) {
+ return;
+ }
+ container.reserve(source.size());
+ for (std::string const& src : source) {
+ container.emplace_back(src, basePrefixLength);
+ }
+}
+
+void cmQtAutoMocUic::JobParseT::MocMacro()
+{
+ for (KeyExpT const& filter : MocConst().MacroFilters) {
+ // Run a simple find string check
+ if (Content.find(filter.Key) == std::string::npos) {
+ continue;
+ }
+ // Run the expensive regular expression check loop
+ cmsys::RegularExpressionMatch match;
+ if (filter.Exp.find(Content.c_str(), match)) {
+ // Keep detected macro name
+ FileHandle->ParseData->Moc.Macro = filter.Key;
+ return;
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobParseT::MocDependecies()
+{
+ if (MocConst().DependFilters.empty()) {
+ return;
+ }
- struct MocInclude
+ // Find dependency strings
+ std::set<std::string> parseDepends;
+ for (KeyExpT const& filter : MocConst().DependFilters) {
+ // Run a simple find string check
+ if (Content.find(filter.Key) == std::string::npos) {
+ continue;
+ }
+ // Run the expensive regular expression check loop
+ const char* contentChars = Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (filter.Exp.find(contentChars, match)) {
+ {
+ std::string dep = match.match(1);
+ if (!dep.empty()) {
+ parseDepends.emplace(std::move(dep));
+ }
+ }
+ contentChars += match.end();
+ }
+ }
+
+ // Store dependency strings
{
- std::string Inc; // full include string
- std::string Dir; // include string directory
- std::string Base; // include string file base
- };
+ auto& Depends = FileHandle->ParseData->Moc.Depends;
+ Depends.reserve(parseDepends.size());
+ for (std::string const& item : parseDepends) {
+ Depends.emplace_back(item);
+ // Replace end of line characters in filenames
+ std::string& path = Depends.back();
+ std::replace(path.begin(), path.end(), '\n', ' ');
+ std::replace(path.begin(), path.end(), '\r', ' ');
+ }
+ }
+}
- // Check if this source file contains a relevant macro
- std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content);
+void cmQtAutoMocUic::JobParseT::MocIncludes()
+{
+ if (Content.find("moc") == std::string::npos) {
+ return;
+ }
- // Extract moc includes from file
- std::deque<MocInclude> mocIncsUsc;
- std::deque<MocInclude> mocIncsDot;
+ std::set<std::string> underscore;
+ std::set<std::string> dot;
{
- if (meta.Content.find("moc") != std::string::npos) {
- const char* contentChars = meta.Content.c_str();
- cmsys::RegularExpressionMatch match;
- while (Gen()->Moc().RegExpInclude.find(contentChars, match)) {
- std::string incString = match.match(2);
- std::string incDir(FileSys().SubDirPrefix(incString));
- std::string incBase =
- FileSys().GetFilenameWithoutLastExtension(incString);
- if (cmHasLiteralPrefix(incBase, "moc_")) {
- // moc_<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();
+ const char* contentChars = Content.c_str();
+ cmsys::RegularExpression const& regExp = MocConst().RegExpInclude;
+ cmsys::RegularExpressionMatch match;
+ while (regExp.find(contentChars, match)) {
+ std::string incString = match.match(2);
+ std::string const incBase =
+ cmSystemTools::GetFilenameWithoutLastExtension(incString);
+ if (cmHasLiteralPrefix(incBase, "moc_")) {
+ // moc_<BASE>.cpp
+ // Remove the moc_ part from the base name
+ underscore.emplace(std::move(incString));
+ } else {
+ // <BASE>.moc
+ dot.emplace(std::move(incString));
+ }
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
+ auto& Include = FileHandle->ParseData->Moc.Include;
+ CreateKeys(Include.Underscore, underscore, MocUnderscoreLength);
+ CreateKeys(Include.Dot, dot, 0);
+}
+
+void cmQtAutoMocUic::JobParseT::UicIncludes()
+{
+ if (Content.find("ui_") == std::string::npos) {
+ return;
+ }
+
+ std::set<std::string> includes;
+ {
+ const char* contentChars = Content.c_str();
+ cmsys::RegularExpression const& regExp = UicConst().RegExpInclude;
+ cmsys::RegularExpressionMatch match;
+ while (regExp.find(contentChars, match)) {
+ includes.emplace(match.match(2));
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
+ CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength);
+}
+
+void cmQtAutoMocUic::JobParseHeaderT::Process()
+{
+ if (!ReadFile()) {
+ return;
+ }
+ // Moc parsing
+ if (FileHandle->Moc) {
+ MocMacro();
+ MocDependecies();
+ }
+ // Uic parsing
+ if (FileHandle->Uic) {
+ UicIncludes();
+ }
+}
+
+void cmQtAutoMocUic::JobParseSourceT::Process()
+{
+ if (!ReadFile()) {
+ return;
+ }
+ // Moc parsing
+ if (FileHandle->Moc) {
+ MocMacro();
+ MocDependecies();
+ MocIncludes();
+ }
+ // Uic parsing
+ if (FileHandle->Uic) {
+ UicIncludes();
+ }
+}
+
+void cmQtAutoMocUic::JobEvaluateT::Process()
+{
+ // Evaluate for moc
+ if (MocConst().Enabled) {
+ // Evaluate headers
+ for (auto const& pair : BaseEval().Headers) {
+ if (!MocEvalHeader(pair.second)) {
+ return;
+ }
+ }
+ // Evaluate sources
+ for (auto const& pair : BaseEval().Sources) {
+ if (!MocEvalSource(pair.second)) {
+ return;
}
}
}
+ // Evaluate for uic
+ if (UicConst().Enabled) {
+ if (!UicEval(BaseEval().Headers) || !UicEval(BaseEval().Sources)) {
+ return;
+ }
+ }
- // Check if there is anything to do
- if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
+ // Add discovered header parse jobs
+ Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered);
+ // Add generate job after
+ Gen()->WorkerPool().EmplaceJob<JobGenerateT>();
+}
+
+bool cmQtAutoMocUic::JobEvaluateT::MocEvalHeader(SourceFileHandleT source)
+{
+ SourceFileT const& sourceFile = *source;
+ auto const& parseData = sourceFile.ParseData->Moc;
+ if (!source->Moc) {
return true;
}
- bool ownDotMocIncluded = false;
- bool ownMocUscIncluded = false;
- std::deque<JobPre> jobs;
+ if (!parseData.Macro.empty()) {
+ // Create a new mapping
+ MappingHandleT handle = std::make_shared<MappingT>();
+ handle->SourceFile = std::move(source);
- // Process moc_<BASE>.cxx includes
- for (const MocInclude& mocInc : mocIncsUsc) {
- std::string const header =
- MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
- if (!header.empty()) {
- // Check if header is skipped
- if (Gen()->Moc().skipped(header)) {
- continue;
+ // Absolute build path
+ if (BaseConst().MultiConfig) {
+ handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath);
+ } else {
+ handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath);
+ }
+
+ // Register mapping in headers map
+ MocRegisterMapping(handle, true);
+ }
+
+ return true;
+}
+
+bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource(
+ SourceFileHandleT const& source)
+{
+ SourceFileT const& sourceFile = *source;
+ auto const& parseData = sourceFile.ParseData->Moc;
+ if (!sourceFile.Moc ||
+ (parseData.Macro.empty() && parseData.Include.Underscore.empty() &&
+ parseData.Include.Dot.empty())) {
+ return true;
+ }
+
+ std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
+ std::string const sourceBase =
+ cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName);
+
+ // For relaxed mode check if the own "moc_" or ".moc" file is included
+ bool const relaxedMode = MocConst().RelaxedMode;
+ bool sourceIncludesMocUnderscore = false;
+ bool sourceIncludesDotMoc = false;
+ // Check if the sources own "moc_" or ".moc" file is included
+ if (relaxedMode) {
+ for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
+ if (incKey.Base == sourceBase) {
+ sourceIncludesMocUnderscore = true;
+ break;
}
- // 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;
+ }
+ }
+ for (IncludeKeyT const& incKey : parseData.Include.Dot) {
+ if (incKey.Base == sourceBase) {
+ sourceIncludesDotMoc = true;
+ break;
+ }
+ }
+
+ // Check if this source needs to be moc processed but doesn't.
+ if (!sourceIncludesDotMoc && !parseData.Macro.empty() &&
+ !(relaxedMode && sourceIncludesMocUnderscore)) {
+ {
+ std::string emsg = "The file contains a ";
+ emsg += Quoted(parseData.Macro);
+ emsg += " macro, but does not include ";
+ emsg += Quoted(sourceBase + ".moc");
+ emsg += "!\nConsider to\n - add #include \"";
+ emsg += sourceBase;
+ emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
+ LogFileError(GenT::MOC, sourceFile.FileName, emsg);
+ }
+ return false;
+ }
+
+ // Evaluate "moc_" includes
+ for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
+ std::string const headerBase = incKey.Dir + incKey.Base;
+ SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
+ if (!header) {
+ {
+ std::string msg = "The file includes the moc file ";
+ msg += Quoted(incKey.Key);
+ msg += ",\nbut the header could not be found "
+ "in the following locations\n";
+ msg += MocMessageTestHeaders(headerBase);
+ LogFileError(GenT::MOC, sourceFile.FileName, msg);
}
- } else {
+ return false;
+ }
+ // The include might be handled differently in relaxed mode
+ if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() &&
+ (incKey.Base == sourceBase)) {
+ // The <BASE>.cpp file includes a Qt macro but does not include the
+ // <BASE>.moc file. In this case, the moc_<BASE>.cpp should probably
+ // be generated from <BASE>.cpp instead of <BASE>.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.
{
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += ", but the header ";
- emsg += Quoted(MocStringHeaders(mocInc.Base));
- emsg += " could not be found.";
- LogFileError(GenT::MOC, FileName, emsg);
+ // Issue a warning
+ std::string msg = "The file contains a ";
+ msg += Quoted(parseData.Macro);
+ msg += " macro, but does not include ";
+ msg += Quoted(sourceBase + ".moc");
+ msg += ".\nInstead it includes ";
+ msg += Quoted(incKey.Key);
+ msg += ".\nRunning moc on the source\n ";
+ msg += Quoted(sourceFile.FileName);
+ msg += "!\nBetter include ";
+ msg += Quoted(sourceBase + ".moc");
+ msg += " for compatibility with strict mode.\n";
+ msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
+ Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
}
+ // Create mapping
+ if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
+ return false;
+ }
+ continue;
+ }
+
+ // Check if header is skipped
+ if (MocConst().skipped(header->FileName)) {
+ continue;
+ }
+ // Create mapping
+ if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) {
return false;
}
}
- // Process <BASE>.moc includes
- for (const MocInclude& mocInc : mocIncsDot) {
- const bool ownMoc = (mocInc.Base == meta.FileBase);
- if (Gen()->Moc().RelaxedMode) {
- // Relaxed mode
- if (!ownMacro.empty() && ownMoc) {
- // Add self
- jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
- ownDotMocIncluded = true;
- } else {
- // In relaxed mode try to find a header instead but issue a warning.
- // This is for KDE4 compatibility
- std::string const header =
- MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
- if (!header.empty()) {
- // Check if header is skipped
- if (Gen()->Moc().skipped(header)) {
- continue;
- }
- // Register moc job
- jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
- if (ownMacro.empty()) {
- if (ownMoc) {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += ", but does not contain a ";
- emsg += Gen()->Moc().MacrosString();
- emsg += " macro.\nRunning moc on\n ";
- emsg += Quoted(header);
- emsg += "!\nBetter include ";
- emsg += Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for a compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- Log().WarningFile(GenT::MOC, FileName, emsg);
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += " instead of ";
- emsg += Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += ".\nRunning moc on\n ";
- emsg += Quoted(header);
- emsg += "!\nBetter include ";
- emsg += Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- Log().WarningFile(GenT::MOC, FileName, emsg);
- }
- }
- } else {
- {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
- "matching header ";
- emsg += Quoted(MocStringHeaders(mocInc.Base));
- emsg += " could not be found.";
- LogFileError(GenT::MOC, FileName, emsg);
- }
+ // Evaluate ".moc" includes
+ if (relaxedMode) {
+ // Relaxed mode
+ for (IncludeKeyT const& incKey : parseData.Include.Dot) {
+ // Check if this is the sources own .moc file
+ bool const ownMoc = (incKey.Base == sourceBase);
+ if (ownMoc && !parseData.Macro.empty()) {
+ // Create mapping for the regular use case
+ if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
return false;
}
+ continue;
}
- } else {
- // Strict mode
- if (ownMoc) {
- // Include self
- jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
- ownDotMocIncluded = true;
- // Accept but issue a warning if moc isn't required
- if (ownMacro.empty()) {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += ", but does not contain a ";
- emsg += Gen()->Moc().MacrosString();
- emsg += " macro.";
- Log().WarningFile(GenT::MOC, FileName, emsg);
- }
+ // Try to find a header instead but issue a warning.
+ // This is for KDE4 compatibility.
+ std::string const headerBase = incKey.Dir + incKey.Base;
+ SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
+ if (!header) {
+ std::string msg = "The file includes the moc file ";
+ msg += Quoted(incKey.Key);
+ msg += ",\nwhich seems to be the moc file from a different source "
+ "file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a matching header"
+ "could not be found in the following locations\n";
+ msg += MocMessageTestHeaders(headerBase);
+ LogFileError(GenT::MOC, sourceFile.FileName, msg);
+ return false;
+ }
+ // Check if header is skipped
+ if (MocConst().skipped(header->FileName)) {
+ continue;
+ }
+ // Issue a warning
+ if (ownMoc && parseData.Macro.empty()) {
+ std::string msg = "The file includes the moc file ";
+ msg += Quoted(incKey.Key);
+ msg += ", but does not contain a\n";
+ msg += MocConst().MacrosString();
+ msg += " macro.\nRunning moc on the header\n ";
+ msg += Quoted(header->FileName);
+ msg += "!\nBetter include ";
+ msg += Quoted("moc_" + incKey.Base + ".cpp");
+ msg += " for a compatibility with strict mode.\n";
+ msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
+ Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
} else {
- // Don't allow <BASE>.moc include other than self in strict mode
- {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file.\nThis is not supported. Include ";
- emsg += Quoted(meta.FileBase + ".moc");
- emsg += " to run moc on this source file.";
- LogFileError(GenT::MOC, FileName, emsg);
- }
+ std::string msg = "The file includes the moc file ";
+ msg += Quoted(incKey.Key);
+ msg += " instead of ";
+ msg += Quoted("moc_" + incKey.Base + ".cpp");
+ msg += ".\nRunning moc on the header\n ";
+ msg += Quoted(header->FileName);
+ msg += "!\nBetter include ";
+ msg += Quoted("moc_" + incKey.Base + ".cpp");
+ msg += " for compatibility with strict mode.\n";
+ msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
+ Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
+ }
+ // Create mapping
+ if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) {
return false;
}
}
- }
-
- if (!ownMacro.empty() && !ownDotMocIncluded) {
- // In this case, check whether the scanned file itself contains a
- // Q_OBJECT.
- // If this is the case, the moc_foo.cpp should probably be generated from
- // foo.cpp instead of foo.h, because otherwise it won't build.
- // But warn, since this is not how it is supposed to be used.
- // This is for KDE4 compatibility.
- if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) {
- JobPre uscJobPre;
- // Remove underscore job request
- {
- auto itC = jobs.begin();
- auto itE = jobs.end();
- for (; itC != itE; ++itC) {
- JobPre& job(*itC);
- if (job.self && job.underscore) {
- uscJobPre = std::move(job);
- jobs.erase(itC);
- break;
- }
- }
+ } else {
+ // Strict mode
+ for (IncludeKeyT const& incKey : parseData.Include.Dot) {
+ // Check if this is the sources own .moc file
+ bool const ownMoc = (incKey.Base == sourceBase);
+ if (!ownMoc) {
+ // Don't allow <BASE>.moc include other than own in strict mode
+ std::string msg = "The file includes the moc file ";
+ msg += Quoted(incKey.Key);
+ msg += ",\nwhich seems to be the moc file from a different "
+ "source file.\nThis is not supported. Include ";
+ msg += Quoted(sourceBase + ".moc");
+ msg += " to run moc on this source file.";
+ LogFileError(GenT::MOC, sourceFile.FileName, msg);
+ return false;
}
- // Issue a warning
- {
- std::string emsg = "The file contains a ";
- emsg += ownMacro;
- emsg += " macro, but does not include ";
- emsg += Quoted(meta.FileBase + ".moc");
- emsg += ". Instead it includes ";
- emsg += Quoted(uscJobPre.IncludeString);
- emsg += ".\nRunning moc on\n ";
- emsg += Quoted(FileName);
- emsg += "!\nBetter include ";
- emsg += Quoted(meta.FileBase + ".moc");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
- Log().WarningFile(GenT::MOC, FileName, emsg);
- }
- // Add own source job
- jobs.emplace_back(
- JobPre{ true, false, FileName, uscJobPre.IncludeString });
- } else {
- // Otherwise always error out since it will not compile.
- {
- std::string emsg = "The file contains a ";
- emsg += ownMacro;
- emsg += " macro, but does not include ";
- emsg += Quoted(meta.FileBase + ".moc");
- emsg += "!\nConsider to\n - add #include \"";
- emsg += meta.FileBase;
- emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
- LogFileError(GenT::MOC, FileName, emsg);
+ // Accept but issue a warning if moc isn't required
+ if (parseData.Macro.empty()) {
+ std::string msg = "The file includes the moc file ";
+ msg += Quoted(incKey.Key);
+ msg += ", but does not contain a ";
+ msg += MocConst().MacrosString();
+ msg += " macro.";
+ Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
+ }
+ // Create mapping
+ if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
+ return false;
}
- return false;
}
}
- // Convert pre jobs to actual jobs
- for (JobPre& jobPre : jobs) {
- cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
- std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
- if (jobPre.self) {
- // Read dependencies from this source
- JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
- Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
- jobMoc.DependsValid = true;
+ return true;
+}
+
+cmQtAutoMocUic::SourceFileHandleT
+cmQtAutoMocUic::JobEvaluateT::MocFindIncludedHeader(
+ std::string const& includerDir, std::string const& includeBase) const
+{
+ // Search in vicinity of the source
+ {
+ SourceFileHandleT res = MocFindHeader(includerDir + includeBase);
+ if (res) {
+ return res;
}
- if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) {
- return false;
+ }
+ // Search in include directories
+ for (std::string const& path : MocConst().IncludePaths) {
+ std::string testPath = path;
+ testPath += '/';
+ testPath += includeBase;
+ SourceFileHandleT res = MocFindHeader(testPath);
+ if (res) {
+ return res;
}
}
- return true;
+ // Return without success
+ return SourceFileHandleT();
}
-bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta)
+cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader(
+ std::string const& basePath) const
{
- bool success = true;
- std::string const macroName = Gen()->Moc().FindMacro(meta.Content);
- if (!macroName.empty()) {
- cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
- std::string(FileName), std::string(), std::string());
- // Read dependencies from this source
- {
- JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
- Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
- jobMoc.DependsValid = true;
+ std::string testPath;
+ testPath.reserve(basePath.size() + 8);
+ for (std::string const& ext : BaseConst().HeaderExtensions) {
+ testPath.clear();
+ testPath += basePath;
+ testPath += '.';
+ testPath += ext;
+ cmFileTime fileTime;
+ if (fileTime.Load(testPath)) {
+ // Compute real path of the file
+ testPath = cmSystemTools::GetRealPath(testPath);
+ // Return a known file if it exists already
+ {
+ auto it = BaseEval().Headers.find(testPath);
+ if (it != BaseEval().Headers.end()) {
+ return it->second;
+ }
+ }
+ // Created and return discovered file entry
+ SourceFileHandleT& res = MocEval().HeadersDiscovered[testPath];
+ if (!res) {
+ res = std::make_shared<SourceFileT>(testPath);
+ res->FileTime = fileTime;
+ res->Moc = true;
+ }
+ return res;
}
- success = Gen()->ParallelJobPushMoc(std::move(jobHandle));
}
- return success;
+ // Return without success
+ return SourceFileHandleT();
}
-std::string cmQtAutoMocUic::JobParseT::MocStringHeaders(
+std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders(
std::string const& fileBase) const
{
- std::string res = fileBase;
- res += ".{";
- res += cmJoin(Gen()->Base().HeaderExtensions, ",");
- res += "}";
- return res;
+ std::ostringstream res;
+ {
+ std::string exts = ".{";
+ exts += cmJoin(BaseConst().HeaderExtensions, ",");
+ exts += '}';
+ // Compose result string
+ res << " " << fileBase << exts << '\n';
+ for (std::string const& path : MocConst().IncludePaths) {
+ res << " " << path << '/' << fileBase << exts << '\n';
+ }
+ }
+ return res.str();
}
-std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader(
- std::string const& includerDir, std::string const& includeBase)
+bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded(
+ std::string const& includeString, SourceFileHandleT includerFileHandle,
+ SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const
{
- std::string header;
- // Search in vicinity of the source
- if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) {
- // Search in include directories
- for (std::string const& path : Gen()->Moc().IncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeBase;
- if (Gen()->Base().FindHeader(header, fullPath)) {
- break;
+ // Check if this file is already included
+ MappingHandleT& handle = MocEval().Includes[includeString];
+ if (handle) {
+ // Check if the output file would be generated from different source files
+ if (handle->SourceFile != sourceFileHandle) {
+ std::string msg = "The source files\n ";
+ msg += Quoted(includerFileHandle->FileName);
+ msg += '\n';
+ for (auto const& item : handle->IncluderFiles) {
+ msg += " ";
+ msg += Quoted(item->FileName);
+ msg += '\n';
}
+ msg += "contain the same include string ";
+ msg += Quoted(includeString);
+ msg += ", but\nthe moc file would be generated from different "
+ "source files\n ";
+ msg += Quoted(sourceFileHandle->FileName);
+ msg += " and\n ";
+ msg += Quoted(handle->SourceFile->FileName);
+ msg += ".\nConsider to\n"
+ " - not include the \"moc_<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";
+ LogError(GenT::MOC, msg);
+ return false;
}
+
+ // The same mapping already exists. Just add to the includers list.
+ handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
+ return true;
}
- // Sanitize
- if (!header.empty()) {
- header = FileSys().GetRealPath(header);
+
+ // Create a new mapping
+ handle = std::make_shared<MappingT>();
+ handle->IncludeString = includeString;
+ handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
+ handle->SourceFile = std::move(sourceFileHandle);
+ handle->OutputFile += Gen()->AbsoluteIncludePath(includeString);
+
+ // Register mapping in sources/headers map
+ MocRegisterMapping(handle, sourceIsHeader);
+ return true;
+}
+
+void cmQtAutoMocUic::JobEvaluateT::MocRegisterMapping(
+ MappingHandleT mappingHandle, bool sourceIsHeader) const
+{
+ auto& regMap =
+ sourceIsHeader ? MocEval().HeaderMappings : MocEval().SourceMappings;
+ // Check if source file already gets mapped
+ auto& regHandle = regMap[mappingHandle->SourceFile->FileName];
+ if (!regHandle) {
+ // Yet unknown mapping
+ regHandle = std::move(mappingHandle);
+ } else {
+ // Mappings with include string override those without
+ if (!mappingHandle->IncludeString.empty()) {
+ regHandle = std::move(mappingHandle);
+ }
}
- return header;
}
-bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta)
+bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap)
{
- bool success = true;
- if (meta.Content.find("ui_") != std::string::npos) {
- const char* contentChars = meta.Content.c_str();
- cmsys::RegularExpressionMatch match;
- while (Gen()->Uic().RegExpInclude.find(contentChars, match)) {
- if (!ParseUicInclude(meta, match.match(2))) {
- success = false;
- break;
- }
- contentChars += match.end();
+ for (auto const& pair : fileMap) {
+ if (!UicEvalFile(pair.second)) {
+ return false;
}
}
- return success;
+ return true;
}
-bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta,
- std::string&& includeString)
+bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile(
+ SourceFileHandleT sourceFileHandle)
{
- bool success = false;
- std::string uiInputFile = UicFindIncludedFile(meta, includeString);
- if (!uiInputFile.empty()) {
- if (!Gen()->Uic().skipped(uiInputFile)) {
- cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>(
- std::move(uiInputFile), FileName, std::move(includeString));
- success = Gen()->ParallelJobPushUic(std::move(jobHandle));
- } else {
- // A skipped file is successful
- success = true;
+ SourceFileT const& sourceFile = *sourceFileHandle;
+ auto const& Include = sourceFile.ParseData->Uic.Include;
+ if (!sourceFile.Uic || Include.empty()) {
+ return true;
+ }
+
+ std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
+ for (IncludeKeyT const& incKey : Include) {
+ // Find .ui file name
+ SourceFileHandleT uiFileHandle =
+ UicFindIncludedUi(sourceFile.FileName, sourceDir, incKey);
+ if (!uiFileHandle || UicConst().skipped(uiFileHandle->FileName)) {
+ continue;
+ }
+ // Register mapping
+ if (!UicRegisterMapping(incKey.Key, std::move(uiFileHandle),
+ std::move(sourceFileHandle))) {
+ return false;
}
}
- return success;
+
+ return true;
}
-std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile(
- MetaT const& meta, std::string const& includeString)
+bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping(
+ std::string const& includeString, SourceFileHandleT uiFileHandle,
+ SourceFileHandleT includerFileHandle)
{
- std::string res;
- std::string searchFile =
- FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
- searchFile += ".ui";
+ auto& Includes = Gen()->UicEval().Includes;
+ auto it = Includes.find(includeString);
+ if (it != Includes.end()) {
+ MappingHandleT const& handle = it->second;
+ if (handle->SourceFile != uiFileHandle) {
+ // The output file already gets generated - from a different .ui file!
+ std::string msg = "The source files\n ";
+ msg += Quoted(includerFileHandle->FileName);
+ msg += '\n';
+ for (auto const& item : handle->IncluderFiles) {
+ msg += " ";
+ msg += Quoted(item->FileName);
+ msg += '\n';
+ }
+ msg += "contain the same include string ";
+ msg += Quoted(includeString);
+ msg += ", but\nthe uic file would be generated from different "
+ "user interface files\n ";
+ msg += Quoted(uiFileHandle->FileName);
+ msg += " and\n ";
+ msg += Quoted(handle->SourceFile->FileName);
+ msg += ".\nConsider to\n"
+ " - add a directory prefix to a \"ui_<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";
+ LogError(GenT::UIC, msg);
+ return false;
+ }
+ // Add includer file to existing mapping
+ handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
+ } else {
+ // New mapping handle
+ MappingHandleT handle = std::make_shared<MappingT>();
+ handle->IncludeString = includeString;
+ handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
+ handle->SourceFile = std::move(uiFileHandle);
+ handle->OutputFile += Gen()->AbsoluteIncludePath(includeString);
+ // Register mapping
+ Includes.emplace(includeString, std::move(handle));
+ }
+ return true;
+}
+
+cmQtAutoMocUic::SourceFileHandleT
+cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi(
+ std::string const& sourceFile, std::string const& sourceDir,
+ IncludeKeyT const& incKey) const
+{
+ std::string searchFileName = incKey.Base;
+ searchFileName += ".ui";
// Collect search paths list
- std::deque<std::string> testFiles;
+ std::vector<std::string> testFiles;
{
- std::string const searchPath = FileSys().SubDirPrefix(includeString);
+ auto& searchPaths = UicConst().SearchPaths;
+ testFiles.reserve((searchPaths.size() + 1) * 2);
- std::string searchFileFull;
- if (!searchPath.empty()) {
- searchFileFull = searchPath;
- searchFileFull += searchFile;
- }
// Vicinity of the source
- {
- std::string const sourcePath = meta.FileDir;
- testFiles.push_back(sourcePath + searchFile);
- if (!searchPath.empty()) {
- testFiles.push_back(sourcePath + searchFileFull);
- }
+ testFiles.emplace_back(sourceDir + searchFileName);
+ if (!incKey.Dir.empty()) {
+ std::string path = sourceDir;
+ path += incKey.Dir;
+ path += searchFileName;
+ testFiles.emplace_back(path);
}
// AUTOUIC search paths
- if (!Gen()->Uic().SearchPaths.empty()) {
- for (std::string const& sPath : Gen()->Uic().SearchPaths) {
- testFiles.push_back((sPath + "/").append(searchFile));
+ if (!searchPaths.empty()) {
+ for (std::string const& sPath : searchPaths) {
+ std::string path = sPath;
+ path += '/';
+ path += searchFileName;
+ testFiles.emplace_back(std::move(path));
}
- if (!searchPath.empty()) {
- for (std::string const& sPath : Gen()->Uic().SearchPaths) {
- testFiles.push_back((sPath + "/").append(searchFileFull));
+ if (!incKey.Dir.empty()) {
+ for (std::string const& sPath : searchPaths) {
+ std::string path = sPath;
+ path += '/';
+ path += incKey.Dir;
+ path += searchFileName;
+ testFiles.emplace_back(std::move(path));
}
}
}
@@ -675,243 +1100,189 @@ std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile(
// Search for the .ui file!
for (std::string const& testFile : testFiles) {
- if (FileSys().FileExists(testFile)) {
- res = FileSys().GetRealPath(testFile);
- break;
+ cmFileTime fileTime;
+ if (fileTime.Load(testFile)) {
+ // .ui file found in files system!
+ std::string realPath = cmSystemTools::GetRealPath(testFile);
+ // Get or create .ui file handle
+ SourceFileHandleT& handle = Gen()->UicEval().UiFiles[realPath];
+ if (!handle) {
+ // The file wasn't registered, yet
+ handle = std::make_shared<SourceFileT>(realPath);
+ handle->FileTime = fileTime;
+ }
+ return handle;
}
}
// Log error
- if (res.empty()) {
- std::string emsg = "Could not find ";
- emsg += Quoted(searchFile);
- emsg += " in\n";
+ {
+ std::string msg = "The file includes the uic file ";
+ msg += Quoted(incKey.Key);
+ msg += ",\nbut the user interface file ";
+ msg += Quoted(searchFileName);
+ msg += "\ncould not be found in the following locations\n";
for (std::string const& testFile : testFiles) {
- emsg += " ";
- emsg += Quoted(testFile);
- emsg += "\n";
+ msg += " ";
+ msg += Quoted(testFile);
+ msg += '\n';
}
- LogFileError(GenT::UIC, FileName, emsg);
+ LogFileError(GenT::UIC, sourceFile, msg);
}
- return res;
+ return SourceFileHandleT();
}
-void cmQtAutoMocUic::JobPostParseT::Process()
+void cmQtAutoMocUic::JobGenerateT::Process()
{
- if (Gen()->Moc().Enabled) {
- // Add mocs compilations fence job
+ // Add moc compile jobs
+ if (MocConst().Enabled) {
+ for (auto const& pair : MocEval().HeaderMappings) {
+ // Register if this mapping is a candidate for mocs_compilation.cpp
+ bool const compFile = pair.second->IncludeString.empty();
+ if (compFile) {
+ MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath);
+ }
+ if (!MocGenerate(pair.second, compFile)) {
+ return;
+ }
+ }
+ for (auto const& pair : MocEval().SourceMappings) {
+ if (!MocGenerate(pair.second, false)) {
+ return;
+ }
+ }
+
+ // Add mocs compilations job on demand
Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
}
+
+ // Add uic compile jobs
+ if (UicConst().Enabled) {
+ for (auto const& pair : Gen()->UicEval().Includes) {
+ if (!UicGenerate(pair.second)) {
+ return;
+ }
+ }
+ }
+
// Add finish job
Gen()->WorkerPool().EmplaceJob<JobFinishT>();
}
-void cmQtAutoMocUic::JobMocsCompilationT::Process()
+bool cmQtAutoMocUic::JobGenerateT::MocGenerate(MappingHandleT const& mapping,
+ bool compFile) const
{
- // Compose mocs compilation file content
- std::string content =
- "// This file is autogenerated. Changes will be overwritten.\n";
- if (Gen()->MocAutoFiles().empty()) {
- // Placeholder content
- content += "// No files found that require moc or the moc files are "
- "included\n";
- content += "enum some_compilers { need_more_than_nothing };\n";
- } else {
- // Valid content
- char const sbeg = Gen()->Base().MultiConfig ? '<' : '"';
- char const send = Gen()->Base().MultiConfig ? '>' : '"';
- for (std::string const& mocfile : Gen()->MocAutoFiles()) {
- content += "#include ";
- content += sbeg;
- content += mocfile;
- content += send;
- content += '\n';
- }
+ std::unique_ptr<std::string> reason;
+ if (Log().Verbose()) {
+ reason = cm::make_unique<std::string>();
}
-
- std::string const& compAbs = Gen()->Moc().CompFileAbs;
- if (FileSys().FileDiffers(compAbs, content)) {
- // Actually write mocs compilation file
- if (Log().Verbose()) {
- Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
- }
- if (!FileSys().FileWrite(compAbs, content)) {
- LogFileError(GenT::MOC, compAbs,
- "mocs compilation file writing failed.");
+ if (MocUpdate(*mapping, reason.get())) {
+ // Create the parent directory
+ if (!MakeParentDirectory(mapping->OutputFile)) {
+ LogFileError(GenT::MOC, mapping->OutputFile,
+ "Could not create parent directory.");
+ return false;
}
- } else if (Gen()->MocAutoFileUpdated()) {
- // Only touch mocs compilation file
- if (Log().Verbose()) {
- Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
+ // Add moc job
+ Gen()->WorkerPool().EmplaceJob<JobMocT>(mapping, std::move(reason));
+ // Check if a moc job for a mocs_compilation.cpp entry was generated
+ if (compFile) {
+ MocEval().CompUpdated = true;
}
- FileSys().Touch(compAbs);
}
+ return true;
}
-void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content)
-{
- Gen()->Moc().FindDependencies(content, Depends);
- DependsValid = true;
-}
-
-void cmQtAutoMocUic::JobMocT::Process()
+bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping,
+ std::string* reason) const
{
- // Compute build file name
- if (!IncludeString.empty()) {
- BuildFile = Gen()->Base().AutogenIncludeDir;
- BuildFile += '/';
- BuildFile += IncludeString;
- } else {
- // Relative build path
- std::string relPath = FileSys().GetFilePathChecksum(SourceFile);
- relPath += "/moc_";
- relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile);
-
- // Register relative file path with duplication check
- relPath = Gen()->ParallelMocAutoRegister(relPath);
-
- // Absolute build path
- if (Gen()->Base().MultiConfig) {
- BuildFile = Gen()->Base().AutogenIncludeDir;
- BuildFile += '/';
- BuildFile += relPath;
- } else {
- BuildFile = Gen()->Base().AbsoluteBuildPath(relPath);
+ std::string const& sourceFile = mapping.SourceFile->FileName;
+ std::string const& outputFile = mapping.OutputFile;
+
+ // Test if the output file exists
+ cmFileTime outputFileTime;
+ if (!outputFileTime.Load(outputFile)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it doesn't exist, from ";
+ *reason += Quoted(sourceFile);
}
+ return true;
}
- if (UpdateRequired()) {
- GenerateMoc();
- }
-}
-
-bool cmQtAutoMocUic::JobMocT::UpdateRequired()
-{
- bool const verbose = Log().Verbose();
-
- // Test if the build file exists
- if (!FileSys().FileExists(BuildFile)) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " from its source file ";
- reason += Quoted(SourceFile);
- reason += " because it doesn't exist";
- Log().Info(GenT::MOC, reason);
+ // Test if any setting changed
+ if (MocConst().SettingsChanged) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because the uic settings changed, from ";
+ *reason += Quoted(sourceFile);
}
return true;
}
- // Test if any setting changed
- if (Gen()->Moc().SettingsChanged) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " from ";
- reason += Quoted(SourceFile);
- reason += " because the MOC settings changed";
- Log().Info(GenT::MOC, reason);
+ // Test if the source file is newer
+ if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it's older than its source file, from ";
+ *reason += Quoted(sourceFile);
}
return true;
}
// Test if the moc_predefs file is newer
- if (!Gen()->Moc().PredefsFileAbs.empty()) {
- bool isOlder = false;
- {
- std::string error;
- isOlder = FileSys().FileIsOlderThan(BuildFile,
- Gen()->Moc().PredefsFileAbs, &error);
- if (!isOlder && !error.empty()) {
- LogError(GenT::MOC, error);
- return false;
- }
- }
- if (isOlder) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " because it's older than: ";
- reason += Quoted(Gen()->Moc().PredefsFileAbs);
- Log().Info(GenT::MOC, reason);
+ if (!MocConst().PredefsFileAbs.empty()) {
+ if (outputFileTime.Older(MocEval().PredefsTime)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it's older than ";
+ *reason += Quoted(MocConst().PredefsFileAbs);
+ *reason += ", from ";
+ *reason += Quoted(sourceFile);
}
return true;
}
}
- // Test if the source file is newer
- {
- bool isOlder = false;
- {
- std::string error;
- isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
- if (!isOlder && !error.empty()) {
- LogError(GenT::MOC, error);
- return false;
- }
- }
- if (isOlder) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " because it's older than its source file ";
- reason += Quoted(SourceFile);
- Log().Info(GenT::MOC, reason);
- }
- return true;
+ // Test if the moc executable is newer
+ if (outputFileTime.Older(MocConst().ExecutableTime)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it's older than the moc executable, from ";
+ *reason += Quoted(sourceFile);
}
+ return true;
}
// Test if a dependency file is newer
{
- // Read dependencies on demand
- if (!DependsValid) {
- std::string content;
- {
- std::string error;
- if (!FileSys().FileRead(content, SourceFile, &error)) {
- std::string emsg = "Could not read file\n ";
- emsg += Quoted(SourceFile);
- emsg += "\nrequired by moc include ";
- emsg += Quoted(IncludeString);
- emsg += " in\n ";
- emsg += Quoted(IncluderFile);
- emsg += ".\n";
- emsg += error;
- LogError(GenT::MOC, emsg);
- return false;
- }
- }
- FindDependencies(content);
- }
// Check dependency timestamps
- std::string error;
- std::string sourceDir = FileSys().SubDirPrefix(SourceFile);
- for (std::string const& depFileRel : Depends) {
- std::string depFileAbs =
- Gen()->Moc().FindIncludedFile(sourceDir, depFileRel);
- if (!depFileAbs.empty()) {
- if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " from ";
- reason += Quoted(SourceFile);
- reason += " because it is older than it's dependency file ";
- reason += Quoted(depFileAbs);
- Log().Info(GenT::MOC, reason);
- }
- return true;
- }
- if (!error.empty()) {
- LogError(GenT::MOC, error);
- return false;
+ std::string const sourceDir = SubDirPrefix(sourceFile);
+ for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) {
+ // Find dependency file
+ auto const depMatch = MocFindDependency(sourceDir, dep);
+ if (depMatch.first.empty()) {
+ Log().WarningFile(GenT::MOC, sourceFile,
+ "Could not find dependency file " + Quoted(dep));
+ continue;
+ }
+ // Test if dependency file is older
+ if (outputFileTime.Older(depMatch.second)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it's older than its dependency file ";
+ *reason += Quoted(depMatch.first);
+ *reason += ", from ";
+ *reason += Quoted(sourceFile);
}
- } else {
- std::string message = "Could not find dependency file ";
- message += Quoted(depFileRel);
- Log().WarningFile(GenT::MOC, SourceFile, message);
+ return true;
}
}
}
@@ -919,198 +1290,254 @@ bool cmQtAutoMocUic::JobMocT::UpdateRequired()
return false;
}
-void cmQtAutoMocUic::JobMocT::GenerateMoc()
+std::pair<std::string, cmFileTime>
+cmQtAutoMocUic::JobGenerateT::MocFindDependency(
+ std::string const& sourceDir, std::string const& includeString) const
{
- // Make sure the parent directory exists
- if (!FileSys().MakeParentDirectory(BuildFile)) {
- LogFileError(GenT::MOC, BuildFile, "Could not create parent directory.");
- return;
- }
+ typedef std::pair<std::string, cmFileTime> ResPair;
+ // Search in vicinity of the source
{
- // Compose moc command
- std::vector<std::string> cmd;
- cmd.push_back(Gen()->Moc().Executable);
- // Add options
- cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(),
- Gen()->Moc().AllOptions.end());
- // Add predefs include
- if (!Gen()->Moc().PredefsFileAbs.empty()) {
- cmd.emplace_back("--include");
- cmd.push_back(Gen()->Moc().PredefsFileAbs);
- }
- cmd.emplace_back("-o");
- cmd.push_back(BuildFile);
- cmd.push_back(SourceFile);
-
- // Execute moc command
- cmWorkerPool::ProcessResultT result;
- if (RunProcess(GenT::MOC, result, cmd)) {
- // Moc command success
- // Print moc output
- if (!result.StdOut.empty()) {
- Log().Info(GenT::MOC, result.StdOut);
- }
- // Notify the generator that a not included file changed (on demand)
- if (IncludeString.empty()) {
- Gen()->ParallelMocAutoUpdated();
- }
- } else {
- // Moc command failed
- {
- std::string emsg = "The moc process failed to compile\n ";
- emsg += Quoted(SourceFile);
- emsg += "\ninto\n ";
- emsg += Quoted(BuildFile);
- emsg += ".\n";
- emsg += result.ErrorMessage;
- LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
- }
- FileSys().FileRemove(BuildFile);
+ ResPair res{ sourceDir + includeString, {} };
+ if (res.second.Load(res.first)) {
+ return res;
+ }
+ }
+ // Search in include directories
+ for (std::string const& includePath : MocConst().IncludePaths) {
+ ResPair res{ includePath, {} };
+ res.first += '/';
+ res.first += includeString;
+ if (res.second.Load(res.first)) {
+ return res;
}
}
+ // Return empty
+ return ResPair();
}
-void cmQtAutoMocUic::JobUicT::Process()
+bool cmQtAutoMocUic::JobGenerateT::UicGenerate(
+ MappingHandleT const& mapping) const
{
- // Compute build file name
- BuildFile = Gen()->Base().AutogenIncludeDir;
- BuildFile += '/';
- BuildFile += IncludeString;
-
- if (UpdateRequired()) {
- GenerateUic();
+ std::unique_ptr<std::string> reason;
+ if (Log().Verbose()) {
+ reason = cm::make_unique<std::string>();
}
+ if (UicUpdate(*mapping, reason.get())) {
+ // Create the parent directory
+ if (!MakeParentDirectory(mapping->OutputFile)) {
+ LogFileError(GenT::UIC, mapping->OutputFile,
+ "Could not create parent directory.");
+ return false;
+ }
+ // Add uic job
+ Gen()->WorkerPool().EmplaceJob<JobUicT>(mapping, std::move(reason));
+ }
+ return true;
}
-bool cmQtAutoMocUic::JobUicT::UpdateRequired()
+bool cmQtAutoMocUic::JobGenerateT::UicUpdate(MappingT const& mapping,
+ std::string* reason) const
{
- bool const verbose = Log().Verbose();
+ std::string const& sourceFile = mapping.SourceFile->FileName;
+ std::string const& outputFile = mapping.OutputFile;
// Test if the build file exists
- if (!FileSys().FileExists(BuildFile)) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " from its source file ";
- reason += Quoted(SourceFile);
- reason += " because it doesn't exist";
- Log().Info(GenT::UIC, reason);
+ cmFileTime outputFileTime;
+ if (!outputFileTime.Load(outputFile)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it doesn't exist, from ";
+ *reason += Quoted(sourceFile);
}
return true;
}
// Test if the uic settings changed
- if (Gen()->Uic().SettingsChanged) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " from ";
- reason += Quoted(SourceFile);
- reason += " because the UIC settings changed";
- Log().Info(GenT::UIC, reason);
+ if (UicConst().SettingsChanged) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because the uic settings changed, from ";
+ *reason += Quoted(sourceFile);
}
return true;
}
// Test if the source file is newer
- {
- bool isOlder = false;
- {
- std::string error;
- isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
- if (!isOlder && !error.empty()) {
- LogError(GenT::UIC, error);
- return false;
- }
+ if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += " because it's older than the source file ";
+ *reason += Quoted(sourceFile);
}
- if (isOlder) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " because it's older than its source file ";
- reason += Quoted(SourceFile);
- Log().Info(GenT::UIC, reason);
- }
- return true;
+ return true;
+ }
+
+ // Test if the uic executable is newer
+ if (outputFileTime.Older(UicConst().ExecutableTime)) {
+ if (reason != nullptr) {
+ *reason = "Generating ";
+ *reason += Quoted(outputFile);
+ *reason += ", because it's older than the uic executable, from ";
+ *reason += Quoted(sourceFile);
}
+ return true;
}
return false;
}
-void cmQtAutoMocUic::JobUicT::GenerateUic()
+void cmQtAutoMocUic::JobMocT::Process()
{
- // Make sure the parent directory exists
- if (!FileSys().MakeParentDirectory(BuildFile)) {
- LogFileError(GenT::UIC, BuildFile, "Could not create parent directory.");
- return;
+ std::string const& sourceFile = Mapping->SourceFile->FileName;
+ std::string const& outputFile = Mapping->OutputFile;
+
+ // Compose moc command
+ std::vector<std::string> cmd;
+ cmd.push_back(MocConst().Executable);
+ // Add options
+ cmd.insert(cmd.end(), MocConst().AllOptions.begin(),
+ MocConst().AllOptions.end());
+ // Add predefs include
+ if (!MocConst().PredefsFileAbs.empty()) {
+ cmd.emplace_back("--include");
+ cmd.push_back(MocConst().PredefsFileAbs);
+ }
+ cmd.emplace_back("-o");
+ cmd.push_back(outputFile);
+ cmd.push_back(sourceFile);
+
+ // Execute moc command
+ cmWorkerPool::ProcessResultT result;
+ if (RunProcess(GenT::MOC, result, cmd, Reason.get())) {
+ // Moc command success. Print moc output.
+ if (!result.StdOut.empty()) {
+ Log().Info(GenT::MOC, result.StdOut);
+ }
+ } else {
+ // Moc command failed
+ std::string msg = "The moc process failed to compile\n ";
+ msg += Quoted(sourceFile);
+ msg += "\ninto\n ";
+ msg += Quoted(outputFile);
+ if (Mapping->IncluderFiles.empty()) {
+ msg += ".\n";
+ } else {
+ msg += "\nincluded by\n";
+ for (auto const& item : Mapping->IncluderFiles) {
+ msg += " ";
+ msg += Quoted(item->FileName);
+ msg += '\n';
+ }
+ }
+ msg += result.ErrorMessage;
+ LogCommandError(GenT::MOC, msg, cmd, result.StdOut);
}
+}
+
+void cmQtAutoMocUic::JobUicT::Process()
+{
+ std::string const& sourceFile = Mapping->SourceFile->FileName;
+ std::string const& outputFile = Mapping->OutputFile;
+
+ // Compose uic command
+ std::vector<std::string> cmd;
+ cmd.push_back(UicConst().Executable);
{
- // Compose uic command
- std::vector<std::string> cmd;
- cmd.push_back(Gen()->Uic().Executable);
- {
- std::vector<std::string> allOpts = Gen()->Uic().TargetOptions;
- auto optionIt = Gen()->Uic().Options.find(SourceFile);
- if (optionIt != Gen()->Uic().Options.end()) {
- UicMergeOptions(allOpts, optionIt->second,
- (Gen()->Base().QtVersionMajor == 5));
- }
- cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+ std::vector<std::string> allOpts = UicConst().TargetOptions;
+ auto optionIt = UicConst().Options.find(sourceFile);
+ if (optionIt != UicConst().Options.end()) {
+ UicMergeOptions(allOpts, optionIt->second,
+ (BaseConst().QtVersionMajor == 5));
}
- cmd.emplace_back("-o");
- cmd.emplace_back(BuildFile);
- cmd.emplace_back(SourceFile);
+ cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+ }
+ cmd.emplace_back("-o");
+ cmd.emplace_back(outputFile);
+ cmd.emplace_back(sourceFile);
- cmWorkerPool::ProcessResultT result;
- if (RunProcess(GenT::UIC, result, cmd)) {
- // Uic command success
- // Print uic output
- if (!result.StdOut.empty()) {
- Log().Info(GenT::UIC, result.StdOut);
- }
- } else {
- // Uic command failed
- {
- std::string emsg = "The uic process failed to compile\n ";
- emsg += Quoted(SourceFile);
- emsg += "\ninto\n ";
- emsg += Quoted(BuildFile);
- emsg += "\nincluded by\n ";
- emsg += Quoted(IncluderFile);
- emsg += ".\n";
- emsg += result.ErrorMessage;
- LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
- }
- FileSys().FileRemove(BuildFile);
+ cmWorkerPool::ProcessResultT result;
+ if (RunProcess(GenT::UIC, result, cmd, Reason.get())) {
+ // Uic command success
+ // Print uic output
+ if (!result.StdOut.empty()) {
+ Log().Info(GenT::UIC, result.StdOut);
}
+ } else {
+ // Uic command failed
+ std::string msg = "The uic process failed to compile\n ";
+ msg += Quoted(sourceFile);
+ msg += "\ninto\n ";
+ msg += Quoted(outputFile);
+ msg += "\nincluded by\n";
+ for (auto const& item : Mapping->IncluderFiles) {
+ msg += " ";
+ msg += Quoted(item->FileName);
+ msg += '\n';
+ }
+ msg += result.ErrorMessage;
+ LogCommandError(GenT::UIC, msg, cmd, result.StdOut);
}
}
-void cmQtAutoMocUic::JobFinishT::Process()
+void cmQtAutoMocUic::JobMocsCompilationT::Process()
{
- Gen()->AbortSuccess();
+ // Compose mocs compilation file content
+ std::string content =
+ "// This file is autogenerated. Changes will be overwritten.\n";
+
+ if (MocEval().CompFiles.empty()) {
+ // Placeholder content
+ content += "// No files found that require moc or the moc files are "
+ "included\n";
+ content += "enum some_compilers { need_more_than_nothing };\n";
+ } else {
+ // Valid content
+ char const clampB = BaseConst().MultiConfig ? '<' : '"';
+ char const clampE = BaseConst().MultiConfig ? '>' : '"';
+ for (std::string const& mocfile : MocEval().CompFiles) {
+ content += "#include ";
+ content += clampB;
+ content += mocfile;
+ content += clampE;
+ content += '\n';
+ }
+ }
+
+ std::string const& compAbs = MocConst().CompFileAbs;
+ if (cmQtAutoGenerator::FileDiffers(compAbs, content)) {
+ // Actually write mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
+ }
+ if (!FileWrite(compAbs, content)) {
+ LogFileError(GenT::MOC, compAbs,
+ "mocs compilation file writing failed.");
+ }
+ } else if (MocEval().CompUpdated) {
+ // Only touch mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
+ }
+ if (!cmSystemTools::Touch(compAbs, false)) {
+ LogFileError(GenT::MOC, compAbs,
+ "mocs compilation file touching failed.");
+ }
+ }
}
-cmQtAutoMocUic::cmQtAutoMocUic()
- : Base_(&FileSys())
- , Moc_(&FileSys())
+void cmQtAutoMocUic::JobFinishT::Process()
{
- // Precompile regular expressions
- Moc_.RegExpInclude.compile(
- "(^|\n)[ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
- Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+ Gen()->AbortSuccess();
}
+cmQtAutoMocUic::cmQtAutoMocUic() = default;
cmQtAutoMocUic::~cmQtAutoMocUic() = default;
bool cmQtAutoMocUic::Init(cmMakefile* makefile)
{
- // -- Meta
- Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
-
// Utility lambdas
auto InfoGet = [makefile](const char* key) {
return makefile->GetSafeDefinition(key);
@@ -1169,171 +1596,185 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile)
cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
return list;
};
+ auto LogInfoError = [this](std::string const& msg) -> bool {
+ std::ostringstream err;
+ err << "In " << Quoted(this->InfoFile()) << ":\n" << msg;
+ this->Log().Error(GenT::GEN, err.str());
+ return false;
+ };
+ auto MatchSizes = [&LogInfoError](const char* keyA, const char* keyB,
+ std::size_t sizeA,
+ std::size_t sizeB) -> bool {
+ if (sizeA == sizeB) {
+ return true;
+ }
+ std::ostringstream err;
+ err << "Lists sizes mismatch " << keyA << '(' << sizeA << ") " << keyB
+ << '(' << sizeB << ')';
+ return LogInfoError(err.str());
+ };
// -- Read info file
if (!makefile->ReadListFile(InfoFile())) {
- Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
- return false;
+ return LogInfoError("File processing failed");
}
// -- Meta
- Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
- Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
+ Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY"));
+ BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
{
- unsigned long num = Base_.NumThreads;
+ unsigned long num = 1;
if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
num = std::max<unsigned long>(num, 1);
num = std::min<unsigned long>(num, ParallelMax);
- Base_.NumThreads = static_cast<unsigned int>(num);
}
- WorkerPool_.SetThreadCount(Base_.NumThreads);
+ WorkerPool_.SetThreadCount(static_cast<unsigned int>(num));
}
+ BaseConst_.HeaderExtensions =
+ makefile->GetCMakeInstance()->GetHeaderExtensions();
// - Files and directories
- Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
- Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
- Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
- Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
- Base_.IncludeProjectDirsBefore =
+ BaseConst_.IncludeProjectDirsBefore =
InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
- Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
- if (Base_.AutogenBuildDir.empty()) {
- Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
- return false;
- }
- // include directory
- Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
- if (Base_.AutogenIncludeDir.empty()) {
- Log().ErrorFile(GenT::GEN, InfoFile(),
- "Autogen include directory missing");
- return false;
- }
-
- // - Files
+ BaseConst_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
+ BaseConst_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
+ BaseConst_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
+ BaseConst_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
+ BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
+ if (BaseConst_.AutogenBuildDir.empty()) {
+ return LogInfoError("Autogen build directory missing.");
+ }
+ BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
+ if (BaseConst_.AutogenIncludeDir.empty()) {
+ return LogInfoError("Autogen include directory missing.");
+ }
+ BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE");
+ if (BaseConst_.CMakeExecutable.empty()) {
+ return LogInfoError("CMake executable file name missing.");
+ }
+ if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) {
+ std::string error = "The CMake executable ";
+ error += Quoted(BaseConst_.CMakeExecutable);
+ error += " does not exist.";
+ return LogInfoError(error);
+ }
+ BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE");
+ if (BaseConst_.ParseCacheFile.empty()) {
+ return LogInfoError("Parse cache file name missing.");
+ }
+
+ // - Settings file
SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
if (SettingsFile_.empty()) {
- Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
- return false;
+ return LogInfoError("Settings file name missing.");
}
// - Qt environment
{
- unsigned long qtv = Base_.QtVersionMajor;
+ unsigned long qtv = BaseConst_.QtVersionMajor;
if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
&qtv)) {
- Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
+ BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv);
}
}
// - Moc
- Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
- Moc_.Enabled = !Moc().Executable.empty();
- if (Moc().Enabled) {
+ MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+ if (!MocConst().Executable.empty()) {
+ MocConst_.Enabled = true;
+ // Load the executable file time
+ if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
+ std::string error = "The moc executable ";
+ error += Quoted(MocConst_.Executable);
+ error += " does not exist.";
+ return LogInfoError(error);
+ }
for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
- Moc_.SkipList.insert(std::move(sfl));
+ MocConst_.SkipList.insert(std::move(sfl));
}
- Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
- Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
- Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
- Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
+ MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+ MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
+ MocConst_.Options = InfoGetList("AM_MOC_OPTIONS");
+ MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
- Moc_.MacroFilters.emplace_back(
+ MocConst_.MacroFilters.emplace_back(
item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
}
{
- auto pushFilter = [this](std::string const& key, std::string const& exp,
- std::string& error) {
- if (!key.empty()) {
- if (!exp.empty()) {
- Moc_.DependFilters.emplace_back();
- KeyExpT& filter(Moc_.DependFilters.back());
- if (filter.Exp.compile(exp)) {
- filter.Key = key;
- } else {
- error = "Regular expression compiling failed";
- }
- } else {
- error = "Regular expression is empty";
- }
- } else {
- error = "Key is empty";
+ auto addFilter = [this, &LogInfoError](std::string const& key,
+ std::string const& exp) -> bool {
+ auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool {
+ std::ostringstream ferr;
+ ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n';
+ ferr << " Key: " << Quoted(key) << '\n';
+ ferr << " Exp: " << Quoted(exp) << '\n';
+ return LogInfoError(ferr.str());
+ };
+ if (key.empty()) {
+ return filterErr("Key is empty");
+ }
+ if (exp.empty()) {
+ return filterErr("Regular expression 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";
+ this->MocConst_.DependFilters.emplace_back(key, exp);
+ if (!this->MocConst_.DependFilters.back().Exp.is_valid()) {
+ return filterErr("Regular expression compiling failed");
}
+ return true;
};
- std::string error;
// Insert default filter for Q_PLUGIN_METADATA
- if (Base().QtVersionMajor != 4) {
- pushFilter("Q_PLUGIN_METADATA",
- "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
- "[^\\)]*FILE[ \t]*\"([^\"]+)\"",
- error);
+ if (BaseConst().QtVersionMajor != 4) {
+ if (!addFilter("Q_PLUGIN_METADATA",
+ "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+ "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) {
+ return false;
+ }
}
// Insert user defined dependency filters
- {
- std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
- if ((flts.size() % 2) == 0) {
- for (std::vector<std::string>::iterator itC = flts.begin(),
- itE = flts.end();
- itC != itE; itC += 2) {
- pushFilter(*itC, *(itC + 1), error);
- if (!error.empty()) {
- break;
- }
- }
- } else {
- Log().ErrorFile(
- GenT::MOC, InfoFile(),
- "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
+ if ((flts.size() % 2) != 0) {
+ return LogInfoError(
+ "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ }
+ for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) {
+ if (!addFilter(*itC, *(itC + 1))) {
return false;
}
}
- if (!error.empty()) {
- Log().ErrorFile(GenT::MOC, InfoFile(), error);
- return false;
- }
- }
- Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
- // Install moc predefs job
- if (!Moc().PredefsCmd.empty()) {
- WorkerPool().EmplaceJob<JobMocPredefsT>();
}
+ MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
}
// - Uic
- Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
- Uic_.Enabled = !Uic().Executable.empty();
- if (Uic().Enabled) {
+ UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+ if (!UicConst().Executable.empty()) {
+ UicConst_.Enabled = true;
+ // Load the executable file time
+ if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) {
+ std::string error = "The uic executable ";
+ error += Quoted(UicConst_.Executable);
+ error += " does not exist.";
+ return LogInfoError(error);
+ }
for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
- Uic_.SkipList.insert(std::move(sfl));
+ UicConst_.SkipList.insert(std::move(sfl));
}
- Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
- Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
+ UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
+ UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
{
- auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
- auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
- // Compare list sizes
- if (sources.size() != options.size()) {
- std::ostringstream ost;
- ost << "files/options lists sizes mismatch (" << sources.size() << "/"
- << options.size() << ")";
- Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
+ const char* keyFiles = "AM_UIC_OPTIONS_FILES";
+ const char* keyOpts = "AM_UIC_OPTIONS_OPTIONS";
+ auto sources = InfoGetList(keyFiles);
+ auto options = InfoGetLists(keyOpts);
+ if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) {
return false;
}
auto fitEnd = sources.cend();
auto fit = sources.begin();
auto oit = options.begin();
while (fit != fitEnd) {
- Uic_.Options[*fit] = std::move(*oit);
+ UicConst_.Options[*fit] = std::move(*oit);
++fit;
++oit;
}
@@ -1341,92 +1782,121 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile)
}
// - Headers and sources
- // Add sources
{
- auto addSource = [this](std::string&& src, bool moc, bool uic) {
- WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false);
- };
- for (std::string& src : InfoGetList("AM_SOURCES")) {
- addSource(std::move(src), true, true);
- }
- if (Moc().Enabled) {
- for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
- addSource(std::move(src), true, false);
+ auto makeSource =
+ [&LogInfoError](std::string const& fileName,
+ std::string const& fileFlags) -> SourceFileHandleT {
+ if (fileFlags.size() != 2) {
+ LogInfoError("Invalid file flags string size");
+ return SourceFileHandleT();
}
- }
- if (Uic().Enabled) {
- for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
- addSource(std::move(src), false, true);
+ cmFileTime fileTime;
+ if (!fileTime.Load(fileName)) {
+ LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) +
+ " does not exist.");
+ return SourceFileHandleT();
}
- }
- }
- // Add Fence job
- WorkerPool().EmplaceJob<JobFenceT>();
- // Add headers
- {
- auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
- WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true);
+ SourceFileHandleT sfh = std::make_shared<SourceFileT>(fileName);
+ sfh->FileTime = fileTime;
+ sfh->Moc = (fileFlags[0] == 'M');
+ sfh->Uic = (fileFlags[1] == 'U');
+ return sfh;
};
- for (std::string& hdr : InfoGetList("AM_HEADERS")) {
- addHeader(std::move(hdr), true, true);
- }
- if (Moc().Enabled) {
- for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
- addHeader(std::move(hdr), true, false);
+
+ // Headers
+ {
+ // Get file lists
+ const char *keyFiles = "AM_HEADERS", *keyFlags = "AM_HEADERS_FLAGS";
+ std::vector<std::string> files = InfoGetList(keyFiles);
+ std::vector<std::string> flags = InfoGetList(keyFlags);
+ std::vector<std::string> builds;
+ if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
+ return false;
+ }
+ if (MocConst().Enabled) {
+ const char* keyPaths = "AM_HEADERS_BUILD_PATHS";
+ builds = InfoGetList(keyPaths);
+ if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) {
+ return false;
+ }
+ }
+ // Process file lists
+ for (std::size_t ii = 0; ii != files.size(); ++ii) {
+ std::string& fileName(files[ii]);
+ SourceFileHandleT sfh = makeSource(fileName, flags[ii]);
+ if (!sfh) {
+ return false;
+ }
+ if (MocConst().Enabled) {
+ sfh->BuildPath = std::move(builds[ii]);
+ if (sfh->BuildPath.empty()) {
+ Log().ErrorFile(GenT::GEN, this->InfoFile(),
+ "Header file build path is empty");
+ return false;
+ }
+ }
+ BaseEval().Headers.emplace(std::move(fileName), std::move(sfh));
}
}
- if (Uic().Enabled) {
- for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
- addHeader(std::move(hdr), false, true);
+
+ // Sources
+ {
+ const char *keyFiles = "AM_SOURCES", *keyFlags = "AM_SOURCES_FLAGS";
+ std::vector<std::string> files = InfoGetList(keyFiles);
+ std::vector<std::string> flags = InfoGetList(keyFlags);
+ if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
+ return false;
+ }
+ // Process file lists
+ for (std::size_t ii = 0; ii != files.size(); ++ii) {
+ std::string& fileName(files[ii]);
+ SourceFileHandleT sfh = makeSource(fileName, flags[ii]);
+ if (!sfh) {
+ return false;
+ }
+ BaseEval().Sources.emplace(std::move(fileName), std::move(sfh));
}
}
}
- // Addpost parse fence job
- WorkerPool().EmplaceJob<JobPostParseT>();
// Init derived information
// ------------------------
- // Init file path checksum generator
- FileSys().setupFilePathChecksum(
- Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
- Base().ProjectBinaryDir);
-
// Moc variables
- if (Moc().Enabled) {
+ if (MocConst().Enabled) {
// Mocs compilation file
- Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
+ MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp");
// Moc predefs file
- if (!Moc_.PredefsCmd.empty()) {
- Moc_.PredefsFileRel = "moc_predefs";
- if (Base_.MultiConfig) {
- Moc_.PredefsFileRel += '_';
- Moc_.PredefsFileRel += InfoConfig();
+ if (!MocConst_.PredefsCmd.empty()) {
+ MocConst_.PredefsFileRel = "moc_predefs";
+ if (BaseConst_.MultiConfig) {
+ MocConst_.PredefsFileRel += '_';
+ MocConst_.PredefsFileRel += InfoConfig();
}
- Moc_.PredefsFileRel += ".h";
- Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
+ MocConst_.PredefsFileRel += ".h";
+ MocConst_.PredefsFileAbs = AbsoluteBuildPath(MocConst().PredefsFileRel);
}
// Sort include directories on demand
- if (Base().IncludeProjectDirsBefore) {
+ if (BaseConst().IncludeProjectDirsBefore) {
// Move strings to temporary list
std::list<std::string> includes;
- includes.insert(includes.end(), Moc().IncludePaths.begin(),
- Moc().IncludePaths.end());
- Moc_.IncludePaths.clear();
- Moc_.IncludePaths.reserve(includes.size());
+ includes.insert(includes.end(), MocConst().IncludePaths.begin(),
+ MocConst().IncludePaths.end());
+ MocConst_.IncludePaths.clear();
+ MocConst_.IncludePaths.reserve(includes.size());
// Append project directories only
{
std::array<std::string const*, 2> const movePaths = {
- { &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
+ { &BaseConst().ProjectBinaryDir, &BaseConst().ProjectSourceDir }
};
for (std::string const* ppath : movePaths) {
std::list<std::string>::iterator it = includes.begin();
while (it != includes.end()) {
std::string const& path = *it;
if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
- Moc_.IncludePaths.push_back(path);
+ MocConst_.IncludePaths.push_back(path);
it = includes.erase(it);
} else {
++it;
@@ -1435,124 +1905,176 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile)
}
}
// Append remaining directories
- Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
- includes.end());
+ MocConst_.IncludePaths.insert(MocConst_.IncludePaths.end(),
+ includes.begin(), includes.end());
}
// Compose moc includes list
{
std::set<std::string> frameworkPaths;
- for (std::string const& path : Moc().IncludePaths) {
- Moc_.Includes.push_back("-I" + path);
+ for (std::string const& path : MocConst().IncludePaths) {
+ MocConst_.Includes.push_back("-I" + path);
// Extract framework path
if (cmHasLiteralSuffix(path, ".framework/Headers")) {
// Go up twice to get to the framework root
std::vector<std::string> pathComponents;
- FileSys().SplitPath(path, pathComponents);
- std::string frameworkPath = FileSys().JoinPath(
- pathComponents.begin(), pathComponents.end() - 2);
- frameworkPaths.insert(frameworkPath);
+ cmSystemTools::SplitPath(path, pathComponents);
+ frameworkPaths.emplace(cmSystemTools::JoinPath(
+ pathComponents.begin(), pathComponents.end() - 2));
}
}
// Append framework includes
for (std::string const& path : frameworkPaths) {
- Moc_.Includes.emplace_back("-F");
- Moc_.Includes.push_back(path);
+ MocConst_.Includes.emplace_back("-F");
+ MocConst_.Includes.push_back(path);
}
}
// Setup single list with all options
{
// Add includes
- Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
- Moc().Includes.end());
+ MocConst_.AllOptions.insert(MocConst_.AllOptions.end(),
+ MocConst().Includes.begin(),
+ MocConst().Includes.end());
// Add definitions
- for (std::string const& def : Moc().Definitions) {
- Moc_.AllOptions.push_back("-D" + def);
+ for (std::string const& def : MocConst().Definitions) {
+ MocConst_.AllOptions.push_back("-D" + def);
}
// Add options
- Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
- Moc().Options.end());
+ MocConst_.AllOptions.insert(MocConst_.AllOptions.end(),
+ MocConst().Options.begin(),
+ MocConst().Options.end());
}
}
return true;
}
+template <class JOBTYPE>
+void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap)
+{
+ cmFileTime const parseCacheTime = BaseEval().ParseCacheTime;
+ ParseCacheT& parseCache = BaseEval().ParseCache;
+ for (auto& src : sourceMap) {
+ // Get or create the file parse data reference
+ ParseCacheT::GetOrInsertT cacheEntry = parseCache.GetOrInsert(src.first);
+ src.second->ParseData = std::move(cacheEntry.first);
+ // Create a parse job if the cache file was missing or is older
+ if (cacheEntry.second || src.second->FileTime.Newer(parseCacheTime)) {
+ BaseEval().ParseCacheChanged = true;
+ WorkerPool().EmplaceJob<JOBTYPE>(src.second);
+ }
+ }
+}
+
+void cmQtAutoMocUic::InitJobs()
+{
+ // Add moc_predefs.h job
+ if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) {
+ WorkerPool().EmplaceJob<JobMocPredefsT>();
+ }
+ // Add header parse jobs
+ CreateParseJobs<JobParseHeaderT>(BaseEval().Headers);
+ // Add source parse jobs
+ CreateParseJobs<JobParseSourceT>(BaseEval().Sources);
+ // Add evaluate job
+ WorkerPool().EmplaceJob<JobEvaluateT>();
+}
+
bool cmQtAutoMocUic::Process()
{
SettingsFileRead();
+ ParseCacheRead();
if (!CreateDirectories()) {
return false;
}
+ InitJobs();
if (!WorkerPool_.Process(this)) {
return false;
}
if (JobError_) {
return false;
}
- return SettingsFileWrite();
+ if (!ParseCacheWrite()) {
+ return false;
+ }
+ if (!SettingsFileWrite()) {
+ return false;
+ }
+ return true;
}
void cmQtAutoMocUic::SettingsFileRead()
{
// Compose current settings strings
{
- cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
- std::string const sep(" ~~~ ");
- if (Moc_.Enabled) {
- std::string str;
- str += Moc().Executable;
- str += sep;
- str += cmJoin(Moc().AllOptions, ";");
- str += sep;
- str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
- str += sep;
- str += cmJoin(Moc().PredefsCmd, ";");
- str += sep;
- SettingsStringMoc_ = crypt.HashString(str);
- }
- if (Uic().Enabled) {
- std::string str;
- str += Uic().Executable;
- str += sep;
- str += cmJoin(Uic().TargetOptions, ";");
- for (const auto& item : Uic().Options) {
- str += sep;
- str += item.first;
- str += sep;
- str += cmJoin(item.second, ";");
- }
- str += sep;
- SettingsStringUic_ = crypt.HashString(str);
+ cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256);
+ std::string const sep(";");
+ auto cha = [&cryptoHash, &sep](std::string const& value) {
+ cryptoHash.Append(value);
+ cryptoHash.Append(sep);
+ };
+
+ if (MocConst_.Enabled) {
+ cryptoHash.Initialize();
+ cha(MocConst().Executable);
+ for (auto const& value : MocConst().AllOptions) {
+ cha(value);
+ }
+ cha(BaseConst().IncludeProjectDirsBefore ? "TRUE" : "FALSE");
+ for (auto const& value : MocConst().PredefsCmd) {
+ cha(value);
+ }
+ for (auto const& filter : MocConst().DependFilters) {
+ cha(filter.Key);
+ }
+ for (auto const& filter : MocConst().MacroFilters) {
+ cha(filter.Key);
+ }
+ SettingsStringMoc_ = cryptoHash.FinalizeHex();
+ }
+
+ if (UicConst().Enabled) {
+ cryptoHash.Initialize();
+ cha(UicConst().Executable);
+ for (auto const& value : UicConst().TargetOptions) {
+ cha(value);
+ }
+ for (const auto& item : UicConst().Options) {
+ cha(item.first);
+ for (auto const& svalue : item.second) {
+ cha(svalue);
+ }
+ }
+ SettingsStringUic_ = cryptoHash.FinalizeHex();
}
}
// Read old settings and compare
{
std::string content;
- if (FileSys().FileRead(content, SettingsFile_)) {
- if (Moc().Enabled) {
+ if (cmQtAutoGenerator::FileRead(content, SettingsFile_)) {
+ if (MocConst().Enabled) {
if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
- Moc_.SettingsChanged = true;
+ MocConst_.SettingsChanged = true;
}
}
- if (Uic().Enabled) {
+ if (UicConst().Enabled) {
if (SettingsStringUic_ != SettingsFind(content, "uic")) {
- Uic_.SettingsChanged = true;
+ UicConst_.SettingsChanged = true;
}
}
// In case any setting changed remove the old settings file.
// This triggers a full rebuild on the next run if the current
// build is aborted before writing the current settings in the end.
- if (Moc().SettingsChanged || Uic().SettingsChanged) {
- FileSys().FileRemove(SettingsFile_);
+ if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
+ cmSystemTools::RemoveFile(SettingsFile_);
}
} else {
// Settings file read failed
- if (Moc().Enabled) {
- Moc_.SettingsChanged = true;
+ if (MocConst().Enabled) {
+ MocConst_.SettingsChanged = true;
}
- if (Uic().Enabled) {
- Uic_.SettingsChanged = true;
+ if (UicConst().Enabled) {
+ UicConst_.SettingsChanged = true;
}
}
}
@@ -1561,7 +2083,7 @@ void cmQtAutoMocUic::SettingsFileRead()
bool cmQtAutoMocUic::SettingsFileWrite()
{
// Only write if any setting changed
- if (Moc().SettingsChanged || Uic().SettingsChanged) {
+ if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
if (Log().Verbose()) {
Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
}
@@ -1582,11 +2104,54 @@ bool cmQtAutoMocUic::SettingsFileWrite()
}
// Write settings file
std::string error;
- if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
+ if (!cmQtAutoGenerator::FileWrite(SettingsFile_, content, &error)) {
Log().ErrorFile(GenT::GEN, SettingsFile_,
"Settings file writing failed. " + error);
// Remove old settings file to trigger a full rebuild on the next run
- FileSys().FileRemove(SettingsFile_);
+ cmSystemTools::RemoveFile(SettingsFile_);
+ return false;
+ }
+ }
+ return true;
+}
+
+void cmQtAutoMocUic::ParseCacheRead()
+{
+ const char* reason = nullptr;
+ // Don't read the cache if it is invalid
+ if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) {
+ reason = "Refreshing parse cache because it doesn't exist.";
+ } else if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
+ reason = "Refreshing parse cache because the settings changed.";
+ } else if (BaseEval().ParseCacheTime.Older(
+ BaseConst().CMakeExecutableTime)) {
+ reason =
+ "Refreshing parse cache because it is older than the CMake executable.";
+ }
+
+ if (reason != nullptr) {
+ // Don't read but refresh the complete parse cache
+ if (Log().Verbose()) {
+ Log().Info(GenT::GEN, reason);
+ }
+ BaseEval().ParseCacheChanged = true;
+ } else {
+ // Read parse cache
+ BaseEval().ParseCache.ReadFromFile(BaseConst().ParseCacheFile);
+ }
+}
+
+bool cmQtAutoMocUic::ParseCacheWrite()
+{
+ if (BaseEval().ParseCacheChanged) {
+ if (Log().Verbose()) {
+ Log().Info(GenT::GEN,
+ "Writing parse cache file " +
+ Quoted(BaseConst().ParseCacheFile));
+ }
+ if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) {
+ Log().ErrorFile(GenT::GEN, BaseConst().ParseCacheFile,
+ "Parse cache file writing failed.");
return false;
}
}
@@ -1596,16 +2161,14 @@ bool cmQtAutoMocUic::SettingsFileWrite()
bool cmQtAutoMocUic::CreateDirectories()
{
// Create AUTOGEN include directory
- if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
- Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
+ if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) {
+ Log().ErrorFile(GenT::GEN, BaseConst().AutogenIncludeDir,
"Could not create directory.");
return false;
}
return true;
}
-// Private method that requires cmQtAutoMocUic::JobsMutex_ to be
-// locked
void cmQtAutoMocUic::Abort(bool error)
{
if (error) {
@@ -1614,132 +2177,20 @@ void cmQtAutoMocUic::Abort(bool error)
WorkerPool_.Abort();
}
-bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle)
-{
- JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle));
- // Do additional tests if this is an included moc job
- if (!mocJob.IncludeString.empty()) {
- std::lock_guard<std::mutex> guard(MocMetaMutex_);
- // Register included moc file
- MocIncludedFiles_.emplace(mocJob.SourceFile);
-
- // Check if the same moc file would be generated from a different
- // source file.
- auto const range = MocIncludes_.equal_range(mocJob.IncludeString);
- for (auto it = range.first; it != range.second; ++it) {
- if (it->second[0] == mocJob.SourceFile) {
- // The output file already gets generated
- return true;
- }
- {
- // The output file already gets generated - from a different source
- // file!
- std::string error = "The two source files\n ";
- error += Quoted(mocJob.IncluderFile);
- error += " and\n ";
- error += Quoted(it->second[1]);
- error += "\ncontain the same moc include string ";
- error += Quoted(mocJob.IncludeString);
- error += "\nbut the moc file would be generated from different "
- "source files\n ";
- error += Quoted(mocJob.SourceFile);
- error += " and\n ";
- error += Quoted(it->second[0]);
- error += ".\nConsider to\n"
- "- not include the \"moc_<NAME>.cpp\" file\n"
- "- add a directory prefix to a \"<NAME>.moc\" include "
- "(e.g \"sub/<NAME>.moc\")\n"
- "- rename the source file(s)\n";
- Log().Error(GenT::MOC, error);
- AbortError();
- return false;
- }
- }
-
- // We're still here so register this job
- MocIncludes_.emplace_hint(range.first, mocJob.IncludeString,
- std::array<std::string, 2>{
- { mocJob.SourceFile, mocJob.IncluderFile } });
- }
- return WorkerPool_.PushJob(std::move(jobHandle));
-}
-
-bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle)
-{
- const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
- {
- std::lock_guard<std::mutex> guard(UicMetaMutex_);
- // Check if the same uic file would be generated from a different
- // source file.
- auto const range = UicIncludes_.equal_range(uicJob.IncludeString);
- for (auto it = range.first; it != range.second; ++it) {
- if (it->second[0] == uicJob.SourceFile) {
- // The output file already gets generated
- return true;
- }
- {
- // The output file already gets generated - from a different .ui
- // file!
- std::string error = "The two source files\n ";
- error += Quoted(uicJob.IncluderFile);
- error += " and\n ";
- error += Quoted(it->second[1]);
- error += "\ncontain the same uic include string ";
- error += Quoted(uicJob.IncludeString);
- error += "\nbut the uic file would be generated from different "
- "source files\n ";
- error += Quoted(uicJob.SourceFile);
- error += " and\n ";
- error += Quoted(it->second[0]);
- error +=
- ".\nConsider to\n"
- "- add a directory prefix to a \"ui_<NAME>.h\" include "
- "(e.g \"sub/ui_<NAME>.h\")\n"
- "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
- "include(s)\n";
- Log().Error(GenT::UIC, error);
- AbortError();
- return false;
- }
- }
-
- // We're still here so register this job
- UicIncludes_.emplace_hint(range.first, uicJob.IncludeString,
- std::array<std::string, 2>{
- { uicJob.SourceFile, uicJob.IncluderFile } });
- }
- return WorkerPool_.PushJob(std::move(jobHandle));
-}
-
-bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile)
+std::string cmQtAutoMocUic::AbsoluteBuildPath(
+ std::string const& relativePath) const
{
- std::lock_guard<std::mutex> guard(MocMetaMutex_);
- return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
+ std::string res(BaseConst().AutogenBuildDir);
+ res += '/';
+ res += relativePath;
+ return res;
}
-std::string cmQtAutoMocUic::ParallelMocAutoRegister(
- std::string const& baseName)
+std::string cmQtAutoMocUic::AbsoluteIncludePath(
+ std::string const& relativePath) const
{
- std::string res;
- {
- std::lock_guard<std::mutex> mocLock(MocMetaMutex_);
- res = baseName;
- res += ".cpp";
- if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
- MocAutoFiles_.emplace(res);
- } else {
- // Append number suffix to the file name
- for (unsigned int ii = 2; ii != 1024; ++ii) {
- res = baseName;
- res += '_';
- res += std::to_string(ii);
- res += ".cpp";
- if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
- MocAutoFiles_.emplace(res);
- break;
- }
- }
- }
- }
+ std::string res(BaseConst().AutogenIncludeDir);
+ res += '/';
+ res += relativePath;
return res;
}