mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-30 02:59:22 -05:00
e7a760fe7d
If by some chance the moc executable does not exist while running AUTOMOC, instead of showing an error, the CMake Autogen invocation hangs indefinitely. This happens because UVProcessFinished() is not called if the process does not launch correctly. Make sure to call UVProcessFinished() even if the process launch fails, and also report the error returned by libuv.
2025 lines
61 KiB
C++
2025 lines
61 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmQtAutoGeneratorMocUic.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmCryptoHash.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmQtAutoGen.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmake.h"
|
|
|
|
#if defined(__APPLE__)
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
// -- Class methods
|
|
|
|
std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
|
|
std::string const& relativePath) const
|
|
{
|
|
return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
|
|
}
|
|
|
|
/**
|
|
* @brief Tries to find the header file to the given file base path by
|
|
* appending different header extensions
|
|
* @return True on success
|
|
*/
|
|
bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader(
|
|
std::string& header, std::string const& testBasePath) const
|
|
{
|
|
for (std::string const& ext : HeaderExtensions) {
|
|
std::string testFilePath(testBasePath);
|
|
testFilePath.push_back('.');
|
|
testFilePath += ext;
|
|
if (FileSys->FileExists(testFilePath)) {
|
|
header = testFilePath;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
|
|
std::string const& fileName) const
|
|
{
|
|
return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the first relevant Qt macro name found in the given C++ code
|
|
* @return The name of the Qt macro or an empty string
|
|
*/
|
|
std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro(
|
|
std::string const& content) const
|
|
{
|
|
for (KeyExpT const& filter : MacroFilters) {
|
|
// Run a simple find string operation before the expensive
|
|
// regular expression check
|
|
if (content.find(filter.Key) != std::string::npos) {
|
|
cmsys::RegularExpressionMatch match;
|
|
if (filter.Exp.find(content.c_str(), match)) {
|
|
// Return macro name on demand
|
|
return filter.Key;
|
|
}
|
|
}
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const
|
|
{
|
|
std::string res;
|
|
const auto itB = MacroFilters.cbegin();
|
|
const auto itE = MacroFilters.cend();
|
|
const auto itL = itE - 1;
|
|
auto itC = itB;
|
|
for (; itC != itE; ++itC) {
|
|
// Separator
|
|
if (itC != itB) {
|
|
if (itC != itL) {
|
|
res += ", ";
|
|
} else {
|
|
res += " or ";
|
|
}
|
|
}
|
|
// Key
|
|
res += itC->Key;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile(
|
|
std::string const& sourcePath, std::string const& includeString) const
|
|
{
|
|
// Search in vicinity of the source
|
|
{
|
|
std::string testPath = sourcePath;
|
|
testPath += includeString;
|
|
if (FileSys->FileExists(testPath)) {
|
|
return FileSys->GetRealPath(testPath);
|
|
}
|
|
}
|
|
// Search in include directories
|
|
for (std::string const& path : IncludePaths) {
|
|
std::string fullPath = path;
|
|
fullPath.push_back('/');
|
|
fullPath += includeString;
|
|
if (FileSys->FileExists(fullPath)) {
|
|
return FileSys->GetRealPath(fullPath);
|
|
}
|
|
}
|
|
// Return empty string
|
|
return std::string();
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies(
|
|
std::string const& content, std::set<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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped(
|
|
std::string const& fileName) const
|
|
{
|
|
return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk)
|
|
{
|
|
if (AutoMoc && Header) {
|
|
// Don't parse header for moc if the file is included by a source already
|
|
if (wrk.Gen().ParallelMocIncluded(FileName)) {
|
|
AutoMoc = false;
|
|
}
|
|
}
|
|
|
|
if (AutoMoc || AutoUic) {
|
|
std::string error;
|
|
MetaT meta;
|
|
if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) {
|
|
if (!meta.Content.empty()) {
|
|
meta.FileDir = wrk.FileSys().SubDirPrefix(FileName);
|
|
meta.FileBase =
|
|
wrk.FileSys().GetFilenameWithoutLastExtension(FileName);
|
|
|
|
bool success = true;
|
|
if (AutoMoc) {
|
|
if (Header) {
|
|
success = ParseMocHeader(wrk, meta);
|
|
} else {
|
|
success = ParseMocSource(wrk, meta);
|
|
}
|
|
}
|
|
if (AutoUic && success) {
|
|
ParseUic(wrk, meta);
|
|
}
|
|
} else {
|
|
wrk.LogFileWarning(GenT::GEN, FileName, "The source file is empty");
|
|
}
|
|
} else {
|
|
wrk.LogFileError(GenT::GEN, FileName,
|
|
"Could not read the file: " + error);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk,
|
|
MetaT const& meta)
|
|
{
|
|
struct JobPre
|
|
{
|
|
bool self; // source file is self
|
|
bool underscore; // "moc_" style include
|
|
std::string SourceFile;
|
|
std::string IncludeString;
|
|
};
|
|
|
|
struct MocInclude
|
|
{
|
|
std::string Inc; // full include string
|
|
std::string Dir; // include string directory
|
|
std::string Base; // include string file base
|
|
};
|
|
|
|
// Check if this source file contains a relevant macro
|
|
std::string const ownMacro = wrk.Moc().FindMacro(meta.Content);
|
|
|
|
// Extract moc includes from file
|
|
std::deque<MocInclude> mocIncsUsc;
|
|
std::deque<MocInclude> mocIncsDot;
|
|
{
|
|
if (meta.Content.find("moc") != std::string::npos) {
|
|
const char* contentChars = meta.Content.c_str();
|
|
cmsys::RegularExpressionMatch match;
|
|
while (wrk.Moc().RegExpInclude.find(contentChars, match)) {
|
|
std::string incString = match.match(2);
|
|
std::string incDir(wrk.FileSys().SubDirPrefix(incString));
|
|
std::string incBase =
|
|
wrk.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();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if there is anything to do
|
|
if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
|
|
return true;
|
|
}
|
|
|
|
bool ownDotMocIncluded = false;
|
|
bool ownMocUscIncluded = false;
|
|
std::deque<JobPre> jobs;
|
|
|
|
// Process moc_<BASE>.cxx includes
|
|
for (const MocInclude& mocInc : mocIncsUsc) {
|
|
std::string const header =
|
|
MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
|
|
if (!header.empty()) {
|
|
// Check if header is skipped
|
|
if (wrk.Moc().skipped(header)) {
|
|
continue;
|
|
}
|
|
// Register moc job
|
|
const bool ownMoc = (mocInc.Base == meta.FileBase);
|
|
jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc });
|
|
// Store meta information for relaxed mode
|
|
if (ownMoc) {
|
|
ownMocUscIncluded = true;
|
|
}
|
|
} else {
|
|
{
|
|
std::string emsg = "The file includes the moc file ";
|
|
emsg += Quoted(mocInc.Inc);
|
|
emsg += ", but the header ";
|
|
emsg += Quoted(MocStringHeaders(wrk, mocInc.Base));
|
|
emsg += " could not be found.";
|
|
wrk.LogFileError(GenT::MOC, FileName, emsg);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Process <BASE>.moc includes
|
|
for (const MocInclude& mocInc : mocIncsDot) {
|
|
const bool ownMoc = (mocInc.Base == meta.FileBase);
|
|
if (wrk.Moc().RelaxedMode) {
|
|
// Relaxed mode
|
|
if (!ownMacro.empty() && ownMoc) {
|
|
// Add self
|
|
jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
|
|
ownDotMocIncluded = true;
|
|
} else {
|
|
// In relaxed mode try to find a header instead but issue a warning.
|
|
// This is for KDE4 compatibility
|
|
std::string const header =
|
|
MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
|
|
if (!header.empty()) {
|
|
// Check if header is skipped
|
|
if (wrk.Moc().skipped(header)) {
|
|
continue;
|
|
}
|
|
// Register moc job
|
|
jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
|
|
if (ownMacro.empty()) {
|
|
if (ownMoc) {
|
|
std::string emsg = "The file includes the moc file ";
|
|
emsg += Quoted(mocInc.Inc);
|
|
emsg += ", but does not contain a ";
|
|
emsg += wrk.Moc().MacrosString();
|
|
emsg += " macro.\nRunning moc on\n ";
|
|
emsg += Quoted(header);
|
|
emsg += "!\nBetter include ";
|
|
emsg += Quoted("moc_" + mocInc.Base + ".cpp");
|
|
emsg += " for a compatibility with strict mode.\n"
|
|
"(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
|
|
wrk.LogFileWarning(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";
|
|
wrk.LogFileWarning(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(wrk, mocInc.Base));
|
|
emsg += " could not be found.";
|
|
wrk.LogFileError(GenT::MOC, FileName, emsg);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
// Strict mode
|
|
if (ownMoc) {
|
|
// Include self
|
|
jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
|
|
ownDotMocIncluded = true;
|
|
// Accept but issue a warning if moc isn't required
|
|
if (ownMacro.empty()) {
|
|
std::string emsg = "The file includes the moc file ";
|
|
emsg += Quoted(mocInc.Inc);
|
|
emsg += ", but does not contain a ";
|
|
emsg += wrk.Moc().MacrosString();
|
|
emsg += " macro.";
|
|
wrk.LogFileWarning(GenT::MOC, FileName, emsg);
|
|
}
|
|
} else {
|
|
// Don't allow <BASE>.moc include other than self in strict mode
|
|
{
|
|
std::string emsg = "The file includes the moc file ";
|
|
emsg += Quoted(mocInc.Inc);
|
|
emsg += ", which seems to be the moc file from a different "
|
|
"source file.\nThis is not supported. Include ";
|
|
emsg += Quoted(meta.FileBase + ".moc");
|
|
emsg += " to run moc on this source file.";
|
|
wrk.LogFileError(GenT::MOC, FileName, emsg);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ownMacro.empty() && !ownDotMocIncluded) {
|
|
// In this case, check whether the scanned file itself contains a
|
|
// Q_OBJECT.
|
|
// If this is the case, the moc_foo.cpp should probably be generated from
|
|
// foo.cpp instead of foo.h, because otherwise it won't build.
|
|
// But warn, since this is not how it is supposed to be used.
|
|
// This is for KDE4 compatibility.
|
|
if (wrk.Moc().RelaxedMode && ownMocUscIncluded) {
|
|
JobPre uscJobPre;
|
|
// Remove underscore job request
|
|
{
|
|
auto itC = jobs.begin();
|
|
auto itE = jobs.end();
|
|
for (; itC != itE; ++itC) {
|
|
JobPre& job(*itC);
|
|
if (job.self && job.underscore) {
|
|
uscJobPre = std::move(job);
|
|
jobs.erase(itC);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Issue a warning
|
|
{
|
|
std::string emsg = "The file contains a ";
|
|
emsg += ownMacro;
|
|
emsg += " macro, but does not include ";
|
|
emsg += Quoted(meta.FileBase + ".moc");
|
|
emsg += ". Instead it includes ";
|
|
emsg += Quoted(uscJobPre.IncludeString);
|
|
emsg += ".\nRunning moc on\n ";
|
|
emsg += Quoted(FileName);
|
|
emsg += "!\nBetter include ";
|
|
emsg += Quoted(meta.FileBase + ".moc");
|
|
emsg += " for compatibility with strict mode.\n"
|
|
"(CMAKE_AUTOMOC_RELAXED_MODE warning)";
|
|
wrk.LogFileWarning(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";
|
|
wrk.LogFileError(GenT::MOC, FileName, emsg);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Convert pre jobs to actual jobs
|
|
for (JobPre& jobPre : jobs) {
|
|
JobHandleT jobHandle = cm::make_unique<JobMocT>(
|
|
std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
|
|
if (jobPre.self) {
|
|
// Read dependencies from this source
|
|
static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
|
|
}
|
|
if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk,
|
|
MetaT const& meta)
|
|
{
|
|
bool success = true;
|
|
std::string const macroName = wrk.Moc().FindMacro(meta.Content);
|
|
if (!macroName.empty()) {
|
|
JobHandleT jobHandle = cm::make_unique<JobMocT>(
|
|
std::string(FileName), std::string(), std::string());
|
|
// Read dependencies from this source
|
|
static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
|
|
success = wrk.Gen().ParallelJobPushMoc(jobHandle);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders(
|
|
WorkerT& wrk, std::string const& fileBase) const
|
|
{
|
|
std::string res = fileBase;
|
|
res += ".{";
|
|
res += cmJoin(wrk.Base().HeaderExtensions, ",");
|
|
res += "}";
|
|
return res;
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader(
|
|
WorkerT& wrk, std::string const& includerDir, std::string const& includeBase)
|
|
{
|
|
std::string header;
|
|
// Search in vicinity of the source
|
|
if (!wrk.Base().FindHeader(header, includerDir + includeBase)) {
|
|
// Search in include directories
|
|
for (std::string const& path : wrk.Moc().IncludePaths) {
|
|
std::string fullPath = path;
|
|
fullPath.push_back('/');
|
|
fullPath += includeBase;
|
|
if (wrk.Base().FindHeader(header, fullPath)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Sanitize
|
|
if (!header.empty()) {
|
|
header = wrk.FileSys().GetRealPath(header);
|
|
}
|
|
return header;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk,
|
|
MetaT const& meta)
|
|
{
|
|
bool success = true;
|
|
if (meta.Content.find("ui_") != std::string::npos) {
|
|
const char* contentChars = meta.Content.c_str();
|
|
cmsys::RegularExpressionMatch match;
|
|
while (wrk.Uic().RegExpInclude.find(contentChars, match)) {
|
|
if (!ParseUicInclude(wrk, meta, match.match(2))) {
|
|
success = false;
|
|
break;
|
|
}
|
|
contentChars += match.end();
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude(
|
|
WorkerT& wrk, MetaT const& meta, std::string&& includeString)
|
|
{
|
|
bool success = false;
|
|
std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString);
|
|
if (!uiInputFile.empty()) {
|
|
if (!wrk.Uic().skipped(uiInputFile)) {
|
|
JobHandleT jobHandle = cm::make_unique<JobUicT>(
|
|
std::move(uiInputFile), FileName, std::move(includeString));
|
|
success = wrk.Gen().ParallelJobPushUic(jobHandle);
|
|
} else {
|
|
// A skipped file is successful
|
|
success = true;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile(
|
|
WorkerT& wrk, MetaT const& meta, std::string const& includeString)
|
|
{
|
|
std::string res;
|
|
std::string searchFile =
|
|
wrk.FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
|
|
searchFile += ".ui";
|
|
// Collect search paths list
|
|
std::deque<std::string> testFiles;
|
|
{
|
|
std::string const searchPath = wrk.FileSys().SubDirPrefix(includeString);
|
|
|
|
std::string searchFileFull;
|
|
if (!searchPath.empty()) {
|
|
searchFileFull = searchPath;
|
|
searchFileFull += searchFile;
|
|
}
|
|
// Vicinity of the source
|
|
{
|
|
std::string const sourcePath = meta.FileDir;
|
|
testFiles.push_back(sourcePath + searchFile);
|
|
if (!searchPath.empty()) {
|
|
testFiles.push_back(sourcePath + searchFileFull);
|
|
}
|
|
}
|
|
// AUTOUIC search paths
|
|
if (!wrk.Uic().SearchPaths.empty()) {
|
|
for (std::string const& sPath : wrk.Uic().SearchPaths) {
|
|
testFiles.push_back((sPath + "/").append(searchFile));
|
|
}
|
|
if (!searchPath.empty()) {
|
|
for (std::string const& sPath : wrk.Uic().SearchPaths) {
|
|
testFiles.push_back((sPath + "/").append(searchFileFull));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Search for the .ui file!
|
|
for (std::string const& testFile : testFiles) {
|
|
if (wrk.FileSys().FileExists(testFile)) {
|
|
res = wrk.FileSys().GetRealPath(testFile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Log error
|
|
if (res.empty()) {
|
|
std::string emsg = "Could not find ";
|
|
emsg += Quoted(searchFile);
|
|
emsg += " in\n";
|
|
for (std::string const& testFile : testFiles) {
|
|
emsg += " ";
|
|
emsg += Quoted(testFile);
|
|
emsg += "\n";
|
|
}
|
|
wrk.LogFileError(GenT::UIC, FileName, emsg);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk)
|
|
{
|
|
// (Re)generate moc_predefs.h on demand
|
|
bool generate(false);
|
|
bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs));
|
|
if (!fileExists) {
|
|
if (wrk.Log().Verbose()) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(wrk.Moc().PredefsFileRel);
|
|
reason += " because it doesn't exist";
|
|
wrk.LogInfo(GenT::MOC, reason);
|
|
}
|
|
generate = true;
|
|
} else if (wrk.Moc().SettingsChanged) {
|
|
if (wrk.Log().Verbose()) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(wrk.Moc().PredefsFileRel);
|
|
reason += " because the settings changed.";
|
|
wrk.LogInfo(GenT::MOC, reason);
|
|
}
|
|
generate = true;
|
|
}
|
|
if (generate) {
|
|
ProcessResultT result;
|
|
{
|
|
// Compose command
|
|
std::vector<std::string> cmd = wrk.Moc().PredefsCmd;
|
|
// Add includes
|
|
cmd.insert(cmd.end(), wrk.Moc().Includes.begin(),
|
|
wrk.Moc().Includes.end());
|
|
// Add definitions
|
|
for (std::string const& def : wrk.Moc().Definitions) {
|
|
cmd.push_back("-D" + def);
|
|
}
|
|
// Execute command
|
|
if (!wrk.RunProcess(GenT::MOC, result, cmd)) {
|
|
std::string emsg = "The content generation command for ";
|
|
emsg += Quoted(wrk.Moc().PredefsFileRel);
|
|
emsg += " failed.\n";
|
|
emsg += result.ErrorMessage;
|
|
wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
|
|
}
|
|
}
|
|
|
|
// (Re)write predefs file only on demand
|
|
if (!result.error()) {
|
|
if (!fileExists ||
|
|
wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
|
|
if (wrk.FileSys().FileWrite(GenT::MOC, wrk.Moc().PredefsFileAbs,
|
|
result.StdOut)) {
|
|
// Success
|
|
} else {
|
|
std::string emsg = "Writing ";
|
|
emsg += Quoted(wrk.Moc().PredefsFileRel);
|
|
emsg += " failed.";
|
|
wrk.LogFileError(GenT::MOC, wrk.Moc().PredefsFileAbs, emsg);
|
|
}
|
|
} else {
|
|
// Touch to update the time stamp
|
|
if (wrk.Log().Verbose()) {
|
|
std::string msg = "Touching ";
|
|
msg += Quoted(wrk.Moc().PredefsFileRel);
|
|
msg += ".";
|
|
wrk.LogInfo(GenT::MOC, msg);
|
|
}
|
|
wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies(
|
|
WorkerT& wrk, std::string const& content)
|
|
{
|
|
wrk.Moc().FindDependencies(content, Depends);
|
|
DependsValid = true;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk)
|
|
{
|
|
// Compute build file name
|
|
if (!IncludeString.empty()) {
|
|
BuildFile = wrk.Base().AutogenIncludeDir;
|
|
BuildFile += '/';
|
|
BuildFile += IncludeString;
|
|
} else {
|
|
// Relative build path
|
|
std::string relPath = wrk.FileSys().GetFilePathChecksum(SourceFile);
|
|
relPath += "/moc_";
|
|
relPath += wrk.FileSys().GetFilenameWithoutLastExtension(SourceFile);
|
|
|
|
// Register relative file path with duplication check
|
|
relPath = wrk.Gen().ParallelMocAutoRegister(relPath);
|
|
|
|
// Absolute build path
|
|
if (wrk.Base().MultiConfig) {
|
|
BuildFile = wrk.Base().AutogenIncludeDir;
|
|
BuildFile += '/';
|
|
BuildFile += relPath;
|
|
} else {
|
|
BuildFile = wrk.Base().AbsoluteBuildPath(relPath);
|
|
}
|
|
}
|
|
|
|
if (UpdateRequired(wrk)) {
|
|
GenerateMoc(wrk);
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk)
|
|
{
|
|
bool const verbose = wrk.Gen().Log().Verbose();
|
|
|
|
// Test if the build file exists
|
|
if (!wrk.FileSys().FileExists(BuildFile)) {
|
|
if (verbose) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(BuildFile);
|
|
reason += " from its source file ";
|
|
reason += Quoted(SourceFile);
|
|
reason += " because it doesn't exist";
|
|
wrk.LogInfo(GenT::MOC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Test if any setting changed
|
|
if (wrk.Moc().SettingsChanged) {
|
|
if (verbose) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(BuildFile);
|
|
reason += " from ";
|
|
reason += Quoted(SourceFile);
|
|
reason += " because the MOC settings changed";
|
|
wrk.LogInfo(GenT::MOC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Test if the moc_predefs file is newer
|
|
if (!wrk.Moc().PredefsFileAbs.empty()) {
|
|
bool isOlder = false;
|
|
{
|
|
std::string error;
|
|
isOlder = wrk.FileSys().FileIsOlderThan(
|
|
BuildFile, wrk.Moc().PredefsFileAbs, &error);
|
|
if (!isOlder && !error.empty()) {
|
|
wrk.LogError(GenT::MOC, error);
|
|
return false;
|
|
}
|
|
}
|
|
if (isOlder) {
|
|
if (verbose) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(BuildFile);
|
|
reason += " because it's older than: ";
|
|
reason += Quoted(wrk.Moc().PredefsFileAbs);
|
|
wrk.LogInfo(GenT::MOC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Test if the source file is newer
|
|
{
|
|
bool isOlder = false;
|
|
{
|
|
std::string error;
|
|
isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
|
|
if (!isOlder && !error.empty()) {
|
|
wrk.LogError(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);
|
|
wrk.LogInfo(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 (!wrk.FileSys().FileRead(content, SourceFile, &error)) {
|
|
std::string emsg = "Could not read file\n ";
|
|
emsg += Quoted(SourceFile);
|
|
emsg += "\nrequired by moc include ";
|
|
emsg += Quoted(IncludeString);
|
|
emsg += " in\n ";
|
|
emsg += Quoted(IncluderFile);
|
|
emsg += ".\n";
|
|
emsg += error;
|
|
wrk.LogError(GenT::MOC, emsg);
|
|
return false;
|
|
}
|
|
}
|
|
FindDependencies(wrk, content);
|
|
}
|
|
// Check dependency timestamps
|
|
std::string error;
|
|
std::string sourceDir = wrk.FileSys().SubDirPrefix(SourceFile);
|
|
for (std::string const& depFileRel : Depends) {
|
|
std::string depFileAbs =
|
|
wrk.Moc().FindIncludedFile(sourceDir, depFileRel);
|
|
if (!depFileAbs.empty()) {
|
|
if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
|
|
if (verbose) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(BuildFile);
|
|
reason += " from ";
|
|
reason += Quoted(SourceFile);
|
|
reason += " because it is older than it's dependency file ";
|
|
reason += Quoted(depFileAbs);
|
|
wrk.LogInfo(GenT::MOC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
if (!error.empty()) {
|
|
wrk.LogError(GenT::MOC, error);
|
|
return false;
|
|
}
|
|
} else {
|
|
std::string message = "Could not find dependency file ";
|
|
message += Quoted(depFileRel);
|
|
wrk.LogFileWarning(GenT::MOC, SourceFile, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
|
|
{
|
|
// Make sure the parent directory exists
|
|
if (wrk.FileSys().MakeParentDirectory(GenT::MOC, BuildFile)) {
|
|
// Compose moc command
|
|
std::vector<std::string> cmd;
|
|
cmd.push_back(wrk.Moc().Executable);
|
|
// Add options
|
|
cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(),
|
|
wrk.Moc().AllOptions.end());
|
|
// Add predefs include
|
|
if (!wrk.Moc().PredefsFileAbs.empty()) {
|
|
cmd.emplace_back("--include");
|
|
cmd.push_back(wrk.Moc().PredefsFileAbs);
|
|
}
|
|
cmd.emplace_back("-o");
|
|
cmd.push_back(BuildFile);
|
|
cmd.push_back(SourceFile);
|
|
|
|
// Execute moc command
|
|
ProcessResultT result;
|
|
if (wrk.RunProcess(GenT::MOC, result, cmd)) {
|
|
// Moc command success
|
|
// Print moc output
|
|
if (!result.StdOut.empty()) {
|
|
wrk.LogInfo(GenT::MOC, result.StdOut);
|
|
}
|
|
// Notify the generator that a not included file changed (on demand)
|
|
if (IncludeString.empty()) {
|
|
wrk.Gen().ParallelMocAutoUpdated();
|
|
}
|
|
} else {
|
|
// Moc command failed
|
|
{
|
|
std::string emsg = "The moc process failed to compile\n ";
|
|
emsg += Quoted(SourceFile);
|
|
emsg += "\ninto\n ";
|
|
emsg += Quoted(BuildFile);
|
|
emsg += ".\n";
|
|
emsg += result.ErrorMessage;
|
|
wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
|
|
}
|
|
wrk.FileSys().FileRemove(BuildFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk)
|
|
{
|
|
// Compute build file name
|
|
BuildFile = wrk.Base().AutogenIncludeDir;
|
|
BuildFile += '/';
|
|
BuildFile += IncludeString;
|
|
|
|
if (UpdateRequired(wrk)) {
|
|
GenerateUic(wrk);
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk)
|
|
{
|
|
bool const verbose = wrk.Gen().Log().Verbose();
|
|
|
|
// Test if the build file exists
|
|
if (!wrk.FileSys().FileExists(BuildFile)) {
|
|
if (verbose) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(BuildFile);
|
|
reason += " from its source file ";
|
|
reason += Quoted(SourceFile);
|
|
reason += " because it doesn't exist";
|
|
wrk.LogInfo(GenT::UIC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Test if the uic settings changed
|
|
if (wrk.Uic().SettingsChanged) {
|
|
if (verbose) {
|
|
std::string reason = "Generating ";
|
|
reason += Quoted(BuildFile);
|
|
reason += " from ";
|
|
reason += Quoted(SourceFile);
|
|
reason += " because the UIC settings changed";
|
|
wrk.LogInfo(GenT::UIC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Test if the source file is newer
|
|
{
|
|
bool isOlder = false;
|
|
{
|
|
std::string error;
|
|
isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
|
|
if (!isOlder && !error.empty()) {
|
|
wrk.LogError(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);
|
|
wrk.LogInfo(GenT::UIC, reason);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
|
|
{
|
|
// Make sure the parent directory exists
|
|
if (wrk.FileSys().MakeParentDirectory(GenT::UIC, BuildFile)) {
|
|
// Compose uic command
|
|
std::vector<std::string> cmd;
|
|
cmd.push_back(wrk.Uic().Executable);
|
|
{
|
|
std::vector<std::string> allOpts = wrk.Uic().TargetOptions;
|
|
auto optionIt = wrk.Uic().Options.find(SourceFile);
|
|
if (optionIt != wrk.Uic().Options.end()) {
|
|
UicMergeOptions(allOpts, optionIt->second,
|
|
(wrk.Base().QtVersionMajor == 5));
|
|
}
|
|
cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
|
|
}
|
|
cmd.emplace_back("-o");
|
|
cmd.push_back(BuildFile);
|
|
cmd.push_back(SourceFile);
|
|
|
|
ProcessResultT result;
|
|
if (wrk.RunProcess(GenT::UIC, result, cmd)) {
|
|
// Uic command success
|
|
// Print uic output
|
|
if (!result.StdOut.empty()) {
|
|
wrk.LogInfo(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;
|
|
wrk.LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
|
|
}
|
|
wrk.FileSys().FileRemove(BuildFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen,
|
|
uv_loop_t* uvLoop)
|
|
: Gen_(gen)
|
|
{
|
|
// Initialize uv asynchronous callback for process starting
|
|
ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this);
|
|
// Start thread
|
|
Thread_ = std::thread(&WorkerT::Loop, this);
|
|
}
|
|
|
|
cmQtAutoGeneratorMocUic::WorkerT::~WorkerT()
|
|
{
|
|
// Join thread
|
|
if (Thread_.joinable()) {
|
|
Thread_.join();
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::LogInfo(
|
|
GenT genType, std::string const& message) const
|
|
{
|
|
Log().Info(genType, message);
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::LogWarning(
|
|
GenT genType, std::string const& message) const
|
|
{
|
|
Log().Warning(genType, message);
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning(
|
|
GenT genType, std::string const& filename, std::string const& message) const
|
|
{
|
|
Log().WarningFile(genType, filename, message);
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::LogError(
|
|
GenT genType, std::string const& message) const
|
|
{
|
|
Gen().ParallelRegisterJobError();
|
|
Log().Error(genType, message);
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::LogFileError(
|
|
GenT genType, std::string const& filename, std::string const& message) const
|
|
{
|
|
Gen().ParallelRegisterJobError();
|
|
Log().ErrorFile(genType, filename, message);
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError(
|
|
GenT genType, std::string const& message,
|
|
std::vector<std::string> const& command, std::string const& output) const
|
|
{
|
|
Gen().ParallelRegisterJobError();
|
|
Log().ErrorCommand(genType, message, command, output);
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess(
|
|
GenT genType, ProcessResultT& result,
|
|
std::vector<std::string> const& command)
|
|
{
|
|
if (command.empty()) {
|
|
return false;
|
|
}
|
|
|
|
// Create process instance
|
|
{
|
|
std::lock_guard<std::mutex> lock(ProcessMutex_);
|
|
Process_ = cm::make_unique<ReadOnlyProcessT>();
|
|
Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir);
|
|
}
|
|
|
|
// Send asynchronous process start request to libuv loop
|
|
ProcessRequest_.send();
|
|
|
|
// Log command
|
|
if (this->Log().Verbose()) {
|
|
std::string msg = "Running command:\n";
|
|
msg += QuotedCommand(command);
|
|
msg += '\n';
|
|
this->LogInfo(genType, msg);
|
|
}
|
|
|
|
// Wait until the process has been finished and destroyed
|
|
{
|
|
std::unique_lock<std::mutex> ulock(ProcessMutex_);
|
|
while (Process_) {
|
|
ProcessCondition_.wait(ulock);
|
|
}
|
|
}
|
|
return !result.error();
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::Loop()
|
|
{
|
|
while (true) {
|
|
Gen().WorkerSwapJob(JobHandle_);
|
|
if (JobHandle_) {
|
|
JobHandle_->Process(*this);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle)
|
|
{
|
|
auto& wrk = *reinterpret_cast<WorkerT*>(handle->data);
|
|
{
|
|
std::lock_guard<std::mutex> lock(wrk.ProcessMutex_);
|
|
if (wrk.Process_ && !wrk.Process_->IsStarted()) {
|
|
wrk.Process_->start(handle->loop, [&wrk] { wrk.UVProcessFinished(); });
|
|
}
|
|
}
|
|
|
|
if (!wrk.Process_->IsStarted()) {
|
|
wrk.UVProcessFinished();
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished()
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(ProcessMutex_);
|
|
if (Process_ && (Process_->IsFinished() || !Process_->IsStarted())) {
|
|
Process_.reset();
|
|
}
|
|
}
|
|
// Notify idling thread
|
|
ProcessCondition_.notify_one();
|
|
}
|
|
|
|
cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
|
|
: Base_(&FileSys())
|
|
, Moc_(&FileSys())
|
|
{
|
|
// Precompile regular expressions
|
|
Moc_.RegExpInclude.compile(
|
|
"(^|\n)[ \t]*#[ \t]*include[ \t]+"
|
|
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
|
|
Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
|
|
"[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
|
|
|
|
// Initialize libuv asynchronous iteration request
|
|
UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
|
|
}
|
|
|
|
cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() = default;
|
|
|
|
bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
|
|
{
|
|
// -- Meta
|
|
Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
|
|
|
|
// Utility lambdas
|
|
auto InfoGet = [makefile](const char* key) {
|
|
return makefile->GetSafeDefinition(key);
|
|
};
|
|
auto InfoGetBool = [makefile](const char* key) {
|
|
return makefile->IsOn(key);
|
|
};
|
|
auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
|
|
std::vector<std::string> list;
|
|
cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
|
|
return list;
|
|
};
|
|
auto InfoGetLists =
|
|
[makefile](const char* key) -> std::vector<std::vector<std::string>> {
|
|
std::vector<std::vector<std::string>> lists;
|
|
{
|
|
std::string const value = makefile->GetSafeDefinition(key);
|
|
std::string::size_type pos = 0;
|
|
while (pos < value.size()) {
|
|
std::string::size_type next = value.find(ListSep, pos);
|
|
std::string::size_type length =
|
|
(next != std::string::npos) ? next - pos : value.size() - pos;
|
|
// Remove enclosing braces
|
|
if (length >= 2) {
|
|
std::string::const_iterator itBeg = value.begin() + (pos + 1);
|
|
std::string::const_iterator itEnd = itBeg + (length - 2);
|
|
{
|
|
std::string subValue(itBeg, itEnd);
|
|
std::vector<std::string> list;
|
|
cmSystemTools::ExpandListArgument(subValue, list);
|
|
lists.push_back(std::move(list));
|
|
}
|
|
}
|
|
pos += length;
|
|
pos += ListSep.size();
|
|
}
|
|
}
|
|
return lists;
|
|
};
|
|
auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
|
|
const char* valueConf = nullptr;
|
|
{
|
|
std::string keyConf = key;
|
|
keyConf += '_';
|
|
keyConf += InfoConfig();
|
|
valueConf = makefile->GetDefinition(keyConf);
|
|
}
|
|
if (valueConf == nullptr) {
|
|
return makefile->GetSafeDefinition(key);
|
|
}
|
|
return std::string(valueConf);
|
|
};
|
|
auto InfoGetConfigList =
|
|
[&InfoGetConfig](const char* key) -> std::vector<std::string> {
|
|
std::vector<std::string> list;
|
|
cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
|
|
return list;
|
|
};
|
|
|
|
// -- Read info file
|
|
if (!makefile->ReadListFile(InfoFile())) {
|
|
Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
|
|
return false;
|
|
}
|
|
|
|
// -- Meta
|
|
Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
|
|
Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
|
|
{
|
|
unsigned long num = Base_.NumThreads;
|
|
if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
|
|
num = std::max<unsigned long>(num, 1);
|
|
num = std::min<unsigned long>(num, ParallelMax);
|
|
Base_.NumThreads = static_cast<unsigned int>(num);
|
|
}
|
|
}
|
|
|
|
// - Files and directories
|
|
Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
|
|
Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
|
|
Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
|
|
Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
|
|
Base_.IncludeProjectDirsBefore =
|
|
InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
|
|
Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
|
|
if (Base_.AutogenBuildDir.empty()) {
|
|
Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
|
|
return false;
|
|
}
|
|
// include directory
|
|
Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
|
|
if (Base_.AutogenIncludeDir.empty()) {
|
|
Log().ErrorFile(GenT::GEN, InfoFile(),
|
|
"Autogen include directory missing");
|
|
return false;
|
|
}
|
|
|
|
// - Files
|
|
SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
|
|
if (SettingsFile_.empty()) {
|
|
Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
|
|
return false;
|
|
}
|
|
|
|
// - Qt environment
|
|
{
|
|
unsigned long qtv = Base_.QtVersionMajor;
|
|
if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
|
|
&qtv)) {
|
|
Base_.QtVersionMajor = static_cast<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));
|
|
}
|
|
Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
|
|
Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
|
|
Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
|
|
Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
|
|
for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
|
|
Moc_.MacroFilters.emplace_back(
|
|
item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
|
|
}
|
|
{
|
|
auto pushFilter = [this](std::string const& key, std::string const& exp,
|
|
std::string& error) {
|
|
if (!key.empty()) {
|
|
if (!exp.empty()) {
|
|
Moc_.DependFilters.emplace_back();
|
|
KeyExpT& filter(Moc_.DependFilters.back());
|
|
if (filter.Exp.compile(exp)) {
|
|
filter.Key = key;
|
|
} else {
|
|
error = "Regular expression compiling failed";
|
|
}
|
|
} else {
|
|
error = "Regular expression is empty";
|
|
}
|
|
} else {
|
|
error = "Key is empty";
|
|
}
|
|
if (!error.empty()) {
|
|
error = ("AUTOMOC_DEPEND_FILTERS: " + error);
|
|
error += "\n";
|
|
error += " Key: ";
|
|
error += Quoted(key);
|
|
error += "\n";
|
|
error += " Exp: ";
|
|
error += Quoted(exp);
|
|
error += "\n";
|
|
}
|
|
};
|
|
|
|
std::string error;
|
|
// Insert default filter for Q_PLUGIN_METADATA
|
|
if (Base().QtVersionMajor != 4) {
|
|
pushFilter("Q_PLUGIN_METADATA",
|
|
"[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
|
|
"[^\\)]*FILE[ \t]*\"([^\"]+)\"",
|
|
error);
|
|
}
|
|
// Insert user defined dependency filters
|
|
{
|
|
std::vector<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");
|
|
return false;
|
|
}
|
|
}
|
|
if (!error.empty()) {
|
|
Log().ErrorFile(GenT::MOC, InfoFile(), error);
|
|
return false;
|
|
}
|
|
}
|
|
Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
|
|
// Install moc predefs job
|
|
if (!Moc().PredefsCmd.empty()) {
|
|
JobQueues_.MocPredefs.emplace_back(cm::make_unique<JobMocPredefsT>());
|
|
}
|
|
}
|
|
|
|
// - Uic
|
|
Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
|
|
Uic_.Enabled = !Uic().Executable.empty();
|
|
if (Uic().Enabled) {
|
|
for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
|
|
Uic_.SkipList.insert(std::move(sfl));
|
|
}
|
|
Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
|
|
Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
|
|
{
|
|
auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
|
|
auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
|
|
// Compare list sizes
|
|
if (sources.size() != options.size()) {
|
|
std::ostringstream ost;
|
|
ost << "files/options lists sizes mismatch (" << sources.size() << "/"
|
|
<< options.size() << ")";
|
|
Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
|
|
return false;
|
|
}
|
|
auto fitEnd = sources.cend();
|
|
auto fit = sources.begin();
|
|
auto oit = options.begin();
|
|
while (fit != fitEnd) {
|
|
Uic_.Options[*fit] = std::move(*oit);
|
|
++fit;
|
|
++oit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// - Headers and sources
|
|
{
|
|
auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
|
|
this->JobQueues_.Headers.emplace_back(
|
|
cm::make_unique<JobParseT>(std::move(hdr), moc, uic, true));
|
|
};
|
|
auto addSource = [this](std::string&& src, bool moc, bool uic) {
|
|
this->JobQueues_.Sources.emplace_back(
|
|
cm::make_unique<JobParseT>(std::move(src), moc, uic, false));
|
|
};
|
|
|
|
// Add headers
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Add sources
|
|
for (std::string& src : InfoGetList("AM_SOURCES")) {
|
|
addSource(std::move(src), true, true);
|
|
}
|
|
if (Moc().Enabled) {
|
|
for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
|
|
addSource(std::move(src), true, false);
|
|
}
|
|
}
|
|
if (Uic().Enabled) {
|
|
for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
|
|
addSource(std::move(src), false, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Init derived information
|
|
// ------------------------
|
|
|
|
// Init file path checksum generator
|
|
FileSys().setupFilePathChecksum(
|
|
Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
|
|
Base().ProjectBinaryDir);
|
|
|
|
// Moc variables
|
|
if (Moc().Enabled) {
|
|
// Mocs compilation file
|
|
Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
|
|
|
|
// Moc predefs file
|
|
if (!Moc_.PredefsCmd.empty()) {
|
|
Moc_.PredefsFileRel = "moc_predefs";
|
|
if (Base_.MultiConfig) {
|
|
Moc_.PredefsFileRel += '_';
|
|
Moc_.PredefsFileRel += InfoConfig();
|
|
}
|
|
Moc_.PredefsFileRel += ".h";
|
|
Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
|
|
}
|
|
|
|
// Sort include directories on demand
|
|
if (Base().IncludeProjectDirsBefore) {
|
|
// Move strings to temporary list
|
|
std::list<std::string> includes;
|
|
includes.insert(includes.end(), Moc().IncludePaths.begin(),
|
|
Moc().IncludePaths.end());
|
|
Moc_.IncludePaths.clear();
|
|
Moc_.IncludePaths.reserve(includes.size());
|
|
// Append project directories only
|
|
{
|
|
std::array<std::string const*, 2> const movePaths = {
|
|
{ &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
|
|
};
|
|
for (std::string const* ppath : movePaths) {
|
|
std::list<std::string>::iterator it = includes.begin();
|
|
while (it != includes.end()) {
|
|
std::string const& path = *it;
|
|
if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
|
|
Moc_.IncludePaths.push_back(path);
|
|
it = includes.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Append remaining directories
|
|
Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
|
|
includes.end());
|
|
}
|
|
// Compose moc includes list
|
|
{
|
|
std::set<std::string> frameworkPaths;
|
|
for (std::string const& path : Moc().IncludePaths) {
|
|
Moc_.Includes.push_back("-I" + path);
|
|
// Extract framework path
|
|
if (cmHasLiteralSuffix(path, ".framework/Headers")) {
|
|
// Go up twice to get to the framework root
|
|
std::vector<std::string> pathComponents;
|
|
FileSys().SplitPath(path, pathComponents);
|
|
std::string frameworkPath = FileSys().JoinPath(
|
|
pathComponents.begin(), pathComponents.end() - 2);
|
|
frameworkPaths.insert(frameworkPath);
|
|
}
|
|
}
|
|
// Append framework includes
|
|
for (std::string const& path : frameworkPaths) {
|
|
Moc_.Includes.emplace_back("-F");
|
|
Moc_.Includes.push_back(path);
|
|
}
|
|
}
|
|
// Setup single list with all options
|
|
{
|
|
// Add includes
|
|
Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
|
|
Moc().Includes.end());
|
|
// Add definitions
|
|
for (std::string const& def : Moc().Definitions) {
|
|
Moc_.AllOptions.push_back("-D" + def);
|
|
}
|
|
// Add options
|
|
Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
|
|
Moc().Options.end());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::Process()
|
|
{
|
|
// Run libuv event loop
|
|
UVRequest().send();
|
|
if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
|
|
if (JobError_) {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle)
|
|
{
|
|
reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage();
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::PollStage()
|
|
{
|
|
switch (Stage_) {
|
|
case StageT::SETTINGS_READ:
|
|
SettingsFileRead();
|
|
SetStage(StageT::CREATE_DIRECTORIES);
|
|
break;
|
|
case StageT::CREATE_DIRECTORIES:
|
|
CreateDirectories();
|
|
SetStage(StageT::PARSE_SOURCES);
|
|
break;
|
|
case StageT::PARSE_SOURCES:
|
|
if (ThreadsStartJobs(JobQueues_.Sources)) {
|
|
SetStage(StageT::PARSE_HEADERS);
|
|
}
|
|
break;
|
|
case StageT::PARSE_HEADERS:
|
|
if (ThreadsStartJobs(JobQueues_.Headers)) {
|
|
SetStage(StageT::MOC_PREDEFS);
|
|
}
|
|
break;
|
|
case StageT::MOC_PREDEFS:
|
|
if (ThreadsStartJobs(JobQueues_.MocPredefs)) {
|
|
SetStage(StageT::MOC_PROCESS);
|
|
}
|
|
break;
|
|
case StageT::MOC_PROCESS:
|
|
if (ThreadsStartJobs(JobQueues_.Moc)) {
|
|
SetStage(StageT::MOCS_COMPILATION);
|
|
}
|
|
break;
|
|
case StageT::MOCS_COMPILATION:
|
|
if (ThreadsJobsDone()) {
|
|
MocGenerateCompilation();
|
|
SetStage(StageT::UIC_PROCESS);
|
|
}
|
|
break;
|
|
case StageT::UIC_PROCESS:
|
|
if (ThreadsStartJobs(JobQueues_.Uic)) {
|
|
SetStage(StageT::SETTINGS_WRITE);
|
|
}
|
|
break;
|
|
case StageT::SETTINGS_WRITE:
|
|
SettingsFileWrite();
|
|
SetStage(StageT::FINISH);
|
|
break;
|
|
case StageT::FINISH:
|
|
if (ThreadsJobsDone()) {
|
|
// Clear all libuv handles
|
|
ThreadsStop();
|
|
UVRequest().reset();
|
|
// Set highest END stage manually
|
|
Stage_ = StageT::END;
|
|
}
|
|
break;
|
|
case StageT::END:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::SetStage(StageT stage)
|
|
{
|
|
if (JobError_) {
|
|
stage = StageT::FINISH;
|
|
}
|
|
// Only allow to increase the stage
|
|
if (Stage_ < stage) {
|
|
Stage_ = stage;
|
|
UVRequest().send();
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::SettingsFileRead()
|
|
{
|
|
// Compose current settings strings
|
|
{
|
|
cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
|
|
std::string const sep(" ~~~ ");
|
|
if (Moc_.Enabled) {
|
|
std::string str;
|
|
str += Moc().Executable;
|
|
str += sep;
|
|
str += cmJoin(Moc().AllOptions, ";");
|
|
str += sep;
|
|
str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
|
|
str += sep;
|
|
str += cmJoin(Moc().PredefsCmd, ";");
|
|
str += sep;
|
|
SettingsStringMoc_ = crypt.HashString(str);
|
|
}
|
|
if (Uic().Enabled) {
|
|
std::string str;
|
|
str += Uic().Executable;
|
|
str += sep;
|
|
str += cmJoin(Uic().TargetOptions, ";");
|
|
for (const auto& item : Uic().Options) {
|
|
str += sep;
|
|
str += item.first;
|
|
str += sep;
|
|
str += cmJoin(item.second, ";");
|
|
}
|
|
str += sep;
|
|
SettingsStringUic_ = crypt.HashString(str);
|
|
}
|
|
}
|
|
|
|
// Read old settings and compare
|
|
{
|
|
std::string content;
|
|
if (FileSys().FileRead(content, SettingsFile_)) {
|
|
if (Moc().Enabled) {
|
|
if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
|
|
Moc_.SettingsChanged = true;
|
|
}
|
|
}
|
|
if (Uic().Enabled) {
|
|
if (SettingsStringUic_ != SettingsFind(content, "uic")) {
|
|
Uic_.SettingsChanged = true;
|
|
}
|
|
}
|
|
// In case any setting changed remove the old settings file.
|
|
// This triggers a full rebuild on the next run if the current
|
|
// build is aborted before writing the current settings in the end.
|
|
if (Moc().SettingsChanged || Uic().SettingsChanged) {
|
|
FileSys().FileRemove(SettingsFile_);
|
|
}
|
|
} else {
|
|
// Settings file read failed
|
|
if (Moc().Enabled) {
|
|
Moc_.SettingsChanged = true;
|
|
}
|
|
if (Uic().Enabled) {
|
|
Uic_.SettingsChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::SettingsFileWrite()
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
// Only write if any setting changed
|
|
if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) {
|
|
if (Log().Verbose()) {
|
|
Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
|
|
}
|
|
// Compose settings file content
|
|
std::string content;
|
|
{
|
|
auto SettingAppend = [&content](const char* key,
|
|
std::string const& value) {
|
|
if (!value.empty()) {
|
|
content += key;
|
|
content += ':';
|
|
content += value;
|
|
content += '\n';
|
|
}
|
|
};
|
|
SettingAppend("moc", SettingsStringMoc_);
|
|
SettingAppend("uic", SettingsStringUic_);
|
|
}
|
|
// Write settings file
|
|
if (!FileSys().FileWrite(GenT::GEN, SettingsFile_, content)) {
|
|
Log().ErrorFile(GenT::GEN, SettingsFile_,
|
|
"Settings file writing failed");
|
|
// Remove old settings file to trigger a full rebuild on the next run
|
|
FileSys().FileRemove(SettingsFile_);
|
|
RegisterJobError();
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::CreateDirectories()
|
|
{
|
|
// Create AUTOGEN include directory
|
|
if (!FileSys().MakeDirectory(GenT::GEN, Base().AutogenIncludeDir)) {
|
|
RegisterJobError();
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
|
|
{
|
|
bool done = false;
|
|
std::size_t queueSize = queue.size();
|
|
|
|
// Change the active queue
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
// Check if there are still unfinished jobs from the previous queue
|
|
if (JobsRemain_ == 0) {
|
|
if (!JobThreadsAbort_) {
|
|
JobQueue_.swap(queue);
|
|
JobsRemain_ = queueSize;
|
|
} else {
|
|
// Abort requested
|
|
queue.clear();
|
|
queueSize = 0;
|
|
}
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
if (done && (queueSize != 0)) {
|
|
// Start new threads on demand
|
|
if (Workers_.empty()) {
|
|
Workers_.resize(Base().NumThreads);
|
|
for (auto& item : Workers_) {
|
|
item = cm::make_unique<WorkerT>(this, UVLoop());
|
|
}
|
|
} else {
|
|
// Notify threads
|
|
if (queueSize == 1) {
|
|
JobsConditionRead_.notify_one();
|
|
} else {
|
|
JobsConditionRead_.notify_all();
|
|
}
|
|
}
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::ThreadsStop()
|
|
{
|
|
if (!Workers_.empty()) {
|
|
// Clear all jobs
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
JobThreadsAbort_ = true;
|
|
JobsRemain_ -= JobQueue_.size();
|
|
JobQueue_.clear();
|
|
|
|
JobQueues_.Sources.clear();
|
|
JobQueues_.Headers.clear();
|
|
JobQueues_.MocPredefs.clear();
|
|
JobQueues_.Moc.clear();
|
|
JobQueues_.Uic.clear();
|
|
}
|
|
// Wake threads
|
|
JobsConditionRead_.notify_all();
|
|
// Join and clear threads
|
|
Workers_.clear();
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
return (JobsRemain_ == 0);
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
|
|
{
|
|
bool const jobProcessed(jobHandle);
|
|
if (jobProcessed) {
|
|
jobHandle.reset();
|
|
}
|
|
{
|
|
std::unique_lock<std::mutex> jobsLock(JobsMutex_);
|
|
// Reduce the remaining job count and notify the libuv loop
|
|
// when all jobs are done
|
|
if (jobProcessed) {
|
|
--JobsRemain_;
|
|
if (JobsRemain_ == 0) {
|
|
UVRequest().send();
|
|
}
|
|
}
|
|
// Wait for new jobs
|
|
while (!JobThreadsAbort_ && JobQueue_.empty()) {
|
|
JobsConditionRead_.wait(jobsLock);
|
|
}
|
|
// Try to pick up a new job handle
|
|
if (!JobThreadsAbort_ && !JobQueue_.empty()) {
|
|
jobHandle = std::move(JobQueue_.front());
|
|
JobQueue_.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
RegisterJobError();
|
|
}
|
|
|
|
// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
|
|
// locked
|
|
void cmQtAutoGeneratorMocUic::RegisterJobError()
|
|
{
|
|
JobError_ = true;
|
|
if (!JobThreadsAbort_) {
|
|
JobThreadsAbort_ = true;
|
|
// Clear remaining jobs
|
|
if (JobsRemain_ != 0) {
|
|
JobsRemain_ -= JobQueue_.size();
|
|
JobQueue_.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle)
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
if (!JobThreadsAbort_) {
|
|
bool pushJobHandle = true;
|
|
// Do additional tests if this is an included moc job
|
|
const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle));
|
|
if (!mocJob.IncludeString.empty()) {
|
|
// Register included moc file and look for collisions
|
|
MocIncludedFiles_.emplace(mocJob.SourceFile);
|
|
if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) {
|
|
// Another source file includes the same moc file!
|
|
for (const JobHandleT& otherHandle : JobQueues_.Moc) {
|
|
const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle));
|
|
if (otherJob.IncludeString == mocJob.IncludeString) {
|
|
// Check if the same moc file would be generated from different
|
|
// source files which is an error.
|
|
if (otherJob.SourceFile != mocJob.SourceFile) {
|
|
// Include string collision
|
|
std::string error = "The two source files\n ";
|
|
error += Quoted(mocJob.IncluderFile);
|
|
error += " and\n ";
|
|
error += Quoted(otherJob.IncluderFile);
|
|
error += "\ncontain the same moc include string ";
|
|
error += Quoted(mocJob.IncludeString);
|
|
error += "\nbut the moc file would be generated from different "
|
|
"source files\n ";
|
|
error += Quoted(mocJob.SourceFile);
|
|
error += " and\n ";
|
|
error += Quoted(otherJob.SourceFile);
|
|
error += ".\nConsider to\n"
|
|
"- not include the \"moc_<NAME>.cpp\" file\n"
|
|
"- add a directory prefix to a \"<NAME>.moc\" include "
|
|
"(e.g \"sub/<NAME>.moc\")\n"
|
|
"- rename the source file(s)\n";
|
|
Log().Error(GenT::MOC, error);
|
|
RegisterJobError();
|
|
}
|
|
// Do not push this job in since the included moc file already
|
|
// gets generated by an other job.
|
|
pushJobHandle = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Push job on demand
|
|
if (pushJobHandle) {
|
|
JobQueues_.Moc.emplace_back(std::move(jobHandle));
|
|
}
|
|
}
|
|
return !JobError_;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle)
|
|
{
|
|
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
|
if (!JobThreadsAbort_) {
|
|
bool pushJobHandle = true;
|
|
// Look for include collisions.
|
|
const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
|
|
for (const JobHandleT& otherHandle : JobQueues_.Uic) {
|
|
const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle));
|
|
if (otherJob.IncludeString == uicJob.IncludeString) {
|
|
// Check if the same uic file would be generated from different
|
|
// source files which would be an error.
|
|
if (otherJob.SourceFile != uicJob.SourceFile) {
|
|
// Include string collision
|
|
std::string error = "The two source files\n ";
|
|
error += Quoted(uicJob.IncluderFile);
|
|
error += " and\n ";
|
|
error += Quoted(otherJob.IncluderFile);
|
|
error += "\ncontain the same uic include string ";
|
|
error += Quoted(uicJob.IncludeString);
|
|
error += "\nbut the uic file would be generated from different "
|
|
"source files\n ";
|
|
error += Quoted(uicJob.SourceFile);
|
|
error += " and\n ";
|
|
error += Quoted(otherJob.SourceFile);
|
|
error +=
|
|
".\nConsider to\n"
|
|
"- add a directory prefix to a \"ui_<NAME>.h\" include "
|
|
"(e.g \"sub/ui_<NAME>.h\")\n"
|
|
"- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
|
|
"include(s)\n";
|
|
Log().Error(GenT::UIC, error);
|
|
RegisterJobError();
|
|
}
|
|
// Do not push this job in since the uic file already
|
|
// gets generated by an other job.
|
|
pushJobHandle = false;
|
|
break;
|
|
}
|
|
}
|
|
if (pushJobHandle) {
|
|
JobQueues_.Uic.emplace_back(std::move(jobHandle));
|
|
}
|
|
}
|
|
return !JobError_;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
|
|
std::string const& sourceFile)
|
|
{
|
|
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
|
return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
|
|
std::string const& baseName)
|
|
{
|
|
std::string res;
|
|
{
|
|
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
|
res = baseName;
|
|
res += ".cpp";
|
|
if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
|
|
MocAutoFiles_.emplace(res);
|
|
} else {
|
|
// Append number suffix to the file name
|
|
for (unsigned int ii = 2; ii != 1024; ++ii) {
|
|
res = baseName;
|
|
res += '_';
|
|
res += std::to_string(ii);
|
|
res += ".cpp";
|
|
if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
|
|
MocAutoFiles_.emplace(res);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
|
|
{
|
|
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
|
MocAutoFileUpdated_ = true;
|
|
}
|
|
|
|
void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
|
|
{
|
|
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
|
if (!JobError_ && Moc().Enabled) {
|
|
// Write mocs compilation build file
|
|
{
|
|
// Compose mocs compilation file content
|
|
std::string content =
|
|
"// This file is autogenerated. Changes will be overwritten.\n";
|
|
if (MocAutoFiles_.empty()) {
|
|
// Placeholder content
|
|
content += "// No files found that require moc or the moc files are "
|
|
"included\n";
|
|
content += "enum some_compilers { need_more_than_nothing };\n";
|
|
} else {
|
|
// Valid content
|
|
char const sbeg = Base().MultiConfig ? '<' : '"';
|
|
char const send = Base().MultiConfig ? '>' : '"';
|
|
for (std::string const& mocfile : MocAutoFiles_) {
|
|
content += "#include ";
|
|
content += sbeg;
|
|
content += mocfile;
|
|
content += send;
|
|
content += '\n';
|
|
}
|
|
}
|
|
|
|
std::string const& compAbs = Moc().CompFileAbs;
|
|
if (FileSys().FileDiffers(compAbs, content)) {
|
|
// Actually write mocs compilation file
|
|
if (Log().Verbose()) {
|
|
Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
|
|
}
|
|
if (!FileSys().FileWrite(GenT::MOC, compAbs, content)) {
|
|
Log().ErrorFile(GenT::MOC, compAbs,
|
|
"mocs compilation file writing failed");
|
|
RegisterJobError();
|
|
return;
|
|
}
|
|
} else if (MocAutoFileUpdated_) {
|
|
// Only touch mocs compilation file
|
|
if (Log().Verbose()) {
|
|
Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
|
|
}
|
|
FileSys().Touch(compAbs);
|
|
}
|
|
}
|
|
// Write mocs compilation wrapper file
|
|
if (Base().MultiConfig) {
|
|
}
|
|
}
|
|
}
|