summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Holtermann <sebholt@xwmw.org>2018-01-03 15:59:40 (GMT)
committerSebastian Holtermann <sebholt@xwmw.org>2018-01-17 16:23:49 (GMT)
commita008578deebfa71b38786281450e3d9cf84f5847 (patch)
tree70173006b0adc6a62626e59d9cc653826f950336
parent488baaf0d6144cd7cedfbbd3bb6eadcc72257fc4 (diff)
downloadCMake-a008578deebfa71b38786281450e3d9cf84f5847.zip
CMake-a008578deebfa71b38786281450e3d9cf84f5847.tar.gz
CMake-a008578deebfa71b38786281450e3d9cf84f5847.tar.bz2
Autogen: Process files concurrently in AUTOMOC and AUTOUIC
This introduces concurrent thread processing in the `_autogen` target wich processes AUTOMOC and AUTOUIC. Source file parsing is distributed among the threads by using a job queue from which the threads pull new parse jobs. Each thread might start an independent ``moc`` or ``uic`` process. Altogether this roughly speeds up the AUTOMOC and AUTOUIC build process by the number of physical CPUs on the host system. The exact number of threads to start in the `_autogen` target is controlled by the new AUTOGEN_PARALLEL target property which is initialized by the new CMAKE_AUTOGEN_PARALLEL variable. If AUTOGEN_PARALLEL is empty or unset (which is the default) the thread count is set to the number of physical CPUs on the host system. The AUTOMOC/AUTOUIC generator and the AUTORCC generator are refactored to use a libuv loop internally. Closes #17422.
-rw-r--r--Modules/AutoRccInfo.cmake.in4
-rw-r--r--Modules/AutogenInfo.cmake.in1
-rw-r--r--Source/cmQtAutoGen.cxx314
-rw-r--r--Source/cmQtAutoGen.h55
-rw-r--r--Source/cmQtAutoGenerator.cxx669
-rw-r--r--Source/cmQtAutoGenerator.h308
-rw-r--r--Source/cmQtAutoGeneratorInitializer.cxx177
-rw-r--r--Source/cmQtAutoGeneratorInitializer.h11
-rw-r--r--Source/cmQtAutoGeneratorMocUic.cxx3029
-rw-r--r--Source/cmQtAutoGeneratorMocUic.h510
-rw-r--r--Source/cmQtAutoGeneratorRcc.cxx719
-rw-r--r--Source/cmQtAutoGeneratorRcc.h97
-rw-r--r--Source/cmTarget.cxx1
13 files changed, 3620 insertions, 2275 deletions
diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in
index 5457a6f..cbab4a7 100644
--- a/Modules/AutoRccInfo.cmake.in
+++ b/Modules/AutoRccInfo.cmake.in
@@ -1,10 +1,6 @@
# Meta
set(ARCC_MULTI_CONFIG @_multi_config@)
# Directories and files
-set(ARCC_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/")
-set(ARCC_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/")
-set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/")
-set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/")
set(ARCC_BUILD_DIR @_build_dir@)
# Qt environment
set(ARCC_RCC_EXECUTABLE @_qt_rcc_executable@)
diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in
index 9a4a06d..7320c0a 100644
--- a/Modules/AutogenInfo.cmake.in
+++ b/Modules/AutogenInfo.cmake.in
@@ -1,5 +1,6 @@
# Meta
set(AM_MULTI_CONFIG @_multi_config@)
+set(AM_PARALLEL @_parallel@)
# Directories and files
set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/")
set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/")
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 255a532..18ecbe7 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -2,16 +2,13 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmQtAutoGen.h"
#include "cmAlgorithms.h"
-#include "cmProcessOutput.h"
#include "cmSystemTools.h"
-#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#include <algorithm>
#include <iterator>
#include <sstream>
-#include <stddef.h>
// - Static variables
@@ -21,8 +18,8 @@ std::string const genNameUic = "AutoUic";
std::string const genNameRcc = "AutoRcc";
std::string const mcNameSingle = "SINGLE";
-std::string const mcNameWrap = "WRAP";
-std::string const mcNameFull = "FULL";
+std::string const mcNameWrapper = "WRAPPER";
+std::string const mcNameMulti = "MULTI";
// - Static functions
@@ -80,203 +77,53 @@ void MergeOptions(std::vector<std::string>& baseOpts,
baseOpts.insert(baseOpts.end(), extraOpts.begin(), extraOpts.end());
}
-/// @brief Reads the resource files list from from a .qrc file - Qt4 version
-/// @return True if the .qrc file was successfully parsed
-static bool RccListInputsQt4(std::string const& fileName,
- std::vector<std::string>& files,
- std::string* errorMessage)
-{
- bool allGood = true;
- // Read qrc file content into string
- std::string qrcContents;
- {
- cmsys::ifstream ifs(fileName.c_str());
- if (ifs) {
- std::ostringstream osst;
- osst << ifs.rdbuf();
- qrcContents = osst.str();
- } else {
- if (errorMessage != nullptr) {
- std::string& err = *errorMessage;
- err = "rcc file not readable:\n ";
- err += cmQtAutoGen::Quoted(fileName);
- err += "\n";
- }
- allGood = false;
- }
- }
- if (allGood) {
- // qrc file directory
- std::string qrcDir(cmSystemTools::GetFilenamePath(fileName));
- if (!qrcDir.empty()) {
- qrcDir += '/';
- }
-
- cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
- cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
-
- size_t offset = 0;
- while (fileMatchRegex.find(qrcContents.c_str() + offset)) {
- std::string qrcEntry = fileMatchRegex.match(1);
- offset += qrcEntry.size();
- {
- fileReplaceRegex.find(qrcEntry);
- std::string tag = fileReplaceRegex.match(1);
- qrcEntry = qrcEntry.substr(tag.size());
- }
- if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) {
- qrcEntry = qrcDir + qrcEntry;
- }
- files.push_back(qrcEntry);
- }
- }
- return allGood;
-}
-
-/// @brief Reads the resource files list from from a .qrc file - Qt5 version
-/// @return True if the .qrc file was successfully parsed
-static bool RccListInputsQt5(std::string const& rccCommand,
- std::vector<std::string> const& rccListOptions,
- std::string const& fileName,
- std::vector<std::string>& files,
- std::string* errorMessage)
-{
- if (rccCommand.empty()) {
- cmSystemTools::Error("rcc executable not available");
- return false;
- }
-
- std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
- std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
-
- // Run rcc list command
- bool result = false;
- int retVal = 0;
- std::string rccStdOut;
- std::string rccStdErr;
- {
- std::vector<std::string> command;
- command.push_back(rccCommand);
- command.insert(command.end(), rccListOptions.begin(),
- rccListOptions.end());
- command.push_back(fileNameName);
- result = cmSystemTools::RunSingleCommand(
- command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
- cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
- }
- if (!result || retVal) {
- if (errorMessage != nullptr) {
- std::string& err = *errorMessage;
- err = "rcc list process failed for:\n ";
- err += cmQtAutoGen::Quoted(fileName);
- err += "\n";
- err += rccStdOut;
- err += "\n";
- err += rccStdErr;
- err += "\n";
- }
- return false;
- }
-
- // Lambda to strip CR characters
- auto StripCR = [](std::string& line) {
- std::string::size_type cr = line.find('\r');
- if (cr != std::string::npos) {
- line = line.substr(0, cr);
- }
- };
-
- // Parse rcc std output
- {
- std::istringstream ostr(rccStdOut);
- std::string oline;
- while (std::getline(ostr, oline)) {
- StripCR(oline);
- if (!oline.empty()) {
- files.push_back(oline);
- }
- }
- }
- // Parse rcc error output
- {
- std::istringstream estr(rccStdErr);
- std::string eline;
- while (std::getline(estr, eline)) {
- StripCR(eline);
- if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
- static std::string searchString = "Cannot find file '";
-
- std::string::size_type pos = eline.find(searchString);
- if (pos == std::string::npos) {
- if (errorMessage != nullptr) {
- std::string& err = *errorMessage;
- err = "rcc lists unparsable output:\n";
- err += cmQtAutoGen::Quoted(eline);
- err += "\n";
- }
- return false;
- }
- pos += searchString.length();
- std::string::size_type sz = eline.size() - pos - 1;
- files.push_back(eline.substr(pos, sz));
- }
- }
- }
-
- // Convert relative paths to absolute paths
- for (std::string& resFile : files) {
- resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile);
- }
-
- return true;
-}
-
// - Class definitions
-std::string const cmQtAutoGen::listSep = "<<<S>>>";
+std::string const cmQtAutoGen::ListSep = "<<<S>>>";
+unsigned int const cmQtAutoGen::ParallelMax = 64;
-std::string const& cmQtAutoGen::GeneratorName(Generator type)
+std::string const& cmQtAutoGen::GeneratorName(GeneratorT type)
{
switch (type) {
- case Generator::GEN:
+ case GeneratorT::GEN:
return genNameGen;
- case Generator::MOC:
+ case GeneratorT::MOC:
return genNameMoc;
- case Generator::UIC:
+ case GeneratorT::UIC:
return genNameUic;
- case Generator::RCC:
+ case GeneratorT::RCC:
return genNameRcc;
}
return genNameGen;
}
-std::string cmQtAutoGen::GeneratorNameUpper(Generator genType)
+std::string cmQtAutoGen::GeneratorNameUpper(GeneratorT genType)
{
return cmSystemTools::UpperCase(cmQtAutoGen::GeneratorName(genType));
}
-std::string const& cmQtAutoGen::MultiConfigName(MultiConfig config)
+std::string const& cmQtAutoGen::MultiConfigName(MultiConfigT config)
{
switch (config) {
- case MultiConfig::SINGLE:
+ case MultiConfigT::SINGLE:
return mcNameSingle;
- case MultiConfig::WRAP:
- return mcNameWrap;
- case MultiConfig::FULL:
- return mcNameFull;
+ case MultiConfigT::WRAPPER:
+ return mcNameWrapper;
+ case MultiConfigT::MULTI:
+ return mcNameMulti;
}
- return mcNameWrap;
+ return mcNameWrapper;
}
-cmQtAutoGen::MultiConfig cmQtAutoGen::MultiConfigType(std::string const& name)
+cmQtAutoGen::MultiConfigT cmQtAutoGen::MultiConfigType(std::string const& name)
{
if (name == mcNameSingle) {
- return MultiConfig::SINGLE;
+ return MultiConfigT::SINGLE;
}
- if (name == mcNameFull) {
- return MultiConfig::FULL;
+ if (name == mcNameMulti) {
+ return MultiConfigT::MULTI;
}
- return MultiConfig::WRAP;
+ return MultiConfigT::WRAPPER;
}
std::string cmQtAutoGen::Quoted(std::string const& text)
@@ -294,6 +141,33 @@ std::string cmQtAutoGen::Quoted(std::string const& text)
return res;
}
+std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
+{
+ std::string res;
+ for (std::string const& item : command) {
+ if (!res.empty()) {
+ res.push_back(' ');
+ }
+ std::string const cesc = cmQtAutoGen::Quoted(item);
+ if (item.empty() || (cesc.size() > (item.size() + 2)) ||
+ (cesc.find(' ') != std::string::npos)) {
+ res += cesc;
+ } else {
+ res += item;
+ }
+ }
+ return res;
+}
+
+std::string cmQtAutoGen::SubDirPrefix(std::string const& filename)
+{
+ std::string res(cmSystemTools::GetFilenamePath(filename));
+ if (!res.empty()) {
+ res += '/';
+ }
+ return res;
+}
+
std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename,
std::string const& suffix)
{
@@ -333,27 +207,79 @@ void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
}
-bool cmQtAutoGen::RccListInputs(std::string const& rccCommand,
- std::vector<std::string> const& rccListOptions,
- std::string const& fileName,
- std::vector<std::string>& files,
- std::string* errorMessage)
+void cmQtAutoGen::RccListParseContent(std::string const& content,
+ std::vector<std::string>& files)
{
- bool allGood = false;
- if (cmSystemTools::FileExists(fileName.c_str())) {
- if (rccListOptions.empty()) {
- allGood = RccListInputsQt4(fileName, files, errorMessage);
- } else {
- allGood = RccListInputsQt5(rccCommand, rccListOptions, fileName, files,
- errorMessage);
+ cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
+ cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
+
+ const char* contentChars = content.c_str();
+ while (fileMatchRegex.find(contentChars)) {
+ std::string const qrcEntry = fileMatchRegex.match(1);
+ contentChars += qrcEntry.size();
+ {
+ fileReplaceRegex.find(qrcEntry);
+ std::string const tag = fileReplaceRegex.match(1);
+ files.push_back(qrcEntry.substr(tag.size()));
}
- } else {
- if (errorMessage != nullptr) {
- std::string& err = *errorMessage;
- err = "rcc resource file does not exist:\n ";
- err += cmQtAutoGen::Quoted(fileName);
- err += "\n";
+ }
+}
+
+bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut,
+ std::string const& rccStdErr,
+ std::vector<std::string>& files,
+ std::string& error)
+{
+ // Lambda to strip CR characters
+ auto StripCR = [](std::string& line) {
+ std::string::size_type cr = line.find('\r');
+ if (cr != std::string::npos) {
+ line = line.substr(0, cr);
}
+ };
+
+ // Parse rcc std output
+ {
+ std::istringstream ostr(rccStdOut);
+ std::string oline;
+ while (std::getline(ostr, oline)) {
+ StripCR(oline);
+ if (!oline.empty()) {
+ files.push_back(oline);
+ }
+ }
+ }
+ // Parse rcc error output
+ {
+ std::istringstream estr(rccStdErr);
+ std::string eline;
+ while (std::getline(estr, eline)) {
+ StripCR(eline);
+ if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
+ static std::string const searchString = "Cannot find file '";
+
+ std::string::size_type pos = eline.find(searchString);
+ if (pos == std::string::npos) {
+ error = "rcc lists unparsable output:\n";
+ error += cmQtAutoGen::Quoted(eline);
+ error += "\n";
+ return false;
+ }
+ pos += searchString.length();
+ std::string::size_type sz = eline.size() - pos - 1;
+ files.push_back(eline.substr(pos, sz));
+ }
+ }
+ }
+
+ return true;
+}
+
+void cmQtAutoGen::RccListConvertFullPath(std::string const& qrcFileDir,
+ std::vector<std::string>& files)
+{
+ for (std::string& entry : files) {
+ std::string tmp = cmSystemTools::CollapseCombinedPath(qrcFileDir, entry);
+ entry = std::move(tmp);
}
- return allGood;
}
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index e769e93..30ce0f6 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -9,14 +9,18 @@
#include <vector>
/** \class cmQtAutoGen
- * \brief Class used as namespace for QtAutogen related types and functions
+ * \brief Common base class for QtAutoGen classes
*/
class cmQtAutoGen
{
public:
- static std::string const listSep;
+ /// @brief Nested lists separator
+ static std::string const ListSep;
+ /// @brief Maximum number of parallel threads/processes in a generator
+ static unsigned int const ParallelMax;
- enum Generator
+ /// @brief AutoGen generator type
+ enum class GeneratorT
{
GEN, // General
MOC,
@@ -24,27 +28,33 @@ public:
RCC
};
- enum MultiConfig
+ /// @brief Multiconfiguration type
+ enum class MultiConfigT
{
- SINGLE, // Single configuration
- WRAP, // Multi configuration using wrapper files
- FULL // Full multi configuration using per config sources
+ SINGLE, // Single configuration
+ WRAPPER, // Multi configuration using wrapper files
+ MULTI // Multi configuration using per config sources
};
public:
/// @brief Returns the generator name
- static std::string const& GeneratorName(Generator genType);
+ static std::string const& GeneratorName(GeneratorT genType);
/// @brief Returns the generator name in upper case
- static std::string GeneratorNameUpper(Generator genType);
+ static std::string GeneratorNameUpper(GeneratorT genType);
/// @brief Returns the multi configuration name string
- static std::string const& MultiConfigName(MultiConfig config);
+ static std::string const& MultiConfigName(MultiConfigT config);
/// @brief Returns the multi configuration type
- static MultiConfig MultiConfigType(std::string const& name);
+ static MultiConfigT MultiConfigType(std::string const& name);
/// @brief Returns a the string escaped and enclosed in quotes
static std::string Quoted(std::string const& text);
+ static std::string QuotedCommand(std::vector<std::string> const& command);
+
+ /// @brief Returns the parent directory of the file with a "/" suffix
+ static std::string SubDirPrefix(std::string const& filename);
+
/// @brief Appends the suffix to the filename before the last dot
static std::string AppendFilenameSuffix(std::string const& filename,
std::string const& suffix);
@@ -59,14 +69,21 @@ public:
std::vector<std::string> const& newOpts,
bool isQt5);
- /// @brief Reads the resource files list from from a .qrc file
- /// @arg fileName Must be the absolute path of the .qrc file
- /// @return True if the rcc file was successfully read
- static bool RccListInputs(std::string const& rccCommand,
- std::vector<std::string> const& rccListOptions,
- std::string const& fileName,
- std::vector<std::string>& files,
- std::string* errorMessage = nullptr);
+ /// @brief Parses the content of a qrc file
+ ///
+ /// Use when rcc does not support the "--list" option
+ static void RccListParseContent(std::string const& content,
+ std::vector<std::string>& files);
+
+ /// @brief Parses the output of the "rcc --list ..." command
+ static bool RccListParseOutput(std::string const& rccStdOut,
+ std::string const& rccStdErr,
+ std::vector<std::string>& files,
+ std::string& error);
+
+ /// @brief Converts relative qrc entry paths to full paths
+ static void RccListConvertFullPath(std::string const& qrcFileDir,
+ std::vector<std::string>& files);
};
#endif
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index ee0ddbc..5b2b6d0 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -4,7 +4,6 @@
#include "cmQtAutoGenerator.h"
#include "cmsys/FStream.hxx"
-#include "cmsys/Terminal.h"
#include "cmAlgorithms.h"
#include "cmGlobalGenerator.h"
@@ -14,108 +13,55 @@
#include "cmSystemTools.h"
#include "cmake.h"
-// -- Static functions
-
-static std::string HeadLine(std::string const& title)
-{
- std::string head = title;
- head += '\n';
- head.append(head.size() - 1, '-');
- head += '\n';
- return head;
-}
-
-static std::string QuotedCommand(std::vector<std::string> const& command)
-{
- std::string res;
- for (std::string const& item : command) {
- if (!res.empty()) {
- res.push_back(' ');
- }
- std::string const cesc = cmQtAutoGen::Quoted(item);
- if (item.empty() || (cesc.size() > (item.size() + 2)) ||
- (cesc.find(' ') != std::string::npos)) {
- res += cesc;
- } else {
- res += item;
- }
- }
- return res;
-}
+#include <algorithm>
// -- Class methods
-cmQtAutoGenerator::cmQtAutoGenerator()
- : Verbose(cmSystemTools::HasEnv("VERBOSE"))
- , ColorOutput(true)
+void cmQtAutoGenerator::Logger::SetVerbose(bool value)
{
- {
- std::string colorEnv;
- cmSystemTools::GetEnv("COLOR", colorEnv);
- if (!colorEnv.empty()) {
- this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str());
- }
- }
+ Verbose_ = value;
}
-bool cmQtAutoGenerator::Run(std::string const& infoFile,
- std::string const& config)
+void cmQtAutoGenerator::Logger::SetColorOutput(bool value)
{
- // Info settings
- this->InfoFile = infoFile;
- cmSystemTools::ConvertToUnixSlashes(this->InfoFile);
- this->InfoDir = cmSystemTools::GetFilenamePath(infoFile);
- this->InfoConfig = config;
-
- cmake cm(cmake::RoleScript);
- cm.SetHomeOutputDirectory(this->InfoDir);
- cm.SetHomeDirectory(this->InfoDir);
- cm.GetCurrentSnapshot().SetDefaultDefinitions();
- cmGlobalGenerator gg(&cm);
-
- cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
- snapshot.GetDirectory().SetCurrentBinary(this->InfoDir);
- snapshot.GetDirectory().SetCurrentSource(this->InfoDir);
-
- auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
- // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
- // https://gitlab.kitware.com/cmake/cmake/issues/17570
- makefile->SetPolicyVersion("3.9");
- gg.SetCurrentMakefile(makefile.get());
-
- return this->Process(makefile.get());
+ ColorOutput_ = value;
}
-void cmQtAutoGenerator::LogBold(std::string const& message) const
+std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title)
{
- cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
- cmsysTerminal_Color_ForegroundBold,
- message.c_str(), true, this->ColorOutput);
+ std::string head = title;
+ head += '\n';
+ head.append(head.size() - 1, '-');
+ head += '\n';
+ return head;
}
-void cmQtAutoGenerator::LogInfo(cmQtAutoGen::Generator genType,
- std::string const& message) const
+void cmQtAutoGenerator::Logger::Info(GeneratorT genType,
+ std::string const& message)
{
- std::string msg = cmQtAutoGen::GeneratorName(genType);
+ std::string msg = GeneratorName(genType);
msg += ": ";
msg += message;
if (msg.back() != '\n') {
msg.push_back('\n');
}
- cmSystemTools::Stdout(msg.c_str(), msg.size());
+ {
+ std::lock_guard<std::mutex> lock(Mutex_);
+ cmSystemTools::Stdout(msg.c_str(), msg.size());
+ }
}
-void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType,
- std::string const& message) const
+void cmQtAutoGenerator::Logger::Warning(GeneratorT genType,
+ std::string const& message)
{
- std::string msg = cmQtAutoGen::GeneratorName(genType);
- msg += " warning:";
+ std::string msg;
if (message.find('\n') == std::string::npos) {
// Single line message
- msg.push_back(' ');
+ msg += GeneratorName(genType);
+ msg += " warning: ";
} else {
// Multi line message
- msg.push_back('\n');
+ msg += HeadLine(GeneratorName(genType) + " warning");
}
// Message
msg += message;
@@ -123,55 +69,60 @@ void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType,
msg.push_back('\n');
}
msg.push_back('\n');
- cmSystemTools::Stdout(msg.c_str(), msg.size());
+ {
+ std::lock_guard<std::mutex> lock(Mutex_);
+ cmSystemTools::Stdout(msg.c_str(), msg.size());
+ }
}
-void cmQtAutoGenerator::LogFileWarning(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& message) const
+void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType,
+ std::string const& filename,
+ std::string const& message)
{
std::string msg = " ";
- msg += cmQtAutoGen::Quoted(filename);
+ msg += Quoted(filename);
msg.push_back('\n');
// Message
msg += message;
- this->LogWarning(genType, msg);
+ Warning(genType, msg);
}
-void cmQtAutoGenerator::LogError(cmQtAutoGen::Generator genType,
- std::string const& message) const
+void cmQtAutoGenerator::Logger::Error(GeneratorT genType,
+ std::string const& message)
{
std::string msg;
- msg.push_back('\n');
- msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error");
+ msg += HeadLine(GeneratorName(genType) + " error");
// Message
msg += message;
if (msg.back() != '\n') {
msg.push_back('\n');
}
msg.push_back('\n');
- cmSystemTools::Stderr(msg.c_str(), msg.size());
+ {
+ std::lock_guard<std::mutex> lock(Mutex_);
+ cmSystemTools::Stderr(msg.c_str(), msg.size());
+ }
}
-void cmQtAutoGenerator::LogFileError(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& message) const
+void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType,
+ std::string const& filename,
+ std::string const& message)
{
std::string emsg = " ";
- emsg += cmQtAutoGen::Quoted(filename);
+ emsg += Quoted(filename);
emsg += '\n';
// Message
emsg += message;
- this->LogError(genType, emsg);
+ Error(genType, emsg);
}
-void cmQtAutoGenerator::LogCommandError(
- cmQtAutoGen::Generator genType, std::string const& message,
- std::vector<std::string> const& command, std::string const& output) const
+void cmQtAutoGenerator::Logger::ErrorCommand(
+ GeneratorT genType, std::string const& message,
+ std::vector<std::string> const& command, std::string const& output)
{
std::string msg;
msg.push_back('\n');
- msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error");
+ msg += HeadLine(GeneratorName(genType) + " subprocess error");
msg += message;
if (msg.back() != '\n') {
msg.push_back('\n');
@@ -189,135 +140,495 @@ void cmQtAutoGenerator::LogCommandError(
msg.push_back('\n');
}
msg.push_back('\n');
- cmSystemTools::Stderr(msg.c_str(), msg.size());
+ {
+ std::lock_guard<std::mutex> lock(Mutex_);
+ cmSystemTools::Stderr(msg.c_str(), msg.size());
+ }
}
-/**
- * @brief Generates the parent directory of the given file on demand
- * @return True on success
- */
-bool cmQtAutoGenerator::MakeParentDirectory(cmQtAutoGen::Generator genType,
- std::string const& filename) const
+std::string cmQtAutoGenerator::FileSystem::RealPath(
+ std::string const& filename)
{
- bool success = true;
- std::string const dirName = cmSystemTools::GetFilenamePath(filename);
- if (!dirName.empty()) {
- if (!cmSystemTools::MakeDirectory(dirName)) {
- this->LogFileError(genType, filename,
- "Could not create parent directory");
- success = false;
- }
- }
- return success;
+ std::lock_guard<std::mutex> lock(Mutex_);
+ return cmSystemTools::GetRealPath(filename);
+}
+
+bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename)
+{
+ std::lock_guard<std::mutex> lock(Mutex_);
+ return cmSystemTools::FileExists(filename);
}
-/**
- * @brief Tests if buildFile is older than sourceFile
- * @return True if buildFile is older than sourceFile.
- * False may indicate an error.
- */
-bool cmQtAutoGenerator::FileIsOlderThan(std::string const& buildFile,
- std::string const& sourceFile,
- std::string* error)
+bool cmQtAutoGenerator::FileSystem::FileIsOlderThan(
+ std::string const& buildFile, std::string const& sourceFile,
+ std::string* error)
{
+ bool res(false);
int result = 0;
- if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) {
- return (result < 0);
+ {
+ std::lock_guard<std::mutex> lock(Mutex_);
+ res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result);
}
- if (error != nullptr) {
- error->append(
- "File modification time comparison failed for the files\n ");
- error->append(cmQtAutoGen::Quoted(buildFile));
- error->append("\nand\n ");
- error->append(cmQtAutoGen::Quoted(sourceFile));
+ 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 false;
+ return res;
}
-bool cmQtAutoGenerator::FileRead(std::string& content,
- std::string const& filename,
- std::string* error)
+bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content,
+ std::string const& filename,
+ std::string* error)
{
bool success = false;
- if (cmSystemTools::FileExists(filename)) {
- std::size_t const length = cmSystemTools::FileLength(filename);
- cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
- if (ifs) {
- content.resize(length);
- ifs.read(&content.front(), content.size());
+ {
+ std::lock_guard<std::mutex> lock(Mutex_);
+ if (cmSystemTools::FileExists(filename)) {
+ std::size_t const length = cmSystemTools::FileLength(filename);
+ cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
if (ifs) {
- success = true;
- } else {
- content.clear();
- if (error != nullptr) {
- error->append("Reading from the file failed.");
+ content.resize(length);
+ ifs.read(&content.front(), content.size());
+ if (ifs) {
+ success = true;
+ } else {
+ content.clear();
+ if (error != nullptr) {
+ error->append("Reading from the file failed.");
+ }
}
+ } else if (error != nullptr) {
+ error->append("Opening the file for reading failed.");
}
} else if (error != nullptr) {
- error->append("Opening the file for reading failed.");
+ error->append("The file does not exist.");
}
- } else if (error != nullptr) {
- error->append("The file does not exist.");
}
return success;
}
-bool cmQtAutoGenerator::FileWrite(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& content)
+bool cmQtAutoGenerator::FileSystem::FileRead(GeneratorT genType,
+ std::string& content,
+ std::string const& filename)
{
std::string error;
+ if (!FileRead(content, filename, &error)) {
+ Log()->ErrorFile(genType, filename, error);
+ return false;
+ }
+ return true;
+}
+
+bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename,
+ std::string const& content,
+ std::string* error)
+{
+ bool success = false;
// Make sure the parent directory exists
- if (this->MakeParentDirectory(genType, filename)) {
+ if (MakeParentDirectory(filename)) {
+ std::lock_guard<std::mutex> lock(Mutex_);
cmsys::ofstream outfile;
outfile.open(filename.c_str(),
(std::ios::out | std::ios::binary | std::ios::trunc));
if (outfile) {
outfile << content;
// Check for write errors
- if (!outfile.good()) {
- error = "File writing failed";
+ if (outfile.good()) {
+ success = true;
+ } else {
+ if (error != nullptr) {
+ error->assign("File writing failed");
+ }
}
} else {
- error = "Opening file for writing failed";
+ if (error != nullptr) {
+ error->assign("Opening file for writing failed");
+ }
+ }
+ } else {
+ if (error != nullptr) {
+ error->assign("Could not create parent directory");
}
}
- if (!error.empty()) {
- this->LogFileError(genType, filename, error);
+ return success;
+}
+
+bool cmQtAutoGenerator::FileSystem::FileWrite(GeneratorT genType,
+ std::string const& filename,
+ std::string const& content)
+{
+ std::string error;
+ if (!FileWrite(filename, content, &error)) {
+ Log()->ErrorFile(genType, filename, error);
return false;
}
return true;
}
-bool cmQtAutoGenerator::FileDiffers(std::string const& filename,
- std::string const& content)
+bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename,
+ std::string const& content)
{
bool differs = true;
{
std::string oldContents;
- if (this->FileRead(oldContents, filename)) {
+ if (FileRead(oldContents, filename)) {
differs = (oldContents != content);
}
}
return differs;
}
-/**
- * @brief Runs a command and returns true on success
- * @return True on success
- */
-bool cmQtAutoGenerator::RunCommand(std::vector<std::string> const& command,
- std::string& output) const
-{
- // Log command
- if (this->Verbose) {
- std::string qcmd = QuotedCommand(command);
- qcmd.push_back('\n');
- cmSystemTools::Stdout(qcmd.c_str(), qcmd.size());
- }
- // Execute command
- int retVal = 0;
- bool res = cmSystemTools::RunSingleCommand(
- command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE);
- return (res && (retVal == 0));
+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)
+{
+ std::lock_guard<std::mutex> lock(Mutex_);
+ return cmSystemTools::Touch(filename, false);
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname)
+{
+ std::lock_guard<std::mutex> lock(Mutex_);
+ return cmSystemTools::MakeDirectory(dirname);
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeDirectory(GeneratorT genType,
+ std::string const& dirname)
+{
+ if (!MakeDirectory(dirname)) {
+ Log()->ErrorFile(genType, dirname, "Could not create directory");
+ return false;
+ }
+ return true;
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
+ std::string const& filename)
+{
+ bool success = true;
+ std::string const dirName = cmSystemTools::GetFilenamePath(filename);
+ if (!dirName.empty()) {
+ success = MakeDirectory(dirName);
+ }
+ return success;
+}
+
+bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
+ GeneratorT genType, std::string const& filename)
+{
+ if (!MakeParentDirectory(filename)) {
+ Log()->ErrorFile(genType, filename, "Could not create parent directory");
+ return false;
+ }
+ return true;
+}
+
+int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
+ ReadOnlyProcessT* process)
+{
+ Process_ = process;
+ Target_ = nullptr;
+ return UVPipe_.init(*uv_loop, 0, this);
+}
+
+int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target)
+{
+ Target_ = target;
+ return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData);
+}
+
+void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset()
+{
+ Process_ = nullptr;
+ Target_ = nullptr;
+ UVPipe_.reset();
+ Buffer_.clear();
+ Buffer_.shrink_to_fit();
+}
+
+void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle,
+ size_t suggestedSize,
+ uv_buf_t* buf)
+{
+ auto& pipe = *reinterpret_cast<PipeT*>(handle->data);
+ pipe.Buffer_.resize(suggestedSize);
+ buf->base = &pipe.Buffer_.front();
+ buf->len = pipe.Buffer_.size();
+}
+
+void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream,
+ ssize_t nread,
+ const uv_buf_t* buf)
+{
+ auto& pipe = *reinterpret_cast<PipeT*>(stream->data);
+ if (nread > 0) {
+ // Append data to merged output
+ if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) {
+ pipe.Target_->append(buf->base, nread);
+ }
+ } else if (nread < 0) {
+ // EOF or error
+ auto* proc = pipe.Process_;
+ // Check it this an unusual error
+ if (nread != UV_EOF) {
+ if (!proc->Result()->error()) {
+ proc->Result()->ErrorMessage =
+ "libuv reading from pipe failed with error code ";
+ proc->Result()->ErrorMessage += std::to_string(nread);
+ }
+ }
+ // Clear libuv pipe handle and try to finish
+ pipe.reset();
+ proc->UVTryFinish();
+ }
+}
+
+void cmQtAutoGenerator::ProcessResultT::reset()
+{
+ ExitStatus = 0;
+ TermSignal = 0;
+ if (!StdOut.empty()) {
+ StdOut.clear();
+ StdOut.shrink_to_fit();
+ }
+ if (!StdErr.empty()) {
+ StdErr.clear();
+ StdErr.shrink_to_fit();
+ }
+ if (!ErrorMessage.empty()) {
+ ErrorMessage.clear();
+ ErrorMessage.shrink_to_fit();
+ }
+}
+
+void cmQtAutoGenerator::ReadOnlyProcessT::setup(
+ ProcessResultT* result, bool mergedOutput,
+ std::vector<std::string> const& command, std::string const& workingDirectory)
+{
+ Setup_.WorkingDirectory = workingDirectory;
+ Setup_.Command = command;
+ Setup_.Result = result;
+ Setup_.MergedOutput = mergedOutput;
+}
+
+bool cmQtAutoGenerator::ReadOnlyProcessT::start(
+ uv_loop_t* uv_loop, std::function<void()>&& finishedCallback)
+{
+ if (IsStarted() || (Result() == nullptr)) {
+ return false;
+ }
+
+ // Reset result before the start
+ Result()->reset();
+
+ // Fill command string pointers
+ if (!Setup().Command.empty()) {
+ CommandPtr_.reserve(Setup().Command.size() + 1);
+ for (std::string const& arg : Setup().Command) {
+ CommandPtr_.push_back(arg.c_str());
+ }
+ CommandPtr_.push_back(nullptr);
+ } else {
+ Result()->ErrorMessage = "Empty command";
+ }
+
+ if (!Result()->error()) {
+ if (UVPipeOut_.init(uv_loop, this) != 0) {
+ Result()->ErrorMessage = "libuv stdout pipe initialization failed";
+ }
+ }
+ if (!Result()->error()) {
+ if (UVPipeErr_.init(uv_loop, this) != 0) {
+ Result()->ErrorMessage = "libuv stderr pipe initialization failed";
+ }
+ }
+ if (!Result()->error()) {
+ // -- Setup process stdio options
+ // stdin
+ UVOptionsStdIO_[0].flags = UV_IGNORE;
+ UVOptionsStdIO_[0].data.stream = nullptr;
+ // stdout
+ UVOptionsStdIO_[1].flags =
+ static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+ UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
+ // stderr
+ UVOptionsStdIO_[2].flags =
+ static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+ UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
+
+ // -- Setup process options
+ std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
+ UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit;
+ UVOptions_.file = CommandPtr_[0];
+ UVOptions_.args = const_cast<char**>(&CommandPtr_.front());
+ UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
+ UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
+ UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
+ UVOptions_.stdio = &UVOptionsStdIO_.front();
+
+ // -- Spawn process
+ if (UVProcess_.spawn(*uv_loop, UVOptions_, this) != 0) {
+ Result()->ErrorMessage = "libuv process spawn failed";
+ }
+ }
+ // -- Start reading from stdio streams
+ if (!Result()->error()) {
+ if (UVPipeOut_.startRead(&Result()->StdOut) != 0) {
+ Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
+ }
+ }
+ if (!Result()->error()) {
+ if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut
+ : &Result()->StdErr) != 0) {
+ Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
+ }
+ }
+
+ if (!Result()->error()) {
+ IsStarted_ = true;
+ FinishedCallback_ = std::move(finishedCallback);
+ } else {
+ // Clear libuv handles and finish
+ UVProcess_.reset();
+ UVPipeOut_.reset();
+ UVPipeErr_.reset();
+ CommandPtr_.clear();
+ }
+
+ return IsStarted();
+}
+
+void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle,
+ int64_t exitStatus,
+ int termSignal)
+{
+ auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data);
+ if (proc.IsStarted() && !proc.IsFinished()) {
+ // Set error message on demand
+ proc.Result()->ExitStatus = exitStatus;
+ proc.Result()->TermSignal = termSignal;
+ if (!proc.Result()->error()) {
+ if (termSignal != 0) {
+ proc.Result()->ErrorMessage = "Process was terminated by signal ";
+ proc.Result()->ErrorMessage +=
+ std::to_string(proc.Result()->TermSignal);
+ } else if (exitStatus != 0) {
+ proc.Result()->ErrorMessage = "Process failed with return value ";
+ proc.Result()->ErrorMessage +=
+ std::to_string(proc.Result()->ExitStatus);
+ }
+ }
+
+ // Reset process handle and try to finish
+ proc.UVProcess_.reset();
+ proc.UVTryFinish();
+ }
+}
+
+void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish()
+{
+ // There still might be data in the pipes after the process has finished.
+ // Therefore check if the process is finished AND all pipes are closed before
+ // signaling the worker thread to continue.
+ if (UVProcess_.get() == nullptr) {
+ if (UVPipeOut_.uv_pipe() == nullptr) {
+ if (UVPipeErr_.uv_pipe() == nullptr) {
+ IsFinished_ = true;
+ FinishedCallback_();
+ }
+ }
+ }
+}
+
+cmQtAutoGenerator::cmQtAutoGenerator()
+ : FileSys_(&Logger_)
+{
+ // Initialize logger
+ Logger_.SetVerbose(cmSystemTools::HasEnv("VERBOSE"));
+ {
+ std::string colorEnv;
+ cmSystemTools::GetEnv("COLOR", colorEnv);
+ if (!colorEnv.empty()) {
+ Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv.c_str()));
+ } else {
+ Logger_.SetColorOutput(true);
+ }
+ }
+
+ // Initialize libuv loop
+ uv_disable_stdio_inheritance();
+#ifdef CMAKE_UV_SIGNAL_HACK
+ UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>();
+#endif
+ UVLoop_ = cm::make_unique<uv_loop_t>();
+ uv_loop_init(UVLoop());
+}
+
+cmQtAutoGenerator::~cmQtAutoGenerator()
+{
+ // Close libuv loop
+ uv_loop_close(UVLoop());
+}
+
+bool cmQtAutoGenerator::Run(std::string const& infoFile,
+ std::string const& config)
+{
+ // Info settings
+ InfoFile_ = infoFile;
+ cmSystemTools::ConvertToUnixSlashes(InfoFile_);
+ InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
+ InfoConfig_ = config;
+
+ bool success = false;
+ {
+ cmake cm(cmake::RoleScript);
+ cm.SetHomeOutputDirectory(InfoDir());
+ cm.SetHomeDirectory(InfoDir());
+ cm.GetCurrentSnapshot().SetDefaultDefinitions();
+ cmGlobalGenerator gg(&cm);
+
+ cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
+ snapshot.GetDirectory().SetCurrentBinary(InfoDir());
+ snapshot.GetDirectory().SetCurrentSource(InfoDir());
+
+ auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
+ // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
+ // https://gitlab.kitware.com/cmake/cmake/issues/17570
+ makefile->SetPolicyVersion("3.9");
+ gg.SetCurrentMakefile(makefile.get());
+ success = this->Init(makefile.get());
+ }
+ if (success) {
+ success = this->Process();
+ }
+ return success;
+}
+
+std::string cmQtAutoGenerator::SettingsFind(std::string const& content,
+ const char* key)
+{
+ std::string prefix(key);
+ prefix += ':';
+ std::string::size_type pos = content.find(prefix);
+ if (pos != std::string::npos) {
+ pos += prefix.size();
+ if (pos < content.size()) {
+ std::string::size_type posE = content.find('\n', pos);
+ if ((posE != std::string::npos) && (posE != pos)) {
+ return content.substr(pos, posE - pos);
+ }
+ }
+ }
+ return std::string();
}
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 285340d..6b35234 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -6,71 +6,283 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmQtAutoGen.h"
+#include "cmUVHandlePtr.h"
+#include "cm_uv.h"
+#include <array>
+#include <functional>
+#include <mutex>
+#include <stddef.h>
+#include <stdint.h>
#include <string>
#include <vector>
class cmMakefile;
-class cmQtAutoGenerator
+/// @brief Base class for QtAutoGen gernerators
+class cmQtAutoGenerator : public cmQtAutoGen
{
CM_DISABLE_COPY(cmQtAutoGenerator)
public:
+ // -- Types
+
+ /// @brief Thread safe logging
+ class Logger
+ {
+ public:
+ // -- Verbosity
+ bool Verbose() const { return this->Verbose_; }
+ void SetVerbose(bool value);
+ bool ColorOutput() const { return this->ColorOutput_; }
+ void SetColorOutput(bool value);
+ // -- Log info
+ void Info(GeneratorT genType, std::string const& message);
+ // -- Log warning
+ void Warning(GeneratorT genType, std::string const& message);
+ void WarningFile(GeneratorT genType, std::string const& filename,
+ std::string const& message);
+ // -- Log error
+ void Error(GeneratorT genType, std::string const& message);
+ void ErrorFile(GeneratorT genType, std::string const& filename,
+ std::string const& message);
+ void ErrorCommand(GeneratorT genType, std::string const& message,
+ std::vector<std::string> const& command,
+ std::string const& output);
+
+ private:
+ static std::string HeadLine(std::string const& title);
+
+ private:
+ std::mutex Mutex_;
+ bool volatile Verbose_ = false;
+ bool volatile ColorOutput_ = false;
+ };
+
+ /// @brief Thread safe file system interface
+ class FileSystem
+ {
+ public:
+ FileSystem(Logger* log)
+ : Log_(log)
+ {
+ }
+
+ Logger* Log() const { return Log_; }
+ std::string RealPath(std::string const& filename);
+ bool FileExists(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);
+ /// @brief Error logging version
+ bool FileRead(GeneratorT genType, std::string& content,
+ std::string const& filename);
+
+ bool FileWrite(std::string const& filename, std::string const& content,
+ std::string* error = nullptr);
+ /// @brief Error logging version
+ bool FileWrite(GeneratorT genType, std::string const& filename,
+ std::string const& content);
+
+ bool FileDiffers(std::string const& filename, std::string const& content);
+
+ bool FileRemove(std::string const& filename);
+ bool Touch(std::string const& filename);
+
+ bool MakeDirectory(std::string const& dirname);
+ /// @brief Error logging version
+ bool MakeDirectory(GeneratorT genType, std::string const& dirname);
+
+ bool MakeParentDirectory(std::string const& filename);
+ /// @brief Error logging version
+ bool MakeParentDirectory(GeneratorT genType, std::string const& filename);
+
+ private:
+ std::mutex Mutex_;
+ Logger* Log_;
+ };
+
+ /// @brief Return value and output of an external process
+ struct ProcessResultT
+ {
+ void reset();
+ bool error() const
+ {
+ return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty();
+ }
+
+ std::int64_t ExitStatus = 0;
+ int TermSignal = 0;
+ std::string StdOut;
+ std::string StdErr;
+ std::string ErrorMessage;
+ };
+
+ /// @brief External process management class
+ struct ReadOnlyProcessT
+ {
+ // -- Types
+
+ /// @brief libuv pipe buffer class
+ class PipeT
+ {
+ public:
+ int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process);
+ int startRead(std::string* target);
+ void reset();
+
+ // -- Libuv casts
+ uv_pipe_t* uv_pipe() { return UVPipe_.get(); }
+ uv_stream_t* uv_stream()
+ {
+ return reinterpret_cast<uv_stream_t*>(uv_pipe());
+ }
+ uv_handle_t* uv_handle()
+ {
+ return reinterpret_cast<uv_handle_t*>(uv_pipe());
+ }
+
+ // -- Libuv callbacks
+ static void UVAlloc(uv_handle_t* handle, size_t suggestedSize,
+ uv_buf_t* buf);
+ static void UVData(uv_stream_t* stream, ssize_t nread,
+ const uv_buf_t* buf);
+
+ private:
+ ReadOnlyProcessT* Process_ = nullptr;
+ std::string* Target_ = nullptr;
+ std::vector<char> Buffer_;
+ cm::uv_pipe_ptr UVPipe_;
+ };
+
+ /// @brief Process settings
+ struct SetupT
+ {
+ std::string WorkingDirectory;
+ std::vector<std::string> Command;
+ ProcessResultT* Result = nullptr;
+ bool MergedOutput = false;
+ };
+
+ // -- Constructor
+ ReadOnlyProcessT() = default;
+
+ // -- Const accessors
+ const SetupT& Setup() const { return Setup_; }
+ ProcessResultT* Result() const { return Setup_.Result; }
+ bool IsStarted() const { return IsStarted_; }
+ bool IsFinished() const { return IsFinished_; }
+
+ // -- Runtime
+ void setup(ProcessResultT* result, bool mergedOutput,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory = std::string());
+ bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback);
+
+ private:
+ // -- Friends
+ friend class PipeT;
+ // -- Libuv callbacks
+ static void UVExit(uv_process_t* handle, int64_t exitStatus,
+ int termSignal);
+ void UVTryFinish();
+
+ // -- Setup
+ SetupT Setup_;
+ // -- Runtime
+ bool IsStarted_ = false;
+ bool IsFinished_ = false;
+ std::function<void()> FinishedCallback_;
+ std::vector<const char*> CommandPtr_;
+ std::array<uv_stdio_container_t, 3> UVOptionsStdIO_;
+ uv_process_options_t UVOptions_;
+ cm::uv_process_ptr UVProcess_;
+ PipeT UVPipeOut_;
+ PipeT UVPipeErr_;
+ };
+
+#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \
+ UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19
+#define CMAKE_UV_SIGNAL_HACK
+ /*
+ libuv does not use SA_RESTART on its signal handler, but C++ streams
+ depend on it for reliable i/o operations. This RAII helper convinces
+ libuv to install its handler, and then revises the handler to add the
+ SA_RESTART flag. We use a distinct uv loop that never runs to avoid
+ ever really getting a callback. libuv may fill the hack loop's signal
+ pipe and then stop writing, but that won't break any real loops.
+ */
+ class cmUVSignalHackRAII
+ {
+ uv_loop_t HackLoop;
+ cm::uv_signal_ptr HackSignal;
+ static void HackCB(uv_signal_t*, int) {}
+ public:
+ cmUVSignalHackRAII()
+ {
+ uv_loop_init(&this->HackLoop);
+ this->HackSignal.init(this->HackLoop);
+ this->HackSignal.start(HackCB, SIGCHLD);
+ struct sigaction hack_sa;
+ sigaction(SIGCHLD, NULL, &hack_sa);
+ if (!(hack_sa.sa_flags & SA_RESTART)) {
+ hack_sa.sa_flags |= SA_RESTART;
+ sigaction(SIGCHLD, &hack_sa, NULL);
+ }
+ }
+ ~cmUVSignalHackRAII()
+ {
+ this->HackSignal.stop();
+ uv_loop_close(&this->HackLoop);
+ }
+ };
+#endif
+
+public:
+ // -- Constructors
cmQtAutoGenerator();
- virtual ~cmQtAutoGenerator() = default;
+ virtual ~cmQtAutoGenerator();
+
+ // -- Run
bool Run(std::string const& infoFile, std::string const& config);
- std::string const& GetInfoFile() const { return InfoFile; }
- std::string const& GetInfoDir() const { return InfoDir; }
- std::string const& GetInfoConfig() const { return InfoConfig; }
- bool GetVerbose() const { return Verbose; }
+ // -- Accessors
+ // Logging
+ Logger& Log() { return Logger_; }
+ // File System
+ FileSystem& FileSys() { return FileSys_; }
+ // InfoFile
+ std::string const& InfoFile() const { return InfoFile_; }
+ std::string const& InfoDir() const { return InfoDir_; }
+ std::string const& InfoConfig() const { return InfoConfig_; }
+ // libuv loop
+ uv_loop_t* UVLoop() { return UVLoop_.get(); }
+ cm::uv_async_ptr& UVRequest() { return UVRequest_; }
-protected:
- // -- Central processing
- virtual bool Process(cmMakefile* makefile) = 0;
-
- // -- Log info
- void LogBold(std::string const& message) const;
- void LogInfo(cmQtAutoGen::Generator genType,
- std::string const& message) const;
- // -- Log warning
- void LogWarning(cmQtAutoGen::Generator genType,
- std::string const& message) const;
- void LogFileWarning(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& message) const;
- // -- Log error
- void LogError(cmQtAutoGen::Generator genType,
- std::string const& message) const;
- void LogFileError(cmQtAutoGen::Generator genType,
- std::string const& filename,
- std::string const& message) const;
- void LogCommandError(cmQtAutoGen::Generator genType,
- std::string const& message,
- std::vector<std::string> const& command,
- std::string const& output) const;
// -- Utility
- bool MakeParentDirectory(cmQtAutoGen::Generator genType,
- std::string const& filename) const;
- 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(cmQtAutoGen::Generator genType, std::string const& filename,
- std::string const& content);
- bool FileDiffers(std::string const& filename, std::string const& content);
- bool RunCommand(std::vector<std::string> const& command,
- std::string& output) const;
+ static std::string SettingsFind(std::string const& content, const char* key);
+
+protected:
+ // -- Abstract processing interface
+ virtual bool Init(cmMakefile* makefile) = 0;
+ virtual bool Process() = 0;
private:
+ // -- Logging
+ Logger Logger_;
+ FileSystem FileSys_;
// -- Info settings
- std::string InfoFile;
- std::string InfoDir;
- std::string InfoConfig;
- // -- Settings
- bool Verbose;
- bool ColorOutput;
+ std::string InfoFile_;
+ std::string InfoDir_;
+ std::string InfoConfig_;
+// -- libuv loop
+#ifdef CMAKE_UV_SIGNAL_HACK
+ std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_;
+#endif
+ std::unique_ptr<uv_loop_t> UVLoop_;
+ cm::uv_async_ptr UVRequest_;
};
#endif
diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx
index de0ba4f..29f3653 100644
--- a/Source/cmQtAutoGeneratorInitializer.cxx
+++ b/Source/cmQtAutoGeneratorInitializer.cxx
@@ -24,6 +24,7 @@
#include "cm_sys_stat.h"
#include "cmake.h"
#include "cmsys/FStream.hxx"
+#include "cmsys/SystemInformation.hxx"
#include <algorithm>
#include <array>
@@ -52,6 +53,20 @@ inline static std::string GetSafeProperty(cmSourceFile const* sf,
return std::string(SafeString(sf->GetProperty(key)));
}
+static std::size_t GetParallelCPUCount()
+{
+ static std::size_t count = 0;
+ // Detect only on the first call
+ if (count == 0) {
+ cmsys::SystemInformation info;
+ info.RunCPUCheck();
+ count = info.GetNumberOfPhysicalCPU();
+ count = std::max<std::size_t>(count, 1);
+ count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax);
+ }
+ return count;
+}
+
static void AddDefinitionEscaped(cmMakefile* makefile, const char* key,
std::string const& value)
{
@@ -85,12 +100,12 @@ static void AddDefinitionEscaped(
seplist.push_back(std::move(blist));
}
makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake(
- cmJoin(seplist, cmQtAutoGen::listSep))
+ cmJoin(seplist, cmQtAutoGen::ListSep))
.c_str());
}
static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
- cmQtAutoGen::Generator genType)
+ cmQtAutoGen::GeneratorT genType)
{
cmSourceGroup* sourceGroup = nullptr;
// Acquire source group
@@ -101,10 +116,10 @@ static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
std::array<std::string, 2> props;
// Use generator specific group name
switch (genType) {
- case cmQtAutoGen::MOC:
+ case cmQtAutoGen::GeneratorT::MOC:
props[0] = "AUTOMOC_SOURCE_GROUP";
break;
- case cmQtAutoGen::RCC:
+ case cmQtAutoGen::GeneratorT::RCC:
props[0] = "AUTORCC_SOURCE_GROUP";
break;
default:
@@ -219,7 +234,7 @@ cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer(
, UicEnabled(uicEnabled)
, RccEnabled(rccEnabled)
, QtVersionMajor(qtVersionMajor)
- , MultiConfig(cmQtAutoGen::WRAP)
+ , MultiConfig(MultiConfigT::WRAPPER)
{
this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion(
target, this->QtVersionMajor);
@@ -240,19 +255,19 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
// Multi configuration
{
if (!globalGen->IsMultiConfig()) {
- this->MultiConfig = cmQtAutoGen::SINGLE;
+ this->MultiConfig = MultiConfigT::SINGLE;
}
// FIXME: Xcode does not support per-config sources, yet.
// (EXCLUDED_SOURCE_FILE_NAMES)
// if (globalGen->GetName().find("Xcode") != std::string::npos) {
- // return cmQtAutoGen::FULL;
+ // return MultiConfigT::MULTI;
//}
// FIXME: Visual Studio does not support per-config sources, yet.
// (EXCLUDED_SOURCE_FILE_NAMES)
// if (globalGen->GetName().find("Visual Studio") != std::string::npos) {
- // return cmQtAutoGen::FULL;
+ // return MultiConfigT::MULTI;
//}
}
@@ -294,7 +309,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
this->AutogenInfoFile += "/AutogenInfo.cmake";
this->AutogenSettingsFile = this->DirInfo;
- this->AutogenSettingsFile += "/AutogenOldSettings.cmake";
+ this->AutogenSettingsFile += "/AutogenOldSettings.txt";
}
// Autogen target FOLDER property
@@ -324,7 +339,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
{
std::string base = this->DirInfo;
base += "/AutogenOldSettings";
- if (this->MultiConfig == cmQtAutoGen::SINGLE) {
+ if (this->MultiConfig == MultiConfigT::SINGLE) {
AddCleanFile(makefile, base.append(".cmake"));
} else {
for (std::string const& cfg : this->ConfigsList) {
@@ -340,7 +355,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
// Add moc compilation to generated files list
if (this->MocEnabled) {
std::string const mocsComp = this->DirBuild + "/mocs_compilation.cpp";
- auto files = this->AddGeneratedSource(mocsComp, cmQtAutoGen::MOC);
+ auto files = this->AddGeneratedSource(mocsComp, GeneratorT::MOC);
for (std::string& file : files) {
autogenProvides.push_back(std::move(file));
}
@@ -349,7 +364,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
// Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
if (this->MocEnabled || this->UicEnabled) {
std::string includeDir = this->DirBuild + "/include";
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
+ if (this->MultiConfig != MultiConfigT::SINGLE) {
includeDir += "_$<CONFIG>";
}
this->Target->AddIncludeDirectory(includeDir, true);
@@ -560,10 +575,10 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
msg += "For compatibility, CMake is excluding the GENERATED source "
"file(s):\n";
for (const std::string& absFile : generatedHeaders) {
- msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n");
+ msg.append(" ").append(Quoted(absFile)).append("\n");
}
for (const std::string& absFile : generatedSources) {
- msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n");
+ msg.append(" ").append(Quoted(absFile)).append("\n");
}
msg += "from processing by ";
msg += tools;
@@ -630,7 +645,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
qrc.InfoFile = base;
qrc.InfoFile += "Info.cmake";
qrc.SettingsFile = base;
- qrc.SettingsFile += "Settings.cmake";
+ qrc.SettingsFile += "Settings.txt";
}
}
}
@@ -650,16 +665,16 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
std::vector<std::string> nameOpts;
nameOpts.emplace_back("-name");
nameOpts.emplace_back(std::move(name));
- cmQtAutoGen::RccMergeOptions(opts, nameOpts, QtV5);
+ RccMergeOptions(opts, nameOpts, QtV5);
}
// Merge file option
- cmQtAutoGen::RccMergeOptions(opts, qrc.Options, QtV5);
+ RccMergeOptions(opts, qrc.Options, QtV5);
qrc.Options = std::move(opts);
}
for (Qrc& qrc : this->Qrcs) {
// Register file at target
std::vector<std::string> const ccOutput =
- this->AddGeneratedSource(qrc.RccFile, cmQtAutoGen::RCC);
+ this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
cmCustomCommandLines commandLines;
{
@@ -686,8 +701,9 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
ccName += qrc.PathChecksum;
}
std::vector<std::string> ccDepends;
- // Add the .qrc file to the custom target dependencies
+ // Add the .qrc and info file to the custom target dependencies
ccDepends.push_back(qrc.QrcFile);
+ ccDepends.push_back(qrc.InfoFile);
cmTarget* autoRccTarget = makefile->AddUtilityCommand(
ccName, cmMakefile::TargetOrigin::Generator, true,
@@ -709,15 +725,14 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets()
{
std::vector<std::string> ccByproducts;
std::vector<std::string> ccDepends;
- // Add the .qrc file to the custom command dependencies
+ // Add the .qrc and info file to the custom command dependencies
ccDepends.push_back(qrc.QrcFile);
+ ccDepends.push_back(qrc.InfoFile);
// Add the resource files to the dependencies
{
std::string error;
- if (cmQtAutoGen::RccListInputs(this->RccExecutable,
- this->RccListOptions, qrc.QrcFile,
- qrc.Resources, &error)) {
+ if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
for (std::string const& fileName : qrc.Resources) {
// Add resource file to the custom command dependencies
ccDepends.push_back(fileName);
@@ -903,8 +918,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
// Basic setup
AddDefinitionEscaped(makefile, "_multi_config",
- cmQtAutoGen::MultiConfigName(this->MultiConfig));
+ MultiConfigName(this->MultiConfig));
AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild);
+ {
+ std::string parallel = GetSafeProperty(this->Target, "AUTOGEN_PARALLEL");
+ // Autodetect number of CPUs
+ if (parallel.empty() || (parallel == "AUTO")) {
+ parallel = std::to_string(GetParallelCPUCount());
+ }
+ AddDefinitionEscaped(makefile, "_parallel", parallel);
+ }
if (this->MocEnabled || this->UicEnabled) {
AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor);
@@ -929,7 +952,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
// Create info directory on demand
if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
std::string emsg = ("Could not create directory: ");
- emsg += cmQtAutoGen::Quoted(this->DirInfo);
+ emsg += Quoted(this->DirInfo);
cmSystemTools::Error(emsg.c_str());
}
@@ -951,7 +974,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
if (!ofs) {
// File open error
std::string error = "Internal CMake error when trying to open file: ";
- error += cmQtAutoGen::Quoted(fileName);
+ error += Quoted(fileName);
error += " for writing.";
cmSystemTools::Error(error.c_str());
}
@@ -984,11 +1007,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
OfsWriteMap("AM_MOC_INCLUDES", this->ConfigMocIncludes);
OfsWriteMap("AM_UIC_TARGET_OPTIONS", this->ConfigUicOptions);
// Settings files (only require for multi configuration generators)
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
+ if (this->MultiConfig != MultiConfigT::SINGLE) {
std::map<std::string, std::string> settingsFiles;
for (std::string const& cfg : this->ConfigsList) {
- settingsFiles[cfg] = cmQtAutoGen::AppendFilenameSuffix(
- this->AutogenSettingsFile, "_" + cfg);
+ settingsFiles[cfg] =
+ AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg);
}
OfsWriteMap("AM_SETTINGS_FILE", settingsFiles);
}
@@ -1033,11 +1056,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets()
OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes);
// Settings files (only require for multi configuration generators)
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
+ if (this->MultiConfig != MultiConfigT::SINGLE) {
std::map<std::string, std::string> settingsFiles;
for (std::string const& cfg : this->ConfigsList) {
settingsFiles[cfg] =
- cmQtAutoGen::AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
+ AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
}
OfsWriteMap("ARCC_SETTINGS_FILE", settingsFiles);
}
@@ -1267,16 +1290,15 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic()
}
std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
- std::string const& filename, cmQtAutoGen::Generator genType)
+ std::string const& filename, GeneratorT genType)
{
std::vector<std::string> genFiles;
// Register source file in makefile and source group
- if (this->MultiConfig != cmQtAutoGen::FULL) {
+ if (this->MultiConfig != MultiConfigT::MULTI) {
genFiles.push_back(filename);
} else {
for (std::string const& cfg : this->ConfigsList) {
- genFiles.push_back(
- cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg));
+ genFiles.push_back(AppendFilenameSuffix(filename, "_" + cfg));
}
}
{
@@ -1292,14 +1314,14 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
}
// Add source file to target
- if (this->MultiConfig != cmQtAutoGen::FULL) {
+ if (this->MultiConfig != MultiConfigT::MULTI) {
this->Target->AddSource(filename);
} else {
for (std::string const& cfg : this->ConfigsList) {
std::string src = "$<$<CONFIG:";
src += cfg;
src += ">:";
- src += cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg);
+ src += AppendFilenameSuffix(filename, "_" + cfg);
src += ">";
this->Target->AddSource(src);
}
@@ -1356,3 +1378,84 @@ bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual(
}
return false;
}
+
+/// @brief Reads the resource files list from from a .qrc file
+/// @arg fileName Must be the absolute path of the .qrc file
+/// @return True if the rcc file was successfully read
+bool cmQtAutoGeneratorInitializer::RccListInputs(
+ std::string const& fileName, std::vector<std::string>& files,
+ std::string& error)
+{
+ if (!cmSystemTools::FileExists(fileName.c_str())) {
+ error = "rcc resource file does not exist:\n ";
+ error += Quoted(fileName);
+ error += "\n";
+ return false;
+ }
+ if (!RccListOptions.empty()) {
+ // Use rcc for file listing
+ if (RccExecutable.empty()) {
+ error = "rcc executable not available";
+ return false;
+ }
+
+ // Run rcc list command in the directory of the qrc file with the pathless
+ // qrc file name argument. This way rcc prints relative paths.
+ // This avoids issues on Windows when the qrc file is in a path that
+ // contains non-ASCII characters.
+ std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
+ std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
+
+ bool result = false;
+ int retVal = 0;
+ std::string rccStdOut;
+ std::string rccStdErr;
+ {
+ std::vector<std::string> cmd;
+ cmd.push_back(RccExecutable);
+ cmd.insert(cmd.end(), RccListOptions.begin(), RccListOptions.end());
+ cmd.push_back(fileNameName);
+ result = cmSystemTools::RunSingleCommand(
+ cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
+ cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
+ }
+ if (!result || retVal) {
+ error = "rcc list process failed for:\n ";
+ error += Quoted(fileName);
+ error += "\n";
+ error += rccStdOut;
+ error += "\n";
+ error += rccStdErr;
+ error += "\n";
+ return false;
+ }
+ if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
+ return false;
+ }
+ } else {
+ // We can't use rcc for the file listing.
+ // Read the qrc file content into string and parse it.
+ {
+ std::string qrcContents;
+ {
+ cmsys::ifstream ifs(fileName.c_str());
+ if (ifs) {
+ std::ostringstream osst;
+ osst << ifs.rdbuf();
+ qrcContents = osst.str();
+ } else {
+ error = "rcc file not readable:\n ";
+ error += Quoted(fileName);
+ error += "\n";
+ return false;
+ }
+ }
+ // Parse string content
+ RccListParseContent(qrcContents, files);
+ }
+ }
+
+ // Convert relative paths to absolute paths
+ RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files);
+ return true;
+}
diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGeneratorInitializer.h
index e06e1c4..0567437 100644
--- a/Source/cmQtAutoGeneratorInitializer.h
+++ b/Source/cmQtAutoGeneratorInitializer.h
@@ -13,7 +13,8 @@
class cmGeneratorTarget;
-class cmQtAutoGeneratorInitializer
+/// @brief Initializes the QtAutoGen generators
+class cmQtAutoGeneratorInitializer : public cmQtAutoGen
{
public:
static std::string GetQtMajorVersion(cmGeneratorTarget const* target);
@@ -55,11 +56,15 @@ private:
void SetupCustomTargetsUic();
std::vector<std::string> AddGeneratedSource(std::string const& filename,
- cmQtAutoGen::Generator genType);
+ GeneratorT genType);
bool QtVersionGreaterOrEqual(unsigned long requestMajor,
unsigned long requestMinor) const;
+ bool RccListInputs(std::string const& fileName,
+ std::vector<std::string>& files,
+ std::string& errorMessage);
+
private:
cmGeneratorTarget* Target;
bool MocEnabled;
@@ -73,7 +78,7 @@ private:
// Configurations
std::string ConfigDefault;
std::vector<std::string> ConfigsList;
- cmQtAutoGen::MultiConfig MultiConfig;
+ MultiConfigT MultiConfig;
// Names
std::string AutogenTargetName;
std::string AutogenFolder;
diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx
index bce148e..4b02e0b 100644
--- a/Source/cmQtAutoGeneratorMocUic.cxx
+++ b/Source/cmQtAutoGeneratorMocUic.cxx
@@ -5,16 +5,15 @@
#include <algorithm>
#include <array>
+#include <functional>
#include <list>
#include <memory>
#include <sstream>
-#include <string.h>
#include <utility>
#include "cmAlgorithms.h"
#include "cmCryptoHash.h"
#include "cmMakefile.h"
-#include "cmOutputConverter.h"
#include "cmSystemTools.h"
#include "cmake.h"
@@ -22,51 +21,1126 @@
#include <unistd.h>
#endif
-// -- Static variables
+// -- Class methods
-static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH";
-static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH";
+std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
+ std::string const& relativePath) const
+{
+ return cmSystemTools::CollapseCombinedPath(AutogenBuildDir, relativePath);
+}
-// -- Static functions
+/**
+ * @brief Tries to find the header file to the given file base path by
+ * appending different header extensions
+ * @return True on success
+ */
+bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader(
+ std::string& header, std::string const& testBasePath) const
+{
+ for (std::string const& ext : HeaderExtensions) {
+ std::string testFilePath(testBasePath);
+ testFilePath.push_back('.');
+ testFilePath += ext;
+ if (FileSys->FileExists(testFilePath)) {
+ header = testFilePath;
+ return true;
+ }
+ }
+ return false;
+}
-static std::string SubDirPrefix(std::string const& fileName)
+bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
+ std::string const& fileName) const
{
- std::string res(cmSystemTools::GetFilenamePath(fileName));
- if (!res.empty()) {
- res += '/';
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+/**
+ * @brief Returns the first relevant Qt macro name found in the given C++ code
+ * @return The name of the Qt macro or an empty string
+ */
+std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro(
+ std::string const& content) const
+{
+ for (KeyExpT const& filter : MacroFilters) {
+ // Run a simple find string operation before the expensive
+ // regular expression check
+ if (content.find(filter.Key) != std::string::npos) {
+ cmsys::RegularExpressionMatch match;
+ if (filter.Exp.find(content.c_str(), match)) {
+ // Return macro name on demand
+ return filter.Key;
+ }
+ }
+ }
+ return std::string();
+}
+
+std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const
+{
+ std::string res;
+ const auto itB = MacroFilters.cbegin();
+ const auto itE = MacroFilters.cend();
+ const auto itL = itE - 1;
+ auto itC = itB;
+ for (; itC != itE; ++itC) {
+ // Separator
+ if (itC != itB) {
+ if (itC != itL) {
+ res += ", ";
+ } else {
+ res += " or ";
+ }
+ }
+ // Key
+ res += itC->Key;
}
return res;
}
-static bool ListContains(std::vector<std::string> const& list,
- std::string const& entry)
+std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile(
+ std::string const& sourcePath, std::string const& includeString) const
+{
+ // Search in vicinity of the source
+ {
+ std::string testPath = sourcePath;
+ testPath += includeString;
+ if (FileSys->FileExists(testPath)) {
+ return FileSys->RealPath(testPath);
+ }
+ }
+ // Search in include directories
+ for (std::string const& path : IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeString;
+ if (FileSys->FileExists(fullPath)) {
+ return FileSys->RealPath(fullPath);
+ }
+ }
+ // Return empty string
+ return std::string();
+}
+
+void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies(
+ std::string const& content, std::set<std::string>& depends) const
{
- return (std::find(list.begin(), list.end(), entry) != list.end());
+ if (!DependFilters.empty() && !content.empty()) {
+ for (KeyExpT const& filter : DependFilters) {
+ // Run a simple find string check
+ if (content.find(filter.Key) != std::string::npos) {
+ // Run the expensive regular expression check loop
+ const char* contentChars = content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (filter.Exp.find(contentChars, match)) {
+ {
+ std::string dep = match.match(1);
+ if (!dep.empty()) {
+ depends.emplace(std::move(dep));
+ }
+ }
+ contentChars += match.end();
+ }
+ }
+ }
+ }
}
-// -- Class methods
+bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped(
+ std::string const& fileName) const
+{
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk)
+{
+ if (AutoMoc && Header) {
+ // Don't parse header for moc if the file is included by a source already
+ if (wrk.Gen().ParallelMocIncluded(FileName)) {
+ AutoMoc = false;
+ }
+ }
+
+ if (AutoMoc || AutoUic) {
+ std::string error;
+ MetaT meta;
+ if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) {
+ if (!meta.Content.empty()) {
+ meta.FileDir = SubDirPrefix(FileName);
+ meta.FileBase =
+ cmSystemTools::GetFilenameWithoutLastExtension(FileName);
+
+ bool success = true;
+ if (AutoMoc) {
+ if (Header) {
+ success = ParseMocHeader(wrk, meta);
+ } else {
+ success = ParseMocSource(wrk, meta);
+ }
+ }
+ if (AutoUic && success) {
+ ParseUic(wrk, meta);
+ }
+ } else {
+ wrk.LogFileWarning(GeneratorT::GEN, FileName,
+ "The source file is empty");
+ }
+ } else {
+ wrk.LogFileError(GeneratorT::GEN, FileName,
+ "Could not read the file: " + error);
+ }
+ }
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk,
+ MetaT const& meta)
+{
+ struct JobPre
+ {
+ bool self; // source file is self
+ bool underscore; // "moc_" style include
+ std::string SourceFile;
+ std::string IncludeString;
+ };
+
+ struct MocInclude
+ {
+ std::string Inc; // full include string
+ std::string Dir; // include string directory
+ std::string Base; // include string file base
+ };
+
+ // Check if this source file contains a relevant macro
+ std::string const ownMacro = wrk.Moc().FindMacro(meta.Content);
+
+ // Extract moc includes from file
+ std::deque<MocInclude> mocIncsUsc;
+ std::deque<MocInclude> mocIncsDot;
+ {
+ if (meta.Content.find("moc") != std::string::npos) {
+ const char* contentChars = meta.Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (wrk.Moc().RegExpInclude.find(contentChars, match)) {
+ std::string incString = match.match(1);
+ std::string incDir(SubDirPrefix(incString));
+ std::string incBase =
+ cmSystemTools::GetFilenameWithoutLastExtension(incString);
+ if (cmHasLiteralPrefix(incBase, "moc_")) {
+ // moc_<BASE>.cxx
+ // Remove the moc_ part from the base name
+ mocIncsUsc.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), incBase.substr(4) });
+ } else {
+ // <BASE>.moc
+ mocIncsDot.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), std::move(incBase) });
+ }
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
+ }
+
+ // Check if there is anything to do
+ if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) {
+ return true;
+ }
+
+ bool ownDotMocIncluded = false;
+ bool ownMocUscIncluded = false;
+ std::deque<JobPre> jobs;
+
+ // Process moc_<BASE>.cxx includes
+ for (const MocInclude& mocInc : mocIncsUsc) {
+ std::string const header =
+ MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (wrk.Moc().skipped(header)) {
+ continue;
+ }
+ // Register moc job
+ const bool ownMoc = (mocInc.Base == meta.FileBase);
+ jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc });
+ // Store meta information for relaxed mode
+ if (ownMoc) {
+ ownMocUscIncluded = true;
+ }
+ } else {
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but the header ";
+ emsg += Quoted(MocStringHeaders(wrk, mocInc.Base));
+ emsg += " could not be found.";
+ wrk.LogFileError(GeneratorT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Process <BASE>.moc includes
+ for (const MocInclude& mocInc : mocIncsDot) {
+ const bool ownMoc = (mocInc.Base == meta.FileBase);
+ if (wrk.Moc().RelaxedMode) {
+ // Relaxed mode
+ if (!ownMacro.empty() && ownMoc) {
+ // Add self
+ jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+ ownDotMocIncluded = true;
+ } else {
+ // In relaxed mode try to find a header instead but issue a warning.
+ // This is for KDE4 compatibility
+ std::string const header =
+ MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (wrk.Moc().skipped(header)) {
+ continue;
+ }
+ // Register moc job
+ jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
+ if (ownMacro.empty()) {
+ if (ownMoc) {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but does not contain a ";
+ emsg += wrk.Moc().MacrosString();
+ emsg += " macro.\nRunning moc on\n ";
+ emsg += Quoted(header);
+ emsg += "!\nBetter include ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += " for a compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+ wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg);
+ } else {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += " instead of ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += ".\nRunning moc on\n ";
+ emsg += Quoted(header);
+ emsg += "!\nBetter include ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += " for compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+ wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg);
+ }
+ }
+ } else {
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", which seems to be the moc file from a different "
+ "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
+ "matching header ";
+ emsg += Quoted(MocStringHeaders(wrk, mocInc.Base));
+ emsg += " could not be found.";
+ wrk.LogFileError(GeneratorT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+ } else {
+ // Strict mode
+ if (ownMoc) {
+ // Include self
+ jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+ ownDotMocIncluded = true;
+ // Accept but issue a warning if moc isn't required
+ if (ownMacro.empty()) {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but does not contain a ";
+ emsg += wrk.Moc().MacrosString();
+ emsg += " macro.";
+ wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg);
+ }
+ } else {
+ // Don't allow <BASE>.moc include other than self in strict mode
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", which seems to be the moc file from a different "
+ "source file.\nThis is not supported. Include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += " to run moc on this source file.";
+ wrk.LogFileError(GeneratorT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+ }
+
+ if (!ownMacro.empty() && !ownDotMocIncluded) {
+ // In this case, check whether the scanned file itself contains a
+ // Q_OBJECT.
+ // If this is the case, the moc_foo.cpp should probably be generated from
+ // foo.cpp instead of foo.h, because otherwise it won't build.
+ // But warn, since this is not how it is supposed to be used.
+ // This is for KDE4 compatibility.
+ if (wrk.Moc().RelaxedMode && ownMocUscIncluded) {
+ JobPre uscJobPre;
+ // Remove underscore job request
+ {
+ auto itC = jobs.begin();
+ auto itE = jobs.end();
+ for (; itC != itE; ++itC) {
+ JobPre& job(*itC);
+ if (job.self && job.underscore) {
+ uscJobPre = std::move(job);
+ jobs.erase(itC);
+ break;
+ }
+ }
+ }
+ // Issue a warning
+ {
+ std::string emsg = "The file contains a ";
+ emsg += ownMacro;
+ emsg += " macro, but does not include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += ". Instead it includes ";
+ emsg += Quoted(uscJobPre.IncludeString);
+ emsg += ".\nRunning moc on\n ";
+ emsg += Quoted(FileName);
+ emsg += "!\nBetter include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += " for compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
+ wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg);
+ }
+ // Add own source job
+ jobs.emplace_back(
+ JobPre{ true, false, FileName, uscJobPre.IncludeString });
+ } else {
+ // Otherwise always error out since it will not compile.
+ {
+ std::string emsg = "The file contains a ";
+ emsg += ownMacro;
+ emsg += " macro, but does not include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += "!\nConsider to\n - add #include \"";
+ emsg += meta.FileBase;
+ emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
+ wrk.LogFileError(GeneratorT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Convert pre jobs to actual jobs
+ for (JobPre& jobPre : jobs) {
+ JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName,
+ std::move(jobPre.IncludeString)));
+ if (jobPre.self) {
+ // Read depdendencies from this source
+ static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
+ }
+ if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk,
+ MetaT const& meta)
+{
+ bool success = true;
+ std::string const macroName = wrk.Moc().FindMacro(meta.Content);
+ if (!macroName.empty()) {
+ JobHandleT jobHandle(
+ new JobMocT(std::string(FileName), std::string(), std::string()));
+ // Read depdendencies from this source
+ static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
+ success = wrk.Gen().ParallelJobPushMoc(jobHandle);
+ }
+ return success;
+}
+
+std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders(
+ WorkerT& wrk, std::string const& fileBase) const
+{
+ std::string res = fileBase;
+ res += ".{";
+ res += cmJoin(wrk.Base().HeaderExtensions, ",");
+ res += "}";
+ return res;
+}
+
+std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader(
+ WorkerT& wrk, std::string const& includerDir, std::string const& includeBase)
+{
+ std::string header;
+ // Search in vicinity of the source
+ if (!wrk.Base().FindHeader(header, includerDir + includeBase)) {
+ // Search in include directories
+ for (std::string const& path : wrk.Moc().IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeBase;
+ if (wrk.Base().FindHeader(header, fullPath)) {
+ break;
+ }
+ }
+ }
+ // Sanitize
+ if (!header.empty()) {
+ header = wrk.FileSys().RealPath(header);
+ }
+ return header;
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk,
+ MetaT const& meta)
+{
+ bool success = true;
+ if (meta.Content.find("ui_") != std::string::npos) {
+ const char* contentChars = meta.Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (wrk.Uic().RegExpInclude.find(contentChars, match)) {
+ if (!ParseUicInclude(wrk, meta, match.match(1))) {
+ success = false;
+ break;
+ }
+ contentChars += match.end();
+ }
+ }
+ return success;
+}
+
+bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude(
+ WorkerT& wrk, MetaT const& meta, std::string&& includeString)
+{
+ bool success = false;
+ std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString);
+ if (!uiInputFile.empty()) {
+ if (!wrk.Uic().skipped(uiInputFile)) {
+ JobHandleT jobHandle(new JobUicT(std::move(uiInputFile), FileName,
+ std::move(includeString)));
+ success = wrk.Gen().ParallelJobPushUic(jobHandle);
+ } else {
+ // A skipped file is successful
+ success = true;
+ }
+ }
+ return success;
+}
+
+std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile(
+ WorkerT& wrk, MetaT const& meta, std::string const& includeString)
+{
+ std::string res;
+ std::string searchFile =
+ cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
+ searchFile += ".ui";
+ // Collect search paths list
+ std::deque<std::string> testFiles;
+ {
+ std::string const searchPath = SubDirPrefix(includeString);
+
+ std::string searchFileFull;
+ if (!searchPath.empty()) {
+ searchFileFull = searchPath;
+ searchFileFull += searchFile;
+ }
+ // Vicinity of the source
+ {
+ std::string const sourcePath = meta.FileDir;
+ testFiles.push_back(sourcePath + searchFile);
+ if (!searchPath.empty()) {
+ testFiles.push_back(sourcePath + searchFileFull);
+ }
+ }
+ // AUTOUIC search paths
+ if (!wrk.Uic().SearchPaths.empty()) {
+ for (std::string const& sPath : wrk.Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFile));
+ }
+ if (!searchPath.empty()) {
+ for (std::string const& sPath : wrk.Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFileFull));
+ }
+ }
+ }
+ }
+
+ // Search for the .ui file!
+ for (std::string const& testFile : testFiles) {
+ if (wrk.FileSys().FileExists(testFile)) {
+ res = wrk.FileSys().RealPath(testFile);
+ break;
+ }
+ }
+
+ // Log error
+ if (res.empty()) {
+ std::string emsg = "Could not find ";
+ emsg += Quoted(searchFile);
+ emsg += " in\n";
+ for (std::string const& testFile : testFiles) {
+ emsg += " ";
+ emsg += Quoted(testFile);
+ emsg += "\n";
+ }
+ wrk.LogFileError(GeneratorT::UIC, FileName, emsg);
+ }
+
+ return res;
+}
+
+void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk)
+{
+ // (Re)generate moc_predefs.h on demand
+ bool generate(false);
+ bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs));
+ if (!fileExists) {
+ if (wrk.Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(wrk.Moc().PredefsFileRel);
+ reason += " because it doesn't exist";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ generate = true;
+ } else if (wrk.Moc().SettingsChanged) {
+ if (wrk.Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(wrk.Moc().PredefsFileRel);
+ reason += " because the settings changed.";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ generate = true;
+ }
+ if (generate) {
+ ProcessResultT result;
+ {
+ // Compose command
+ std::vector<std::string> cmd = wrk.Moc().PredefsCmd;
+ // Add includes
+ cmd.insert(cmd.end(), wrk.Moc().Includes.begin(),
+ wrk.Moc().Includes.end());
+ // Add definitions
+ for (std::string const& def : wrk.Moc().Definitions) {
+ cmd.push_back("-D" + def);
+ }
+ // Execute command
+ if (!wrk.RunProcess(GeneratorT::MOC, result, cmd)) {
+ std::string emsg = "The content generation command for ";
+ emsg += Quoted(wrk.Moc().PredefsFileRel);
+ emsg += " failed.\n";
+ emsg += result.ErrorMessage;
+ wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut);
+ }
+ }
+
+ // (Re)write predefs file only on demand
+ if (!result.error()) {
+ if (!fileExists ||
+ wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
+ if (wrk.FileSys().FileWrite(GeneratorT::MOC, wrk.Moc().PredefsFileAbs,
+ result.StdOut)) {
+ // Success
+ } else {
+ std::string emsg = "Writing ";
+ emsg += Quoted(wrk.Moc().PredefsFileRel);
+ emsg += " failed.";
+ wrk.LogFileError(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, emsg);
+ }
+ } else {
+ // Touch to update the time stamp
+ if (wrk.Log().Verbose()) {
+ std::string msg = "Touching ";
+ msg += Quoted(wrk.Moc().PredefsFileRel);
+ msg += ".";
+ wrk.LogInfo(GeneratorT::MOC, msg);
+ }
+ wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs);
+ }
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies(
+ WorkerT& wrk, std::string const& content)
+{
+ wrk.Moc().FindDependencies(content, Depends);
+ DependsValid = true;
+}
+
+void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk)
+{
+ // Compute build file name
+ if (!IncludeString.empty()) {
+ BuildFile = wrk.Base().AutogenIncludeDirAbs;
+ BuildFile += IncludeString;
+ } else {
+ std::string buildRel = wrk.Base().FilePathChecksum.getPart(SourceFile);
+ buildRel += '/';
+ buildRel += "moc_";
+ buildRel += cmSystemTools::GetFilenameWithoutLastExtension(SourceFile);
+ if (wrk.Base().MultiConfig != MultiConfigT::SINGLE) {
+ buildRel += wrk.Base().ConfigSuffix;
+ }
+ buildRel += ".cpp";
+ wrk.Gen().ParallelMocAutoRegister(buildRel);
+ BuildFile = wrk.Base().AbsoluteBuildPath(buildRel);
+ }
+
+ if (UpdateRequired(wrk)) {
+ GenerateMoc(wrk);
+ }
+}
+
+bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk)
+{
+ bool const verbose = wrk.Gen().Log().Verbose();
+
+ // Test if the build file exists
+ if (!wrk.FileSys().FileExists(BuildFile)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from its source file ";
+ reason += Quoted(SourceFile);
+ reason += " because it doesn't exist";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if any setting changed
+ if (wrk.Moc().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the MOC settings changed";
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if the moc_predefs file is newer
+ if (!wrk.Moc().PredefsFileAbs.empty()) {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = wrk.FileSys().FileIsOlderThan(
+ BuildFile, wrk.Moc().PredefsFileAbs, &error);
+ if (!isOlder && !error.empty()) {
+ wrk.LogError(GeneratorT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than: ";
+ reason += Quoted(wrk.Moc().PredefsFileAbs);
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ wrk.LogError(GeneratorT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than its source file ";
+ reason += Quoted(SourceFile);
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if a dependency file is newer
+ {
+ // Read dependencies on demand
+ if (!DependsValid) {
+ std::string content;
+ {
+ std::string error;
+ if (!wrk.FileSys().FileRead(content, SourceFile, &error)) {
+ std::string emsg = "Could not read file\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\nrequired by moc include ";
+ emsg += Quoted(IncludeString);
+ emsg += " in\n ";
+ emsg += Quoted(IncluderFile);
+ emsg += ".\n";
+ emsg += error;
+ wrk.LogError(GeneratorT::MOC, emsg);
+ return false;
+ }
+ }
+ FindDependencies(wrk, content);
+ }
+ // Check dependency timestamps
+ std::string error;
+ std::string sourceDir = SubDirPrefix(SourceFile);
+ for (std::string const& depFileRel : Depends) {
+ std::string depFileAbs =
+ wrk.Moc().FindIncludedFile(sourceDir, depFileRel);
+ if (!depFileAbs.empty()) {
+ if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because it is older than it's dependency file ";
+ reason += Quoted(depFileAbs);
+ wrk.LogInfo(GeneratorT::MOC, reason);
+ }
+ return true;
+ }
+ if (!error.empty()) {
+ wrk.LogError(GeneratorT::MOC, error);
+ return false;
+ }
+ } else {
+ std::string message = "Could not find dependency file ";
+ message += Quoted(depFileRel);
+ wrk.LogFileWarning(GeneratorT::MOC, SourceFile, message);
+ }
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
+{
+ // Make sure the parent directory exists
+ if (wrk.FileSys().MakeParentDirectory(GeneratorT::MOC, BuildFile)) {
+ // Compose moc command
+ std::vector<std::string> cmd;
+ cmd.push_back(wrk.Moc().Executable);
+ // Add options
+ cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(),
+ wrk.Moc().AllOptions.end());
+ // Add predefs include
+ if (!wrk.Moc().PredefsFileAbs.empty()) {
+ cmd.push_back("--include");
+ cmd.push_back(wrk.Moc().PredefsFileAbs);
+ }
+ cmd.push_back("-o");
+ cmd.push_back(BuildFile);
+ cmd.push_back(SourceFile);
+
+ // Execute moc command
+ ProcessResultT result;
+ if (wrk.RunProcess(GeneratorT::MOC, result, cmd)) {
+ // Moc command success
+ if (IncludeString.empty()) {
+ // Notify the generator that a not included file changed
+ wrk.Gen().ParallelMocAutoUpdated();
+ }
+ } else {
+ // Moc command failed
+ {
+ std::string emsg = "The moc process failed to compile\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\ninto\n ";
+ emsg += Quoted(BuildFile);
+ emsg += ".\n";
+ emsg += result.ErrorMessage;
+ wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut);
+ }
+ wrk.FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk)
+{
+ // Compute build file name
+ BuildFile = wrk.Base().AutogenIncludeDirAbs;
+ BuildFile += IncludeString;
+
+ if (UpdateRequired(wrk)) {
+ GenerateUic(wrk);
+ }
+}
+
+bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk)
+{
+ bool const verbose = wrk.Gen().Log().Verbose();
+
+ // Test if the build file exists
+ if (!wrk.FileSys().FileExists(BuildFile)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from its source file ";
+ reason += Quoted(SourceFile);
+ reason += " because it doesn't exist";
+ wrk.LogInfo(GeneratorT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the uic settings changed
+ if (wrk.Uic().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the UIC settings changed";
+ wrk.LogInfo(GeneratorT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ wrk.LogError(GeneratorT::UIC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than its source file ";
+ reason += Quoted(SourceFile);
+ wrk.LogInfo(GeneratorT::UIC, reason);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
+{
+ // Make sure the parent directory exists
+ if (wrk.FileSys().MakeParentDirectory(GeneratorT::UIC, BuildFile)) {
+ // Compose uic command
+ std::vector<std::string> cmd;
+ cmd.push_back(wrk.Uic().Executable);
+ {
+ std::vector<std::string> allOpts = wrk.Uic().TargetOptions;
+ auto optionIt = wrk.Uic().Options.find(SourceFile);
+ if (optionIt != wrk.Uic().Options.end()) {
+ UicMergeOptions(allOpts, optionIt->second,
+ (wrk.Base().QtVersionMajor == 5));
+ }
+ cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+ }
+ cmd.push_back("-o");
+ cmd.push_back(BuildFile);
+ cmd.push_back(SourceFile);
+
+ ProcessResultT result;
+ if (wrk.RunProcess(GeneratorT::UIC, result, cmd)) {
+ // Success
+ } else {
+ // Command failed
+ {
+ std::string emsg = "The uic process failed to compile\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\ninto\n ";
+ emsg += Quoted(BuildFile);
+ emsg += "\nincluded by\n ";
+ emsg += Quoted(IncluderFile);
+ emsg += ".\n";
+ emsg += result.ErrorMessage;
+ wrk.LogCommandError(GeneratorT::UIC, emsg, cmd, result.StdOut);
+ }
+ wrk.FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::JobDeleterT::operator()(JobT* job)
+{
+ delete job;
+}
+
+cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen,
+ uv_loop_t* uvLoop)
+ : Gen_(gen)
+{
+ // Initialize uv asynchronous callback for process starting
+ ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this);
+ // Start thread
+ Thread_ = std::thread(&WorkerT::Loop, this);
+}
+
+cmQtAutoGeneratorMocUic::WorkerT::~WorkerT()
+{
+ // Join thread
+ if (Thread_.joinable()) {
+ Thread_.join();
+ }
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogInfo(
+ GeneratorT genType, std::string const& message) const
+{
+ return Log().Info(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogWarning(
+ GeneratorT genType, std::string const& message) const
+{
+ return Log().Warning(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning(
+ GeneratorT genType, std::string const& filename,
+ std::string const& message) const
+{
+ return Log().WarningFile(genType, filename, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogError(
+ GeneratorT genType, std::string const& message) const
+{
+ Gen().ParallelRegisterJobError();
+ Log().Error(genType, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogFileError(
+ GeneratorT genType, std::string const& filename,
+ std::string const& message) const
+{
+ Gen().ParallelRegisterJobError();
+ Log().ErrorFile(genType, filename, message);
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError(
+ GeneratorT genType, std::string const& message,
+ std::vector<std::string> const& command, std::string const& output) const
+{
+ Gen().ParallelRegisterJobError();
+ Log().ErrorCommand(genType, message, command, output);
+}
+
+bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess(
+ GeneratorT genType, ProcessResultT& result,
+ std::vector<std::string> const& command)
+{
+ if (command.empty()) {
+ return false;
+ }
+
+ // Create process instance
+ {
+ std::lock_guard<std::mutex> lock(ProcessMutex_);
+ Process_ = cm::make_unique<ReadOnlyProcessT>();
+ Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir);
+ }
+
+ // Send asynchronous process start request to libuv loop
+ ProcessRequest_.send();
+
+ // Log command
+ if (this->Log().Verbose()) {
+ std::string msg = "Running command:\n";
+ msg += QuotedCommand(command);
+ msg += '\n';
+ this->LogInfo(genType, msg);
+ }
+
+ // Wait until the process has been finished and destroyed
+ {
+ std::unique_lock<std::mutex> ulock(ProcessMutex_);
+ while (Process_) {
+ ProcessCondition_.wait(ulock);
+ }
+ }
+ return !result.error();
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::Loop()
+{
+ while (true) {
+ Gen().WorkerSwapJob(JobHandle_);
+ if (JobHandle_) {
+ JobHandle_->Process(*this);
+ } else {
+ break;
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle)
+{
+ auto& wrk = *reinterpret_cast<WorkerT*>(handle->data);
+ {
+ std::lock_guard<std::mutex> lock(wrk.ProcessMutex_);
+ if (wrk.Process_ && !wrk.Process_->IsStarted()) {
+ wrk.Process_->start(handle->loop,
+ std::bind(&WorkerT::UVProcessFinished, &wrk));
+ }
+ }
+}
+
+void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished()
+{
+ {
+ std::lock_guard<std::mutex> lock(ProcessMutex_);
+ if (Process_ && Process_->IsFinished()) {
+ Process_.reset();
+ }
+ }
+ // Notify idling thread
+ ProcessCondition_.notify_one();
+}
cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
- : MultiConfig(cmQtAutoGen::WRAP)
- , IncludeProjectDirsBefore(false)
- , QtVersionMajor(4)
- , MocSettingsChanged(false)
- , MocPredefsChanged(false)
- , MocRelaxedMode(false)
- , UicSettingsChanged(false)
+ : Base_(&FileSys())
+ , Moc_(&FileSys())
+ , Stage_(StageT::SETTINGS_READ)
+ , JobsRemain_(0)
+ , JobError_(false)
+ , JobThreadsAbort_(false)
+ , MocAutoFileUpdated_(false)
{
// Precompile regular expressions
- this->MocRegExpInclude.compile(
+ Moc_.RegExpInclude.compile(
"[\n][ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
- this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+ Uic_.RegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+
+ // Initialize libuv asynchronous iteration request
+ UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
}
-bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
+cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic()
+{
+}
+
+bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
{
// -- Meta
- this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
+ Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
// Utility lambdas
auto InfoGet = [makefile](const char* key) {
@@ -87,7 +1161,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
std::string const value = makefile->GetSafeDefinition(key);
std::string::size_type pos = 0;
while (pos < value.size()) {
- std::string::size_type next = value.find(cmQtAutoGen::listSep, pos);
+ std::string::size_type next = value.find(ListSep, pos);
std::string::size_type length =
(next != std::string::npos) ? next - pos : value.size() - pos;
// Remove enclosing braces
@@ -102,7 +1176,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
}
}
pos += length;
- pos += cmQtAutoGen::listSep.size();
+ pos += ListSep.size();
}
}
return lists;
@@ -112,7 +1186,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
{
std::string keyConf = key;
keyConf += '_';
- keyConf += this->GetInfoConfig();
+ keyConf += InfoConfig();
valueConf = makefile->GetDefinition(keyConf);
}
if (valueConf == nullptr) {
@@ -128,122 +1202,177 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
};
// -- Read info file
- if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
- this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
- "File processing failed");
+ if (!makefile->ReadListFile(InfoFile().c_str())) {
+ Log().ErrorFile(GeneratorT::GEN, InfoFile(), "File processing failed");
return false;
}
// -- Meta
- this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
- this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
- if (this->ConfigSuffix.empty()) {
- this->ConfigSuffix = "_";
- this->ConfigSuffix += this->GetInfoConfig();
+ Base_.MultiConfig = MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
+
+ Base_.ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
+ if (Base_.ConfigSuffix.empty()) {
+ Base_.ConfigSuffix = "_";
+ Base_.ConfigSuffix += InfoConfig();
}
- this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE");
- if (this->SettingsFile.empty()) {
- this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
- "Settings file name missing");
+ SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
+ if (SettingsFile_.empty()) {
+ Log().ErrorFile(GeneratorT::GEN, InfoFile(), "Settings file name missing");
return false;
}
+ {
+ unsigned long num = Base_.NumThreads;
+ if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL"), &num)) {
+ num = std::max<unsigned long>(num, 1);
+ num = std::min<unsigned long>(num, ParallelMax);
+ Base_.NumThreads = static_cast<unsigned int>(num);
+ }
+ }
+
// - Files and directories
- this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
- this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
- this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
- this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
- this->IncludeProjectDirsBefore =
+ Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
+ Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
+ Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
+ Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
+ Base_.IncludeProjectDirsBefore =
InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
- this->AutogenBuildDir = InfoGet("AM_BUILD_DIR");
- if (this->AutogenBuildDir.empty()) {
- this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
- "Autogen build directory missing");
+ Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
+ if (Base_.AutogenBuildDir.empty()) {
+ Log().ErrorFile(GeneratorT::GEN, InfoFile(),
+ "Autogen build directory missing");
return false;
}
// - Qt environment
- if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"),
- &this->QtVersionMajor)) {
- this->QtVersionMajor = 4;
+ {
+ unsigned long qtv = Base_.QtVersionMajor;
+ if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) {
+ Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
+ }
}
- this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE");
- this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE");
// - Moc
- if (this->MocEnabled()) {
- this->MocSkipList = InfoGetList("AM_MOC_SKIP");
- this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+ Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+ Moc_.Enabled = !Moc().Executable.empty();
+ if (Moc().Enabled) {
+ {
+ auto lst = InfoGetList("AM_MOC_SKIP");
+ Moc_.SkipList.insert(lst.begin(), lst.end());
+ }
+ Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
#ifdef _WIN32
{
- std::string const win32("WIN32");
- if (!ListContains(this->MocDefinitions, win32)) {
- this->MocDefinitions.push_back(win32);
+ std::string win32("WIN32");
+ auto itB = Moc().Definitions.cbegin();
+ auto itE = Moc().Definitions.cend();
+ if (std::find(itB, itE, win32) == itE) {
+ Moc_.Definitions.emplace_back(std::move(win32));
}
}
#endif
- this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
- this->MocOptions = InfoGetList("AM_MOC_OPTIONS");
- this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
- {
- std::vector<std::string> const MocMacroNames =
- InfoGetList("AM_MOC_MACRO_NAMES");
- for (std::string const& item : MocMacroNames) {
- this->MocMacroFilters.emplace_back(
- item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
- }
+ Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
+ Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
+ Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
+ for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
+ Moc_.MacroFilters.emplace_back(
+ item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
}
{
- std::vector<std::string> const mocDependFilters =
- InfoGetList("AM_MOC_DEPEND_FILTERS");
- // Insert Q_PLUGIN_METADATA dependency filter
- if (this->QtVersionMajor != 4) {
- this->MocDependFilterPush("Q_PLUGIN_METADATA",
- "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
- "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
+ auto pushFilter = [this](std::string const& key, std::string const& exp,
+ std::string& error) {
+ if (!key.empty()) {
+ if (!exp.empty()) {
+ Moc_.DependFilters.push_back(KeyExpT());
+ KeyExpT& filter(Moc_.DependFilters.back());
+ if (filter.Exp.compile(exp)) {
+ filter.Key = key;
+ } else {
+ error = "Regular expression compiling failed";
+ }
+ } else {
+ error = "Regular expression is empty";
+ }
+ } else {
+ error = "Key is empty";
+ }
+ if (!error.empty()) {
+ error = ("AUTOMOC_DEPEND_FILTERS: " + error);
+ error += "\n";
+ error += " Key: ";
+ error += Quoted(key);
+ error += "\n";
+ error += " Exp: ";
+ error += Quoted(exp);
+ error += "\n";
+ }
+ };
+
+ std::string error;
+ // Insert default filter for Q_PLUGIN_METADATA
+ if (Base().QtVersionMajor != 4) {
+ pushFilter("Q_PLUGIN_METADATA", "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+ "[^\\)]*FILE[ \t]*\"([^\"]+)\"",
+ error);
}
// Insert user defined dependency filters
- if ((mocDependFilters.size() % 2) == 0) {
- for (std::vector<std::string>::const_iterator
- dit = mocDependFilters.begin(),
- ditEnd = mocDependFilters.end();
- dit != ditEnd; dit += 2) {
- if (!this->MocDependFilterPush(*dit, *(dit + 1))) {
- return false;
+ {
+ std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
+ if ((flts.size() % 2) == 0) {
+ for (std::vector<std::string>::iterator itC = flts.begin(),
+ itE = flts.end();
+ itC != itE; itC += 2) {
+ pushFilter(*itC, *(itC + 1), error);
+ if (!error.empty()) {
+ break;
+ }
}
+ } else {
+ Log().ErrorFile(
+ GeneratorT::MOC, InfoFile(),
+ "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ return false;
}
- } else {
- this->LogFileError(
- cmQtAutoGen::MOC, this->GetInfoFile(),
- "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ }
+ if (!error.empty()) {
+ Log().ErrorFile(GeneratorT::MOC, InfoFile(), error);
return false;
}
}
- this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+ Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+ // Install moc predefs job
+ if (!Moc().PredefsCmd.empty()) {
+ JobQueues_.MocPredefs.emplace_back(new JobMocPredefsT());
+ }
}
// - Uic
- if (this->UicEnabled()) {
- this->UicSkipList = InfoGetList("AM_UIC_SKIP");
- this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
- this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
+ Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+ Uic_.Enabled = !Uic().Executable.empty();
+ if (Uic().Enabled) {
+ {
+ auto lst = InfoGetList("AM_UIC_SKIP");
+ Uic_.SkipList.insert(lst.begin(), lst.end());
+ }
+ Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
+ Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
{
auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
// Compare list sizes
if (sources.size() != options.size()) {
std::ostringstream ost;
- ost << "files/options lists sizes mismatch (" << sources.size() << "/"
+ ost << "files/options lists sizes missmatch (" << sources.size() << "/"
<< options.size() << ")";
- this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str());
+ Log().ErrorFile(GeneratorT::UIC, InfoFile(), ost.str());
return false;
}
auto fitEnd = sources.cend();
auto fit = sources.begin();
auto oit = options.begin();
while (fit != fitEnd) {
- this->UicOptions[*fit] = std::move(*oit);
+ Uic_.Options[*fit] = std::move(*oit);
++fit;
++oit;
}
@@ -252,54 +1381,51 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
// Initialize source file jobs
{
- // Utility lambdas
- auto AddJob = [this](std::map<std::string, SourceJob>& jobs,
- std::string&& sourceFile) {
- const bool moc = !this->MocSkip(sourceFile);
- const bool uic = !this->UicSkip(sourceFile);
- if (moc || uic) {
- SourceJob& job = jobs[std::move(sourceFile)];
- job.Moc = moc;
- job.Uic = uic;
- }
- };
+ std::hash<std::string> stringHash;
+ std::set<std::size_t> uniqueHeaders;
// Add header jobs
for (std::string& hdr : InfoGetList("AM_HEADERS")) {
- AddJob(this->HeaderJobs, std::move(hdr));
+ const bool moc = !Moc().skipped(hdr);
+ const bool uic = !Uic().skipped(hdr);
+ if ((moc || uic) && uniqueHeaders.emplace(stringHash(hdr)).second) {
+ JobQueues_.Headers.emplace_back(
+ new JobParseT(std::move(hdr), moc, uic, true));
+ }
}
// Add source jobs
{
std::vector<std::string> sources = InfoGetList("AM_SOURCES");
// Add header(s) for the source file
- for (std::string const& src : sources) {
- const bool srcMoc = !this->MocSkip(src);
- const bool srcUic = !this->UicSkip(src);
+ for (std::string& src : sources) {
+ const bool srcMoc = !Moc().skipped(src);
+ const bool srcUic = !Uic().skipped(src);
if (!srcMoc && !srcUic) {
continue;
}
// Search for the default header file and a private header
- std::array<std::string, 2> headerBases;
- headerBases[0] = SubDirPrefix(src);
- headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
- headerBases[1] = headerBases[0];
- headerBases[1] += "_p";
- for (std::string const& headerBase : headerBases) {
- std::string header;
- if (this->FindHeader(header, headerBase)) {
- const bool moc = srcMoc && !this->MocSkip(header);
- const bool uic = srcUic && !this->UicSkip(header);
- if (moc || uic) {
- SourceJob& job = this->HeaderJobs[std::move(header)];
- job.Moc = moc;
- job.Uic = uic;
+ {
+ std::array<std::string, 2> bases;
+ bases[0] = SubDirPrefix(src);
+ bases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
+ bases[1] = bases[0];
+ bases[1] += "_p";
+ for (std::string const& headerBase : bases) {
+ std::string header;
+ if (Base().FindHeader(header, headerBase)) {
+ const bool moc = srcMoc && !Moc().skipped(header);
+ const bool uic = srcUic && !Uic().skipped(header);
+ if ((moc || uic) &&
+ uniqueHeaders.emplace(stringHash(header)).second) {
+ JobQueues_.Headers.emplace_back(
+ new JobParseT(std::move(header), moc, uic, true));
+ }
}
}
}
- }
- // Add Source jobs
- for (std::string& src : sources) {
- AddJob(this->SourceJobs, std::move(src));
+ // Add source job
+ JobQueues_.Sources.emplace_back(
+ new JobParseT(std::move(src), srcMoc, srcUic));
}
}
}
@@ -308,58 +1434,58 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
// ------------------------
// Init file path checksum generator
- this->FilePathChecksum.setupParentDirs(
- this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
- this->ProjectBinaryDir);
+ Base_.FilePathChecksum.setupParentDirs(
+ Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
+ Base().ProjectBinaryDir);
// include directory
- this->AutogenIncludeDir = "include";
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
- this->AutogenIncludeDir += this->ConfigSuffix;
+ Base_.AutogenIncludeDirRel = "include";
+ if (Base().MultiConfig != MultiConfigT::SINGLE) {
+ Base_.AutogenIncludeDirRel += Base().ConfigSuffix;
}
- this->AutogenIncludeDir += "/";
+ Base_.AutogenIncludeDirRel += "/";
+ Base_.AutogenIncludeDirAbs =
+ Base_.AbsoluteBuildPath(Base().AutogenIncludeDirRel);
// Moc variables
- if (this->MocEnabled()) {
+ if (Moc().Enabled) {
// Mocs compilation file
- this->MocCompFileRel = "mocs_compilation";
- if (this->MultiConfig == cmQtAutoGen::FULL) {
- this->MocCompFileRel += this->ConfigSuffix;
+ Moc_.CompFileRel = "mocs_compilation";
+ if (Base_.MultiConfig == MultiConfigT::MULTI) {
+ Moc_.CompFileRel += Base().ConfigSuffix;
}
- this->MocCompFileRel += ".cpp";
- this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, this->MocCompFileRel);
+ Moc_.CompFileRel += ".cpp";
+ Moc_.CompFileAbs = Base_.AbsoluteBuildPath(Moc().CompFileRel);
// Moc predefs file
- if (!this->MocPredefsCmd.empty()) {
- this->MocPredefsFileRel = "moc_predefs";
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
- this->MocPredefsFileRel += this->ConfigSuffix;
+ if (!Moc_.PredefsCmd.empty()) {
+ Moc_.PredefsFileRel = "moc_predefs";
+ if (Base_.MultiConfig != MultiConfigT::SINGLE) {
+ Moc_.PredefsFileRel += Base().ConfigSuffix;
}
- this->MocPredefsFileRel += ".h";
- this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, this->MocPredefsFileRel);
+ Moc_.PredefsFileRel += ".h";
+ Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
}
// Sort include directories on demand
- if (this->IncludeProjectDirsBefore) {
+ if (Base().IncludeProjectDirsBefore) {
// Move strings to temporary list
std::list<std::string> includes;
- includes.insert(includes.end(), this->MocIncludePaths.begin(),
- this->MocIncludePaths.end());
- this->MocIncludePaths.clear();
- this->MocIncludePaths.reserve(includes.size());
+ includes.insert(includes.end(), Moc().IncludePaths.begin(),
+ Moc().IncludePaths.end());
+ Moc_.IncludePaths.clear();
+ Moc_.IncludePaths.reserve(includes.size());
// Append project directories only
{
std::array<std::string const*, 2> const movePaths = {
- { &this->ProjectBinaryDir, &this->ProjectSourceDir }
+ { &Base().ProjectBinaryDir, &Base().ProjectSourceDir }
};
for (std::string const* ppath : movePaths) {
std::list<std::string>::iterator it = includes.begin();
while (it != includes.end()) {
std::string const& path = *it;
if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
- this->MocIncludePaths.push_back(path);
+ Moc_.IncludePaths.push_back(path);
it = includes.erase(it);
} else {
++it;
@@ -368,14 +1494,14 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
}
}
// Append remaining directories
- this->MocIncludePaths.insert(this->MocIncludePaths.end(),
- includes.begin(), includes.end());
+ Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
+ includes.end());
}
// Compose moc includes list
{
std::set<std::string> frameworkPaths;
- for (std::string const& path : this->MocIncludePaths) {
- this->MocIncludes.push_back("-I" + path);
+ for (std::string const& path : Moc().IncludePaths) {
+ Moc_.Includes.push_back("-I" + path);
// Extract framework path
if (cmHasLiteralSuffix(path, ".framework/Headers")) {
// Go up twice to get to the framework root
@@ -388,1346 +1514,511 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
}
// Append framework includes
for (std::string const& path : frameworkPaths) {
- this->MocIncludes.push_back("-F");
- this->MocIncludes.push_back(path);
+ Moc_.Includes.push_back("-F");
+ Moc_.Includes.push_back(path);
}
}
// Setup single list with all options
{
// Add includes
- this->MocAllOptions.insert(this->MocAllOptions.end(),
- this->MocIncludes.begin(),
- this->MocIncludes.end());
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
+ Moc().Includes.end());
// Add definitions
- for (std::string const& def : this->MocDefinitions) {
- this->MocAllOptions.push_back("-D" + def);
+ for (std::string const& def : Moc().Definitions) {
+ Moc_.AllOptions.push_back("-D" + def);
}
// Add options
- this->MocAllOptions.insert(this->MocAllOptions.end(),
- this->MocOptions.begin(),
- this->MocOptions.end());
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
+ Moc().Options.end());
}
}
return true;
}
-void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile)
+bool cmQtAutoGeneratorMocUic::Process()
+{
+ // Run libuv event loop
+ UVRequest().send();
+ if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
+ if (JobError_) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle)
+{
+ reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage();
+}
+
+void cmQtAutoGeneratorMocUic::PollStage()
+{
+ switch (Stage_) {
+ case StageT::SETTINGS_READ:
+ SettingsFileRead();
+ SetStage(StageT::CREATE_DIRECTORIES);
+ break;
+ case StageT::CREATE_DIRECTORIES:
+ CreateDirectories();
+ SetStage(StageT::PARSE_SOURCES);
+ break;
+ case StageT::PARSE_SOURCES:
+ if (ThreadsStartJobs(JobQueues_.Sources)) {
+ SetStage(StageT::PARSE_HEADERS);
+ }
+ break;
+ case StageT::PARSE_HEADERS:
+ if (ThreadsStartJobs(JobQueues_.Headers)) {
+ SetStage(StageT::MOC_PREDEFS);
+ }
+ break;
+ case StageT::MOC_PREDEFS:
+ if (ThreadsStartJobs(JobQueues_.MocPredefs)) {
+ SetStage(StageT::MOC_PROCESS);
+ }
+ break;
+ case StageT::MOC_PROCESS:
+ if (ThreadsStartJobs(JobQueues_.Moc)) {
+ SetStage(StageT::MOCS_COMPILATION);
+ }
+ break;
+ case StageT::MOCS_COMPILATION:
+ if (ThreadsJobsDone()) {
+ MocGenerateCompilation();
+ SetStage(StageT::UIC_PROCESS);
+ }
+ break;
+ case StageT::UIC_PROCESS:
+ if (ThreadsStartJobs(JobQueues_.Uic)) {
+ SetStage(StageT::SETTINGS_WRITE);
+ }
+ break;
+ case StageT::SETTINGS_WRITE:
+ SettingsFileWrite();
+ SetStage(StageT::FINISH);
+ break;
+ case StageT::FINISH:
+ if (ThreadsJobsDone()) {
+ // Clear all libuv handles
+ ThreadsStop();
+ UVRequest().reset();
+ // Set highest END stage manually
+ Stage_ = StageT::END;
+ }
+ break;
+ case StageT::END:
+ break;
+ }
+}
+
+void cmQtAutoGeneratorMocUic::SetStage(StageT stage)
+{
+ if (JobError_) {
+ stage = StageT::FINISH;
+ }
+ // Only allow to increase the stage
+ if (Stage_ < stage) {
+ Stage_ = stage;
+ UVRequest().send();
+ }
+}
+
+void cmQtAutoGeneratorMocUic::SettingsFileRead()
{
// Compose current settings strings
{
cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
std::string const sep(" ~~~ ");
- if (this->MocEnabled()) {
+ if (Moc_.Enabled) {
std::string str;
- str += this->MocExecutable;
+ str += Moc().Executable;
str += sep;
- str += cmJoin(this->MocAllOptions, ";");
+ str += cmJoin(Moc().AllOptions, ";");
str += sep;
- str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
+ str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
str += sep;
- str += cmJoin(this->MocPredefsCmd, ";");
+ str += cmJoin(Moc().PredefsCmd, ";");
str += sep;
- this->SettingsStringMoc = crypt.HashString(str);
+ SettingsStringMoc_ = crypt.HashString(str);
}
- if (this->UicEnabled()) {
+ if (Uic().Enabled) {
std::string str;
- str += this->UicExecutable;
+ str += Uic().Executable;
str += sep;
- str += cmJoin(this->UicTargetOptions, ";");
- for (const auto& item : this->UicOptions) {
+ str += cmJoin(Uic().TargetOptions, ";");
+ for (const auto& item : Uic().Options) {
str += sep;
str += item.first;
str += sep;
str += cmJoin(item.second, ";");
}
str += sep;
- this->SettingsStringUic = crypt.HashString(str);
+ SettingsStringUic_ = crypt.HashString(str);
}
}
- // Read old settings
- if (makefile->ReadListFile(this->SettingsFile.c_str())) {
- {
- auto SMatch = [makefile](const char* key, std::string const& value) {
- return (value == makefile->GetSafeDefinition(key));
- };
- if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) {
- this->MocSettingsChanged = true;
+ // Read old settings and compare
+ {
+ std::string content;
+ if (FileSys().FileRead(content, SettingsFile_)) {
+ if (Moc().Enabled) {
+ if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
+ Moc_.SettingsChanged = true;
+ }
}
- if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) {
- this->UicSettingsChanged = true;
+ if (Uic().Enabled) {
+ if (SettingsStringUic_ != SettingsFind(content, "uic")) {
+ Uic_.SettingsChanged = true;
+ }
+ }
+ // In case any setting changed remove the old settings file.
+ // This triggers a full rebuild on the next run if the current
+ // build is aborted before writing the current settings in the end.
+ if (Moc().SettingsChanged || Uic().SettingsChanged) {
+ FileSys().FileRemove(SettingsFile_);
+ }
+ } else {
+ // Settings file read failed
+ if (Moc().Enabled) {
+ Moc_.SettingsChanged = true;
+ }
+ if (Uic().Enabled) {
+ Uic_.SettingsChanged = true;
}
}
- // In case any setting changed remove the old settings file.
- // This triggers a full rebuild on the next run if the current
- // build is aborted before writing the current settings in the end.
- if (this->SettingsChanged()) {
- cmSystemTools::RemoveFile(this->SettingsFile);
- }
- } else {
- // If the file could not be read re-generate everythiung.
- this->MocSettingsChanged = true;
- this->UicSettingsChanged = true;
}
}
-bool cmQtAutoGeneratorMocUic::SettingsFileWrite()
+void cmQtAutoGeneratorMocUic::SettingsFileWrite()
{
- bool success = true;
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
// Only write if any setting changed
- if (this->SettingsChanged()) {
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " +
- cmQtAutoGen::Quoted(this->SettingsFile));
+ if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) {
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::GEN,
+ "Writing settings file " + Quoted(SettingsFile_));
}
// Compose settings file content
- std::string settings;
+ std::string content;
{
- auto SettingAppend = [&settings](const char* key,
- std::string const& value) {
- settings += "set(";
- settings += key;
- settings += " ";
- settings += cmOutputConverter::EscapeForCMake(value);
- settings += ")\n";
+ auto SettingAppend = [&content](const char* key,
+ std::string const& value) {
+ if (!value.empty()) {
+ content += key;
+ content += ':';
+ content += value;
+ content += '\n';
+ }
};
- SettingAppend(SettingsKeyMoc, this->SettingsStringMoc);
- SettingAppend(SettingsKeyUic, this->SettingsStringUic);
+ SettingAppend("moc", SettingsStringMoc_);
+ SettingAppend("uic", SettingsStringUic_);
}
// Write settings file
- if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) {
- this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile,
- "Settings file writing failed");
+ if (!FileSys().FileWrite(GeneratorT::GEN, SettingsFile_, content)) {
+ Log().ErrorFile(GeneratorT::GEN, SettingsFile_,
+ "Settings file writing failed");
// Remove old settings file to trigger a full rebuild on the next run
- cmSystemTools::RemoveFile(this->SettingsFile);
- success = false;
+ FileSys().FileRemove(SettingsFile_);
+ RegisterJobError();
}
}
- return success;
}
-bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile)
+void cmQtAutoGeneratorMocUic::CreateDirectories()
{
- // the program goes through all .cpp files to see which moc files are
- // included. It is not really interesting how the moc file is named, but
- // what file the moc is created from. Once a moc is included the same moc
- // may not be included in the mocs_compilation.cpp file anymore.
- // OTOH if there's a header containing Q_OBJECT where no corresponding
- // moc file is included anywhere a moc_<filename>.cpp file is created and
- // included in the mocs_compilation.cpp file.
-
- if (!this->InitInfoFile(makefile)) {
- return false;
- }
- // Read latest settings
- this->SettingsFileRead(makefile);
-
// Create AUTOGEN include directory
- {
- std::string const incDirAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, this->AutogenIncludeDir);
- if (!cmSystemTools::MakeDirectory(incDirAbs)) {
- this->LogFileError(cmQtAutoGen::GEN, incDirAbs,
- "Could not create directory");
- return false;
- }
- }
-
- // Parse source files
- for (const auto& item : this->SourceJobs) {
- if (!this->ParseSourceFile(item.first, item.second)) {
- return false;
- }
- }
- // Parse header files
- for (const auto& item : this->HeaderJobs) {
- if (!this->ParseHeaderFile(item.first, item.second)) {
- return false;
- }
- }
- // Read missing dependency information
- if (!this->ParsePostprocess()) {
- return false;
+ if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDirAbs)) {
+ RegisterJobError();
}
-
- // Generate files
- if (!this->MocGenerateAll()) {
- return false;
- }
- if (!this->UicGenerateAll()) {
- return false;
- }
-
- if (!this->SettingsFileWrite()) {
- return false;
- }
-
- return true;
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename,
- const SourceJob& job)
+bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
{
- std::string contentText;
- std::string error;
- bool success = this->FileRead(contentText, absFilename, &error);
- if (success) {
- if (!contentText.empty()) {
- if (job.Moc) {
- success = this->MocParseSourceContent(absFilename, contentText);
- }
- if (success && job.Uic) {
- success = this->UicParseContent(absFilename, contentText);
+ bool done = false;
+ std::size_t queueSize = queue.size();
+
+ // Change the active queue
+ {
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ // Check if there are still unfinished jobs from the previous queue
+ if (JobsRemain_ == 0) {
+ if (!JobThreadsAbort_) {
+ JobQueue_.swap(queue);
+ JobsRemain_ = queueSize;
+ } else {
+ // Abort requested
+ queue.clear();
+ queueSize = 0;
}
- } else {
- this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
- "The source file is empty");
+ done = true;
}
- } else {
- this->LogFileError(cmQtAutoGen::GEN, absFilename,
- "Could not read the source file: " + error);
}
- return success;
-}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename,
- const SourceJob& job)
-{
- std::string contentText;
- std::string error;
- bool success = this->FileRead(contentText, absFilename, &error);
- if (success) {
- if (!contentText.empty()) {
- if (job.Moc) {
- this->MocParseHeaderContent(absFilename, contentText);
- }
- if (job.Uic) {
- success = this->UicParseContent(absFilename, contentText);
+ if (done && (queueSize != 0)) {
+ // Start new threads on demand
+ if (Workers_.empty()) {
+ Workers_.resize(Base().NumThreads);
+ for (auto& item : Workers_) {
+ item = cm::make_unique<WorkerT>(this, UVLoop());
}
} else {
- this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
- "The header file is empty");
- }
- } else {
- this->LogFileError(cmQtAutoGen::GEN, absFilename,
- "Could not read the header file: " + error);
- }
- return success;
-}
-
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::ParsePostprocess()
-{
- bool success = true;
- // Read missing dependencies
- for (auto& item : this->MocJobsIncluded) {
- if (!item->DependsValid) {
- std::string content;
- std::string error;
- if (this->FileRead(content, item->SourceFile, &error)) {
- this->MocFindDepends(item->SourceFile, content, item->Depends);
- item->DependsValid = true;
+ // Notify threads
+ if (queueSize == 1) {
+ JobsConditionRead_.notify_one();
} else {
- std::string emsg = "Could not read file\n ";
- emsg += item->SourceFile;
- emsg += "\nrequired by moc include \"";
- emsg += item->IncludeString;
- emsg += "\".\n";
- emsg += error;
- this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg);
- success = false;
- break;
+ JobsConditionRead_.notify_all();
}
}
}
- return success;
-}
-
-/**
- * @brief Tests if the file should be ignored for moc scanning
- * @return True if the file should be ignored
- */
-bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const
-{
- if (this->MocEnabled()) {
- // Test if the file name is on the skip list
- if (!ListContains(this->MocSkipList, absFilename)) {
- return false;
- }
- }
- return true;
-}
-/**
- * @brief Tests if the C++ content requires moc processing
- * @return True if moc is required
- */
-bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText,
- std::string* macroName)
-{
- for (KeyRegExp& filter : this->MocMacroFilters) {
- // Run a simple find string operation before the expensive
- // regular expression check
- if (contentText.find(filter.Key) != std::string::npos) {
- if (filter.RegExp.find(contentText)) {
- // Return macro name on demand
- if (macroName != nullptr) {
- *macroName = filter.Key;
- }
- return true;
- }
- }
- }
- return false;
+ return done;
}
-std::string cmQtAutoGeneratorMocUic::MocStringMacros() const
+void cmQtAutoGeneratorMocUic::ThreadsStop()
{
- std::string res;
- const auto itB = this->MocMacroFilters.cbegin();
- const auto itE = this->MocMacroFilters.cend();
- const auto itL = itE - 1;
- auto itC = itB;
- for (; itC != itE; ++itC) {
- // Separator
- if (itC != itB) {
- if (itC != itL) {
- res += ", ";
- } else {
- res += " or ";
- }
- }
- // Key
- res += itC->Key;
+ if (!Workers_.empty()) {
+ // Clear all jobs
+ {
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ JobThreadsAbort_ = true;
+ JobsRemain_ -= JobQueue_.size();
+ JobQueue_.clear();
+
+ JobQueues_.Sources.clear();
+ JobQueues_.Headers.clear();
+ JobQueues_.MocPredefs.clear();
+ JobQueues_.Moc.clear();
+ JobQueues_.Uic.clear();
+ }
+ // Wake threads
+ JobsConditionRead_.notify_all();
+ // Join and clear threads
+ Workers_.clear();
}
- return res;
}
-std::string cmQtAutoGeneratorMocUic::MocStringHeaders(
- std::string const& fileBase) const
+bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
{
- std::string res = fileBase;
- res += ".{";
- res += cmJoin(this->HeaderExtensions, ",");
- res += "}";
- return res;
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ return (JobsRemain_ == 0);
}
-std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader(
- std::string const& sourcePath, std::string const& includeBase) const
+void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
{
- std::string header;
- // Search in vicinity of the source
- if (!this->FindHeader(header, sourcePath + includeBase)) {
- // Search in include directories
- for (std::string const& path : this->MocIncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeBase;
- if (this->FindHeader(header, fullPath)) {
- break;
- }
- }
- }
- // Sanitize
- if (!header.empty()) {
- header = cmSystemTools::GetRealPath(header);
+ bool const jobProcessed(jobHandle);
+ if (jobProcessed) {
+ jobHandle.reset(nullptr);
}
- return header;
-}
-
-bool cmQtAutoGeneratorMocUic::MocFindIncludedFile(
- std::string& absFile, std::string const& sourcePath,
- std::string const& includeString) const
-{
- bool success = false;
- // Search in vicinity of the source
{
- std::string testPath = sourcePath;
- testPath += includeString;
- if (cmSystemTools::FileExists(testPath.c_str())) {
- absFile = cmSystemTools::GetRealPath(testPath);
- success = true;
- }
- }
- // Search in include directories
- if (!success) {
- for (std::string const& path : this->MocIncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeString;
- if (cmSystemTools::FileExists(fullPath.c_str())) {
- absFile = cmSystemTools::GetRealPath(fullPath);
- success = true;
- break;
+ std::unique_lock<std::mutex> jobsLock(JobsMutex_);
+ // Reduce the remaining job count and notify the libuv loop
+ // when all jobs are done
+ if (jobProcessed) {
+ --JobsRemain_;
+ if (JobsRemain_ == 0) {
+ UVRequest().send();
}
}
- }
- return success;
-}
-
-bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key,
- std::string const& regExp)
-{
- std::string error;
- if (!key.empty()) {
- if (!regExp.empty()) {
- KeyRegExp filter;
- filter.Key = key;
- if (filter.RegExp.compile(regExp)) {
- this->MocDependFilters.push_back(std::move(filter));
- } else {
- error = "Regular expression compiling failed";
- }
- } else {
- error = "Regular expression is empty";
- }
- } else {
- error = "Key is empty";
- }
- if (!error.empty()) {
- std::string emsg = "AUTOMOC_DEPEND_FILTERS: ";
- emsg += error;
- emsg += "\n";
- emsg += " Key: ";
- emsg += cmQtAutoGen::Quoted(key);
- emsg += "\n";
- emsg += " RegExp: ";
- emsg += cmQtAutoGen::Quoted(regExp);
- emsg += "\n";
- this->LogError(cmQtAutoGen::MOC, emsg);
- return false;
- }
- return true;
-}
-
-void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename,
- std::string const& contentText,
- std::set<std::string>& depends)
-{
- if (this->MocDependFilters.empty() && contentText.empty()) {
- return;
- }
-
- std::vector<std::string> matches;
- for (KeyRegExp& filter : this->MocDependFilters) {
- // Run a simple find string check
- if (contentText.find(filter.Key) != std::string::npos) {
- // Run the expensive regular expression check loop
- const char* contentChars = contentText.c_str();
- while (filter.RegExp.find(contentChars)) {
- std::string match = filter.RegExp.match(1);
- if (!match.empty()) {
- matches.emplace_back(std::move(match));
- }
- contentChars += filter.RegExp.end();
- }
+ // Wait for new jobs
+ while (!JobThreadsAbort_ && JobQueue_.empty()) {
+ JobsConditionRead_.wait(jobsLock);
}
- }
-
- if (!matches.empty()) {
- std::string const sourcePath = SubDirPrefix(absFilename);
- for (std::string const& match : matches) {
- // Find the dependency file
- std::string incFile;
- if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
- depends.insert(incFile);
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " +
- cmQtAutoGen::Quoted(absFilename) + "\n " +
- cmQtAutoGen::Quoted(incFile));
- }
- } else {
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
- "Could not find dependency file " +
- cmQtAutoGen::Quoted(match));
- }
+ // Try to pick up a new job handle
+ if (!JobThreadsAbort_ && !JobQueue_.empty()) {
+ jobHandle = std::move(JobQueue_.front());
+ JobQueue_.pop_front();
}
}
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::MocParseSourceContent(
- std::string const& absFilename, std::string const& contentText)
+void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
{
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
- }
-
- auto AddJob = [this, &absFilename](std::string const& sourceFile,
- std::string const& includeString,
- std::string const* content) {
- auto job = cm::make_unique<MocJobIncluded>();
- job->SourceFile = sourceFile;
- job->BuildFileRel = this->AutogenIncludeDir;
- job->BuildFileRel += includeString;
- job->Includer = absFilename;
- job->IncludeString = includeString;
- job->DependsValid = (content != nullptr);
- if (job->DependsValid) {
- this->MocFindDepends(sourceFile, *content, job->Depends);
- }
- this->MocJobsIncluded.push_back(std::move(job));
- };
-
- struct MocInc
- {
- std::string Inc; // full include string
- std::string Dir; // include string directory
- std::string Base; // include string file base
- };
-
- // Extract moc includes from file
- std::vector<MocInc> mocIncsUsc;
- std::vector<MocInc> mocIncsDot;
- {
- const char* contentChars = contentText.c_str();
- if (strstr(contentChars, "moc") != nullptr) {
- while (this->MocRegExpInclude.find(contentChars)) {
- std::string incString = this->MocRegExpInclude.match(1);
- std::string incDir(SubDirPrefix(incString));
- std::string incBase =
- cmSystemTools::GetFilenameWithoutLastExtension(incString);
- if (cmHasLiteralPrefix(incBase, "moc_")) {
- // moc_<BASE>.cxx
- // Remove the moc_ part from the base name
- mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir),
- incBase.substr(4) });
- } else {
- // <BASE>.moc
- mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir),
- std::move(incBase) });
- }
- // Forward content pointer
- contentChars += this->MocRegExpInclude.end();
- }
- }
- }
-
- std::string selfMacroName;
- const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName);
-
- // Check if there is anything to do
- if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) {
- return true;
- }
-
- // Scan file variables
- std::string const scanFileDir = SubDirPrefix(absFilename);
- std::string const scanFileBase =
- cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
- // Relaxed mode variables
- bool ownDotMocIncluded = false;
- std::string ownMocUscInclude;
- std::string ownMocUscHeader;
-
- // Process moc_<BASE>.cxx includes
- for (const MocInc& mocInc : mocIncsUsc) {
- std::string const header =
- this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
- if (!header.empty()) {
- // Check if header is skipped
- if (this->MocSkip(header)) {
- continue;
- }
- // Register moc job
- AddJob(header, mocInc.Inc, nullptr);
- // Store meta information for relaxed mode
- if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) {
- ownMocUscInclude = mocInc.Inc;
- ownMocUscHeader = header;
- }
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", but could not find the header ";
- emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base));
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
-
- // Process <BASE>.moc includes
- for (const MocInc& mocInc : mocIncsDot) {
- const bool ownMoc = (mocInc.Base == scanFileBase);
- if (this->MocRelaxedMode) {
- // Relaxed mode
- if (selfRequiresMoc && ownMoc) {
- // Add self
- AddJob(absFilename, mocInc.Inc, &contentText);
- ownDotMocIncluded = true;
- } else {
- // In relaxed mode try to find a header instead but issue a warning.
- // This is for KDE4 compatibility
- std::string const header =
- this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
- if (!header.empty()) {
- // Check if header is skipped
- if (this->MocSkip(header)) {
- continue;
- }
- // Register moc job
- AddJob(header, mocInc.Inc, nullptr);
- if (!selfRequiresMoc) {
- if (ownMoc) {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", but does not contain a ";
- emsg += this->MocStringMacros();
- emsg += " macro.\nRunning moc on\n ";
- emsg += cmQtAutoGen::Quoted(header);
- emsg += "!\nBetter include ";
- emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for a compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += " instead of ";
- emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += ".\nRunning moc on\n ";
- emsg += cmQtAutoGen::Quoted(header);
- emsg += "!\nBetter include ";
- emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
- }
- }
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file. CMake also could not find a matching "
- "header.";
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
- } else {
- // Strict mode
- if (ownMoc) {
- // Include self
- AddJob(absFilename, mocInc.Inc, &contentText);
- ownDotMocIncluded = true;
- // Accept but issue a warning if moc isn't required
- if (!selfRequiresMoc) {
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", but does not contain a ";
- emsg += this->MocStringMacros();
- emsg += " macro.";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
- }
- } else {
- // Don't allow <BASE>.moc include other than self in strict mode
- std::string emsg = "The file includes the moc file ";
- emsg += cmQtAutoGen::Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file.\nThis is not supported. Include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += " to run moc on this source file.";
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
- }
-
- if (selfRequiresMoc && !ownDotMocIncluded) {
- // In this case, check whether the scanned file itself contains a Q_OBJECT.
- // If this is the case, the moc_foo.cpp should probably be generated from
- // foo.cpp instead of foo.h, because otherwise it won't build.
- // But warn, since this is not how it is supposed to be used.
- if (this->MocRelaxedMode && !ownMocUscInclude.empty()) {
- // This is for KDE4 compatibility:
- std::string emsg = "The file contains a ";
- emsg += selfMacroName;
- emsg += " macro, but does not include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += ". Instead it includes ";
- emsg += cmQtAutoGen::Quoted(ownMocUscInclude);
- emsg += ".\nRunning moc on\n ";
- emsg += cmQtAutoGen::Quoted(absFilename);
- emsg += "!\nBetter include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
- this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
-
- // Remove own header job
- {
- auto itC = this->MocJobsIncluded.begin();
- auto itE = this->MocJobsIncluded.end();
- for (; itC != itE; ++itC) {
- if ((*itC)->SourceFile == ownMocUscHeader) {
- if ((*itC)->IncludeString == ownMocUscInclude) {
- this->MocJobsIncluded.erase(itC);
- break;
- }
- }
- }
- }
- // Add own source job
- AddJob(absFilename, ownMocUscInclude, &contentText);
- } else {
- // Otherwise always error out since it will not compile:
- std::string emsg = "The file contains a ";
- emsg += selfMacroName;
- emsg += " macro, but does not include ";
- emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
- emsg += "!\nConsider to\n - add #include \"";
- emsg += scanFileBase;
- emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
- this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
- return false;
- }
- }
- return true;
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ RegisterJobError();
}
-void cmQtAutoGeneratorMocUic::MocParseHeaderContent(
- std::string const& absFilename, std::string const& contentText)
+// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
+// locked
+void cmQtAutoGeneratorMocUic::RegisterJobError()
{
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
- }
-
- auto const fit =
- std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(),
- [&absFilename](std::unique_ptr<MocJobIncluded> const& job) {
- return job->SourceFile == absFilename;
- });
- if (fit == this->MocJobsIncluded.cend()) {
- if (this->MocRequired(contentText)) {
- auto job = cm::make_unique<MocJobAuto>();
- job->SourceFile = absFilename;
- {
- std::string& bld = job->BuildFileRel;
- bld = this->FilePathChecksum.getPart(absFilename);
- bld += '/';
- bld += "moc_";
- bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
- if (this->MultiConfig != cmQtAutoGen::SINGLE) {
- bld += this->ConfigSuffix;
- }
- bld += ".cpp";
- }
- this->MocFindDepends(absFilename, contentText, job->Depends);
- this->MocJobsAuto.push_back(std::move(job));
+ JobError_ = true;
+ if (!JobThreadsAbort_) {
+ JobThreadsAbort_ = true;
+ // Clear remaining jobs
+ if (JobsRemain_ != 0) {
+ JobsRemain_ -= JobQueue_.size();
+ JobQueue_.clear();
}
}
}
-bool cmQtAutoGeneratorMocUic::MocGenerateAll()
+bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle)
{
- if (!this->MocEnabled()) {
- return true;
- }
-
- // Look for name collisions in included moc files
- {
- bool collision = false;
- std::map<std::string, std::vector<MocJobIncluded const*>> collisions;
- for (auto const& job : this->MocJobsIncluded) {
- auto& list = collisions[job->IncludeString];
- if (!list.empty()) {
- collision = true;
- }
- list.push_back(job.get());
- }
- if (collision) {
- std::string emsg =
- "Included moc files with the same name will be "
- "generated from different sources.\n"
- "Consider to\n"
- " - not include the \"moc_<NAME>.cpp\" file\n"
- " - add a directory prefix to a \"<NAME>.moc\" include "
- "(e.g \"sub/<NAME>.moc\")\n"
- " - rename the source file(s)\n"
- "Include conflicts\n"
- "-----------------\n";
- const auto& colls = collisions;
- for (auto const& coll : colls) {
- if (coll.second.size() > 1) {
- emsg += cmQtAutoGen::Quoted(coll.first);
- emsg += " included in\n";
- for (const MocJobIncluded* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->Includer);
- emsg += "\n";
- }
- emsg += "would be generated from\n";
- for (const MocJobIncluded* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->SourceFile);
- emsg += "\n";
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ if (!JobThreadsAbort_) {
+ bool pushJobHandle = true;
+ // Do additional tests if this is an included moc job
+ const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle));
+ if (!mocJob.IncludeString.empty()) {
+ // Register included moc file and look for collisions
+ MocIncludedFiles_.emplace(mocJob.SourceFile);
+ if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) {
+ // Another source file includes the same moc file!
+ for (const JobHandleT& otherHandle : JobQueues_.Moc) {
+ const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle));
+ if (otherJob.IncludeString == mocJob.IncludeString) {
+ // Check if the same moc file would be generated from different
+ // source files which is an error.
+ if (otherJob.SourceFile != mocJob.SourceFile) {
+ // Include string collision
+ std::string error = "The two source files\n ";
+ error += Quoted(mocJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(otherJob.IncluderFile);
+ error += "\ncontain the the same moc include string ";
+ error += Quoted(mocJob.IncludeString);
+ error += "\nbut the moc file would be generated from different "
+ "source files\n ";
+ error += Quoted(mocJob.SourceFile);
+ error += " and\n ";
+ error += Quoted(otherJob.SourceFile);
+ error += ".\nConsider to\n"
+ "- not include the \"moc_<NAME>.cpp\" file\n"
+ "- add a directory prefix to a \"<NAME>.moc\" include "
+ "(e.g \"sub/<NAME>.moc\")\n"
+ "- rename the source file(s)\n";
+ Log().Error(GeneratorT::MOC, error);
+ RegisterJobError();
+ }
+ // Do not push this job in since the included moc file already
+ // gets generated by an other job.
+ pushJobHandle = false;
+ break;
}
}
}
- this->LogError(cmQtAutoGen::MOC, emsg);
- return false;
}
- }
-
- // (Re)generate moc_predefs.h on demand
- if (!this->MocPredefsCmd.empty()) {
- if (this->MocSettingsChanged ||
- !cmSystemTools::FileExists(this->MocPredefsFileAbs)) {
- if (this->GetVerbose()) {
- this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
- }
-
- std::string output;
- {
- // Compose command
- std::vector<std::string> cmd = this->MocPredefsCmd;
- // Add includes
- cmd.insert(cmd.end(), this->MocIncludes.begin(),
- this->MocIncludes.end());
- // Add definitions
- for (std::string const& def : this->MocDefinitions) {
- cmd.push_back("-D" + def);
- }
- // Execute command
- if (!this->RunCommand(cmd, output)) {
- this->LogCommandError(cmQtAutoGen::MOC,
- "moc_predefs generation failed", cmd, output);
- return false;
- }
- }
-
- // (Re)write predefs file only on demand
- if (this->FileDiffers(this->MocPredefsFileAbs, output)) {
- if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
- output)) {
- this->MocPredefsChanged = true;
- } else {
- this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
- "moc_predefs file writing failed");
- return false;
- }
- } else {
- // Touch to update the time stamp
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC,
- "Touching moc_predefs " + this->MocPredefsFileRel);
- }
- cmSystemTools::Touch(this->MocPredefsFileAbs, false);
- }
- }
-
- // Add moc_predefs.h to moc file dependencies
- for (auto const& item : this->MocJobsIncluded) {
- item->Depends.insert(this->MocPredefsFileAbs);
- }
- for (auto const& item : this->MocJobsAuto) {
- item->Depends.insert(this->MocPredefsFileAbs);
- }
- }
-
- // Generate moc files that are included by source files.
- for (auto const& item : this->MocJobsIncluded) {
- if (!this->MocGenerateFile(*item)) {
- return false;
- }
- }
- // Generate moc files that are _not_ included by source files.
- bool autoNameGenerated = false;
- for (auto const& item : this->MocJobsAuto) {
- if (!this->MocGenerateFile(*item, &autoNameGenerated)) {
- return false;
- }
- }
-
- // Compose mocs compilation file content
- {
- std::string mocs =
- "// This file is autogenerated. Changes will be overwritten.\n";
- if (this->MocJobsAuto.empty()) {
- // Placeholder content
- mocs +=
- "// No files found that require moc or the moc files are included\n";
- mocs += "enum some_compilers { need_more_than_nothing };\n";
- } else {
- // Valid content
- for (const auto& item : this->MocJobsAuto) {
- mocs += "#include \"";
- mocs += item->BuildFileRel;
- mocs += "\"\n";
- }
- }
-
- if (this->FileDiffers(this->MocCompFileAbs, mocs)) {
- // Actually write mocs compilation file
- if (this->GetVerbose()) {
- this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
- }
- if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) {
- this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs,
- "mocs compilation file writing failed");
- return false;
- }
- } else if (autoNameGenerated) {
- // Only touch mocs compilation file
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::MOC,
- "Touching mocs compilation " + this->MocCompFileRel);
- }
- cmSystemTools::Touch(this->MocCompFileAbs, false);
+ // Push job on demand
+ if (pushJobHandle) {
+ JobQueues_.Moc.emplace_back(std::move(jobHandle));
}
}
-
- return true;
+ return !JobError_;
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob,
- bool* generated)
+bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle)
{
- bool success = true;
-
- std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, mocJob.BuildFileRel);
-
- bool generate = false;
- std::string generateReason;
- if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from its source file ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because it doesn't exist";
- }
- generate = true;
- }
- if (!generate && this->MocSettingsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because the MOC settings changed";
- }
- generate = true;
- }
- if (!generate && this->MocPredefsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because moc_predefs.h changed";
- }
- generate = true;
- }
- if (!generate) {
- std::string error;
- if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " because it's older than its source file ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- }
- generate = true;
- } else {
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::MOC, error);
- success = false;
- }
- }
- }
- if (success && !generate) {
- // Test if a dependency file is newer
- std::string error;
- for (std::string const& depFile : mocJob.Depends) {
- if (FileIsOlderThan(mocFileAbs, depFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(mocFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
- generateReason += " because it is older than ";
- generateReason += cmQtAutoGen::Quoted(depFile);
+ std::lock_guard<std::mutex> jobsLock(JobsMutex_);
+ if (!JobThreadsAbort_) {
+ bool pushJobHandle = true;
+ // Look for include collisions.
+ const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
+ for (const JobHandleT& otherHandle : JobQueues_.Uic) {
+ const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle));
+ if (otherJob.IncludeString == uicJob.IncludeString) {
+ // Check if the same uic file would be generated from different
+ // source files which would be an error.
+ if (otherJob.SourceFile != uicJob.SourceFile) {
+ // Include string collision
+ std::string error = "The two source files\n ";
+ error += Quoted(uicJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(otherJob.IncluderFile);
+ error += "\ncontain the the same uic include string ";
+ error += Quoted(uicJob.IncludeString);
+ error += "\nbut the uic file would be generated from different "
+ "source files\n ";
+ error += Quoted(uicJob.SourceFile);
+ error += " and\n ";
+ error += Quoted(otherJob.SourceFile);
+ error +=
+ ".\nConsider to\n"
+ "- add a directory prefix to a \"ui_<NAME>.h\" include "
+ "(e.g \"sub/ui_<NAME>.h\")\n"
+ "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
+ "include(s)\n";
+ Log().Error(GeneratorT::UIC, error);
+ RegisterJobError();
}
- generate = true;
- break;
- }
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::MOC, error);
- success = false;
+ // Do not push this job in since the uic file already
+ // gets generated by an other job.
+ pushJobHandle = false;
break;
}
}
- }
-
- if (generate) {
- // Log
- if (this->GetVerbose()) {
- this->LogBold("Generating MOC source " + mocJob.BuildFileRel);
- this->LogInfo(cmQtAutoGen::MOC, generateReason);
- }
-
- // Make sure the parent directory exists
- if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) {
- // Compose moc command
- std::vector<std::string> cmd;
- cmd.push_back(this->MocExecutable);
- // Add options
- cmd.insert(cmd.end(), this->MocAllOptions.begin(),
- this->MocAllOptions.end());
- // Add predefs include
- if (!this->MocPredefsFileAbs.empty()) {
- cmd.push_back("--include");
- cmd.push_back(this->MocPredefsFileAbs);
- }
- cmd.push_back("-o");
- cmd.push_back(mocFileAbs);
- cmd.push_back(mocJob.SourceFile);
-
- // Execute moc command
- std::string output;
- if (this->RunCommand(cmd, output)) {
- // Success
- if (generated != nullptr) {
- *generated = true;
- }
- } else {
- // Moc command failed
- {
- std::string emsg = "moc failed for\n ";
- emsg += cmQtAutoGen::Quoted(mocJob.SourceFile);
- this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output);
- }
- cmSystemTools::RemoveFile(mocFileAbs);
- success = false;
- }
- } else {
- // Parent directory creation failed
- success = false;
+ if (pushJobHandle) {
+ JobQueues_.Uic.emplace_back(std::move(jobHandle));
}
}
- return success;
+ return !JobError_;
}
-/**
- * @brief Tests if the file name is in the skip list
- */
-bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const
+bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
+ std::string const& sourceFile)
{
- if (this->UicEnabled()) {
- // Test if the file name is on the skip list
- if (!ListContains(this->UicSkipList, absFilename)) {
- return false;
- }
- }
- return true;
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
}
-bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename,
- std::string const& contentText)
+void cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
+ std::string const& mocFile)
{
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename);
- }
-
- std::vector<std::string> includes;
- // Extracte includes
- {
- const char* contentChars = contentText.c_str();
- if (strstr(contentChars, "ui_") != nullptr) {
- while (this->UicRegExpInclude.find(contentChars)) {
- includes.push_back(this->UicRegExpInclude.match(1));
- contentChars += this->UicRegExpInclude.end();
- }
- }
- }
-
- for (std::string const& includeString : includes) {
- std::string uiInputFile;
- if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) {
- return false;
- }
- // Check if this file should be skipped
- if (this->UicSkip(uiInputFile)) {
- continue;
- }
- // Check if the job already exists
- bool jobExists = false;
- for (const auto& job : this->UicJobs) {
- if ((job->SourceFile == uiInputFile) &&
- (job->IncludeString == includeString)) {
- jobExists = true;
- break;
- }
- }
- if (!jobExists) {
- auto job = cm::make_unique<UicJob>();
- job->SourceFile = uiInputFile;
- job->BuildFileRel = this->AutogenIncludeDir;
- job->BuildFileRel += includeString;
- job->Includer = absFilename;
- job->IncludeString = includeString;
- this->UicJobs.push_back(std::move(job));
- }
- }
-
- return true;
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ MocAutoFiles_.emplace(mocFile);
}
-bool cmQtAutoGeneratorMocUic::UicFindIncludedFile(
- std::string& absFile, std::string const& sourceFile,
- std::string const& includeString)
+void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
{
- bool success = false;
- std::string searchFile =
- cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
- searchFile += ".ui";
- // Collect search paths list
- std::vector<std::string> testFiles;
- {
- std::string const searchPath = SubDirPrefix(includeString);
-
- std::string searchFileFull;
- if (!searchPath.empty()) {
- searchFileFull = searchPath;
- searchFileFull += searchFile;
- }
- // Vicinity of the source
- {
- std::string const sourcePath = SubDirPrefix(sourceFile);
- testFiles.push_back(sourcePath + searchFile);
- if (!searchPath.empty()) {
- testFiles.push_back(sourcePath + searchFileFull);
- }
- }
- // AUTOUIC search paths
- if (!this->UicSearchPaths.empty()) {
- for (std::string const& sPath : this->UicSearchPaths) {
- testFiles.push_back((sPath + "/").append(searchFile));
- }
- if (!searchPath.empty()) {
- for (std::string const& sPath : this->UicSearchPaths) {
- testFiles.push_back((sPath + "/").append(searchFileFull));
- }
- }
- }
- }
-
- // Search for the .ui file!
- for (std::string const& testFile : testFiles) {
- if (cmSystemTools::FileExists(testFile.c_str())) {
- absFile = cmSystemTools::GetRealPath(testFile);
- success = true;
- break;
- }
- }
-
- // Log error
- if (!success) {
- std::string emsg = "Could not find ";
- emsg += cmQtAutoGen::Quoted(searchFile);
- emsg += " in\n";
- for (std::string const& testFile : testFiles) {
- emsg += " ";
- emsg += cmQtAutoGen::Quoted(testFile);
- emsg += "\n";
- }
- this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg);
- }
-
- return success;
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ MocAutoFileUpdated_ = true;
}
-bool cmQtAutoGeneratorMocUic::UicGenerateAll()
+void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
{
- if (!this->UicEnabled()) {
- return true;
- }
-
- // Look for name collisions in included uic files
- {
- bool collision = false;
- std::map<std::string, std::vector<UicJob const*>> collisions;
- for (auto const& job : this->UicJobs) {
- auto& list = collisions[job->IncludeString];
- if (!list.empty()) {
- collision = true;
- }
- list.push_back(job.get());
- }
- if (collision) {
- std::string emsg =
- "Included uic files with the same name will be "
- "generated from different sources.\n"
- "Consider to\n"
- " - add a directory prefix to a \"ui_<NAME>.h\" include "
- "(e.g \"sub/ui_<NAME>.h\")\n"
- " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
- "include(s)\n"
- "Include conflicts\n"
- "-----------------\n";
- const auto& colls = collisions;
- for (auto const& coll : colls) {
- if (coll.second.size() > 1) {
- emsg += cmQtAutoGen::Quoted(coll.first);
- emsg += " included in\n";
- for (const UicJob* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->Includer);
- emsg += "\n";
- }
- emsg += "would be generated from\n";
- for (const UicJob* job : coll.second) {
- emsg += " - ";
- emsg += cmQtAutoGen::Quoted(job->SourceFile);
- emsg += "\n";
- }
+ std::lock_guard<std::mutex> mocLock(JobsMutex_);
+ if (!JobThreadsAbort_ && Moc().Enabled) {
+ // Compose mocs compilation file content
+ {
+ std::string content =
+ "// This file is autogenerated. Changes will be overwritten.\n";
+ if (MocAutoFiles_.empty()) {
+ // Placeholder content
+ content += "// No files found that require moc or the moc files are "
+ "included\n";
+ content += "enum some_compilers { need_more_than_nothing };\n";
+ } else {
+ // Valid content
+ for (std::string const& mocfile : MocAutoFiles_) {
+ content += "#include \"";
+ content += mocfile;
+ content += "\"\n";
}
}
- this->LogError(cmQtAutoGen::UIC, emsg);
- return false;
- }
- }
-
- // Generate ui header files
- for (const auto& item : this->UicJobs) {
- if (!this->UicGenerateFile(*item)) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob)
-{
- bool success = true;
-
- std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath(
- this->AutogenBuildDir, uicJob.BuildFileRel);
-
- bool generate = false;
- std::string generateReason;
- if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(uicFileAbs);
- generateReason += " from its source file ";
- generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
- generateReason += " because it doesn't exist";
- }
- generate = true;
- }
- if (!generate && this->UicSettingsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(uicFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
- generateReason += " because the UIC settings changed";
- }
- generate = true;
- }
- if (!generate) {
- std::string error;
- if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(uicFileAbs);
- generateReason += " because it's older than its source file ";
- generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
- }
- generate = true;
- } else {
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::UIC, error);
- success = false;
- }
- }
- }
- if (generate) {
- // Log
- if (this->GetVerbose()) {
- this->LogBold("Generating UIC header " + uicJob.BuildFileRel);
- this->LogInfo(cmQtAutoGen::UIC, generateReason);
- }
- // Make sure the parent directory exists
- if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) {
- // Compose uic command
- std::vector<std::string> cmd;
- cmd.push_back(this->UicExecutable);
- {
- std::vector<std::string> allOpts = this->UicTargetOptions;
- auto optionIt = this->UicOptions.find(uicJob.SourceFile);
- if (optionIt != this->UicOptions.end()) {
- cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second,
- (this->QtVersionMajor == 5));
+ std::string const& compRel = Moc().CompFileRel;
+ std::string const& compAbs = Moc().CompFileAbs;
+ if (FileSys().FileDiffers(compAbs, content)) {
+ // Actually write mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::MOC, "Generating MOC compilation " + compRel);
}
- cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
- }
- cmd.push_back("-o");
- cmd.push_back(uicFileAbs);
- cmd.push_back(uicJob.SourceFile);
-
- std::string output;
- if (this->RunCommand(cmd, output)) {
- // Success
- } else {
- // Command failed
- {
- std::string emsg = "uic failed for\n ";
- emsg += cmQtAutoGen::Quoted(uicJob.SourceFile);
- emsg += "\nincluded by\n ";
- emsg += cmQtAutoGen::Quoted(uicJob.Includer);
- this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output);
+ if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) {
+ Log().ErrorFile(GeneratorT::MOC, compAbs,
+ "mocs compilation file writing failed");
+ RegisterJobError();
+ return;
}
- cmSystemTools::RemoveFile(uicFileAbs);
- success = false;
+ } else if (MocAutoFileUpdated_) {
+ // Only touch mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compRel);
+ }
+ FileSys().Touch(compAbs);
}
- } else {
- // Parent directory creation failed
- success = false;
}
}
- return success;
-}
-
-/**
- * @brief Tries to find the header file to the given file base path by
- * appending different header extensions
- * @return True on success
- */
-bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header,
- std::string const& testBasePath) const
-{
- for (std::string const& ext : this->HeaderExtensions) {
- std::string testFilePath(testBasePath);
- testFilePath.push_back('.');
- testFilePath += ext;
- if (cmSystemTools::FileExists(testFilePath.c_str())) {
- header = testFilePath;
- return true;
- }
- }
- return false;
}
diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h
index d510939..215e25a 100644
--- a/Source/cmQtAutoGeneratorMocUic.h
+++ b/Source/cmQtAutoGeneratorMocUic.h
@@ -8,188 +8,434 @@
#include "cmFilePathChecksum.h"
#include "cmQtAutoGen.h"
#include "cmQtAutoGenerator.h"
+#include "cmUVHandlePtr.h"
+#include "cm_uv.h"
#include "cmsys/RegularExpression.hxx"
+#include <algorithm>
+#include <condition_variable>
+#include <cstddef>
+#include <deque>
#include <map>
#include <memory> // IWYU pragma: keep
+#include <mutex>
#include <set>
#include <string>
+#include <thread>
#include <vector>
class cmMakefile;
+// @brief AUTOMOC and AUTOUIC generator
class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator
{
CM_DISABLE_COPY(cmQtAutoGeneratorMocUic)
public:
cmQtAutoGeneratorMocUic();
+ ~cmQtAutoGeneratorMocUic() override;
-private:
+public:
// -- Types
+ class WorkerT;
/// @brief Search key plus regular expression pair
- struct KeyRegExp
+ ///
+ struct KeyExpT
{
- KeyRegExp() = default;
+ KeyExpT() = default;
- KeyRegExp(const char* key, const char* regExp)
+ KeyExpT(const char* key, const char* exp)
: Key(key)
- , RegExp(regExp)
+ , Exp(exp)
{
}
- KeyRegExp(std::string const& key, std::string const& regExp)
+ KeyExpT(std::string const& key, std::string const& exp)
: Key(key)
- , RegExp(regExp)
+ , Exp(exp)
{
}
std::string Key;
- cmsys::RegularExpression RegExp;
+ cmsys::RegularExpression Exp;
};
- /// @brief Source file job
- struct SourceJob
+ /// @brief Common settings
+ ///
+ class BaseSettingsT
{
- bool Moc = false;
- bool Uic = false;
+ CM_DISABLE_COPY(BaseSettingsT)
+ public:
+ // -- Volatile methods
+ BaseSettingsT(FileSystem* fileSystem)
+ : MultiConfig(MultiConfigT::WRAPPER)
+ , IncludeProjectDirsBefore(false)
+ , QtVersionMajor(4)
+ , NumThreads(1)
+ , FileSys(fileSystem)
+ {
+ }
+
+ // -- Const methods
+ std::string AbsoluteBuildPath(std::string const& relativePath) const;
+ bool FindHeader(std::string& header,
+ std::string const& testBasePath) const;
+
+ // -- Attributes
+ // - Config
+ std::string ConfigSuffix;
+ MultiConfigT MultiConfig;
+ bool IncludeProjectDirsBefore;
+ unsigned int QtVersionMajor;
+ unsigned int NumThreads;
+ // - Directories
+ std::string ProjectSourceDir;
+ std::string ProjectBinaryDir;
+ std::string CurrentSourceDir;
+ std::string CurrentBinaryDir;
+ std::string AutogenBuildDir;
+ std::string AutogenIncludeDirRel;
+ std::string AutogenIncludeDirAbs;
+ // - Files
+ cmFilePathChecksum FilePathChecksum;
+ std::vector<std::string> HeaderExtensions;
+ // - File system
+ FileSystem* FileSys;
};
- /// @brief MOC job
- struct MocJobAuto
+ /// @brief Moc settings
+ ///
+ class MocSettingsT
{
- std::string SourceFile;
- std::string BuildFileRel;
- std::set<std::string> Depends;
+ CM_DISABLE_COPY(MocSettingsT)
+ public:
+ MocSettingsT(FileSystem* fileSys)
+ : FileSys(fileSys)
+ {
+ }
+
+ // -- 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;
+ std::string Executable;
+ std::string CompFileRel;
+ std::string CompFileAbs;
+ std::string PredefsFileRel;
+ std::string PredefsFileAbs;
+ std::set<std::string> SkipList;
+ std::vector<std::string> IncludePaths;
+ std::vector<std::string> Includes;
+ std::vector<std::string> Definitions;
+ std::vector<std::string> Options;
+ std::vector<std::string> AllOptions;
+ std::vector<std::string> PredefsCmd;
+ std::vector<KeyExpT> DependFilters;
+ std::vector<KeyExpT> MacroFilters;
+ cmsys::RegularExpression RegExpInclude;
+ // - File system
+ FileSystem* FileSys;
};
- /// @brief MOC job
- struct MocJobIncluded : MocJobAuto
+ /// @brief Uic settings
+ ///
+ class UicSettingsT
{
- bool DependsValid = false;
- std::string Includer;
+ CM_DISABLE_COPY(UicSettingsT)
+ public:
+ UicSettingsT() = default;
+ // -- Const methods
+ bool skipped(std::string const& fileName) const;
+
+ // -- Attributes
+ bool Enabled = false;
+ bool SettingsChanged = false;
+ std::string Executable;
+ std::set<std::string> SkipList;
+ std::vector<std::string> TargetOptions;
+ std::map<std::string, std::vector<std::string>> Options;
+ std::vector<std::string> SearchPaths;
+ cmsys::RegularExpression RegExpInclude;
+ };
+
+ /// @brief Abstract job class for threaded processing
+ ///
+ class JobT
+ {
+ CM_DISABLE_COPY(JobT)
+ public:
+ JobT() = default;
+ virtual ~JobT() = default;
+ // -- Abstract processing interface
+ virtual void Process(WorkerT& wrk) = 0;
+ };
+
+ /// @brief Deleter for classes derived from Job
+ ///
+ struct JobDeleterT
+ {
+ void operator()(JobT* job);
+ };
+
+ // Job management types
+ typedef std::unique_ptr<JobT, JobDeleterT> JobHandleT;
+ typedef std::deque<JobHandleT> JobQueueT;
+
+ /// @brief Parse source job
+ ///
+ class JobParseT : public JobT
+ {
+ public:
+ JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false)
+ : FileName(std::move(fileName))
+ , AutoMoc(moc)
+ , AutoUic(uic)
+ , Header(header)
+ {
+ }
+
+ private:
+ struct MetaT
+ {
+ std::string Content;
+ std::string FileDir;
+ std::string FileBase;
+ };
+
+ void Process(WorkerT& wrk) override;
+ bool ParseMocSource(WorkerT& wrk, MetaT const& meta);
+ bool ParseMocHeader(WorkerT& wrk, MetaT const& meta);
+ std::string MocStringHeaders(WorkerT& wrk,
+ std::string const& fileBase) const;
+ std::string MocFindIncludedHeader(WorkerT& wrk,
+ std::string const& includerDir,
+ std::string const& includeBase);
+ bool ParseUic(WorkerT& wrk, MetaT const& meta);
+ bool ParseUicInclude(WorkerT& wrk, MetaT const& meta,
+ std::string&& includeString);
+ std::string UicFindIncludedFile(WorkerT& wrk, MetaT const& meta,
+ std::string const& includeString);
+
+ private:
+ std::string FileName;
+ bool AutoMoc = false;
+ bool AutoUic = false;
+ bool Header = false;
+ };
+
+ /// @brief Generate moc_predefs
+ ///
+ class JobMocPredefsT : public JobT
+ {
+ private:
+ void Process(WorkerT& wrk) override;
+ };
+
+ /// @brief Moc a file job
+ ///
+ class JobMocT : public JobT
+ {
+ public:
+ JobMocT(std::string&& sourceFile, std::string const& includerFile,
+ std::string&& includeString)
+ : SourceFile(std::move(sourceFile))
+ , IncluderFile(includerFile)
+ , IncludeString(std::move(includeString))
+ {
+ }
+
+ void FindDependencies(WorkerT& wrk, std::string const& content);
+
+ private:
+ void Process(WorkerT& wrk) override;
+ bool UpdateRequired(WorkerT& wrk);
+ void GenerateMoc(WorkerT& wrk);
+
+ public:
+ std::string SourceFile;
+ std::string IncluderFile;
std::string IncludeString;
+ std::string BuildFile;
+ bool DependsValid = false;
+ std::set<std::string> Depends;
};
- /// @brief UIC job
- struct UicJob
+ /// @brief Uic a file job
+ ///
+ class JobUicT : public JobT
{
+ public:
+ JobUicT(std::string&& sourceFile, std::string const& includerFile,
+ std::string&& includeString)
+ : SourceFile(std::move(sourceFile))
+ , IncluderFile(includerFile)
+ , IncludeString(std::move(includeString))
+ {
+ }
+
+ private:
+ void Process(WorkerT& wrk) override;
+ bool UpdateRequired(WorkerT& wrk);
+ void GenerateUic(WorkerT& wrk);
+
+ public:
std::string SourceFile;
- std::string BuildFileRel;
- std::string Includer;
+ std::string IncluderFile;
std::string IncludeString;
+ std::string BuildFile;
};
- // -- Initialization
- bool InitInfoFile(cmMakefile* makefile);
+ /// @brief Worker Thread
+ ///
+ class WorkerT
+ {
+ CM_DISABLE_COPY(WorkerT)
+ public:
+ WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop);
+ ~WorkerT();
+
+ // -- Const accessors
+ cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; }
+ Logger& Log() const { return Gen_->Log(); }
+ FileSystem& FileSys() const { return Gen_->FileSys(); }
+ const BaseSettingsT& Base() const { return Gen_->Base(); }
+ const MocSettingsT& Moc() const { return Gen_->Moc(); }
+ const UicSettingsT& Uic() const { return Gen_->Uic(); }
- // -- Settings file
- void SettingsFileRead(cmMakefile* makefile);
- bool SettingsFileWrite();
- bool SettingsChanged() const
+ // -- Log info
+ void LogInfo(GeneratorT genType, std::string const& message) const;
+ // -- Log warning
+ void LogWarning(GeneratorT genType, std::string const& message) const;
+ void LogFileWarning(GeneratorT genType, std::string const& filename,
+ std::string const& message) const;
+ // -- Log error
+ void LogError(GeneratorT genType, std::string const& message) const;
+ void LogFileError(GeneratorT genType, std::string const& filename,
+ std::string const& message) const;
+ void LogCommandError(GeneratorT genType, std::string const& message,
+ std::vector<std::string> const& command,
+ std::string const& output) const;
+
+ // -- External processes
+ /// @brief Verbose logging version
+ bool RunProcess(GeneratorT genType, ProcessResultT& result,
+ std::vector<std::string> const& command);
+
+ private:
+ /// @brief Thread main loop
+ void Loop();
+
+ // -- Libuv callbacks
+ static void UVProcessStart(uv_async_t* handle);
+ void UVProcessFinished();
+
+ private:
+ // -- Generator
+ cmQtAutoGeneratorMocUic* Gen_;
+ // -- Job handle
+ JobHandleT JobHandle_;
+ // -- Process management
+ std::mutex ProcessMutex_;
+ cm::uv_async_ptr ProcessRequest_;
+ std::condition_variable ProcessCondition_;
+ std::unique_ptr<ReadOnlyProcessT> Process_;
+ // -- System thread
+ std::thread Thread_;
+ };
+
+ /// @brief Processing stage
+ enum class StageT
{
- return (this->MocSettingsChanged || this->UicSettingsChanged);
- }
-
- // -- Central processing
- bool Process(cmMakefile* makefile) override;
-
- // -- Source parsing
- bool ParseSourceFile(std::string const& absFilename, const SourceJob& job);
- bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job);
- bool ParsePostprocess();
-
- // -- Moc
- bool MocEnabled() const { return !this->MocExecutable.empty(); }
- bool MocSkip(std::string const& absFilename) const;
- bool MocRequired(std::string const& contentText,
- std::string* macroName = nullptr);
- // Moc strings
- std::string MocStringMacros() const;
- std::string MocStringHeaders(std::string const& fileBase) const;
- std::string MocFindIncludedHeader(std::string const& sourcePath,
- std::string const& includeBase) const;
- bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile,
- std::string const& includeString) const;
- // Moc depends
- bool MocDependFilterPush(std::string const& key, std::string const& regExp);
- void MocFindDepends(std::string const& absFilename,
- std::string const& contentText,
- std::set<std::string>& depends);
- // Moc
- bool MocParseSourceContent(std::string const& absFilename,
- std::string const& contentText);
- void MocParseHeaderContent(std::string const& absFilename,
- std::string const& contentText);
-
- bool MocGenerateAll();
- bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr);
-
- // -- Uic
- bool UicEnabled() const { return !this->UicExecutable.empty(); }
- bool UicSkip(std::string const& absFilename) const;
- bool UicParseContent(std::string const& fileName,
- std::string const& contentText);
- bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile,
- std::string const& includeString);
- bool UicGenerateAll();
- bool UicGenerateFile(const UicJob& uicJob);
-
- // -- Utility
- bool FindHeader(std::string& header, std::string const& testBasePath) const;
-
- // -- Meta
- std::string ConfigSuffix;
- cmQtAutoGen::MultiConfig MultiConfig;
+ SETTINGS_READ,
+ CREATE_DIRECTORIES,
+ PARSE_SOURCES,
+ PARSE_HEADERS,
+ MOC_PREDEFS,
+ MOC_PROCESS,
+ MOCS_COMPILATION,
+ UIC_PROCESS,
+ SETTINGS_WRITE,
+ FINISH,
+ END
+ };
+
+ // -- Const settings interface
+ const BaseSettingsT& Base() const { return this->Base_; }
+ const MocSettingsT& Moc() const { return this->Moc_; }
+ const UicSettingsT& Uic() const { return this->Uic_; }
+
+ // -- Worker thread interface
+ void WorkerSwapJob(JobHandleT& jobHandle);
+ // -- Parallel job processing interface
+ void ParallelRegisterJobError();
+ bool ParallelJobPushMoc(JobHandleT& jobHandle);
+ bool ParallelJobPushUic(JobHandleT& jobHandle);
+ bool ParallelMocIncluded(std::string const& sourceFile);
+ void ParallelMocAutoRegister(std::string const& mocFile);
+ void ParallelMocAutoUpdated();
+
+private:
+ // -- Abstract processing interface
+ bool Init(cmMakefile* makefile) override;
+ bool Process() override;
+ // -- Process stage
+ static void UVPollStage(uv_async_t* handle);
+ void PollStage();
+ void SetStage(StageT stage);
+ // -- Settings file
+ void SettingsFileRead();
+ void SettingsFileWrite();
+ // -- Thread processing
+ bool ThreadsStartJobs(JobQueueT& queue);
+ bool ThreadsJobsDone();
+ void ThreadsStop();
+ void RegisterJobError();
+ // -- Generation
+ void CreateDirectories();
+ void MocGenerateCompilation();
+
+private:
// -- Settings
- bool IncludeProjectDirsBefore;
- std::string SettingsFile;
- std::string SettingsStringMoc;
- std::string SettingsStringUic;
- // -- Directories
- std::string ProjectSourceDir;
- std::string ProjectBinaryDir;
- std::string CurrentSourceDir;
- std::string CurrentBinaryDir;
- std::string AutogenBuildDir;
- std::string AutogenIncludeDir;
- // -- Qt environment
- unsigned long QtVersionMajor;
- std::string MocExecutable;
- std::string UicExecutable;
- // -- File lists
- std::map<std::string, SourceJob> HeaderJobs;
- std::map<std::string, SourceJob> SourceJobs;
- std::vector<std::string> HeaderExtensions;
- cmFilePathChecksum FilePathChecksum;
- // -- Moc
- bool MocSettingsChanged;
- bool MocPredefsChanged;
- bool MocRelaxedMode;
- std::string MocCompFileRel;
- std::string MocCompFileAbs;
- std::string MocPredefsFileRel;
- std::string MocPredefsFileAbs;
- std::vector<std::string> MocSkipList;
- std::vector<std::string> MocIncludePaths;
- std::vector<std::string> MocIncludes;
- std::vector<std::string> MocDefinitions;
- std::vector<std::string> MocOptions;
- std::vector<std::string> MocAllOptions;
- std::vector<std::string> MocPredefsCmd;
- std::vector<KeyRegExp> MocDependFilters;
- std::vector<KeyRegExp> MocMacroFilters;
- cmsys::RegularExpression MocRegExpInclude;
- std::vector<std::unique_ptr<MocJobIncluded>> MocJobsIncluded;
- std::vector<std::unique_ptr<MocJobAuto>> MocJobsAuto;
- // -- Uic
- bool UicSettingsChanged;
- std::vector<std::string> UicSkipList;
- std::vector<std::string> UicTargetOptions;
- std::map<std::string, std::vector<std::string>> UicOptions;
- std::vector<std::string> UicSearchPaths;
- cmsys::RegularExpression UicRegExpInclude;
- std::vector<std::unique_ptr<UicJob>> UicJobs;
+ BaseSettingsT Base_;
+ MocSettingsT Moc_;
+ UicSettingsT Uic_;
+ // -- Progress
+ StageT Stage_;
+ // -- Job queues
+ std::mutex JobsMutex_;
+ struct
+ {
+ JobQueueT Sources;
+ JobQueueT Headers;
+ JobQueueT MocPredefs;
+ JobQueueT Moc;
+ JobQueueT Uic;
+ } JobQueues_;
+ JobQueueT JobQueue_;
+ std::size_t volatile JobsRemain_;
+ bool volatile JobError_;
+ bool volatile JobThreadsAbort_;
+ std::condition_variable JobsConditionRead_;
+ // -- Moc meta
+ std::set<std::string> MocIncludedStrings_;
+ std::set<std::string> MocIncludedFiles_;
+ std::set<std::string> MocAutoFiles_;
+ bool volatile MocAutoFileUpdated_;
+ // -- Settings file
+ std::string SettingsFile_;
+ std::string SettingsStringMoc_;
+ std::string SettingsStringUic_;
+ // -- Threads and loops
+ std::vector<std::unique_ptr<WorkerT>> Workers_;
};
#endif
diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx
index 3c9f1a8..e8ff75a 100644
--- a/Source/cmQtAutoGeneratorRcc.cxx
+++ b/Source/cmQtAutoGeneratorRcc.cxx
@@ -6,22 +6,30 @@
#include "cmAlgorithms.h"
#include "cmCryptoHash.h"
#include "cmMakefile.h"
-#include "cmOutputConverter.h"
#include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
-// -- Static variables
-
-static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH";
+#include <functional>
// -- Class methods
cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc()
- : MultiConfig(cmQtAutoGen::WRAP)
- , SettingsChanged(false)
+ : SettingsChanged_(false)
+ , MultiConfig_(MultiConfigT::WRAPPER)
+ , Stage_(StageT::SETTINGS_READ)
+ , Error_(false)
+ , Generate_(false)
+ , BuildFileChanged_(false)
{
+ // Initialize libuv asynchronous iteration request
+ UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this);
}
-bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
+cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc()
+{
+}
+
+bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile)
{
// Utility lambdas
auto InfoGet = [makefile](const char* key) {
@@ -37,7 +45,7 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
{
std::string keyConf = key;
keyConf += '_';
- keyConf += this->GetInfoConfig();
+ keyConf += InfoConfig();
valueConf = makefile->GetDefinition(keyConf);
}
if (valueConf == nullptr) {
@@ -53,79 +61,180 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
};
// -- Read info file
- if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
- this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
- "File processing failed");
+ if (!makefile->ReadListFile(InfoFile().c_str())) {
+ Log().ErrorFile(GeneratorT::RCC, InfoFile(), "File processing failed");
return false;
}
// -- Meta
- this->MultiConfig =
- cmQtAutoGen::MultiConfigType(InfoGet("ARCC_MULTI_CONFIG"));
- this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX");
- if (this->ConfigSuffix.empty()) {
- this->ConfigSuffix = "_";
- this->ConfigSuffix += this->GetInfoConfig();
+ MultiConfig_ = MultiConfigType(InfoGet("ARCC_MULTI_CONFIG"));
+ ConfigSuffix_ = InfoGetConfig("ARCC_CONFIG_SUFFIX");
+ if (ConfigSuffix_.empty()) {
+ ConfigSuffix_ = "_";
+ ConfigSuffix_ += InfoConfig();
}
- this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE");
+ SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE");
// - Files and directories
- this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR");
- this->ProjectBinaryDir = InfoGet("ARCC_CMAKE_BINARY_DIR");
- this->CurrentSourceDir = InfoGet("ARCC_CMAKE_CURRENT_SOURCE_DIR");
- this->CurrentBinaryDir = InfoGet("ARCC_CMAKE_CURRENT_BINARY_DIR");
- this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR");
+ AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
// - Qt environment
- this->RccExecutable = InfoGet("ARCC_RCC_EXECUTABLE");
- this->RccListOptions = InfoGetList("ARCC_RCC_LIST_OPTIONS");
+ RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
+ RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
// - Job
- this->QrcFile = InfoGet("ARCC_SOURCE");
- this->RccFile = InfoGet("ARCC_OUTPUT");
- this->Options = InfoGetConfigList("ARCC_OPTIONS");
- this->Inputs = InfoGetList("ARCC_INPUTS");
+ QrcFile_ = InfoGet("ARCC_SOURCE");
+ QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_);
+ QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_);
+ RccFile_ = InfoGet("ARCC_OUTPUT");
+ Options_ = InfoGetConfigList("ARCC_OPTIONS");
+ Inputs_ = InfoGetList("ARCC_INPUTS");
// - Validity checks
- if (this->SettingsFile.empty()) {
- this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
- "Settings file name missing");
+ if (SettingsFile_.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Settings file name missing");
return false;
}
- if (this->AutogenBuildDir.empty()) {
- this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
- "Autogen build directory missing");
+ if (AutogenBuildDir_.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, InfoFile(),
+ "Autogen build directory missing");
return false;
}
- if (this->RccExecutable.empty()) {
- this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
- "rcc executable missing");
+ if (RccExecutable_.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc executable missing");
return false;
}
- if (this->QrcFile.empty()) {
- this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
- "rcc input file missing");
+ if (QrcFile_.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc input file missing");
return false;
}
- if (this->RccFile.empty()) {
- this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
- "rcc output file missing");
+ if (RccFile_.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc output file missing");
return false;
}
// Init derived information
// ------------------------
- // Init file path checksum generator
- this->FilePathChecksum.setupParentDirs(
- this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
- this->ProjectBinaryDir);
+ // Compute rcc output file name
+ {
+ std::string suffix;
+ switch (MultiConfig_) {
+ case MultiConfigT::SINGLE:
+ break;
+ case MultiConfigT::WRAPPER:
+ suffix = "_CMAKE";
+ suffix += ConfigSuffix_;
+ suffix += "_";
+ break;
+ case MultiConfigT::MULTI:
+ suffix = ConfigSuffix_;
+ break;
+ }
+ RccFileBuild_ = AppendFilenameSuffix(RccFile_, suffix);
+ }
+
+ return true;
+}
+bool cmQtAutoGeneratorRcc::Process()
+{
+ // Run libuv event loop
+ UVRequest().send();
+ if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
+ if (Error_) {
+ return false;
+ }
+ } else {
+ return false;
+ }
return true;
}
-void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile)
+void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle)
+{
+ reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage();
+}
+
+void cmQtAutoGeneratorRcc::PollStage()
+{
+ switch (Stage_) {
+ // -- Initialize
+ case StageT::SETTINGS_READ:
+ SettingsFileRead();
+ SetStage(StageT::TEST_QRC_RCC_FILES);
+ break;
+
+ // -- Change detection
+ case StageT::TEST_QRC_RCC_FILES:
+ if (TestQrcRccFiles()) {
+ SetStage(StageT::GENERATE);
+ } else {
+ SetStage(StageT::TEST_RESOURCES_READ);
+ }
+ break;
+ case StageT::TEST_RESOURCES_READ:
+ if (TestResourcesRead()) {
+ SetStage(StageT::TEST_RESOURCES);
+ }
+ break;
+ case StageT::TEST_RESOURCES:
+ if (TestResources()) {
+ SetStage(StageT::GENERATE);
+ } else {
+ SetStage(StageT::TEST_INFO_FILE);
+ }
+ break;
+ case StageT::TEST_INFO_FILE:
+ TestInfoFile();
+ SetStage(StageT::GENERATE_WRAPPER);
+ break;
+
+ // -- Generation
+ case StageT::GENERATE:
+ GenerateParentDir();
+ SetStage(StageT::GENERATE_RCC);
+ break;
+ case StageT::GENERATE_RCC:
+ if (GenerateRcc()) {
+ SetStage(StageT::GENERATE_WRAPPER);
+ }
+ break;
+ case StageT::GENERATE_WRAPPER:
+ GenerateWrapper();
+ SetStage(StageT::SETTINGS_WRITE);
+ break;
+
+ // -- Finalize
+ case StageT::SETTINGS_WRITE:
+ SettingsFileWrite();
+ SetStage(StageT::FINISH);
+ break;
+ case StageT::FINISH:
+ // Clear all libuv handles
+ UVRequest().reset();
+ // Set highest END stage manually
+ Stage_ = StageT::END;
+ break;
+ case StageT::END:
+ break;
+ }
+}
+
+void cmQtAutoGeneratorRcc::SetStage(StageT stage)
+{
+ if (Error_) {
+ stage = StageT::FINISH;
+ }
+ // Only allow to increase the stage
+ if (Stage_ < stage) {
+ Stage_ = stage;
+ UVRequest().send();
+ }
+}
+
+void cmQtAutoGeneratorRcc::SettingsFileRead()
{
// Compose current settings strings
{
@@ -133,293 +242,375 @@ void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile)
std::string const sep(" ~~~ ");
{
std::string str;
- str += this->RccExecutable;
+ str += RccExecutable_;
str += sep;
- str += cmJoin(this->RccListOptions, ";");
+ str += cmJoin(RccListOptions_, ";");
str += sep;
- str += this->QrcFile;
+ str += QrcFile_;
str += sep;
- str += this->RccFile;
+ str += RccFile_;
str += sep;
- str += cmJoin(this->Options, ";");
+ str += cmJoin(Options_, ";");
str += sep;
- str += cmJoin(this->Inputs, ";");
+ str += cmJoin(Inputs_, ";");
str += sep;
- this->SettingsString = crypt.HashString(str);
+ SettingsString_ = crypt.HashString(str);
}
}
// Read old settings
- if (makefile->ReadListFile(this->SettingsFile.c_str())) {
- {
- auto SMatch = [makefile](const char* key, std::string const& value) {
- return (value == makefile->GetSafeDefinition(key));
- };
- if (!SMatch(SettingsKeyRcc, this->SettingsString)) {
- this->SettingsChanged = true;
+ {
+ std::string content;
+ if (FileSys().FileRead(content, SettingsFile_)) {
+ SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc"));
+ // 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 (SettingsChanged_) {
+ FileSys().FileRemove(SettingsFile_);
}
+ } else {
+ SettingsChanged_ = true;
}
- // In case any setting changed remove the old settings file.
- // This triggers a full rebuild on the next run if the current
- // build is aborted before writing the current settings in the end.
- if (this->SettingsChanged) {
- cmSystemTools::RemoveFile(this->SettingsFile);
- }
- } else {
- // If the file could not be read re-generate everythiung.
- this->SettingsChanged = true;
}
}
-bool cmQtAutoGeneratorRcc::SettingsFileWrite()
+void cmQtAutoGeneratorRcc::SettingsFileWrite()
{
- bool success = true;
// Only write if any setting changed
- if (this->SettingsChanged) {
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " +
- cmQtAutoGen::Quoted(this->SettingsFile));
- }
- // Compose settings file content
- std::string settings;
- {
- auto SettingAppend = [&settings](const char* key,
- std::string const& value) {
- settings += "set(";
- settings += key;
- settings += " ";
- settings += cmOutputConverter::EscapeForCMake(value);
- settings += ")\n";
- };
- SettingAppend(SettingsKeyRcc, this->SettingsString);
+ if (SettingsChanged_) {
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::RCC,
+ "Writing settings file " + Quoted(SettingsFile_));
}
// Write settings file
- if (!this->FileWrite(cmQtAutoGen::RCC, this->SettingsFile, settings)) {
- this->LogFileError(cmQtAutoGen::RCC, this->SettingsFile,
- "Settings file writing failed");
+ std::string content = "rcc:";
+ content += SettingsString_;
+ content += '\n';
+ if (!FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, content)) {
+ Log().ErrorFile(GeneratorT::RCC, SettingsFile_,
+ "Settings file writing failed");
// Remove old settings file to trigger a full rebuild on the next run
- cmSystemTools::RemoveFile(this->SettingsFile);
- success = false;
+ FileSys().FileRemove(SettingsFile_);
+ Error_ = true;
}
}
- return success;
}
-bool cmQtAutoGeneratorRcc::Process(cmMakefile* makefile)
+bool cmQtAutoGeneratorRcc::TestQrcRccFiles()
{
- // Read info file
- if (!this->InfoFileRead(makefile)) {
- return false;
+ // Do basic checks if rcc generation is required
+
+ // Test if the rcc output file exists
+ if (!FileSys().FileExists(RccFileBuild_)) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(RccFileBuild_);
+ reason += " from its source file ";
+ reason += Quoted(QrcFile_);
+ reason += " because it doesn't exist";
+ Log().Info(GeneratorT::RCC, reason);
+ }
+ Generate_ = true;
+ return Generate_;
}
- // Read latest settings
- this->SettingsFileRead(makefile);
- // Generate rcc file
- if (!this->RccGenerate()) {
- return false;
+
+ // Test if the settings changed
+ if (SettingsChanged_) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(RccFileBuild_);
+ reason += " from ";
+ reason += Quoted(QrcFile_);
+ reason += " because the RCC settings changed";
+ Log().Info(GeneratorT::RCC, reason);
+ }
+ Generate_ = true;
+ return Generate_;
}
- // Write latest settings
- if (!this->SettingsFileWrite()) {
- return false;
+
+ // Test if the rcc output file is older than the .qrc file
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(RccFileBuild_, QrcFile_, &error);
+ if (!error.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+ Error_ = true;
+ }
+ }
+ if (isOlder) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(RccFileBuild_);
+ reason += " because it is older than ";
+ reason += Quoted(QrcFile_);
+ Log().Info(GeneratorT::RCC, reason);
+ }
+ Generate_ = true;
+ }
}
- return true;
+
+ return Generate_;
}
-/**
- * @return True on success
- */
-bool cmQtAutoGeneratorRcc::RccGenerate()
+bool cmQtAutoGeneratorRcc::TestResourcesRead()
{
- bool success = true;
- bool rccGenerated = false;
-
- std::string rccFileAbs;
- {
- std::string suffix;
- switch (this->MultiConfig) {
- case cmQtAutoGen::SINGLE:
- break;
- case cmQtAutoGen::WRAP:
- suffix = "_CMAKE";
- suffix += this->ConfigSuffix;
- suffix += "_";
- break;
- case cmQtAutoGen::FULL:
- suffix = this->ConfigSuffix;
- break;
- }
- rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(this->RccFile, suffix);
+ if (!Inputs_.empty()) {
+ // Inputs are known already
+ return true;
}
- std::string const rccFileRel = cmSystemTools::RelativePath(
- this->AutogenBuildDir.c_str(), rccFileAbs.c_str());
- // Check if regeneration is required
- bool generate = false;
- std::string generateReason;
- if (!cmSystemTools::FileExists(this->QrcFile)) {
- {
- std::string error = "Could not find the file\n ";
- error += cmQtAutoGen::Quoted(this->QrcFile);
- this->LogError(cmQtAutoGen::RCC, error);
+ if (!RccListOptions_.empty()) {
+ // Start a rcc list process and parse the output
+ if (Process_) {
+ // Process is running already
+ if (Process_->IsFinished()) {
+ // Process is finished
+ if (!ProcessResult_.error()) {
+ // Process success
+ std::string parseError;
+ if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr,
+ Inputs_, parseError)) {
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_, parseError);
+ Error_ = true;
+ }
+ } else {
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_,
+ ProcessResult_.ErrorMessage);
+ Error_ = true;
+ }
+ // Clean up
+ Process_.reset();
+ ProcessResult_.reset();
+ } else {
+ // Process is not finished, yet.
+ return false;
+ }
+ } else {
+ // Start a new process
+ // rcc prints relative entry paths when started in the directory of the
+ // qrc file with a pathless qrc file name argument.
+ // This is important because on Windows absolute paths returned by rcc
+ // might contain bad multibyte characters when the qrc file path
+ // contains non-ASCII pcharacters.
+ std::vector<std::string> cmd;
+ cmd.push_back(RccExecutable_);
+ cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end());
+ cmd.push_back(QrcFileName_);
+ // We're done here if the process fails to start
+ return !StartProcess(QrcFileDir_, cmd, false);
}
- success = false;
- }
- if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(rccFileAbs);
- generateReason += " from its source file ";
- generateReason += cmQtAutoGen::Quoted(this->QrcFile);
- generateReason += " because it doesn't exist";
+ } else {
+ // rcc does not support the --list command.
+ // Read the qrc file content and parse it.
+ std::string qrcContent;
+ if (FileSys().FileRead(GeneratorT::RCC, qrcContent, QrcFile_)) {
+ RccListParseContent(qrcContent, Inputs_);
}
- generate = true;
}
- if (success && !generate && this->SettingsChanged) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(rccFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(this->QrcFile);
- generateReason += " because the RCC settings changed";
- }
- generate = true;
+
+ if (!Inputs_.empty()) {
+ // Convert relative paths to absolute paths
+ RccListConvertFullPath(QrcFileDir_, Inputs_);
+ }
+
+ return true;
+}
+
+bool cmQtAutoGeneratorRcc::TestResources()
+{
+ if (Inputs_.empty()) {
+ return true;
}
- if (success && !generate) {
+ {
std::string error;
- if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(rccFileAbs);
- generateReason += " because it is older than ";
- generateReason += cmQtAutoGen::Quoted(this->QrcFile);
+ for (std::string const& resFile : Inputs_) {
+ // Check if the resource file exists
+ if (!FileSys().FileExists(resFile)) {
+ error = "Could not find the resource file\n ";
+ error += Quoted(resFile);
+ error += '\n';
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+ Error_ = true;
+ break;
}
- generate = true;
- } else {
+ // Check if the resource file is newer than the build file
+ if (FileSys().FileIsOlderThan(RccFileBuild_, resFile, &error)) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(RccFileBuild_);
+ reason += " from ";
+ reason += Quoted(QrcFile_);
+ reason += " because it is older than ";
+ reason += Quoted(resFile);
+ Log().Info(GeneratorT::RCC, reason);
+ }
+ Generate_ = true;
+ break;
+ }
+ // Print error and break on demand
if (!error.empty()) {
- this->LogError(cmQtAutoGen::RCC, error);
- success = false;
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+ Error_ = true;
+ break;
}
}
}
- if (success && !generate) {
- // Acquire input file list
- std::vector<std::string> readFiles;
- std::vector<std::string> const* files = nullptr;
- if (!this->Inputs.empty()) {
- files = &this->Inputs;
- } else {
- // Read input file list from qrc file
+
+ return Generate_;
+}
+
+void cmQtAutoGeneratorRcc::TestInfoFile()
+{
+ // Test if the rcc output file is older than the info file
+ {
+ bool isOlder = false;
+ {
std::string error;
- if (cmQtAutoGen::RccListInputs(this->RccExecutable, this->RccListOptions,
- this->QrcFile, readFiles, &error)) {
- files = &readFiles;
- } else {
- this->LogFileError(cmQtAutoGen::RCC, this->QrcFile, error);
- success = false;
+ isOlder = FileSys().FileIsOlderThan(RccFileBuild_, InfoFile(), &error);
+ if (!error.empty()) {
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
+ Error_ = true;
}
}
- // Test if any input file is newer than the build file
- if (files != nullptr) {
- std::string error;
- for (std::string const& resFile : *files) {
- if (!cmSystemTools::FileExists(resFile.c_str())) {
- error = "Could not find the file\n ";
- error += cmQtAutoGen::Quoted(resFile);
- error += "\nwhich is listed in\n ";
- error += cmQtAutoGen::Quoted(this->QrcFile);
- break;
- }
- if (FileIsOlderThan(rccFileAbs, resFile, &error)) {
- if (this->GetVerbose()) {
- generateReason = "Generating ";
- generateReason += cmQtAutoGen::Quoted(rccFileAbs);
- generateReason += " from ";
- generateReason += cmQtAutoGen::Quoted(this->QrcFile);
- generateReason += " because it is older than ";
- generateReason += cmQtAutoGen::Quoted(resFile);
- }
- generate = true;
- break;
- }
- if (!error.empty()) {
- break;
- }
- }
- // Print error
- if (!error.empty()) {
- this->LogError(cmQtAutoGen::RCC, error);
- success = false;
+ if (isOlder) {
+ if (Log().Verbose()) {
+ std::string reason = "Touching ";
+ reason += Quoted(RccFileBuild_);
+ reason += " because it is older than ";
+ reason += Quoted(InfoFile());
+ Log().Info(GeneratorT::RCC, reason);
}
+ // Touch build file
+ FileSys().Touch(RccFileBuild_);
+ BuildFileChanged_ = true;
}
}
- // Regenerate on demand
- if (generate) {
- // Log
- if (this->GetVerbose()) {
- this->LogBold("Generating RCC source " + rccFileRel);
- this->LogInfo(cmQtAutoGen::RCC, generateReason);
- }
+}
- // Make sure the parent directory exists
- if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) {
- // Compose rcc command
- std::vector<std::string> cmd;
- cmd.push_back(this->RccExecutable);
- cmd.insert(cmd.end(), this->Options.begin(), this->Options.end());
- cmd.push_back("-o");
- cmd.push_back(rccFileAbs);
- cmd.push_back(this->QrcFile);
-
- std::string output;
- if (this->RunCommand(cmd, output)) {
- // Success
- rccGenerated = true;
+void cmQtAutoGeneratorRcc::GenerateParentDir()
+{
+ // Make sure the parent directory exists
+ if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileBuild_)) {
+ Error_ = true;
+ }
+}
+
+/**
+ * @return True when finished
+ */
+bool cmQtAutoGeneratorRcc::GenerateRcc()
+{
+ if (!Generate_) {
+ // Nothing to do
+ return true;
+ }
+
+ if (Process_) {
+ // Process is running already
+ if (Process_->IsFinished()) {
+ // Process is finished
+ if (!ProcessResult_.error()) {
+ // Process success
+ BuildFileChanged_ = true;
} else {
+ // Process failed
{
- std::string emsg = "rcc failed for\n ";
- emsg += cmQtAutoGen::Quoted(this->QrcFile);
- this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output);
+ std::string emsg = "The rcc process failed to compile\n ";
+ emsg += Quoted(QrcFile_);
+ emsg += "\ninto\n ";
+ emsg += Quoted(RccFileBuild_);
+ if (ProcessResult_.error()) {
+ emsg += "\n";
+ emsg += ProcessResult_.ErrorMessage;
+ }
+ Log().ErrorCommand(GeneratorT::RCC, emsg, Process_->Setup().Command,
+ ProcessResult_.StdOut);
}
- cmSystemTools::RemoveFile(rccFileAbs);
- success = false;
+ FileSys().FileRemove(RccFileBuild_);
+ Error_ = true;
}
+ // Clean up
+ Process_.reset();
+ ProcessResult_.reset();
} else {
- // Parent directory creation failed
- success = false;
+ // Process is not finished, yet.
+ return false;
}
+ } else {
+ // Start a rcc process
+ std::vector<std::string> cmd;
+ cmd.push_back(RccExecutable_);
+ cmd.insert(cmd.end(), Options_.begin(), Options_.end());
+ cmd.push_back("-o");
+ cmd.push_back(RccFileBuild_);
+ cmd.push_back(QrcFile_);
+ // We're done here if the process fails to start
+ return !StartProcess(AutogenBuildDir_, cmd, true);
}
+ return true;
+}
+
+void cmQtAutoGeneratorRcc::GenerateWrapper()
+{
// Generate a wrapper source file on demand
- if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) {
+ if (MultiConfig_ == MultiConfigT::WRAPPER) {
// Wrapper file name
- std::string const& wrapperFileAbs = this->RccFile;
- std::string const wrapperFileRel = cmSystemTools::RelativePath(
- this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str());
+ std::string const& wrapperAbs = RccFile_;
// Wrapper file content
std::string content = "// This is an autogenerated configuration "
"wrapper file. Changes will be overwritten.\n"
"#include \"";
- content += cmSystemTools::GetFilenameName(rccFileRel);
+ content += cmSystemTools::GetFilenameName(RccFileBuild_);
content += "\"\n";
// Write content to file
- if (this->FileDiffers(wrapperFileAbs, content)) {
+ if (FileSys().FileDiffers(wrapperAbs, content)) {
// Write new wrapper file
- if (this->GetVerbose()) {
- this->LogBold("Generating RCC wrapper " + wrapperFileRel);
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::RCC, "Generating RCC wrapper " + wrapperAbs);
}
- if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) {
- this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs,
- "rcc wrapper file writing failed");
- success = false;
+ if (!FileSys().FileWrite(GeneratorT::RCC, wrapperAbs, content)) {
+ Log().ErrorFile(GeneratorT::RCC, wrapperAbs,
+ "RCC wrapper file writing failed");
+ Error_ = true;
}
- } else if (rccGenerated) {
+ } else if (BuildFileChanged_) {
// Just touch the wrapper file
- if (this->GetVerbose()) {
- this->LogInfo(cmQtAutoGen::RCC,
- "Touching RCC wrapper " + wrapperFileRel);
+ if (Log().Verbose()) {
+ Log().Info(GeneratorT::RCC, "Touching RCC wrapper " + wrapperAbs);
}
- cmSystemTools::Touch(wrapperFileAbs, false);
+ FileSys().Touch(wrapperAbs);
}
}
+}
+
+bool cmQtAutoGeneratorRcc::StartProcess(
+ std::string const& workingDirectory, std::vector<std::string> const& command,
+ bool mergedOutput)
+{
+ // Log command
+ if (Log().Verbose()) {
+ std::string msg = "Running command:\n";
+ msg += QuotedCommand(command);
+ msg += '\n';
+ Log().Info(GeneratorT::RCC, msg);
+ }
- return success;
+ // Create process handler
+ Process_ = cm::make_unique<ReadOnlyProcessT>();
+ Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory);
+ // Start process
+ if (!Process_->start(UVLoop(),
+ std::bind(&cm::uv_async_ptr::send, &UVRequest()))) {
+ Log().ErrorFile(GeneratorT::RCC, QrcFile_, ProcessResult_.ErrorMessage);
+ Error_ = true;
+ // Clean up
+ Process_.reset();
+ ProcessResult_.reset();
+ return false;
+ }
+ return true;
}
diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h
index 0e3f690..8a69c6c 100644
--- a/Source/cmQtAutoGeneratorRcc.h
+++ b/Source/cmQtAutoGeneratorRcc.h
@@ -5,52 +5,97 @@
#include "cmConfigure.h" // IWYU pragma: keep
-#include "cmFilePathChecksum.h"
#include "cmQtAutoGen.h"
#include "cmQtAutoGenerator.h"
+#include "cm_uv.h"
#include <string>
#include <vector>
class cmMakefile;
+// @brief AUTORCC generator
class cmQtAutoGeneratorRcc : public cmQtAutoGenerator
{
CM_DISABLE_COPY(cmQtAutoGeneratorRcc)
public:
cmQtAutoGeneratorRcc();
+ ~cmQtAutoGeneratorRcc() override;
private:
- // -- Initialization & settings
- bool InfoFileRead(cmMakefile* makefile);
- void SettingsFileRead(cmMakefile* makefile);
- bool SettingsFileWrite();
- // -- Central processing
- bool Process(cmMakefile* makefile) override;
- bool RccGenerate();
+ // -- Types
+ /// @brief Processing stage
+ enum class StageT
+ {
+ SETTINGS_READ,
+ TEST_QRC_RCC_FILES,
+ TEST_RESOURCES_READ,
+ TEST_RESOURCES,
+ TEST_INFO_FILE,
+ GENERATE,
+ GENERATE_RCC,
+ GENERATE_WRAPPER,
+ SETTINGS_WRITE,
+ FINISH,
+ END
+ };
+
+ // -- Abstract processing interface
+ bool Init(cmMakefile* makefile) override;
+ bool Process() override;
+ // -- Process stage
+ static void UVPollStage(uv_async_t* handle);
+ void PollStage();
+ void SetStage(StageT stage);
+ // -- Settings file
+ void SettingsFileRead();
+ void SettingsFileWrite();
+ // -- Tests
+ bool TestQrcRccFiles();
+ bool TestResourcesRead();
+ bool TestResources();
+ void TestInfoFile();
+ // -- Generation
+ void GenerateParentDir();
+ bool GenerateRcc();
+ void GenerateWrapper();
+
+ // -- Utility
+ bool StartProcess(std::string const& workingDirectory,
+ std::vector<std::string> const& command,
+ bool mergedOutput);
+
+private:
// -- Config settings
- std::string ConfigSuffix;
- cmQtAutoGen::MultiConfig MultiConfig;
- // -- Settings
- bool SettingsChanged;
- std::string SettingsFile;
- std::string SettingsString;
+ bool SettingsChanged_;
+ std::string ConfigSuffix_;
+ MultiConfigT MultiConfig_;
// -- Directories
- std::string ProjectSourceDir;
- std::string ProjectBinaryDir;
- std::string CurrentSourceDir;
- std::string CurrentBinaryDir;
- std::string AutogenBuildDir;
- cmFilePathChecksum FilePathChecksum;
+ std::string AutogenBuildDir_;
// -- Qt environment
- std::string RccExecutable;
- std::vector<std::string> RccListOptions;
+ std::string RccExecutable_;
+ std::vector<std::string> RccListOptions_;
// -- Job
- std::string QrcFile;
- std::string RccFile;
- std::vector<std::string> Options;
- std::vector<std::string> Inputs;
+ std::string QrcFile_;
+ std::string QrcFileName_;
+ std::string QrcFileDir_;
+ std::string RccFile_;
+ std::string RccFileWrapper_;
+ std::string RccFileBuild_;
+ std::vector<std::string> Options_;
+ std::vector<std::string> Inputs_;
+ // -- Subprocess
+ ProcessResultT ProcessResult_;
+ std::unique_ptr<ReadOnlyProcessT> Process_;
+ // -- Settings file
+ std::string SettingsFile_;
+ std::string SettingsString_;
+ // -- libuv loop
+ StageT Stage_;
+ bool Error_;
+ bool Generate_;
+ bool BuildFileChanged_;
};
#endif
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 1974be3..663a4c9 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -247,6 +247,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->SetPropertyDefault("AUTOMOC", nullptr);
this->SetPropertyDefault("AUTOUIC", nullptr);
this->SetPropertyDefault("AUTORCC", nullptr);
+ this->SetPropertyDefault("AUTOGEN_PARALLEL", nullptr);
this->SetPropertyDefault("AUTOMOC_COMPILER_PREDEFINES", nullptr);
this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", nullptr);
this->SetPropertyDefault("AUTOMOC_MACRO_NAMES", nullptr);