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"
This commit is contained in:
Sebastian Holtermann
2019-05-07 12:42:19 +02:00
parent c6f6e2b305
commit 7d50e1c611
9 changed files with 2172 additions and 1703 deletions
+4 -4
View File
@@ -194,11 +194,11 @@ std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
std::string cmQtAutoGen::SubDirPrefix(std::string const& filename)
{
std::string res(cmSystemTools::GetFilenamePath(filename));
if (!res.empty()) {
res += '/';
std::string::size_type slash_pos = filename.rfind('/');
if (slash_pos == std::string::npos) {
return std::string();
}
return res;
return filename.substr(0, slash_pos + 1);
}
std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename,
+105 -44
View File
@@ -34,6 +34,7 @@
#include <map>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -77,7 +78,8 @@ static std::string FileProjectRelativePath(cmMakefile* makefile,
return res;
}
/* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its
/**
* Tests if targetDepend is a STATIC_LIBRARY and if any of its
* recursive STATIC_LIBRARY dependencies depends on targetOrigin
* (STATIC_LIBRARY cycle).
*/
@@ -384,6 +386,10 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
} else {
AddCleanFile(makefile, this->AutogenTarget.SettingsFile);
}
this->AutogenTarget.ParseCacheFile = this->Dir.Info;
this->AutogenTarget.ParseCacheFile += "/ParseCache.txt";
AddCleanFile(makefile, this->AutogenTarget.ParseCacheFile);
}
// Autogen target: Compute user defined dependencies
@@ -1255,53 +1261,107 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
ofs.Write("AM_INCLUDE_DIR", this->Dir.Include);
ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude);
// Use sorted sets
std::set<std::string> headers;
std::set<std::string> sources;
std::set<std::string> moc_headers;
std::set<std::string> moc_sources;
std::vector<std::string> headers;
std::vector<std::string> headersFlags;
std::vector<std::string> headersBuildPaths;
std::vector<std::string> sources;
std::vector<std::string> sourcesFlags;
std::set<std::string> moc_skip;
std::set<std::string> uic_headers;
std::set<std::string> uic_sources;
std::set<std::string> uic_skip;
// Filter headers
for (auto const& pair : this->AutogenTarget.Headers) {
MUFile const& muf = *pair.second;
if (muf.Generated && !this->CMP0071Accept) {
continue;
{
auto headerCount = this->AutogenTarget.Headers.size();
headers.reserve(headerCount);
headersFlags.reserve(headerCount);
std::vector<MUFile const*> sortedHeaders;
{
sortedHeaders.reserve(headerCount);
for (auto const& pair : this->AutogenTarget.Headers) {
sortedHeaders.emplace_back(pair.second.get());
}
std::sort(sortedHeaders.begin(), sortedHeaders.end(),
[](MUFile const* a, MUFile const* b) {
return (a->RealPath < b->RealPath);
});
}
if (muf.SkipMoc) {
moc_skip.insert(muf.RealPath);
}
if (muf.SkipUic) {
uic_skip.insert(muf.RealPath);
}
if (muf.MocIt && muf.UicIt) {
headers.insert(muf.RealPath);
} else if (muf.MocIt) {
moc_headers.insert(muf.RealPath);
} else if (muf.UicIt) {
uic_headers.insert(muf.RealPath);
for (MUFile const* const muf : sortedHeaders) {
if (muf->Generated && !this->CMP0071Accept) {
continue;
}
if (muf->SkipMoc) {
moc_skip.insert(muf->RealPath);
}
if (muf->SkipUic) {
uic_skip.insert(muf->RealPath);
}
if (muf->MocIt || muf->UicIt) {
headers.emplace_back(muf->RealPath);
std::string flags;
flags += muf->MocIt ? 'M' : 'm';
flags += muf->UicIt ? 'U' : 'u';
headersFlags.emplace_back(std::move(flags));
}
}
}
// Header build paths
{
cmFilePathChecksum const fpathCheckSum(makefile);
std::unordered_set<std::string> emitted;
for (std::string const& hdr : headers) {
std::string basePath = fpathCheckSum.getPart(hdr);
basePath += "/moc_";
basePath += cmSystemTools::GetFilenameWithoutLastExtension(hdr);
for (unsigned int ii = 1; ii != 1024; ++ii) {
std::string path = basePath;
if (ii > 1) {
path += '_';
path += std::to_string(ii);
}
path += ".cpp";
if (emitted.emplace(path).second) {
headersBuildPaths.emplace_back(std::move(path));
break;
}
}
}
}
// Filter sources
for (auto const& pair : this->AutogenTarget.Sources) {
MUFile const& muf = *pair.second;
if (muf.Generated && !this->CMP0071Accept) {
continue;
{
auto sourcesCount = this->AutogenTarget.Sources.size();
sources.reserve(sourcesCount);
sourcesFlags.reserve(sourcesCount);
std::vector<MUFile const*> sorted;
sorted.reserve(sourcesCount);
for (auto const& pair : this->AutogenTarget.Sources) {
sorted.emplace_back(pair.second.get());
}
if (muf.SkipMoc) {
moc_skip.insert(muf.RealPath);
}
if (muf.SkipUic) {
uic_skip.insert(muf.RealPath);
}
if (muf.MocIt && muf.UicIt) {
sources.insert(muf.RealPath);
} else if (muf.MocIt) {
moc_sources.insert(muf.RealPath);
} else if (muf.UicIt) {
uic_sources.insert(muf.RealPath);
std::sort(sorted.begin(), sorted.end(),
[](MUFile const* a, MUFile const* b) {
return (a->RealPath < b->RealPath);
});
for (MUFile const* const muf : sorted) {
if (muf->Generated && !this->CMP0071Accept) {
continue;
}
if (muf->SkipMoc) {
moc_skip.insert(muf->RealPath);
}
if (muf->SkipUic) {
uic_skip.insert(muf->RealPath);
}
if (muf->MocIt || muf->UicIt) {
sources.emplace_back(muf->RealPath);
std::string flags;
flags += muf->MocIt ? 'M' : 'm';
flags += muf->UicIt ? 'U' : 'u';
sourcesFlags.emplace_back(std::move(flags));
}
}
}
@@ -1311,17 +1371,20 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable);
ofs.Write("# Files\n");
ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile);
ofs.WriteConfig("AM_SETTINGS_FILE",
this->AutogenTarget.ConfigSettingsFile);
ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
ofs.WriteStrings("AM_HEADERS", headers);
ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags);
ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths);
ofs.WriteStrings("AM_SOURCES", sources);
ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags);
// Write moc settings
if (this->Moc.Enabled) {
ofs.Write("# MOC settings\n");
ofs.WriteStrings("AM_MOC_HEADERS", moc_headers);
ofs.WriteStrings("AM_MOC_SOURCES", moc_sources);
ofs.WriteStrings("AM_MOC_SKIP", moc_skip);
ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines);
ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines);
@@ -1343,8 +1406,6 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
ofs.Write("# UIC settings\n");
ofs.WriteStrings("AM_UIC_HEADERS", uic_headers);
ofs.WriteStrings("AM_UIC_SOURCES", uic_sources);
ofs.WriteStrings("AM_UIC_SKIP", uic_skip);
ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options);
ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions);
+1
View File
@@ -183,6 +183,7 @@ private:
// Configuration files
std::string InfoFile;
std::string SettingsFile;
std::string ParseCacheFile;
std::map<std::string, std::string> ConfigSettingsFile;
// Dependencies
bool DependOrigin = false;
+25 -176
View File
@@ -66,7 +66,8 @@ std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title)
return head;
}
void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message)
void cmQtAutoGenerator::Logger::Info(GenT genType,
std::string const& message) const
{
std::string msg = GeneratorName(genType);
msg += ": ";
@@ -81,7 +82,7 @@ void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message)
}
void cmQtAutoGenerator::Logger::Warning(GenT genType,
std::string const& message)
std::string const& message) const
{
std::string msg;
if (message.find('\n') == std::string::npos) {
@@ -106,7 +107,7 @@ void cmQtAutoGenerator::Logger::Warning(GenT genType,
void cmQtAutoGenerator::Logger::WarningFile(GenT genType,
std::string const& filename,
std::string const& message)
std::string const& message) const
{
std::string msg = " ";
msg += Quoted(filename);
@@ -116,7 +117,8 @@ void cmQtAutoGenerator::Logger::WarningFile(GenT genType,
Warning(genType, msg);
}
void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message)
void cmQtAutoGenerator::Logger::Error(GenT genType,
std::string const& message) const
{
std::string msg;
msg += HeadLine(GeneratorName(genType) + " error");
@@ -134,7 +136,7 @@ void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message)
void cmQtAutoGenerator::Logger::ErrorFile(GenT genType,
std::string const& filename,
std::string const& message)
std::string const& message) const
{
std::string emsg = " ";
emsg += Quoted(filename);
@@ -146,7 +148,7 @@ void cmQtAutoGenerator::Logger::ErrorFile(GenT genType,
void cmQtAutoGenerator::Logger::ErrorCommand(
GenT genType, std::string const& message,
std::vector<std::string> const& command, std::string const& output)
std::vector<std::string> const& command, std::string const& output) const
{
std::string msg;
msg.push_back('\n');
@@ -191,7 +193,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content,
content.clear();
if (!cmSystemTools::FileExists(filename, true)) {
if (error != nullptr) {
error->append("Not a file.");
*error = "Not a file.";
}
return false;
}
@@ -203,7 +205,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content,
return [&ifs, length, &content, error]() -> bool {
if (!ifs) {
if (error != nullptr) {
error->append("Opening the file for reading failed.");
*error = "Opening the file for reading failed.";
}
return false;
}
@@ -213,7 +215,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content,
if (!ifs) {
content.clear();
if (error != nullptr) {
error->append("Reading from the file failed.");
*error = "Reading from the file failed.";
}
return false;
}
@@ -228,7 +230,7 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename,
// Make sure the parent directory exists
if (!cmQtAutoGenerator::MakeParentDirectory(filename)) {
if (error != nullptr) {
error->assign("Could not create parent directory.");
*error = "Could not create parent directory.";
}
return false;
}
@@ -240,14 +242,14 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename,
return [&ofs, &content, error]() -> bool {
if (!ofs) {
if (error != nullptr) {
error->assign("Opening file for writing failed.");
*error = "Opening file for writing failed.";
}
return false;
}
ofs << content;
if (!ofs.good()) {
if (error != nullptr) {
error->assign("File writing failed.");
*error = "File writing failed.";
}
return false;
}
@@ -255,176 +257,17 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename,
}();
}
cmQtAutoGenerator::FileSystem::FileSystem() = default;
cmQtAutoGenerator::FileSystem::~FileSystem() = default;
std::string cmQtAutoGenerator::FileSystem::GetRealPath(
std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::GetRealPath(filename);
}
std::string cmQtAutoGenerator::FileSystem::CollapseFullPath(
std::string const& file, std::string const& dir)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::CollapseFullPath(file, dir);
}
void cmQtAutoGenerator::FileSystem::SplitPath(
const std::string& p, std::vector<std::string>& components,
bool expand_home_dir)
{
std::lock_guard<std::mutex> lock(Mutex_);
cmSystemTools::SplitPath(p, components, expand_home_dir);
}
std::string cmQtAutoGenerator::FileSystem::JoinPath(
const std::vector<std::string>& components)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::JoinPath(components);
}
std::string cmQtAutoGenerator::FileSystem::JoinPath(
std::vector<std::string>::const_iterator first,
std::vector<std::string>::const_iterator last)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::JoinPath(first, last);
}
std::string cmQtAutoGenerator::FileSystem::GetFilenameWithoutLastExtension(
const std::string& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::GetFilenameWithoutLastExtension(filename);
}
std::string cmQtAutoGenerator::FileSystem::SubDirPrefix(
std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmQtAutoGen::SubDirPrefix(filename);
}
void cmQtAutoGenerator::FileSystem::setupFilePathChecksum(
std::string const& currentSrcDir, std::string const& currentBinDir,
std::string const& projectSrcDir, std::string const& projectBinDir)
{
std::lock_guard<std::mutex> lock(Mutex_);
FilePathChecksum_.setupParentDirs(currentSrcDir, currentBinDir,
projectSrcDir, projectBinDir);
}
std::string cmQtAutoGenerator::FileSystem::GetFilePathChecksum(
std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return FilePathChecksum_.getPart(filename);
}
bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::FileExists(filename);
}
bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename,
bool isFile)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::FileExists(filename, isFile);
}
unsigned long cmQtAutoGenerator::FileSystem::FileLength(
std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::FileLength(filename);
}
bool cmQtAutoGenerator::FileSystem::FileIsOlderThan(
std::string const& buildFile, std::string const& sourceFile,
std::string* error)
{
bool res(false);
int result = 0;
{
std::lock_guard<std::mutex> lock(Mutex_);
res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result);
}
if (res) {
res = (result < 0);
} else {
if (error != nullptr) {
error->append(
"File modification time comparison failed for the files\n ");
error->append(Quoted(buildFile));
error->append("\nand\n ");
error->append(Quoted(sourceFile));
}
}
return res;
}
bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content,
std::string const& filename,
std::string* error)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmQtAutoGenerator::FileRead(content, filename, error);
}
bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename,
std::string const& content,
std::string* error)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmQtAutoGenerator::FileWrite(filename, content, error);
}
bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename,
std::string const& content)
bool cmQtAutoGenerator::FileDiffers(std::string const& filename,
std::string const& content)
{
bool differs = true;
{
std::string oldContents;
if (FileRead(oldContents, filename)) {
differs = (oldContents != content);
}
std::string oldContents;
if (FileRead(oldContents, filename) && (oldContents == content)) {
differs = false;
}
return differs;
}
bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::RemoveFile(filename);
}
bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename,
bool create)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::Touch(filename, create);
}
bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmSystemTools::MakeDirectory(dirname);
}
bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
std::string const& filename)
{
std::lock_guard<std::mutex> lock(Mutex_);
return cmQtAutoGenerator::MakeParentDirectory(filename);
}
cmQtAutoGenerator::cmQtAutoGenerator() = default;
cmQtAutoGenerator::~cmQtAutoGenerator() = default;
@@ -435,6 +278,12 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile,
// Info settings
InfoFile_ = infoFile;
cmSystemTools::ConvertToUnixSlashes(InfoFile_);
if (!InfoFileTime_.Load(InfoFile_)) {
std::string msg = "Autogen: The info file ";
msg += Quoted(InfoFile_);
msg += " is not readable\n";
cmSystemTools::Stderr(msg);
}
InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
InfoConfig_ = config;
+19 -75
View File
@@ -5,7 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmFilePathChecksum.h"
#include "cmFileTime.h"
#include "cmQtAutoGen.h"
#include <mutex>
@@ -14,13 +14,17 @@
class cmMakefile;
/// @brief Base class for QtAutoGen gernerators
/** \class cmQtAutoGenerator
* \brief Base class for QtAutoGen generators
*/
class cmQtAutoGenerator : public cmQtAutoGen
{
public:
// -- Types
/// @brief Thread safe logging
/**
* Thread safe logger
*/
class Logger
{
public:
@@ -37,24 +41,24 @@ public:
bool ColorOutput() const { return this->ColorOutput_; }
void SetColorOutput(bool value);
// -- Log info
void Info(GenT genType, std::string const& message);
void Info(GenT genType, std::string const& message) const;
// -- Log warning
void Warning(GenT genType, std::string const& message);
void Warning(GenT genType, std::string const& message) const;
void WarningFile(GenT genType, std::string const& filename,
std::string const& message);
std::string const& message) const;
// -- Log error
void Error(GenT genType, std::string const& message);
void Error(GenT genType, std::string const& message) const;
void ErrorFile(GenT genType, std::string const& filename,
std::string const& message);
std::string const& message) const;
void ErrorCommand(GenT genType, std::string const& message,
std::vector<std::string> const& command,
std::string const& output);
std::string const& output) const;
private:
static std::string HeadLine(std::string const& title);
private:
std::mutex Mutex_;
mutable std::mutex Mutex_;
unsigned int Verbosity_ = 0;
bool ColorOutput_ = false;
};
@@ -66,70 +70,8 @@ public:
static bool FileWrite(std::string const& filename,
std::string const& content,
std::string* error = nullptr);
/// @brief Thread safe file system interface
class FileSystem
{
public:
FileSystem();
~FileSystem();
// -- Paths
/// @brief Wrapper for cmSystemTools::GetRealPath
std::string GetRealPath(std::string const& filename);
/// @brief Wrapper for cmSystemTools::CollapseFullPath
std::string CollapseFullPath(std::string const& file,
std::string const& dir);
/// @brief Wrapper for cmSystemTools::SplitPath
void SplitPath(const std::string& p, std::vector<std::string>& components,
bool expand_home_dir = true);
/// @brief Wrapper for cmSystemTools::JoinPath
std::string JoinPath(const std::vector<std::string>& components);
/// @brief Wrapper for cmSystemTools::JoinPath
std::string JoinPath(std::vector<std::string>::const_iterator first,
std::vector<std::string>::const_iterator last);
/// @brief Wrapper for cmSystemTools::GetFilenameWithoutLastExtension
std::string GetFilenameWithoutLastExtension(const std::string& filename);
/// @brief Wrapper for cmQtAutoGen::SubDirPrefix
std::string SubDirPrefix(std::string const& filename);
/// @brief Wrapper for cmFilePathChecksum::setupParentDirs
void setupFilePathChecksum(std::string const& currentSrcDir,
std::string const& currentBinDir,
std::string const& projectSrcDir,
std::string const& projectBinDir);
/// @brief Wrapper for cmFilePathChecksum::getPart
std::string GetFilePathChecksum(std::string const& filename);
// -- File access
/// @brief Wrapper for cmSystemTools::FileExists
bool FileExists(std::string const& filename);
/// @brief Wrapper for cmSystemTools::FileExists
bool FileExists(std::string const& filename, bool isFile);
/// @brief Wrapper for cmSystemTools::FileLength
unsigned long FileLength(std::string const& filename);
bool FileIsOlderThan(std::string const& buildFile,
std::string const& sourceFile,
std::string* error = nullptr);
bool FileRead(std::string& content, std::string const& filename,
std::string* error = nullptr);
bool FileWrite(std::string const& filename, std::string const& content,
std::string* error = nullptr);
bool FileDiffers(std::string const& filename, std::string const& content);
bool FileRemove(std::string const& filename);
bool Touch(std::string const& filename, bool create = false);
// -- Directory access
bool MakeDirectory(std::string const& dirname);
bool MakeParentDirectory(std::string const& filename);
private:
std::mutex Mutex_;
cmFilePathChecksum FilePathChecksum_;
};
static bool FileDiffers(std::string const& filename,
std::string const& content);
public:
// -- Constructors
@@ -142,8 +84,9 @@ public:
// -- Run
bool Run(std::string const& infoFile, std::string const& config);
// InfoFile
// -- InfoFile
std::string const& InfoFile() const { return InfoFile_; }
cmFileTime const& InfoFileTime() const { return InfoFileTime_; }
std::string const& InfoDir() const { return InfoDir_; }
std::string const& InfoConfig() const { return InfoConfig_; }
@@ -158,6 +101,7 @@ protected:
private:
// -- Info settings
std::string InfoFile_;
cmFileTime InfoFileTime_;
std::string InfoDir_;
std::string InfoConfig_;
};
+1676 -1225
View File
@@ -4,7 +4,6 @@
#include <algorithm>
#include <array>
#include <deque>
#include <list>
#include <memory>
#include <set>
@@ -13,69 +12,189 @@
#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
};
}
cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default;
cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default;
void cmQtAutoMocUic::ParseCacheT::Clear()
{
Map_.clear();
}
bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName)
{
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 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()));
}
/**
* @brief Returns the first relevant Qt macro name found in the given C++ code
* @return The name of the Qt macro or an empty string
*/
std::string cmQtAutoMocUic::MocSettingsT::FindMacro(
std::string const& content) const
{
for (KeyExpT const& filter : MacroFilters) {
// Run a simple find string operation before the expensive
// regular expression check
if (content.find(filter.Key) != std::string::npos) {
cmsys::RegularExpressionMatch match;
if (filter.Exp.find(content.c_str(), match)) {
// Return macro name on demand
return filter.Key;
}
}
}
return std::string();
}
std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
{
std::string res;
@@ -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 (generate) {
if (!Update(reason.get())) {
return;
}
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);
}
}
}
}
void cmQtAutoMocUic::JobParseT::Process()
{
if (AutoMoc && Header) {
// Don't parse header for moc if the file is included by a source already
if (Gen()->ParallelMocIncluded(FileName)) {
AutoMoc = false;
}
}
if (AutoMoc || AutoUic) {
std::string error;
MetaT meta;
if (FileSys().FileRead(meta.Content, FileName, &error)) {
if (!meta.Content.empty()) {
meta.FileDir = FileSys().SubDirPrefix(FileName);
meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName);
bool success = true;
if (AutoMoc) {
if (Header) {
success = ParseMocHeader(meta);
} else {
success = ParseMocSource(meta);
}
}
if (AutoUic && success) {
ParseUic(meta);
}
} else {
Log().WarningFile(GenT::GEN, FileName, "The source file is empty");
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 {
LogFileError(GenT::GEN, FileName, "Could not read the file: " + error);
}
}
}
bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta)
{
struct JobPre
{
bool self; // source file is self
bool underscore; // "moc_" style include
std::string SourceFile;
std::string IncludeString;
};
struct MocInclude
{
std::string Inc; // full include string
std::string Dir; // include string directory
std::string Base; // include string file base
};
// Check if this source file contains a relevant macro
std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content);
// Extract moc includes from file
std::deque<MocInclude> mocIncsUsc;
std::deque<MocInclude> mocIncsDot;
{
if (meta.Content.find("moc") != std::string::npos) {
const char* contentChars = meta.Content.c_str();
cmsys::RegularExpressionMatch match;
while (Gen()->Moc().RegExpInclude.find(contentChars, match)) {
std::string incString = match.match(2);
std::string incDir(FileSys().SubDirPrefix(incString));
std::string incBase =
FileSys().GetFilenameWithoutLastExtension(incString);
if (cmHasLiteralPrefix(incBase, "moc_")) {
// moc_<BASE>.cxx
// Remove the moc_ part from the base name
mocIncsUsc.emplace_back(MocInclude{
std::move(incString), std::move(incDir), incBase.substr(4) });
} else {
// <BASE>.moc
mocIncsDot.emplace_back(MocInclude{
std::move(incString), std::move(incDir), std::move(incBase) });
}
// Forward content pointer
contentChars += match.end();
// 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;
}
}
}
// Check if there is anything to do
if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
// Read file time afterwards
if (!MocEval().PredefsTime.Load(predefsFileAbs)) {
LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed.");
return;
}
}
bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const
{
// 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;
}
bool ownDotMocIncluded = false;
bool ownMocUscIncluded = false;
std::deque<JobPre> jobs;
// 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;
}
// 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;
// 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 += ".";
}
return true;
}
// Register moc job
const bool ownMoc = (mocInc.Base == meta.FileBase);
jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc });
// Store meta information for relaxed mode
if (ownMoc) {
ownMocUscIncluded = true;
}
} else {
}
}
return false;
}
bool cmQtAutoMocUic::JobParseT::ReadFile()
{
// 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
{
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;
}
// 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 emsg = "The file includes the moc file ";
emsg += Quoted(mocInc.Inc);
emsg += ", but the header ";
emsg += Quoted(MocStringHeaders(mocInc.Base));
emsg += " could not be found.";
LogFileError(GenT::MOC, FileName, emsg);
std::string dep = match.match(1);
if (!dep.empty()) {
parseDepends.emplace(std::move(dep));
}
}
contentChars += match.end();
}
}
// Store dependency strings
{
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', ' ');
}
}
}
void cmQtAutoMocUic::JobParseT::MocIncludes()
{
if (Content.find("moc") == std::string::npos) {
return;
}
std::set<std::string> underscore;
std::set<std::string> dot;
{
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;
}
}
// 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;
}
if (!parseData.Macro.empty()) {
// Create a new mapping
MappingHandleT handle = std::make_shared<MappingT>();
handle->SourceFile = std::move(source);
// Absolute build path
if (BaseConst().MultiConfig) {
handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath);
} else {
handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath);
}
// Register mapping in headers map
MocRegisterMapping(handle, true);
}
return true;
}
bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource(
SourceFileHandleT const& source)
{
SourceFileT const& sourceFile = *source;
auto const& parseData = sourceFile.ParseData->Moc;
if (!sourceFile.Moc ||
(parseData.Macro.empty() && parseData.Include.Underscore.empty() &&
parseData.Include.Dot.empty())) {
return true;
}
std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
std::string const sourceBase =
cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName);
// For relaxed mode check if the own "moc_" or ".moc" file is included
bool const relaxedMode = MocConst().RelaxedMode;
bool sourceIncludesMocUnderscore = false;
bool sourceIncludesDotMoc = false;
// Check if the sources own "moc_" or ".moc" file is included
if (relaxedMode) {
for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
if (incKey.Base == sourceBase) {
sourceIncludesMocUnderscore = true;
break;
}
}
}
for (IncludeKeyT const& incKey : parseData.Include.Dot) {
if (incKey.Base == sourceBase) {
sourceIncludesDotMoc = true;
break;
}
}
// Check if this source needs to be moc processed but doesn't.
if (!sourceIncludesDotMoc && !parseData.Macro.empty() &&
!(relaxedMode && sourceIncludesMocUnderscore)) {
{
std::string emsg = "The file contains a ";
emsg += Quoted(parseData.Macro);
emsg += " macro, but does not include ";
emsg += Quoted(sourceBase + ".moc");
emsg += "!\nConsider to\n - add #include \"";
emsg += sourceBase;
emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
LogFileError(GenT::MOC, sourceFile.FileName, emsg);
}
return false;
}
// Evaluate "moc_" includes
for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
std::string const headerBase = incKey.Dir + incKey.Base;
SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
if (!header) {
{
std::string msg = "The file includes the moc file ";
msg += Quoted(incKey.Key);
msg += ",\nbut the header could not be found "
"in the following locations\n";
msg += MocMessageTestHeaders(headerBase);
LogFileError(GenT::MOC, sourceFile.FileName, msg);
}
return false;
}
// The include might be handled differently in relaxed mode
if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() &&
(incKey.Base == sourceBase)) {
// The <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.
{
// 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;
}
}
} 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;
}
// 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;
}
}
}
if (!ownMacro.empty() && !ownDotMocIncluded) {
// In this case, check whether the scanned file itself contains a
// Q_OBJECT.
// If this is the case, the moc_foo.cpp should probably be generated from
// foo.cpp instead of foo.h, because otherwise it won't build.
// But warn, since this is not how it is supposed to be used.
// This is for KDE4 compatibility.
if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) {
JobPre uscJobPre;
// Remove underscore job request
{
auto itC = jobs.begin();
auto itE = jobs.end();
for (; itC != itE; ++itC) {
JobPre& job(*itC);
if (job.self && job.underscore) {
uscJobPre = std::move(job);
jobs.erase(itC);
break;
}
}
}
// Issue a warning
{
std::string emsg = "The file contains a ";
emsg += ownMacro;
emsg += " macro, but does not include ";
emsg += Quoted(meta.FileBase + ".moc");
emsg += ". Instead it includes ";
emsg += Quoted(uscJobPre.IncludeString);
emsg += ".\nRunning moc on\n ";
emsg += Quoted(FileName);
emsg += "!\nBetter include ";
emsg += Quoted(meta.FileBase + ".moc");
emsg += " for compatibility with strict mode.\n"
"(CMAKE_AUTOMOC_RELAXED_MODE warning)";
Log().WarningFile(GenT::MOC, FileName, emsg);
}
// Add own source job
jobs.emplace_back(
JobPre{ true, false, FileName, uscJobPre.IncludeString });
} else {
// Otherwise always error out since it will not compile.
{
std::string emsg = "The file contains a ";
emsg += ownMacro;
emsg += " macro, but does not include ";
emsg += Quoted(meta.FileBase + ".moc");
emsg += "!\nConsider to\n - add #include \"";
emsg += meta.FileBase;
emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
LogFileError(GenT::MOC, FileName, emsg);
}
return false;
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;
}
}
// 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;
// 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;
}
if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) {
}
// Return without success
return SourceFileHandleT();
}
cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader(
std::string const& basePath) const
{
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;
}
}
// Return without success
return SourceFileHandleT();
}
std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders(
std::string const& fileBase) const
{
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();
}
bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded(
std::string const& includeString, SourceFileHandleT includerFileHandle,
SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const
{
// 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;
}
// 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);
}
}
}
bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap)
{
for (auto const& pair : fileMap) {
if (!UicEvalFile(pair.second)) {
return false;
}
}
return true;
}
bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta)
bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile(
SourceFileHandleT sourceFileHandle)
{
bool success = true;
std::string const macroName = Gen()->Moc().FindMacro(meta.Content);
if (!macroName.empty()) {
cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
std::string(FileName), std::string(), std::string());
// Read dependencies from this source
{
JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
jobMoc.DependsValid = true;
}
success = Gen()->ParallelJobPushMoc(std::move(jobHandle));
SourceFileT const& sourceFile = *sourceFileHandle;
auto const& Include = sourceFile.ParseData->Uic.Include;
if (!sourceFile.Uic || Include.empty()) {
return true;
}
return success;
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 true;
}
std::string cmQtAutoMocUic::JobParseT::MocStringHeaders(
std::string const& fileBase) const
bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping(
std::string const& includeString, SourceFileHandleT uiFileHandle,
SourceFileHandleT includerFileHandle)
{
std::string res = fileBase;
res += ".{";
res += cmJoin(Gen()->Base().HeaderExtensions, ",");
res += "}";
return res;
}
std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader(
std::string const& includerDir, std::string const& includeBase)
{
std::string header;
// Search in vicinity of the source
if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) {
// Search in include directories
for (std::string const& path : Gen()->Moc().IncludePaths) {
std::string fullPath = path;
fullPath.push_back('/');
fullPath += includeBase;
if (Gen()->Base().FindHeader(header, fullPath)) {
break;
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));
}
// Sanitize
if (!header.empty()) {
header = FileSys().GetRealPath(header);
}
return header;
return true;
}
bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta)
cmQtAutoMocUic::SourceFileHandleT
cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi(
std::string const& sourceFile, std::string const& sourceDir,
IncludeKeyT const& incKey) const
{
bool success = true;
if (meta.Content.find("ui_") != std::string::npos) {
const char* contentChars = meta.Content.c_str();
cmsys::RegularExpressionMatch match;
while (Gen()->Uic().RegExpInclude.find(contentChars, match)) {
if (!ParseUicInclude(meta, match.match(2))) {
success = false;
break;
}
contentChars += match.end();
}
}
return success;
}
bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta,
std::string&& includeString)
{
bool success = false;
std::string uiInputFile = UicFindIncludedFile(meta, includeString);
if (!uiInputFile.empty()) {
if (!Gen()->Uic().skipped(uiInputFile)) {
cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>(
std::move(uiInputFile), FileName, std::move(includeString));
success = Gen()->ParallelJobPushUic(std::move(jobHandle));
} else {
// A skipped file is successful
success = true;
}
}
return success;
}
std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile(
MetaT const& meta, std::string const& includeString)
{
std::string res;
std::string searchFile =
FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
searchFile += ".ui";
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,414 +1100,430 @@ 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>();
}
bool cmQtAutoMocUic::JobGenerateT::MocGenerate(MappingHandleT const& mapping,
bool compFile) const
{
std::unique_ptr<std::string> reason;
if (Log().Verbose()) {
reason = cm::make_unique<std::string>();
}
if (MocUpdate(*mapping, reason.get())) {
// Create the parent directory
if (!MakeParentDirectory(mapping->OutputFile)) {
LogFileError(GenT::MOC, mapping->OutputFile,
"Could not create parent directory.");
return false;
}
// Add moc job
Gen()->WorkerPool().EmplaceJob<JobMocT>(mapping, std::move(reason));
// Check if a moc job for a mocs_compilation.cpp entry was generated
if (compFile) {
MocEval().CompUpdated = true;
}
}
return true;
}
bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping,
std::string* reason) const
{
std::string const& sourceFile = mapping.SourceFile->FileName;
std::string const& outputFile = mapping.OutputFile;
// Test if the output file exists
cmFileTime outputFileTime;
if (!outputFileTime.Load(outputFile)) {
if (reason != nullptr) {
*reason = "Generating ";
*reason += Quoted(outputFile);
*reason += ", because it doesn't exist, from ";
*reason += Quoted(sourceFile);
}
return true;
}
// Test if any setting changed
if (MocConst().SettingsChanged) {
if (reason != nullptr) {
*reason = "Generating ";
*reason += Quoted(outputFile);
*reason += ", because the uic settings changed, from ";
*reason += Quoted(sourceFile);
}
return true;
}
// Test if the 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 (!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 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
{
// Check dependency timestamps
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);
}
return true;
}
}
}
return false;
}
std::pair<std::string, cmFileTime>
cmQtAutoMocUic::JobGenerateT::MocFindDependency(
std::string const& sourceDir, std::string const& includeString) const
{
typedef std::pair<std::string, cmFileTime> ResPair;
// Search in vicinity of the source
{
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();
}
bool cmQtAutoMocUic::JobGenerateT::UicGenerate(
MappingHandleT const& mapping) const
{
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::JobGenerateT::UicUpdate(MappingT const& mapping,
std::string* reason) const
{
std::string const& sourceFile = mapping.SourceFile->FileName;
std::string const& outputFile = mapping.OutputFile;
// Test if the build file exists
cmFileTime outputFileTime;
if (!outputFileTime.Load(outputFile)) {
if (reason != nullptr) {
*reason = "Generating ";
*reason += Quoted(outputFile);
*reason += ", because it doesn't exist, from ";
*reason += Quoted(sourceFile);
}
return true;
}
// Test if the uic settings changed
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
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);
}
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::JobMocT::Process()
{
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);
{
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.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, 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::JobMocsCompilationT::Process()
{
// Compose mocs compilation file content
std::string content =
"// This file is autogenerated. Changes will be overwritten.\n";
if (Gen()->MocAutoFiles().empty()) {
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 sbeg = Gen()->Base().MultiConfig ? '<' : '"';
char const send = Gen()->Base().MultiConfig ? '>' : '"';
for (std::string const& mocfile : Gen()->MocAutoFiles()) {
char const clampB = BaseConst().MultiConfig ? '<' : '"';
char const clampE = BaseConst().MultiConfig ? '>' : '"';
for (std::string const& mocfile : MocEval().CompFiles) {
content += "#include ";
content += sbeg;
content += clampB;
content += mocfile;
content += send;
content += clampE;
content += '\n';
}
}
std::string const& compAbs = Gen()->Moc().CompFileAbs;
if (FileSys().FileDiffers(compAbs, content)) {
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 (!FileSys().FileWrite(compAbs, content)) {
if (!FileWrite(compAbs, content)) {
LogFileError(GenT::MOC, compAbs,
"mocs compilation file writing failed.");
}
} else if (Gen()->MocAutoFileUpdated()) {
} else if (MocEval().CompUpdated) {
// Only touch mocs compilation file
if (Log().Verbose()) {
Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
}
FileSys().Touch(compAbs);
}
}
void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content)
{
Gen()->Moc().FindDependencies(content, Depends);
DependsValid = true;
}
void cmQtAutoMocUic::JobMocT::Process()
{
// Compute build file name
if (!IncludeString.empty()) {
BuildFile = Gen()->Base().AutogenIncludeDir;
BuildFile += '/';
BuildFile += IncludeString;
} else {
// Relative build path
std::string relPath = FileSys().GetFilePathChecksum(SourceFile);
relPath += "/moc_";
relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile);
// Register relative file path with duplication check
relPath = Gen()->ParallelMocAutoRegister(relPath);
// Absolute build path
if (Gen()->Base().MultiConfig) {
BuildFile = Gen()->Base().AutogenIncludeDir;
BuildFile += '/';
BuildFile += relPath;
} else {
BuildFile = Gen()->Base().AbsoluteBuildPath(relPath);
}
}
if (UpdateRequired()) {
GenerateMoc();
}
}
bool cmQtAutoMocUic::JobMocT::UpdateRequired()
{
bool const verbose = Log().Verbose();
// Test if the build file exists
if (!FileSys().FileExists(BuildFile)) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " from its source file ";
reason += Quoted(SourceFile);
reason += " because it doesn't exist";
Log().Info(GenT::MOC, reason);
}
return true;
}
// Test if any setting changed
if (Gen()->Moc().SettingsChanged) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " from ";
reason += Quoted(SourceFile);
reason += " because the MOC settings changed";
Log().Info(GenT::MOC, reason);
}
return true;
}
// Test if the moc_predefs file is newer
if (!Gen()->Moc().PredefsFileAbs.empty()) {
bool isOlder = false;
{
std::string error;
isOlder = FileSys().FileIsOlderThan(BuildFile,
Gen()->Moc().PredefsFileAbs, &error);
if (!isOlder && !error.empty()) {
LogError(GenT::MOC, error);
return false;
}
}
if (isOlder) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " because it's older than: ";
reason += Quoted(Gen()->Moc().PredefsFileAbs);
Log().Info(GenT::MOC, reason);
}
return true;
}
}
// Test if the source file is newer
{
bool isOlder = false;
{
std::string error;
isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
if (!isOlder && !error.empty()) {
LogError(GenT::MOC, error);
return false;
}
}
if (isOlder) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " because it's older than its source file ";
reason += Quoted(SourceFile);
Log().Info(GenT::MOC, reason);
}
return true;
}
}
// Test if a dependency file is newer
{
// Read dependencies on demand
if (!DependsValid) {
std::string content;
{
std::string error;
if (!FileSys().FileRead(content, SourceFile, &error)) {
std::string emsg = "Could not read file\n ";
emsg += Quoted(SourceFile);
emsg += "\nrequired by moc include ";
emsg += Quoted(IncludeString);
emsg += " in\n ";
emsg += Quoted(IncluderFile);
emsg += ".\n";
emsg += error;
LogError(GenT::MOC, emsg);
return false;
}
}
FindDependencies(content);
}
// Check dependency timestamps
std::string error;
std::string sourceDir = FileSys().SubDirPrefix(SourceFile);
for (std::string const& depFileRel : Depends) {
std::string depFileAbs =
Gen()->Moc().FindIncludedFile(sourceDir, depFileRel);
if (!depFileAbs.empty()) {
if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " from ";
reason += Quoted(SourceFile);
reason += " because it is older than it's dependency file ";
reason += Quoted(depFileAbs);
Log().Info(GenT::MOC, reason);
}
return true;
}
if (!error.empty()) {
LogError(GenT::MOC, error);
return false;
}
} else {
std::string message = "Could not find dependency file ";
message += Quoted(depFileRel);
Log().WarningFile(GenT::MOC, SourceFile, message);
}
}
}
return false;
}
void cmQtAutoMocUic::JobMocT::GenerateMoc()
{
// Make sure the parent directory exists
if (!FileSys().MakeParentDirectory(BuildFile)) {
LogFileError(GenT::MOC, BuildFile, "Could not create parent directory.");
return;
}
{
// Compose moc command
std::vector<std::string> cmd;
cmd.push_back(Gen()->Moc().Executable);
// Add options
cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(),
Gen()->Moc().AllOptions.end());
// Add predefs include
if (!Gen()->Moc().PredefsFileAbs.empty()) {
cmd.emplace_back("--include");
cmd.push_back(Gen()->Moc().PredefsFileAbs);
}
cmd.emplace_back("-o");
cmd.push_back(BuildFile);
cmd.push_back(SourceFile);
// Execute moc command
cmWorkerPool::ProcessResultT result;
if (RunProcess(GenT::MOC, result, cmd)) {
// Moc command success
// Print moc output
if (!result.StdOut.empty()) {
Log().Info(GenT::MOC, result.StdOut);
}
// Notify the generator that a not included file changed (on demand)
if (IncludeString.empty()) {
Gen()->ParallelMocAutoUpdated();
}
} else {
// Moc command failed
{
std::string emsg = "The moc process failed to compile\n ";
emsg += Quoted(SourceFile);
emsg += "\ninto\n ";
emsg += Quoted(BuildFile);
emsg += ".\n";
emsg += result.ErrorMessage;
LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
}
FileSys().FileRemove(BuildFile);
}
}
}
void cmQtAutoMocUic::JobUicT::Process()
{
// Compute build file name
BuildFile = Gen()->Base().AutogenIncludeDir;
BuildFile += '/';
BuildFile += IncludeString;
if (UpdateRequired()) {
GenerateUic();
}
}
bool cmQtAutoMocUic::JobUicT::UpdateRequired()
{
bool const verbose = Log().Verbose();
// Test if the build file exists
if (!FileSys().FileExists(BuildFile)) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " from its source file ";
reason += Quoted(SourceFile);
reason += " because it doesn't exist";
Log().Info(GenT::UIC, reason);
}
return true;
}
// Test if the uic settings changed
if (Gen()->Uic().SettingsChanged) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " from ";
reason += Quoted(SourceFile);
reason += " because the UIC settings changed";
Log().Info(GenT::UIC, reason);
}
return true;
}
// Test if the source file is newer
{
bool isOlder = false;
{
std::string error;
isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
if (!isOlder && !error.empty()) {
LogError(GenT::UIC, error);
return false;
}
}
if (isOlder) {
if (verbose) {
std::string reason = "Generating ";
reason += Quoted(BuildFile);
reason += " because it's older than its source file ";
reason += Quoted(SourceFile);
Log().Info(GenT::UIC, reason);
}
return true;
}
}
return false;
}
void cmQtAutoMocUic::JobUicT::GenerateUic()
{
// Make sure the parent directory exists
if (!FileSys().MakeParentDirectory(BuildFile)) {
LogFileError(GenT::UIC, BuildFile, "Could not create parent directory.");
return;
}
{
// Compose uic command
std::vector<std::string> cmd;
cmd.push_back(Gen()->Uic().Executable);
{
std::vector<std::string> allOpts = Gen()->Uic().TargetOptions;
auto optionIt = Gen()->Uic().Options.find(SourceFile);
if (optionIt != Gen()->Uic().Options.end()) {
UicMergeOptions(allOpts, optionIt->second,
(Gen()->Base().QtVersionMajor == 5));
}
cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
}
cmd.emplace_back("-o");
cmd.emplace_back(BuildFile);
cmd.emplace_back(SourceFile);
cmWorkerPool::ProcessResultT result;
if (RunProcess(GenT::UIC, result, cmd)) {
// Uic command success
// Print uic output
if (!result.StdOut.empty()) {
Log().Info(GenT::UIC, result.StdOut);
}
} else {
// Uic command failed
{
std::string emsg = "The uic process failed to compile\n ";
emsg += Quoted(SourceFile);
emsg += "\ninto\n ";
emsg += Quoted(BuildFile);
emsg += "\nincluded by\n ";
emsg += Quoted(IncluderFile);
emsg += ".\n";
emsg += result.ErrorMessage;
LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
}
FileSys().FileRemove(BuildFile);
if (!cmSystemTools::Touch(compAbs, false)) {
LogFileError(GenT::MOC, compAbs,
"mocs compilation file touching failed.");
}
}
}
@@ -1092,25 +1533,11 @@ void cmQtAutoMocUic::JobFinishT::Process()
Gen()->AbortSuccess();
}
cmQtAutoMocUic::cmQtAutoMocUic()
: Base_(&FileSys())
, Moc_(&FileSys())
{
// Precompile regular expressions
Moc_.RegExpInclude.compile(
"(^|\n)[ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
}
cmQtAutoMocUic::cmQtAutoMocUic() = default;
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;
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.");
}
// include directory
Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
if (Base_.AutogenIncludeDir.empty()) {
Log().ErrorFile(GenT::GEN, InfoFile(),
"Autogen include directory missing");
return false;
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.");
}
// - Files
// - 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) {
for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
Moc_.SkipList.insert(std::move(sfl));
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);
}
Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
MocConst_.SkipList.insert(std::move(sfl));
}
MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
MocConst_.Options = InfoGetList("AM_MOC_OPTIONS");
MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
Moc_.MacroFilters.emplace_back(
MocConst_.MacroFilters.emplace_back(
item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
}
{
auto pushFilter = [this](std::string const& key, std::string const& exp,
std::string& error) {
if (!key.empty()) {
if (!exp.empty()) {
Moc_.DependFilters.emplace_back();
KeyExpT& filter(Moc_.DependFilters.back());
if (filter.Exp.compile(exp)) {
filter.Key = key;
} else {
error = "Regular expression compiling failed";
}
} else {
error = "Regular expression is empty";
}
} else {
error = "Key is empty";
auto addFilter = [this, &LogInfoError](std::string const& key,
std::string const& exp) -> bool {
auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool {
std::ostringstream ferr;
ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n';
ferr << " Key: " << Quoted(key) << '\n';
ferr << " Exp: " << Quoted(exp) << '\n';
return LogInfoError(ferr.str());
};
if (key.empty()) {
return filterErr("Key is empty");
}
if (!error.empty()) {
error = ("AUTOMOC_DEPEND_FILTERS: " + error);
error += "\n";
error += " Key: ";
error += Quoted(key);
error += "\n";
error += " Exp: ";
error += Quoted(exp);
error += "\n";
if (exp.empty()) {
return filterErr("Regular expression is empty");
}
this->MocConst_.DependFilters.emplace_back(key, exp);
if (!this->MocConst_.DependFilters.back().Exp.is_valid()) {
return filterErr("Regular expression compiling failed");
}
return true;
};
std::string error;
// Insert default filter for Q_PLUGIN_METADATA
if (Base().QtVersionMajor != 4) {
pushFilter("Q_PLUGIN_METADATA",
"[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
"[^\\)]*FILE[ \t]*\"([^\"]+)\"",
error);
}
// 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");
if (BaseConst().QtVersionMajor != 4) {
if (!addFilter("Q_PLUGIN_METADATA",
"[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
"[^\\)]*FILE[ \t]*\"([^\"]+)\"")) {
return false;
}
}
if (!error.empty()) {
Log().ErrorFile(GenT::MOC, InfoFile(), error);
return false;
// Insert user defined dependency filters
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;
}
}
}
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) {
for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
Uic_.SkipList.insert(std::move(sfl));
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);
}
Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
UicConst_.SkipList.insert(std::move(sfl));
}
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);
auto makeSource =
[&LogInfoError](std::string const& fileName,
std::string const& fileFlags) -> SourceFileHandleT {
if (fileFlags.size() != 2) {
LogInfoError("Invalid file flags string size");
return SourceFileHandleT();
}
cmFileTime fileTime;
if (!fileTime.Load(fileName)) {
LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) +
" does not exist.");
return SourceFileHandleT();
}
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& 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);
// 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& src : InfoGetList("AM_UIC_SOURCES")) {
addSource(std::move(src), 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));
}
}
}
// Add Fence job
WorkerPool().EmplaceJob<JobFenceT>();
// Add headers
{
auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true);
};
for (std::string& hdr : InfoGetList("AM_HEADERS")) {
addHeader(std::move(hdr), true, true);
}
if (Moc().Enabled) {
for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
addHeader(std::move(hdr), true, false);
}
}
if (Uic().Enabled) {
for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
addHeader(std::move(hdr), false, true);
}
}
}
// Addpost parse fence job
WorkerPool().EmplaceJob<JobPostParseT>();
// Init derived information
// ------------------------
// Init file path checksum generator
FileSys().setupFilePathChecksum(
Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
Base().ProjectBinaryDir);
// Moc variables
if (Moc().Enabled) {
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, ";");
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);
}
str += sep;
SettingsStringUic_ = crypt.HashString(str);
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)
std::string cmQtAutoMocUic::AbsoluteBuildPath(
std::string const& relativePath) const
{
JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle));
// Do additional tests if this is an included moc job
if (!mocJob.IncludeString.empty()) {
std::lock_guard<std::mutex> guard(MocMetaMutex_);
// Register included moc file
MocIncludedFiles_.emplace(mocJob.SourceFile);
// Check if the same moc file would be generated from a different
// source file.
auto const range = MocIncludes_.equal_range(mocJob.IncludeString);
for (auto it = range.first; it != range.second; ++it) {
if (it->second[0] == mocJob.SourceFile) {
// The output file already gets generated
return true;
}
{
// The output file already gets generated - from a different source
// file!
std::string error = "The two source files\n ";
error += Quoted(mocJob.IncluderFile);
error += " and\n ";
error += Quoted(it->second[1]);
error += "\ncontain the same moc include string ";
error += Quoted(mocJob.IncludeString);
error += "\nbut the moc file would be generated from different "
"source files\n ";
error += Quoted(mocJob.SourceFile);
error += " and\n ";
error += Quoted(it->second[0]);
error += ".\nConsider to\n"
"- not include the \"moc_<NAME>.cpp\" file\n"
"- add a directory prefix to a \"<NAME>.moc\" include "
"(e.g \"sub/<NAME>.moc\")\n"
"- rename the source file(s)\n";
Log().Error(GenT::MOC, error);
AbortError();
return false;
}
}
// We're still here so register this job
MocIncludes_.emplace_hint(range.first, mocJob.IncludeString,
std::array<std::string, 2>{
{ mocJob.SourceFile, mocJob.IncluderFile } });
}
return WorkerPool_.PushJob(std::move(jobHandle));
}
bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle)
{
const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
{
std::lock_guard<std::mutex> guard(UicMetaMutex_);
// Check if the same uic file would be generated from a different
// source file.
auto const range = UicIncludes_.equal_range(uicJob.IncludeString);
for (auto it = range.first; it != range.second; ++it) {
if (it->second[0] == uicJob.SourceFile) {
// The output file already gets generated
return true;
}
{
// The output file already gets generated - from a different .ui
// file!
std::string error = "The two source files\n ";
error += Quoted(uicJob.IncluderFile);
error += " and\n ";
error += Quoted(it->second[1]);
error += "\ncontain the same uic include string ";
error += Quoted(uicJob.IncludeString);
error += "\nbut the uic file would be generated from different "
"source files\n ";
error += Quoted(uicJob.SourceFile);
error += " and\n ";
error += Quoted(it->second[0]);
error +=
".\nConsider to\n"
"- add a directory prefix to a \"ui_<NAME>.h\" include "
"(e.g \"sub/ui_<NAME>.h\")\n"
"- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
"include(s)\n";
Log().Error(GenT::UIC, error);
AbortError();
return false;
}
}
// We're still here so register this job
UicIncludes_.emplace_hint(range.first, uicJob.IncludeString,
std::array<std::string, 2>{
{ uicJob.SourceFile, uicJob.IncluderFile } });
}
return WorkerPool_.PushJob(std::move(jobHandle));
}
bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile)
{
std::lock_guard<std::mutex> guard(MocMetaMutex_);
return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
std::string 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;
}
+340 -177
View File
@@ -5,25 +5,28 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmFileTime.h"
#include "cmQtAutoGen.h"
#include "cmQtAutoGenerator.h"
#include "cmWorkerPool.h"
#include "cmsys/RegularExpression.hxx"
#include <array>
#include <atomic>
#include <cstddef>
#include <map>
#include <memory> // IWYU pragma: keep
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
class cmMakefile;
// @brief AUTOMOC and AUTOUIC generator
/** \class cmQtAutoMocUic
* \brief AUTOMOC and AUTOUIC generator
*/
class cmQtAutoMocUic : public cmQtAutoGenerator
{
public:
@@ -35,10 +38,10 @@ public:
public:
// -- Types
typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap;
/// @brief Search key plus regular expression pair
///
/**
* Search key plus regular expression pair
*/
struct KeyExpT
{
KeyExpT() = default;
@@ -59,35 +62,123 @@ public:
cmsys::RegularExpression Exp;
};
/// @brief Common settings
///
/**
* Include string with sub parts
*/
struct IncludeKeyT
{
IncludeKeyT(std::string const& key, std::size_t basePrefixLength);
std::string Key; // Full include string
std::string Dir; // Include directory
std::string Base; // Base part of the include file name
};
/**
* Source file parsing cache
*/
class ParseCacheT
{
public:
// -- Types
/**
* Entry of the file parsing cache
*/
struct FileT
{
void Clear();
struct MocT
{
std::string Macro;
struct IncludeT
{
std::vector<IncludeKeyT> Underscore;
std::vector<IncludeKeyT> Dot;
} Include;
std::vector<std::string> Depends;
} Moc;
struct UicT
{
std::vector<IncludeKeyT> Include;
std::vector<std::string> Depends;
} Uic;
};
typedef std::shared_ptr<FileT> FileHandleT;
typedef std::pair<FileHandleT, bool> GetOrInsertT;
public:
ParseCacheT();
~ParseCacheT();
void Clear();
bool ReadFromFile(std::string const& fileName);
bool WriteToFile(std::string const& fileName);
//! Might return an invalid handle
FileHandleT Get(std::string const& fileName) const;
//! Always returns a valid handle
GetOrInsertT GetOrInsert(std::string const& fileName);
private:
std::unordered_map<std::string, FileHandleT> Map_;
};
/**
* Source file data
*/
class SourceFileT
{
public:
SourceFileT(std::string fileName)
: FileName(std::move(fileName))
{
}
public:
std::string FileName;
cmFileTime FileTime;
ParseCacheT::FileHandleT ParseData;
std::string BuildPath;
bool Moc = false;
bool Uic = false;
};
typedef std::shared_ptr<SourceFileT> SourceFileHandleT;
typedef std::map<std::string, SourceFileHandleT> SourceFileMapT;
/**
* Meta compiler file mapping information
*/
struct MappingT
{
SourceFileHandleT SourceFile;
std::string OutputFile;
std::string IncludeString;
std::vector<SourceFileHandleT> IncluderFiles;
};
typedef std::shared_ptr<MappingT> MappingHandleT;
typedef std::map<std::string, MappingHandleT> MappingMapT;
/**
* Common settings
*/
class BaseSettingsT
{
public:
// -- Volatile methods
BaseSettingsT(FileSystem* fileSystem)
: MultiConfig(false)
, IncludeProjectDirsBefore(false)
, QtVersionMajor(4)
, NumThreads(1)
, FileSys(fileSystem)
{
}
// -- Constructors
BaseSettingsT();
~BaseSettingsT();
BaseSettingsT(BaseSettingsT const&) = delete;
BaseSettingsT& operator=(BaseSettingsT const&) = delete;
// -- Const methods
std::string AbsoluteBuildPath(std::string const& relativePath) const;
bool FindHeader(std::string& header,
std::string const& testBasePath) const;
// -- Attributes
// - Config
bool MultiConfig;
bool IncludeProjectDirsBefore;
unsigned int QtVersionMajor;
unsigned int NumThreads;
bool MultiConfig = false;
bool IncludeProjectDirsBefore = false;
unsigned int QtVersionMajor = 4;
// - Directories
std::string ProjectSourceDir;
std::string ProjectBinaryDir;
@@ -96,37 +187,50 @@ public:
std::string AutogenBuildDir;
std::string AutogenIncludeDir;
// - Files
std::string CMakeExecutable;
cmFileTime CMakeExecutableTime;
std::string ParseCacheFile;
std::vector<std::string> HeaderExtensions;
// - File system
FileSystem* FileSys;
};
/// @brief Moc settings
///
/**
* Shared common variables
*/
class BaseEvalT
{
public:
// -- Parse Cache
bool ParseCacheChanged = false;
cmFileTime ParseCacheTime;
ParseCacheT ParseCache;
// -- Sources
SourceFileMapT Headers;
SourceFileMapT Sources;
};
/**
* Moc settings
*/
class MocSettingsT
{
public:
MocSettingsT(FileSystem* fileSys)
: FileSys(fileSys)
{
}
// -- Constructors
MocSettingsT();
~MocSettingsT();
MocSettingsT(MocSettingsT const&) = delete;
MocSettingsT& operator=(MocSettingsT const&) = delete;
// -- Const methods
bool skipped(std::string const& fileName) const;
std::string FindMacro(std::string const& content) const;
std::string MacrosString() const;
std::string FindIncludedFile(std::string const& sourcePath,
std::string const& includeString) const;
void FindDependencies(std::string const& content,
std::set<std::string>& depends) const;
// -- Attributes
bool Enabled = false;
bool SettingsChanged = false;
bool RelaxedMode = false;
cmFileTime ExecutableTime;
std::string Executable;
std::string CompFileAbs;
std::string PredefsFileRel;
@@ -141,16 +245,35 @@ public:
std::vector<KeyExpT> DependFilters;
std::vector<KeyExpT> MacroFilters;
cmsys::RegularExpression RegExpInclude;
// - File system
FileSystem* FileSys;
};
/// @brief Uic settings
///
/**
* Moc shared variables
*/
class MocEvalT
{
public:
// -- predefines file
cmFileTime PredefsTime;
// -- Mappings
MappingMapT HeaderMappings;
MappingMapT SourceMappings;
MappingMapT Includes;
// -- Discovered files
SourceFileMapT HeadersDiscovered;
// -- Mocs compilation
bool CompUpdated = false;
std::vector<std::string> CompFiles;
};
/**
* Uic settings
*/
class UicSettingsT
{
public:
UicSettingsT() = default;
UicSettingsT();
~UicSettingsT();
UicSettingsT(UicSettingsT const&) = delete;
UicSettingsT& operator=(UicSettingsT const&) = delete;
@@ -161,6 +284,7 @@ public:
// -- Attributes
bool Enabled = false;
bool SettingsChanged = false;
cmFileTime ExecutableTime;
std::string Executable;
std::unordered_set<std::string> SkipList;
std::vector<std::string> TargetOptions;
@@ -169,8 +293,19 @@ public:
cmsys::RegularExpression RegExpInclude;
};
/// @brief Abstract job class for concurrent job processing
///
/**
* Uic shared variables
*/
class UicEvalT
{
public:
SourceFileMapT UiFiles;
MappingMapT Includes;
};
/**
* Abstract job class for concurrent job processing
*/
class JobT : public cmWorkerPool::JobT
{
protected:
@@ -188,10 +323,14 @@ public:
return static_cast<cmQtAutoMocUic*>(UserData());
};
//! Get the file system interface. Only valid during Process() call!
FileSystem& FileSys() { return Gen()->FileSys(); }
//! Get the logger. Only valid during Process() call!
Logger& Log() { return Gen()->Log(); }
// -- Accessors. Only valid during Process() call!
Logger const& Log() const { return Gen()->Log(); }
BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); }
BaseEvalT& BaseEval() const { return Gen()->BaseEval(); }
MocSettingsT const& MocConst() const { return Gen()->MocConst(); }
MocEvalT& MocEval() const { return Gen()->MocEval(); }
UicSettingsT const& UicConst() const { return Gen()->UicConst(); }
UicEvalT& UicEval() const { return Gen()->UicEval(); }
// -- Error logging with automatic abort
void LogError(GenT genType, std::string const& message) const;
@@ -205,11 +344,13 @@ public:
* @brief Run an external process. Use only during Process() call!
*/
bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
std::vector<std::string> const& command);
std::vector<std::string> const& command,
std::string* infoMessage = nullptr);
};
/// @brief Fence job utility class
///
/**
* Fence job utility class
*/
class JobFenceT : public JobT
{
public:
@@ -220,58 +361,143 @@ public:
void Process() override{};
};
/// @brief Generate moc_predefs.h
///
class JobMocPredefsT : public JobT
/**
* Generate moc_predefs.h
*/
class JobMocPredefsT : public JobFenceT
{
private:
void Process() override;
bool Update(std::string* reason) const;
};
/// @brief Parses a source file
///
/**
* File parse job base class
*/
class JobParseT : public JobT
{
public:
JobParseT(std::string fileName, bool moc, bool uic, bool header = false)
: FileName(std::move(fileName))
, AutoMoc(moc)
, AutoUic(uic)
, Header(header)
JobParseT(SourceFileHandleT fileHandle)
: FileHandle(std::move(fileHandle))
{
}
private:
struct MetaT
{
std::string Content;
std::string FileDir;
std::string FileBase;
};
protected:
bool ReadFile();
void CreateKeys(std::vector<IncludeKeyT>& container,
std::set<std::string> const& source,
std::size_t basePrefixLength);
void MocMacro();
void MocDependecies();
void MocIncludes();
void UicIncludes();
void Process() override;
bool ParseMocSource(MetaT const& meta);
bool ParseMocHeader(MetaT const& meta);
std::string MocStringHeaders(std::string const& fileBase) const;
std::string MocFindIncludedHeader(std::string const& includerDir,
std::string const& includeBase);
bool ParseUic(MetaT const& meta);
bool ParseUicInclude(MetaT const& meta, std::string&& includeString);
std::string UicFindIncludedFile(MetaT const& meta,
std::string const& includeString);
private:
std::string FileName;
bool AutoMoc = false;
bool AutoUic = false;
bool Header = false;
protected:
SourceFileHandleT FileHandle;
std::string Content;
};
/// @brief Generates additional jobs after all files have been parsed
///
class JobPostParseT : public JobFenceT
/**
* Header file parse job
*/
class JobParseHeaderT : public JobParseT
{
private:
public:
using JobParseT::JobParseT;
void Process() override;
};
/**
* Source file parse job
*/
class JobParseSourceT : public JobParseT
{
public:
using JobParseT::JobParseT;
void Process() override;
};
/**
* Evaluate parsed files
*/
class JobEvaluateT : public JobFenceT
{
void Process() override;
// -- Moc
bool MocEvalHeader(SourceFileHandleT source);
bool MocEvalSource(SourceFileHandleT const& source);
SourceFileHandleT MocFindIncludedHeader(
std::string const& includerDir, std::string const& includeBase) const;
SourceFileHandleT MocFindHeader(std::string const& basePath) const;
std::string MocMessageTestHeaders(std::string const& fileBase) const;
bool MocRegisterIncluded(std::string const& includeString,
SourceFileHandleT includerFileHandle,
SourceFileHandleT sourceFileHandle,
bool sourceIsHeader) const;
void MocRegisterMapping(MappingHandleT mappingHandle,
bool sourceIsHeader) const;
// -- Uic
bool UicEval(SourceFileMapT const& fileMap);
bool UicEvalFile(SourceFileHandleT sourceFileHandle);
SourceFileHandleT UicFindIncludedUi(std::string const& sourceFile,
std::string const& sourceDir,
IncludeKeyT const& incKey) const;
bool UicRegisterMapping(std::string const& includeString,
SourceFileHandleT uiFileHandle,
SourceFileHandleT includerFileHandle);
};
/**
* Generates moc/uic jobs
*/
class JobGenerateT : public JobFenceT
{
void Process() override;
// -- Moc
bool MocGenerate(MappingHandleT const& mapping, bool compFile) const;
bool MocUpdate(MappingT const& mapping, std::string* reason) const;
std::pair<std::string, cmFileTime> MocFindDependency(
std::string const& sourceDir, std::string const& includeString) const;
// -- Uic
bool UicGenerate(MappingHandleT const& mapping) const;
bool UicUpdate(MappingT const& mapping, std::string* reason) const;
};
/**
* File compiling base job
*/
class JobCompileT : public JobT
{
public:
JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason)
: Mapping(std::move(uicMapping))
, Reason(std::move(reason))
{
}
protected:
MappingHandleT Mapping;
std::unique_ptr<std::string> Reason;
};
/**
* moc compiles a file
*/
class JobMocT : public JobCompileT
{
public:
using JobCompileT::JobCompileT;
void Process() override;
};
/**
* uic compiles a file
*/
class JobUicT : public JobCompileT
{
public:
using JobCompileT::JobCompileT;
void Process() override;
};
@@ -283,60 +509,6 @@ public:
void Process() override;
};
/// @brief Moc a file job
///
class JobMocT : public JobT
{
public:
JobMocT(std::string sourceFile, std::string includerFile,
std::string includeString)
: SourceFile(std::move(sourceFile))
, IncluderFile(std::move(includerFile))
, IncludeString(std::move(includeString))
{
}
void FindDependencies(std::string const& content);
private:
void Process() override;
bool UpdateRequired();
void GenerateMoc();
public:
std::string SourceFile;
std::string IncluderFile;
std::string IncludeString;
std::string BuildFile;
bool DependsValid = false;
std::set<std::string> Depends;
};
/// @brief Uic a file job
///
class JobUicT : public JobT
{
public:
JobUicT(std::string sourceFile, std::string includerFile,
std::string includeString)
: SourceFile(std::move(sourceFile))
, IncluderFile(std::move(includerFile))
, IncludeString(std::move(includeString))
{
}
private:
void Process() override;
bool UpdateRequired();
void GenerateUic();
public:
std::string SourceFile;
std::string IncluderFile;
std::string IncludeString;
std::string BuildFile;
};
/// @brief The last job
///
class JobFinishT : public JobFenceT
@@ -346,39 +518,37 @@ public:
};
// -- Const settings interface
const BaseSettingsT& Base() const { return this->Base_; }
const MocSettingsT& Moc() const { return this->Moc_; }
const UicSettingsT& Uic() const { return this->Uic_; }
BaseSettingsT const& BaseConst() const { return this->BaseConst_; }
BaseEvalT& BaseEval() { return this->BaseEval_; }
MocSettingsT const& MocConst() const { return this->MocConst_; }
MocEvalT& MocEval() { return this->MocEval_; }
UicSettingsT const& UicConst() const { return this->UicConst_; }
UicEvalT& UicEval() { return this->UicEval_; }
// -- Parallel job processing interface
cmWorkerPool& WorkerPool() { return WorkerPool_; }
void AbortError() { Abort(true); }
void AbortSuccess() { Abort(false); }
bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle);
bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle);
// -- Mocs compilation include file updated flag
void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); }
bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); }
// -- Mocs compilation file register
std::string ParallelMocAutoRegister(std::string const& baseName);
bool ParallelMocIncluded(std::string const& sourceFile);
std::set<std::string> const& MocAutoFiles() const
{
return this->MocAutoFiles_;
}
// -- Utility
std::string AbsoluteBuildPath(std::string const& relativePath) const;
std::string AbsoluteIncludePath(std::string const& relativePath) const;
template <class JOBTYPE>
void CreateParseJobs(SourceFileMapT const& sourceMap);
private:
// -- Utility accessors
Logger& Log() { return Logger_; }
FileSystem& FileSys() { return FileSys_; }
Logger const& Log() const { return Logger_; }
// -- Abstract processing interface
bool Init(cmMakefile* makefile) override;
void InitJobs();
bool Process() override;
// -- Settings file
void SettingsFileRead();
bool SettingsFileWrite();
// -- Parse cache
void ParseCacheRead();
bool ParseCacheWrite();
// -- Thread processing
void Abort(bool error);
// -- Generation
@@ -387,25 +557,18 @@ private:
private:
// -- Utility
Logger Logger_;
FileSystem FileSys_;
// -- Settings
BaseSettingsT Base_;
MocSettingsT Moc_;
UicSettingsT Uic_;
// -- Moc meta
std::mutex MocMetaMutex_;
std::set<std::string> MocIncludedFiles_;
IncludesMap MocIncludes_;
std::set<std::string> MocAutoFiles_;
std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false);
// -- Uic meta
std::mutex UicMetaMutex_;
IncludesMap UicIncludes_;
BaseSettingsT BaseConst_;
BaseEvalT BaseEval_;
MocSettingsT MocConst_;
MocEvalT MocEval_;
UicSettingsT UicConst_;
UicEvalT UicEval_;
// -- Settings file
std::string SettingsFile_;
std::string SettingsStringMoc_;
std::string SettingsStringUic_;
// -- Thread pool and job queue
// -- Worker thread pool
std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
cmWorkerPool WorkerPool_;
};
+1 -1
View File
@@ -57,7 +57,7 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile)
}
// - Configurations
Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG");
// - Directories
+1 -1
View File
@@ -26,7 +26,7 @@ public:
private:
// -- Utility
Logger& Log() { return Logger_; }
Logger const& Log() const { return Logger_; }
bool IsMultiConfig() const { return MultiConfig_; }
std::string MultiConfigOutput() const;