mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-26 04:09:35 +03:00
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.
This commit is contained 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@)
|
||||
|
||||
@@ -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@/")
|
||||
|
||||
+121
-195
@@ -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);
|
||||
}
|
||||
} else {
|
||||
if (errorMessage != nullptr) {
|
||||
std::string& err = *errorMessage;
|
||||
err = "rcc resource file does not exist:\n ";
|
||||
err += cmQtAutoGen::Quoted(fileName);
|
||||
err += "\n";
|
||||
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()));
|
||||
}
|
||||
}
|
||||
return allGood;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-19
@@ -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
|
||||
|
||||
+516
-205
@@ -4,7 +4,6 @@
|
||||
#include "cmQtAutoGenerator.h"
|
||||
|
||||
#include "cmsys/FStream.hxx"
|
||||
#include "cmsys/Terminal.h"
|
||||
|
||||
#include "cmAlgorithms.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
@@ -14,9 +13,21 @@
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmake.h"
|
||||
|
||||
// -- Static functions
|
||||
#include <algorithm>
|
||||
|
||||
static std::string HeadLine(std::string const& title)
|
||||
// -- Class methods
|
||||
|
||||
void cmQtAutoGenerator::Logger::SetVerbose(bool value)
|
||||
{
|
||||
Verbose_ = value;
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::Logger::SetColorOutput(bool value)
|
||||
{
|
||||
ColorOutput_ = value;
|
||||
}
|
||||
|
||||
std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title)
|
||||
{
|
||||
std::string head = title;
|
||||
head += '\n';
|
||||
@@ -25,153 +36,93 @@ static std::string HeadLine(std::string const& title)
|
||||
return head;
|
||||
}
|
||||
|
||||
static std::string QuotedCommand(std::vector<std::string> const& command)
|
||||
void cmQtAutoGenerator::Logger::Info(GeneratorT genType,
|
||||
std::string const& message)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// -- Class methods
|
||||
|
||||
cmQtAutoGenerator::cmQtAutoGenerator()
|
||||
: Verbose(cmSystemTools::HasEnv("VERBOSE"))
|
||||
, ColorOutput(true)
|
||||
{
|
||||
{
|
||||
std::string colorEnv;
|
||||
cmSystemTools::GetEnv("COLOR", colorEnv);
|
||||
if (!colorEnv.empty()) {
|
||||
this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmQtAutoGenerator::Run(std::string const& infoFile,
|
||||
std::string const& config)
|
||||
{
|
||||
// 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());
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::LogBold(std::string const& message) const
|
||||
{
|
||||
cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
|
||||
cmsysTerminal_Color_ForegroundBold,
|
||||
message.c_str(), true, this->ColorOutput);
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::LogInfo(cmQtAutoGen::Generator genType,
|
||||
std::string const& message) const
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType,
|
||||
std::string const& message) const
|
||||
{
|
||||
std::string msg = cmQtAutoGen::GeneratorName(genType);
|
||||
msg += " warning:";
|
||||
if (message.find('\n') == std::string::npos) {
|
||||
// Single line message
|
||||
msg.push_back(' ');
|
||||
} else {
|
||||
// Multi line message
|
||||
msg.push_back('\n');
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex_);
|
||||
cmSystemTools::Stdout(msg.c_str(), msg.size());
|
||||
}
|
||||
// Message
|
||||
msg += message;
|
||||
if (msg.back() != '\n') {
|
||||
msg.push_back('\n');
|
||||
}
|
||||
msg.push_back('\n');
|
||||
cmSystemTools::Stdout(msg.c_str(), msg.size());
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::LogFileWarning(cmQtAutoGen::Generator genType,
|
||||
std::string const& filename,
|
||||
std::string const& message) const
|
||||
{
|
||||
std::string msg = " ";
|
||||
msg += cmQtAutoGen::Quoted(filename);
|
||||
msg.push_back('\n');
|
||||
// Message
|
||||
msg += message;
|
||||
this->LogWarning(genType, msg);
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::LogError(cmQtAutoGen::Generator genType,
|
||||
std::string const& message) const
|
||||
void cmQtAutoGenerator::Logger::Warning(GeneratorT genType,
|
||||
std::string const& message)
|
||||
{
|
||||
std::string msg;
|
||||
msg.push_back('\n');
|
||||
msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error");
|
||||
if (message.find('\n') == std::string::npos) {
|
||||
// Single line message
|
||||
msg += GeneratorName(genType);
|
||||
msg += " warning: ";
|
||||
} else {
|
||||
// Multi line message
|
||||
msg += HeadLine(GeneratorName(genType) + " warning");
|
||||
}
|
||||
// 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::Stdout(msg.c_str(), msg.size());
|
||||
}
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::LogFileError(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 += Quoted(filename);
|
||||
msg.push_back('\n');
|
||||
// Message
|
||||
msg += message;
|
||||
Warning(genType, msg);
|
||||
}
|
||||
|
||||
void cmQtAutoGenerator::Logger::Error(GeneratorT genType,
|
||||
std::string const& message)
|
||||
{
|
||||
std::string msg;
|
||||
msg += HeadLine(GeneratorName(genType) + " error");
|
||||
// Message
|
||||
msg += message;
|
||||
if (msg.back() != '\n') {
|
||||
msg.push_back('\n');
|
||||
}
|
||||
msg.push_back('\n');
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex_);
|
||||
cmSystemTools::Stderr(msg.c_str(), msg.size());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
bool cmQtAutoGenerator::FileSystem::FileIsOlderThan(
|
||||
std::string const& buildFile, std::string const& sourceFile,
|
||||
std::string* error)
|
||||
{
|
||||
bool res(false);
|
||||
int result = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex_);
|
||||
res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result);
|
||||
}
|
||||
if (res) {
|
||||
res = (result < 0);
|
||||
} else {
|
||||
if (error != nullptr) {
|
||||
error->append(
|
||||
"File modification time comparison failed for the files\n ");
|
||||
error->append(Quoted(buildFile));
|
||||
error->append("\nand\n ");
|
||||
error->append(Quoted(sourceFile));
|
||||
}
|
||||
}
|
||||
return success;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
int result = 0;
|
||||
if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) {
|
||||
return (result < 0);
|
||||
}
|
||||
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));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename)
|
||||
{
|
||||
// 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));
|
||||
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();
|
||||
}
|
||||
|
||||
+260
-48
@@ -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_; }
|
||||
|
||||
// -- Utility
|
||||
static std::string SettingsFind(std::string const& content, const char* key);
|
||||
|
||||
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;
|
||||
// -- 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1696
-1405
@@ -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";
|
||||
|
||||
// -- Static functions
|
||||
|
||||
static std::string SubDirPrefix(std::string const& fileName)
|
||||
std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
|
||||
std::string const& relativePath) const
|
||||
{
|
||||
std::string res(cmSystemTools::GetFilenamePath(fileName));
|
||||
if (!res.empty()) {
|
||||
res += '/';
|
||||
return cmSystemTools::CollapseCombinedPath(AutogenBuildDir, relativePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to find the header file to the given file base path by
|
||||
* appending different header extensions
|
||||
* @return True on success
|
||||
*/
|
||||
bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader(
|
||||
std::string& header, std::string const& testBasePath) const
|
||||
{
|
||||
for (std::string const& ext : HeaderExtensions) {
|
||||
std::string testFilePath(testBasePath);
|
||||
testFilePath.push_back('.');
|
||||
testFilePath += ext;
|
||||
if (FileSys->FileExists(testFilePath)) {
|
||||
header = testFilePath;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
|
||||
std::string const& fileName) const
|
||||
{
|
||||
return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first relevant Qt macro name found in the given C++ code
|
||||
* @return The name of the Qt macro or an empty string
|
||||
*/
|
||||
std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro(
|
||||
std::string const& content) const
|
||||
{
|
||||
for (KeyExpT const& filter : MacroFilters) {
|
||||
// Run a simple find string operation before the expensive
|
||||
// regular expression check
|
||||
if (content.find(filter.Key) != std::string::npos) {
|
||||
cmsys::RegularExpressionMatch match;
|
||||
if (filter.Exp.find(content.c_str(), match)) {
|
||||
// Return macro name on demand
|
||||
return filter.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const
|
||||
{
|
||||
std::string res;
|
||||
const auto itB = MacroFilters.cbegin();
|
||||
const auto itE = MacroFilters.cend();
|
||||
const auto itL = itE - 1;
|
||||
auto itC = itB;
|
||||
for (; itC != itE; ++itC) {
|
||||
// Separator
|
||||
if (itC != itB) {
|
||||
if (itC != itL) {
|
||||
res += ", ";
|
||||
} else {
|
||||
res += " or ";
|
||||
}
|
||||
}
|
||||
// Key
|
||||
res += itC->Key;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return (std::find(list.begin(), list.end(), entry) != list.end());
|
||||
// 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();
|
||||
}
|
||||
|
||||
// -- Class methods
|
||||
void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies(
|
||||
std::string const& content, std::set<std::string>& depends) const
|
||||
{
|
||||
if (!DependFilters.empty() && !content.empty()) {
|
||||
for (KeyExpT const& filter : DependFilters) {
|
||||
// Run a simple find string check
|
||||
if (content.find(filter.Key) != std::string::npos) {
|
||||
// Run the expensive regular expression check loop
|
||||
const char* contentChars = content.c_str();
|
||||
cmsys::RegularExpressionMatch match;
|
||||
while (filter.Exp.find(contentChars, match)) {
|
||||
{
|
||||
std::string dep = match.match(1);
|
||||
if (!dep.empty()) {
|
||||
depends.emplace(std::move(dep));
|
||||
}
|
||||
}
|
||||
contentChars += match.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped(
|
||||
std::string const& fileName) const
|
||||
{
|
||||
return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
|
||||
}
|
||||
|
||||
void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk)
|
||||
{
|
||||
if (AutoMoc && Header) {
|
||||
// Don't parse header for moc if the file is included by a source already
|
||||
if (wrk.Gen().ParallelMocIncluded(FileName)) {
|
||||
AutoMoc = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (AutoMoc || AutoUic) {
|
||||
std::string error;
|
||||
MetaT meta;
|
||||
if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) {
|
||||
if (!meta.Content.empty()) {
|
||||
meta.FileDir = 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
|
||||
if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDirAbs)) {
|
||||
RegisterJobError();
|
||||
}
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
|
||||
{
|
||||
bool done = false;
|
||||
std::size_t queueSize = queue.size();
|
||||
|
||||
// Change the active queue
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
|
||||
"The source file is empty");
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
} 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;
|
||||
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 {
|
||||
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;
|
||||
// Abort requested
|
||||
queue.clear();
|
||||
queueSize = 0;
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
std::string cmQtAutoGeneratorMocUic::MocStringMacros() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string cmQtAutoGeneratorMocUic::MocStringHeaders(
|
||||
std::string const& fileBase) const
|
||||
{
|
||||
std::string res = fileBase;
|
||||
res += ".{";
|
||||
res += cmJoin(this->HeaderExtensions, ",");
|
||||
res += "}";
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader(
|
||||
std::string const& sourcePath, std::string const& includeBase) const
|
||||
{
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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";
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
// Notify threads
|
||||
if (queueSize == 1) {
|
||||
JobsConditionRead_.notify_one();
|
||||
} else {
|
||||
this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
|
||||
"Could not find dependency file " +
|
||||
cmQtAutoGen::Quoted(match));
|
||||
JobsConditionRead_.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True on success
|
||||
*/
|
||||
bool cmQtAutoGeneratorMocUic::MocParseSourceContent(
|
||||
std::string const& absFilename, std::string const& contentText)
|
||||
void cmQtAutoGeneratorMocUic::ThreadsStop()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void cmQtAutoGeneratorMocUic::MocParseHeaderContent(
|
||||
std::string const& absFilename, std::string const& contentText)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::MocGenerateAll()
|
||||
{
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True on success
|
||||
*/
|
||||
bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob,
|
||||
bool* generated)
|
||||
{
|
||||
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);
|
||||
}
|
||||
generate = true;
|
||||
break;
|
||||
}
|
||||
if (!error.empty()) {
|
||||
this->LogError(cmQtAutoGen::MOC, error);
|
||||
success = 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;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests if the file name is in the skip list
|
||||
*/
|
||||
bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const
|
||||
{
|
||||
if (this->UicEnabled()) {
|
||||
// Test if the file name is on the skip list
|
||||
if (!ListContains(this->UicSkipList, absFilename)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename,
|
||||
std::string const& contentText)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::UicFindIncludedFile(
|
||||
std::string& absFile, std::string const& sourceFile,
|
||||
std::string const& includeString)
|
||||
{
|
||||
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
|
||||
if (!Workers_.empty()) {
|
||||
// Clear all jobs
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
||||
JobThreadsAbort_ = true;
|
||||
JobsRemain_ -= JobQueue_.size();
|
||||
JobQueue_.clear();
|
||||
|
||||
// Search for the .ui file!
|
||||
for (std::string const& testFile : testFiles) {
|
||||
if (cmSystemTools::FileExists(testFile.c_str())) {
|
||||
absFile = cmSystemTools::GetRealPath(testFile);
|
||||
success = true;
|
||||
break;
|
||||
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();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::UicGenerateAll()
|
||||
bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
|
||||
{
|
||||
if (!this->UicEnabled()) {
|
||||
return true;
|
||||
}
|
||||
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
||||
return (JobsRemain_ == 0);
|
||||
}
|
||||
|
||||
// Look for name collisions in included uic files
|
||||
void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
|
||||
{
|
||||
bool const jobProcessed(jobHandle);
|
||||
if (jobProcessed) {
|
||||
jobHandle.reset(nullptr);
|
||||
}
|
||||
{
|
||||
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;
|
||||
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();
|
||||
}
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
this->LogError(cmQtAutoGen::UIC, emsg);
|
||||
return false;
|
||||
// Wait for new jobs
|
||||
while (!JobThreadsAbort_ && JobQueue_.empty()) {
|
||||
JobsConditionRead_.wait(jobsLock);
|
||||
}
|
||||
// Try to pick up a new job handle
|
||||
if (!JobThreadsAbort_ && !JobQueue_.empty()) {
|
||||
jobHandle = std::move(JobQueue_.front());
|
||||
JobQueue_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
|
||||
{
|
||||
bool success = true;
|
||||
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
||||
RegisterJobError();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
|
||||
// locked
|
||||
void cmQtAutoGeneratorMocUic::RegisterJobError()
|
||||
{
|
||||
JobError_ = true;
|
||||
if (!JobThreadsAbort_) {
|
||||
JobThreadsAbort_ = true;
|
||||
// Clear remaining jobs
|
||||
if (JobsRemain_ != 0) {
|
||||
JobsRemain_ -= JobQueue_.size();
|
||||
JobQueue_.clear();
|
||||
}
|
||||
}
|
||||
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));
|
||||
bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle)
|
||||
{
|
||||
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
||||
if (!JobThreadsAbort_) {
|
||||
bool pushJobHandle = true;
|
||||
// Do additional tests if this is an included moc job
|
||||
const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle));
|
||||
if (!mocJob.IncludeString.empty()) {
|
||||
// Register included moc file and look for collisions
|
||||
MocIncludedFiles_.emplace(mocJob.SourceFile);
|
||||
if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) {
|
||||
// Another source file includes the same moc file!
|
||||
for (const JobHandleT& otherHandle : JobQueues_.Moc) {
|
||||
const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle));
|
||||
if (otherJob.IncludeString == mocJob.IncludeString) {
|
||||
// Check if the same moc file would be generated from different
|
||||
// source files which is an error.
|
||||
if (otherJob.SourceFile != mocJob.SourceFile) {
|
||||
// Include string collision
|
||||
std::string error = "The two source files\n ";
|
||||
error += Quoted(mocJob.IncluderFile);
|
||||
error += " and\n ";
|
||||
error += Quoted(otherJob.IncluderFile);
|
||||
error += "\ncontain the 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;
|
||||
}
|
||||
}
|
||||
cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
|
||||
}
|
||||
cmd.push_back("-o");
|
||||
cmd.push_back(uicFileAbs);
|
||||
cmd.push_back(uicJob.SourceFile);
|
||||
}
|
||||
// Push job on demand
|
||||
if (pushJobHandle) {
|
||||
JobQueues_.Moc.emplace_back(std::move(jobHandle));
|
||||
}
|
||||
}
|
||||
return !JobError_;
|
||||
}
|
||||
|
||||
std::string output;
|
||||
if (this->RunCommand(cmd, output)) {
|
||||
// Success
|
||||
bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle)
|
||||
{
|
||||
std::lock_guard<std::mutex> jobsLock(JobsMutex_);
|
||||
if (!JobThreadsAbort_) {
|
||||
bool pushJobHandle = true;
|
||||
// Look for include collisions.
|
||||
const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
|
||||
for (const JobHandleT& otherHandle : JobQueues_.Uic) {
|
||||
const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle));
|
||||
if (otherJob.IncludeString == uicJob.IncludeString) {
|
||||
// Check if the same uic file would be generated from different
|
||||
// source files which would be an error.
|
||||
if (otherJob.SourceFile != uicJob.SourceFile) {
|
||||
// Include string collision
|
||||
std::string error = "The two source files\n ";
|
||||
error += Quoted(uicJob.IncluderFile);
|
||||
error += " and\n ";
|
||||
error += Quoted(otherJob.IncluderFile);
|
||||
error += "\ncontain the 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();
|
||||
}
|
||||
// Do not push this job in since the uic file already
|
||||
// gets generated by an other job.
|
||||
pushJobHandle = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pushJobHandle) {
|
||||
JobQueues_.Uic.emplace_back(std::move(jobHandle));
|
||||
}
|
||||
}
|
||||
return !JobError_;
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
|
||||
std::string const& sourceFile)
|
||||
{
|
||||
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
||||
return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
|
||||
}
|
||||
|
||||
void cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
|
||||
std::string const& mocFile)
|
||||
{
|
||||
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
||||
MocAutoFiles_.emplace(mocFile);
|
||||
}
|
||||
|
||||
void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
|
||||
{
|
||||
std::lock_guard<std::mutex> mocLock(JobsMutex_);
|
||||
MocAutoFileUpdated_ = true;
|
||||
}
|
||||
|
||||
void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
|
||||
{
|
||||
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 {
|
||||
// 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);
|
||||
// Valid content
|
||||
for (std::string const& mocfile : MocAutoFiles_) {
|
||||
content += "#include \"";
|
||||
content += mocfile;
|
||||
content += "\"\n";
|
||||
}
|
||||
cmSystemTools::RemoveFile(uicFileAbs);
|
||||
success = false;
|
||||
}
|
||||
} 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;
|
||||
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);
|
||||
}
|
||||
if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) {
|
||||
Log().ErrorFile(GeneratorT::MOC, compAbs,
|
||||
"mocs compilation file writing failed");
|
||||
RegisterJobError();
|
||||
return;
|
||||
}
|
||||
} else if (MocAutoFileUpdated_) {
|
||||
// Only touch mocs compilation file
|
||||
if (Log().Verbose()) {
|
||||
Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compRel);
|
||||
}
|
||||
FileSys().Touch(compAbs);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+373
-127
@@ -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
|
||||
{
|
||||
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 Uic settings
|
||||
///
|
||||
class UicSettingsT
|
||||
{
|
||||
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 BuildFileRel;
|
||||
std::string IncluderFile;
|
||||
std::string IncludeString;
|
||||
std::string BuildFile;
|
||||
bool DependsValid = false;
|
||||
std::set<std::string> Depends;
|
||||
};
|
||||
|
||||
/// @brief MOC job
|
||||
struct MocJobIncluded : MocJobAuto
|
||||
/// @brief Uic a file job
|
||||
///
|
||||
class JobUicT : public JobT
|
||||
{
|
||||
bool DependsValid = false;
|
||||
std::string Includer;
|
||||
std::string IncludeString;
|
||||
};
|
||||
public:
|
||||
JobUicT(std::string&& sourceFile, std::string const& includerFile,
|
||||
std::string&& includeString)
|
||||
: SourceFile(std::move(sourceFile))
|
||||
, IncluderFile(includerFile)
|
||||
, IncludeString(std::move(includeString))
|
||||
{
|
||||
}
|
||||
|
||||
/// @brief UIC job
|
||||
struct UicJob
|
||||
{
|
||||
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);
|
||||
|
||||
// -- Settings file
|
||||
void SettingsFileRead(cmMakefile* makefile);
|
||||
bool SettingsFileWrite();
|
||||
bool SettingsChanged() const
|
||||
/// @brief Worker Thread
|
||||
///
|
||||
class WorkerT
|
||||
{
|
||||
return (this->MocSettingsChanged || this->UicSettingsChanged);
|
||||
}
|
||||
CM_DISABLE_COPY(WorkerT)
|
||||
public:
|
||||
WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop);
|
||||
~WorkerT();
|
||||
|
||||
// -- Central processing
|
||||
bool Process(cmMakefile* makefile) override;
|
||||
// -- 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(); }
|
||||
|
||||
// -- Source parsing
|
||||
bool ParseSourceFile(std::string const& absFilename, const SourceJob& job);
|
||||
bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job);
|
||||
bool ParsePostprocess();
|
||||
// -- 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;
|
||||
|
||||
// -- 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);
|
||||
// -- External processes
|
||||
/// @brief Verbose logging version
|
||||
bool RunProcess(GeneratorT genType, ProcessResultT& result,
|
||||
std::vector<std::string> const& command);
|
||||
|
||||
bool MocGenerateAll();
|
||||
bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr);
|
||||
private:
|
||||
/// @brief Thread main loop
|
||||
void Loop();
|
||||
|
||||
// -- 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);
|
||||
// -- Libuv callbacks
|
||||
static void UVProcessStart(uv_async_t* handle);
|
||||
void UVProcessFinished();
|
||||
|
||||
// -- Utility
|
||||
bool FindHeader(std::string& header, std::string const& testBasePath) const;
|
||||
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_;
|
||||
};
|
||||
|
||||
// -- Meta
|
||||
std::string ConfigSuffix;
|
||||
cmQtAutoGen::MultiConfig MultiConfig;
|
||||
/// @brief Processing stage
|
||||
enum class StageT
|
||||
{
|
||||
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
|
||||
|
||||
+458
-267
@@ -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);
|
||||
}
|
||||
|
||||
cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc()
|
||||
{
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
|
||||
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;
|
||||
}
|
||||
|
||||
void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile)
|
||||
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::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 Generate_;
|
||||
}
|
||||
|
||||
bool cmQtAutoGeneratorRcc::TestResourcesRead()
|
||||
{
|
||||
if (!Inputs_.empty()) {
|
||||
// Inputs are known already
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} 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_);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Inputs_.empty()) {
|
||||
// Convert relative paths to absolute paths
|
||||
RccListConvertFullPath(QrcFileDir_, Inputs_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True on success
|
||||
*/
|
||||
bool cmQtAutoGeneratorRcc::RccGenerate()
|
||||
bool cmQtAutoGeneratorRcc::TestResources()
|
||||
{
|
||||
bool success = true;
|
||||
bool rccGenerated = false;
|
||||
|
||||
std::string rccFileAbs;
|
||||
if (Inputs_.empty()) {
|
||||
return true;
|
||||
}
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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";
|
||||
}
|
||||
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 (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);
|
||||
void cmQtAutoGeneratorRcc::GenerateParentDir()
|
||||
{
|
||||
// Make sure the parent directory exists
|
||||
if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileBuild_)) {
|
||||
Error_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string output;
|
||||
if (this->RunCommand(cmd, output)) {
|
||||
// Success
|
||||
rccGenerated = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user